Documentation PostgreSQL 7.4.29 | ||||
---|---|---|---|---|
Précédent | Arrière rapide | Chapitre 27. libpq - Bibliothèque C | Avance rapide | Suivant |
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 :
PQexec
attend que la commande se termine. L'application
pourrait avoir d'autres travaux à réaliser (comme le rafraichissement de
l'interface utilisateur), auquel cas il ne voudra pas être bloqué 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.
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.
Précédent | Sommaire | Suivant |
Fonctions de commandes d'exécution | Niveau supérieur | Interface à chemin rapide |