Protocolo

Esta sección describe el flujo de mensajes. Existen cuatro tipos diferentes de flujo dependiendo del estado de la conexión: inicio, consulta, llamada de función y final. Existen tambien provisiones especiales para notificación de respuestas y cancelación de comandos, que pueden ocurrir en cualquier instante despues de la fase de inicio.

Inicio

El inicio se divide en fase de autentificación y fase de arranque del backend.

Inicialmente, el frontend envía un StartupPacket. El postmaster utiliza esta información y el contenido del fichero pg_hba.conf(5) para determinar que método de autentificación debe emplear. El postmaster responde entonces con uno de los siguientes mensajes:

ErrorResponse

El postmaster cierra la comunicación inmediatamente.

AuthenticationOk

El postmaster entonces cede la comunicación al backend. El postmaster no toma parte en la comunicación posteriormente.

AuthenticationKerberosV4

El frontend debe tomar parte en una diálogo de autentificación Kerberos V4 (no descrito aquí) con el postmaster. En caso de éxito, el postmaster responde con un AuthenticationOk, en caso contrario responde con un ErrorResponse.

AuthenticationKerberosV5

El frontend debe tomar parte en un diálogo de autentificación Kerberos V5 (no descrito aquí) con el postmaster. En caso de éxito, el postmaster responde con un AuthenticationOk, en otro caso responde con un ErrorResponse.

AuthenticationUnencryptedPassword

El frontend debe enviar un UnencryptedPasswordPacket. Si este es el password correcto, el postmaster responde con un AuthenticationOk, en caso contrario responde con un ErrorResponse.

AuthenticationEncryptedPassword

El frontend debe enviar un EncryptedPasswordPacket. Si este esel password correcto, el postmaster responde con un AuthenticationOk, en caso contrario responde con un ErrorResponse.

Si el frontend no soporta el método de autentificación requerido por el postmaster, debería cerrar inmediatamente la conexión.

Despues de enviar AuthenticationOk, el postmaster intenta lanzar un proceso backend. Como esto podría fallar, o el backend podría encontrar un error durante el arranque, el frontend debe esperar por una confirmación de inicio correcto del backend. El frontend no debería enviar mensajes en este momento. Los posibles mensajes procedentes del backend durante esta fase son:

BackendKeyData

Este mensaje es enviado despues de un inicio correcto del backend. Proporciona una clave secreta que el frontend debe guardar is quiere ser capaz de enviar peticiones de cancelación más tarde. El frontend no debería responder a este mensaje, pero podría continuar escuchando por un mensaje ReadyForQuery.

ReadyForQuery

El arranque del backend tuvo éxito. El frontend puede ahora enviar mensajes de peticiones o llamandas a función.

ErrorResponse

El arranque del backend no tuvo éxito. La conexión es cerrada despues de enviar este mensaje.

NoticeResponse

Se envía un mensaje de advertencia. El frontend debería mostrar un mensaje pero debería continuar a la espera de un mensaje ReadyForQuery o ErrorResponse.

El mensaje ReadyForQuery es el mismo que el backend debe enviar despues de cada ciclo de consulta. Dependiendo de las necesiades de codificado del frontend, es razonable considerar ReadyForQuery como iniciando un ciclo de consulta (y entonces BackendKeyData indica una conclusión correcta de la fase de inicio), o considerar ReadyForQuery como finalizando la fase de arranque y cada subsiguiente ciclo de consulta.

Consulta

Un ciclo de consulta se inicia por el frontend enviando un mensaje Query al backend. El backend entonces envía uno o más mensajes de respuesta dependiendo del contenido de la cadea de consulta, y finalmente un mensaje ReadyForQuery. ReadyForQuery informa al frontend que puede enviar una nueva consulta o llamada de función de forma segura.

Los posibles mensajes del backend son:

CompletedResponse

Una sentencia SQL se completó con normalidad.

CopyInResponse

El backend está preparado para copiar datos del frontend a una relación. El frontend debería enviar entonces un mensaje CopyDataRows. El backend responde con un mensaje CompletedResponse con un tag de "COPY".

CopyOutResponse

El backend está listo para copiar datos de una relación al frontend. El envía entonces un mensaje CopyDataRows, y un mensaje CompletedResponse con un tag de "COPY".

CursorResponse

La consulta fue bien un insert(l), delete(l), update(l), fetch(l) o una sentencia select(l). Si la transacción ha sido abortada entonces el backend envía un mensaje CompletedResponse con un tag "*ABORT STATE*". En otro caso las siguientes respuesta son enviadas.

Para una sentencia insert(l), el backend envía un mensaje CompletedResponse con un tag de "INSERT oid rows" donde rows es el número de filas insertadas, y oid es el ID de objeto de la fila insertada si rows es 1, en otro caso oid es 0.

Para una sentencia delete(l), el backend envía un mensaje CompletedResponse con un tag de "DELETE rows" donde rows es el número de filas borradas.

Para una sentencia update(l) el backend envía un mensaje CompletedResponse con un tag de "UPDATE rows" donde rows es el número de filas modificadas.

para una sentencia fetch(l) o select(l), el backend envía un mensaje RowDescription. Es seguido después con un mensaje AsciiRow o BinaryRow (dependiendo de si fué especificado un cursor binario) para cada fila que es envíada al frontend. Por último, el backend envía un mensaje CompletedResponse con un tag de "SELECT".

EmptyQueryResponse

Se encontro una caden de consulta vacía. (La necesidad de distinguir este caso concreto es histórica).

ErrorResponse

Ocurrió un error.

ReadyForQuery

El procesado de la cadena de consulta se completó. Un mensaje seperado es enviado para indicar esto debido a que la cadena de consulta puede contener múltiples sentencias SQL. (CompletedResponse marca el final el procesado del una sentencia SQL, no de toda la cadena). Siempre se enviará ReadyForQuery, bien el procesado terminase con éxito o con error.

NoticeResponse

Un mensaje de advertencia fué enviado en relación con la consulta. Estas advertencias se envían en adición a otras respuestas, es decir, el backend continuará procesando la sentencia.

Un frontend debe estar preparado para aceptar mensaje ErrorResponse y NoticeResponse cuando se espere cualquier otro tipo de mensaje.

De hecho, es posible que NoticeResponse se reciba incluso cuando el frontned no está esperando ningún tipo de mensaje, es decir, cuando el backend está normalmente inactivo. En particular, el frontend puede solicitar la finalización del backend. En este caso se envía una NoticeResponse antes de cerrar la conexión. Se recomienda que el frontend compruebe esas advertencias asíncronas antes de enviar cada sentencia.

También, si el frontend envía cualquier comando listen(l), entonces debe estar preparado para aceptar mensajes NotificationResponse en cualquier momento. Véase más abajo.

Llamada a función

Un ciclo de llamada a función se inicia por el frontend enviando un mensaje FunctionCall al backend. El backend entonces envía uno o más mensajes de respueste dependiendo de los resultados de la llamada a función, y finalmente un mensaje ReadyForQuery. ReadyForQuery informa al frontend que puede enviar una nueva consulta o llamada a función de forma segura.

Los posibles mensajes de respuesta provinientes de backend son:

ErrorResponse

Ocurrió un error.

FunctionResultResponse

La llamada a función fue ejecutada y devolvió un resultado.

FunctionVoidResponse

La llamada a función fue ejecutada y no devolvió resultados.

ReadyForQuery

El procesado de la llamada a función se completó. ReadyForQuery se enviará siempre, aunque el procesado termine con éxito o error.

NoticeResponse

Un mensaje de advertencia se generó en relación con la llamada a función. Estas advertencias aparecen en adición a otras respuestas, es decir, el backend continuará procesando el comando.

El frontend debe estar preparado para aceptar mensajes ErrorResponse y NoticeResponse cuando se esperen otro tipo de mensajes. También si envía cualquier comando listen(l) debe estar preparado para aceptar mensajes NotificationResponse en cualquier momento, véase más abajo.

Respuestas de notificación

Si un frontend envía un comando listen(l), entonces el backend enviará un mensaje NotificationResponse (no se confunca con NoticeResponse!) cuando un comando notify(l) sea ejecutado para el mismo nombre de notificación.

Las respuestas de notificación son permitidas en cualquier punto en el protocolo (despues del inicio), excepto dentro de otro mensaje del backend. Así, el frontend debe estar preparado para reconocer un mensaje NotificationResponse cuando está esperando cualquier mensaje. De hecho debería ser capaz de manejar mensajes NotificationResponse incluso cuando no está envuelto en una consulta.

NotificationResponse

Un comando notify(l) ha sido ejecutado para un nombre para el que se ejecutó previamente un comando listen(l). Se pueden enviar notifiaciones en cualquier momento.

Puede merecer la pena apuntar que los nombres utilizados en los comandos listen y notify no necesitan tener nada que ver con los nombres de relaciones (tablas) y bases de datos SQL. Los nombres de notificación son simplemente nombres arbitrariamente seleccionados.

Cancelación de peticiones en progreso

Durante el procesado de una consulta, el frontend puede solicitar la cancelación de la consulta mediante el envío de una peticion apropiada al postmaster. La petición de cancelación no es enviada directamente al backend por razones de eficiencia de implementación: no deseamos tener al backend constantemente esperando nuevos datos del frontend durante el procesado de consultas. Las peticiones de cancelación deberían ser relativamente infrecuentes, por lo que las hacemos un poco mas voluminosas con el fín de evitar una penalización en el caso normal.

Para enviar una petición de cancelación, el frontend abre una nueva conexión con el postmaster y envía un mensaje CancelRequest, en vez del mensaje StartupPacket que enviaría normalmente en una nueva conexión. El postmaster procesará esta petición y cerrará la conexión. Por razones de seguridad, no se envía una respuesta directa al mensaje de cancelación.

Un mensaje CancelRequest será ignorado a menos que contenga los mismos datos clave (PID y clave secreta) enviados al frontend durante el inicio de la conexión. Si la petición contiene el PID e clave secreta el backend aborta el procesado de la consulta actual.

La señal de cancelación puede tener o no tener efectos - por ejemplo, si llega despues de que el backend haya finalizado de procesar la petición, entonces no tendrá efecto. Si la cancelación es efectiva, produce la terminación prematura del comando actual dando un mensaje de error.

La consecuencia de todo esto es que por razones tanto de seguridad como de eficiencia, el frontend no tiene forma directa de decidir cuando una petición de cancelación tuvo éxito. Debe continuar esperando hasta que el backend responda a al petición. Enviar unha petición de cancelación simplemente aumenta las probabilidades de que la consulta actual finalice pronto, y aumenta las probabilidades de que falle con un mensaje de error en vez de terminar con éxito.

Ya que la petición de cancelación es enviada al postmaster y no a través del enlace normal frontend/backend, es posible que cualquier proceso realice la petición, no sólo el frontend cuya consulta va a ser cancelada. Esto puede tener algún beneficio de cara a aumentar la flexibilidad al diseñar aplicaciones multi-proceso. Tambien introduce un riesgo de seguridad, ya que personas no autorizadas podrían intentar cancelar consultas. El riesgo de seguridad es afrontado requiriendo la clave secreta generada dinámicamente.

Finalización

El procedimiento de finalización normal es que el frontend envíe un mensaje Terminate y cierre inmediatamente la conexión. Al recibir el mensaje, el backend cierra inmediatamente la conexión y finaliza.

Una finalización anormal puede ocurrir debido a fallos de software (i.e. core dump) en cualquier extremo. Si el frontend o el backend ve un cierre inexperado de la conexión, debería liberar resursos y finalizar. El frontend tiene la opción de lanzar un nuevo backen recontactando el postmaster, si lo desea.