44.2. Flux de messages
Cette section décrit le flux des messages et la sémantique de chaque
type de message (les détails concernant la représentation exacte de
chaque message apparaissent dans Section 44.4, « Formats de
message »). il existe différents sous-protocoles en fonction
de l'état de la connexion : lancement, requête, appel de fonction,
COPY et clôture. Il existe aussi des provisions spéciales pour les
opérations asynchrones (incluant les réponses aux notifications et
les annulations de commande), qui peuvent arriver à tout moment après
la phase de lancement.
44.2.1. Lancement
Pour débuter une session, un client ouvre une connexion au serveur
et envoie un message de démarrage. Ce message inclut les noms de
l'utilisateur et de la base de données à laquelle le client
souhaite se connecter ; il identifie aussi la version particulière
du protocole à utiliser (optionnellement, le message de démarrage
peut inclure des précisions supplémentaires pour les paramètres
d'exécution). Le serveur utilise ces informations et le contenu des
fichiers de configuration (tels que pg_hba.conf) pour déterminer si la connexion est
acceptable et quelle éventuelle authentification supplémentaire est
requise.
Le serveur envoie ensuite le message de demande d'authentification
approprié, auquel le client doit répondre avec le message de
réponse d'authentification adapté (tel un mot de passe). En
principe, le cycle demande/réponse d'authentification peut requérir
plusieurs itérations mais aucune des méthodes d'authentification
actuelles n'utilise plus d'un cycle. Avec certaines méthodes,
aucune réponse du client n'est nécessaire aucune demande
d'authentification n'est alors effectuée.
Le cycle d'authentification se termine lorque le serveur rejette la
tentative de connexion (ErrorResponse) ou l'accepte
(AuthenticationOk).
Les messages possibles du serveur dans cette phase sont :
-
errorresponse
-
La tentative de connexion a été rejetée. Le serveur ferme
immédiatement la connexion.
-
authenticationok
-
L'échange d'authentification s'est terminé avec succès.
-
authenticationkerberosv5
-
Le client doit alors prendre part à un dialogue
d'authentification Kerberos V5 (spécification Kerberos, non
décrite ici) avec le serveur. En cas de succès, le serveur
répond AuthenticationOk, ErrorResponse sinon.
-
authenticationcleartextpassword
-
Le client doit alors envoyer un PasswordMessage contenant le
mot de passe en clair. Si le mot de passe est correct, le
serveur répond AuthenticationOk, ErrorResponse sinon.
-
authenticationcryptpassword
-
Le client doit alors envoyer un PasswordMessage contenant le
mot de passe chiffré à l'aide de crypt(3), en utilisant le
composant salt de deux caractères spécifié dans le message
AuthenticationCryptPassword. Si le mot de passe est correct,
le serveur répond AuthenticationOk, ErrorResponse sinon.
-
authenticationmd5password
-
Le client doit alors envoyer un PasswordMessage contenant le
mot de passe chiffré à l'aide de MD5, en utilisant le
composant salt de quatre caractères spécifié dans le message
AuthenticationMD5Password. Si le mot de passe est correct, le
serveur répond AuthenticationOk, ErrorResponse sinon.
-
authenticationscmcredential
-
Cette réponse est possible uniquement pour les connexions
locales de domaine Unix sur les plateformes qui supportent
les messages de légitimation SCM. Le client doit fournir un
message de légitimation SCM, puis envoyer une donnée d'un
octet. Le contenu de cet octet importe peu ; il n'est utilisé
que pour s'assurer que le serveur attend assez longtemps pour
recevoir le message de légitimation. Si la légitimation est
acceptable, le serveur répond AuthenticationOk, ErrorResponse
sinon.
Si le client ne supporte pas la méhode d'authentification demandée
par le serveur, il doit immédiatement fermer la connexion.
Après la réception du message AuthenticationOk, le client attend
d'autres messages du serveur. Au cours de cette phase, un processus
serveur est lancé et le client est simplement en attente. Il est
encore possible que la tentative de lancement échoue
(ErrorResponse) mais, dans la plupart des cas, le serveur enverra
les messages ParameterStatus, BackendKeyData et enfin
ReadyForQuery.
Durant cette phase, le serveur tentera d'appliquer tous les
paramètres d'exécution supplémentaires qui ont été fournis par le
message de lancement. En cas de succès, ces valeurs deviennent les
valeurs par défaut de la session. Une erreur engendre ErrorResponse
et déclenche la sortie.
Les messages possibles du serveur dans cette phase sont :
-
backendkeydata
-
Ce message fournit une clé secrète que le client doit
conserver s'il souhaite envoyer des annulations de requêtes
par la suite. Le client ne devrait pas répondre à ce message,
mais continuer à attendre un message ReadyForQuery.
-
parameterstatus
-
Ce message informe le client de la configuration actuelle
(initiale) des paramètres du serveur, tels client_encoding ou datestyle. le client peut ignorer ce message
ou enregistrer la configuration pour ses besoins futurs ;
voir Section 44.2.6,
« Opérations asynchrones » pour plus de
détails. le client ne devrait pas répondre à ce message mais
continuer à attendre un message ReadyForQuery.
-
readyforquery
-
Le lancement est terminé. Le client peut dès lors envoyer des
commandes.
-
errorresponse
-
Le lancement a échoué. La connexion est fermée après l'envoi
de ce message.
-
noticeresponse
-
Un message d'avertissement a été envoyé. Le client devrait
afficher ce message mais continuer à attendre un
ReadyForQuery ou un ErrorResponse.
Le même message ReadyForQuery est envoyé à chaque cycle de
commande. En fonction des besoins de codage du client, il est
possible de considérer ReadyForQuery comme le début d'un cycle de
commande, ou de le considérer comme terminant la phase de lancement
et chaque cycle de commande.
44.2.2. Requête simple
Un cycle de requête simple est initié par le client qui envoie un
message Query au serveur. Le message inclut une commande SQL (ou
plusieurs) exprimée comme une chaîne texte. Le serveur envoie,
alors, un ou plusieurs messages de réponse dépendant du contenu de
la chaîne représentant la requête et enfin un message
ReadyForQuery. ReadyForQuery informe le client qu'il peut envoyer
une nouvelle commande. Il n'est pas nécessaire que le client
attende ReadyForQuery avant de lancer une autre commande mais le
client prend alors la responsabilité de ce qui arrive si la
commande précédente échoue et que les commandes suivantes, déjà
lancées, réussissent.
Les messages de réponse du serveur sont :
-
commandcomplete
-
Commande SQL terminée normalement.
-
copyinresponse
-
Le serveur est prêt à copier des données du client vers une
table voir Section 44.2.5,
« Opérations copy ».
-
copyoutresponse
-
Le serveur est prêt à copier des données d'une table vers le
client ; voir Section 44.2.5,
« Opérations copy ».
-
rowdescription
-
Indique que des lignes vont être envoyées en réponse à une
requête
select
,
fetch
... Le
contenu de ce message décrit le placement des colonnes dans
les lignes. Le contenu est suivi d'un message DataRow pour
chaque ligne envoyée au client.
-
datarow
-
Un des ensembles de lignes retournés par une requête
select
,
fetch
...
-
emptyqueryresponse
-
Une chaîne de requête vide a été reconnue.
-
errorresponse
-
Une erreur est survenue.
-
readyforquery
-
Le traitement d'une requête est terminé. Un message séparé
est envoyé pour l'indiquer parce qu'il se peut que la chaîne
de la requête contienne plusieurs commandes SQL.
CommandComplete marque la fin du traitement d'une commande
SQL, pas de la chaîne complète. ReadyForQuery sera toujours
envoyé que le traitement se termine avec succès ou non.
-
noticeresponse
-
Un message d'avertissement concernant la requête a été
envoyé. Les avertissements sont complémentaires des autres
réponses, le serveur continuera à traiter la commande.
La réponse à une requête
select
(ou à d'autres requêtes,
telles
explain
ou
show
, qui retournent
des ensembles de données) consiste normalement en un
RowDescription, plusieurs messages DataRow (ou aucun) et pour finir
un CommandComplete.
copy
depuis ou vers le client
utilise un protocole spécial décrit dans Section 44.2.5,
« Opérations copy ». tous les autres types de
requêtes produisent uniquement un message CommandComplete.
Puisqu'une chaîne de caractères peut contenir plusieurs requêtes
(séparées par des points virgules), il peut y avoir plusieurs
séquences de réponses avant que le serveur ne finisse de traiter la
chaîne. ReadyForQuery est envoyé lorsque la chaîne complète a été
traitée et que le serveur est prêt à accepter une nouvelle chaîne
de requêtes.
Si une chaîne de requêtes complètement vide est reçue (aucun
contenu autre que des espaces fines), la réponse sera
EmptyQueryResponse suivie de ReadyForQuery.
En cas d'erreur, ErrorResponse est envoyé suivi de ReadyForQuery.
Tous les traitements suivants de la chaîne sont annulés par
ErrorResponse (quelque soit le nombre de requêtes restant à
traiter). Ceci peut survenir au milieu de la séquence de messages
engendrés par une requête individuelle.
En mode de requêtage simple, les valeurs récupérées sont toujours
au format texte, sauf si la commande est un
fetch
sur un curseur déclaré avec
l'option binary. dans ce cas, les valeurs
récupérées sont au format binaire. Les codes de format donnés dans
le message RowDescription indiquent le format utilisé.
La planification de requêtes pour des instructions préparées
survient lorsque le message Parse est reçu. Si une requête sera
exécuté de façon répété avec différents paramètres, il pourrait
être bénéfique d'envoyer un seul message Parse contenant une
requête avec paramètres, suivie de plusieurs messages Bind et
Execute. Ceci évitera de planifier de nouveau la requête pour
chaque exécution.
L'instruction préparée non nommée est planifiée lors du traitement
de Parse si le message Parse ne définit aucun paramètre. Mais s'il
existe des paramètres, la planification de la requête est repoussée
jusqu'à ce que le premier message Bind de cette instruction est
reçu. Le planificateur considérera les valeurs réelles des
paramètres fournies dans le message Bind lors de la planification
de la requête.
Note
Les plans de requêtes générés à partir d'une requête avec
paramètres pourraient être moins efficaces que les plans de
requêtes générés à partir d'une requête équivalente dont les
valeurs de paramètres réelles ont été placées. Le planificateur
de requêtes ne peut pas prendre les décisions suivant les
valeurs réelles des paramètres (par exemple, la sélectivité de
l'index) lors de la planification d'une requête avec paramètres
affectée à un objet instruction préparée nommée. La pénalité
possible est évitée lors de l'utilisation d'une instruction non
nommée car elle n'est pas planifiée jusqu'à ce que des valeurs
réelles de paramètres soient disponibles.
Si un autre Bind référençant l'objet instruction préparée non
nommée est reçu, la requête n'est pas de nouveau planifiée. Les
valeurs de paramètres utilisées dans le premier message Bind
pourrait produire un plan de requête qui est seulement efficace
pour un sous-ensemble des valeurs de paramètres possibles. Pour
forcer une nouvelle planification de la requête pour un
ensemble nouveau de paramètres, envoyez un autre message Parse
pour remplacer l'objet instruction préparée non nommée.
Un client doit être préparé à accepter des messages ErrorResponse
et NoticeResponse quand bien même il s'attendrait à un autre type
de message. Voir aussi Section 44.2.6,
« Opérations asynchrones » concernant les messages
que le client pourrait engendrer du fait d'événements extérieurs.
La bonne pratique consiste à coder les clients dans un style
machine-état qui acceptera tout type de message à tout moment
plutôt que de parier sur la séquence exacte des messages.
Le protocole de requête étendu divise le protocole de requêtage
simple décrit ci-dessus en plusieurs étapes. Les résultats des
étapes de préparation peuvent être réutilisés plusieurs fois pour
plus d'efficacité. De plus, des fonctionnalités supplémentaires
sont disponibles, telles que la possibilité de fournir les valeurs
des données comme des paramètres séparés au lieu d'avoir à les
insérer directement dans une chaîne de requêtes.
Dans le protocole étendu, le client envoie tout d'abord un message
Parse qui contient une chaîne de requête, optionnellement quelques
informations sur les types de données aux emplacements des
paramètres, et le nom de l'objet de destination d'une instruction
préparée (une chaîne vide sélectionne l'instruction préparée sans
nom). La réponse est soit ParseComplete soit ErrorResponse. Les
types de données des paramètres peuvent être spécifiés par l'OID ;
dans le cas contraire, l'analyseur tente d'inférer les types de
données de la même façon qu'il le ferait pour les constantes
chaînes littérales non typées.
Note
La chaîne contenue dans un message Parse ne peut pas inclure
plus d'une instruction SQL, sinon une erreur de syntaxe est
rapportée. Cette restriction n'existe pas dans le protocole de
requête simple, mais est présente dans le protocole étendu. En
effet, permettre aux instructions préparées ou aux portails de
contenir de multiples commandes compliquerait inutilement le
protocole.
En cas de succès de sa création, une instruction préparée nommée
dure jusqu'à la fin de la session courante, sauf si elle est
détruite explicitement. Une instruction préparée non nommée ne dure
que jusqu'à la prochaine instruction Parse spécifiant l'instruction
non nommée comme destination. Un simple message Query détruit
également l'instruction non nommée. Les instructions préparées
nommées doivent être explicitement closes avant de pouvoir être
redéfinies par un message Parse. Ce n'est pas obligatoire pour une
instruction non nommée. Il est également possible de créer des
instructions préparées nommées, et d'y accéder, en ligne de
commandes SQL à l'aide des instructions
prepare
et
execute
.
Dès lors qu'une instruction préparée existe, elle est déclarée
exécutable par un message Bind. Le message Bind donne le nom de
l'instruction préparée source (une chaîne vide désigne
l'instruction préparée non nommée), le nom du portail destination
(une chaîne vide désigne le portail non nommé) et les valeurs à
utiliser pour tout emplacement de paramètres présent dans
l'instruction préparée. L'ensemble des paramètres fournis doit
correspondre à ceux nécessaires à l'instruction préparée. Bind
spécifie aussi le format à utiliser pour toutes les données
renvoyées par la requête ; le format peut être spécifié
complètement ou par colonne. La réponse est, soit BindComplete,
soit ErrorResponse.
Note
Le choix entre sortie texte et binaire est déterminé par les
codes de format donnés dans Bind, quelque soit la commande SQL
impliquée. L'attribut binary dans les
déclarations du curseur n'est pas pertinent lors de
l'utilisation du protocole de requête étendue.
En cas de succès de sa création, un objet portail nommé dure
jusqu'à la fin de la transaction courante sauf s'il est
explicitement détruit. Un portail non nommé est détruit à la fin de
la transaction ou dès la prochaine instruction Bind spécifiant le
portail non nommé comme destination. Un simple message Query
détruit également le portail non nommé. Les portails nommés doivent
être explicitement fermés avant de pouvoir être redéfinis par un
message Bind. Cela n'est pas obligatoire pour le portail non nommé.
Il est également possible de créer des portails nommés, et d'y
accéder, en ligne de commandes SQL à l'aide des instructions
declare cursor
et
fetch
.
Dès lors qu'un portail existe, il peut être exécuté à l'aide d'un
message Execute. Ce message spécifie le nom du portail (une chaîne
vide désigne le portail non nommé) et un nombre maximum de lignes
de résultat (zéro signifiant la « récupération de toutes les lignes »). le nombre
de lignes de résultat a seulement un sens pour les portails
contenant des commandes qui renvoient des ensembles de lignes ;
dans les autres cas, la commande est toujours exécutée jusqu'à la
fin et le nombre de lignes est ignoré. Les réponses possibles
d'Execute sont les même que celles décrites ci-dessus pour les
requêtes lancées via le protocole de requête simple, si ce n'est
qu'Execute ne cause pas l'envoi de ReadyForQuery ou de
RowDescription.
Si Execute se termine avant la fin de l'exécution d'un portail (du
fait d'un nombre de lignes de résultats différent de zéro), il
enverra un message PortalSuspended ; la survenue de ce message
indique au client qu'un autre Execute devrait être lancé sur le
même portail pour terminer l'opération. Le message CommandComplete
indiquant la fin de la commande SQL n'est pas envoyé avant
l'exécution complète du portail. Une phase Execute est toujours
terminée par la survenue d'un seul de ces messages :
CommandComplete, EmptyQueryResponse (si le portail a été créé à
partir d'une chaîne de requête vide), ErrorResponse ou
PortalSuspended.
À la réalisation complète de chaque série de messages de requêtes
étendues, le client doit lancer un message Sync. Ce message sans
paramètre oblige le serveur à fermer la transaction courante si
elle n'est pas à l'intérieur d'un bloc de transaction
begin
/
commit
(« fermer » signifiant valider en l'absence
d'erreur ou annuler sinon). Une réponse ReadyForQuery est alors
envoyée. Le but de Sync est de fournir un point de
resynchronisation pour les récupérations d'erreurs. Quand une
erreur est détectée lors du traitement d'un message de requête
étendue, le serveur lance ErrorResponse, puis lit et annule les
messages jusqu'à ce qu'un Sync soit atteint. Il envoie ensuite
ReadyForQuery et retourne au traitement normal des messages. Aucun
échappement n'est réalisé si une erreur est détectée
lors
du traitement de sync -- l'unicité
du ReadyForQuery envoyé pour chaque Sync est ainsi assurée.
Note
Sync n'impose pas la fermeture d'un bloc de transactions ouvert
avec
begin
. cette
situation est détectable car le message ReadyForQuery inclut le
statut de la transaction.
En plus de ces opérations fondamentales, requises, il y a plusieurs
opérations optionnelles qui peuvent être utilisées avec le
protocole de requête étendue.
Le message Describe (variante de portail) spécifie le nom d'un
portail existant (ou une chaîne vide pour le portail non nommé). La
réponse est un message RowDescription décrivant les lignes qui
seront renvoyées par l'exécution du portail ; ou un message NoData
si le portail ne contient pas de requête renvoyant des lignes ; ou
ErrorResponse le portail n'existe pas.
Le message Describe (variante d'instruction) spécifie le nom d'une
instruction préparée existante (ou une chaîne vide pour
l'instruction préparée non nommée). La réponse est un message
ParameterDescription décrivant les paramètres nécessaires à
l'instruction, suivi d'un message RowDescription décrivant les
lignes qui seront renvoyées lors de l'éventuelle exécution de
l'instruction (ou un message NoData si l'instruction ne renvoie pas
de lignes). ErrorResponse est retourné si l'instruction préparée
n'existe pas. Comme Bind n'a pas encore été exécuté, les formats à
utiliser pour les lignes retournées ne sont pas encore connues du
serveur ; dans ce cas, les champs du code de format dans le message
RowDescription seront composés de zéros.
Astuce
Dans la plupart des scénarios, le client devra exécuter une des
variantes de Describe avant de lancer Execute pour s'assurer
qu'il sait interpréter les résultats reçus.
Le message Close ferme une instruction préparée ou un portail et
libère les ressources. L'exécution de Close sur une instruction ou
un portail inexistant ne constitue pas une erreur. La réponse est
en général CloseComplete mais peut être ErrorResponse si une
difficulté quelconque est rencontrée lors de la libération des
ressources. Clore une instruction préparée ferme implicitement tout
autre portail ouvert construit à partir de cette instruction.
Le message Flush n'engendre pas de sortie spécifique, mais force le
serveur à délivrer toute donnée restante dans les tampons de
sortie. Un Flush doit être envoyé après toute commande de requête
étendue, à l'exception de Sync, si le client souhaite examiner le
résultat de cette commande avant de lancer d'autres commandes. Sans
Flush, les messages retournés par le serveur seront combinés en un
nombre minimum de paquets pour minimiser la charge réseau.
Note
Le message Query simple est approximativement équivalent aux
séries Parse, Bind, Describe sur un portail, Execute, Close,
Sync utilisant les objets de l'instruction préparée ou du
portail, non nommés et sans paramètres. Une différence est
l'acceptation de plusieurs instructions SQL dans la chaîne de
requêtes, la séquence bind/describe/execute étant
automatiquement réalisée pour chacune, successivement. Il en
diffère également en ne retournant pas les messages
ParseComplete, BindComplete, CloseComplete ou NoData.
44.2.4. Appel de fonction
Le sous-protocole d'appel de fonction (NDT : Function Call dans la
version originale) permet au client d'effectuer un appel direct à
toute fonction du catalogue système pg_proc de la base de données. Le client doit
avoir le droit d'exécution de la fonction.
Note
Le sous-protocole d'appel de fonction est une fonctionnalité
qu'il vaudrait probablement mieux éviter dans tout nouveau
code. Des résultats similaires peuvent être obtenus en
initialisant une instruction préparée qui lance select function($1, ...). le cycle de l'appel de
fonction peut alors être remplacé par Bind/Execute.
Un cycle d'appel de fonction est initié par le client envoyant un
message FunctionCall au serveur. Le serveur envoie alors un ou
plusieurs messages de réponse en fonction des résultats de l'appel
de la fonction et finalement un message de réponse ReadyForQuery.
ReadyForQuery informe le client qu'il peut envoyer en toute
sécurité une nouvelle requête ou un nouvel appel de fonction.
Les messages de réponse possibles du serveur sont :
-
errorresponse
-
Une erreur est survenue.
-
functioncallresponse
-
L'appel de la fonction est terminé et a retourné le résultat
donné dans le message. Le protocole d'appel de fonction ne
peut gérer qu'un résultat scalaire simple, pas un type ligne
ou un ensemble de résultats.
-
readyforquery
-
Le traitement de l'appel de fonction est terminé.
ReadyForQuery sera toujours envoyé, que le traitement se
termine avec succès ou avec une erreur.
-
noticeresponse
-
Un message d'avertissement relatif à l'appel de fonction a
été retourné. Les avertissements sont complémentaires des
autres réponses, c'est-à-dire que le seveur continuera à
traiter la commande.
La commande
copy
permet des transferts rapides de données en lot vers ou à partir du
serveur. Les opérations Copy-in et Copy-out basculent chacune la
connexion dans un sous-protocole distinct qui existe jusqu'à la fin
de l'opération.
Le mode Copy-in (transfert de données vers le serveur) est initié
quand le serveur exécute une instruction SQL
copy from stdin
. le serveur
envoie une message CopyInResponse au client. Le client peut alors
envoyer zéro (ou plusieurs) message(s) CopyData, formant un flux de
données en entrée (il n'est pas nécessaire que les limites du
message aient un rapport avec les limites de la ligne, mais cela
est souvent un choix raisonnable). Le client peut terminer le mode
Copy-in en envoyant un message CopyDone (permettant une fin avec
succès) ou un message CopyFail (qui causera l'échec de
l'instruction SQL
copy
avec une erreur). Le serveur
retourne alors au mode de traitement de la commande précédant le
début de
copy
,
protocole de requête simple ou étendu. il enverra enfin
CommandComplete (en cas de succès) ou ErrorResponse (sinon).
Si le serveur détecte un erreur en mode copy-in (ce qui inclut la
réception d'un message CopyFail), il enverra un message
ErrorResponse. Si la commande
copy
a été lancée à l'aide d'un
message de requête étendue, le serveur annulera les messages du
client jusqu'à ce qu'un message Sync soit reçu. Il enverra alors un
message ReadyForQuery et retournera dans le mode de fonctionnement
normal. Si la commande
copy
a été lancée dans un message
simple Query, le reste de ce message est annulé et ReadyForQuery
est envoyé. Dans tous les cas, les messages CopyData, CopyDone ou
CopyFail suivants envoyés par l'interface seront simplement
annulés.
Le serveur ignorera les messages Flush et Sync reçus en mode
copy-in. La réception de tout autre type de messages hors-copie
constitue une erreur qui annulera l'état Copy-in, comme cela est
décrit plus haut. L'exception pour Flush et Sync est faite pour les
bibliothèques clientes qui envoient systématiquement Flush ou Sync
après un message Execute sans vérifier si la commande à exécuter
est
copy from stdin
.
Le mode Copy-out (transfert de données à partir du serveur) est
initié lorsque le serveur exécute une instruction SQL
copy to stdout
. Le moteur envoie
un message CopyOutResponse au client suivi de zéro (ou plusieurs)
message(s) CopyData (un par ligne), suivi de CopyDone. Le serveur
retourne ensuite au mode de traitement de commande dans lequel il
se trouvait avant le lancement de
copy
et envoie commandcomplete.
Le client ne peut pas annuler le transfert (sauf en fermant la
connexion ou en lançant une requête d'annulation, Cancel), mais il
peut ignorer les messages CopyData et CopyDone non souhaités.
Si le serveur détecte une erreur en mode Copy-out, il enverra un
message ErrorResponse et retournera dans le mode de traitement
normal. Le client devrait traiter la réception d'un message
ErrorResponse (ou en fait tout type de message autre que CopyData
ou CopyDone) par la clôture du mode Copy-out.
Les messages CopyInResponse et CopyOutResponse incluent les champs
qui informent le client du nombre de colonnes par ligne et des
codes de format de chaque colonne. Dans l'implantation actuelle,
toutes les colonnes d'une opération
copy
donnée utilisent le même
format, mais la conception du message n'en tient pas compte.
44.2.6. Opérations asynchrones
Il existe plusieurs cas pour lesquels le serveur enverra des
messages qui ne sont pas spécifiquement demandés par le flux de
commande du client. Les clients doivent être préparés à gérer ces
messages à tout moment même si aucune requête n'est en cours.
Vérifier ces cas avant de commencer à lire la réponse d'une requête
est un minimum.
Il est possible que des messages NoticeResponse soient engendrés en
dehors de toute activité ; par exemple, si l'administrateur de la
base de données commande un arrêt « rapide » de la base de données, le serveur
enverra un NoticeResponse l'indiquant avant de fermer la connexion.
Les clients devraient toujours être prêts à accepter et afficher
les messages NoticeResponse, même si la connexion est inactive.
Des messages ParameterStatus seront engendrés à chaque fois que la
valeur active d'un paramètre est modifiée, et cela pour tout
paramètre que le serveur pense utile au client. Cela survient plus
généralement en réponse à une commande SQL
set
exécutée par le client. ce
cas est en fait synchrone -- mais il est possible aussi que le
changement de statut d'un paramètre survienne à la suite d'une
modification par l'administrateur des fichiers de configuration ;
changements suivis de l'envoi du signal sighup au postmaster. de plus, si une commande
set est annulée, un message ParameterStatus approprié sera engendré
pour rapporter la valeur effective.
À ce jour, il existe un certain nombre de paramètres codés en dur
pour lesquels des messages ParameterStatus seront engendrés : on
trouve server_version, client_encoding, server_encoding, is_superuser, session_authorization et session_authorization, datestyle, timezone et
integer_datetimes (server_encoding, timezone
et integer_datetimes n'ont pas été
reportés par les sorties avant la 8.0). Notez que server_version, server_encoding et integer_datetimes sont des pseudo-paramètres qui ne
peuvent pas changer après le lancement. Cet ensemble pourrait
changer dans le futur, voire devenir configurable. De toute façon,
un client peut ignorer un message ParameterStatus pour les
paramètres qu'il ne comprend pas ou qui ne le concernent pas.
Si un client lance une commande
listen
, alors le serveur enverra
un message NotificationResponse (à ne pas confondre avec
NoticeResponse !) à chaque fois qu'une commande
notify
est exécutée pour la
notification de même nom.
Note
Actuellement, NotificationResponse ne peut être envoyé qu'à
l'extérieur d'une transaction. Il ne surviendra donc pas au
milieu d'une réponse à une commande, mais il peut survenir
juste avant ReadyForQuery. Il est toutefois déconseillé de
concevoir un client en partant de ce principe. La bonne
pratique est d'être capable d'accepter NotificationResponse à
tout moment du protocole.
44.2.7. Annulation de requêtes en cours
Pendant le traitement d'une requête, le client peut demander
l'annulation de la requête. La demande d'annulation n'est pas
envoyée directement au serveur par la connexion ouverte pour des
raisons d'efficacité de l'implémentation : il n'est pas admissible
que le serveur vérifie constamment les messages émanant du client
lors du traitement des requêtes. Les demandes d'annulation sont
relativement inhabituelles ; c'est pourquoi elles sont traitées de
manière relativement simple afin d'éviter que ce traitement ne
pénalise le fonctionnement normal.
Pour effectuer une demande d'annulation, le client ouvre une
nouvelle connexion au serveur et envoie un message CancelRequest à
la place du message StartupMessage envoyé habituellement à
l'ouverture d'une connexion. Le serveur traitera cette requête et
fermera la connexion. Pour des raisons de sécurité, aucune réponse
directe n'est faite au message de requête d'annulation.
Un message CancelRequest sera ignoré sauf s'il contient la même
donnée clé (PID et clé secrète) que celle passée au client lors du
démarrage de la connexion. Si la donnée clé correspond, le
traitement de la requête en cours est annulé (dans l'implantation
existante, ceci est obtenu en envoyant un signal spécial au
processus serveur qui traite la requête).
Le signal d'annulation peut ou non être suivi d'effet -- par
exemple, s'il arrive après la fin du traitement de la requête par
le serveur, il n'aura alors aucun effet. Si l'annulation est
effective, il en résulte la fin précoce de la commande accompagnée
d'un message d'erreur.
De tout ceci, il ressort que, pour des raisons de sécurité et
d'efficacité, le client n'a aucun moyen de savoir si la demande
d'annulation a abouti. Il continuera d'attendre que le serveur
réponde à la requête. Effectuer une annulation permet simplement
d'augmenter la probabilité de voir la requête en cours finir
rapidement et échouer accompagnée d'un message d'erreur plutôt que
réussir.
Comme la requête d'annulation est envoyée via une nouvelle
connexion au serveur et non pas au travers du lien de communication
client/serveur établi, il est possible que la requête d'annulation
soit lancée par un processus quelconque, pas forcément celui du
client pour lequel la requête doit être annulée. Cela peut
présenter quelques avantages de flexibilité dans la construction
d'applications multi-processus ; mais également une faille de
sécurité puisque des personnes non autorisées pourraient tenter
d'annuler des requêtes. La faille de sécurité est comblée par
l'exigence d'une clé secrète, engendrée dynamiquement, pour toute
requête d'annulation.
44.2.8. Fin
Lors de la procédure normale de fin le client envoie un message
Terminate et ferme immédiatement la connexion. À la réception de ce
message, le serveur ferme la connexion et s'arrête.
Dans de rares cas (tel un arrêt de la base de données par
l'administrateur), le serveur peut se déconnecter sans demande du
client. Dans de tels cas, le serveur tentera d'envoyer un message
d'erreur ou d'avertissement en donnant la raison de la déconnexion
avant de fermer la connexion.
D'autres scénarios de fin peuvent être dus à différents cas
d'échecs, tels qu'un « core
dump » côté client ou serveur, la perte du lien de
communications, la perte de synchronisation des limites du message,
etc. Que le client ou le serveur s'aperçoive d'une fermeture de la
connexion, le buffer sera vidé et le processus terminé. Le client a
la possibilité de lancer un nouveau processus serveur en
recontactant le serveur s'il ne souhaite pas se finir. Il peut
également envisager de clore la connexion si un type de message non
reconnu est reçu ; en effet, ceci est probablement le résultat de
la perte de synchronisation des limite de messages.
Que la fin soit normale ou non, toute transaction ouverte est
annulée, non pas validée. Si un client se déconnecte alors qu'une
requête autre que
select
est en cours de
traitement, le serveur terminera probablement la requête avant de
prendre connaissance de la déconnexion. Si la requête est en dehors
d'un bloc de transaction (séquence
begin
...
commit
), il se peut que les
résultats soient validés avant que la connexion ne soit reconnue.
44.2.9. Chiffrement ssl de session
Si postgresql™ a été
construit avec le support de ssl, les communications client/serveur peuvent
être chiffrées en l'utilisant. Ce chiffrement assure la sécurité de
la communication dans les environnements où des agresseurs
pourraient capturer le trafic de la session. Pour plus
d'informations sur le cryptage des sessions postgresql™ avec ssl, voir Section 16.7,
« Connexions tcp/ip sécurisées avec ssl ».
Pour initier une connexion chiffrée par ssl, le client envoie initialement un message
SSLRequest à la place d'un StartupMessage. Le serveur répond avec
un seul octet contenant s ou n indiquant respectivement s'il souhaite ou non
utiliser le ssl. Le client peut
alors clore la connexion s'il n'est pas satisfait de la réponse.
Pour continuer après un s, il faut
échanger une poignée de main ssl
(handshake) (non décrite ici car faisant partie de la spécification
ssl) avec le serveur. en cas de
succès, le StartupMessage habituel est envoyé. Dans ce cas,
StartupMessage et toutes les données suivantes seront chiffrées
avec ssl. pour continuer après
un n, il suffit d'envoyer le
startupmessage habituel et de continuer sans chiffrage.
Le client doit être préparé à gérer une réponse ErrorMessage à un
SSLRequest émanant du serveur. Ceci ne peut survenir que si le
serveur ne dispose pas du support de ssl. dans ce cas, la connexion doit être
fermée, mais le client peut choisir d'ouvrir une nouvelle connexion
et procéder sans ssl.
Un SSLRequest initial peut également être utilisé dans une
connexion en cours d'ouverture pour envoyer un message
CancelRequest.
Alors que le protocole lui-même ne fournit pas au serveur de moyen
de forcer le chiffrage ssl,
l'administrateur peut configurer le serveur pour rejeter les
sessions non chiffrées, ce qui est une autre façon de vérifier
l'authentification.