PostgreSQLLa base de données la plus sophistiquée au monde.
Documentation PostgreSQL 17.6 » Internes » Protocole Frontend/Backend » Flot de messages

53.2. Flot de messages #

Cette section dĂ©crit le flot de message et la sĂ©mantique de chaque type de message. (Les dĂ©tails sur la reprĂ©sentation exacte de chaque message apparaĂźt dans Section 53.7.) Il existe plusieurs sous-protocoles diffĂ©rents dĂ©pendant de l'Ă©tat de la connexion : dĂ©marrage, requĂȘte, appel de fonction, COPY, et fin. Il existe aussi plusieurs cas spĂ©ciaux pour les opĂ©rations asynchrones (incluant les rĂ©ponses aux notifications et les annulations de commande), qui peuvent survenir Ă  tout moment aprĂšs la phase de dĂ©marrage.

53.2.1. DĂ©marrage (Start-up) #

Pour commencer 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 sur laquelle l'utilisateur souhaite se connecter ; il identifie aussi la version particuliĂšre du protocole Ă  utiliser. (En option, le message de dĂ©marrage peut inclure diffĂ©rentes configurations de paramĂštres.) Le serveur utilise ensuite cette information et le contenu des fichiers de configuration (tels que pg_hba.conf) pour dĂ©terminer si la connexion est acceptable et le type d'authentification requis (si c'est le cas).

Ensuite, le serveur envoie un message appropriĂ© de demande d'authentification, auquel le client doit rĂ©pondre avec un message appropriĂ© de rĂ©ponse d'authentification (par exemple, avec un mot de passe). Pour toutes les mĂ©thodes d'authentification, sauf GSSAPI, SSPI et SASL, il existe au moins une requĂȘte et une rĂ©ponse. Pour certaines mĂ©thodes, aucune rĂ©ponse n'est nĂ©cessaire, et donc aucune demande d'authentification n'arrive. Pour les mĂ©thodes GSSAPI, SSPI et SASL, plusieurs Ă©changes de paquets pourraient ĂȘtre nĂ©cessaires pour terminer l'authentification.

Le cycle d'authentification se termine avec le serveur rejetant la tentative de connexion (message ErrorResponse), ou renvoyant le message AuthenticationOk.

Dans cette phase, les messages possibles provenant du serveur sont :

ErrorResponse

La demande 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 maintenant prendre part à un dialogue d'authentification Kerberos V5 (qui n'est pas décrit ici, et fait partie de la spécification Kerberos) avec le serveur. Si ce dialogue réussit, le serveur répond avec un AuthenticationOk, sinon il répond avec un ErrorResponse. Ceci n'est plus accepté.

AuthenticationCleartextPassword

Le client doit maintenant envoyer un message PasswordMessage contenant le mot de passe en clair. Si le mot de passe est bon, le serveur répond avec un message AuthenticationOk, sinon il répond avec un message ErrorResponse.

AuthenticationMD5Password

Le client doit maintenant envoyer un message PasswordMessage contenant le mot de passe (avec le nom de l'utilisateur) chiffrĂ© avec MD5, puis chiffrĂ© de nouveau en utilisant le sel alĂ©atoire de quatre octets indiquĂ© dans le message AuthenticationMD5Password. S'il s'agit du bon mot de passe, le serveur rĂ©pond avec un message AuthenticationOk, sinon il rĂ©pond avec nu message ErrorResponse. Le message PasswordMessage rĂ©el peut ĂȘtre calculĂ© en SQL avec un concat('md5', md5(concat(md5(concat(motdepasse, nomdutilisateur)), selalĂ©atoire))). (Gardez en tĂȘte que la fonction md5() renvoie le rĂ©sultat sous la forme d'une chaĂźne hexadĂ©cimale.)

AuthenticationGSS

Le client doit maintenant initier une négociation GSSAPI. Le client enverra un message GSSResponse avec la premiÚre partie du flux de données GSSAPI en réponse à cela. Si plusieurs messages sont nécessaires, le serveur répondra avec AuthenticationGSSContinue.

AuthenticationSSPI

Le client doit maintenant initier une négociation SSPI. Le client enverra un message GSSResponse avec la premiÚre partie du flux de données SSPI en réponse à cela. Si plusieurs messages sont nécessaires, le serveur répondra avec AuthenticationGSSContinue.

AuthenticationGSSContinue

Ce message contient les données de réponse de l'étape précédente de la négociation GSSAPI ou SSPI (AuthenticationGSS, AuthenticationSSPI ou un AuthenticationGSSContinue précédent). Si les données GSSAPI ou SSPI de ce message indique que plus de données sont nécessaires pour terminer l'authentification, le client doit envoyer les données dans un autre message GSSResponse. Si l'authentification GSSAPI ou SSPI est terminée par ce message, le serveur enverra ensuite un message AuthenticationOk pour indiquer une authentification réussie ou un message ErrorResponse pour indiquer un échec.

AuthenticationSASL

Le client doit maintenant initier une nĂ©gociation SASL, en utilisant un des mĂ©canismes SASL listĂ©s dans le message. Le client doit envoyer un message SASLInitialResponse avec le nom du mĂ©canisme sĂ©lectionnĂ©, et la premiĂšre partie du flux de donnĂ©es SASL en rĂ©ponse Ă  ceci. Si plus de messages sont nĂ©cessaires, le serveur rĂ©pondra avec un message AuthenticationSASLContinue. Voir Section 53.3 pour les dĂ©tails.

AuthenticationSASLContinue

Ce message contient des données de challenge provenant des étapes précédentes de la négociation SASL (AuthenticationSASL, ou d'un précédent AuthenticationSASLContinue). Le client doit répondre avec un message SASLResponse.

AuthenticationSASLFinal

L'authentification SASL a terminĂ© avec les donnĂ©es supplĂ©mentaires spĂ©cifiques du mĂ©canisme pour le client. Le serveur enverra ensuite le message AuthenticationOk pour indiquer une authentification rĂ©ussie ou un message ErrorResponse pour indiquer un Ă©chec. Ce message est envoyĂ© uniquement si le mĂ©canisme SASL indique que des donnĂ©es supplĂ©mentaires doivent ĂȘtre envoyĂ©es du serveur au client Ă  la fin.

NegotiateProtocolVersion

Le serveur n'accepte pas la version mineure du protocole rĂ©clamĂ©e par le client, mais accepte une version plus ancienne du protocole ; ce message indique la version mineure la plus haute que le serveur acceptĂ©. Ce message sera aussi envoyĂ© si le client a demandĂ© des options non acceptĂ©es du protocole (par exemple commençant avec _pq_.) dans le paquet de dĂ©marrage. Ce message sera suivi par un message ErrorResponse ou par un message indiquant le succĂšs ou l'Ă©chec de l'authentification.

Si le client n'accepte pas la méthode d'authentification demandée par le client, il doit immédiatement fermer la connexion.

AprÚs avoir reçu le message AuthenticationOk, le client doit attendre plus de messages du serveur. Dans cette phase, un processus backend est démarré, et le client est juste un partenaire intéressé. Il est toujours possible que la demande de démarrage échoue (ErrorResponse) ou que le serveur décline le support de la version mineure demandée du protocole (NegotiateProtocolVersion), mais dans un cas normal, le backend enverra quelques messages ParameterStatus, BackendKeyData et enfin ReadyForQuery.

Lors de cette phase, le backend tentera d'appliquer toute configuration supplémentaire de paramÚtre d'exécution donnée lors du message de démarrage. EN cas de succÚs, ces valeurs deviennent les valeurs par défaut de la session. Une erreur cause un message ErrorResponse puis quitte.

Les messages possibles provenant du backend dans cette phase sont :

BackendKeyData

Ce message fournit des donnĂ©es de clĂ© secrĂšte que le client doit conserver s'il souhaite ĂȘtre capable d'annuler des requĂȘtes plus tard. Le client ne doit pas rĂ©pondre Ă  ce message mais doit continuer Ă  attendre un message ReadyForQuery.

ParameterStatus

Ce message informe le client sur la configuration actuelle (initiale) d'un paramĂštre serveur, tel que client_encoding ou datestyle. Le client peut ignorer ce message, ou enregistrer la configuration pour une utilisation ultĂ©rieure ; voir Section 53.2.7 pour plus de dĂ©tails. Le client ne doit pas rĂ©pondre Ă  ce message, mais doit continuer Ă  attendre un message ReadyForQuery.

ReadyForQuery

Le démarrage est terminé. Le client peut maintenant exécuter des commandes.

ErrorResponse

Le démarrage a échoué. La connexion est fermée aprÚs l'envoi de ce message.

NoticeResponse

Un message d'avertissement a été envoyé. Le client doit afficher le message mais continuer à attendre des messages ReadyForQuery ou ErrorResponse.

Le message ReadyForQuery est le mĂȘme que le backend enverra aprĂšs chaque cycle de commande. Suivant les besoins du client, il est raisonnable de considĂ©rer le message ReadyForQuery comme dĂ©butant un cycle de commande ou de le considĂ©rer comme terminant la phase de dĂ©marrage et chaque cycle de commandes suivant.

53.2.2. RequĂȘte simple #

Un cycle de requĂȘte simple est initiĂ© par le client envoyant un message Query au backend. The message inclut une commande SQL (ou des commandes) exprimĂ©e sous la forme d'une chaĂźne de caractĂšres. Le backend envoie alors une ou plusieurs rĂ©ponses suivant le contenu de la chaĂźne de texte, et termine par un message ReadyForQuery. Ce dernier message informe le client qu'il peut envoyer une nouvelle commande. (Il n'est pas absolument nĂ©cessaire que le client attende le message ReadyForQuery avant de lancer une autre commande, mais le client prend alors la responsabilitĂ© de comprendre ce qu'il se passe si la commande prĂ©cĂ©dente Ă©choue et que les commandes suivantes dĂ©jĂ  lancĂ©es rĂ©ussissent.)

Les messages de rĂ©ponse possibles du backend sont :

CommandComplete

Une commande SQL terminée normalement.

CopyInResponse

Le backend est prĂȘt Ă  copier des donnĂ©es du client vers une table ; voir Section 53.2.6.

CopyOutResponse

Le backend est prĂȘt Ă  copier les donnĂ©es d'une table vers le client ; voir Section 53.2.6.

RowDescription

Indique que les lignes sont prĂȘtes Ă  ĂȘtre renvoyĂ©es en rĂ©ponse Ă  une requĂȘte SELECT, FETCH, etc. Le contenu de ce message dĂ©crit la disposition des colonnes pour les lignes. Ceci sera suivi par un message DataRow pour chaque ligne renvoyĂ©e au client.

DataRow

Un des ensembles de lignes renvoyĂ©s par une requĂȘte SELECT, FETCH, etc.

EmptyQueryResponse

Une chaĂźne a Ă©tĂ© reconnue Ă  la place d'une requĂȘte.

ErrorResponse

Une erreur est survenue.

ReadyForQuery

Le traitement de la requĂȘte est terminĂ©. Un message sĂ©parĂ© est envoyĂ© pour l'indiquer parce que la chaĂźne de texte pourrait contenir plusieurs commandes SQL. (Le message CommandComplete marque la fin du traitement d'une commande SQL, et non pas de la chaĂźne complĂšte.) Le message ReadyForQuery sera toujours envoyĂ©, que le traitement se termine avec succĂšs ou avec Ă©chec.

NoticeResponse

Un message d'avertissement a Ă©tĂ© envoyĂ© en relation Ă  la requĂȘte. Les avertissements sont en plus des autres rĂ©ponses, autrement dit le backend continuera de traiter la commande.

La rĂ©ponse Ă  une requĂȘte SELECT (ou autres requĂȘtes qui renvoient des ensembles de lignes, tels que EXPLAIN ou SHOW), consiste normalement en des messages RowDescription, zĂ©ro ou plusieurs messages DataRow, et enfin un message CommandComplete. COPY vers ou Ă  partir du client fait appel Ă  un protocole spĂ©cial dĂ©crit dans Section 53.2.6. Tous les autres types de requĂȘtes produisent normalement seulement un message CommandComplete.

Comme une chaĂźne de texte peut contenir plusieurs requĂȘtes (sĂ©parĂ©es par des points-virgules), il pourrait y avoir plusieurs sĂ©quences de rĂ©ponses avant que le backend finisse le traitement de la chaĂźne. Le message ReadyForQuery est lancĂ© quand la chaĂźne entiĂšre a Ă©tĂ© traitĂ©e et que le backend est prĂȘt Ă  accepter une nouvelle chaĂźne de texte.

Si une chaßne de caractÚres complÚtement vide est reçue (aucun contenu autre que les espaces blancs), la réponse est EmptyQueryResponse suivi par ReadyForQuery.

Au cas oĂč une erreur survient, le message ErrorResponse est envoyĂ© suivi d'un message ReadyForQuery. Tous les autres traitement de chaĂźne de texte sont annulĂ©s par le message ErrorResponse (mĂȘme s'il reste des requĂȘtes dans la chaĂźne). Notez que ceci pourrait survenir au milieu de la sĂ©quence de messages gĂ©nĂ©rĂ©s par une requĂȘte individuel.

Dans le mode simple requĂȘte, le format de valeurs rĂ©cupĂ©rĂ©es est toujours du texte, sauf quand la commande donnĂ©es est un FETCH Ă  partir d'un curseur dĂ©clarĂ© avec l'option BINARY. Dans ce cas, les valeurs rĂ©cupĂ©rĂ©es sont dans un format binaire. Les codes format donnĂ©s dans le message RowDescription indiquent le format utilisĂ©.

Un client doit ĂȘtre prĂ©parĂ© pour accepter des messages ErrorResponse et NoticeResponse Ă  chaque fois qu'il est attendu d'autres types de message. Voir aussi Section 53.2.7 pour les messages que le backend pourrait gĂ©nĂ©rer Ă  cause d'Ă©vĂ©nements externes.

Une pratique recommandĂ©e est de coder des clients dans un style machine d'Ă©tats qui acceptera tout type de message Ă  tout moment oĂč cela aurait du sens, plutĂŽt que de coder des suppositions sur la sĂ©quence exacte des messages.

53.2.2.1. Plusieurs requĂȘtes dans le mode requĂȘte simple #

Quand un message simple Query contient plus d'une requĂȘte SQL (sĂ©parĂ© par des points-virgules), ces requĂȘtes sont exĂ©cutĂ©es comme une seule transaction, sauf si des commandes de contrĂŽle de transaction explicites sont inclus pour forcer un comportement diffĂ©rent. Par exemple, si le message contient :

INSERT INTO mytable VALUES(1);
SELECT 1/0;
INSERT INTO mytable VALUES(2);

alors une erreur de division par zĂ©ro dans la requĂȘte SELECT forcera l'annulation du premier INSERT. De plus, comme l'exĂ©cution du message est abandonnĂ©e Ă  la premiĂšre erreur, le deuxiĂšme INSERT n'est jamais tentĂ©.

Si le message contient Ă  la place :

BEGIN;
INSERT INTO mytable VALUES(1);
COMMIT;
INSERT INTO mytable VALUES(2);
SELECT 1/0;

alors le premier INSERT est validé par la commande COMMIT explicite. Le deuxiÚme INSERT et le SELECT sont toujours traités comme une seule transaction, donc l'échec de division par zéro annulera le second INSERT, mais pas le premier.

Ce comportement est implĂ©mentĂ© en exĂ©cutant les requĂȘtes dans un message message multi-requĂȘtes dans un bloc de transaction implicite sauf s'il existe un bloc de transaction explicite pour leur exĂ©cution. La principale diffĂ©rence entre un bloc de transaction implicite et un bloc standard est qu'un bloc implicite est fermĂ© automatiquement Ă  la fin d'un message Query, soit par une validation implicite s'il n'y a pas d'erreur, soit par une annulation implicite s'il y avait une erreur. Ceci est similaire Ă  la validation ou Ă  l'annulation implicite qui survient pour une requĂȘte exĂ©cutĂ©e par elle-mĂȘme (quand elle n'est pas dans un bloc de transaction).

Si la session est dĂ©jĂ  dans un bloc de transaction, en rĂ©sultat d'un BEGIN dans un message prĂ©cĂ©dent, alors le message Query continue simplement ce bloc de transaction, si le message contient une ou plusieurs requĂȘtes. NĂ©anmoins, si le message Query contient un COMMIT ou un ROLLBACK fermant le bloc de transaction existant, alors toutes les requĂȘtes suivantes sont exĂ©cutĂ©es dans un bloc de transaction explicite. Inversement, si un BEGIN apparaĂźt dans un message Query multi-requĂȘtes, alors il dĂ©marre un bloc de transaction standard qui sera uniquement terminĂ© par un COMMIT ou ROLLBACK explicite, apparaissant soit dans ce message Query soit dans un message suivant. Si le BEGIN suit certaines requĂȘtes qui ont Ă©tĂ© exĂ©cutĂ©es sous la forme d'un bloc de transaction implicite, ces requĂȘtes ne sont pas immĂ©diatement validĂ©es ; en effet, elles sont inclues rĂ©troactivement dans le nouveau bloc de transaction standard.

Un COMMIT ou un ROLLBACK apparaissant dans un bloc de transaction implicite est exĂ©cutĂ© de façon normale, fermant le bloc implicite ; nĂ©anmoins, un message d'avertissement sera renvoyĂ© car un COMMIT ou un ROLLBACK sans BEGIN pourrait ĂȘtre une erreur. Si des requĂȘtes suivent, un nouveau bloc de transaction implicite sera dĂ©marrĂ© pour elles.

Les savepoints ne sont pas autorisés dans un bloc de transaction implicite car elles pourraient entrer en conflit avec le comportement de fermeture automatique du bloc en cas d'erreur.

Rappelez-vous que, quelque soient les commandes de contrĂŽle des transactions prĂ©sentes, l'exĂ©cution d'un message Query s'arrĂȘte Ă  la premiĂšre erreur. Donc sur cet exemple :

BEGIN;
SELECT 1/0;
ROLLBACK;

dans un seul message Query, la session sera laissée à l'intérieur d'un bloc de transaction standard en échec car ROLLBACK n'est pas atteint aprÚs l'erreur de division par zéro. Un autre ROLLBACK sera nécessaire pour restaurer la session dans un état utilisable.

Un autre comportement Ă  noter est que l'analyse lexicale et syntaxique est rĂ©alisĂ©e sur la chaĂźne de caractĂšres entiĂšre avant qu'une seule requĂȘte ne soit exĂ©cutĂ©e. De ce fait, les erreurs simples (telle qu'un mot clĂ© mal orthographiĂ©) dans des requĂȘtes ultĂ©rieures peuvent empĂȘcher l'exĂ©cution des requĂȘtes. Ceci est habituellement invisible pour les utilisateurs car les requĂȘtes vont de toute façon ĂȘtre intĂ©gralement annulĂ©es quand elles font partie d'un bloc de transaction implicite. Cependant, cela peut se voir lors d'une tentative sur plusieurs transactions dans un message Query multi-requĂȘtes. Par exemple, si une faute est intĂ©grĂ©e Ă  l'exemple prĂ©cĂ©dent comme ceci :

BEGIN;
INSERT INTO mytable VALUES(1);
COMMIT;
INSERT INTO mytable VALUES(2);
SELCT 1/0;

alors aucune des requĂȘtes ne sera exĂ©cutĂ©e, rĂ©sultant en une diffĂ©rence visible, Ă  savoir que le premier INSERT n'est pas validĂ©. Les erreurs dĂ©tectĂ©es lors de l'analyse sĂ©mantique ou plus tard, comme une table ou une colonne mal nommĂ©e, n'ont pas cet effet.

Enfin, notez que toutes les requĂȘtes du message Query auront la mĂȘme valeur pour la fonction statement_timestamp(), car cet horodatage est mis Ă  jour seulement lors de la rĂ©ception d'un message Query. De ce fait, ils auront aussi tous la mĂȘme valeur pour transaction_timestamp(), sauf dans les cas oĂč la chaĂźne de requĂȘtes finit avec une transaction prĂ©cĂ©demment dĂ©marrĂ©e et en commence une nouvelle.

53.2.3. RequĂȘte Ă©tendue #

Le protocole de requĂȘte Ă©tendue divise le protocole de requĂȘte simple dĂ©crit ci-dessus en plusieurs Ă©tapes. Le rĂ©sultat des Ă©tapes prĂ©paratoires peut ĂȘtre rĂ©-utilisĂ© plusieurs fois pour amĂ©liorer l'efficacitĂ©. De plus, des fonctionnalitĂ©s supplĂ©mentaires sont disponibles, tel que la possibilitĂ© de fournir des valeurs de donnĂ©es comme paramĂštres sĂ©parĂ©s au lieu d'avoir Ă  les insĂ©rer directement dans la chaĂźne de la requĂȘte.

Dans le protocole Ă©tendu, le client envoie en premier lieu un message Parse, qui contient une chaĂźne de caractĂšres pour la requĂȘte, avec en options quelques informations sur les types de donnĂ©es des paramĂštres, et le nom d'un objet reprĂ©sentant la requĂȘte prĂ©parĂ©e (une chaĂźne vide indiquera une requĂȘte prĂ©parĂ©e sans nom). La rĂ©ponse est soit un message ParseComplete soit un message ErrorResponse. Les types de donnĂ©es des paramĂštres peuvent ĂȘtre des types de donnĂ©es indiquĂ©es par leur OID. Si aucun type n'est indiquĂ©, l'analyseur tentera de deviner les types de donnĂ©es de la mĂȘme façon qu'il le ferait pour des constantes de chaĂźnes non typĂ©es.

Note

Un type de paramĂštre peut ĂȘtre laissĂ© sans spĂ©cification en le configurant Ă  zĂ©ro ou en faisant en sorte que le table d'OID des types de paramĂštres soit de taille infĂ©rieur au nombre de symboles de paramĂštres ($n) utilisĂ©s dans la chaĂźne de requĂȘte. Un autre cas spĂ©cial est qu'un type de paramĂštre peut ĂȘtre indiquĂ© comme void (autrement dit, l'OID du pseudo-type void). Ceci permet l'utilisation de symboles de paramĂštre pour les paramĂštres de fonction qui sont en rĂ©alitĂ© des paramĂštres OUT. D'habitude, il n'y a pas de contexte dans lequel un paramĂštre void pourrait ĂȘtre utilisĂ©, mais si un tel symbole de paramĂštre apparaĂźt dans la liste de paramĂštres d'une fonction, il est en fait ignorĂ©. Par exemple, un appel de fonction tel que foo($1,$2,$3,$4) pourrait correspondre Ă  une fonction avec deux arguments IN et deux arguments OUT, si $3 et $4 sont indiquĂ©s comme Ă©tant de type void.

Note

La chaĂźne de requĂȘte contenue dans un message Parse ne peut pas inclure plus d'une requĂȘte SQL ; dans le cas contraire une erreur SQL est levĂ©e. Cette restriction n'existe pas dans le protocole requĂȘte simple mais elle existe dans le protocole Ă©tendu parce que permettre Ă  des requĂȘtes prĂ©parĂ©es ou Ă  des portails de contenir plusieurs commandes compliquerait indument le protocole.

En cas de succĂšs Ă  la crĂ©ation, un objet nommĂ© de requĂȘte prĂ©parĂ© existe jusqu'Ă  la fin de la session, sauf s'il est dĂ©truit explicitement. Une requĂȘte prĂ©parĂ©e sans nom dure seulement jusqu'Ă  l'exĂ©cution du prochain message Parse pour une requĂȘte prĂ©parĂ©e sans nom. (Notez qu'un message Query dĂ©truit aussi une requĂȘte prĂ©parĂ©e sans nom.) Les requĂȘtes prĂ©parĂ©es nommĂ©es doivent ĂȘtre explicitement fermĂ©es avant de pouvoir ĂȘtre redĂ©finies par un autre message Parse, mais ceci n'est pas requis pour une requĂȘte prĂ©parĂ©e sans nom. Les requĂȘtes prĂ©parĂ©es nommĂ©es peuvent aussi ĂȘtre créées et accĂ©dĂ©es au niveau des commandes SQL en utilisant les instructions PREPARE et EXECUTE.

Une fois qu'une requĂȘte prĂ©parĂ©e existe, elle peut ĂȘtre prĂ©parĂ©e pour une exĂ©cution en utilisant le message Bind. Ce dernier donne le nom de la requĂȘte prĂ©parĂ©e source (ou une chaĂźne vide dans le cas d'une requĂȘte prĂ©parĂ©e sans nom) et les valeurs Ă  utiliser pour tous les paramĂštres prĂ©sents dans la requĂȘte prĂ©parĂ©e. L'ensemble de paramĂštres fournis doit correspondre ceux requis par la requĂȘte prĂ©parĂ©e. (Si vous avez indiquĂ© un ou plusieurs paramĂštres void dans le message Parse, donnez des valeurs NULL pour chacune dans le message Bind.) Bind spĂ©cifie aussi le format Ă  utiliser pour toute donnĂ©e renvoyĂ©e par la requĂȘte ; le format peut ĂȘtre spĂ©cifiĂ© de façon globale ou par colonne. La rĂ©ponse est soit BindComplete soit ErrorResponse.

Note

Le choix entre une sortie texte et une sortie binaire est dĂ©terminĂ© par les codes format donnĂ©s dans Bind, quelque soit la commande SQL impliquĂ©e. L'attribut BINARY dans les dĂ©clarations de curseur est hors sujet lors de l'utilisation du protocole de requĂȘte Ă©tendue.

La planification/optimisation de la requĂȘte survient typiquement quand le message Bind est traitĂ©. Si la requĂȘte prĂ©parĂ©e n'a pas de paramĂštres ou si elle est exĂ©cutĂ©e de façon rĂ©pĂ©tĂ©e, le serveur pourrait sauvegarde le plan créé et le rĂ©-utiliser lors des messages Bind suivants pour la mĂȘme requĂȘte prĂ©parĂ©e. NĂ©anmoins, il le fera seulement s'il trouve qu'un plan gĂ©nĂ©rique peut ĂȘtre créé, sans ĂȘtre trop inefficace par rapport Ă  un plan dĂ©pendant des valeurs spĂ©cifiques fournies pour les paramĂštres. Ceci survient de façon transparente pour ce qui concerne le protocole.

S'il est créé avec succĂšs, un objet portail nommĂ© dure jusqu'Ă  la fin de la transaction en cours, sauf en cas de destruction explicite. Un portail non nommĂ© est dĂ©truit Ă  la fin de la transaction ou dĂšs l'exĂ©cution du prochain message Bind pour un portal non nommĂ©. (Notez qu'un simple message Query dĂ©truit aussi le portail sans nom.) Les portails nommĂ©s doivent ĂȘtre fermĂ©s explicitement avant de pouvoir ĂȘtre redĂ©finis par un autre message Bind, mais ceci n'est pas requis pour un portail non nommĂ©. Les portails nommĂ©s peuvent aussi ĂȘtre utilisĂ©s et accĂ©dĂ©s au niveau des commandes SQL, en utilisant les instructions DECLARE CURSOR et FETCH.

Une fois que le portail existe, il peut ĂȘtre exĂ©cutĂ© en utilisant un message Execute. Ce dernier spĂ©cifie le nom du portail (une chaĂźne vide indique un portail non nommĂ©) et un nombre maximum de lignes de rĂ©sultat (zĂ©ro signifiant « rĂ©cupĂšre toutes les lignes Â»). Ce nombre a seulement un sens pour les portails contenant des commandes renvoyant des ensembles de lignes ; dans les autres cas, la commande est toujours exĂ©cutĂ©e jusqu'Ă  sa fin, et le nombre de lignes est ignorĂ©. Les rĂ©ponses possibles Ă  ExĂ©cute sont les mĂȘmes que celles dĂ©crites ci-dessus pour les requĂȘtes lancĂ©es via le protocole de requĂȘte simple, sauf que Execute ne cause pas l'exĂ©cution de ReadyForQuery ou RowDescription.

Si Execute termine avant la fin de l'exĂ©cution d'un portail (Ă  cause de l'atteinte d'un nombre de lignes rĂ©sultats diffĂ©rent de zĂ©ro), il enverra un message PortalSuspended ; l'apparence de ce message indique au client qu'un autre Execute devrait ĂȘtre exĂ©cutĂ© contre le mĂȘme portail pour terminer l'opĂ©ration. Le message CommandComplete indiquant la fin de la commande SQL source n'est pas envoyĂ© jusqu'Ă  la fin de l'exĂ©cution du portail. De ce fait, une phase Execute est toujours terminĂ©e par l'apparition d'exactement un de ces messages : CommandComplete, EmptyQueryResponse (si le portail a Ă©tĂ© créé Ă  partir d'une chaĂźne vide de requĂȘte), ErrorResponse ou PortalSuspended.

À la fin de chaque sĂ©rie de messages du protocole de requĂȘte Ă©tendue, le client doit envoyer un message Sync. Ce message sans paramĂštre fait que le backend ferme la transaction en cours si elle n'est pas Ă  l'intĂ©rieur d'un bloc de transaction BEGIN/COMMIT (« fermer Â» signifiant une validation s'il n'y a pas d'erreur et une annulation dans le cas d'une erreur). Une rĂ©ponse ReadyForQuery est ensuite produite. Le but de Sync est de fournir un point de resynchronisation pour les erreurs. Quand une erreur est dĂ©tectĂ©e lors du traitement de tout message du protocole de requĂȘte Ă©tendu, le backend envoie un message ErrorResponse, puis lit et annule les messages jusqu'Ă  la rĂ©ception d'un Sync, envoie un message ReadyForQuery et enfin retourne Ă  un traitement habituelle des messages. (Mais notez qu'une erreur dans le traitement du message Sync n'est pas ignorĂ© -- ceci assure qu'il y a bien un et un seul ReadyForQuery envoyĂ© pour chaque Sync.)

Note

Sync ne ferme pas un bloc de transaction ouvert avec BEGIN. Il est possible de détecter cette situation car le message ReadyForQuery inclut des informations de statut de la transaction.

En plus de ces opĂ©rations fondamentales et requises, il existe plusieurs opĂ©rations optionnelles pouvant ĂȘtre utilisĂ©es avec le protocole de requĂȘte Ă©tendue.

Le message Describe (variant du portail) indique 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 retournĂ©es par l'exĂ©cution du portail ; ou un message NoData si le portail ne contient pas une requĂȘte qui renverra des lignes ; ou ErrorResponse s'il n'existe pas un tel portail.

Le message Describe (variant de la requĂȘte) indique le nom d'une requĂȘte prĂ©parĂ©e existante (ou une chaĂźne vide pour la requĂȘte prĂ©parĂ©e non nommĂ©e). La rĂ©ponse est un message ParameterDescription dĂ©crivant les paramĂštres nĂ©cessaires pour la requĂȘte, suivi par un message RowDescription dĂ©crivant les lignes qui seront renvoyĂ©s quand la requĂȘte sera enfin exĂ©cutĂ©e (ou un message NoData si la requĂȘte ne renverra pas de lignes). Le message ErrorResponse est renvoyĂ© si cette requĂȘte prĂ©parĂ©e n'existe pas. Notez que, comme Bind n'a pas Ă©tĂ© exĂ©cutĂ©e, les formats Ă  utiliser pour les colonnes renvoyĂ©s ne sont pas encore connus du backend ; les champs de code format seront Ă  zĂ©ro dans le message RowDescription dans ce cas.

Astuce

Dans la plupart des scénarios, le client doit envoyer une variante ou l'autre de Describe avant d'envoyer le message Execute, pour s'assurer qu'il sait comment interpréter les résultats qu'il récupérera.

Le message Close ferme une requĂȘte prĂ©parĂ©e ou un portail existant, et libĂšre les ressources. Ce n'est pas une erreur d'envoyer Close pour un nom inexistant de requĂȘte prĂ©parĂ©e ou de portail. La rĂ©ponse est habituellement CloseComplete, mais pourrait ĂȘtre ErrorResponse si des difficultĂ©s sont rencontrĂ©es lors de la libĂ©ration des ressources. Notez que fermer implicitement une requĂȘte prĂ©parĂ©e ferme tout portail ouvert qui Ă©tait construit par cette requĂȘte.

Le message Flush ne cause pas la gĂ©nĂ©ration d'une sortie spĂ©cifique mais force le backend Ă  renvoyer toutes les donnĂ©es en attente dans les buffers de sortie. Un Flush doit ĂȘtre envoyĂ© aprĂšs toute commande de requĂȘte Ă©tendue sauf Sync, si le client souhaite examiner les rĂ©sultats de cette commande avant de lancer d'autres commandes. Sans Flush, les messages renvoyĂ©s par le backend seront combinĂ©s dans le plus petit nombre de paquets pour minimiser la surcharge rĂ©seau.

Note

Le message Query en requĂȘte simple est approximativement Ă©quivalent Ă  une sĂ©rie Parse, Bind, Describe portail, Execute, Close, Sync, en utilisant une requĂȘte prĂ©parĂ©e et un portail non nommĂ©s et aucun paramĂštre. Une diffĂ©rence est qu'il acceptera plusieurs requĂȘtes SQL dans la chaĂźne de requĂȘte, rĂ©alisant automatiquement les sĂ©quences bind/describe/execute pour chaque requĂȘte, les unes Ă  la suite des autres. Une autre diffĂ©rence est qu'il ne renvoie pas les messages ParseComplete, BindComplete, CloseComplete et NoData.

53.2.4. Pipelines #

L'utilisation du protocole de requĂȘte Ă©tendue autorise les pipelines, autrement dit l'envoi d'une sĂ©rie de requĂȘtes sans attendre que les premiĂšres se terminent. Ceci rĂ©duit le nombre d'aller/retour rĂ©seau nĂ©cessaire pour terminer une sĂ©rie d'opĂ©rations. NĂ©anmoins, l'utilisateur doit faire attention au comportement souhaitĂ© si une des Ă©tapes Ă©choue car les requĂȘtes suivantes seront dĂ©jĂ  envoyĂ©es au serveur.

Une façon de gĂ©rer cela est de transformer la sĂ©rie complĂšte de requĂȘte en une seule transaction, donc de l'entourer des commandes BEGIN ... COMMIT. Cela n'aide cependant pas les personnes qui souhaiteraient que certaines commandes soient validĂ©es indĂ©pendamment des autres.

Le protocole de requĂȘte Ă©tendue fournit un autre moyen pour gĂ©rer cette problĂ©matique. Il s'agit d'oublier d'envoyer les messages Sync entre les Ă©tapes qui sont dĂ©pendantes. Comme, aprĂšs une erreur, le moteur ignorera les messages des commandes jusqu'Ă  ce qu'il trouve un message Sync, cela autorise les commandes ultĂ©rieures d'un pipeline d'ĂȘtre automatiquement ignorĂ©es si une commande prĂ©cĂ©dente Ă©choue, sans que le client ait Ă  gĂ©rer cela explicitement avec des commandes BEGIN et COMMIT. Les segments Ă  valider indĂ©pendamment dans le pipeline peuvent ĂȘtre sĂ©parĂ©es par des messages Sync.

Si le client n'a pas exĂ©cutĂ© un BEGIN explicite, alors chaque Sync implique un COMMIT implicite si les Ă©tapes prĂ©dĂ©dentes ont rĂ©ussi ou un ROLLBACK implicite si elles ont Ă©chouĂ©. NĂ©anmoins, il existe quelques commandes DDL (comme CREATE DATABASE) qui ne peuvent pas ĂȘtre exĂ©cutĂ©es dans un bloc de transaction. Si une de ces commandes est exĂ©cutĂ©e dans un pipeline, cela Ă©chouera sauf s'il s'agit de la premiĂšre commande du pipeline. De plus, en cas de succĂšs, cela forcera une validation immĂ©diate pour prĂ©server la cohĂ©rence de la base. De ce fait, un Sync suivant immĂ©diatement une des ces commandes n'aura pas d'effet autre que de rĂ©pondre avec ReadyForQuery.

Lors de l'utilisation de cette mĂ©thode, la fin du pipelin doit ĂȘtre dĂ©terminĂ©e en comptant les messages ReadyForQuery et en attendant que cela atteigne le nombre de Sync envoyĂ©s. Compter les rĂ©ponses de fin de commande n'est pas fiable car certaines commandes pourraient ĂȘtre ignorĂ©es et donc ne pas produire de message de fin.

53.2.5. Appel de fonction (Function Call) #

Le sous-protocole d'appel de fonction permet au client de demander un appel direct de toute fonction qui existe dans le catalogue systÚme pg_proc de la base de données. Le client doit avoir le droit d'exécution sur la fonction.

Note

Le sous-protocole d'appel de fonction est une ancienne fonctionnalitĂ© qu'il est certainement prĂ©fĂ©rable d'Ă©viter dans du nouveau code. Des rĂ©sultats similaires peuvent ĂȘtre accomplis en configurant une requĂȘte prĂ©parĂ©e qui exĂ©cute SELECT function($1, ...). Le cycle de l'appel de fonction peut ensuite ĂȘtre remplacĂ© avec les messages Bind/Execute.

Un cycle d'appel de fonction est initiĂ© par le client en envoyant un message FunctionCall au backend. Le backend envoie alors un ou plusieurs messages de rĂ©ponse suivant le rĂ©sultat de l'appel de fonction, et termine avec un message ReadyForQuery. Ce message informe le client qu'il peut envoyer une nouvelle requĂȘte ou un nouvel appel de fonction.

Les messages de rĂ©ponse possible provenant du backend sont :

ErrorResponse

Une erreur est survenue

FunctionCallResponse

L'appel de fonction s'est terminé et a renvoyé le résultat donné dans le message. (Notez que le protocole d'appel de fonction peut seulement gérer 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é. Le message ReadyForQuery sera toujours envoyé, que le traitement termine avec succÚs ou avec une erreur.

NoticeResponse

Un message d'avertissement a été lancé en relation avec l'appel de fonction. Les notes sont en plus des autres réponses, autrement dit le backend continuera à traiter la commande.

53.2.6. OpĂ©rations COPY #

La commande COPY permet une transfert de données en masse trÚs rapide du ou à partir du serveur. Les opérations de copie vers le serveur (copy-in) ou à partir du serveur (copy-out) font basculer la connexion dans un sous-protocole distinct, qui dure jusqu'à la fin de l'opération.

Le mode Copy-in (transfert de donnĂ©es vers le serveur) est initiĂ© quand le backend exĂ©cute une requĂȘte SQL COPY FROM STDIN. Le backend envoie un message CopyInResponse au client. Le client doit alors envoyer zĂ©ro ou plusieurs messages CopyData, formant un flux de donnĂ©es en entrĂ©e. (Les limites du message ne sont pas nĂ©cessaires liĂ©es aux limites des lignes, bien qu'il s'agit souvent d'un choix raisonnable.) Le client peut terminer le mode copy-in en envoyant soit un message CopyDone (permettant une fin avec succĂšs) ou un message CopyFail (qui causera l'Ă©chec de la requĂȘte SQL COPY avec un message d'erreur). Le backend annule alors le mode de traitement de commande dans lequel il Ă©tait entrĂ© au dĂ©but de la commande COPY, qui sera soit le protocole de requĂȘte simple ou celui de requĂȘte Ă©tendu. Il enverra ensuite soit un message CommandComplete (en cas de rĂ©ussite) soit un message ErrorResponse (dans le cas contraire).

Dans le cas d'une erreur dĂ©tectĂ© par le backend lors du mode copy-in (incluant la rĂ©ception d'un message CopyFail), le backend enverra un message ErrorResponse. Si la commande COPY a Ă©tĂ© lancĂ©e via un message de requĂȘte Ă©tendue, le backend ignorera maintenant les messages du client jusqu'Ă  la rĂ©ception d'un message Sync. AprĂšs, il enverra un message ReadyForQuery et retournera Ă  un traitement normal. Si la commande COPY a Ă©tĂ© lancĂ© via un message Query, le reste de ce message est ignorĂ© et le message ReadyForQuery est envoyĂ©. Dans tous les cas, les messages CopyData, CopyDone ou CopyFail suivants envoyĂ©s par le client seront simplement ignorĂ©s.

Le backend ignorera les messages Flush et Sync reçus lors du mode copy-in. La réception de tout autre message qui ne concerne pas la copie constitue une erreur qui annulera l'état copy-in comme décrit ci-dessus. (L'exception pour Flush et Sync est pour simplifier les bibliothÚques clientes qui envoient toujours Flush ou Sync aprÚs un message Execute, sans vérifier si la commande à exécuter est un COPY FROM STDIN.)

Le mode Copy-out (transfert de donnĂ©es Ă  partir du serveur) est initiĂ© lorsque le backend exĂ©cute une requĂȘte SQL COPY TO STDOUT. Le backend envoie un message CopyOutResponse au client, suivi par zĂ©ro ou plusieurs messages CopyData (toujours un par ligne), suivi par un message CopyDone. Le backend annulera alors le mode de traitement de commande dans lequel il Ă©tait avant le lancement de COPY. Enfin, il envoie CommandComplete. Le client ne peut pas annuler le transfert (sauf en fermant la connexion ou en envoyant une requĂȘte Cancel), mais il peut ignorer les messages CopyData et CopyDone indĂ©sirables.

Dans le cas d'une erreur détectée par le backend en mode copy-out, le backend lancera un message ErrorResponse et reviendra en traitement normal. Le client devrait traiter la réception de ErrorResponse comme terminant le mode copy-out.

Il est possible que les messages NoticeResponse et ParameterStatus soient mĂ©langĂ©s entre des messages CopyData ; les clients doivent gĂ©rer ces cas, et devraient ĂȘtre prĂ©parĂ©s aussi pour tout type de message asynchrone (voir Section 53.2.7). Sinon, tout type de message autre que CopyData ou CopyDone peut ĂȘtre traitĂ© comme terminant le mode copy-out.

Il existe un autre mode relatif Ă  la copie, appelĂ© copy-both, qui permet des transferts de donnĂ©es en masse et trĂšs rapides vers et Ă  partir du serveur. Le mode copy-both est initiĂ© quand un backend en mode walsender exĂ©cute une instruction START_REPLICATION. Le backend envoie un message CopyBothResponse au client. Le backend et le client peuvent ensuite envoyer des messages CopyData jusqu'Ă  ce que l'un des deux envoie un message CopyDone. Une fois que le client a envoyĂ© un message CopyDone, le connexion passe du mode copy-both au mode copy-out et le client ne peut plus envoyer de messages CopyData. De la mĂȘme façon, quand le serveur envoie un message CopyDone, la connexion passe en mode copy-in, et le serveur ne peut plus envoyer de messages CopyData. Une fois que les deux ont envoyĂ© un message CopyDone, le mode de copie est terminĂ©, et le backend revient au mode de traitement des commandes. Dans le cas oĂč une erreur est dĂ©tectĂ©e par le backend pendant le mode copy-both, le backend enverra un message ErrorResponse, ignorera les messages du client jusqu'Ă  ce qu'un message Sync ne soit reçu, puis enverra un message ReadyForQuery avant de revenir au traitement normal. Le client doit traiter la rĂ©ception d'un message ErrorResponse comme terminant la copie dans les deux directions ; aucun message CopyDone ne devra ĂȘtre envoyĂ© dans ce cas. Voir Section 53.4 pour plus d'informations sur le sous-protocole transmis sur le mode copy-both.

Les messages CopyInResponse, CopyOutResponse et CopyBothResponse incluent des champs qui informent le client du nombre de colonnes par ligne et des codes format utilisĂ©s pour chaque colonne. (Sur l'implĂ©mentation actuelle, toutes les colonnes d'une opĂ©ration COPY donnĂ©e utiliseront le mĂȘme format, mais le design des messages ne le force pas.)

53.2.7. OpĂ©rations asynchrones #

Il existe diffĂ©rents cas oĂč le backend 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 aussi ces messages Ă  tout moment, mĂȘme quand une requĂȘte n'est pas en cours. Au minimum, un client devrait vĂ©rifier ces cas avant de continuer Ă  lire la rĂ©ponse Ă  une requĂȘte.

Il est possible que des messages NoticeResponse soient gĂ©nĂ©rĂ©s Ă  cause d'une activitĂ© externe ; par exemple, si l'administrateur de la base de donnĂ©es ordonne un arrĂȘt « rapide Â» de la base de donnĂ©es, le backend enverra un message NoticeResponse indiquant ce fait avant de fermer la connexion. Les clients doivent donc ĂȘtre prĂȘts Ă  accepter et afficher les messages NoticeResponse, mĂȘme quand la connexion n'exĂ©cute pas de requĂȘtes.

Les messages ParameterStatus seront gĂ©nĂ©rĂ©s Ă  chaque fois qu'une valeur change pour un des paramĂštres pour lesquels le backend pense que le client doit ĂȘtre notifiĂ©. La plupart du temps, ceci survient en rĂ©ponse Ă  une commande SQL SET exĂ©cutĂ©e par le client, et ce cas est effectivement synchrone -- mais il est aussi possible qu'un changement de valeur de paramĂštres survient parce que l'administrateur a modifiĂ© un fichier de configuration, puis envoyĂ© le signal SIGHUP au serveur. De plus, si une commande SET est annulĂ©, un message ParameterStatus appropriĂ© sera gĂ©nĂ©rĂ© pour rapporter la valeur effective.

Il existe actuellement un ensemble, codĂ© en dur, de paramĂštres pour lesquels un message ParameterStatus sera gĂ©nĂ©rĂ©. En voici la liste :

application_nameis_superuser
client_encodingscram_iterations
DateStyleserver_encoding
default_transaction_read_onlyserver_version
in_hot_standbysession_authorization
integer_datetimesstandard_conforming_strings
IntervalStyleTimeZone

(default_transaction_read_only et in_hot_standby ne sont pas rapportĂ©s avant la version 14 ; scram_iterations n'est par rapportĂ© avant la version 16.) Notez que server_version, server_encoding et integer_datetimes sont des pseudos-paramĂštres qui ne peuvent pas changer aprĂšs le dĂ©marrage. Cet ensemble pourrrait ĂȘtre modifiĂ© dans le futur, voire mĂȘme devenir configurable. En consĂ©quence, un client devrait simplement ignorer les messages ParameterStatus pour les paramĂštres qu'il ne comprend pas ou qui ne sont pas intĂ©ressants pour lui.

Si un client exĂ©cute une commande LISTEN, alors le backend enverra un message NotificationResponse (Ă  ne pas confondre avec NoticeResponse !) Ă  chaque fois qu'une commande NOTIFY est exĂ©cutĂ©e pour le mĂȘme nom de canal.

Note

Actuellement, NotificationResponse peut seulement ĂȘtre envoyĂ© en dehors d'une transaction, et donc il ne surviendra pas au milieu d'une sĂ©rie de rĂ©ponses Ă  des commandes, bien qu'il puisse survenir juste avant un message ReadyForQuery. Il est dĂ©conseillĂ© de concevoir la logique du client comme assumant cela. Une bonne pratique est d'ĂȘtre capable d'accepter un message NotificationResponse Ă  tout moment dans le protocole.

53.2.8. Annuler des requĂȘtes en cours #

Lors du traitement d'une requĂȘte, le client pourrait demander l'annulation de la requĂȘte. La demande d'annulation n'est pas envoyĂ©e directement sur la connexion ouverte sur le backend pour des raisons d'efficacitĂ© de l'implĂ©mentation : nous ne voulons pas avoir le backend en train de vĂ©rifier constamment pour une nouvelle entrĂ©e en provenance du client lors du traitement de la requĂȘte. Les demandes d'annulation sont relativement peu frĂ©quentes, donc nous pouvons les rendre un peu compliquĂ©es pour Ă©viter un pĂ©nalitĂ© sur les cas normaux.

Pour demander une annulation, le client ouvre une nouvelle connexion au serveur et envoie un message CancelRequest, plutÎt que le message StartupMessage qui serait habituellement envoyé lors d'une nouvelle connexion. Le serveur traitera cette demande, puis fermera la connexion. Pour des raisons de sécurité, aucune réponse directe n'est faite au message de demande d'annulation.

Un message CancelRequest sera ignorĂ© sauf s'il contient la mĂȘme donnĂ©e clĂ© (PID et clĂ© sĂ©crĂšte) passĂ©e au client lors de la connexion. Si la demande correspond au PID et Ă  la clĂ© secrĂšte pour un backend en cours d'exĂ©cution d'une requĂȘte, le traitement de la requĂȘte est annulĂ©. (Dans l'implĂ©mentation actuelle, ceci se fait en envoyant un signal spĂ©cial au processus backend qui traite la requĂȘte.)

Le signal d'annulation pourrait avoir un effet ou pas -- par exemple, s'il arrive aprĂšs que le backend ait terminĂ© le traitement de la requĂȘte, il n'aura aucun effet. Si l'annulation est rĂ©elle, la commande en cours est arrĂȘtĂ©e avec un message d'erreur.

Le rĂ©sultat de tout ceci est que, pour des raisons de sĂ©curitĂ© et d'efficacitĂ©, le client n'a pas de façon de savoir si la requĂȘte d'annulation a rĂ©ussi. Il doit continuer d'attendre que le backend rĂ©ponse Ă  la requĂȘte. ExĂ©cuter une annulation amĂ©liore simplement les chances que la requĂȘte en cours termine rapidement, et amĂ©liore les chances qu'elle Ă©chouera avec un message d'erreur au lieu d'un succĂšs.

Comme la requĂȘte d'annulation est envoyĂ©e sur une nouvelle connexion au serveur et non pas via le lien standard de communication client/backend, il est possible d'envoyer une demande d'annulation pour tout processus, pas uniquement pour le client dont la requĂȘte est Ă  annuler. Ceci apporte une flexibilitĂ© supplĂ©mentaire lors de la construction d'applications multi-processus. Cela introduit aussi un risque de sĂ©curitĂ© dans le fait que des personnes non autorisĂ©es pourraient tenter d'annuler des requĂȘtes. Le risque de sĂ©curitĂ© est mitigĂ© par le besoin d'une clĂ© secrĂšte gĂ©nĂ©rĂ©e dynamiquement, Ă  fournir lors de demandes d'annulation.

53.2.9. ArrĂȘt #

La procĂ©dure normale et propre d'arrĂȘt est que le client envoie un message Terminate et ferme immĂ©diatement la connexion. Sur rĂ©ception de ce message, le backend ferme la connexion et quitte.

Dans les rares cas (tel qu'un arrĂȘt de base demandĂ© par l'administrateur), le backend pourrait se dĂ©connecter sans demande du client. Dans ce genre de cas, le backend tentera d'envoyer un message d'erreur ou d'information, donnant la raison de la dĂ©connexion avant de fermer la connexion.

D'autres scĂ©narios d'arrĂȘt surgissent de diffĂ©rents cas d'Ă©chec, tel qu'un core dump d'un bout ou de l'autre, une perte du lien de communication, une perte de la synchronisation sur les messages, etc. Si soit le client soit le backend voit une fin inattendue de la connexion, il doit faire son mĂ©nage et quitter. Le client a l'option de lancer un nouveau backend en recontactant le serveur s'il ne veut pas quitter. Fermer la connexion est aussi conseillĂ© si un type de message non reconnu est reçu car cela indique probablement la perte de synchronisation des messages.

Pour un arrĂȘt normal ou anormal, toute transaction ouverte est annulĂ©e, et non pas validĂ©e. NĂ©anmoins, notez que si un client se dĂ©connecter alors qu'une requĂȘte autre qu'un SELECT est en cours de traitement, le backend finira probablement la requĂȘte sans se rendre compte de la dĂ©connexion. Si la requĂȘte est exĂ©cutĂ©e en dehors d'un bloc de transaction (sĂ©quence BEGIN ... COMMIT), alors ses rĂ©sultats pourraient ĂȘtre validĂ©es avant que le backend ne s'aperçoive de la dĂ©connexion.

53.2.10. Chiffrement SSL de la session #

Si PostgreSQL a Ă©tĂ© compilĂ© avec le support de SSL, les communications client/backend peuvent ĂȘtre chiffrĂ©s en utilisant SSL. Cela fournit une sĂ©curitĂ© sur la communication pour les environnements oĂč des attaquants pourraient ĂȘtre en mesure de capturer le trafic de la session. Pour plus d'informations sur le chiffrement des sessions PostgreSQL avec SSL, voir Section 18.9.

Pour initier une connexion chiffrĂ©e avec SSL, le client envoie dĂšs le dĂ©but un message SSLRequest au lieu de StartupMessage. Le serveur rĂ©pond avec un seul octet contenant S ou N, indiquant s'il est, respectivement, prĂȘt ou non Ă  utiliser SSL. Le client pourrait fermer la connexion Ă  ce point s'il n'est pas satisfait par la rĂ©ponse. Pour continuer aprĂšs un S, le client rĂ©alise une poignĂ©e de main SSL de dĂ©marrage (non dĂ©crit ici, car partie de la spĂ©cification SSL) avec le serveur. Si la poignĂ©e de main est rĂ©ussie, le client continue avec l'habituel StartupMessage. Dans ce cas, le message StartupMessage et toutes les donnĂ©es qui suivent seront chiffrĂ©s avec SSL. Pour continuer aprĂšs un N, le client envoie le message habituel StartupMessage et continue sans chiffrement. (Il est aussi possible de lancer un message GSSENCRequest aprĂšs une rĂ©ponse N pour tenter l'utilisation du chiffrement GSSAPI Ă  la place du chiffrement SSL.)

Le client doit aussi se prĂ©parer Ă  gĂ©rer un message ErrorMessage en rĂ©ponse Ă  un message SSLRequest. Le client ne doit pas afficher ce message d'erreur Ă  l'utilisateur/application car le serveur n'a pas Ă©tĂ© authentifiĂ© (CVE-2024-10977). Dans ce cas, la connexion doit ĂȘtre fermĂ©e mais le client pourrait choisir d'ouvrir une connexion propre et continuer sans demander SSL.

Quand le chiffrement SSL peut ĂȘtre rĂ©alisĂ©, le serveur est supposĂ© envoyer l'octet S, puis attendre que le client initie une poignĂ©e de main SSL. Si des octets supplĂ©mentaires sont disponibles en lecture, cela signifie probablement qu'une attaque de type man-in-the-middle est en cours et tente de rĂ©aliser la technique buffer-stuffing (CVE-2021-23222). Les clients doivent ĂȘtre codĂ©s soit pour lire exactement un octet de la socket avant de renvoyer la socket Ă  leur bibliothĂšque SSL ou de la traiter comme une violation du protocole s'ils voient qu'ils ont dĂ©jĂ  des octets supplĂ©mentaires.

De la mĂȘme façon, le serveur s'attend Ă  ce que le client ne commence pas la nĂ©gociation SSL tant qu'il n'a pas reçu la rĂ©ponse en un octet du serveur Ă  la demande de SSL. Si le client commence la nĂ©gociation SSL immĂ©diatement, sans attendre la rĂ©ception de la rĂ©ponse du serveur, cela peut rĂ©duire la latence de connexion d'un aller/retour. NĂ©anmoins, cela a un coĂ»t : ne pas ĂȘtre capable de gĂ©rer le cas oĂč le serveur envoie une rĂ©ponse nĂ©gative Ă  la demande de SSL. Dans ce cas, au lieu de continuer avec une connexion GSSAPI ou non chiffrĂ©e ou une erreur de procotole, le serveur se dĂ©connectera tout simplement.

Un message initial SSLRequest peut aussi ĂȘtre utilisĂ© dans une connexion en cours d'ouverture pour envoyer un message CancelRequest.

Une autre façon d'initier le chiffrement SSL est disponible. Le serveur reconnaitra les connexions qui commencent immĂ©diatement une nĂ©gociation SSL sans paquet SSLRequest. Une fois la connexion SSL Ă©tablie, le serveur attendra un paquet standard startup-request et continuera la nĂ©gociation sur un canal chiffrĂ©. Dans ce cas, toute autre requĂȘte de chiffrement sera refusĂ©e. Cette mĂ©thode n'est pas prĂ©fĂ©rĂ©e pour les outils gĂ©nĂ©ralistes car elle ne peut pas nĂ©gotier le meilleur chiffrement de la connexion et ne peut pas gĂ©rer les connexions non chiffrĂ©es. Cependant, c'est utile pour des environnements oĂč le serveur et le client sont contrĂŽlĂ©s ensemble. Dans ce cas, cela Ă©vite une latence d'un aller/retour, et permet l'utilisation d'outils rĂ©seaux dĂ©pendants de connexions standards SSL. Lors de l'utilisation de connexions SSL de ce style, le client doit utiliser l'extension ALPN dĂ©finie par la RFC 7301 pour se protĂ©ger contre les attaques de confusion de protocole. Le protocole PostgreSQL est enregistrĂ© sous le nom "postgresql" sur le resgistre IANA TLS ALPN Protocol IDs.

Alors que le protocole lui-mĂȘme ne fournit pas de moyens pour que le serveur force un chiffrement SSL, l'administrateur peut configurer le serveur de telle façon qu'il rejette les sessions non chiffrĂ©es. Cela se fait pendant la vĂ©rification de l'authentification.

53.2.11. Chiffrement GSSAPI des sessions #

Si PostgreSQL a Ă©tĂ© compilĂ© avec le support de GSSAPI, les communications client/backend peuvent ĂȘtre chiffrĂ©es en utilisant GSSAPI. Ceci fournit une sĂ©curitĂ© de la communication dans les environnements oĂč les attaquants pourraient ĂȘtre en mesure de capturer le trafic de la session. Pour plus d'informations sur le chiffrement des sessions PostgreSQL avec GSSAPI, voir Section 18.10.

Pour initier une connexion chiffrée avec GSSAPI, le client doit au départ envoyer un message GSSENCRequest plutÎt qu'un message StartupMessage. Le serveur répond avec un seul octet contenant G ou N, indiquant s'il est, respectivement, disposé ou non pour réaliser un chiffrement GSSAPI. Le client pourrait fermer la connexion à ce stage s'il n'est pas satisfait par la réponse. Pour continuer aprÚs un G, en utilisant les fonctions C de GSSAPI comme indiqué dans la RFC 2744 ou un équivalent, le client réalise une initialisation de GSSAPI en appelant gss_init_sec_context() dans une boucle et en envoyant le résultat au serveur, en commençant avec une entrée vide, et en continuant avec chaque résultat du serveur jusqu'à la fin. Lors de l'envoi de résultats de gss_init_sec_context() au serveur, il ajoute la longueur du message sous la forme d'un entier sous quatre octets dans l'ordre des octets du réseau. Pour continuer aprÚs un N, il envoie le message habituel StartupMessage puis continue sans chiffrement. (Une autre solution est de lancer un message SSLRequest aprÚs une réponse N pour tenter l'utilisation d'un chiffrement SSL à la place du chiffrement GSSAPI.)

Le client doit aussi ĂȘtre prĂ©parĂ© Ă  gĂ©rer une rĂ©ponse ErrorMessage pour GSSENCRequest provenant du serveur. Le client ne doit pas afficher ce message d'erreur Ă  l'utilisation/application carle serveur n'a pas Ă©tĂ© authentifiĂ© (CVE-2024-10977). Dans ce cas, la connexion doit ĂȘtre termĂ©e, mais le client peut choisir d'ouvrir une nouvelle connexion et de continuer sans demander de chiffrement GSSAPI.

Quand le chiffrement GSSAPI peut ĂȘtre rĂ©alisĂ©, le serveur est supposĂ© envoyer l'octet G, puis attendre que le client initie une poignĂ©e de main GSSAPI. Si des octets supplĂ©mentaires sont disponibles en lecture, cela signifie probablement qu'une attaque de type man-in-the-middle est en cours et tente de rĂ©aliser la technique buffer-stuffing (CVE-2021-23222). Les clients doivent ĂȘtre codĂ©s soit pour lire exactement un octet de la socket avant de renvoyer la socket Ă  leur bibliothĂšque GSSAPI ou de la traiter comme une violation du protocole s'ils voient qu'ils ont dĂ©jĂ  des octets supplĂ©mentaires Ă  lire.

Un message initial GSSENCRequest peut aussi ĂȘtre utilisĂ© dans une connexion en cours d'ouverture pour envoyer un message CancelRequest.

Une fois que le chiffrement GSSAPI a Ă©tĂ© Ă©tabli avec succĂšs, le client utilise la fonction gss_wrap() pour chiffrer le message StartupMessage habituel et toutes les donnĂ©es qui suivent, ajoutant la longueur du rĂ©sultat de gss_wrap() sous la forme d'un entier sous quatre octets dans l'ordre d'octets du rĂ©seau Ă  la charge chiffrĂ©e rĂ©elle. Notez que le serveur acceptera seulement des paquets chiffrĂ©s provenant du client qui font moins de 16 Ko ; gss_wrap_size_limit() devrait ĂȘtre utilisĂ© par le client pour dĂ©terminer la taille du message non chiffrĂ© qui sera contenu dans cette limite, et les messages plus larges devront ĂȘtre divisĂ©s en plusieurs appels Ă  gss_wrap(). Les segments typiques sont 8 Ko de donnĂ©es non chiffrĂ©es, rĂ©sultant en des paquets chiffrĂ©s lĂ©gĂšrement supĂ©rieurs Ă  8 Ko mais bien contenu dans les 16 Ko maximum. Il est attendu que le serveur n'envoie pas de paquets de plus de 16 ;Ko au client.

Alors que le protocole lui-mĂȘme ne fournit pas de moyens pour que le serveur force un chiffrement GSSAPI, l'administrateur peut configurer le serveur de telle façon qu'il rejette les sessions non chiffrĂ©es. Cela se fait pendant la vĂ©rification de l'authentification.