27.4. Traitement des commandes asynchrones

La fonction PQexec est parfaite pour soumettre des commandes aux applications standards, synchrones. N�anmoins, elle a quelques d�ficiences qui peuvent �tre importantes pour certaines utilisateurs :

Les applications qui n'appr�cient pas ces limitations peuvent utiliser � la place les fonctions sous-jacentes � partir desquelles PQexec est construite : PQsendQuery et PQgetResult. Il existe aussi PQsendQueryParams et PQsendQueryPrepared, pouvant �tre utilis�es avec PQgetResult pour dupliquer les fonctionnalit�s de respectivement PQexecParams et PQexecPrepared.

PQsendQuery

Soumet une commande au serveur sans attendre le(s) r�sultat(s). 1 est renvoy� si la commande a �t� correctement envoy�e et 0 sinon (dans ce cas, utilisez la fonction PQerrorMessage pour obtenir plus d'informations sur l'�chec).

int PQsendQuery(PGconn *conn, const char *command);

Apr�s un appel r�ussi � PQsendQuery, appelez PQgetResult une ou plusieurs fois pour obtenir les r�sultats. PQsendQuery ne peut �tre appel�e de nouveau (sur la m�me connexion) tant que PQgetResult n'a pas renvoy� un pointeur nul, indiquant que la commande est termin�e.

PQsendQueryParams

Soumet une commande et des param�tres s�par�s au serveur sans attendre le(s) r�sultat(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);

Ceci est �quivalent � PQsendQuery sauf que les param�tres de requ�tes peuvent �tre sp�cifi�s s�par�ment de la cha�ne de requ�te. Les param�tres de la fonction sont g�r�s de fa�on identique � PQexecParams. Comme PQexecParams, PQsendQueryParams ne fonctionne pas pour les connexions utilisant le protocole 2.0 et ne permet qu'une seule commande dans la cha�ne de requ�te.

PQsendQueryPrepared

Envoie une requ�te pour ex�cuter une instruction pr�par�e, avec les param�tres donn�s, sans attendre le(s) r�sultat(s).

int PQsendQueryPrepared(PGconn *conn,
                        const char *stmtName,
                        int nParams,
                        const char * const *paramValues,
                        const int *paramLengths,
                        const int *paramFormats,
                        int resultFormat);

Cette fonction est similaire � PQsendQueryParams mais la commande � ex�cuter est sp�cifi�e en nommant une instruction pr�c�demment pr�par�e au lieu de donner une cha�ne contenant la requ�te. Les param�tres de la fonction sont g�r�s de fa�on identique � PQexecPrepared. Comme PQexecPrepared, PQsendQueryPrepared ne fonctionne pas pour les connexions utilisant le protocole 2.0.

PQgetResult

Attend le prochain r�sultat d'un appel pr�c�dent � PQsendQuery, PQsendQueryParams ou PQsendQueryPrepared, et le renvoie. Un pointeur nul est renvoy� quand la commande est termin�e et qu'il n'y aura plus de r�sultats.

PGresult *PQgetResult(PGconn *conn);

PQgetResult doit �tre appel�e de fa�on r�p�t�e jusqu'� ce qu'elle retourne un pointeur nul indiquant que la commande s'est termin�e. (Si elle est appel�e � un moment o� aucune commande n'est active, PQgetResult renvoie un pointeur nul d�s la premi�re fois.) Chaque r�sultat non nul provenant de PQgetResult doit �tre trait� en utilisant les m�mes fonctions d'acc�s � PGresult que celles pr�c�demment d�crites. N'oubliez pas de lib�rer chaque objet r�sultat avec PQclear une fois que vous en avez termin�. Notez que PQgetResult bloque seulement si la commande est active et que les donn�es n�cessaires en r�ponse n'ont pas encore �t� lues par PQconsumeInput.

Utiliser PQsendQuery et PQgetResult r�sout un des probl�mes de PQexec : si une cha�ne de commande contient plusieurs commandes SQL, les r�sultats de ces commandes peuvent �tre obtenus individuellement. (Ceci permet une forme simple de traitement en parall�le : le client peut g�rer les r�sultats d'une commande alors que le serveur travaille sur d'autres requ�tes de la m�me cha�ne de commandes.) N�anmoins, appeler PQgetResult cause toujours un blocage du client jusqu'� la fin de la prochaine commande SQL. Ceci est �vitable en utilisant proprement deux fonctions suppl�mentaires :

PQconsumeInput

Si une entr�e est disponible � partir du serveur, elle est consomm�e.

int PQconsumeInput(PGconn *conn);

PQconsumeInput renvoie normalement 1 ce qui indique <<�aucune erreur�>>, mais renvoie z�ro s'il y a eu une erreur (auquel cas PQerrorMessage peut �tre consult�e). Notez que le r�sultat ne dit pas si des donn�es ont �t� r�cup�r�es en entr�es. Apr�s avoir appel� PQconsumeInput, l'application doit v�rifier PQisBusy et/ou PQnotifies pour voir si leur �tat a chang�.

PQconsumeInput peut �tre appel�e m�me si l'application n'est pas encore pr�par� � g�rer un r�sultat ou une notification. La fonction lit les donn�es disponibles et les sauvegarde dans un tampon, ce qui remet � z�ro l'indication qu'une lecture de select() est disponible. L'application peut donc utiliser PQconsumeInput pour effacer la condition select() imm�diatement, puis examiner les r�sultats plus tard � sa guise.

PQisBusy

Renvoie 1 si une commande est occup�e, c'est-�-dire si PQgetResult bloquerait en attendant une entr�e. Un z�ro indique que PQgetResult peut �tre appel�e avec l'assurance de ne pas �tre bloqu�e.

int PQisBusy(PGconn *conn);

PQisBusy ne tente pas elle-m�me de lire les donn�es � partir du serveur ; du coup, PQconsumeInput doit �tre appel�e d'abord, sans quoi l'�tat occup� ne s'arr�tera jamais.

Une application typique de l'utilisation des ces fonctions a une boucle principale utilisant select() ou poll() pour attendre toutes les conditions auxquelles elle doit r�pondre. Une des conditions est que des donn�es sont disponibles � partir du serveur, ce qui signifie pour select() des donn�es lisibles sur le descripteur de fichier identifi� par PQsocket. Lorsque la boucle principale d�tecte la disponibilit� de donn�es, elle doit appeler PQconsumeInput pour lire l'ent�te. Elle peut ensuite appeler PQisBusy puis PQgetResult si PQisBusy renvoie false (0). Elle peut aussi appeler PQnotifies pour d�tecter les messages NOTIFY (voir Section 27.6).

Un client qui utilise PQsendQuery/PQgetResult peut aussi tenter d'annuler une commande en cours de traitement par le serveur.

PQrequestCancel

Demande au serveur d'abandonner le traitement de la commande en cours.

int PQrequestCancel(PGconn *conn);

Le code de retour est 1 si la demande d'annulation a �t� correctement envoy�e et 0 sinon. (Si non, PQerrorMessage dira pourquoi.) Un envoi correct ne garantit pas que la demande aura un effet. Quel que soit le code de retour de PQrequestCancel, l'application doit continuer la s�quence normale de lecture/r�sultat en utilisant PQgetResult. Si l'annulation est r�elle, la commande en cours se termine rapidement et renvoie un r�sultat d'erreur. Si l'annulation �choue (par exemple parce que le serveur a d�j� termin� l'ex�cution de la commande), aucun r�sultat n'est visible.

Notez que si la commande en cours fait partie d'un bloc de transaction, l'annulation est effective pour la transaction compl�te.

PQrequestCancel peut �tre appel�e en toute s�curit� depuis un gestionnaire de signaux. Donc, il est aussi possible de l'utiliser avec des PQexec, si la d�cision d'annuler doit �tre faite dans un gestionnaire de signaux. Par exemple, psql appelle PQrequestCancel � partir du gestionnaire du signal SIGINT, autorisant du coup l'annulation interactive des commandes qu'il envoie via PQexec.

En utilisant les fonctions d�crites ci-dessus, il est possible d'�viter le blocage pendant l'attente de donn�es du serveur. N�anmoins, il est toujours possible que l'application se bloque en attendant l'envoi vers le serveur. C'est relativement peu fr�quent mais cela peut arriver si de tr�s longues commandes SQL ou donn�es sont envoy�es. (C'est bien plus probable si l'application envoie des donn�es via COPY IN.) Pour emp�cher cette possibilit� et r�ussir des op�rations de bases de donn�es totalement non bloquantes, les fonctions suppl�mentaires suivantes peuvent �tre utilis�es.

PQsetnonblocking

Initialise le statut non bloquant de la connexion.

int PQsetnonblocking(PGconn *conn, int arg);

Initialise l'�tat de la connexion � non bloquant si arg vaut 1 et � bloquant si arg vaut 0. Renvoie 0 si OK, -1 en cas d'erreur.

Dans l'�tat non bloquant, les appels � PQsendQuery, PQputline, PQputnbytes, et PQendcopy ne bloquent pas mais renvoient � la place une erreur s'ils ont besoin d'�tre de nouveau appel�s.

Notez que PQexec n'honore pas le mode non bloquant ; si elle est appel�e, elle agira d'une fa�on bloquante malgr� tout.

PQisnonblocking

Renvoie le statut bloquant ou non de la connexion � la base de donn�es.

int PQisnonblocking(const PGconn *conn);

Renvoie 1 si la connexion est en mode non bloquant, 0 dans le cas contraire.

PQflush

Tente de vider les donn�es en attente d'envoi vers le serveur. Renvoie 0 en cas de succ�s (ou si la queue d'envoi est vide), -1 en cas d'�chec quelle que soit la raison ou 1 si elle n'a pas encore pu envoyer toutes les donn�es dans la queue d'envoi (ce cas arrive seulement si la connexion est non bloquante).

int PQflush(PGconn *conn);

Apr�s avoir envoy� une commande ou des donn�es dans une connexion non bloquante, appelez PQflush. Si elle renvoie 1, attendez que la socket soit disponible en �criture et appelez-la de nouveau ; r�p�tez cela jusqu'� ce qu'elle renvoie 0. Une fois que PQflush renvoie 0, attendez que la socket soit disponible en lecture puis lisez la r�ponse comme d�crit ci-dessus.