La fonction PQexec
est adéquate pour soumettre des
commandes aux applications standards, synchrones. Néanmoins, elle a quelques
défauts pouvant être d'importance à certains utilisateurs :
PQexec
attend que la commande se termine. L'application
pourrait avoir du travail ailleurs (comme le rafraîchissement de
l'interface utilisateur), auquel cas elle ne voudra pas être bloquée en attente
de la réponse.
Comme l'exécution de l'application cliente est suspendue en attendant le résultat, il est difficile pour l'application de décider qu'elle voudrait annuler la commande en cours (c'est possible avec un gestionnaire de signaux mais pas autrement).
PQexec
ne peut renvoyer qu'une structure
PGresult
. Si la chaîne de commande soumise contient
plusieurs commandes SQL, toutes les structures
PGresult
sont annulées par
PQexec
, sauf la dernière.
PQexec
récupère toujours le résultat entier de la
commande, le mettant en cache dans une seule structure
PGresult
. Bien que cela simplifie la logique
de la gestion des erreurs pour l'application, cela peut ne pas se
révéler pratique pour les résultats contenant de nombreuses lignes.
Les applications qui n'apprécient pas ces limitations peuvent utiliser à la
place les fonctions sous-jacentes à partir desquelles PQexec
est construit : PQsendQuery
et PQgetResult
. Il
existe aussi PQsendQueryParams
, PQsendPrepare
, PQsendQueryPrepared
, PQsendDescribePrepared
et PQsendDescribePortal
, pouvant être utilisées avec PQgetResult
pour dupliquer les fonctionnalités de
respectivement PQexecParams
, PQprepare
, PQexecPrepared
, PQdescribePrepared
et PQdescribePortal
.
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 dans le cas
contraire (auquel 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 pas être appelé
de nouveau (sur la même connexion) tant que
PQgetResult
ne renvoie pas de pointeur NULL,
indiquant que la commande a terminé.
Dans le mode pipeline, les chaînes de commande contenant plus d'une commande SQL sont interdites.
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 à partir de la chaîne de
requête. Les paramètres de la fonction sont gérés de façon identique à
PQexecParams
.
PQsendPrepare
Envoie une requête pour créer une instruction préparée avec les paramètres donnés et redonne la main sans attendre la fin de son exécution.
int PQsendPrepare(PGconn *conn, const char *stmtName, const char *query, int nParams, const Oid *paramTypes);
Ceci est la version asynchrone de PQprepare
: elle
renvoie 1 si elle a été capable d'envoyer la requête, 0 sinon. Après un
appel terminé avec succès, appelez PQgetResult
pour
déterminer si le serveur a créé avec succès l'instruction préparée. Les
paramètres de la fonction sont gérés de façon identique à
PQprepare
.
PQsendQueryPrepared
Envoie une requête pour exécuter une instruction préparée avec des 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);
Ceci est similaire à PQsendQueryParams
mais la
commande à exécuter est spécifiée en nommant une instruction
préparée précédente 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
.
PQsendDescribePrepared
Soumet une requête pour obtenir des informations sur l'instruction préparée indiquée sans attendre sa fin.
int PQsendDescribePrepared(PGconn *conn, const char *stmtName);
Ceci est la version asynchrone de PQdescribePrepared
:
elle renvoie 1 si elle a été capable d'envoyer la requête, 0 dans le cas
contraire. Après un appel réussi, appelez PQgetResult
pour obtenir les résultats.
Les paramètres de la fonction sont gérés de façon identique à
PQdescribePrepared
.
PQsendDescribePortal
Soumet une requête pour obtenir des informations sur le portail indiqué sans attendre la fin de la commande.
int PQsendDescribePortal(PGconn *conn, const char *portalName);
Ceci est la version asynchrone de PQdescribePortal
:
elle renvoie 1 si elle a été capable d'envoyer la requête, 0 dans le cas
contraire. Après un appel réussi, appelez PQgetResult
pour obtenir les résultats.
Les paramètres de la fonction sont gérés de façon identique à
PQdescribePortal
.
PQgetResult
Attend le prochain résultat d'un appel précédent à
PQsendQuery
,
PQsendQueryParams
,
PQsendPrepare
,
PQsendQueryPrepared
,
PQsendDescribePrepared
,
PQsendDescribePortal
, ou
PQpipelineSync
, et le renvoie. Un pointeur
NULL 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é de façon répétée
jusqu'à ce qu'il retourne un pointeur NULL indiquant que la commande
s'est terminée. (Si appelée à un moment où aucune commande n'est
active, PQgetResult
renverra juste immédiatement un
pointeur NULL). Chaque résultat non NULL provenant de
PQgetResult
devrait ê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
quand vous en avez terminé.
Notez que PQgetResult
bloquera seulement si la
commande est active et que les données nécessaires en réponse n'ont
pas encore été lues par PQconsumeInput
.
Dans le mode pipeline, PQgetResult
retournera
normalement jusqu'à l'arrivée d'une erreur ; pour les requêtes
suivantes envoyées après celle qui a causé l'erreur jusqu'au point
suivant de synchronisation (ce dernier exclus), un résultat spécial de
type PGRES_PIPELINE_ABORTED
sera renvoyé, et un
pointeur nul sera renvoyé après cela. Quand le pointde synchronisation
du pipeline est atteint, un résultat de type
PGRES_PIPELINE_SYNC
sera renvoyé. Le résultat de la
requête suivant le point de synchronisation suit immédiatement
(autrement dit aucun pointeur nul n'est renvoyé après le point de
synchronisation).
Même quand PQresultStatus
indique une erreur
fatale, PQgetResult
doit être appelé
jusqu'à ce qu'il renvoie un pointeur NULL pour permettre à
libpq de traiter l'information sur
l'erreur correctement.
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 simple
forme 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).
Une autre fonctionnalité fréquemment demandée, pouvant être obtenue avec
PQsendQuery
et PQgetResult
est
la récupération d'un gros résultat une ligne à la fois. Ceci est discuté
dans Section 34.6.
Néanmoins, appeler PQgetResult
causera 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 l'entrée est disponible à partir du serveur, la consomme.
int PQconsumeInput(PGconn *conn);
PQconsumeInput
renvoie normalement 1 indiquant
« aucune erreur », mais renvoie zéro s'il y a eu une erreur (auquel
cas PQerrorMessage
peut être consulté). Notez que le
résultat ne dit pas si des données ont été récupérées en entrée. Après avoir
appelé PQconsumeInput
, l'application devrait 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ée à traiter un résultat ou une notification. La fonction
lira les données disponibles et les sauvegardera dans un tampon indiquant
ainsi qu'une lecture d'un select()
est possible.
L'application peut donc utiliser PQconsumeInput
pour
effacer la condition select()
immédiatement, puis
examiner les résultats à loisir.
PQisBusy
Renvoie 1 si une commande est occupée, c'est-à-dire que
PQgetResult
bloque en attendant une entrée. Un zéro
indique que PQgetResult
peut être appelé avec
l'assurance de ne pas bloquer.
int PQisBusy(PGconn *conn);
PQisBusy
ne tentera pas lui-même de lire les données à
partir du serveur ; du coup, PQconsumeInput
doit être
appelé d'abord ou l'état occupé ne prendra jamais fin.
Une application typique de l'utilisation de ces fonctions aura une boucle
principale utilisant select()
ou poll()
pour
attendre que toutes les conditions soient remplies. Une des conditions
sera la disponibilité des données à partir du serveur, ce qui signifie des données
lisibles pour select()
sur le descripteur de
fichier identifié par PQsocket
. Lorsque la boucle
principale détecte la disponibilité de données, elle devrait appeler
PQconsumeInput
pour lire l'en-tête. Elle peut ensuite appeler
PQisBusy
suivi par PQgetResult
si
PQisBusy
renvoie false (0). Elle peut aussi appeler
PQnotifies
pour détecter les messages NOTIFY
(voir la Section 34.9).
Un client qui utilise
PQsendQuery
/PQgetResult
peut aussi
tenter d'annuler une commande en cours de traitement par le
serveur ; voir la Section 34.7. Mais quelle que soit la valeur
renvoyée par PQcancel
, l'application doit continuer avec
la séquence normale de lecture du résultat en utilisant
PQgetResult
. Une annulation réussie causera simplement une
fin plus rapide de la commande.
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 (mais c'est bien plus probable si
l'application envoie des données via COPY IN
). Pour
éviter cette possibilité et parvenir à 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 réussis à PQsendQuery
, PQputline
, PQputnbytes
, PQputCopyData
et
PQendcopy
ne bloqueront pas ; leurs
modifications seront stockées dans un tampon local de sortie jusqu'à ce
qu'elles soient vidées sur disque. Les appels échoués renverront une
erreur et devront être tentés de nouveau.
Notez que PQexec
n'honore pas le mode non
bloquant ; s'il est appelé, il agira d'une façon bloquante malgré tout.
PQisnonblocking
Renvoie le statut bloquant de la connexion à la base de données.
int PQisnonblocking(const PGconn *conn);
Renvoie 1 si la connexion est en mode non bloquant, 1 dans le cas contraire.
PQflush
Tente de vider les données des queues de sortie du 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 s'il a été incapable d'envoyer encore 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
. S'il renvoie 1, attendez que la socket
devienne prête en lecture ou en écriture. Si elle est prête en écriture,
appelez de nouveau PQflush
. Si elle est prête en lecture,
appelez PQconsumeInput
, puis appelez
PQflush
. Répétez jusqu'à ce que
PQflush
renvoie 0. (Il est nécessaire de vérifier si elle
est prête en lecture, et de vidanger l'entrée avec
PQconsumeInput
car le serveur peut bloquer en essayant
d'envoyer des données, par exemple des messages NOTICE, et ne va pas lire nos
données tant que nous n'avons pas lu les siennes.) Une fois que
PQflush
renvoie 0, attendez que la socket soit disponible
en lecture, puis lisez la réponse comme décrit ci-dessus.