Let’s take a look at how we can execute a query in asynchronous mode. We will use a pair of functions that PQexec uses to do its work: PQsendQuery and PQgetResult.
The idea is that we call PQsendQuery to send our SQL to the server. Once it is on its way, we can get on with other things.
int PQsendQuery(PGconn *conn, const char *query);
PQsendQuery returns zero if the query is not dispatched, and sets an error condition we can retrieve with PQerrorMessage. Otherwise, it returns 1 to indicate successfully sending the query.
If the server responds immediately, the result set will be kept waiting for us until we are ready to retrieve it. When we are ready, we will call PQgetResult one or more times to retrieve the query results as they come in:
PGresult *PQgetResult(PGconn *conn);
PQgetResult will return a result set each time it’s called, until all the results from the active query have been returned. The result set may contain no tuples if no further data was immedi- ately available, but this does not mean that all the data has been received. At that point, PQgetResult returns a NULL pointer. Each of the result sets returned by PQgetResult must be cleared when we are finished with them by calling PQclear.
For this to work, we must make sure that neither PQsendQuery nor PQgetResult will block and leave our application waiting for them. To help with PQsendQuery, we can set the connec- tion itself to a nonblocking state by calling PQsetnonblocking:
int PQsetnonblocking(PGconn *conn, int arg);
To prevent PQsendQuery from blocking, we make a call to PQsetnonblocking with a nonzero argument. Then PQsendQuery will return an error if it would have otherwise blocked, and the application may try again at a later point.
PQsetnonblocking returns zero if successful, and -1 if there is a problem in changing the mode of the connection. We can check the blocking mode of a connection by calling PQisnonblocking:
int PQisnonblocking(const PGconn *conn);
We get a nonzero value if the connection is in a nonblocking mode.
The programming we need to run this nonblocking operation is something like this (async1.c):
#include <stdlib.h>
#include <libpq-fe.h>
void printTuples(PGresult *result) {
int r, n;
int nrows = PQntuples(result);
int nfields = PQnfields(result);
printf("number of rows returned = %d\n", nrows);
printf("number of fields returned = %d\n", nfields);
for(r = 0; r < nrows; r++) { for(n = 0; n < nfields; n++) printf(" %s = %s(%d),", PQfname(result, n), PQgetvalue(result, r, n), PQgetlength(result, r, n));
printf("\n");
} }
int main() {
PGresult *result;
PGconn *conn;
conn = PQconnectdb("");
if(PQstatus(conn) == CONNECTION_OK &&
PQsetnonblocking(conn,1) == 0) { printf("connection made\n");
PQsendQuery(conn, "SELECT * FROM customer");
while(result = PQgetResult(conn)) { printTuples(result);
PQclear(result);
} } else
printf("connection failed\n");
PQfinish(conn);
return EXIT_SUCCESS;
}
This program sends off a query, and then collects the results without blocking. As with the earlier example of using cursors, we now have no easy way of telling how many rows will be returned altogether, but this is rarely a problem in practice.
There are still some rare circumstances under which PQgetResult can block, one of them being while the back-end server is busy. There are ways around this, using some lower-level libpq functions. If you need to have very precise control over your connection, and you are executing very complex queries that will cause the server to be busy for significantly long periods, you can use the functions shown in Table 13-3.
Check out the PostgreSQL documentation that is included in the source code distribution or at http://www.postgresql.org for details on using the PQisBusy, PQconsumeInput, and PQflush functions.
If your application is using the select system call (not to be confused with the SQL SELECT statement) to react to read and write events on file descriptors or network sockets, you can include the PostgreSQL connection in the select, too. To do this, you must obtain the socket that is being used by the database connection. This can be obtained from PQsocket:
int PQsocket(const PGconn *conn);
Table 13-3. libpq Functions for Controlling Blocking
Function Description
int PQisBusy(PGconn *conn); Returns 1 if the current query is busy, and PQgetResult would block if it were called.
int PQflush(PGconn *conn); Tries to send any outstanding data that is waiting to go to the server. It returns zero if it successfully empties the queue, or it was already empty.
int PQconsumeInput(PGconn *conn); Transfers data waiting to be read on the database connection into the internal libpq data structures.
It is normally called by functions like PQexec, but you can make explicit calls to it when you need control over blocking behavior of your applications.
The socket will signal activity when there is back-end data to be processed, but there are a couple of conditions that must be met for this to work:
• There must be no data outstanding to be sent to the server. We can ensure this by calling PQflush until it returns zero.
• Data must be read from the connection by PQconsumeInput before calling PQgetResult.