Commit acb7e4eb authored by Alvaro Herrera's avatar Alvaro Herrera

Implement pipeline mode in libpq

Pipeline mode in libpq lets an application avoid the Sync messages in
the FE/BE protocol that are implicit in the old libpq API after each
query.  The application can then insert Sync at its leisure with a new
libpq function PQpipelineSync.  This can lead to substantial reductions
in query latency.
Co-authored-by: default avatarCraig Ringer <craig.ringer@enterprisedb.com>
Co-authored-by: default avatarMatthieu Garrigues <matthieu.garrigues@gmail.com>
Co-authored-by: default avatarÁlvaro Herrera <alvherre@alvh.no-ip.org>
Reviewed-by: default avatarAndres Freund <andres@anarazel.de>
Reviewed-by: default avatarAya Iwata <iwata.aya@jp.fujitsu.com>
Reviewed-by: default avatarDaniel Vérité <daniel@manitou-mail.org>
Reviewed-by: default avatarDavid G. Johnston <david.g.johnston@gmail.com>
Reviewed-by: default avatarJustin Pryzby <pryzby@telsasoft.com>
Reviewed-by: default avatarKirk Jamison <k.jamison@fujitsu.com>
Reviewed-by: default avatarMichael Paquier <michael.paquier@gmail.com>
Reviewed-by: default avatarNikhil Sontakke <nikhils@2ndquadrant.com>
Reviewed-by: default avatarVaishnavi Prabakaran <VaishnaviP@fast.au.fujitsu.com>
Reviewed-by: default avatarZhihong Yu <zyu@yugabyte.com>

Discussion: https://postgr.es/m/CAMsr+YFUjJytRyV4J-16bEoiZyH=4nj+sQ7JP9ajwz=B4dMMZw@mail.gmail.com
Discussion: https://postgr.es/m/CAJkzx4T5E-2cQe3dtv2R78dYFvz+in8PY7A8MArvLhs_pg75gg@mail.gmail.com
parent 146cb388
This diff is collapsed.
...@@ -130,6 +130,10 @@ ...@@ -130,6 +130,10 @@
<application>libpq</application> library. <application>libpq</application> library.
</para> </para>
<para>
Client applications cannot use these functions while a libpq connection is in pipeline mode.
</para>
<sect2 id="lo-create"> <sect2 id="lo-create">
<title>Creating a Large Object</title> <title>Creating a Large Object</title>
......
...@@ -1019,6 +1019,12 @@ libpqrcv_exec(WalReceiverConn *conn, const char *query, ...@@ -1019,6 +1019,12 @@ libpqrcv_exec(WalReceiverConn *conn, const char *query,
walres->err = _("empty query"); walres->err = _("empty query");
break; break;
case PGRES_PIPELINE_SYNC:
case PGRES_PIPELINE_ABORTED:
walres->status = WALRCV_ERROR;
walres->err = _("unexpected pipeline mode");
break;
case PGRES_NONFATAL_ERROR: case PGRES_NONFATAL_ERROR:
case PGRES_FATAL_ERROR: case PGRES_FATAL_ERROR:
case PGRES_BAD_RESPONSE: case PGRES_BAD_RESPONSE:
......
...@@ -929,6 +929,8 @@ should_processing_continue(PGresult *res) ...@@ -929,6 +929,8 @@ should_processing_continue(PGresult *res)
case PGRES_COPY_IN: case PGRES_COPY_IN:
case PGRES_COPY_BOTH: case PGRES_COPY_BOTH:
case PGRES_SINGLE_TUPLE: case PGRES_SINGLE_TUPLE:
case PGRES_PIPELINE_SYNC:
case PGRES_PIPELINE_ABORTED:
return false; return false;
} }
return true; return true;
......
...@@ -179,3 +179,7 @@ PQgetgssctx 176 ...@@ -179,3 +179,7 @@ PQgetgssctx 176
PQsetSSLKeyPassHook_OpenSSL 177 PQsetSSLKeyPassHook_OpenSSL 177
PQgetSSLKeyPassHook_OpenSSL 178 PQgetSSLKeyPassHook_OpenSSL 178
PQdefaultSSLKeyPassHook_OpenSSL 179 PQdefaultSSLKeyPassHook_OpenSSL 179
PQenterPipelineMode 180
PQexitPipelineMode 181
PQpipelineSync 182
PQpipelineStatus 183
...@@ -522,6 +522,23 @@ pqDropConnection(PGconn *conn, bool flushInput) ...@@ -522,6 +522,23 @@ pqDropConnection(PGconn *conn, bool flushInput)
} }
} }
/*
* pqFreeCommandQueue
* Free all the entries of PGcmdQueueEntry queue passed.
*/
static void
pqFreeCommandQueue(PGcmdQueueEntry *queue)
{
while (queue != NULL)
{
PGcmdQueueEntry *cur = queue;
queue = cur->next;
if (cur->query)
free(cur->query);
free(cur);
}
}
/* /*
* pqDropServerData * pqDropServerData
...@@ -553,6 +570,12 @@ pqDropServerData(PGconn *conn) ...@@ -553,6 +570,12 @@ pqDropServerData(PGconn *conn)
} }
conn->notifyHead = conn->notifyTail = NULL; conn->notifyHead = conn->notifyTail = NULL;
pqFreeCommandQueue(conn->cmd_queue_head);
conn->cmd_queue_head = conn->cmd_queue_tail = NULL;
pqFreeCommandQueue(conn->cmd_queue_recycle);
conn->cmd_queue_recycle = NULL;
/* Reset ParameterStatus data, as well as variables deduced from it */ /* Reset ParameterStatus data, as well as variables deduced from it */
pstatus = conn->pstatus; pstatus = conn->pstatus;
while (pstatus != NULL) while (pstatus != NULL)
...@@ -2459,6 +2482,7 @@ keep_going: /* We will come back to here until there is ...@@ -2459,6 +2482,7 @@ keep_going: /* We will come back to here until there is
/* Drop any PGresult we might have, too */ /* Drop any PGresult we might have, too */
conn->asyncStatus = PGASYNC_IDLE; conn->asyncStatus = PGASYNC_IDLE;
conn->xactStatus = PQTRANS_IDLE; conn->xactStatus = PQTRANS_IDLE;
conn->pipelineStatus = PQ_PIPELINE_OFF;
pqClearAsyncResult(conn); pqClearAsyncResult(conn);
/* Reset conn->status to put the state machine in the right state */ /* Reset conn->status to put the state machine in the right state */
...@@ -3917,6 +3941,7 @@ makeEmptyPGconn(void) ...@@ -3917,6 +3941,7 @@ makeEmptyPGconn(void)
conn->status = CONNECTION_BAD; conn->status = CONNECTION_BAD;
conn->asyncStatus = PGASYNC_IDLE; conn->asyncStatus = PGASYNC_IDLE;
conn->pipelineStatus = PQ_PIPELINE_OFF;
conn->xactStatus = PQTRANS_IDLE; conn->xactStatus = PQTRANS_IDLE;
conn->options_valid = false; conn->options_valid = false;
conn->nonblocking = false; conn->nonblocking = false;
...@@ -4084,8 +4109,6 @@ freePGconn(PGconn *conn) ...@@ -4084,8 +4109,6 @@ freePGconn(PGconn *conn)
if (conn->connip) if (conn->connip)
free(conn->connip); free(conn->connip);
/* Note that conn->Pfdebug is not ours to close or free */ /* Note that conn->Pfdebug is not ours to close or free */
if (conn->last_query)
free(conn->last_query);
if (conn->write_err_msg) if (conn->write_err_msg)
free(conn->write_err_msg); free(conn->write_err_msg);
if (conn->inBuffer) if (conn->inBuffer)
...@@ -4174,6 +4197,7 @@ closePGconn(PGconn *conn) ...@@ -4174,6 +4197,7 @@ closePGconn(PGconn *conn)
conn->status = CONNECTION_BAD; /* Well, not really _bad_ - just absent */ conn->status = CONNECTION_BAD; /* Well, not really _bad_ - just absent */
conn->asyncStatus = PGASYNC_IDLE; conn->asyncStatus = PGASYNC_IDLE;
conn->xactStatus = PQTRANS_IDLE; conn->xactStatus = PQTRANS_IDLE;
conn->pipelineStatus = PQ_PIPELINE_OFF;
pqClearAsyncResult(conn); /* deallocate result */ pqClearAsyncResult(conn); /* deallocate result */
resetPQExpBuffer(&conn->errorMessage); resetPQExpBuffer(&conn->errorMessage);
release_conn_addrinfo(conn); release_conn_addrinfo(conn);
...@@ -6726,6 +6750,15 @@ PQbackendPID(const PGconn *conn) ...@@ -6726,6 +6750,15 @@ PQbackendPID(const PGconn *conn)
return conn->be_pid; return conn->be_pid;
} }
PGpipelineStatus
PQpipelineStatus(const PGconn *conn)
{
if (!conn)
return PQ_PIPELINE_OFF;
return conn->pipelineStatus;
}
int int
PQconnectionNeedsPassword(const PGconn *conn) PQconnectionNeedsPassword(const PGconn *conn)
{ {
......
This diff is collapsed.
...@@ -158,6 +158,18 @@ pqParseInput3(PGconn *conn) ...@@ -158,6 +158,18 @@ pqParseInput3(PGconn *conn)
if (conn->asyncStatus != PGASYNC_IDLE) if (conn->asyncStatus != PGASYNC_IDLE)
return; return;
/*
* We're also notionally not-IDLE when in pipeline mode the state
* says "idle" (so we have completed receiving the results of one
* query from the server and dispatched them to the application)
* but another query is queued; yield back control to caller so
* that they can initiate processing of the next query in the
* queue.
*/
if (conn->pipelineStatus != PQ_PIPELINE_OFF &&
conn->cmd_queue_head != NULL)
return;
/* /*
* Unexpected message in IDLE state; need to recover somehow. * Unexpected message in IDLE state; need to recover somehow.
* ERROR messages are handled using the notice processor; * ERROR messages are handled using the notice processor;
...@@ -179,6 +191,7 @@ pqParseInput3(PGconn *conn) ...@@ -179,6 +191,7 @@ pqParseInput3(PGconn *conn)
} }
else else
{ {
/* Any other case is unexpected and we summarily skip it */
pqInternalNotice(&conn->noticeHooks, pqInternalNotice(&conn->noticeHooks,
"message type 0x%02x arrived from server while idle", "message type 0x%02x arrived from server while idle",
id); id);
...@@ -217,10 +230,37 @@ pqParseInput3(PGconn *conn) ...@@ -217,10 +230,37 @@ pqParseInput3(PGconn *conn)
return; return;
conn->asyncStatus = PGASYNC_READY; conn->asyncStatus = PGASYNC_READY;
break; break;
case 'Z': /* backend is ready for new query */ case 'Z': /* sync response, backend is ready for new
* query */
if (getReadyForQuery(conn)) if (getReadyForQuery(conn))
return; return;
conn->asyncStatus = PGASYNC_IDLE; if (conn->pipelineStatus != PQ_PIPELINE_OFF)
{
conn->result = PQmakeEmptyPGresult(conn,
PGRES_PIPELINE_SYNC);
if (!conn->result)
{
appendPQExpBufferStr(&conn->errorMessage,
libpq_gettext("out of memory"));
pqSaveErrorResult(conn);
}
else
{
conn->pipelineStatus = PQ_PIPELINE_ON;
conn->asyncStatus = PGASYNC_READY;
}
}
else
{
/*
* In simple query protocol, advance the command queue
* (see PQgetResult).
*/
if (conn->cmd_queue_head &&
conn->cmd_queue_head->queryclass == PGQUERY_SIMPLE)
pqCommandQueueAdvance(conn);
conn->asyncStatus = PGASYNC_IDLE;
}
break; break;
case 'I': /* empty query */ case 'I': /* empty query */
if (conn->result == NULL) if (conn->result == NULL)
...@@ -238,7 +278,8 @@ pqParseInput3(PGconn *conn) ...@@ -238,7 +278,8 @@ pqParseInput3(PGconn *conn)
break; break;
case '1': /* Parse Complete */ case '1': /* Parse Complete */
/* If we're doing PQprepare, we're done; else ignore */ /* If we're doing PQprepare, we're done; else ignore */
if (conn->queryclass == PGQUERY_PREPARE) if (conn->cmd_queue_head &&
conn->cmd_queue_head->queryclass == PGQUERY_PREPARE)
{ {
if (conn->result == NULL) if (conn->result == NULL)
{ {
...@@ -285,7 +326,8 @@ pqParseInput3(PGconn *conn) ...@@ -285,7 +326,8 @@ pqParseInput3(PGconn *conn)
conn->inCursor += msgLength; conn->inCursor += msgLength;
} }
else if (conn->result == NULL || else if (conn->result == NULL ||
conn->queryclass == PGQUERY_DESCRIBE) (conn->cmd_queue_head &&
conn->cmd_queue_head->queryclass == PGQUERY_DESCRIBE))
{ {
/* First 'T' in a query sequence */ /* First 'T' in a query sequence */
if (getRowDescriptions(conn, msgLength)) if (getRowDescriptions(conn, msgLength))
...@@ -316,7 +358,8 @@ pqParseInput3(PGconn *conn) ...@@ -316,7 +358,8 @@ pqParseInput3(PGconn *conn)
* instead of PGRES_TUPLES_OK. Otherwise we can just * instead of PGRES_TUPLES_OK. Otherwise we can just
* ignore this message. * ignore this message.
*/ */
if (conn->queryclass == PGQUERY_DESCRIBE) if (conn->cmd_queue_head &&
conn->cmd_queue_head->queryclass == PGQUERY_DESCRIBE)
{ {
if (conn->result == NULL) if (conn->result == NULL)
{ {
...@@ -445,7 +488,7 @@ handleSyncLoss(PGconn *conn, char id, int msgLength) ...@@ -445,7 +488,7 @@ handleSyncLoss(PGconn *conn, char id, int msgLength)
id, msgLength); id, msgLength);
/* build an error result holding the error message */ /* build an error result holding the error message */
pqSaveErrorResult(conn); pqSaveErrorResult(conn);
conn->asyncStatus = PGASYNC_READY; /* drop out of GetResult wait loop */ conn->asyncStatus = PGASYNC_READY; /* drop out of PQgetResult wait loop */
/* flush input data since we're giving up on processing it */ /* flush input data since we're giving up on processing it */
pqDropConnection(conn, true); pqDropConnection(conn, true);
conn->status = CONNECTION_BAD; /* No more connection to backend */ conn->status = CONNECTION_BAD; /* No more connection to backend */
...@@ -471,7 +514,9 @@ getRowDescriptions(PGconn *conn, int msgLength) ...@@ -471,7 +514,9 @@ getRowDescriptions(PGconn *conn, int msgLength)
* PGresult created by getParamDescriptions, and we should fill data into * PGresult created by getParamDescriptions, and we should fill data into
* that. Otherwise, create a new, empty PGresult. * that. Otherwise, create a new, empty PGresult.
*/ */
if (conn->queryclass == PGQUERY_DESCRIBE) if (!conn->cmd_queue_head ||
(conn->cmd_queue_head &&
conn->cmd_queue_head->queryclass == PGQUERY_DESCRIBE))
{ {
if (conn->result) if (conn->result)
result = conn->result; result = conn->result;
...@@ -568,7 +613,9 @@ getRowDescriptions(PGconn *conn, int msgLength) ...@@ -568,7 +613,9 @@ getRowDescriptions(PGconn *conn, int msgLength)
* If we're doing a Describe, we're done, and ready to pass the result * If we're doing a Describe, we're done, and ready to pass the result
* back to the client. * back to the client.
*/ */
if (conn->queryclass == PGQUERY_DESCRIBE) if ((!conn->cmd_queue_head) ||
(conn->cmd_queue_head &&
conn->cmd_queue_head->queryclass == PGQUERY_DESCRIBE))
{ {
conn->asyncStatus = PGASYNC_READY; conn->asyncStatus = PGASYNC_READY;
return 0; return 0;
...@@ -841,6 +888,10 @@ pqGetErrorNotice3(PGconn *conn, bool isError) ...@@ -841,6 +888,10 @@ pqGetErrorNotice3(PGconn *conn, bool isError)
PQExpBufferData workBuf; PQExpBufferData workBuf;
char id; char id;
/* If in pipeline mode, set error indicator for it */
if (isError && conn->pipelineStatus != PQ_PIPELINE_OFF)
conn->pipelineStatus = PQ_PIPELINE_ABORTED;
/* /*
* If this is an error message, pre-emptively clear any incomplete query * If this is an error message, pre-emptively clear any incomplete query
* result we may have. We'd just throw it away below anyway, and * result we may have. We'd just throw it away below anyway, and
...@@ -897,8 +948,8 @@ pqGetErrorNotice3(PGconn *conn, bool isError) ...@@ -897,8 +948,8 @@ pqGetErrorNotice3(PGconn *conn, bool isError)
* might need it for an error cursor display, which is only true if there * might need it for an error cursor display, which is only true if there
* is a PG_DIAG_STATEMENT_POSITION field. * is a PG_DIAG_STATEMENT_POSITION field.
*/ */
if (have_position && conn->last_query && res) if (have_position && res && conn->cmd_queue_head && conn->cmd_queue_head->query)
res->errQuery = pqResultStrdup(res, conn->last_query); res->errQuery = pqResultStrdup(res, conn->cmd_queue_head->query);
/* /*
* Now build the "overall" error message for PQresultErrorMessage. * Now build the "overall" error message for PQresultErrorMessage.
...@@ -1817,7 +1868,8 @@ pqEndcopy3(PGconn *conn) ...@@ -1817,7 +1868,8 @@ pqEndcopy3(PGconn *conn)
* If we sent the COPY command in extended-query mode, we must issue a * If we sent the COPY command in extended-query mode, we must issue a
* Sync as well. * Sync as well.
*/ */
if (conn->queryclass != PGQUERY_SIMPLE) if (conn->cmd_queue_head &&
conn->cmd_queue_head->queryclass != PGQUERY_SIMPLE)
{ {
if (pqPutMsgStart('S', conn) < 0 || if (pqPutMsgStart('S', conn) < 0 ||
pqPutMsgEnd(conn) < 0) pqPutMsgEnd(conn) < 0)
...@@ -1897,6 +1949,9 @@ pqFunctionCall3(PGconn *conn, Oid fnid, ...@@ -1897,6 +1949,9 @@ pqFunctionCall3(PGconn *conn, Oid fnid,
int avail; int avail;
int i; int i;
/* already validated by PQfn */
Assert(conn->pipelineStatus == PQ_PIPELINE_OFF);
/* PQfn already validated connection state */ /* PQfn already validated connection state */
if (pqPutMsgStart('F', conn) < 0 || /* function call msg */ if (pqPutMsgStart('F', conn) < 0 || /* function call msg */
......
...@@ -96,7 +96,10 @@ typedef enum ...@@ -96,7 +96,10 @@ typedef enum
PGRES_NONFATAL_ERROR, /* notice or warning message */ PGRES_NONFATAL_ERROR, /* notice or warning message */
PGRES_FATAL_ERROR, /* query failed */ PGRES_FATAL_ERROR, /* query failed */
PGRES_COPY_BOTH, /* Copy In/Out data transfer in progress */ PGRES_COPY_BOTH, /* Copy In/Out data transfer in progress */
PGRES_SINGLE_TUPLE /* single tuple from larger resultset */ PGRES_SINGLE_TUPLE, /* single tuple from larger resultset */
PGRES_PIPELINE_SYNC, /* pipeline synchronization point */
PGRES_PIPELINE_ABORTED, /* Command didn't run because of an abort
* earlier in a pipeline */
} ExecStatusType; } ExecStatusType;
typedef enum typedef enum
...@@ -136,6 +139,16 @@ typedef enum ...@@ -136,6 +139,16 @@ typedef enum
PQPING_NO_ATTEMPT /* connection not attempted (bad params) */ PQPING_NO_ATTEMPT /* connection not attempted (bad params) */
} PGPing; } PGPing;
/*
* PGpipelineStatus - Current status of pipeline mode
*/
typedef enum
{
PQ_PIPELINE_OFF,
PQ_PIPELINE_ON,
PQ_PIPELINE_ABORTED
} PGpipelineStatus;
/* PGconn encapsulates a connection to the backend. /* PGconn encapsulates a connection to the backend.
* The contents of this struct are not supposed to be known to applications. * The contents of this struct are not supposed to be known to applications.
*/ */
...@@ -327,6 +340,7 @@ extern int PQserverVersion(const PGconn *conn); ...@@ -327,6 +340,7 @@ extern int PQserverVersion(const PGconn *conn);
extern char *PQerrorMessage(const PGconn *conn); extern char *PQerrorMessage(const PGconn *conn);
extern int PQsocket(const PGconn *conn); extern int PQsocket(const PGconn *conn);
extern int PQbackendPID(const PGconn *conn); extern int PQbackendPID(const PGconn *conn);
extern PGpipelineStatus PQpipelineStatus(const PGconn *conn);
extern int PQconnectionNeedsPassword(const PGconn *conn); extern int PQconnectionNeedsPassword(const PGconn *conn);
extern int PQconnectionUsedPassword(const PGconn *conn); extern int PQconnectionUsedPassword(const PGconn *conn);
extern int PQclientEncoding(const PGconn *conn); extern int PQclientEncoding(const PGconn *conn);
...@@ -434,6 +448,11 @@ extern PGresult *PQgetResult(PGconn *conn); ...@@ -434,6 +448,11 @@ extern PGresult *PQgetResult(PGconn *conn);
extern int PQisBusy(PGconn *conn); extern int PQisBusy(PGconn *conn);
extern int PQconsumeInput(PGconn *conn); extern int PQconsumeInput(PGconn *conn);
/* Routines for pipeline mode management */
extern int PQenterPipelineMode(PGconn *conn);
extern int PQexitPipelineMode(PGconn *conn);
extern int PQpipelineSync(PGconn *conn);
/* LISTEN/NOTIFY support */ /* LISTEN/NOTIFY support */
extern PGnotify *PQnotifies(PGconn *conn); extern PGnotify *PQnotifies(PGconn *conn);
......
...@@ -217,21 +217,16 @@ typedef enum ...@@ -217,21 +217,16 @@ typedef enum
{ {
PGASYNC_IDLE, /* nothing's happening, dude */ PGASYNC_IDLE, /* nothing's happening, dude */
PGASYNC_BUSY, /* query in progress */ PGASYNC_BUSY, /* query in progress */
PGASYNC_READY, /* result ready for PQgetResult */ PGASYNC_READY, /* query done, waiting for client to fetch
* result */
PGASYNC_READY_MORE, /* query done, waiting for client to fetch
* result, more results expected from this
* query */
PGASYNC_COPY_IN, /* Copy In data transfer in progress */ PGASYNC_COPY_IN, /* Copy In data transfer in progress */
PGASYNC_COPY_OUT, /* Copy Out data transfer in progress */ PGASYNC_COPY_OUT, /* Copy Out data transfer in progress */
PGASYNC_COPY_BOTH /* Copy In/Out data transfer in progress */ PGASYNC_COPY_BOTH /* Copy In/Out data transfer in progress */
} PGAsyncStatusType; } PGAsyncStatusType;
/* PGQueryClass tracks which query protocol we are now executing */
typedef enum
{
PGQUERY_SIMPLE, /* simple Query protocol (PQexec) */
PGQUERY_EXTENDED, /* full Extended protocol (PQexecParams) */
PGQUERY_PREPARE, /* Parse only (PQprepare) */
PGQUERY_DESCRIBE /* Describe Statement or Portal */
} PGQueryClass;
/* Target server type (decoded value of target_session_attrs) */ /* Target server type (decoded value of target_session_attrs) */
typedef enum typedef enum
{ {
...@@ -305,6 +300,29 @@ typedef enum pg_conn_host_type ...@@ -305,6 +300,29 @@ typedef enum pg_conn_host_type
CHT_UNIX_SOCKET CHT_UNIX_SOCKET
} pg_conn_host_type; } pg_conn_host_type;
/*
* PGQueryClass tracks which query protocol is in use for each command queue
* entry, or special operation in execution
*/
typedef enum
{
PGQUERY_SIMPLE, /* simple Query protocol (PQexec) */
PGQUERY_EXTENDED, /* full Extended protocol (PQexecParams) */
PGQUERY_PREPARE, /* Parse only (PQprepare) */
PGQUERY_DESCRIBE, /* Describe Statement or Portal */
PGQUERY_SYNC /* Sync (at end of a pipeline) */
} PGQueryClass;
/*
* An entry in the pending command queue.
*/
typedef struct PGcmdQueueEntry
{
PGQueryClass queryclass; /* Query type */
char *query; /* SQL command, or NULL if none/unknown/OOM */
struct PGcmdQueueEntry *next; /* list link */
} PGcmdQueueEntry;
/* /*
* pg_conn_host stores all information about each of possibly several hosts * pg_conn_host stores all information about each of possibly several hosts
* mentioned in the connection string. Most fields are derived by splitting * mentioned in the connection string. Most fields are derived by splitting
...@@ -389,12 +407,11 @@ struct pg_conn ...@@ -389,12 +407,11 @@ struct pg_conn
ConnStatusType status; ConnStatusType status;
PGAsyncStatusType asyncStatus; PGAsyncStatusType asyncStatus;
PGTransactionStatusType xactStatus; /* never changes to ACTIVE */ PGTransactionStatusType xactStatus; /* never changes to ACTIVE */
PGQueryClass queryclass;
char *last_query; /* last SQL command, or NULL if unknown */
char last_sqlstate[6]; /* last reported SQLSTATE */ char last_sqlstate[6]; /* last reported SQLSTATE */
bool options_valid; /* true if OK to attempt connection */ bool options_valid; /* true if OK to attempt connection */
bool nonblocking; /* whether this connection is using nonblock bool nonblocking; /* whether this connection is using nonblock
* sending semantics */ * sending semantics */
PGpipelineStatus pipelineStatus; /* status of pipeline mode */
bool singleRowMode; /* return current query result row-by-row? */ bool singleRowMode; /* return current query result row-by-row? */
char copy_is_binary; /* 1 = copy binary, 0 = copy text */ char copy_is_binary; /* 1 = copy binary, 0 = copy text */
int copy_already_done; /* # bytes already returned in COPY OUT */ int copy_already_done; /* # bytes already returned in COPY OUT */
...@@ -407,6 +424,19 @@ struct pg_conn ...@@ -407,6 +424,19 @@ struct pg_conn
pg_conn_host *connhost; /* details about each named host */ pg_conn_host *connhost; /* details about each named host */
char *connip; /* IP address for current network connection */ char *connip; /* IP address for current network connection */
/*
* The pending command queue as a singly-linked list. Head is the command
* currently in execution, tail is where new commands are added.
*/
PGcmdQueueEntry *cmd_queue_head;
PGcmdQueueEntry *cmd_queue_tail;
/*
* To save malloc traffic, we don't free entries right away; instead we
* save them in this list for possible reuse.
*/
PGcmdQueueEntry *cmd_queue_recycle;
/* Connection data */ /* Connection data */
pgsocket sock; /* FD for socket, PGINVALID_SOCKET if pgsocket sock; /* FD for socket, PGINVALID_SOCKET if
* unconnected */ * unconnected */
...@@ -622,6 +652,7 @@ extern void pqSaveMessageField(PGresult *res, char code, ...@@ -622,6 +652,7 @@ extern void pqSaveMessageField(PGresult *res, char code,
extern void pqSaveParameterStatus(PGconn *conn, const char *name, extern void pqSaveParameterStatus(PGconn *conn, const char *name,
const char *value); const char *value);
extern int pqRowProcessor(PGconn *conn, const char **errmsgp); extern int pqRowProcessor(PGconn *conn, const char **errmsgp);
extern void pqCommandQueueAdvance(PGconn *conn);
extern int PQsendQueryContinue(PGconn *conn, const char *query); extern int PQsendQueryContinue(PGconn *conn, const char *query);
/* === in fe-protocol3.c === */ /* === in fe-protocol3.c === */
...@@ -795,6 +826,11 @@ extern ssize_t pg_GSS_read(PGconn *conn, void *ptr, size_t len); ...@@ -795,6 +826,11 @@ extern ssize_t pg_GSS_read(PGconn *conn, void *ptr, size_t len);
*/ */
#define pqIsnonblocking(conn) ((conn)->nonblocking) #define pqIsnonblocking(conn) ((conn)->nonblocking)
/*
* Connection's outbuffer threshold, for pipeline mode.
*/
#define OUTBUFFER_THRESHOLD 65536
#ifdef ENABLE_NLS #ifdef ENABLE_NLS
extern char *libpq_gettext(const char *msgid) pg_attribute_format_arg(1); extern char *libpq_gettext(const char *msgid) pg_attribute_format_arg(1);
extern char *libpq_ngettext(const char *msgid, const char *msgid_plural, unsigned long n) pg_attribute_format_arg(1) pg_attribute_format_arg(2); extern char *libpq_ngettext(const char *msgid, const char *msgid_plural, unsigned long n) pg_attribute_format_arg(1) pg_attribute_format_arg(2);
......
...@@ -10,6 +10,7 @@ SUBDIRS = \ ...@@ -10,6 +10,7 @@ SUBDIRS = \
delay_execution \ delay_execution \
dummy_index_am \ dummy_index_am \
dummy_seclabel \ dummy_seclabel \
libpq_pipeline \
plsample \ plsample \
snapshot_too_old \ snapshot_too_old \
test_bloomfilter \ test_bloomfilter \
......
# Generated subdirectories
/log/
/results/
/tmp_check/
/libpq_pipeline
# src/test/modules/libpq_pipeline/Makefile
PROGRAM = libpq_pipeline
OBJS = libpq_pipeline.o
PG_CPPFLAGS = -I$(libpq_srcdir)
PG_LIBS_INTERNAL += $(libpq_pgport)
TAP_TESTS = 1
ifdef USE_PGXS
PG_CONFIG = pg_config
PGXS := $(shell $(PG_CONFIG) --pgxs)
include $(PGXS)
else
subdir = src/test/modules/libpq_pipeline
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
include $(top_srcdir)/contrib/contrib-global.mk
endif
Test programs and libraries for libpq
This diff is collapsed.
use strict;
use warnings;
use Config;
use PostgresNode;
use TestLib;
use Test::More tests => 8;
use Cwd;
my $node = get_new_node('main');
$node->init;
$node->start;
my $numrows = 10000;
$ENV{PATH} = "$ENV{PATH}:" . getcwd();
my ($out, $err) = run_command(['libpq_pipeline', 'tests']);
die "oops: $err" unless $err eq '';
my @tests = split(/\s/, $out);
for my $testname (@tests)
{
$node->command_ok(
[ 'libpq_pipeline', $testname, $node->connstr('postgres'), $numrows ],
"libpq_pipeline $testname");
}
$node->stop('fast');
...@@ -33,10 +33,11 @@ my @unlink_on_exit; ...@@ -33,10 +33,11 @@ my @unlink_on_exit;
# Set of variables for modules in contrib/ and src/test/modules/ # Set of variables for modules in contrib/ and src/test/modules/
my $contrib_defines = { 'refint' => 'REFINT_VERBOSE' }; my $contrib_defines = { 'refint' => 'REFINT_VERBOSE' };
my @contrib_uselibpq = ('dblink', 'oid2name', 'postgres_fdw', 'vacuumlo'); my @contrib_uselibpq =
my @contrib_uselibpgport = ('oid2name', 'vacuumlo'); ('dblink', 'oid2name', 'postgres_fdw', 'vacuumlo', 'libpq_pipeline');
my @contrib_uselibpgcommon = ('oid2name', 'vacuumlo'); my @contrib_uselibpgport = ('libpq_pipeline', 'oid2name', 'vacuumlo');
my $contrib_extralibs = undef; my @contrib_uselibpgcommon = ('libpq_pipeline', 'oid2name', 'vacuumlo');
my $contrib_extralibs = { 'libpq_pipeline' => ['ws2_32.lib'] };
my $contrib_extraincludes = { 'dblink' => ['src/backend'] }; my $contrib_extraincludes = { 'dblink' => ['src/backend'] };
my $contrib_extrasource = { my $contrib_extrasource = {
'cube' => [ 'contrib/cube/cubescan.l', 'contrib/cube/cubeparse.y' ], 'cube' => [ 'contrib/cube/cubescan.l', 'contrib/cube/cubeparse.y' ],
......
...@@ -1563,10 +1563,12 @@ PG_Locale_Strategy ...@@ -1563,10 +1563,12 @@ PG_Locale_Strategy
PG_Lock_Status PG_Lock_Status
PG_init_t PG_init_t
PGcancel PGcancel
PGcmdQueueEntry
PGconn PGconn
PGdataValue PGdataValue
PGlobjfuncs PGlobjfuncs
PGnotify PGnotify
PGpipelineStatus
PGresAttDesc PGresAttDesc
PGresAttValue PGresAttValue
PGresParamDesc PGresParamDesc
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment