32.4. Asynchronous Command Processing#
ThePQexec
function is adequate for submitting commands in normal, synchronous applications. It has a few deficiencies, however, that can be of importance to some users:
PQexec
waits for the command to be completed. The application might have other work to do (such as maintaining a user interface), in which case it won't want to block waiting for the response.Since the execution of the client application is suspended while it waits for the result, it is hard for the application to decide that it would like to try to cancel the ongoing command. (It can be done from a signal handler, but not otherwise.)
PQexec
can return only onePGresult
structure. If the submitted command string contains multipleSQL commands, all but the lastPGresult
are discarded byPQexec
.PQexec
always collects the command's entire result, buffering it in a singlePGresult
. While this simplifies error-handling logic for the application, it can be impractical for results containing many rows.
Applications that do not like these limitations can instead use the underlying functions thatPQexec
is built from:PQsendQuery
andPQgetResult
. There are alsoPQsendQueryParams
,PQsendPrepare
,PQsendQueryPrepared
,PQsendDescribePrepared
,PQsendDescribePortal
,PQsendClosePrepared
, andPQsendClosePortal
, which can be used withPQgetResult
to duplicate the functionality ofPQexecParams
,PQprepare
,PQexecPrepared
,PQdescribePrepared
,PQdescribePortal
,PQclosePrepared
, andPQclosePortal
respectively.
PQsendQuery
#Submits a command to the server without waiting for the result(s). 1 is returned if the command was successfully dispatched and 0 if not (in which case, use
PQerrorMessage
to get more information about the failure).int PQsendQuery(PGconn *conn, const char *command);
After successfully calling
PQsendQuery
, callPQgetResult
one or more times to obtain the results.PQsendQuery
cannot be called again (on the same connection) untilPQgetResult
has returned a null pointer, indicating that the command is done.In pipeline mode, this function is disallowed.
PQsendQueryParams
#Submits a command and separate parameters to the server without waiting for the result(s).
int PQsendQueryParams(PGconn *conn, const char *command, int nParams, const Oid *paramTypes, const char * const *paramValues, const int *paramLengths, const int *paramFormats, int resultFormat);
This is equivalent to
PQsendQuery
except that query parameters can be specified separately from the query string. The function's parameters are handled identically toPQexecParams
. LikePQexecParams
, it allows only one command in the query string.PQsendPrepare
#Sends a request to create a prepared statement with the given parameters, without waiting for completion.
int PQsendPrepare(PGconn *conn, const char *stmtName, const char *query, int nParams, const Oid *paramTypes);
This is an asynchronous version of
PQprepare
: it returns 1 if it was able to dispatch the request, and 0 if not. After a successful call, callPQgetResult
to determine whether the server successfully created the prepared statement. The function's parameters are handled identically toPQprepare
.PQsendQueryPrepared
#Sends a request to execute a prepared statement with given parameters, without waiting for the result(s).
int PQsendQueryPrepared(PGconn *conn, const char *stmtName, int nParams, const char * const *paramValues, const int *paramLengths, const int *paramFormats, int resultFormat);
This is similar to
PQsendQueryParams
, but the command to be executed is specified by naming a previously-prepared statement, instead of giving a query string. The function's parameters are handled identically toPQexecPrepared
.PQsendDescribePrepared
#Submits a request to obtain information about the specified prepared statement, without waiting for completion.
int PQsendDescribePrepared(PGconn *conn, const char *stmtName);
This is an asynchronous version of
PQdescribePrepared
: it returns 1 if it was able to dispatch the request, and 0 if not. After a successful call, callPQgetResult
to obtain the results. The function's parameters are handled identically toPQdescribePrepared
.PQsendDescribePortal
#Submits a request to obtain information about the specified portal, without waiting for completion.
int PQsendDescribePortal(PGconn *conn, const char *portalName);
This is an asynchronous version of
PQdescribePortal
: it returns 1 if it was able to dispatch the request, and 0 if not. After a successful call, callPQgetResult
to obtain the results. The function's parameters are handled identically toPQdescribePortal
.PQsendClosePrepared
#Submits a request to close the specified prepared statement, without waiting for completion.
int PQsendClosePrepared(PGconn *conn, const char *stmtName);
This is an asynchronous version of
PQclosePrepared
: it returns 1 if it was able to dispatch the request, and 0 if not. After a successful call, callPQgetResult
to obtain the results. The function's parameters are handled identically toPQclosePrepared
.PQsendClosePortal
#Submits a request to close specified portal, without waiting for completion.
int PQsendClosePortal(PGconn *conn, const char *portalName);
This is an asynchronous version of
PQclosePortal
: it returns 1 if it was able to dispatch the request, and 0 if not. After a successful call, callPQgetResult
to obtain the results. The function's parameters are handled identically toPQclosePortal
.PQgetResult
#Waits for the next result from a prior
PQsendQuery
,PQsendQueryParams
,PQsendPrepare
,PQsendQueryPrepared
,PQsendDescribePrepared
,PQsendDescribePortal
,PQsendClosePrepared
,PQsendClosePortal
,PQsendPipelineSync
, orPQpipelineSync
call, and returns it. A null pointer is returned when the command is complete and there will be no more results.PGresult *PQgetResult(PGconn *conn);
PQgetResult
must be called repeatedly until it returns a null pointer, indicating that the command is done. (If called when no command is active,PQgetResult
will just return a null pointer at once.) Each non-null result fromPQgetResult
should be processed using the samePGresult
accessor functions previously described. Don't forget to free each result object withPQclear
when done with it. Note thatPQgetResult
will block only if a command is active and the necessary response data has not yet been read byPQconsumeInput
.In pipeline mode,
PQgetResult
will return normally unless an error occurs; for any subsequent query sent after the one that caused the error until (and excluding) the next synchronization point, a special result of typePGRES_PIPELINE_ABORTED
will be returned, and a null pointer will be returned after it. When the pipeline synchronization point is reached, a result of typePGRES_PIPELINE_SYNC
will be returned. The result of the next query after the synchronization point follows immediately (that is, no null pointer is returned after the synchronization point).Note
Even when
PQresultStatus
indicates a fatal error,PQgetResult
should be called until it returns a null pointer, to allowlibpq to process the error information completely.
Using Another frequently-desired feature that can be obtained with By itself, calling If input is available from the server, consume it. Returns 1 if a command is busy, that is, A typical application using these functions will have a main loop that uses A client that uses By using the functions described above, it is possible to avoid blocking while waiting for input from the database server. However, it is still possible that the application will block waiting to send output to the server. This is relatively uncommon but can happen if very long SQL commands or data values are sent. (It is much more probable if the application sends data via Sets the nonblocking status of the connection. Sets the state of the connection to nonblocking if In the nonblocking state, successful calls to Note that Returns the blocking status of the database connection. Returns 1 if the connection is set to nonblocking mode and 0 if blocking. Attempts to flush any queued output data to the server. Returns 0 if successful (or if the send queue is empty), -1 if it failed for some reason, or 1 if it was unable to send all the data in the send queue yet (this case can only occur if the connection is nonblocking). After sending any command or data on a nonblocking connection, callPQsendQuery
andPQgetResult
solves one ofPQexec
's problems: If a command string contains multipleSQL commands, the results of those commands can be obtained individually. (This allows a simple form of overlapped processing, by the way: the client can be handling the results of one command while the server is still working on later queries in the same command string.)PQsendQuery
andPQgetResult
is retrieving large query results a limited number of rows at a time. This is discussed inSection 32.6.PQgetResult
will still cause the client to block until the server completes the nextSQL command. This can be avoided by proper use of two more functions:PQconsumeInput
#int PQconsumeInput(PGconn *conn);
PQconsumeInput
normally returns 1 indicating“no error”, but returns 0 if there was some kind of trouble (in which casePQerrorMessage
can be consulted). Note that the result does not say whether any input data was actually collected. After callingPQconsumeInput
, the application can checkPQisBusy
and/orPQnotifies
to see if their state has changed.PQconsumeInput
can be called even if the application is not prepared to deal with a result or notification just yet. The function will read available data and save it in a buffer, thereby causing aselect()
read-ready indication to go away. The application can thus usePQconsumeInput
to clear theselect()
condition immediately, and then examine the results at leisure.PQisBusy
#PQgetResult
would block waiting for input. A 0 return indicates thatPQgetResult
can be called with assurance of not blocking.int PQisBusy(PGconn *conn);
PQisBusy
will not itself attempt to read data from the server; thereforePQconsumeInput
must be invoked first, or the busy state will never end.select()
orpoll()
to wait for all the conditions that it must respond to. One of the conditions will be input available from the server, which in terms ofselect()
means readable data on the file descriptor identified byPQsocket
. When the main loop detects input ready, it should callPQconsumeInput
to read the input. It can then callPQisBusy
, followed byPQgetResult
ifPQisBusy
returns false (0). It can also callPQnotifies
to detectNOTIFY
messages (seeSection 32.9).PQsendQuery
/PQgetResult
can also attempt to cancel a command that is still being processed by the server; seeSection 32.7. But regardless of the return value ofPQcancelBlocking
, the application must continue with the normal result-reading sequence usingPQgetResult
. A successful cancellation will simply cause the command to terminate sooner than it would have otherwise.COPY IN
, however.) To prevent this possibility and achieve completely nonblocking database operation, the following additional functions can be used.PQsetnonblocking
#int PQsetnonblocking(PGconn *conn, int arg);
arg
is 1, or blocking ifarg
is 0. Returns 0 if OK, -1 if error.PQsendQuery
,PQputline
,PQputnbytes
,PQputCopyData
, andPQendcopy
will not block; their changes are stored in the local output buffer until they are flushed. Unsuccessful calls will return an error and must be retried.PQexec
does not honor nonblocking mode; if it is called, it will act in blocking fashion anyway.PQisnonblocking
#int PQisnonblocking(const PGconn *conn);
PQflush
#int PQflush(PGconn *conn);
PQflush
. If it returns 1, wait for the socket to become read- or write-ready. If it becomes write-ready, callPQflush
again. If it becomes read-ready, callPQconsumeInput
, then callPQflush
again. Repeat untilPQflush
returns 0. (It is necessary to check for read-ready and drain the input withPQconsumeInput
, because the server can block trying to send us data, e.g., NOTICE messages, and won't read our data until we read its.) OncePQflush
returns 0, wait for the socket to be read-ready and then read the response as described above.