Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit c01641f

Browse files
committed
libpq failed to cope with COPY FROM STDIN if the command was issued
via extended query protocol, because it sends Sync right after Execute without realizing that the command to be executed is COPY. There seems to be no reasonable way for it to realize that, either, so the best fix seems to be to make the backend ignore Sync during copy-in mode. Bit of a wart on the protocol, but little alternative. Also, libpq must send another Sync after terminating the COPY, if the command was issued via Execute.
1 parent 0be731a commit c01641f

File tree

5 files changed

+66
-20
lines changed

5 files changed

+66
-20
lines changed

doc/src/sgml/protocol.sgml

+11-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<!-- $Header: /cvsroot/pgsql/doc/src/sgml/protocol.sgml,v 1.39 2003/06/27 19:08:37 tgl Exp $ -->
1+
<!-- $Header: /cvsroot/pgsql/doc/src/sgml/protocol.sgml,v 1.40 2003/08/13 18:56:21 tgl Exp $ -->
22

33
<chapter id="protocol">
44
<title>Frontend/Backend Protocol</title>
@@ -919,8 +919,7 @@
919919

920920
<para>
921921
In the event of a backend-detected error during copy-in mode (including
922-
receipt of a CopyFail message, or indeed any frontend message other than
923-
CopyData or CopyDone), the backend will issue an ErrorResponse
922+
receipt of a CopyFail message), the backend will issue an ErrorResponse
924923
message. If the <command>COPY</> command was issued via an extended-query
925924
message, the backend will now discard frontend messages until a Sync
926925
message is received, then it will issue ReadyForQuery and return to normal
@@ -930,6 +929,15 @@
930929
messages issued by the frontend will simply be dropped.
931930
</para>
932931

932+
<para>
933+
The backend will ignore Flush and Sync messages received during copy-in
934+
mode. Receipt of any other non-copy message type constitutes an error
935+
that will abort the copy-in state as described above. (The exception for
936+
Flush and Sync is for the convenience of client libraries that always
937+
send Flush or Sync after an Execute message, without checking whether
938+
the command to be executed is a <command>COPY FROM STDIN</>.)
939+
</para>
940+
933941
<para>
934942
Copy-out mode (data transfer from the server) is initiated when the
935943
backend executes a <command>COPY TO STDOUT</> SQL statement. The backend

src/backend/commands/copy.c

+11-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.208 2003/08/08 21:41:30 momjian Exp $
11+
* $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.209 2003/08/13 18:56:21 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -386,6 +386,7 @@ CopyGetData(void *databuf, int datasize)
386386
/* Try to receive another message */
387387
int mtype;
388388

389+
readmessage:
389390
mtype = pq_getbyte();
390391
if (mtype == EOF)
391392
ereport(ERROR,
@@ -409,6 +410,15 @@ CopyGetData(void *databuf, int datasize)
409410
errmsg("COPY from stdin failed: %s",
410411
pq_getmsgstring(copy_msgbuf))));
411412
break;
413+
case 'H': /* Flush */
414+
case 'S': /* Sync */
415+
/*
416+
* Ignore Flush/Sync for the convenience of
417+
* client libraries (such as libpq) that may
418+
* send those without noticing that the command
419+
* they just sent was COPY.
420+
*/
421+
goto readmessage;
412422
default:
413423
ereport(ERROR,
414424
(errcode(ERRCODE_PROTOCOL_VIOLATION),

src/interfaces/libpq/fe-exec.c

+28-12
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.144 2003/08/13 16:29:03 tgl Exp $
11+
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.145 2003/08/13 18:56:21 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -647,6 +647,9 @@ PQsendQuery(PGconn *conn, const char *query)
647647
return 0;
648648
}
649649

650+
/* remember we are using simple query protocol */
651+
conn->ext_query = false;
652+
650653
/*
651654
* Give the data a push. In nonblock mode, don't complain if we're
652655
* unable to send it all; PQgetResult() will do any additional
@@ -901,6 +904,9 @@ PQsendQueryGuts(PGconn *conn,
901904
pqPutMsgEnd(conn) < 0)
902905
goto sendFailed;
903906

907+
/* remember we are using extended query protocol */
908+
conn->ext_query = true;
909+
904910
/*
905911
* Give the data a push. In nonblock mode, don't complain if we're
906912
* unable to send it all; PQgetResult() will do any additional
@@ -1187,29 +1193,28 @@ PQexecStart(PGconn *conn)
11871193
*/
11881194
while ((result = PQgetResult(conn)) != NULL)
11891195
{
1190-
if (result->resultStatus == PGRES_COPY_IN)
1196+
ExecStatusType resultStatus = result->resultStatus;
1197+
1198+
PQclear(result); /* only need its status */
1199+
if (resultStatus == PGRES_COPY_IN)
11911200
{
11921201
if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
11931202
{
11941203
/* In protocol 3, we can get out of a COPY IN state */
11951204
if (PQputCopyEnd(conn,
11961205
libpq_gettext("COPY terminated by new PQexec")) < 0)
1197-
{
1198-
PQclear(result);
11991206
return false;
1200-
}
12011207
/* keep waiting to swallow the copy's failure message */
12021208
}
12031209
else
12041210
{
12051211
/* In older protocols we have to punt */
1206-
PQclear(result);
12071212
printfPQExpBuffer(&conn->errorMessage,
12081213
libpq_gettext("COPY IN state must be terminated first\n"));
12091214
return false;
12101215
}
12111216
}
1212-
else if (result->resultStatus == PGRES_COPY_OUT)
1217+
else if (resultStatus == PGRES_COPY_OUT)
12131218
{
12141219
if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
12151220
{
@@ -1224,13 +1229,11 @@ PQexecStart(PGconn *conn)
12241229
else
12251230
{
12261231
/* In older protocols we have to punt */
1227-
PQclear(result);
12281232
printfPQExpBuffer(&conn->errorMessage,
12291233
libpq_gettext("COPY OUT state must be terminated first\n"));
12301234
return false;
12311235
}
12321236
}
1233-
PQclear(result);
12341237
}
12351238

12361239
/* OK to send a command */
@@ -1409,6 +1412,16 @@ PQputCopyEnd(PGconn *conn, const char *errormsg)
14091412
pqPutMsgEnd(conn) < 0)
14101413
return -1;
14111414
}
1415+
/*
1416+
* If we sent the COPY command in extended-query mode, we must
1417+
* issue a Sync as well.
1418+
*/
1419+
if (conn->ext_query)
1420+
{
1421+
if (pqPutMsgStart('S', false, conn) < 0 ||
1422+
pqPutMsgEnd(conn) < 0)
1423+
return -1;
1424+
}
14121425
}
14131426
else
14141427
{
@@ -2055,12 +2068,15 @@ PQgetisnull(const PGresult *res, int tup_num, int field_num)
20552068
int
20562069
PQsetnonblocking(PGconn *conn, int arg)
20572070
{
2071+
bool barg;
2072+
20582073
if (!conn || conn->status == CONNECTION_BAD)
20592074
return -1;
20602075

2061-
arg = (arg == TRUE) ? 1 : 0;
2076+
barg = (arg ? TRUE : FALSE);
2077+
20622078
/* early out if the socket is already in the state requested */
2063-
if (arg == conn->nonblocking)
2079+
if (barg == conn->nonblocking)
20642080
return (0);
20652081

20662082
/*
@@ -2074,7 +2090,7 @@ PQsetnonblocking(PGconn *conn, int arg)
20742090
if (pqFlush(conn))
20752091
return (-1);
20762092

2077-
conn->nonblocking = arg;
2093+
conn->nonblocking = barg;
20782094

20792095
return (0);
20802096
}

src/interfaces/libpq/fe-protocol3.c

+11-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-protocol3.c,v 1.7 2003/08/12 21:34:44 tgl Exp $
11+
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-protocol3.c,v 1.8 2003/08/13 18:56:21 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -1086,6 +1086,16 @@ pqEndcopy3(PGconn *conn)
10861086
if (pqPutMsgStart('c', false, conn) < 0 ||
10871087
pqPutMsgEnd(conn) < 0)
10881088
return 1;
1089+
/*
1090+
* If we sent the COPY command in extended-query mode, we must
1091+
* issue a Sync as well.
1092+
*/
1093+
if (conn->ext_query)
1094+
{
1095+
if (pqPutMsgStart('S', false, conn) < 0 ||
1096+
pqPutMsgEnd(conn) < 0)
1097+
return 1;
1098+
}
10891099
}
10901100

10911101
/*

src/interfaces/libpq/libpq-int.h

+5-3
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
1313
* Portions Copyright (c) 1994, Regents of the University of California
1414
*
15-
* $Id: libpq-int.h,v 1.80 2003/08/04 02:40:20 momjian Exp $
15+
* $Id: libpq-int.h,v 1.81 2003/08/13 18:56:21 tgl Exp $
1616
*
1717
*-------------------------------------------------------------------------
1818
*/
@@ -262,8 +262,10 @@ struct pg_conn
262262
PGAsyncStatusType asyncStatus;
263263
PGTransactionStatusType xactStatus;
264264
/* note: xactStatus never changes to ACTIVE */
265-
int nonblocking; /* whether this connection is using a
266-
* blocking socket to the backend or not */
265+
bool nonblocking; /* whether this connection is using
266+
* nonblock sending semantics */
267+
bool ext_query; /* was our last query sent with extended
268+
* query protocol? */
267269
char copy_is_binary; /* 1 = copy binary, 0 = copy text */
268270
int copy_already_done; /* # bytes already returned in
269271
* COPY OUT */

0 commit comments

Comments
 (0)