Postgresql 中文操作指南
55.2. Message Flow #
本节介绍每种消息类型的消息流和语义。(每条消息的确切表示的详细信息均显示在 Section 55.7 中。)连接状态取决于几种不同的子协议:启动、查询、函数调用、COPY 和终止。对于异步操作(包括通知响应和命令取消),还有特殊条款,这些操作可以在启动阶段后随时发生。
This section describes the message flow and the semantics of each message type. (Details of the exact representation of each message appear in Section 55.7.) There are several different sub-protocols depending on the state of the connection: start-up, query, function call, COPY, and termination. There are also special provisions for asynchronous operations (including notification responses and command cancellation), which can occur at any time after the start-up phase.
55.2.1. Start-up #
为了开始会话,前端打开与服务器的连接并发送启动消息。此消息包括用户以及用户想要连接的数据库的名称;它还标识要使用的特定协议版本。(启动消息可以选择包括运行时参数的其他设置。)然后,服务器使用此信息和它的配置文件(如 pg_hba.conf)的内容,来确定连接是否初步可接受,以及需要什么其他身份验证(如果有)。
To begin a session, a frontend opens a connection to the server and sends a startup message. This message includes the names of the user and of the database the user wants to connect to; it also identifies the particular protocol version to be used. (Optionally, the startup message can include additional settings for run-time parameters.) The server then uses this information and the contents of its configuration files (such as pg_hba.conf) to determine whether the connection is provisionally acceptable, and what additional authentication is required (if any).
然后,服务器发送一个适当的身份验证请求消息,前端必须用一个适当的身份验证响应消息(如密码)进行回复。对于除 GSSAPI、SSPI 和 SASL 之外的所有身份验证方法,最多一个请求和一个响应。在某些方法中,前端根本不需要任何响应,所以不会发生身份验证请求。对于 GSSAPI、SSPI 和 SASL,可能需要多次交换数据包才能完成身份验证。
The server then sends an appropriate authentication request message, to which the frontend must reply with an appropriate authentication response message (such as a password). For all authentication methods except GSSAPI, SSPI and SASL, there is at most one request and one response. In some methods, no response at all is needed from the frontend, and so no authentication request occurs. For GSSAPI, SSPI and SASL, multiple exchanges of packets may be needed to complete the authentication.
身份验证周期在服务器拒绝连接尝试(错误响应)或发送身份验证确定后结束。
The authentication cycle ends with the server either rejecting the connection attempt (ErrorResponse), or sending AuthenticationOk.
此阶段中服务器可能的消息包括:
The possible messages from the server in this phase are:
-
ErrorResponse
-
The connection attempt has been rejected. The server then immediately closes the connection.
-
-
AuthenticationOk
-
The authentication exchange is successfully completed.
-
-
AuthenticationKerberosV5
-
The frontend must now take part in a Kerberos V5 authentication dialog (not described here, part of the Kerberos specification) with the server. If this is successful, the server responds with an AuthenticationOk, otherwise it responds with an ErrorResponse. This is no longer supported.
-
-
AuthenticationCleartextPassword
-
The frontend must now send a PasswordMessage containing the password in clear-text form. If this is the correct password, the server responds with an AuthenticationOk, otherwise it responds with an ErrorResponse.
-
-
AuthenticationMD5Password
-
The frontend must now send a PasswordMessage containing the password (with user name) encrypted via MD5, then encrypted again using the 4-byte random salt specified in the AuthenticationMD5Password message. If this is the correct password, the server responds with an AuthenticationOk, otherwise it responds with an ErrorResponse. The actual PasswordMessage can be computed in SQL as concat('md5', md5(concat(md5(concat(password, username)), random-salt))). (Keep in mind the md5() function returns its result as a hex string.)
-
-
AuthenticationGSS
-
The frontend must now initiate a GSSAPI negotiation. The frontend will send a GSSResponse message with the first part of the GSSAPI data stream in response to this. If further messages are needed, the server will respond with AuthenticationGSSContinue.
-
-
AuthenticationSSPI
-
The frontend must now initiate an SSPI negotiation. The frontend will send a GSSResponse with the first part of the SSPI data stream in response to this. If further messages are needed, the server will respond with AuthenticationGSSContinue.
-
-
AuthenticationGSSContinue
-
This message contains the response data from the previous step of GSSAPI or SSPI negotiation (AuthenticationGSS, AuthenticationSSPI or a previous AuthenticationGSSContinue). If the GSSAPI or SSPI data in this message indicates more data is needed to complete the authentication, the frontend must send that data as another GSSResponse message. If GSSAPI or SSPI authentication is completed by this message, the server will next send AuthenticationOk to indicate successful authentication or ErrorResponse to indicate failure.
-
-
AuthenticationSASL
-
The frontend must now initiate a SASL negotiation, using one of the SASL mechanisms listed in the message. The frontend will send a SASLInitialResponse with the name of the selected mechanism, and the first part of the SASL data stream in response to this. If further messages are needed, the server will respond with AuthenticationSASLContinue. See Section 55.3 for details.
-
-
AuthenticationSASLContinue
-
This message contains challenge data from the previous step of SASL negotiation (AuthenticationSASL, or a previous AuthenticationSASLContinue). The frontend must respond with a SASLResponse message.
-
-
AuthenticationSASLFinal
-
SASL authentication has completed with additional mechanism-specific data for the client. The server will next send AuthenticationOk to indicate successful authentication, or an ErrorResponse to indicate failure. This message is sent only if the SASL mechanism specifies additional data to be sent from server to client at completion.
-
-
NegotiateProtocolVersion
-
The server does not support the minor protocol version requested by the client, but does support an earlier version of the protocol; this message indicates the highest supported minor version. This message will also be sent if the client requested unsupported protocol options (i.e., beginning with _pq._) in the startup packet. This message will be followed by an ErrorResponse or a message indicating the success or failure of authentication.
-
如果前端不支持服务器请求的身份验证方法,则应立即关闭连接。
If the frontend does not support the authentication method requested by the server, then it should immediately close the connection.
接收到身份验证确定后,前端必须等待服务器的进一步消息。在此阶段正在启动一个后端进程,前端只是一个感兴趣的旁观者。启动尝试仍然可能失败(错误响应),或者服务器拒绝支持所请求的微小协议版本(协商协议版本),但在正常情况下后端将发送一些参数状态消息、后端密钥数据,最后是准备查询。
After having received AuthenticationOk, the frontend must wait for further messages from the server. In this phase a backend process is being started, and the frontend is just an interested bystander. It is still possible for the startup attempt to fail (ErrorResponse) or the server to decline support for the requested minor protocol version (NegotiateProtocolVersion), but in the normal case the backend will send some ParameterStatus messages, BackendKeyData, and finally ReadyForQuery.
在此阶段,后端将尝试应用在启动消息中给出的任何其他运行时参数设置。如果成功,这些值将成为会话默认值。错误将导致错误响应并退出。
During this phase the backend will attempt to apply any additional run-time parameter settings that were given in the startup message. If successful, these values become session defaults. An error causes ErrorResponse and exit.
此阶段中来自后端的可能消息包括:
The possible messages from the backend in this phase are:
-
BackendKeyData
-
This message provides secret-key data that the frontend must save if it wants to be able to issue cancel requests later. The frontend should not respond to this message, but should continue listening for a ReadyForQuery message.
-
-
ParameterStatus
-
This message informs the frontend about the current (initial) setting of backend parameters, such as client_encoding or DateStyle. The frontend can ignore this message, or record the settings for its future use; see Section 55.2.7 for more details. The frontend should not respond to this message, but should continue listening for a ReadyForQuery message.
-
-
ReadyForQuery
-
Start-up is completed. The frontend can now issue commands.
-
-
ErrorResponse
-
Start-up failed. The connection is closed after sending this message.
-
-
NoticeResponse
-
A warning message has been issued. The frontend should display the message but continue listening for ReadyForQuery or ErrorResponse.
-
ReadyForQuery 消息与后端在每个命令周期后发出的消息相同。根据前端的编码需要,合理地将 ReadyForQuery视为启动命令周期,或将 ReadyForQuery视为结束启动阶段和每个后续命令周期。
The ReadyForQuery message is the same one that the backend will issue after each command cycle. Depending on the coding needs of the frontend, it is reasonable to consider ReadyForQuery as starting a command cycle, or to consider ReadyForQuery as ending the start-up phase and each subsequent command cycle.
55.2.2. Simple Query #
一个简单的查询周期由前端向后端发送 Query 消息启动。该消息包括一个以文本字符串形式表示的 SQL 命令(或命令)。然后,后端根据查询命令字符串的内容发送一条或多条响应消息,最后发送一个 ReadyForQuery 响应消息。ReadyForQuery 告知前端它可以安全地发送新命令。(实际上,在发出另一个命令之前,前端不必等待 ReadyForQuery,但前端必须承担弄清如果前面的命令失败且已经发出的后续命令成功时会发生什么情况的责任。)
A simple query cycle is initiated by the frontend sending a Query message to the backend. The message includes an SQL command (or commands) expressed as a text string. The backend then sends one or more response messages depending on the contents of the query command string, and finally a ReadyForQuery response message. ReadyForQuery informs the frontend that it can safely send a new command. (It is not actually necessary for the frontend to wait for ReadyForQuery before issuing another command, but the frontend must then take responsibility for figuring out what happens if the earlier command fails and already-issued later commands succeed.)
后端可能的响应消息是:
The possible response messages from the backend are:
-
CommandComplete
-
An SQL command completed normally.
-
-
CopyInResponse
-
The backend is ready to copy data from the frontend to a table; see Section 55.2.6.
-
-
CopyOutResponse
-
The backend is ready to copy data from a table to the frontend; see Section 55.2.6.
-
-
RowDescription
-
Indicates that rows are about to be returned in response to a SELECT, FETCH, etc. query. The contents of this message describe the column layout of the rows. This will be followed by a DataRow message for each row being returned to the frontend.
-
-
DataRow
-
One of the set of rows returned by a SELECT, FETCH, etc. query.
-
-
EmptyQueryResponse
-
An empty query string was recognized.
-
-
ErrorResponse
-
An error has occurred.
-
-
ReadyForQuery
-
Processing of the query string is complete. A separate message is sent to indicate this because the query string might contain multiple SQL commands. (CommandComplete marks the end of processing one SQL command, not the whole string.) ReadyForQuery will always be sent, whether processing terminates successfully or with an error.
-
-
NoticeResponse
-
A warning message has been issued in relation to the query. Notices are in addition to other responses, i.e., the backend will continue processing the command.
-
对 SELECT 查询(或其他返回行集的查询,例如 EXPLAIN 或 SHOW)的响应通常包括 RowDescription、零个或更多个 DataRow 消息,然后是 CommandComplete。到前端或从前端的 COPY 会调用特殊协议,如 Section 55.2.6 中所述。所有其他查询类型通常只生成 CommandComplete 消息。
The response to a SELECT query (or other queries that return row sets, such as EXPLAIN or SHOW) normally consists of RowDescription, zero or more DataRow messages, and then CommandComplete. COPY to or from the frontend invokes special protocol as described in Section 55.2.6. All other query types normally produce only a CommandComplete message.
由于一个查询字符串可以包含多个查询(用分号分隔),因此在后端完成处理查询字符串之前,可能会有多个这样的响应序列。当整个字符串已处理完毕并且后端准备接受新的查询字符串时,发出 ReadyForQuery。
Since a query string could contain several queries (separated by semicolons), there might be several such response sequences before the backend finishes processing the query string. ReadyForQuery is issued when the entire string has been processed and the backend is ready to accept a new query string.
如果接收到一个完全为空的(除空格外无其他内容)的查询字符串,响应是 EmptyQueryResponse 接着是 ReadyForQuery。
If a completely empty (no contents other than whitespace) query string is received, the response is EmptyQueryResponse followed by ReadyForQuery.
如果遇到错误,则会发出 ErrorResponse,后跟 ReadyForQuery。ErrorResponse 会中止对查询字符串的所有进一步处理(即使其中仍有更多查询)。请注意,这可能发生在由单个查询生成的系列消息期间的某个时间点。
In the event of an error, ErrorResponse is issued followed by ReadyForQuery. All further processing of the query string is aborted by ErrorResponse (even if more queries remained in it). Note that this might occur partway through the sequence of messages generated by an individual query.
在简单查询模式中,检索到的值格式始终为文本,除非给定的命令是使用 BINARY 选项声明的光标 FETCH。在这种情况下,检索到的值将采用二进制格式。RowDescription 消息中给出的格式代码告诉正在使用哪种格式。
In simple Query mode, the format of retrieved values is always text, except when the given command is a FETCH from a cursor declared with the BINARY option. In that case, the retrieved values are in binary format. The format codes given in the RowDescription message tell which format is being used.
每当前端期望任何其他类型的消息时,它都必须做好接受 ErrorResponse 和 NoticeResponse 消息的准备。另请参见 Section 55.2.7,了解后端可能由于外部事件而生成的消息。
A frontend must be prepared to accept ErrorResponse and NoticeResponse messages whenever it is expecting any other type of message. See also Section 55.2.7 concerning messages that the backend might generate due to outside events.
推荐的做法是使用状态机样式对前端进行编码,该样式可以在任何时刻接受任何消息类型(前提是这有意义),而不是将有关消息的确切顺序的假设编入其中。
Recommended practice is to code frontends in a state-machine style that will accept any message type at any time that it could make sense, rather than wiring in assumptions about the exact sequence of messages.
55.2.2.1. Multiple Statements in a Simple Query #
当简单的查询消息包含多条 SQL 语句(由分号分隔)时,这些语句将作为单个事务执行,除非包含显式的交易控制命令来强制执行不同的行为。例如,如果该消息包含
When a simple Query message contains more than one SQL statement (separated by semicolons), those statements are executed as a single transaction, unless explicit transaction control commands are included to force a different behavior. For example, if the message contains
INSERT INTO mytable VALUES(1);
SELECT 1/0;
INSERT INTO mytable VALUES(2);
那么,SELECT 中的除以零故障将强制回滚第一个 INSERT。此外,由于在第一个错误时放弃该消息的执行,因此根本不会尝试第二个 INSERT。
then the divide-by-zero failure in the SELECT will force rollback of the first INSERT. Furthermore, because execution of the message is abandoned at the first error, the second INSERT is never attempted at all.
如果消息改为包含
If instead the message contains
BEGIN;
INSERT INTO mytable VALUES(1);
COMMIT;
INSERT INTO mytable VALUES(2);
SELECT 1/0;
则第一个 INSERT 将被显式 COMMIT 命令提交。第二个 INSERT 和 SELECT 仍被视为单个事务,以便除以零故障将回滚第二个 INSERT,但不会回滚第一个。
then the first INSERT is committed by the explicit COMMIT command. The second INSERT and the SELECT are still treated as a single transaction, so that the divide-by-zero failure will roll back the second INSERT, but not the first one.
通过在 implicit transaction block 中运行多语句查询消息中的语句,来实现此行为,除非有某个显式的事务块供它们运行。隐式事务块和常规事务块之间的主要区别在于,隐式事务块会在查询消息的末尾自动关闭,如果没有任何错误,则会隐式提交;如果出现错误,则会隐式回滚。这类似于为自身执行的语句(当不在事务块中时)发生的隐式提交或回滚。
This behavior is implemented by running the statements in a multi-statement Query message in an implicit transaction block unless there is some explicit transaction block for them to run in. The main difference between an implicit transaction block and a regular one is that an implicit block is closed automatically at the end of the Query message, either by an implicit commit if there was no error, or an implicit rollback if there was an error. This is similar to the implicit commit or rollback that happens for a statement executed by itself (when not in a transaction block).
如果会话已处于事务块中,这是由于之前某个消息中的 BEGIN,则查询消息仅继续该事务块,无论该消息包含一条或多条语句。但是,如果查询消息包含 COMMIT 或 ROLLBACK 来关闭现有事务块,则任何后续语句都将在隐式事务块中执行。相反,如果 BEGIN 出现在多语句查询消息中,则它将启动一个常规事务块,该事务块只能由显式的 COMMIT 或 ROLLBACK 来终止,无论它出现在此查询消息中还是之后的某个消息中。如果 BEGIN 位于作为隐式事务块执行的某个语句之后,则不会立即提交该语句;实际上,它们会追溯地纳入新的常规事务块中。
If the session is already in a transaction block, as a result of a BEGIN in some previous message, then the Query message simply continues that transaction block, whether the message contains one statement or several. However, if the Query message contains a COMMIT or ROLLBACK closing the existing transaction block, then any following statements are executed in an implicit transaction block. Conversely, if a BEGIN appears in a multi-statement Query message, then it starts a regular transaction block that will only be terminated by an explicit COMMIT or ROLLBACK, whether that appears in this Query message or a later one. If the BEGIN follows some statements that were executed as an implicit transaction block, those statements are not immediately committed; in effect, they are retroactively included into the new regular transaction block.
出现在隐式事务块中的 COMMIT 或 ROLLBACK 会正常执行,关闭隐式块;然而,系统会发出警告,因为没有先前的 BEGIN 出现的 COMMIT 或 ROLLBACK 可能表示错误。如果还有更多语句,将为它们启动一个新的隐式事务块。
A COMMIT or ROLLBACK appearing in an implicit transaction block is executed as normal, closing the implicit block; however, a warning will be issued since a COMMIT or ROLLBACK without a previous BEGIN might represent a mistake. If more statements follow, a new implicit transaction block will be started for them.
隐式事务块中不允许使用保存点,因为它们会与在任何错误时自动关闭此块的行为相冲突。
Savepoints are not allowed in an implicit transaction block, since they would conflict with the behavior of automatically closing the block upon any error.
请记住,无论存在哪些交易控制命令,查询消息的执行都会在第一个错误时停止。因此,例如给定
Remember that, regardless of any transaction control commands that may be present, execution of the Query message stops at the first error. Thus for example given
BEGIN;
SELECT 1/0;
ROLLBACK;
在单个查询消息中,将在失败的常规事务块内部退出会话,因为在除以零错误之后无法达到 ROLLBACK。将需要另一个 ROLLBACK 来使会话恢复到可使用状态。
in a single Query message, the session will be left inside a failed regular transaction block, since the ROLLBACK is not reached after the divide-by-zero error. Another ROLLBACK will be needed to restore the session to a usable state.
值得注意的另一种行为是,在执行任何查询字符串之前,会对整个查询字符串进行初始词法和语法分析。因此,后面的语句中的简单错误(例如拼写错误的关键词)可能会阻止任何语句的执行。由于在作为隐式事务块处理时,这些语句无论如何都会回滚,因此对于用户来说,这通常是不可见的。但是,在尝试在多语句查询中执行多个事务时,这可能是可见的。例如,如果错别字将我们之前的示例变成
Another behavior of note is that initial lexical and syntactic analysis is done on the entire query string before any of it is executed. Thus simple errors (such as a misspelled keyword) in later statements can prevent execution of any of the statements. This is normally invisible to users since the statements would all roll back anyway when done as an implicit transaction block. However, it can be visible when attempting to do multiple transactions within a multi-statement Query. For instance, if a typo turned our previous example into
BEGIN;
INSERT INTO mytable VALUES(1);
COMMIT;
INSERT INTO mytable VALUES(2);
SELCT 1/0;
那么不会运行任何语句,从而导致第一个 INSERT 无法提交的明显差异。语义分析或之后检测到的错误,例如拼写错误的表或列名,不会产生此效果。
then none of the statements would get run, resulting in the visible difference that the first INSERT is not committed. Errors detected at semantic analysis or later, such as a misspelled table or column name, do not have this effect.
55.2.3. Extended Query #
扩展查询协议将上述简单查询协议分解为多个步骤。准备步骤的结果可以多次重复使用,以提高效率。此外,还提供了附加功能,例如将数据值作为单独参数提供而不是必须直接插入到查询字符串中的可能性。
The extended query protocol breaks down the above-described simple query protocol into multiple steps. The results of preparatory steps can be re-used multiple times for improved efficiency. Furthermore, additional features are available, such as the possibility of supplying data values as separate parameters instead of having to insert them directly into a query string.
在扩展协议中,前端首先发送一个 Parse 消息,该消息包含一个文本查询字符串(可选地包含有关参数占位符的数据类型的一些信息),以及一个目标已准备好的语句对象的名称(空字符串选择未命名的已准备好的语句)。响应要么是 ParseComplete,要么是 ErrorResponse。可以通过 OID 指定参数数据类型;如果没有给出,解析器将尝试以与处理未键入的字符串常量相同的方式推断数据类型。
In the extended protocol, the frontend first sends a Parse message, which contains a textual query string, optionally some information about data types of parameter placeholders, and the name of a destination prepared-statement object (an empty string selects the unnamed prepared statement). The response is either ParseComplete or ErrorResponse. Parameter data types can be specified by OID; if not given, the parser attempts to infer the data types in the same way as it would do for untyped literal string constants.
Note
可以将参数数据类型留空,方法是将其设置为零,或将参数类型 OID 数组弄得比查询字符串中使用的参数符号数量 ($__n) 短。另一特殊情况是,可以指定参数的类型为 void(即 void 伪类型的 OID)。这旨在允许将参数符号用于实际上是 OUT 参数的函数参数。通常情况下,void 参数没有可用于的上下文,但如果这样的参数符号出现在函数的参数列表中,它将被有效忽略。例如,像 foo($1,$2,$3,$4) 这样的函数调用可以与具有两个 IN 和两个 OUT 自变量的函数相匹配,如果 $3 和 $4 被指定为类型 void。
A parameter data type can be left unspecified by setting it to zero, or by making the array of parameter type OIDs shorter than the number of parameter symbols ($__n) used in the query string. Another special case is that a parameter’s type can be specified as void (that is, the OID of the void pseudo-type). This is meant to allow parameter symbols to be used for function parameters that are actually OUT parameters. Ordinarily there is no context in which a void parameter could be used, but if such a parameter symbol appears in a function’s parameter list, it is effectively ignored. For example, a function call such as foo($1,$2,$3,$4) could match a function with two IN and two OUT arguments, if $3 and $4 are specified as having type void.
Note
包含在解析消息中的查询字符串不能包含多条 SQL 语句;否则将报告一个语法错误。此限制在简单查询协议中不存在,但在扩展协议中存在,这是因为允许准备语句或门户包含多条命令会使协议变得过于复杂。
The query string contained in a Parse message cannot include more than one SQL statement; else a syntax error is reported. This restriction does not exist in the simple-query protocol, but it does exist in the extended protocol, because allowing prepared statements or portals to contain multiple commands would complicate the protocol unduly.
如果成功创建,则命名的已准备好的语句对象将持续到当前会话结束,除非显式销毁。未命名的已准备好的语句仅持续到发出将未命名的语句指定为目标的下一个 Parse 语句为止。(请注意,简单的查询消息也会销毁未命名的语句。)在可以使用另一个 Parse 消息重新定义命名的已准备好的语句之前,必须显式关闭它们,但对于未命名的语句则不需要。还可以使用 PREPARE 和 EXECUTE 在 SQL 命令级别创建和访问命名的已准备好的语句。
If successfully created, a named prepared-statement object lasts till the end of the current session, unless explicitly destroyed. An unnamed prepared statement lasts only until the next Parse statement specifying the unnamed statement as destination is issued. (Note that a simple Query message also destroys the unnamed statement.) Named prepared statements must be explicitly closed before they can be redefined by another Parse message, but this is not required for the unnamed statement. Named prepared statements can also be created and accessed at the SQL command level, using PREPARE and EXECUTE.
准备语句准备完成后,可以使用关联消息为其准备好执行。该关联消息提供源准备语句的名称(空字符串表示未命名准备语句),目标门户名称(空字符串表示未命名门户)以及准备语句中出现的所有参数占位符要使用的值。提供的参数设置必须与准备语句所需的相同。(如果您在解析消息中声明任何 void 参数,则在关联消息中为其传递 NULL 值。)关联消息还指定用于查询返回的所有数据的格式;该格式可以全部指定,也可以按列指定。响应为关联完成或错误响应。
Once a prepared statement exists, it can be readied for execution using a Bind message. The Bind message gives the name of the source prepared statement (empty string denotes the unnamed prepared statement), the name of the destination portal (empty string denotes the unnamed portal), and the values to use for any parameter placeholders present in the prepared statement. The supplied parameter set must match those needed by the prepared statement. (If you declared any void parameters in the Parse message, pass NULL values for them in the Bind message.) Bind also specifies the format to use for any data returned by the query; the format can be specified overall, or per-column. The response is either BindComplete or ErrorResponse.
Note
文本和二进制输出之间的选择是由 Bind 中给定的格式代码决定的,与所涉及的 SQL 命令无关。在使用扩展查询协议时,游标声明中的 BINARY 属性是不相关的。
The choice between text and binary output is determined by the format codes given in Bind, regardless of the SQL command involved. The BINARY attribute in cursor declarations is irrelevant when using extended query protocol.
在处理关联消息时,通常会进行查询计划。如果准备语句没有参数,或者重复执行,则服务器可能会保存创建的计划并在后续关联消息中为同一个准备语句重复使用该计划。然而,服务器仅当发现可以创建一个通用计划时,这个通用的计划的效率不会远低于依赖于所提供的特定参数值计划的效率。从协议而言,这是透明的。
Query planning typically occurs when the Bind message is processed. If the prepared statement has no parameters, or is executed repeatedly, the server might save the created plan and re-use it during subsequent Bind messages for the same prepared statement. However, it will do so only if it finds that a generic plan can be created that is not much less efficient than a plan that depends on the specific parameter values supplied. This happens transparently so far as the protocol is concerned.
如果成功创建,则命名门户对象持续到当前事务结束,除非明确销毁。未命名门户在事务结束时销毁,或者在指定未命名门户作为目标的下一个关联语句发出后立即销毁。(请注意,简单的查询消息也会销毁未命名门户。)在由其他关联消息重新定义之前,必须明确关闭命名门户,但这对于未命名门户不是必须的。命名门户也可以使用 DECLARE CURSOR 和 FETCH 在 SQL 命令级别创建和访问。
If successfully created, a named portal object lasts till the end of the current transaction, unless explicitly destroyed. An unnamed portal is destroyed at the end of the transaction, or as soon as the next Bind statement specifying the unnamed portal as destination is issued. (Note that a simple Query message also destroys the unnamed portal.) Named portals must be explicitly closed before they can be redefined by another Bind message, but this is not required for the unnamed portal. Named portals can also be created and accessed at the SQL command level, using DECLARE CURSOR and FETCH.
创建门户后,可以使用执行消息执行该门户。执行消息指定门户名称(空字符串表示未命名门户)和最大结果行数(零表示“获取所有行”)。结果行数仅对包含返回行集的命令的门户有意义;在其他情况下,该命令总是执行到完成,并且忽略行数。执行的可行响应与通过简单查询协议发出的查询的描述相同,只是执行不会导致发出准备好查询或行描述。
Once a portal exists, it can be executed using an Execute message. The Execute message specifies the portal name (empty string denotes the unnamed portal) and a maximum result-row count (zero meaning “fetch all rows”). The result-row count is only meaningful for portals containing commands that return row sets; in other cases the command is always executed to completion, and the row count is ignored. The possible responses to Execute are the same as those described above for queries issued via simple query protocol, except that Execute doesn’t cause ReadyForQuery or RowDescription to be issued.
如果执行在完成门户的执行前终止(由于达到非零结果行数),它将发送门户暂停消息;此消息的出现告诉前端,应该针对同一个门户发出另一个执行消息来完成操作。表示完成源 SQL 命令的命令完成消息在门户执行完成之前不会发送。因此,执行阶段总是以出现以下消息之一的方式终止:命令完成、空查询响应(如果门户是从空查询字符串创建的)、错误响应或门户暂停。
If Execute terminates before completing the execution of a portal (due to reaching a nonzero result-row count), it will send a PortalSuspended message; the appearance of this message tells the frontend that another Execute should be issued against the same portal to complete the operation. The CommandComplete message indicating completion of the source SQL command is not sent until the portal’s execution is completed. Therefore, an Execute phase is always terminated by the appearance of exactly one of these messages: CommandComplete, EmptyQueryResponse (if the portal was created from an empty query string), ErrorResponse, or PortalSuspended.
在每个系列的扩展查询消息完成时,前端都应该发出同步消息。这个无参数消息导致后端关闭当前事务,如果它不在 BEGIN/COMMIT 事务块内(“关闭”的意思是如果没有错误则提交,如果有错误则回滚)。然后发出准备好查询响应。同步的目的是为错误恢复提供重新同步点。在处理任何扩展查询消息时检测到错误时,后端会发出错误响应,然后读取并丢弃消息直到到达同步,然后发出准备好查询并返回到正常消息处理。(但请注意,如果在 while 处理同步时检测到错误,不会跳过任何内容 - 这确保对每次同步只发送一次准备好查询。)
At completion of each series of extended-query messages, the frontend should issue a Sync message. This parameterless message causes the backend to close the current transaction if it’s not inside a BEGIN/COMMIT transaction block (“close” meaning to commit if no error, or roll back if error). Then a ReadyForQuery response is issued. The purpose of Sync is to provide a resynchronization point for error recovery. When an error is detected while processing any extended-query message, the backend issues ErrorResponse, then reads and discards messages until a Sync is reached, then issues ReadyForQuery and returns to normal message processing. (But note that no skipping occurs if an error is detected while processing Sync — this ensures that there is one and only one ReadyForQuery sent for each Sync.)
Note
Sync 不会导致使用 BEGIN 打开的事务块关闭。因为 ReadyForQuery 消息包含事务状态信息,所以可以检测到这种情况。
Sync does not cause a transaction block opened with BEGIN to be closed. It is possible to detect this situation since the ReadyForQuery message includes transaction status information.
除了这些基本的必需操作,扩展查询协议还可以使用其他几个可选操作。
In addition to these fundamental, required operations, there are several optional operations that can be used with extended-query protocol.
描述消息(门户变量)指定现有门户的名称(或针对未命名门户的空字符串)。响应是行描述消息,描述执行门户后返回的行;或者如果没有包含将返回行的查询的门户,则为无数据消息;或者如果没有该门户,则为错误响应。
The Describe message (portal variant) specifies the name of an existing portal (or an empty string for the unnamed portal). The response is a RowDescription message describing the rows that will be returned by executing the portal; or a NoData message if the portal does not contain a query that will return rows; or ErrorResponse if there is no such portal.
描述消息(语句变量)指定现有准备语句的名称(或未命名准备语句的空字符串)。响应是参数描述消息,描述语句需要的参数,然后是行描述消息,描述最终执行语句时将返回的行(或者如果语句不会返回行,则为无数据消息)。如果没有该准备语句,则会发出错误响应。请注意,由于关联尚未发出,后端还不知道将用于返回列的格式;在这种情况下,行描述消息中的格式代码字段将为零。
The Describe message (statement variant) specifies the name of an existing prepared statement (or an empty string for the unnamed prepared statement). The response is a ParameterDescription message describing the parameters needed by the statement, followed by a RowDescription message describing the rows that will be returned when the statement is eventually executed (or a NoData message if the statement will not return rows). ErrorResponse is issued if there is no such prepared statement. Note that since Bind has not yet been issued, the formats to be used for returned columns are not yet known to the backend; the format code fields in the RowDescription message will be zeroes in this case.
Tip
在大多数情况下,前端应该在发出执行之前发出描述的一个或另一个变量,以确保它知道如何解释它将获得的结果。
In most scenarios the frontend should issue one or the other variant of Describe before issuing Execute, to ensure that it knows how to interpret the results it will get back.
关闭消息关闭现有准备语句或门户并释放资源。针对不存在的语句或门户名称发出关闭不是错误。响应通常是关闭完成,但如果在释放资源时遇到一些困难,可能是错误响应。请注意,关闭准备语句意味着会隐式关闭从该语句构建的任何开放门户。
The Close message closes an existing prepared statement or portal and releases resources. It is not an error to issue Close against a nonexistent statement or portal name. The response is normally CloseComplete, but could be ErrorResponse if some difficulty is encountered while releasing resources. Note that closing a prepared statement implicitly closes any open portals that were constructed from that statement.
刷新消息不会导致生成任何特定输出,但会强制后端传送其输出缓冲区中保留的任何数据。前端如果想在发送更多命令前检查该命令的结果,则必须在除了同步消息之外的任何扩展查询命令之后发送刷新消息。如果不刷新,则后端返回的消息将组合成尽可能少的包,以最大程度减少网络开销。
The Flush message does not cause any specific output to be generated, but forces the backend to deliver any data pending in its output buffers. A Flush must be sent after any extended-query command except Sync, if the frontend wishes to examine the results of that command before issuing more commands. Without Flush, messages returned by the backend will be combined into the minimum possible number of packets to minimize network overhead.
Note
简单的 Query 消息大约等同于使用未命名准备好的语句和门户对象且无参数的 Parse、Bind、portal Describe、Execute、Close、Sync 系列。不同的是,它会在查询字符串中接受多个 SQL 语句,并自动依次执行每个语句的 bind/describe/execute 序列。另一个不同之处是,它不会返回 ParseComplete、BindComplete、CloseComplete 或 NoData 消息。
The simple Query message is approximately equivalent to the series Parse, Bind, portal Describe, Execute, Close, Sync, using the unnamed prepared statement and portal objects and no parameters. One difference is that it will accept multiple SQL statements in the query string, automatically performing the bind/describe/execute sequence for each one in succession. Another difference is that it will not return ParseComplete, BindComplete, CloseComplete, or NoData messages.
55.2.4. Pipelining #
使用扩展查询协议允许 pipelining,这意味着发送一系列查询,而不等待之前的查询完成。这样可以减少完成一系列给定操作所需的网络往返次数。然而,用户必须仔细考虑步骤之一失败时所需的行为,因为后续查询将已经处于向服务器传输中。
Use of the extended query protocol allows pipelining, which means sending a series of queries without waiting for earlier ones to complete. This reduces the number of network round trips needed to complete a given series of operations. However, the user must carefully consider the required behavior if one of the steps fails, since later queries will already be in flight to the server.
处理这种问题的方法之一是将整个查询系列作为单一事务,即用 BEGIN … COMMIT 将其包装起来。然而,如果希望其中一些命令独立于其他命令提交,这并无帮助。
One way to deal with that is to make the whole query series be a single transaction, that is wrap it in BEGIN … COMMIT. However, this does not help if one wishes for some of the commands to commit independently of others.
扩展查询协议提供了另一种管理这个问题的方法,即省略在有依赖关系的步骤之间发送同步消息。由于在出现错误后,后端将跳过命令消息,直到找到同步,所以如果较早的一个失败,允许管道中的后续命令自动跳过,而无需客户端使用 BEGIN 和 COMMIT 明确管理这一点。可以通过同步消息分隔管道中可以独立提交的段。
The extended query protocol provides another way to manage this concern, which is to omit sending Sync messages between steps that are dependent. Since, after an error, the backend will skip command messages until it finds Sync, this allows later commands in a pipeline to be skipped automatically when an earlier one fails, without the client having to manage that explicitly with BEGIN and COMMIT. Independently-committable segments of the pipeline can be separated by Sync messages.
如果客户端没有发出明确的 BEGIN,则每个同步通常会导致隐式 COMMIT(如果前一个步骤成功)或隐式 ROLLBACK(如果它们失败)。然而,有少数 DDL 命令(如 CREATE DATABASE),它们无法在事务块中执行。如果管道中执行其中一个命令,它将失败,除非它是管道中的第一个命令。此外,它如果成功,它将强制立即提交以保留数据库一致性。因此,紧跟这些命令之一后的同步除用准备好查询响应之外没有其他作用。
If the client has not issued an explicit BEGIN, then each Sync ordinarily causes an implicit COMMIT if the preceding step(s) succeeded, or an implicit ROLLBACK if they failed. However, there are a few DDL commands (such as CREATE DATABASE) that cannot be executed inside a transaction block. If one of these is executed in a pipeline, it will fail unless it is the first command in the pipeline. Furthermore, upon success it will force an immediate commit to preserve database consistency. Thus a Sync immediately following one of these commands has no effect except to respond with ReadyForQuery.
当使用此方法时,管道的完成必须通过计数准备好查询消息并等待其达到已发送同步的数量来确定。计数命令完成响应不可靠,因为某些命令可能会跳过,因此不会生成完成消息。
When using this method, completion of the pipeline must be determined by counting ReadyForQuery messages and waiting for that to reach the number of Syncs sent. Counting command completion responses is unreliable, since some of the commands may be skipped and thus not produce a completion message.
55.2.5. Function Call #
函数调用子协议允许客户端请求数据库 pg_proc 系统目录中存在的任何函数的直接调用。客户端必须具有该函数的执行权限。
The Function Call sub-protocol allows the client to request a direct call of any function that exists in the database’s pg_proc system catalog. The client must have execute permission for the function.
Note
函数调用子协议是一项遗留功能,最好在编写新代码时避免使用。通过设置执行 SELECT function($1, …) 的准备好的语句,能实现类似结果。然后,可以用 Bind/Execute 替换函数调用循环。
The Function Call sub-protocol is a legacy feature that is probably best avoided in new code. Similar results can be accomplished by setting up a prepared statement that does SELECT function($1, …). The Function Call cycle can then be replaced with Bind/Execute.
函数调用周期通过前端向后端发送函数调用消息开始。然后,后端根据函数调用的结果发送一个或多个响应消息,最后发送一个准备好查询响应消息。准备好查询通知前端可以安全地发送新的查询或函数调用。
A Function Call cycle is initiated by the frontend sending a FunctionCall message to the backend. The backend then sends one or more response messages depending on the results of the function call, and finally a ReadyForQuery response message. ReadyForQuery informs the frontend that it can safely send a new query or function call.
后端可能的响应消息是:
The possible response messages from the backend are:
-
ErrorResponse
-
An error has occurred.
-
-
FunctionCallResponse
-
The function call was completed and returned the result given in the message. (Note that the Function Call protocol can only handle a single scalar result, not a row type or set of results.)
-
-
ReadyForQuery
-
Processing of the function call is complete. ReadyForQuery will always be sent, whether processing terminates successfully or with an error.
-
-
NoticeResponse
-
A warning message has been issued in relation to the function call. Notices are in addition to other responses, i.e., the backend will continue processing the command.
-
55.2.6. COPY Operations #
COPY 命令允许以高速将大量数据传输到或从服务器。 每个复制输入和复制输出操作将连接切换到一个不同的子协议,该子协议将持续到操作完成。
The COPY command allows high-speed bulk data transfer to or from the server. Copy-in and copy-out operations each switch the connection into a distinct sub-protocol, which lasts until the operation is completed.
当后端执行 COPY FROM STDIN SQL 语句时,将启动复制输入模式(将数据传输到服务器)。 后端向前端发送 CopyInResponse 消息。然后,前端应发送零个或多个 CopyData 消息,以形成输入数据流。(不需要消息边界与行边界有任何关系,尽管这通常是一个合理的选项。)前端可以通过发送 CopyDone 消息(允许成功终止)或 CopyFail 消息(将导致带有错误的 COPY SQL 语句失败)终止复制输入模式。然后,后端将恢复到 COPY 启动前的命令处理模式,该模式将是简单或扩展的查询协议。接下来,它将发送 CommandComplete(如果成功)或 ErrorResponse(如果不成功)。
Copy-in mode (data transfer to the server) is initiated when the backend executes a COPY FROM STDIN SQL statement. The backend sends a CopyInResponse message to the frontend. The frontend should then send zero or more CopyData messages, forming a stream of input data. (The message boundaries are not required to have anything to do with row boundaries, although that is often a reasonable choice.) The frontend can terminate the copy-in mode by sending either a CopyDone message (allowing successful termination) or a CopyFail message (which will cause the COPY SQL statement to fail with an error). The backend then reverts to the command-processing mode it was in before the COPY started, which will be either simple or extended query protocol. It will next send either CommandComplete (if successful) or ErrorResponse (if not).
如果在复制输入模式期间检测到后端错误(包括收到 CopyFail 消息),后端将发出 ErrorResponse 消息。 如果 COPY 命令是通过扩展查询消息发出的,则后端现在将丢弃前端消息,直到收到 Sync 消息,然后发出 ReadyForQuery 并返回正常处理。 如果在简单查询消息中发出 COPY 命令,则该消息的其余部分将被丢弃,并发出 ReadyForQuery。 在任何一种情况下,前端发出的任何后续 CopyData、CopyDone 或 CopyFail 消息都将被简单地丢弃。
In the event of a backend-detected error during copy-in mode (including receipt of a CopyFail message), the backend will issue an ErrorResponse message. If the COPY command was issued via an extended-query message, the backend will now discard frontend messages until a Sync message is received, then it will issue ReadyForQuery and return to normal processing. If the COPY command was issued in a simple Query message, the rest of that message is discarded and ReadyForQuery is issued. In either case, any subsequent CopyData, CopyDone, or CopyFail messages issued by the frontend will simply be dropped.
后端将在复制输入模式期间忽略收到的 Flush 和 Sync 消息。收到任何其他非复制消息类型都会构成一个错误,该错误将导致复制输入状态中止,如上所述。(对 Flush 和 Sync 的例外情况是为了方便始终在 Execute 消息后发送 Flush 或 Sync 的客户端库,而无需检查要执行的命令是否是 COPY FROM STDIN。)
The backend will ignore Flush and Sync messages received during copy-in mode. Receipt of any other non-copy message type constitutes an error that will abort the copy-in state as described above. (The exception for Flush and Sync is for the convenience of client libraries that always send Flush or Sync after an Execute message, without checking whether the command to be executed is a COPY FROM STDIN.)
当后端执行 COPY TO STDOUT SQL 语句时,将启动复制输出模式(将数据从服务器传输)。 后端向前端发送 CopyOutResponse 消息,随后发送零个或多个 CopyData 消息(始终每行一个),后跟 CopyDone。然后,后端将恢复到 COPY 启动前的命令处理模式,并发送 CommandComplete。 前端无法中止传输(除非关闭连接或发出取消请求),但它可以丢弃不需要的 CopyData 和 CopyDone 消息。
Copy-out mode (data transfer from the server) is initiated when the backend executes a COPY TO STDOUT SQL statement. The backend sends a CopyOutResponse message to the frontend, followed by zero or more CopyData messages (always one per row), followed by CopyDone. The backend then reverts to the command-processing mode it was in before the COPY started, and sends CommandComplete. The frontend cannot abort the transfer (except by closing the connection or issuing a Cancel request), but it can discard unwanted CopyData and CopyDone messages.
如果在复制输出模式期间检测到后端错误,后端将发出 ErrorResponse 消息并恢复为正常处理。前端应将收到 ErrorResponse 视为终止复制输出模式。
In the event of a backend-detected error during copy-out mode, the backend will issue an ErrorResponse message and revert to normal processing. The frontend should treat receipt of ErrorResponse as terminating the copy-out mode.
在 CopyData 消息之间插入 NoticeResponse 和 ParameterStatus 消息是可能的;前端必须处理这些情况,并应为其他异步消息类型做好准备(请参见 Section 55.2.7)。否则,任何除 CopyData 或 CopyDone 以外的消息类型都可以视为终止 copy-out 模式。
It is possible for NoticeResponse and ParameterStatus messages to be interspersed between CopyData messages; frontends must handle these cases, and should be prepared for other asynchronous message types as well (see Section 55.2.7). Otherwise, any message type other than CopyData or CopyDone may be treated as terminating copy-out mode.
还有另一种与复制相关的模式称为 copy-both,它允许高速将大量数据从服务器传输到 and。当 walsender 模式的后端执行 START_REPLICATION 语句时,将启动 copy-both 模式。后端向前端发送 CopyBothResponse 消息。然后,后端和前端都可以发送 CopyData 消息,直到任一端发送 CopyDone 消息。当客户端发送 CopyDone 消息后,连接会从 copy-both 模式切换到 copy-out 模式,客户端可能无法再发送任何 CopyData 消息。同样,当服务器发送 CopyDone 消息后,连接会进入 copy-in 模式,服务器可能无法再发送任何 CopyData 消息。在双方都发送了 CopyDone 消息后,复制模式终止,后端会恢复到命令处理模式。如果在 copy-both 模式期间后端检测到错误,后端会发出 ErrorResponse 消息,丢弃前端消息,直至收到 Sync 消息,然后发出 ReadyForQuery 并返回到正常处理。前端应将收到 ErrorResponse 视为终止两个方向上的复制;在此情况下不应发送 CopyDone。有关在 copy-both 模式下传输的子协议的更多信息,请参见 Section 55.4。
There is another Copy-related mode called copy-both, which allows high-speed bulk data transfer to and from the server. Copy-both mode is initiated when a backend in walsender mode executes a START_REPLICATION statement. The backend sends a CopyBothResponse message to the frontend. Both the backend and the frontend may then send CopyData messages until either end sends a CopyDone message. After the client sends a CopyDone message, the connection goes from copy-both mode to copy-out mode, and the client may not send any more CopyData messages. Similarly, when the server sends a CopyDone message, the connection goes into copy-in mode, and the server may not send any more CopyData messages. After both sides have sent a CopyDone message, the copy mode is terminated, and the backend reverts to the command-processing mode. In the event of a backend-detected error during copy-both mode, the backend will issue an ErrorResponse message, discard frontend messages until a Sync message is received, and then issue ReadyForQuery and return to normal processing. The frontend should treat receipt of ErrorResponse as terminating the copy in both directions; no CopyDone should be sent in this case. See Section 55.4 for more information on the subprotocol transmitted over copy-both mode.
CopyInResponse、CopyOutResponse 和 CopyBothResponse 消息包括通知前端每行列数和用于每列的格式代码的字段。(在目前的实现中,给定的 COPY 操作中的所有列都将使用相同的格式,但消息设计并没有对此做出假设。)
The CopyInResponse, CopyOutResponse and CopyBothResponse messages include fields that inform the frontend of the number of columns per row and the format codes being used for each column. (As of the present implementation, all columns in a given COPY operation will use the same format, but the message design does not assume this.)
55.2.7. Asynchronous Operations #
在后端不会由前端的命令流专门提示发送消息的几种情况下。前端必须随时准备好处理这些消息,即使不执行查询。至少,在开始读取查询响应之前,应检查这些情况。
There are several cases in which the backend will send messages that are not specifically prompted by the frontend’s command stream. Frontends must be prepared to deal with these messages at any time, even when not engaged in a query. At minimum, one should check for these cases before beginning to read a query response.
由于外部活动可能会生成 NoticeResponse 消息;例如,如果数据库管理员命令“快速”关闭数据库,则后端将在关闭连接之前发送 NoticeResponse 表示此事实。因此,即使连接实际上处于空闲状态,前端也应始终做好接受和显示 NoticeResponse 消息的准备。
It is possible for NoticeResponse messages to be generated due to outside activity; for example, if the database administrator commands a “fast” database shutdown, the backend will send a NoticeResponse indicating this fact before closing the connection. Accordingly, frontends should always be prepared to accept and display NoticeResponse messages, even when the connection is nominally idle.
只要对后端认为前端应该了解的任何参数的活动值发生更改,就会生成 ParameterStatus 消息。最常见的情况是响应前端执行的 SET SQL 命令,并且此情况下实际上是同步的——但也有可能由于管理员更改了配置文件然后向服务器发送 SIGHUP 信号而发生参数状态更改。此外,如果 SET 命令回滚,则会生成一个适当的 ParameterStatus 消息来报告当前有效值。
ParameterStatus messages will be generated whenever the active value changes for any of the parameters the backend believes the frontend should know about. Most commonly this occurs in response to a SET SQL command executed by the frontend, and this case is effectively synchronous — but it is also possible for parameter status changes to occur because the administrator changed a configuration file and then sent the SIGHUP signal to the server. Also, if a SET command is rolled back, an appropriate ParameterStatus message will be generated to report the current effective value.
目前有一组固定参数,ParameterStatus 将为此生成。 他们是:
At present there is a hard-wired set of parameters for which ParameterStatus will be generated. They are:
(server_encoding、TimeZone 和 integer_datetimes 未在 8.0 之前的版本中报告;standard_conforming_strings 未在 8.1 以前的版本中报告;IntervalStyle 未在 8.4 以前的版本中报告;application_name 未在 9.0 之前的版本中报告;default_transaction_read_only 和 in_hot_standby 未在 14 之前的版本中报告;scram_iterations 未在 16 之前的版本中报告。)请注意,server_version、server_encoding 和 integer_datetimes 是启动后无法更改的伪参数。此设置可能会在未来更改,甚至变得可配置。因此,前端应简单地忽略它不理解或不关心的参数的 ParameterStatus。
(server_encoding, TimeZone, and integer_datetimes were not reported by releases before 8.0; standard_conforming_strings was not reported by releases before 8.1; IntervalStyle was not reported by releases before 8.4; application_name was not reported by releases before 9.0; default_transaction_read_only and in_hot_standby were not reported by releases before 14; scram_iterations was not reported by releases before 16.) Note that server_version, server_encoding and integer_datetimes are pseudo-parameters that cannot change after startup. This set might change in the future, or even become configurable. Accordingly, a frontend should simply ignore ParameterStatus for parameters that it does not understand or care about.
如果前端发出 LISTEN 命令,则只要为同一个通道名称执行 NOTIFY 命令,后端就会发送 NotificationResponse 消息(不要与 NoticeResponse 混淆!)。
If a frontend issues a LISTEN command, then the backend will send a NotificationResponse message (not to be confused with NoticeResponse!) whenever a NOTIFY command is executed for the same channel name.
Note
目前,NotificationResponse 只能在事务外发送,因此它不会出现在命令-响应系列的中间,尽管它可能会在 ReadyForQuery 前出现。不过,设计假定此行为的前端逻辑是不明智的。良好的做法是能够在协议中的任何位置接受 NotificationResponse。
At present, NotificationResponse can only be sent outside a transaction, and thus it will not occur in the middle of a command-response series, though it might occur just before ReadyForQuery. It is unwise to design frontend logic that assumes that, however. Good practice is to be able to accept NotificationResponse at any point in the protocol.
55.2.8. Canceling Requests in Progress #
在处理查询期间,前端可能会请求取消查询。出于实现效率的原因,取消请求不会直接发送到与后端建立的开放连接上:我们不希望后端在查询处理期间不断检查来自前端的新输入。取消请求相对较少见,因此我们让它们稍有麻烦,以避免在正常情况下受到惩罚。
During the processing of a query, the frontend might request cancellation of the query. The cancel request is not sent directly on the open connection to the backend for reasons of implementation efficiency: we don’t want to have the backend constantly checking for new input from the frontend during query processing. Cancel requests should be relatively infrequent, so we make them slightly cumbersome in order to avoid a penalty in the normal case.
要发出取消请求,前端会打开与服务器的新连接,并发送 CancelRequest 消息,而不是新连接中正常发送的 StartupMessage 消息。服务器将处理此请求,然后关闭连接。出于安全原因,取消请求消息没有直接的回复。
To issue a cancel request, the frontend opens a new connection to the server and sends a CancelRequest message, rather than the StartupMessage message that would ordinarily be sent across a new connection. The server will process this request and then close the connection. For security reasons, no direct reply is made to the cancel request message.
除非 CancelRequest 消息包含连接启动期间传递给前端的相同密钥数据(PID 和密钥),否则将忽略该消息。如果该请求与当前执行的后端匹配 PID 和密钥,则会终止当前查询的处理。(在现有实现中,这是通过向处理查询的后端进程发送特殊信号来完成的。)
A CancelRequest message will be ignored unless it contains the same key data (PID and secret key) passed to the frontend during connection start-up. If the request matches the PID and secret key for a currently executing backend, the processing of the current query is aborted. (In the existing implementation, this is done by sending a special signal to the backend process that is processing the query.)
取消信号可能有或可能没有影响——例如,如果取消信号在后端完成查询处理后再到达,那么其将没有任何影响。如果取消有效,它会导致当前命令因出错消息而提前终止。
The cancellation signal might or might not have any effect — for example, if it arrives after the backend has finished processing the query, then it will have no effect. If the cancellation is effective, it results in the current command being terminated early with an error message.
这一切的结果是,出于安全和效率的考虑,前端没有直接的方法来验证取消请求是否成功。其必须继续等待后端对查询做出响应。发出取消只是提高了当前查询将很快完成的可能性,并提高了其将因出错消息而失败而不是成功完成的可能性。
The upshot of all this is that for reasons of both security and efficiency, the frontend has no direct way to tell whether a cancel request has succeeded. It must continue to wait for the backend to respond to the query. Issuing a cancel simply improves the odds that the current query will finish soon, and improves the odds that it will fail with an error message instead of succeeding.
由于取消请求是通过与服务器的新连接发送的,而不是通过常规的前端/后端通信链接发送的,因此可以由任何进程发出取消请求,而不仅仅是需要取消其查询的前端。这在构建多进程应用程序时可能会提供额外的灵活性。它还引入了一个安全风险,即未经授权的人员可能会尝试取消查询。通过要求在取消请求中提供动态生成的密钥来解决安全风险。
Since the cancel request is sent across a new connection to the server and not across the regular frontend/backend communication link, it is possible for the cancel request to be issued by any process, not just the frontend whose query is to be canceled. This might provide additional flexibility when building multiple-process applications. It also introduces a security risk, in that unauthorized persons might try to cancel queries. The security risk is addressed by requiring a dynamically generated secret key to be supplied in cancel requests.
55.2.9. Termination #
正常的优雅终止程序是,前端发送一个 Terminate 消息并立即关闭连接。收到此消息后,后端关闭连接并终止。
The normal, graceful termination procedure is that the frontend sends a Terminate message and immediately closes the connection. On receipt of this message, the backend closes the connection and terminates.
在罕见的情况下(例如管理员命令的数据库关闭),后端可能会在没有任何前端请求的情况下断开连接。在这种情况下,后端将在关闭连接之前尝试发送一个提供断开连接原因的错误消息或通知消息。
In rare cases (such as an administrator-commanded database shutdown) the backend might disconnect without any frontend request to do so. In such cases the backend will attempt to send an error or notice message giving the reason for the disconnection before it closes the connection.
其他终止场景是由各种故障案例引起的,例如一端的核心转储或另一端,通信链接的丢失,消息边界同步的丢失等。如果前端或后端看到连接意外关闭,它应该清理并终止。前端可以选择通过重新联系服务器来启动一个新后端,如果它不想自行终止。如果接收到无法识别的消息类型,也建议关闭连接,因为这可能表明消息边界同步丢失。
Other termination scenarios arise from various failure cases, such as core dump at one end or the other, loss of the communications link, loss of message-boundary synchronization, etc. If either frontend or backend sees an unexpected closure of the connection, it should clean up and terminate. The frontend has the option of launching a new backend by recontacting the server if it doesn’t want to terminate itself. Closing the connection is also advisable if an unrecognizable message type is received, since this probably indicates loss of message-boundary sync.
对于正常或异常终止,任何打开的事务都不会提交,而是回滚。然而应该注意的是,如果一个前端在处理非 SELECT 查询时断开连接,后端可能会在注意到断开连接之前完成查询。如果查询在任何事务块之外 (BEGIN … COMMIT 序列),那么在识别断开连接之前其结果可能会被提交。
For either normal or abnormal termination, any open transaction is rolled back, not committed. One should note however that if a frontend disconnects while a non-SELECT query is being processed, the backend will probably finish the query before noticing the disconnection. If the query is outside any transaction block (BEGIN … COMMIT sequence) then its results might be committed before the disconnection is recognized.
55.2.10. SSL Session Encryption #
如果使用 SSL 支持构建 PostgreSQL,可以使用 SSL 对前端/后端通信进行加密。这在攻击者可能捕获会话流量的环境中提供了通信安全性。有关使用 SSL 加密 PostgreSQL 会话的更多信息,请参见 Section 19.9。
If PostgreSQL was built with SSL support, frontend/backend communications can be encrypted using SSL. This provides communication security in environments where attackers might be able to capture the session traffic. For more information on encrypting PostgreSQL sessions with SSL, see Section 19.9.
为了启动 SSL 加密连接,前端最初发送一个 SSLRequest 消息,而不是 StartupMessage。然后服务器使用单个字节进行响应,包含 S 或 N,分别表示它愿意或不愿意执行 SSL。如果前端对响应不满意,它可能会在此处关闭连接。要在 S 之后继续执行,请与服务器执行 SSL 启动握手(此处未描述,是 SSL 规范的一部分)。如果成功,请继续发送常规的 StartupMessage。在这种情况下,StartupMessage 和所有后续数据都将通过 SSL 加密。要在 N 之后继续执行,请发送常规的 StartupMessage 并继续执行而不加密。(或者,允许在 N 响应之后发出 GSSENCRequest 消息,尝试使用 GSSAPI 加密而不是 SSL。)
To initiate an SSL-encrypted connection, the frontend initially sends an SSLRequest message rather than a StartupMessage. The server then responds with a single byte containing S or N, indicating that it is willing or unwilling to perform SSL, respectively. The frontend might close the connection at this point if it is dissatisfied with the response. To continue after S, perform an SSL startup handshake (not described here, part of the SSL specification) with the server. If this is successful, continue with sending the usual StartupMessage. In this case the StartupMessage and all subsequent data will be SSL-encrypted. To continue after N, send the usual StartupMessage and proceed without encryption. (Alternatively, it is permissible to issue a GSSENCRequest message after an N response to try to use GSSAPI encryption instead of SSL.)
前端还应做好准备,处理服务器对 SSLRequest 发出的 ErrorMessage 响应。这只会出现在服务器早于 SSL 支持添加到 PostgreSQL 的情况下。(此类服务器现在非常古老,并且可能不再存在。)在这种情况下,必须关闭连接,但前端可能会选择打开一个全新连接,并在不请求 SSL 的情况下继续执行。
The frontend should also be prepared to handle an ErrorMessage response to SSLRequest from the server. This would only occur if the server predates the addition of SSL support to PostgreSQL. (Such servers are now very ancient, and likely do not exist in the wild anymore.) In this case the connection must be closed, but the frontend might choose to open a fresh connection and proceed without requesting SSL.
当可以执行 SSL 加密时,服务器将只发送单个 S 字节,然后等待前端启动 SSL 握手。如果此时能读取到额外的字节,这可能意味着中间人在尝试执行缓冲区填充攻击 ( CVE-2021-23222)。前端的编写方式应为要么在将套接字转交给其 SSL 库之前从套接字中读取恰好一个字节,要么在发现已读取额外的字节时将其视为协议违规。
When SSL encryption can be performed, the server is expected to send only the single S byte and then wait for the frontend to initiate an SSL handshake. If additional bytes are available to read at this point, it likely means that a man-in-the-middle is attempting to perform a buffer-stuffing attack (CVE-2021-23222). Frontends should be coded either to read exactly one byte from the socket before turning the socket over to their SSL library, or to treat it as a protocol violation if they find they have read additional bytes.
初始 SSLRequest 也可以用于正在打开发送 CancelRequest 消息的连接中。
An initial SSLRequest can also be used in a connection that is being opened to send a CancelRequest message.
虽然协议本身没有提供服务器强制 SSL 加密的方法,但管理员可以将服务器配置为将未加密会话作为身份验证检查的副产品拒之门外。
While the protocol itself does not provide a way for the server to force SSL encryption, the administrator can configure the server to reject unencrypted sessions as a byproduct of authentication checking.
55.2.11. GSSAPI Session Encryption #
如果 PostgreSQL 是使用 GSSAPI 支持构建的,则前端/后端的通信可以使用 GSSAPI 加密。在攻击者可能能够捕获会话流量的环境中,这提供了通信安全性。有关使用 GSSAPI 加密 PostgreSQL 会话的详细信息,请参见 Section 19.10。
If PostgreSQL was built with GSSAPI support, frontend/backend communications can be encrypted using GSSAPI. This provides communication security in environments where attackers might be able to capture the session traffic. For more information on encrypting PostgreSQL sessions with GSSAPI, see Section 19.10.
要启动经过 GSSAPI 加密的连接,前端最初发送 GSSENCRequest 消息而不是 StartupMessage 消息。然后,服务器用包含 G 或 N 的单个字节响应,分别表示它愿意或不愿意执行 GSSAPI 加密。如果对响应不满意,前端可能在此时关闭连接。要继续执行 G,使用 RFC 2744 中讨论的 GSSAPI C 绑定或等效项,通过循环调用 gss_init_sec_context() 执行 GSSAPI 初始化,并将结果发送至服务器,从空输入开始,然后逐个使用服务器的每个结果,直到它不返回输出。将 gss_init_sec_context() 的结果发送至服务器时,在消息长度前面加上四字节整数(按网络字节顺序)。要继续执行 N,发送常见的 StartupMessage,并在不加密的情况下继续执行。(或者,也可以在 N 响应之后发出 SSLRequest 消息,以尝试使用 SSL 加密,而不是 GSSAPI。)
To initiate a GSSAPI-encrypted connection, the frontend initially sends a GSSENCRequest message rather than a StartupMessage. The server then responds with a single byte containing G or N, indicating that it is willing or unwilling to perform GSSAPI encryption, respectively. The frontend might close the connection at this point if it is dissatisfied with the response. To continue after G, using the GSSAPI C bindings as discussed in RFC 2744 or equivalent, perform a GSSAPI initialization by calling gss_init_sec_context() in a loop and sending the result to the server, starting with an empty input and then with each result from the server, until it returns no output. When sending the results of gss_init_sec_context() to the server, prepend the length of the message as a four byte integer in network byte order. To continue after N, send the usual StartupMessage and proceed without encryption. (Alternatively, it is permissible to issue an SSLRequest message after an N response to try to use SSL encryption instead of GSSAPI.)
前端还应做好准备,处理服务器对 GSSENCRequest 发出的 ErrorMessage 响应。这只会出现在服务器早于 GSSAPI 加密支持添加到 PostgreSQL 的情况下。在这种情况下,必须关闭连接,但前端可能会选择打开一个全新连接,并在不请求 GSSAPI 加密的情况下继续执行。
The frontend should also be prepared to handle an ErrorMessage response to GSSENCRequest from the server. This would only occur if the server predates the addition of GSSAPI encryption support to PostgreSQL. In this case the connection must be closed, but the frontend might choose to open a fresh connection and proceed without requesting GSSAPI encryption.
当可以执行 GSSAPI 加密时,服务器将只发送单个 G 字节,然后等待前端启动 GSSAPI 握手。如果此时能读取到额外的字节,这可能意味着中间人在尝试执行缓冲区填充攻击 ( CVE-2021-23222)。前端应编制成要么在将套接字转交给其 GSSAPI 库之前从套接字中读取恰好一个字节,要么在发现已读取额外的字节的情况下将其视为协议违规。
When GSSAPI encryption can be performed, the server is expected to send only the single G byte and then wait for the frontend to initiate a GSSAPI handshake. If additional bytes are available to read at this point, it likely means that a man-in-the-middle is attempting to perform a buffer-stuffing attack (CVE-2021-23222). Frontends should be coded either to read exactly one byte from the socket before turning the socket over to their GSSAPI library, or to treat it as a protocol violation if they find they have read additional bytes.
初始 GSSENCRequest 也可以在正在打开的连接中使用,以发送 CancelRequest 消息。
An initial GSSENCRequest can also be used in a connection that is being opened to send a CancelRequest message.
一旦 GSSAPI 加密成功建立,请使用 gss_wrap() 加密常规的 StartupMessage 和所有后续数据,将 gss_wrap() 结果的长度作为四字节整型数(按照网络字节顺序)预先置入实际加密负载中。请注意,服务器将只接受客户端发送的加密数据包大小不超过 16kB;gss_wrap_size_limit() 应由客户端使用以确定将满足此限制条件的非加密消息的大小,并且应将更大的消息分解成多个 gss_wrap() 调用。典型的段是 8kB 的非加密数据,产生略大于 8kB 的加密数据包,但远小于 16kB 的最大值。服务器可能不会向客户端发送大于 16kB 的加密数据包。
Once GSSAPI encryption has been successfully established, use gss_wrap() to encrypt the usual StartupMessage and all subsequent data, prepending the length of the result from gss_wrap() as a four byte integer in network byte order to the actual encrypted payload. Note that the server will only accept encrypted packets from the client which are less than 16kB; gss_wrap_size_limit() should be used by the client to determine the size of the unencrypted message which will fit within this limit and larger messages should be broken up into multiple gss_wrap() calls. Typical segments are 8kB of unencrypted data, resulting in encrypted packets of slightly larger than 8kB but well within the 16kB maximum. The server can be expected to not send encrypted packets of larger than 16kB to the client.
尽管该协议本身并未提供服务器强制执行 GSSAPI 加密的方法,但管理员可以将服务器配置为拒绝未加密的会话,作为身份验证检查的副产品。
While the protocol itself does not provide a way for the server to force GSSAPI encryption, the administrator can configure the server to reject unencrypted sessions as a byproduct of authentication checking.