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 52.7). 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.
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). Pour toutes les mĂ©thodes d'authentification, sauf GSSAPI, SSPI et SASL, il y a au maximum une requĂȘte et une rĂ©ponse. Avec certaines mĂ©thodes, aucune rĂ©ponse du client n'est nĂ©cessaire, et aucune demande d'authentification n'est alors effectuĂ©e. Pour GSSAPI, SSPI et SASL, plusieurs Ă©changes de paquets peuvent ĂȘtre nĂ©cessaires pour terminer l'authentification.
Le cycle d'authentification se termine lorsque le serveur rejette la tentative de connexion (ErrorResponse) ou l'accepte (AuthenticationOk).
Les messages possibles du serveur dans cette phase sont :
La tentative de connexion a été rejetée. Le serveur ferme immédiatement la connexion.
L'échange d'authentification s'est terminé avec succÚs.
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. Ce n'est plus supporté.
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.
Le client doit envoyer un PasswordMessage contenant le mot de passe
(avec le nom de l'utilisateur) chiffré en MD5, puis chiffré de
nouveau avec un salt aléatoire sur 4 octets indiqué dans le message
AuthenticationMD5Password. S'il s'agit du bon mot de passe, le
serveur répond avec un AuthenticationOk, sinon il répond avec un
ErrorResponse. Le PasswordMessage rĂ©el peut ĂȘtre calculĂ© en SQL
avec concat('md5',
md5(concat(md5(concat(password, username)), random-salt)))
.
(Gardez en tĂȘte que la fonction md5()
renvoie
son résultat sous la forme d'une chaßne hexadécimale.)
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. (Ce type de message n'est envoyĂ© que par des serveurs dont la version est antĂ©rieure Ă la 9.1. Il pourrait ĂȘtre supprimĂ© de la spĂ©cification du protocole.)
L'interface doit maintenant initier une négociation GSSAPI. L'interface doit envoyer un GSSResponse avec la premiÚre partie du flux de données GSSAPI en réponse à ceci. Si plus de messages sont nécessaires, le serveur répondra avec AuthenticationGSSContinue.
L'interface doit maintenant initier une négociation SSPI. L'interface doit envoyer un GSSResponse avec la premiÚre partie du flux de données SSPI en réponse à ceci. Si plus de messages sont nécessaires, le serveur répondra avec AuthenticationGSSContinue.
Ce message contient les données de la réponse de l'étape précédente pour la négociation GSSAPI ou SSPI (AuthenticationGSS ou un précédent AuthenticationGSSContinue). Si les données GSSAPI dans ce message indique que plus de données sont nécessaire pour terminer l'authentification, l'interface doit envoyer cette donnée dans un autre GSSResponse. Si l'authentification GSSAPI ou SSPI est terminée par ce message, le serveur enverra ensuite AuthenticationOk pour indiquer une authentification réussie ou ErrorResponse pour indiquer l'échec.
L'interface doit maintenant initier une négociation SASL en utilisant un des mécanismes SASL listés dans le message. L'interface enverra un 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 AuthenticationSASLContinue. Voir Section 52.5 pour les détails.
Ce message contient les données de challenge provenant de l'étape précédente de la négociation SASL (AuthenticationSASL ou un précédent AuthenticationSASLContinue). L'interface doit répondre avec un message SASLResponse.
L'authentification SASL s'est terminée avec les données supplémentaires du mécanisme sélectionné pour le client. Le serveur enverra ensuite AuthenticationOk pour indiquer le succÚs de l'authentification ou un ErrorResponse pour indiquer l'échec. Ce message est seulement envoyé si le mécanisme SASL indique l'envoi de données supplémentaires du serveur au client à la fin.
Le serveur ne supporte par la version mineure du protocole réclamée
par le client mais supporte une version précédente du protocole. Ce
message indique la plus haute version mineure supportée. Ce message
sera aussi envoyé si le client demande des options de protocole non
supportées (commençant par _pq_.
) dans le paquet de
démarrage. Ce message sera suivi par un ErrorResponse ou un message
indiquant le succÚs ou l'échec de l'authentification.
Si le client ne supporte pas la méthode 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) ou que le serveur décline le support de la version mineure du protocole demandée (NegotiateProtocolVersion) 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 :
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.
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 52.2.7 pour plus de détails. Le client ne
devrait pas répondre à ce message mais continuer à attendre un message
ReadyForQuery.
Le lancement est terminé. Le client peut dÚs lors envoyer des commandes.
Le lancement a échoué. La connexion est fermée aprÚs l'envoi de ce message.
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.
En protocole Simple Query, un cycle 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 :
Commande SQL terminée normalement.
Le serveur est prĂȘt Ă copier des donnĂ©es du client vers une table ; voir Section 52.2.6.
Le serveur est prĂȘt Ă copier des donnĂ©es d'une table vers le client ; voir Section 52.2.6.
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.
Un des ensembles de lignes retournĂ©s par une requĂȘte
select
, fetch
...
Une chaĂźne de requĂȘte vide a Ă©tĂ© reconnue.
Une erreur est survenue.
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.
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 52.2.6. 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 Simple Query, 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é.
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 52.2.7 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 Extended Query divise le protocole Simple Query 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.
Un type de paramĂštre peut ĂȘtre laissĂ© non spĂ©cifiĂ© en le positionnant
à O, ou en créant un tableau d'OID de type plus court que le nombre de
paramĂštres ($
n
) utilisés
dans la chaĂźne de requĂȘte.
Un autre cas particulier est d'utiliser void
comme type de
paramĂštre (c'est Ă dire l'OID du pseudo-type void
). Cela
permet d'utiliser des paramĂštres dans des fonctions en tant qu'argument
OUT.
Généralement, il n'y a pas de contexte dans lequel void
peut ĂȘtre utilisĂ©, mais si un tel paramĂštre apparaĂźt dans les arguments
d'une fonction, il sera simplement ignoré.
Par exemple, un appel de fonction comme
foo($1,$2,$3,$4)
peut correspondre Ă une fonction avec
2 arguments IN et 2 autres OUT si $3
et
$4
sont spécifiés avec le type void
.
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 Simple Query, 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 message Simple 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 autre 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. (Si des
paramÚtres sont déclarés à void
dans le message Parse,
il faut passer NULL comme valeur associée dans le message Bind.)
Bind spécifie aussi le format à utiliser pour toutes les données
renvoyĂ©es par la requĂȘte ; le format peut ĂȘtre spĂ©cifiĂ© globalement
ou par colonne. La réponse est soit BindComplete, soit ErrorResponse.
Le choix entre sortie texte et binaire est déterminé par les codes
de format donnés dans Bind, quelle que 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 Extended Query.
La planification de la requĂȘte survient gĂ©nĂ©ralement quand le message Bind est traitĂ©. Si la requĂȘte prĂ©parĂ©e n'a pas de paramĂštre ou si elle est exĂ©cutĂ©e de façon rĂ©pĂ©tĂ©e, le serveur peut sauvegarder le plan créé et le rĂ©-utiliser lors des appels suivants Ă Bind pour la mĂȘme requĂȘte prĂ©parĂ©e. NĂ©anmoins, il ne le fera que s'il estime qu'un plan gĂ©nĂ©rique peut ĂȘtre créé en Ă©tant pratiquement aussi efficace qu'un plan dĂ©pendant des valeurs des paramĂštres. Cela arrive de façon transparente en ce qui concerne le protocole.
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.
(à noter qu'un message Simple 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 autre 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ĂȘmes que celles dĂ©crites ci-dessus pour les requĂȘtes lancĂ©es via le protocole Simple Query, 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 Extended Query,
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 Extended Query, 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.
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 Extended Query.
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 si 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.
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 Extended Query, Ă 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.
Le message Simple Query est approximativement Ă©quivalent Ă la sĂ©quence 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.
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 forcera une validation immédiate en cas de succÚs pour
préserver la cohérence de la base. 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 ne pas ĂȘtre exĂ©cutĂ©es et donc ne pas produire de message de fin.
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.
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 :
Une erreur est survenue.
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.
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.
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 serveur 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
, soit les protocoles Simple Query ou Extended Query. Il
enverra enfin CommandComplete (en cas de succĂšs) ou ErrorResponse (sinon).
Si le serveur détecte une 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
Extended Query, 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 comme terminant le mode « copy-out ».
Il est possible que les messages NoticeResponse et ParameterStatus soient entremĂȘlĂ©s avec des messages CopyData ; les interfaces doivent gĂ©rer ce cas, et devraient ĂȘtre aussi prĂ©parĂ©es Ă d'autres types de messages asynchrones (voir Section 52.2.7). Sinon, tout type de message autre que CopyData et CopyDone pourrait ĂȘtre traitĂ© comme terminant le mode copy-out.
Il existe un autre mode relatif à Copy appelé Copy-both. Il permet
un transfert de données en flot à grande vitesse vers
et Ă partir du serveur. Le mode Copy-both est
initié quand un processus serveur en mode walsender exécute une
instruction START_REPLICATION
. Le processus
serveur envoie un message CopyBothResponse au client. Le processus
serveur et le client peuvent ensuite envoyer des messages CopyData
jusqu'Ă ce que l'un des deux envoie un message CopyDone. AprĂšs que le client
ait envoyé un message CopyDone, la connexion se transforme en mode copy-out,
et le client ne peut plus envoyer des 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 cÎtés ont envoyé un message CopyDone, le mode copie est terminé
et le processus serveur retourne dans le mode de traitement des commandes.
Si une erreur est détectée par le serveur pendant le mode copy-both, le
processus serveur enverra un message ErrorResponse, ignorera les messages
du client jusqu'à réception d'un message Sync message, puis enverra un
message ReadyForQuery avant de continuer le traitement habituel. Le client
doit traiter la réception d'un ErrorResponse comme une fin de la copie dans
les deux sens ; aucun CopyDone ne doit ĂȘtre envoyĂ© dans ce cas. Voir
Section 52.6 pour plus d'informations sur le
sous-protocole transmis pendant le mode copy-both.
Les messages CopyInResponse, CopyOutResponse et CopyBothResponse
incluent des champs qui informent le client du nombre de colonnes
par ligne et les codes de format utilisés par chaque colonne. (Avec
l'implémentation courante, toutes les colonnes d'une opération
COPY
donnĂ©e utiliseront le mĂȘme format mais la
conception du message ne le suppose pas.)
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 ajouté 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
,
server_encoding
,
client_encoding
,
application_name
,
is_superuser
,
session_authorization
,
DateStyle
,
IntervalStyle
,
TimeZone
,
integer_datetimes
, et
standard_conforming_strings
.
(server_encoding
, TimeZone
et
integer_datetimes
n'ont pas été reportés par les
sorties avant la 8.0 ; standard_conforming_strings
n'a pas été reporté par les sorties avant la 8.1 ;
IntervalStyle
n'a pas été reporté par les sorties
avant la 8.4;
application_name
n'a pas été reporté par les sorties
avant la 9.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 le canal de mĂȘme nom.
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.
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 fournir une flexibilitĂ© supplĂ©mentaire 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.
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.
Si PostgreSQL a Ă©tĂ© compilĂ© 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 18.9.
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. (De tels serveurs sont maintenant trĂšs anciens, et ne doivent certainement plus exister.) Dans ce cas, la connexion doit ĂȘtre fermĂ©e, mais le client peut choisir d'ouvrir une nouvelle connexion et procĂ©der sans SSL.
Quand le chiffrement SSL doit ĂȘtre rĂ©alisĂ©, le serveur
doit envoyer seulement l'octet S
, puis attendre que le
client initie une poignée de main SSL. Si des octets
supplémentaires sont disponibles en lecture à ce moment, cela pourrait
signifier qu'une attaque man-in-the-middle
tente de réaliser une attaque
buffer-stuffing
(CVE-2021-23222).
Les clients doivent ĂȘtre codĂ©s pour soit lire exactement un octet du
socket avant de rendre le socket Ă la bibliothĂšque SSL, soit traiter
comme une violation de protocole tout octet supplémentaire.
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.