1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-30 21:42: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

@ -64,10 +64,14 @@ static backslashResult exec_command(const char *cmd,
PQExpBuffer previous_buf);
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_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_connect(PsqlScanState scan_state, bool active_branch);
static backslashResult exec_command_cd(PsqlScanState scan_state, bool active_branch,
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_copy(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_print(PsqlScanState scan_state, bool active_branch,
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_prompt(PsqlScanState scan_state, bool active_branch,
const char *cmd);
@ -312,12 +318,16 @@ exec_command(const char *cmd,
status = exec_command_a(scan_state, active_branch);
else if (strcmp(cmd, "bind") == 0)
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)
status = exec_command_C(scan_state, active_branch);
else if (strcmp(cmd, "c") == 0 || strcmp(cmd, "connect") == 0)
status = exec_command_connect(scan_state, active_branch);
else if (strcmp(cmd, "cd") == 0)
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)
status = exec_command_conninfo(scan_state, active_branch);
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)
status = exec_command_print(scan_state, active_branch,
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)
status = exec_command_password(scan_state, active_branch);
else if (strcmp(cmd, "prompt") == 0)
@ -472,6 +484,7 @@ exec_command_bind(PsqlScanState scan_state, bool active_branch)
int nalloc = 0;
pset.bind_params = NULL;
pset.stmtName = NULL;
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_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
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;
}
/*
* \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
*/
@ -2096,6 +2191,39 @@ exec_command_print(PsqlScanState scan_state, bool active_branch,
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
*/