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 by PQexec.
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 and PQendcopy
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 the PQexec
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, use
PQerrorMessage to get more information about the failure).
int PQsendQuery(PGconn *conn,
const char *query);
After successfully calling PQsendQuery, call
PQgetResult one or more
times to obtain the results. PQsendQuery may not be called
again (on the same connection) until PQgetResult has returned NULL,
indicating that the command is done.
PQgetResult
Wait for the next result from a prior PQsendQuery,
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 from PQgetResult should be processed using
the same PGresult accessor functions previously described.
Don't forget to free each result object with PQclear when done with it.
Note that PQgetResult will block only if a query is active and the
necessary response data has not yet been read by PQconsumeInput.
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 case
PQerrorMessage is set). Note that the result does not say
whether any input data was actually collected. After calling
PQconsumeInput, the application may check
PQisBusy and/or PQnotifies 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 a select() read-ready indication to go away. The
application can thus use PQconsumeInput to clear the
select() 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 that PQgetResult can
be called with assurance of not blocking.
int PQisBusy(PGconn *conn);
PQisBusy will not itself attempt to read data from the backend;
therefore PQconsumeInput 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 calling select() 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 used
PQsetnonblocking 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 executing select(). This allows an
application using a blocking connection to wait for either backend responses or
other conditions.
If the result of select() indicates that data can be read from
the backend socket, then PQconsumeInput should be called to read the
data; after which, PQisBusy, PQgetResult,
and/or PQnotifies can be used to process the response.
Nonblocking connections (that have used PQsetnonblocking)
should not use select() until PQflush
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 of PQrequestCancel,
the application must continue with the normal result-reading
sequence using PQgetResult. 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.