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:
parent
a36aa223ec
commit
d55322b0da
@ -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>
|
||||||
|
@ -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
|
||||||
*/
|
*/
|
||||||
|
@ -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)
|
||||||
{
|
{
|
||||||
|
case PSQL_SEND_EXTENDED_CLOSE: /* \close */
|
||||||
|
free(pset.stmtName);
|
||||||
|
break;
|
||||||
|
case PSQL_SEND_EXTENDED_PARSE: /* \parse */
|
||||||
|
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++)
|
for (i = 0; i < pset.bind_nparams; i++)
|
||||||
free(pset.bind_params[i]);
|
free(pset.bind_params[i]);
|
||||||
free(pset.bind_params);
|
free(pset.bind_params);
|
||||||
|
free(pset.stmtName);
|
||||||
pset.bind_params = NULL;
|
pset.bind_params = NULL;
|
||||||
pset.bind_flag = false;
|
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 = 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);
|
success = PQsendQuery(pset.db, query);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (!success)
|
if (!success)
|
||||||
{
|
{
|
||||||
|
@ -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");
|
||||||
|
|
||||||
|
@ -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 */
|
||||||
|
|
||||||
|
@ -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",
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user