La función PQexec es adecuada para emitir consultas en aplicaciones síncronas sencillas. Sin embargo, tiene una porción de definciencias importantes:
PQexec espera hasta que se completa la consulta. La aplicación puede tener otro trabajo para hacer (como por ejemplo mantener una interfaz de usuario), en cuyo caso no se querrá bloquear esperando la respuesta.
Una vez que el control se pasa a PQexec, la aplicación cliente tiene muy dificil intentar cancelar la consulta en curso. (Se puede hacer con un manipulador de señales, pero no de otra forma).
PQexec sólo puede devolver una estructura PGresult. Si la cadena de la consulta emitida contiene múltiples comands SQL, se perderán todos excepto el último.
Las aplicaciones que no se quieren encontrar con estas limitaciones, pueden utilizar en su lugar las funciones que subyacen bajo PQexec: PQsendQuery y PQgetResult.
Para los programas antiguos que utilizaban esta funcionalidad utilizando PQputline y PQputnbytes y esperaban bloqueados el envío de datos del servidor, se añadió la función PQsetnonblocking.
Las aplicaciones antíguas pueden rechazar el uso de PQsetnonblocking y mantener el comportamiento anterior potencialmente bloquante. Los programas más nuevos pueden utilizar PQsetnonblocking para conseguir una conexión con el servidor completamente no bloqueante.
PQsetnonblocking fija el estado de la conexión a no bloqueante.
int PQsetnonblocking(PGconn *conn) |
Cuando una conexión a una base de datos se ha fijado como no bloqueante, y se llama a PQexec, se cambiará el estado temporalmente a bloqueante, hasta que se completa la ejecución de PQexec.
Se espera que en el próximo futuro, la mayoría de libp se haga segura para la funcionalida de PQsetnonblocking.
PQisnonblocking Devuelve la situación de bloqueante o no de la conexión a la base de datos.
int PQisnonblocking(const PGconn *conn) |
PQsendQuery Envía una consulta a Postgres sin esperar los resultados. Devuelve TRUE si la consulta se despachó correctamente, y FALSE si no fue así (en cuyo caso, utilice PQerrorMessage para obtener más información sobre el fallo).
int PQsendQuery(PGconn *conn, const char *query); |
PQgetResult Espera el siguiente resultado de una ejecución previa de PQsendQuery, y lo devuelve. Se devuelve NULL cuando la consulta está completa y ya no habrá más resultados.
PGresult *PQgetResult(PGconn *conn); |
Utilizando PQsendQuery y PQgetResult se resuelve uno de los problemas de PQexec: Si una cadena de consulta contiene múltiples comandos SQL, los resultados de esos comandos se pueden obtener individualmente. (Esto permite una forma sencilla de procesamiento paralelo: la aplicación cliente puede estar manipulando los resultados de una consulta mientras el servidor sigue trabajando sobre consultas posteriores de la misma cadena de consulta). Sin embargo, la llamada a PQgetResult seguirá probocando que el cliente quede bloqueado hasta que el servidor complete el siguiente comando SQL de la cadena. Esto se puede impedir con el uso adecuado de tres funciones más:
PQconsumeInput Si hay una entrada disponible desde el servidor, la recoge.
int PQconsumeInput(PGconn *conn); |
PQconsumeInput se puede llamar incluso si la aplicación aún no está preparada para recibir un resultado o una notificación. La rutina leerá los datos disponibles y los situará en un almacenamiento intermedio, probocando así una indicación de preparado para leer a la función select(2) para que continúe. La aplicación puede por ello utilizar PQconsumeInput para limpiar la condición select inmediatamente, y examinar después los resultado tranquilamente.
PQisBusy Devuelve 1 si una consulta está ocupada, es decir, si PQgetResult se quedaría bloqueada esperando una entrada. Un 0 indica que se puede llamar a PQgetResult con la seguridad de no bloquear.
int PQisBusy(PGconn *conn); |
PQflush Intenta lanzar cualquier dato encolado al servidor, y devuelve 0 si lo consigue (o si la cola de envío está vacía) y EOF si ha fallado por algún motivo.
int PQflush(PGconn *conn); |
PQsocket Obtiene el número descriptor de fichero para el socket de conexión con el servidor. Un descriptor válido sera >= 0; un resultado de indica que no hay actualmente ninguna conexión con el servidor abierta.
int PQsocket(const PGconn *conn); |
Las conexiónes no bloqueantes (que han utilizado PQsetnonblocking) no deberían utilizar select hasta que PQflush haya devuelto 0 indicando que no quedan datos almacenados esperando ser enviados al servidor.
Una aplicación cliente típica que utilice estas funciones tendrá un bucle principal que utiliza select(2) para esperar todas las condiciones a las que debe responder. Una de estas condiciones será la entrada disponible desde el servidor, lo que en terminos de select son datos legibles en el descriptor de fichero identificado por PQsocket. Cuando el bucle principal detecta que hay preparada una entrada, debería llamar a PQconsumeInput para leer la entrada. Puede después llamar a PQisBusy, seguido de PQgetResult si PQisBusy devuelve falso (0). Puede llamar también a PQnotifies para detectar mensajes NOTIFY (ver "Notificación Asíncrona", más abajo).
Una aplicación cliente que utilice PQsendQuery/PQgetResult también puede intentar cancelar una consulta que aún se esté procesando en el servidor.
PQrequestCancel Requiere de Postgres que abandone el procesado de la consulta actual.
int PQrequestCancel(PGconn *conn); |
Nótese que si la consulta forma parte de una transacción, la cancelación abortará la transacción completa.
PQrequestCancel se puede invocar de modo seguro desde un manipulador de señales. De esta forma, se puede utilizar en conjunción con PQexec plano, si la decisión de cancelar se puede tomar en un manipulador de señales. Por ejemplo, psql invoca a PQrequestCancel desde un manipulador de la señal SIGINT, permitiendo de este modo la cancelación interactiva de consultas que él gestiona a través de PQexec. Observese que PQrequestCancel no tendrá efecto si la conexión no está abierta en ese momento, o si el servidor no está procesando una consulta.