1
0
mirror of https://github.com/postgres/postgres.git synced 2026-10-23 13:29:09 +03:00

psql: count every COPY FROM STDIN when scanning a query string.

When SendQuery() is not told how many COPY FROM STDIN commands the
query string contains (as for -c, \gexec, and \watch), it scans the
string to count them itself.  But it called psql_scan() only once,
which stops at the first semicolon, so any COPY FROM STDIN past the
first sub-command was not counted, causing failure of cases that used
to work.  Oversight in commit 3045a25ba.

Author: Zsolt Parragi <zsolt.parragi@percona.com>
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/CAN4CZFPqa6c+u4uX5jJ8LANHTQ4dxM3m4_8G9WmX_A4-2wuv2A@mail.gmail.com
Backpatch-through: 14
This commit is contained in:
Tom Lane
2026-08-14 12:14:24 -04:00
parent 0468246f5d
commit bcb776671d
2 changed files with 53 additions and 2 deletions

View File

@@ -1794,15 +1794,36 @@ ExecQueryAndProcessResults(const char *query,
PsqlScanState scan_state;
PQExpBuffer query_buf;
promptStatus_t prompt_tmp;
PsqlScanResult scan_result;
scan_state = psql_scan_create(&psqlscan_callbacks);
psql_scan_setup(scan_state, query, strlen(query),
pset.encoding, standard_strings());
query_buf = createPQExpBuffer();
(void) psql_scan(scan_state, query_buf, &prompt_tmp);
/*
* A semicolon ends only one sub-command; keep scanning so that COPY
* FROM STDIN commands past the first semicolon are counted too. The
* count accumulates in scan_state across the psql_scan() calls.
*/
do
{
scan_result = psql_scan(scan_state, query_buf, &prompt_tmp);
} while (scan_result == PSCAN_SEMICOLON);
num_copy_from_stdin = psql_scan_count_copy_from_stdin(scan_state);
/*
* We expect the result now to be PSCAN_EOL. If it is PSCAN_BACKSLASH
* or PSCAN_INCOMPLETE, the server will get a parse error and refuse
* to execute any part of the command string, so don't expect any
* PGRES_COPY_IN results. (This will mean that we don't attempt to
* discard any following data, but this seems consistent with the
* general contract of psql_scan_count_copy_from_stdin, which is that
* it only promises to count syntactically-valid COPY commands.)
*/
if (scan_result == PSCAN_EOL)
num_copy_from_stdin = psql_scan_count_copy_from_stdin(scan_state);
else
num_copy_from_stdin = 0;
destroyPQExpBuffer(query_buf);
psql_scan_destroy(scan_state);

View File

@@ -532,6 +532,36 @@ psql_fails_like(
qr/COPY in a pipeline is not supported, aborting connection/,
'\copy to in pipeline: fails');
# Test execution of COPY FROM STDIN in -c. This case is a bit weird
# because it will read from psql's stdin not from the command source.
# To make it even weirder, try two such commands, to stress psql's logic
# that counts them. Also test both \. and EOF termination.
{
$node->safe_psql('postgres', 'CREATE TABLE copy_stdin_count (a int)');
my ($stdin, $stdout, $stderr) = ("50\n\\.\n60\n", '', '');
my $ret = IPC::Run::run(
[
'psql', '--no-psqlrc',
'--set' => 'ON_ERROR_STOP=1',
'--dbname' => $node->connstr('postgres'),
'--command' =>
'COPY copy_stdin_count FROM STDIN; COPY copy_stdin_count FROM STDIN',
],
'<' => \$stdin,
'>' => \$stdout,
'2>' => \$stderr);
ok($ret, '-c COPY FROM STDIN: psql exits 0');
unlike(
$stderr,
qr/unexpected COPY_IN result/,
'-c COPY FROM STDIN: unexpected COPY_IN result');
my $data = $node->safe_psql('postgres', 'SELECT * FROM copy_stdin_count');
is($data, "50\n60", '-c COPY FROM STDIN: correct data loaded');
}
# Test \restrict and \unrestrict.
psql_fails_like(
$node,
qq{\\restrict test