COPY TO regression with psql -c

Started by Zsolt Parragi12 days ago7 messagesbugs
Beta feature

Hackorum builds and tests every patch posted to the lists, not only commitfest submissions. This is Hackorum's own CI rather than the PostgreSQL project's, and it is still under testing - please report anything that looks wrong.

won't retrysuccessCI history

This thread has been committed, so CI has stopped here. Anything below is the last result it produced.

You can run a PostgreSQL built from this patch straight from Docker, with no checkout and no build:

docker run --rm -p 5432:5432 ghcr.io/hackorum-dev/postgres-patch:t253381
psql -h localhost -U postgres

Built from patchset v5 (message #5), August 13, 2026 at 08:57 PM.

Every patchset is also pushed to a branch of our PostgreSQL fork, so you can check out the same tree CI built. Without a PostgreSQL checkout:

git clone --branch t253381_5 https://github.com/hackorum-dev/postgres.git

In a checkout you already have, add the fork once:

git remote add hackorum https://github.com/hackorum-dev/postgres.git

then, for this patchset and every later one:

git fetch hackorum t253381_5 && git checkout t253381_5

Patchset v5 (message #5) is on t253381_5

Jump to latest
#1Zsolt Parragi
zsolt.parragi@percona.com

Hello

The recent COPY ... FROM STDIN improvement caused a regression in COPY
TO ... FROM STDIN when used together with psql -c: it only considers
the first statement, so the copy fails if multiple commands are
specified. A very simple example is:

psql -c "SELECT 1; COPY t FROM STDIN;"

now fails with unexpected COPY_IN.

This is probably an uncommon use case, but it has a few legitimate
uses in scripts, such as:

gzip -dc data.csv.gz | psql -c 'TRUNCATE t; COPY t FROM STDIN WITH (FORMAT CSV)'

to (re)load a table's data.

I attached a proposed patch with a tap test case that showcases the issue.

Attachments:

t253381_1
0001-psql-count-every-COPY-FROM-STDIN-when-scanning-a-que.patchapplication/octet-stream; name=0001-psql-count-every-COPY-FROM-STDIN-when-scanning-a-que.patchDownload+90-2
#2Christoph Berg
myon@debian.org
In reply to: Zsolt Parragi (#1)
Re: COPY TO regression with psql -c

Re: Zsolt Parragi

psql -c "SELECT 1; COPY t FROM STDIN;"

The Debian package tests are also tripping over this. The test case
there is (in the encoding test file, hence the weird chars):

printf '���' | psql -qc "set client_encoding='iso-8859-5'; create table t (x varchar); copy t from stdin"
SET
CREATE TABLE
unexpected COPY_IN result, aborting connection

2026-08-12 10:31:46.434 CEST [77760] ERROR: unexpected EOF on client connection with an open transaction
2026-08-12 10:31:46.434 CEST [77760] CONTEXT: COPY t, line 1
2026-08-12 10:31:46.434 CEST [77760] STATEMENT: set client_encoding='iso-8859-5'; create table t (x varchar); copy t from stdin
2026-08-12 10:31:46.434 CEST [77760] LOG: could not send data to client: Broken pipe
2026-08-12 10:31:46.434 CEST [77760] STATEMENT: set client_encoding='iso-8859-5'; create table t (x varchar); copy t from stdin
2026-08-12 10:31:46.434 CEST [77760] FATAL: terminating connection because protocol synchronization was lost

This is probably an uncommon use case, but it has a few legitimate
uses in scripts, such as:

gzip -dc data.csv.gz | psql -c 'TRUNCATE t; COPY t FROM STDIN WITH (FORMAT CSV)'

I think that's a pretty common case. This "create and copy" is another example.

Christoph

#3Tom Lane
tgl@sss.pgh.pa.us
In reply to: Zsolt Parragi (#1)
Re: COPY TO regression with psql -c

Zsolt Parragi <zsolt.parragi@percona.com> writes:

The recent COPY ... FROM STDIN improvement caused a regression in COPY
TO ... FROM STDIN when used together with psql -c: it only considers
the first statement, so the copy fails if multiple commands are
specified. A very simple example is:

Yeah, this is clearly an oversight.

I attached a proposed patch with a tap test case that showcases the issue.

I took a brief look at this. The question the code immediately raises
is "what to do if we get PSCAN_BACKSLASH?". For example, someone
might try
psql postgres -c 'select 1; \echo hello\\ select 2;'
which is syntax that'd work just fine at a command prompt. As things
stand today, we'll ship the whole string to the server, which will
throw a syntax error and do nothing. (You could imagine improving the
-c option parser to split the string into pieces and make this work
like it does at a command prompt, but that's surely not something
we'd back-patch.) Where the rubber meets the road for the current
problem is
psql postgres -c 'select 1; \echo hello\\ copy tab from stdin;'
Should we act as though we expect PGRES_COPY_IN from this? How about
psql postgres -c 'copy tab from stdin; \echo hello'
?

Thinking about it, I think it's probably a non-problem in practice:
all of these forms will result in server errors with no PGRES_COPY_IN
issued, and since these don't attempt to consume data from the rest
of the -c string, there's not really a hazard of failing to skip over
data. But I think the issue deserves explanation in a comment.

Also, I'd drop the resetPQExpBuffer(query_buf); line. That's a false
analogy: since we're not sending the string-so-far to the server,
this situation is more like "\;" than like ";", and we'd not clear
query_buf for that. It probably makes no difference right now, but
perhaps future lexer behavior would notice the difference.

On the test case: I don't love adding a new TAP script for this.
That implies spinning up a new server, making this very expensive
for the amount of actual testing it's doing. Is there a reason not
to fold this into psql/t/001_basic.pl ?

regards, tom lane

#4Zsolt Parragi
zsolt.parragi@percona.com
In reply to: Tom Lane (#3)
Re: COPY TO regression with psql -c

I took a brief look at this. The question the code immediately raises
is "what to do if we get PSCAN_BACKSLASH?" ...

Yeah, I forgot to mention this in the email / commit but I checked
this and decided that it's a non-issue because we don't support it. I
added a comment about it, if support for that gets implemented in the
future, we can also extend this logic.

I dropped the reset buffer call and moved the test to the btasic est -
there's no reason to keep it separate. I just keep forgetting that I
should extend existing tests instead of adding new. Attached v2.

Attachments:

t253381_4
v2-0001-psql-count-every-COPY-FROM-STDIN-when-scanning-a-.patchapplication/octet-stream; name=v2-0001-psql-count-every-COPY-FROM-STDIN-when-scanning-a-.patchDownload+66-2
#5Tom Lane
tgl@sss.pgh.pa.us
In reply to: Zsolt Parragi (#4)
Re: COPY TO regression with psql -c

After thinking some more about how to handle cases where we stop with
PSCAN_BACKSLASH or PSCAN_INCOMPLETE, I feel that the safest answer is
to set num_copy_from_stdin = 0 in those cases. This is consistent
with the fact that we know we won't get a PGRES_COPY_IN message,
even if there was a valid COPY FROM STDIN in the string before the
syntax error. This might prevent us from skipping following data
in cases where it'd be best to do that, but here are two arguments
against trying to do so:

* The ambition of the security patch extended only to handling
syntactically-valid cases, which these aren't. Trying to do more
leads into a guessing game, eg should we skip data after "COPY
mytable FRPM STDIN"?

* Not trying to skip data ensures that the behavior of such cases
is the same as it was before the security patch, which seems like
the right direction to err in.

So v3 attached does it like that. I also simplified the test
script. The two-COPY-commands case seems like it covers everything
we want to test; the other cases just add cycles and complicate
the script.

regards, tom lane

Attachments:

t253381_5
v3-0001-psql-count-every-COPY-FROM-STDIN-when-scanning-a-.patchtext/x-diff; charset=us-ascii; name*0=v3-0001-psql-count-every-COPY-FROM-STDIN-when-scanning-a-.p; name*1=atchDownload+53-3
#6Zsolt Parragi
zsolt.parragi@percona.com
In reply to: Tom Lane (#5)
Re: COPY TO regression with psql -c

The changes look good to me.

The two-COPY-commands case seems like it covers everything
we want to test

It does, it's just not as realistic use case as the other two, but that's fine for a test.

#7Tom Lane
tgl@sss.pgh.pa.us
In reply to: Zsolt Parragi (#6)
Re: COPY TO regression with psql -c

Zsolt Parragi <zsolt.parragi@percona.com> writes:

The changes look good to me.

Thanks, pushed.

regards, tom lane