1
0
mirror of https://github.com/postgres/postgres.git synced 2025-05-09 18:21:05 +03:00

psql: Add more meta-commands able to use the extended protocol

Currently, only unnamed prepared statement are supported by psql with
the meta-command \bind.  With only this command, it is not possible to
test named statement creation, execution or close through the extended
protocol.

This commit introduces three additional commands:
* \parse creates a prepared statement using the extended protocol,
acting as a wrapper of libpq's PQsendPrepare().
* \bind_named binds and executes an existing prepared statement using
the extended protocol, for PQsendQueryPrepared().
* \close closes an existing prepared statement using the extended
protocol, for PQsendClosePrepared().

This is going to be useful to add regression tests for the extended
query protocol, and I have some plans for that on separate threads.
Note that \bind relies on PQsendQueryParams().

The code of psql is refactored so as bind_flag is replaced by an enum in
_psqlSettings that tracks the type of libpq routine to execute, based on
the meta-command involved, with the default being PQsendQuery().  This
refactoring piece has been written by me, while Anthonin has implemented
the rest.

Author: Anthonin Bonnefoy, Michael Paquier
Reviewed-by: Aleksander Alekseev, Jelte Fennema-Nio
Discussion: https://postgr.es/m/CAO6_XqpSq0Q0kQcVLCbtagY94V2GxNP3zCnR6WnOM8WqXPK4nw@mail.gmail.com
This commit is contained in:
Michael Paquier 2024-08-22 16:25:57 +09:00
parent a36aa223ec
commit d55322b0da
9 changed files with 369 additions and 19 deletions

View File

@ -917,6 +917,36 @@ INSERT INTO tbl1 VALUES ($1, $2) \bind 'first value' 'second value' \g
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry id="app-psql-meta-command-bind-named">
<term><literal>\bind_named</literal> <replaceable class="parameter">statement_name</replaceable> [ <replaceable class="parameter">parameter</replaceable> ] ... </term>
<listitem>
<para>
<literal>\bind_named</literal> is equivalent to <literal>\bind</literal>,
except that it takes the name of an existing prepared statement as
first parameter. An empty string denotes the unnamed prepared
statement.
</para>
<para>
Example:
<programlisting>
INSERT INTO tbls1 VALUES ($1, $2) \parse stmt1
\bind_named stmt1 'first value' 'second value' \g
</programlisting>
</para>
<para>
This command causes the extended query protocol (see
<xref linkend="protocol-query-concepts"/>) to be used, unlike normal
<application>psql</application> operation, which uses the simple
query protocol. So this command can be useful to test the extended
query protocol from <application>psql</application>.
</para>
</listitem>
</varlistentry>
<varlistentry id="app-psql-meta-command-c-lc"> <varlistentry id="app-psql-meta-command-c-lc">
<term><literal>\c</literal> or <literal>\connect [ -reuse-previous=<replaceable class="parameter">on|off</replaceable> ] [ <replaceable class="parameter">dbname</replaceable> [ <replaceable class="parameter">username</replaceable> ] [ <replaceable class="parameter">host</replaceable> ] [ <replaceable class="parameter">port</replaceable> ] | <replaceable class="parameter">conninfo</replaceable> ]</literal></term> <term><literal>\c</literal> or <literal>\connect [ -reuse-previous=<replaceable class="parameter">on|off</replaceable> ] [ <replaceable class="parameter">dbname</replaceable> [ <replaceable class="parameter">username</replaceable> ] [ <replaceable class="parameter">host</replaceable> ] [ <replaceable class="parameter">port</replaceable> ] | <replaceable class="parameter">conninfo</replaceable> ]</literal></term>
<listitem> <listitem>
@ -1038,6 +1068,35 @@ INSERT INTO tbl1 VALUES ($1, $2) \bind 'first value' 'second value' \g
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry id="app-psql-meta-command-close">
<term><literal>\close</literal> <replaceable class="parameter">prepared_statement_name</replaceable></term>
<listitem>
<para>
Closes the specified prepared statement. An empty string denotes the
unnamed prepared statement. If no prepared statement exists with this
name, the operation is a no-op.
</para>
<para>
Example:
<programlisting>
SELECT $1 \parse stmt1
\close stmt1
</programlisting>
</para>
<para>
This command causes the extended query protocol to be used,
unlike normal <application>psql</application> operation, which
uses the simple query protocol. So this command can be useful
to test the extended query protocol from
<application>psql</application>.
</para>
</listitem>
</varlistentry>
<varlistentry id="app-psql-meta-commands-copy"> <varlistentry id="app-psql-meta-commands-copy">
<term><literal>\copy { <replaceable class="parameter">table</replaceable> [ ( <replaceable class="parameter">column_list</replaceable> ) ] } <term><literal>\copy { <replaceable class="parameter">table</replaceable> [ ( <replaceable class="parameter">column_list</replaceable> ) ] }
<literal>from</literal> <literal>from</literal>
@ -2780,6 +2839,37 @@ lo_import 152801
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry id="app-psql-meta-command-parse">
<term><literal>\parse <replaceable class="parameter">statement_name</replaceable></literal></term>
<listitem>
<para>
Creates a prepared statement from the current query buffer, based on
the name of a destination prepared-statement object. An empty string
denotes the unnamed prepared statement.
</para>
<para>
Example:
<programlisting>
SELECT $1 \parse stmt1
</programlisting>
</para>
<para>
This command causes the extended query protocol to be used, unlike
normal <application>psql</application> operation, which uses the
simple query protocol. A
<xref linkend="protocol-message-formats-Parse"/>
message will be issued by this command so it can be useful to
test the extended query protocol from
<application>psql</application>. This command affects only the next
query executed; all subsequent queries will use the simple query
protocol by default.
</para>
</listitem>
</varlistentry>
<varlistentry id="app-psql-meta-command-password"> <varlistentry id="app-psql-meta-command-password">
<term><literal>\password [ <replaceable class="parameter">username</replaceable> ]</literal></term> <term><literal>\password [ <replaceable class="parameter">username</replaceable> ]</literal></term>
<listitem> <listitem>

View File

@ -64,10 +64,14 @@ static backslashResult exec_command(const char *cmd,
PQExpBuffer previous_buf); PQExpBuffer previous_buf);
static backslashResult exec_command_a(PsqlScanState scan_state, bool active_branch); static backslashResult exec_command_a(PsqlScanState scan_state, bool active_branch);
static backslashResult exec_command_bind(PsqlScanState scan_state, bool active_branch); static backslashResult exec_command_bind(PsqlScanState scan_state, bool active_branch);
static backslashResult exec_command_bind_named(PsqlScanState scan_state, bool active_branch,
const char *cmd);
static backslashResult exec_command_C(PsqlScanState scan_state, bool active_branch); static backslashResult exec_command_C(PsqlScanState scan_state, bool active_branch);
static backslashResult exec_command_connect(PsqlScanState scan_state, bool active_branch); static backslashResult exec_command_connect(PsqlScanState scan_state, bool active_branch);
static backslashResult exec_command_cd(PsqlScanState scan_state, bool active_branch, static backslashResult exec_command_cd(PsqlScanState scan_state, bool active_branch,
const char *cmd); const char *cmd);
static backslashResult exec_command_close(PsqlScanState scan_state, bool active_branch,
const char *cmd);
static backslashResult exec_command_conninfo(PsqlScanState scan_state, bool active_branch); static backslashResult exec_command_conninfo(PsqlScanState scan_state, bool active_branch);
static backslashResult exec_command_copy(PsqlScanState scan_state, bool active_branch); static backslashResult exec_command_copy(PsqlScanState scan_state, bool active_branch);
static backslashResult exec_command_copyright(PsqlScanState scan_state, bool active_branch); static backslashResult exec_command_copyright(PsqlScanState scan_state, bool active_branch);
@ -116,6 +120,8 @@ static backslashResult exec_command_lo(PsqlScanState scan_state, bool active_bra
static backslashResult exec_command_out(PsqlScanState scan_state, bool active_branch); static backslashResult exec_command_out(PsqlScanState scan_state, bool active_branch);
static backslashResult exec_command_print(PsqlScanState scan_state, bool active_branch, static backslashResult exec_command_print(PsqlScanState scan_state, bool active_branch,
PQExpBuffer query_buf, PQExpBuffer previous_buf); PQExpBuffer query_buf, PQExpBuffer previous_buf);
static backslashResult exec_command_parse(PsqlScanState scan_state, bool active_branch,
const char *cmd);
static backslashResult exec_command_password(PsqlScanState scan_state, bool active_branch); static backslashResult exec_command_password(PsqlScanState scan_state, bool active_branch);
static backslashResult exec_command_prompt(PsqlScanState scan_state, bool active_branch, static backslashResult exec_command_prompt(PsqlScanState scan_state, bool active_branch,
const char *cmd); const char *cmd);
@ -312,12 +318,16 @@ exec_command(const char *cmd,
status = exec_command_a(scan_state, active_branch); status = exec_command_a(scan_state, active_branch);
else if (strcmp(cmd, "bind") == 0) else if (strcmp(cmd, "bind") == 0)
status = exec_command_bind(scan_state, active_branch); status = exec_command_bind(scan_state, active_branch);
else if (strcmp(cmd, "bind_named") == 0)
status = exec_command_bind_named(scan_state, active_branch, cmd);
else if (strcmp(cmd, "C") == 0) else if (strcmp(cmd, "C") == 0)
status = exec_command_C(scan_state, active_branch); status = exec_command_C(scan_state, active_branch);
else if (strcmp(cmd, "c") == 0 || strcmp(cmd, "connect") == 0) else if (strcmp(cmd, "c") == 0 || strcmp(cmd, "connect") == 0)
status = exec_command_connect(scan_state, active_branch); status = exec_command_connect(scan_state, active_branch);
else if (strcmp(cmd, "cd") == 0) else if (strcmp(cmd, "cd") == 0)
status = exec_command_cd(scan_state, active_branch, cmd); status = exec_command_cd(scan_state, active_branch, cmd);
else if (strcmp(cmd, "close") == 0)
status = exec_command_close(scan_state, active_branch, cmd);
else if (strcmp(cmd, "conninfo") == 0) else if (strcmp(cmd, "conninfo") == 0)
status = exec_command_conninfo(scan_state, active_branch); status = exec_command_conninfo(scan_state, active_branch);
else if (pg_strcasecmp(cmd, "copy") == 0) else if (pg_strcasecmp(cmd, "copy") == 0)
@ -379,6 +389,8 @@ exec_command(const char *cmd,
else if (strcmp(cmd, "p") == 0 || strcmp(cmd, "print") == 0) else if (strcmp(cmd, "p") == 0 || strcmp(cmd, "print") == 0)
status = exec_command_print(scan_state, active_branch, status = exec_command_print(scan_state, active_branch,
query_buf, previous_buf); query_buf, previous_buf);
else if (strcmp(cmd, "parse") == 0)
status = exec_command_parse(scan_state, active_branch, cmd);
else if (strcmp(cmd, "password") == 0) else if (strcmp(cmd, "password") == 0)
status = exec_command_password(scan_state, active_branch); status = exec_command_password(scan_state, active_branch);
else if (strcmp(cmd, "prompt") == 0) else if (strcmp(cmd, "prompt") == 0)
@ -472,6 +484,7 @@ exec_command_bind(PsqlScanState scan_state, bool active_branch)
int nalloc = 0; int nalloc = 0;
pset.bind_params = NULL; pset.bind_params = NULL;
pset.stmtName = NULL;
while ((opt = psql_scan_slash_option(scan_state, OT_NORMAL, NULL, false))) while ((opt = psql_scan_slash_option(scan_state, OT_NORMAL, NULL, false)))
{ {
@ -485,7 +498,57 @@ exec_command_bind(PsqlScanState scan_state, bool active_branch)
} }
pset.bind_nparams = nparams; pset.bind_nparams = nparams;
pset.bind_flag = true; pset.send_mode = PSQL_SEND_EXTENDED_QUERY_PARAMS;
}
else
ignore_slash_options(scan_state);
return status;
}
/*
* \bind_named -- set query parameters for an existing prepared statement
*/
static backslashResult
exec_command_bind_named(PsqlScanState scan_state, bool active_branch,
const char *cmd)
{
backslashResult status = PSQL_CMD_SKIP_LINE;
if (active_branch)
{
char *opt;
int nparams = 0;
int nalloc = 0;
pset.bind_params = NULL;
pset.stmtName = NULL;
/* get the mandatory prepared statement name */
opt = psql_scan_slash_option(scan_state, OT_NORMAL, NULL, false);
if (!opt)
{
pg_log_error("\\%s: missing required argument", cmd);
status = PSQL_CMD_ERROR;
}
else
{
pset.stmtName = opt;
pset.send_mode = PSQL_SEND_EXTENDED_QUERY_PREPARED;
/* set of parameters */
while ((opt = psql_scan_slash_option(scan_state, OT_NORMAL, NULL, false)))
{
nparams++;
if (nparams > nalloc)
{
nalloc = nalloc ? nalloc * 2 : 1;
pset.bind_params = pg_realloc_array(pset.bind_params, char *, nalloc);
}
pset.bind_params[nparams - 1] = opt;
}
pset.bind_nparams = nparams;
}
} }
else else
ignore_slash_options(scan_state); ignore_slash_options(scan_state);
@ -643,6 +706,38 @@ exec_command_cd(PsqlScanState scan_state, bool active_branch, const char *cmd)
return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR; return success ? PSQL_CMD_SKIP_LINE : PSQL_CMD_ERROR;
} }
/*
* \close -- close a previously prepared statement
*/
static backslashResult
exec_command_close(PsqlScanState scan_state, bool active_branch, const char *cmd)
{
backslashResult status = PSQL_CMD_SKIP_LINE;
if (active_branch)
{
char *opt = psql_scan_slash_option(scan_state,
OT_NORMAL, NULL, false);
pset.stmtName = NULL;
if (!opt)
{
pg_log_error("\\%s: missing required argument", cmd);
status = PSQL_CMD_ERROR;
}
else
{
pset.stmtName = opt;
pset.send_mode = PSQL_SEND_EXTENDED_CLOSE;
status = PSQL_CMD_SEND;
}
}
else
ignore_slash_options(scan_state);
return status;
}
/* /*
* \conninfo -- display information about the current connection * \conninfo -- display information about the current connection
*/ */
@ -2096,6 +2191,39 @@ exec_command_print(PsqlScanState scan_state, bool active_branch,
return PSQL_CMD_SKIP_LINE; return PSQL_CMD_SKIP_LINE;
} }
/*
* \parse -- parse query
*/
static backslashResult
exec_command_parse(PsqlScanState scan_state, bool active_branch,
const char *cmd)
{
backslashResult status = PSQL_CMD_SKIP_LINE;
if (active_branch)
{
char *opt = psql_scan_slash_option(scan_state,
OT_NORMAL, NULL, false);
pset.stmtName = NULL;
if (!opt)
{
pg_log_error("\\%s: missing required argument", cmd);
status = PSQL_CMD_ERROR;
}
else
{
pset.stmtName = opt;
pset.send_mode = PSQL_SEND_EXTENDED_PARSE;
status = PSQL_CMD_SEND;
}
}
else
ignore_slash_options(scan_state);
return status;
}
/* /*
* \password -- set user password * \password -- set user password
*/ */

View File

@ -1274,15 +1274,28 @@ sendquery_cleanup:
pset.gsavepopt = NULL; pset.gsavepopt = NULL;
} }
/* clean up after \bind */ /* clean up after extended protocol queries */
if (pset.bind_flag) switch (pset.send_mode)
{ {
for (i = 0; i < pset.bind_nparams; i++) case PSQL_SEND_EXTENDED_CLOSE: /* \close */
free(pset.bind_params[i]); free(pset.stmtName);
free(pset.bind_params); break;
pset.bind_params = NULL; case PSQL_SEND_EXTENDED_PARSE: /* \parse */
pset.bind_flag = false; free(pset.stmtName);
break;
case PSQL_SEND_EXTENDED_QUERY_PARAMS: /* \bind */
case PSQL_SEND_EXTENDED_QUERY_PREPARED: /* \bind_named */
for (i = 0; i < pset.bind_nparams; i++)
free(pset.bind_params[i]);
free(pset.bind_params);
free(pset.stmtName);
pset.bind_params = NULL;
break;
case PSQL_SEND_QUERY:
break;
} }
pset.stmtName = NULL;
pset.send_mode = PSQL_SEND_QUERY;
/* reset \gset trigger */ /* reset \gset trigger */
if (pset.gset_prefix) if (pset.gset_prefix)
@ -1456,7 +1469,7 @@ ExecQueryAndProcessResults(const char *query,
const printQueryOpt *opt, FILE *printQueryFout) const printQueryOpt *opt, FILE *printQueryFout)
{ {
bool timing = pset.timing; bool timing = pset.timing;
bool success; bool success = false;
bool return_early = false; bool return_early = false;
instr_time before, instr_time before,
after; after;
@ -1469,10 +1482,32 @@ ExecQueryAndProcessResults(const char *query,
else else
INSTR_TIME_SET_ZERO(before); INSTR_TIME_SET_ZERO(before);
if (pset.bind_flag) switch (pset.send_mode)
success = PQsendQueryParams(pset.db, query, pset.bind_nparams, NULL, (const char *const *) pset.bind_params, NULL, NULL, 0); {
else case PSQL_SEND_EXTENDED_CLOSE:
success = PQsendQuery(pset.db, query); success = PQsendClosePrepared(pset.db, pset.stmtName);
break;
case PSQL_SEND_EXTENDED_PARSE:
success = PQsendPrepare(pset.db, pset.stmtName, query, 0, NULL);
break;
case PSQL_SEND_EXTENDED_QUERY_PARAMS:
Assert(pset.stmtName == NULL);
success = PQsendQueryParams(pset.db, query,
pset.bind_nparams, NULL,
(const char *const *) pset.bind_params,
NULL, NULL, 0);
break;
case PSQL_SEND_EXTENDED_QUERY_PREPARED:
Assert(pset.stmtName != NULL);
success = PQsendQueryPrepared(pset.db, pset.stmtName,
pset.bind_nparams,
(const char *const *) pset.bind_params,
NULL, NULL, 0);
break;
case PSQL_SEND_QUERY:
success = PQsendQuery(pset.db, query);
break;
}
if (!success) if (!success)
{ {

View File

@ -165,6 +165,9 @@ slashUsage(unsigned short int pager)
HELP0("General\n"); HELP0("General\n");
HELP0(" \\bind [PARAM]... set query parameters\n"); HELP0(" \\bind [PARAM]... set query parameters\n");
HELP0(" \\bind_named STMT_NAME [PARAM]...\n"
" set query parameters for an existing prepared statement\n");
HELP0(" \\close STMT_NAME close an existing prepared statement\n");
HELP0(" \\copyright show PostgreSQL usage and distribution terms\n"); HELP0(" \\copyright show PostgreSQL usage and distribution terms\n");
HELP0(" \\crosstabview [COLUMNS] execute query and display result in crosstab\n"); HELP0(" \\crosstabview [COLUMNS] execute query and display result in crosstab\n");
HELP0(" \\errverbose show most recent error message at maximum verbosity\n"); HELP0(" \\errverbose show most recent error message at maximum verbosity\n");
@ -312,6 +315,7 @@ slashUsage(unsigned short int pager)
" connect to new database (currently no connection)\n"); " connect to new database (currently no connection)\n");
HELP0(" \\conninfo display information about current connection\n"); HELP0(" \\conninfo display information about current connection\n");
HELP0(" \\encoding [ENCODING] show or set client encoding\n"); HELP0(" \\encoding [ENCODING] show or set client encoding\n");
HELP0(" \\parse STMT_NAME create a prepared statement\n");
HELP0(" \\password [USERNAME] securely change the password for a user\n"); HELP0(" \\password [USERNAME] securely change the password for a user\n");
HELP0("\n"); HELP0("\n");

View File

@ -62,6 +62,15 @@ typedef enum
PSQL_COMP_CASE_LOWER, PSQL_COMP_CASE_LOWER,
} PSQL_COMP_CASE; } PSQL_COMP_CASE;
typedef enum
{
PSQL_SEND_QUERY,
PSQL_SEND_EXTENDED_CLOSE,
PSQL_SEND_EXTENDED_PARSE,
PSQL_SEND_EXTENDED_QUERY_PARAMS,
PSQL_SEND_EXTENDED_QUERY_PREPARED,
} PSQL_SEND_MODE;
typedef enum typedef enum
{ {
hctl_none = 0, hctl_none = 0,
@ -96,10 +105,12 @@ typedef struct _psqlSettings
char *gset_prefix; /* one-shot prefix argument for \gset */ char *gset_prefix; /* one-shot prefix argument for \gset */
bool gdesc_flag; /* one-shot request to describe query result */ bool gdesc_flag; /* one-shot request to describe query result */
bool gexec_flag; /* one-shot request to execute query result */ bool gexec_flag; /* one-shot request to execute query result */
bool bind_flag; /* one-shot request to use extended query PSQL_SEND_MODE send_mode; /* one-shot request to send query with normal
* protocol */ * or extended query protocol */
int bind_nparams; /* number of parameters */ int bind_nparams; /* number of parameters */
char **bind_params; /* parameters for extended query protocol call */ char **bind_params; /* parameters for extended query protocol call */
char *stmtName; /* prepared statement name used for extended
* query protocol commands */
bool crosstab_flag; /* one-shot request to crosstab result */ bool crosstab_flag; /* one-shot request to crosstab result */
char *ctv_args[4]; /* \crosstabview arguments */ char *ctv_args[4]; /* \crosstabview arguments */

View File

@ -1713,8 +1713,8 @@ psql_completion(const char *text, int start, int end)
/* psql's backslash commands. */ /* psql's backslash commands. */
static const char *const backslash_commands[] = { static const char *const backslash_commands[] = {
"\\a", "\\a",
"\\bind", "\\bind", "\\bind_named",
"\\connect", "\\conninfo", "\\C", "\\cd", "\\copy", "\\connect", "\\conninfo", "\\C", "\\cd", "\\close", "\\copy",
"\\copyright", "\\crosstabview", "\\copyright", "\\crosstabview",
"\\d", "\\da", "\\dA", "\\dAc", "\\dAf", "\\dAo", "\\dAp", "\\d", "\\da", "\\dA", "\\dAc", "\\dAf", "\\dAo", "\\dAp",
"\\db", "\\dc", "\\dconfig", "\\dC", "\\dd", "\\ddp", "\\dD", "\\db", "\\dc", "\\dconfig", "\\dC", "\\dd", "\\ddp", "\\dD",
@ -1731,7 +1731,7 @@ psql_completion(const char *text, int start, int end)
"\\if", "\\include", "\\include_relative", "\\ir", "\\if", "\\include", "\\include_relative", "\\ir",
"\\list", "\\lo_import", "\\lo_export", "\\lo_list", "\\lo_unlink", "\\list", "\\lo_import", "\\lo_export", "\\lo_list", "\\lo_unlink",
"\\out", "\\out",
"\\password", "\\print", "\\prompt", "\\pset", "\\parse", "\\password", "\\print", "\\prompt", "\\pset",
"\\qecho", "\\quit", "\\qecho", "\\quit",
"\\reset", "\\reset",
"\\s", "\\set", "\\setenv", "\\sf", "\\sv", "\\s", "\\set", "\\setenv", "\\sf", "\\sv",

View File

@ -98,6 +98,53 @@ two | 2
1 | 2 1 | 2
(1 row) (1 row)
-- \parse (extended query protocol)
\parse
\parse: missing required argument
SELECT 1 \parse ''
SELECT 2 \parse stmt1
SELECT $1 \parse stmt2
SELECT $1, $2 \parse stmt3
-- \bind_named (extended query protocol)
\bind_named
\bind_named: missing required argument
\bind_named '' \g
?column?
----------
1
(1 row)
\bind_named stmt1 \g
?column?
----------
2
(1 row)
\bind_named stmt2 'foo' \g
?column?
----------
foo
(1 row)
\bind_named stmt3 'foo' 'bar' \g
?column? | ?column?
----------+----------
foo | bar
(1 row)
-- \close (extended query protocol)
\close
\close: missing required argument
\close ''
\close stmt2
\close stmt2
SELECT name, statement FROM pg_prepared_statements ORDER BY name;
name | statement
-------+----------------
stmt1 | SELECT 2
stmt3 | SELECT $1, $2
(2 rows)
-- \bind (extended query protocol) -- \bind (extended query protocol)
SELECT 1 \bind \g SELECT 1 \bind \g
?column? ?column?
@ -129,6 +176,11 @@ ERROR: cannot insert multiple commands into a prepared statement
-- bind error -- bind error
SELECT $1, $2 \bind 'foo' \g SELECT $1, $2 \bind 'foo' \g
ERROR: bind message supplies 1 parameters, but prepared statement "" requires 2 ERROR: bind message supplies 1 parameters, but prepared statement "" requires 2
-- bind_named error
\bind_named stmt2 'baz' \g
ERROR: prepared statement "stmt2" does not exist
\bind_named stmt3 'baz' \g
ERROR: bind message supplies 1 parameters, but prepared statement "stmt3" requires 2
-- \gset -- \gset
select 10 as test01, 20 as test02, 'Hello' as test03 \gset pref01_ select 10 as test01, 20 as test02, 'Hello' as test03 \gset pref01_
\echo :pref01_test01 :pref01_test02 :pref01_test03 \echo :pref01_test01 :pref01_test02 :pref01_test03
@ -4507,9 +4559,11 @@ bar 'bar' "bar"
\pset fieldsep | `nosuchcommand` :foo :'foo' :"foo" \pset fieldsep | `nosuchcommand` :foo :'foo' :"foo"
\a \a
SELECT $1 \bind 1 \g SELECT $1 \bind 1 \g
\bind_named stmt1 1 2 \g
\C arg1 \C arg1
\c arg1 arg2 arg3 arg4 \c arg1 arg2 arg3 arg4
\cd arg1 \cd arg1
\close stmt1
\conninfo \conninfo
\copy arg1 arg2 arg3 arg4 arg5 arg6 \copy arg1 arg2 arg3 arg4 arg5 arg6
\copyright \copyright
@ -4538,6 +4592,7 @@ invalid command \lo
\lo_list \lo_list
\o arg1 \o arg1
\p \p
SELECT 1 \parse
\password arg1 \password arg1
\prompt arg1 arg2 \prompt arg1 arg2
\pset arg1 arg2 \pset arg1 arg2

View File

@ -45,8 +45,28 @@ SELECT 1 as one, 2 as two \g (format=csv csv_fieldsep='\t')
SELECT 1 as one, 2 as two \gx (title='foo bar') SELECT 1 as one, 2 as two \gx (title='foo bar')
\g \g
-- \bind (extended query protocol) -- \parse (extended query protocol)
\parse
SELECT 1 \parse ''
SELECT 2 \parse stmt1
SELECT $1 \parse stmt2
SELECT $1, $2 \parse stmt3
-- \bind_named (extended query protocol)
\bind_named
\bind_named '' \g
\bind_named stmt1 \g
\bind_named stmt2 'foo' \g
\bind_named stmt3 'foo' 'bar' \g
-- \close (extended query protocol)
\close
\close ''
\close stmt2
\close stmt2
SELECT name, statement FROM pg_prepared_statements ORDER BY name;
-- \bind (extended query protocol)
SELECT 1 \bind \g SELECT 1 \bind \g
SELECT $1 \bind 'foo' \g SELECT $1 \bind 'foo' \g
SELECT $1, $2 \bind 'foo' 'bar' \g SELECT $1, $2 \bind 'foo' 'bar' \g
@ -58,6 +78,9 @@ SELECT foo \bind \g
SELECT 1 \; SELECT 2 \bind \g SELECT 1 \; SELECT 2 \bind \g
-- bind error -- bind error
SELECT $1, $2 \bind 'foo' \g SELECT $1, $2 \bind 'foo' \g
-- bind_named error
\bind_named stmt2 'baz' \g
\bind_named stmt3 'baz' \g
-- \gset -- \gset
@ -990,9 +1013,11 @@ select \if false \\ (bogus \else \\ 42 \endif \\ forty_two;
\pset fieldsep | `nosuchcommand` :foo :'foo' :"foo" \pset fieldsep | `nosuchcommand` :foo :'foo' :"foo"
\a \a
SELECT $1 \bind 1 \g SELECT $1 \bind 1 \g
\bind_named stmt1 1 2 \g
\C arg1 \C arg1
\c arg1 arg2 arg3 arg4 \c arg1 arg2 arg3 arg4
\cd arg1 \cd arg1
\close stmt1
\conninfo \conninfo
\copy arg1 arg2 arg3 arg4 arg5 arg6 \copy arg1 arg2 arg3 arg4 arg5 arg6
\copyright \copyright
@ -1020,6 +1045,7 @@ select \if false \\ (bogus \else \\ 42 \endif \\ forty_two;
\lo_list \lo_list
\o arg1 \o arg1
\p \p
SELECT 1 \parse
\password arg1 \password arg1
\prompt arg1 arg2 \prompt arg1 arg2
\pset arg1 arg2 \pset arg1 arg2

View File

@ -1958,6 +1958,7 @@ PSQL_COMP_CASE
PSQL_ECHO PSQL_ECHO
PSQL_ECHO_HIDDEN PSQL_ECHO_HIDDEN
PSQL_ERROR_ROLLBACK PSQL_ERROR_ROLLBACK
PSQL_SEND_MODE
PTEntryArray PTEntryArray
PTIterationArray PTIterationArray
PTOKEN_PRIVILEGES PTOKEN_PRIVILEGES