Asynchronous Query Processing
1.4. Asynchronous Query Processing
The PQexec
function is adequate for submitting commands in
simple synchronous
applications. It has a couple of major deficiencies however:
PQexec
waits for the command to be completed. The application may 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 control is buried inside
PQexec
, it is hard for the frontend to decide 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 one PGresult structure. If the submitted command string contains multiple SQL commands, all but the last PGresult are discarded byPQexec
.
Applications that do not like these limitations can instead use the
underlying functions that PQexec
is built from:
PQsendQuery
and PQgetResult
.
Older programs that used this functionality as well as
PQputline
and PQputnbytes
could block waiting to send data to the backend. To
address that issue, the function PQsetnonblocking
was added.
Old applications can neglect to use PQsetnonblocking
and get the older potentially blocking behavior. Newer programs can use
PQsetnonblocking
to achieve a completely nonblocking
connection to the backend.
PQsetnonblocking
Sets the nonblocking status of the connection.int PQsetnonblocking(PGconn *conn, int arg)
Sets the state of the connection to nonblocking if arg is 1, blocking if arg is 0. Returns 0 if OK, -1 if error.
In the nonblocking state, calls to
PQputline
,PQputnbytes
,PQsendQuery
andPQendcopy
will not block but instead return an error if they need to be called again.When a database connection has been set to nonblocking mode and
PQexec
is called, it will temporarily set the state of the connection to blocking until thePQexec
completes.More of libpq is expected to be made safe for
PQsetnonblocking
functionality in the near future.PQisnonblocking
Returns the blocking status of the database connection.int PQisnonblocking(const PGconn *conn)
Returns 1 if the connection is set to nonblocking mode, 0 if blocking.
PQsendQuery
Submit a command to the server without waiting for the result(s). 1 is returned if the command was successfully dispatched, 0 if not (in which case, usePQerrorMessage
to get more information about the failure).int PQsendQuery(PGconn *conn, const char *query);
After successfully calling
PQsendQuery
, callPQgetResult
one or more times to obtain the results.PQsendQuery
may not be called again (on the same connection) untilPQgetResult
has returned NULL, indicating that the command is done.PQgetResult
Wait for the next result from a priorPQsendQuery
, and return it. NULL is returned when the query is complete and there will be no more results.PGresult *PQgetResult(PGconn *conn);
PQgetResult
must be called repeatedly until it returns NULL, indicating that the command is done. (If called when no command is active,PQgetResult
will just return NULL at once.) Each non-NULL result fromPQgetResult
should be processed using the same PGresult accessor functions previously described. Don't forget to free each result object withPQclear
when done with it. Note thatPQgetResult
will block only if a query is active and the necessary response data has not yet been read byPQconsumeInput
.
Using PQsendQuery
and PQgetResult
solves one of PQexec
's problems:
If a command string contains multiple SQL commands, the results of those
commands can be obtained individually. (This allows a simple form of
overlapped processing, by the way: the frontend can be handling the
results of one query while the backend is still working on later
queries in the same command string.) However, calling PQgetResult
will
still cause the frontend to block until the backend completes the
next SQL command. This can be avoided by proper use of three more
functions:
PQconsumeInput
If input is available from the backend, consume it.int PQconsumeInput(PGconn *conn);
PQconsumeInput
normally returns 1 indicating "no error", but returns 0 if there was some kind of trouble (in which casePQerrorMessage
is set). Note that the result does not say whether any input data was actually collected. After callingPQconsumeInput
, the application may checkPQisBusy
and/orPQnotifies
to see if their state has changed.PQconsumeInput
may be called even if the application is not prepared to deal with a result or notification just yet. The routine 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
Returns 1 if a query is busy, that is,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 backend; thereforePQconsumeInput
must be invoked first, or the busy state will never end.PQflush
Attempt to flush any data queued to the backend, returns 0 if successful (or if the send queue is empty) or EOF if it failed for some reason.int PQflush(PGconn *conn);
PQflush
needs to be called on a nonblocking connection before callingselect()
to determine if a response has arrived. If 0 is returned it ensures that there is no data queued to the backend that has not actually been sent. Only applications that have usedPQsetnonblocking
have a need for this.PQsocket
Obtain the file descriptor number for the backend connection socket. A valid descriptor will be >= 0; a result of -1 indicates that no backend connection is currently open.int PQsocket(const PGconn *conn);
PQsocket
should be used to obtain the backend socket descriptor in preparation for executingselect()
. This allows an application using a blocking connection to wait for either backend responses or other conditions. If the result ofselect()
indicates that data can be read from the backend socket, thenPQconsumeInput
should be called to read the data; after which,PQisBusy
,PQgetResult
, and/orPQnotifies
can be used to process the response.Nonblocking connections (that have used
PQsetnonblocking
) should not useselect()
untilPQflush
has returned 0 indicating that there is no buffered data waiting to be sent to the backend.
A typical frontend using these functions will have a main loop that uses
select
to wait for all the conditions that it must
respond to. One of the conditions will be input available from the backend,
which in select
's terms is readable data on the file
descriptor identified by PQsocket
.
When the main loop detects input ready, it should call
PQconsumeInput
to read the input. It can then call
PQisBusy
, followed by PQgetResult
if PQisBusy
returns false (0). It can also call
PQnotifies
to detect NOTIFY messages (see Section 1.6).
A frontend that uses PQsendQuery
/PQgetResult
can also attempt to cancel a command that is still being processed by the backend.
PQrequestCancel
Request that PostgreSQL abandon processing of the current command.int PQrequestCancel(PGconn *conn);
The return value is 1 if the cancel request was successfully dispatched, 0 if not. (If not,
PQerrorMessage
tells why not.) Successful dispatch is no guarantee that the request will have any effect, however. Regardless of the return value ofPQrequestCancel
, the application must continue with the normal result-reading sequence usingPQgetResult
. If the cancellation is effective, the current command will terminate early and return an error result. If the cancellation fails (say, because the backend was already done processing the command), then there will be no visible result at all.
Note that if the current command is part of a transaction, cancellation will abort the whole transaction.
PQrequestCancel
can safely be invoked from a signal handler.
So, it is also possible to use it in conjunction with plain
PQexec
, if the decision to cancel can be made in a signal
handler. For example, psql invokes
PQrequestCancel
from a SIGINT signal handler, thus allowing
interactive cancellation of queries that it issues through PQexec
.
Note that PQrequestCancel
will have no effect if the connection
is not currently open or the backend is not currently processing a command.