Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit0032a54

Browse files
committed
Remove PQsendQuery support in pipeline mode
The extended query protocol implementation I added in commitacb7e4e has bugs when used in pipeline mode. Rather than spendmore time trying to fix it, remove that code and make the function relyon simple query protocol only, meaning it can no longer be used inpipeline mode.Users can easily change their applications to use PQsendQueryParamsinstead. We leave PQsendQuery in place for Postgres 14, just in casesomebody is using it and has not hit the mentioned bugs; but we shouldrecommend that it not be used.Backpatch to 15.Per bug report from Gabriele Varrazzo.Discussion:https://postgr.es/m/CA+mi_8ZGSQNmW6-mk_iSR4JZB_LJ4ww3suOF+1vGNs3MrLsv4g@mail.gmail.com
1 parentd11a41a commit0032a54

File tree

4 files changed

+41
-115
lines changed

4 files changed

+41
-115
lines changed

‎doc/src/sgml/libpq.sgml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4601,8 +4601,7 @@ int PQsendQuery(PGconn *conn, const char *command);
46014601
</para>
46024602

46034603
<para>
4604-
In pipeline mode, command strings containing more than one SQL command
4605-
are disallowed.
4604+
In pipeline mode, this function is disallowed.
46064605
</para>
46074606
</listitem>
46084607
</varlistentry>
@@ -5056,6 +5055,7 @@ int PQflush(PGconn *conn);
50565055
<xref linkend="libpq-PQpipelineStatus"/> can be used
50575056
to test whether pipeline mode is active.
50585057
In pipeline mode, only <link linkend="libpq-async">asynchronous operations</link>
5058+
that utilize the extended query protocol
50595059
are permitted, command strings containing multiple SQL commands are
50605060
disallowed, and so is <literal>COPY</literal>.
50615061
Using synchronous command execution functions
@@ -5067,6 +5067,8 @@ int PQflush(PGconn *conn);
50675067
<function>PQdescribePrepared</function>,
50685068
<function>PQdescribePortal</function>,
50695069
is an error condition.
5070+
<function>PQsendQuery</function> is
5071+
also disallowed, because it uses the simple query protocol.
50705072
Once all dispatched commands have had their results processed, and
50715073
the end pipeline result has been consumed, the application may return
50725074
to non-pipelined mode with <xref linkend="libpq-PQexitPipelineMode"/>.
@@ -5095,8 +5097,7 @@ int PQflush(PGconn *conn);
50955097

50965098
<para>
50975099
After entering pipeline mode, the application dispatches requests using
5098-
<xref linkend="libpq-PQsendQuery"/>,
5099-
<xref linkend="libpq-PQsendQueryParams"/>,
5100+
<xref linkend="libpq-PQsendQueryParams"/>
51005101
or its prepared-query sibling
51015102
<xref linkend="libpq-PQsendQueryPrepared"/>.
51025103
These requests are queued on the client-side until flushed to the server;

‎src/interfaces/libpq/fe-exec.c

Lines changed: 23 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -1433,7 +1433,6 @@ static int
14331433
PQsendQueryInternal(PGconn*conn,constchar*query,boolnewQuery)
14341434
{
14351435
PGcmdQueueEntry*entry=NULL;
1436-
PGcmdQueueEntry*entry2=NULL;
14371436

14381437
if (!PQsendQueryStart(conn,newQuery))
14391438
return0;
@@ -1446,103 +1445,48 @@ PQsendQueryInternal(PGconn *conn, const char *query, bool newQuery)
14461445
return0;
14471446
}
14481447

1449-
entry=pqAllocCmdQueueEntry(conn);
1450-
if (entry==NULL)
1451-
return0;/* error msg already set */
14521448
if (conn->pipelineStatus!=PQ_PIPELINE_OFF)
14531449
{
1454-
entry2=pqAllocCmdQueueEntry(conn);
1455-
if (entry2==NULL)
1456-
gotosendFailed;
1450+
appendPQExpBuffer(&conn->errorMessage,
1451+
libpq_gettext("%s not allowed in pipeline mode\n"),
1452+
"PQsendQuery");
1453+
return0;
14571454
}
14581455

1456+
entry=pqAllocCmdQueueEntry(conn);
1457+
if (entry==NULL)
1458+
return0;/* error msg already set */
1459+
14591460
/* Send the query message(s) */
1460-
if (conn->pipelineStatus==PQ_PIPELINE_OFF)
1461+
/* construct the outgoing Query message */
1462+
if (pqPutMsgStart('Q',conn)<0||
1463+
pqPuts(query,conn)<0||
1464+
pqPutMsgEnd(conn)<0)
14611465
{
1462-
/* construct the outgoing Query message */
1463-
if (pqPutMsgStart('Q',conn)<0||
1464-
pqPuts(query,conn)<0||
1465-
pqPutMsgEnd(conn)<0)
1466-
{
1467-
/* error message should be set up already */
1468-
pqRecycleCmdQueueEntry(conn,entry);
1469-
return0;
1470-
}
1471-
1472-
/* remember we are using simple query protocol */
1473-
entry->queryclass=PGQUERY_SIMPLE;
1474-
/* and remember the query text too, if possible */
1475-
entry->query=strdup(query);
1466+
/* error message should be set up already */
1467+
pqRecycleCmdQueueEntry(conn,entry);
1468+
return0;
14761469
}
1477-
else
1478-
{
1479-
/*
1480-
* In pipeline mode we cannot use the simple protocol, so we send
1481-
* Parse, Bind, Describe Portal, Execute, Close Portal (with the
1482-
* unnamed portal).
1483-
*/
1484-
if (pqPutMsgStart('P',conn)<0||
1485-
pqPuts("",conn)<0||
1486-
pqPuts(query,conn)<0||
1487-
pqPutInt(0,2,conn)<0||
1488-
pqPutMsgEnd(conn)<0)
1489-
gotosendFailed;
1490-
if (pqPutMsgStart('B',conn)<0||
1491-
pqPuts("",conn)<0||
1492-
pqPuts("",conn)<0||
1493-
pqPutInt(0,2,conn)<0||
1494-
pqPutInt(0,2,conn)<0||
1495-
pqPutInt(0,2,conn)<0||
1496-
pqPutMsgEnd(conn)<0)
1497-
gotosendFailed;
1498-
if (pqPutMsgStart('D',conn)<0||
1499-
pqPutc('P',conn)<0||
1500-
pqPuts("",conn)<0||
1501-
pqPutMsgEnd(conn)<0)
1502-
gotosendFailed;
1503-
if (pqPutMsgStart('E',conn)<0||
1504-
pqPuts("",conn)<0||
1505-
pqPutInt(0,4,conn)<0||
1506-
pqPutMsgEnd(conn)<0)
1507-
gotosendFailed;
1508-
if (pqPutMsgStart('C',conn)<0||
1509-
pqPutc('P',conn)<0||
1510-
pqPuts("",conn)<0||
1511-
pqPutMsgEnd(conn)<0)
1512-
gotosendFailed;
15131470

1514-
entry->queryclass=PGQUERY_EXTENDED;
1515-
entry->query=strdup(query);
1516-
}
1471+
/* remember we are using simple query protocol */
1472+
entry->queryclass=PGQUERY_SIMPLE;
1473+
/* and remember the query text too, if possible */
1474+
entry->query=strdup(query);
15171475

15181476
/*
15191477
* Give the data a push. In nonblock mode, don't complain if we're unable
15201478
* to send it all; PQgetResult() will do any additional flushing needed.
15211479
*/
1522-
if (pqPipelineFlush(conn)<0)
1480+
if (pqFlush(conn)<0)
15231481
gotosendFailed;
15241482

15251483
/* OK, it's launched! */
15261484
pqAppendCmdQueueEntry(conn,entry);
15271485

1528-
/*
1529-
* When pipeline mode is in use, we need a second entry in the command
1530-
* queue to represent Close Portal message. This allows us later to wait
1531-
* for the CloseComplete message to be received before getting in IDLE
1532-
* state.
1533-
*/
1534-
if (conn->pipelineStatus!=PQ_PIPELINE_OFF)
1535-
{
1536-
entry2->queryclass=PGQUERY_CLOSE;
1537-
entry2->query=NULL;
1538-
pqAppendCmdQueueEntry(conn,entry2);
1539-
}
1540-
15411486
return1;
15421487

15431488
sendFailed:
15441489
pqRecycleCmdQueueEntry(conn,entry);
1545-
pqRecycleCmdQueueEntry(conn,entry2);
15461490
/* error message should be set up already */
15471491
return0;
15481492
}
@@ -2246,22 +2190,6 @@ PQgetResult(PGconn *conn)
22462190
break;
22472191
}
22482192

2249-
/* If the next command we expect is CLOSE, read and consume it */
2250-
if (conn->asyncStatus==PGASYNC_PIPELINE_IDLE&&
2251-
conn->cmd_queue_head&&
2252-
conn->cmd_queue_head->queryclass==PGQUERY_CLOSE)
2253-
{
2254-
if (res&&res->resultStatus!=PGRES_FATAL_ERROR)
2255-
{
2256-
conn->asyncStatus=PGASYNC_BUSY;
2257-
parseInput(conn);
2258-
conn->asyncStatus=PGASYNC_PIPELINE_IDLE;
2259-
}
2260-
else
2261-
/* we won't ever see the Close */
2262-
pqCommandQueueAdvance(conn);
2263-
}
2264-
22652193
/* Time to fire PGEVT_RESULTCREATE events, if there are any */
22662194
if (res&&res->nEvents>0)
22672195
(void)PQfireResultCreateEvents(conn,res);
@@ -2977,8 +2905,9 @@ PQfn(PGconn *conn,
29772905

29782906
if (conn->pipelineStatus!=PQ_PIPELINE_OFF)
29792907
{
2980-
appendPQExpBufferStr(&conn->errorMessage,
2981-
libpq_gettext("PQfn not allowed in pipeline mode\n"));
2908+
appendPQExpBuffer(&conn->errorMessage,
2909+
libpq_gettext("%s not allowed in pipeline mode\n"),
2910+
"PQfn");
29822911
returnNULL;
29832912
}
29842913

‎src/interfaces/libpq/fe-protocol3.c

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -282,24 +282,8 @@ pqParseInput3(PGconn *conn)
282282
}
283283
break;
284284
case'2':/* Bind Complete */
285-
/* Nothing to do for this message type */
286-
break;
287285
case'3':/* Close Complete */
288-
/*
289-
* If we get CloseComplete when waiting for it, consume
290-
* the queue element and keep going. A result is not
291-
* expected from this message; it is just there so that
292-
* we know to wait for it when PQsendQuery is used in
293-
* pipeline mode, before going in IDLE state. Failing to
294-
* do this makes us receive CloseComplete when IDLE, which
295-
* creates problems.
296-
*/
297-
if (conn->cmd_queue_head&&
298-
conn->cmd_queue_head->queryclass==PGQUERY_CLOSE)
299-
{
300-
pqCommandQueueAdvance(conn);
301-
}
302-
286+
/* Nothing to do for these message types */
303287
break;
304288
case'S':/* parameter status */
305289
if (getParameterStatus(conn))

‎src/test/modules/libpq_pipeline/libpq_pipeline.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,18 @@ test_disallowed_in_pipeline(PGconn *conn)
106106
res=PQexec(conn,"SELECT 1");
107107
if (PQresultStatus(res)!=PGRES_FATAL_ERROR)
108108
pg_fatal("PQexec should fail in pipeline mode but succeeded");
109+
if (strcmp(PQerrorMessage(conn),
110+
"synchronous command execution functions are not allowed in pipeline mode\n")!=0)
111+
pg_fatal("did not get expected error message; got: \"%s\"",
112+
PQerrorMessage(conn));
113+
114+
/* PQsendQuery should fail in pipeline mode */
115+
if (PQsendQuery(conn,"SELECT 1")!=0)
116+
pg_fatal("PQsendQuery should fail in pipeline mode but succeeded");
117+
if (strcmp(PQerrorMessage(conn),
118+
"PQsendQuery not allowed in pipeline mode\n")!=0)
119+
pg_fatal("did not get expected error message; got: \"%s\"",
120+
PQerrorMessage(conn));
109121

110122
/* Entering pipeline mode when already in pipeline mode is OK */
111123
if (PQenterPipelineMode(conn)!=1)

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp