PostgreSQL 消息流

2021-09-14 16:42 更新
52.2.1. 啟動
52.2.2. 簡單查詢
52.2.3. 擴(kuò)展查詢
52.2.4. 函數(shù)調(diào)用
52.2.5. COPY操作
52.2.6. 異步操作
52.2.7. 取消正在處理的請求
52.2.8. 終止
52.2.9. SSL會話加密
52.2.10. GSSAPI 會話加密

本節(jié)描述消息流以及每種消息類型的語意(每種信息的準(zhǔn)確形式在第 52.7 節(jié)里)。根據(jù)連接的狀態(tài)不同,存在幾種不同的子協(xié)議: 啟動、查詢、函數(shù)調(diào)用、COPY和終止。還有特殊的規(guī)定用于一步操作(包括通知響應(yīng)和命令取消),這些可能在啟動階段過后的任何時(shí)間產(chǎn)生。

52.2.1. 啟動

要開始一個(gè)會話,前端打開一個(gè)與服務(wù)器的連接并且發(fā)送一個(gè)啟動消息。這個(gè)消息包括用戶名以及用戶希望連接的數(shù)據(jù)庫名; 它還標(biāo)識要使用的特定的協(xié)議版本(啟動信息可以有選擇地包括運(yùn)行時(shí)參數(shù)的額外設(shè)置)。服務(wù)器然后就使用這些信息及服務(wù)器配置文件的內(nèi)容 (比如 pg_hba.conf)來判斷這個(gè)連接是否可以接受以及需要什么樣的額外的認(rèn)證(如果需要)。

然后服務(wù)器就發(fā)送合適的認(rèn)證請求信息,前端必須用合適的認(rèn)證響應(yīng)信息來響應(yīng)(比如一個(gè)口令)。對于除了GSSAPI、SSPI和SASL之外的所有認(rèn)證方式都至少有一個(gè)請求和一個(gè)響應(yīng)。在有些方法中前端不需要發(fā)出任何響應(yīng),并且因此就不會由任何認(rèn)證請求發(fā)生。對于GSSAPI、SSPI和SASL,可能需要多個(gè)包的交換才能完成認(rèn)證。

認(rèn)證周期要么以服務(wù)器的拒絕連接(ErrorResponse)結(jié)束, 要么以AuthenticationOK 結(jié)束。

這個(gè)階段來自服務(wù)器可能消息是:

ErrorResponse

連接請求被拒絕。然后服務(wù)器馬上關(guān)閉連接。

AuthenticationOk

認(rèn)證交換成功完成。

AuthenticationKerberosV5

現(xiàn)在前端必須與服務(wù)器進(jìn)行一次Kerberos V5認(rèn)證對話(在這里沒有描述,Kerberos規(guī)范的一部分)。 如果對話成功,服務(wù)器響應(yīng)一個(gè)AuthenticationOk,否則它響應(yīng)一個(gè)ErrorResponse。這已經(jīng)不再被支持。

AuthenticationCleartextPassword

現(xiàn)在前端必須以明文形式發(fā)送一個(gè)包含口令的PasswordMessage。如果這是正確的口令,服務(wù)器用一個(gè) AuthenticationOk,否則它響應(yīng)一個(gè)ErrorResponse。

AuthenticationMD5Password

現(xiàn)在前端必須發(fā)送一個(gè)PasswordMessage,其中包含口令,且口令先用用戶名做MD5加密,然后使用在AuthenticationMD5Password消息里指定的4字節(jié)鹽粒加密。 如果這是正確口令,服務(wù)器用一個(gè)AuthenticationOk 響應(yīng),否則它用一個(gè)ErrorResponse 響應(yīng)。實(shí)際的PasswordMessage可以用SQL來計(jì)算:concat('md5', md5(concat(md5(concat(password, username)), random-salt)))(記住 md5()函數(shù)返回的結(jié)果是一個(gè)十六進(jìn)制串)。

AuthenticationSCMCredential

這個(gè)響應(yīng)只用于在支持SCM信任消息的平臺上的本地Unix域連接。前端必須發(fā)出一條SCM信任消息然后發(fā)送一個(gè)數(shù)據(jù)字節(jié)(數(shù)據(jù)字節(jié)的內(nèi)容并沒有意義; 它只被用于確保服務(wù)器等待足夠長的時(shí)間來接受信任信息)。如果信任是可以接受的, 那么服務(wù)器用AuthenticationOk響應(yīng),否則用ErrorResponse響應(yīng)(該消息只在9.1之前的服務(wù)器中發(fā)出。它可能最終會從協(xié)議規(guī)范中被刪除)。

AuthenticationGSS

前端必須現(xiàn)在開始一次GSSAPI談判。前端將發(fā)送一個(gè)帶有GSSAPI數(shù)據(jù)流第一部分的GSSResponse消息來響應(yīng)。如果需要進(jìn)一步的消息,服務(wù)器將會響應(yīng)AuthenticationGSSContinue。

AuthenticationSSPI

前端必須現(xiàn)在開始一次SSPI談判。前端將發(fā)送一個(gè)帶有SSPI數(shù)據(jù)流第一部分的GSSResponse來響應(yīng)。如果需要進(jìn)一步的消息,服務(wù)器將會響應(yīng)AuthenticationGSSContinue。

AuthenticationGSSContinue

這個(gè)消息包含對于前一步的GSSAPI或SSPI談判(AuthenticationGSS、AuthenticationSSPI或者前一個(gè)AuthenticationGSSContinue)的響應(yīng)數(shù)據(jù)。如果這個(gè)消息中的GSSAPI或SSPI數(shù)據(jù)指示需要更多數(shù)據(jù)來完成認(rèn)證,前端必須將所需的數(shù)據(jù)作為另一個(gè)GSSResponse發(fā)送。如果這個(gè)消息就能完成GSSAPI或SSPI認(rèn)證,服務(wù)器將接著發(fā)送AuthenticationOk來指示成功認(rèn)證,或者發(fā)送ErrorResponse來指示失敗。

AuthenticationSASL

前端現(xiàn)在必須發(fā)起一次SASL協(xié)商,使用該消息中列出的一種SASL機(jī)制。前端將用選中機(jī)制的名字發(fā)送一個(gè)SASLInitialResponse,并且SASL數(shù)據(jù)流的第一部分對應(yīng)于此。如果需要進(jìn)一步的消息,服務(wù)器將用AuthenticationSASLContinue回復(fù)。詳見第 52.3 節(jié)。

AuthenticationSASLContinue

這個(gè)消息包含從SASL協(xié)商(AuthenticationSASL或者一個(gè)之前的AuthenticationSASLContinue)的前一步中得到的挑戰(zhàn)數(shù)據(jù)。前端必須用一個(gè)SASLResponse消息響應(yīng)。

AuthenticationSASLFinal

已經(jīng)用額外的與機(jī)制相關(guān)的數(shù)據(jù)為該客戶端完成SASL認(rèn)證。服務(wù)器接下來將發(fā)送AuthenticationOk來表示認(rèn)證成功,或者發(fā)送ErrorResponse表示失敗。只有在SASL機(jī)制指定要在完成時(shí)從服務(wù)器發(fā)送額外數(shù)據(jù)到客戶端的情況下才會發(fā)送這個(gè)消息。

NegotiateProtocolVersion

服務(wù)器不支持客戶端請求的次協(xié)議版本,但是支持該協(xié)議更早的一個(gè)版本,這個(gè)消息會指出受支持的最高次版本。如果客戶端在啟動包中請求了不受支持的協(xié)議選項(xiàng)(例如以_pq_.開始),則這個(gè)消息也會被發(fā)出。這個(gè)消息后面將會跟著一個(gè)ErrorResponse或者一個(gè)指示認(rèn)證成功或失敗的消息。

如果前端不支持服務(wù)器要求的認(rèn)證方式,那么它應(yīng)該馬上關(guān)閉連接。

在收到AuthenticationOk包之后,前端必須等待來自服務(wù)器的進(jìn)一步消息。在這個(gè)階段會啟動一個(gè)后端進(jìn)程, 而前端只是一個(gè)感興趣的旁觀者。啟動嘗試仍有可能失?。‥rrorResponse),服務(wù)器也有可能拒絕支持所請求的次協(xié)議版本(NegotiateProtocolVersion),但是通常情況下,后端將發(fā)送一些ParameterStatus消息、BackendKeyData以及最后的ReadyForQuery。

在這個(gè)階段,后端將嘗試應(yīng)用任何在啟動消息里給出的額外的運(yùn)行時(shí)參數(shù)設(shè)置。如果成功,這些值將成為會話的缺省值。錯(cuò)誤將導(dǎo)致ErrorResponse并退出。

這個(gè)階段來自后端的可能消息是:

BackendKeyData

這個(gè)消息提供了密鑰數(shù)據(jù),前端如果想要在稍后發(fā)出取消請求,則必須保存這個(gè)數(shù)據(jù)。前端不應(yīng)該響應(yīng)這個(gè)信息,但是應(yīng)該繼續(xù)偵聽等待ReadyForQuery消息。

ParameterStatus

這個(gè)消息告訴前端有關(guān)后端參數(shù)的當(dāng)前(初始)設(shè)置,比如client_encoding或者DateStyle等。前端可以忽略這些信息,或者記錄其設(shè)置用于將來使用; 參閱第 52.2.6 節(jié)獲取更多細(xì)節(jié)。前端不應(yīng)該響應(yīng)這些信息, 而是應(yīng)該繼續(xù)偵聽ReadyForQuery消息。

ReadyForQuery

啟動成功,前端現(xiàn)在可以發(fā)出命令。

ErrorResponse

啟動失敗,在發(fā)送完這個(gè)消息之后連接被關(guān)閉。

NoticeResponse

發(fā)出了一個(gè)警告信息。前端應(yīng)該顯示這個(gè)信息,但是要繼續(xù)監(jiān)聽ReadyForQuery或ErrorResponse。

后端在每個(gè)命令周期后都會發(fā)出一個(gè)相同的ReadyForQuery消息。 出于前端的編碼需要,前端可以合理地認(rèn)為ReadyForQuery是一個(gè)命令周期的開始,或者認(rèn)為ReadyForQuery 是啟動階段和每個(gè)隨后命令周期的結(jié)束, 具體是那種情況取決于前端的編碼需要。

52.2.2.  簡單查詢

一個(gè)簡單查詢周期是由前端發(fā)送一條Query消息給后端進(jìn)行初始化的。這條消息包含一個(gè)用文本字符串表達(dá)的 SQL 命令(或者一些命令)。 后端根據(jù)查詢命令串的內(nèi)容發(fā)送一條或者更多條響應(yīng)消息給前端,并且最后是一條ReadyForQuery響應(yīng)消息。ReadyForQuery通知前端它可以安全地發(fā)送新命令了 (實(shí)際上前端不必在發(fā)送其它命令之前等待ReadyForQuery,但是這樣一來,前端必須能發(fā)現(xiàn)較早發(fā)出的命令失敗而稍后發(fā)出的命令成功的情況)。

來自后端的可能的消息是:

CommandComplete

一個(gè)SQL命令正常結(jié)束。

CopyInResponse

后端已經(jīng)準(zhǔn)備好從前端拷貝數(shù)據(jù)到一個(gè)表里面去,參見第 52.2.5 節(jié)。

CopyOutResponse

后端已經(jīng)準(zhǔn)備好從一個(gè)表里拷貝數(shù)據(jù)到前端,參見第 52.2.5 節(jié)

RowDescription

表示為了響應(yīng)一個(gè)SELECT、FETCH等查詢, 將要返回行。這條消息的內(nèi)容描述了行的列布局。這條消息后面將跟著DataRow消息,每個(gè)DataRow消息包含一個(gè)要被返回的行。

DataRow

SELECT、FETCH等查詢返回的結(jié)果集中的一行。

EmptyQueryResponse

識別了一個(gè)空的查詢字符串。

ErrorResponse

出錯(cuò)了。

ReadyForQuery

查詢字符串的處理完成。發(fā)送一個(gè)獨(dú)立的消息來標(biāo)識這種情況是因?yàn)椴樵冏址赡馨鄠€(gè)SQL命令(CommandComplete只是標(biāo)記一條SQL命令處理完畢,而不是整個(gè)查詢字符串處理完畢)。不管是處理成功結(jié)束還是產(chǎn)生錯(cuò)誤,ReadyForQuery總會被發(fā)送。

NoticeResponse

發(fā)送了一個(gè)與查詢有關(guān)的警告信息。提示信息是附加在其他響應(yīng)上的,也就是說,后端將繼續(xù)處理該命令。

對SELECT查詢(或其它返回行集的查詢,比如EXPLAINSHOW)的響應(yīng)通常包含 RowDescription、零個(gè)或者多個(gè) DataRow 消息以及最后的 CommandComplete。 COPY到前端或者從前端COPY會調(diào)用第 52.2.5 節(jié)里描述的特殊協(xié)議。所有其他查詢類型通常只產(chǎn)生一個(gè)CommandComplete消息。

因?yàn)椴樵冏址赡馨舾蓚€(gè)查詢(用分號分隔),所以在后端完成查詢字符串的處理之前可能有好幾個(gè)這樣的響應(yīng)序列。如果整個(gè)字符串已經(jīng)處理完,后端已經(jīng)準(zhǔn)備好接受新查詢字符串的時(shí)候則發(fā)出 ReadyForQuery消息。

如果收到一個(gè)完全空(除了空白之外沒有內(nèi)容)的查詢字符串,那么響應(yīng)是一條EmptyQueryResponse后面跟著ReadyForQuery。

在出現(xiàn)錯(cuò)誤的時(shí)候,發(fā)出一個(gè)ErrorResponse消息,后面跟著ReadyForQuery。查詢字符串的所有進(jìn)一步的處理都被ErrorResponse中止(即使里面還有查詢)。請注意這些事情可能在處理一個(gè)查詢產(chǎn)生的消息序列的中途發(fā)生。

在簡單查詢模式中,檢索出來的值的格式總是文本,除非給出的命令是在一個(gè)使用BINARY選項(xiàng)聲明的游標(biāo)上FETCH。 在這種情況下,檢索出來的值是二進(jìn)制格式的。在 RowDescription消息里給出的格式代碼將告訴我們用了那種格式。

前端在等待其他類型的消息時(shí)必須準(zhǔn)備接收ErrorResponse和NoticeResponse消息。 參閱 第 52.2.6 節(jié)來了解后端因?yàn)橥獠渴录赡苌傻南ⅰ?/p>

我們建議的方法是把前端代碼寫成狀態(tài)機(jī)的風(fēng)格,它可以在任何時(shí)刻接受任何有意義的消息類型,而不是假設(shè)消息的序列總是準(zhǔn)確。

52.2.2.1. 一個(gè)簡單查詢中的多條語句

當(dāng)一個(gè)簡單查詢消息中包含多于一條SQL語句(被分號分隔)時(shí),那些語句會被當(dāng)做一個(gè)事務(wù)中執(zhí)行,除非其中包括顯式事務(wù)控制命令來強(qiáng)制不同的行為。例如,如果消息包括

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

SELECT中的除零失敗將強(qiáng)制回滾第一個(gè)INSERT。進(jìn)而,因?yàn)樵撓⒌膱?zhí)行在第一個(gè)錯(cuò)誤時(shí)就被放棄,第二個(gè)INSERT根本都不會被嘗試。

如果該消息包含的是

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

那么第一個(gè)INSERT會被這個(gè)顯式的COMMIT命令提交。第二個(gè)INSERT以及SELECT仍會被當(dāng)作一個(gè)單一事務(wù),這樣除零失敗將回滾第二個(gè)INSERT,但不會回滾第一個(gè)。

這種行為通過在一個(gè)隱式事務(wù)塊中的一個(gè)多語句Query消息中運(yùn)行那些語句來實(shí)現(xiàn),除非它們運(yùn)行在某個(gè)顯式事務(wù)塊中。隱式事務(wù)塊與常規(guī)事務(wù)塊之間的區(qū)別在于隱式塊會在Query消息結(jié)束時(shí)自動被關(guān)閉,或者是在沒有錯(cuò)誤的情況下由一個(gè)隱式提交關(guān)閉,或者是在有錯(cuò)誤時(shí)由一個(gè)隱式的回滾關(guān)閉。這類似于一個(gè)語句自己執(zhí)行(當(dāng)不在事務(wù)塊中時(shí))時(shí)發(fā)生的隱式提交或回滾。

如果會話已經(jīng)在一個(gè)事務(wù)塊中,作為前面某個(gè)消息中BEGIN的結(jié)果,那么Query消息會簡單地繼續(xù)那個(gè)事務(wù)塊,不管該消息包含一個(gè)語句還是多個(gè)語句。不過,如果該Query消息包含一個(gè)關(guān)閉現(xiàn)有事務(wù)塊的COMMIT或者ROLLBACK,那么任何接下來的語句都會在一個(gè)隱式事務(wù)塊中被執(zhí)行。反過來,如果在多語句Query消息中出現(xiàn)一個(gè) BEGIN,那么它會開始一個(gè)常規(guī)事務(wù)塊,這個(gè)常規(guī)事務(wù)塊將只能被一個(gè)顯式的COMMIT或者ROLLBACK終止,不管這兩種命令是出現(xiàn)在這個(gè)Query消息還是后面的一個(gè)Query消息中。如果BEGIN跟在一些作為隱式事務(wù)塊執(zhí)行的語句后面,那些語句不會被立刻提交。實(shí)際上,它們會被包括到新的常規(guī)事務(wù)塊中。

出現(xiàn)在一個(gè)隱式事務(wù)塊中的COMMIT或者ROLLBACK會被正常執(zhí)行并且關(guān)閉該隱式塊。不過,由于沒有先前的BEGIN配對的COMMIT或者ROLLBACK表示一種錯(cuò)誤,所以將會發(fā)出一個(gè)警告。如果后面還有更多語句,將會為它們開始一個(gè)新的隱式事務(wù)塊。

在隱式事務(wù)塊中不允許保存點(diǎn),因?yàn)樗鼈儠c發(fā)生錯(cuò)誤時(shí)自動關(guān)閉塊的行為發(fā)生沖突。

記住,不管任何事務(wù)控制命令存不存在,Query消息的執(zhí)行會在第一個(gè)錯(cuò)誤時(shí)停止。因此,對于下面的在一個(gè)Query消息中的例子

BEGIN;
SELECT 1/0;
ROLLBACK;

會話中將留下一個(gè)失敗的常規(guī)事務(wù)塊,因?yàn)樵诔霈F(xiàn)除零錯(cuò)誤后不會到達(dá)ROLLBACK。將需要另一個(gè)ROLLBACK把會話恢復(fù)到一種可用的狀態(tài)。

另一種要注意的行為是,最初的詞法和語法分析是在整個(gè)查詢字符串被執(zhí)行之前進(jìn)行的。因此后面的語句中的簡單錯(cuò)誤(例如拼寫錯(cuò)誤的關(guān)鍵詞)可能會阻止任何語句的執(zhí)行。這通常對用戶是不可見的,因?yàn)樵诋?dāng)作一個(gè)隱式事務(wù)塊執(zhí)行時(shí),這些語句不管怎樣都會全部被回滾。不過,在嘗試于一個(gè)多語句Query中執(zhí)行多個(gè)事務(wù)時(shí),這種現(xiàn)象可能是可見的。例如,如果一個(gè)拼寫錯(cuò)誤把我們之前的例子變成

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

那么這些語句都不會被運(yùn)行,導(dǎo)致可見的差別,即第一個(gè)INSERT沒有被提交。在語義分析及其后階段檢測到的錯(cuò)誤(例如拼錯(cuò)的表名或者列名)不會有這種效果。

52.2.3. 擴(kuò)展查詢

擴(kuò)展查詢協(xié)議把上面描述的簡單協(xié)議分裂成若干個(gè)步驟。準(zhǔn)備步驟的結(jié)果可以被多次復(fù)用以提高效率。另外,還可以獲得額外的特性, 比如可以把數(shù)據(jù)值作為獨(dú)立的參數(shù)提供而不是必須把它們直接插入一個(gè)查詢字符串。

在擴(kuò)展協(xié)議里,前端首先發(fā)送一個(gè)Parse消息,它包含一個(gè)文本查詢字符串, 另外還有一些可選的有關(guān)參數(shù)占位符的數(shù)據(jù)類型的信息,以及一個(gè)最終的預(yù)備語句對象的名字(一個(gè)空字符串選擇未命名的預(yù)備語句)。響應(yīng)是一個(gè)ParseComplete或者ErrorResponse。 參數(shù)的數(shù)據(jù)類型可以用OID來指定;如果沒有給出,那么分析器將試圖用應(yīng)付無類型文字符串常量的方法來推導(dǎo)其數(shù)據(jù)類型。

注意

一個(gè)參數(shù)的數(shù)據(jù)類型可以通過設(shè)置為零, 或者讓參數(shù)類型OID的數(shù)目比查詢字符串里的參數(shù)符號($n)的數(shù)目少的方式不予指定。另外一個(gè)特例是參數(shù)的類型可以聲明為void(也就是說,偽類型void的OID)。這是為了允許用于某些函數(shù)參數(shù)的參數(shù)符號實(shí)際上是OUT參數(shù)。通常情況下,沒有什么環(huán)境會用到 void參數(shù), 但是如果在函數(shù)的參數(shù)列表里出現(xiàn)了這么一個(gè)參數(shù)符號,那么它實(shí)際上會被忽略。比如,一個(gè)像這樣的函數(shù)調(diào)用:foo($1,$2,$3,$4),如果$3$4被指定為具有類型是void,那么這個(gè)函數(shù)調(diào)用會匹配一個(gè)帶有兩個(gè)IN和兩個(gè)OUT參數(shù)的函數(shù)。

注意

在一個(gè)Parse消息里包含的查詢字符串不能包含超過一個(gè)SQL語句;否則就會報(bào)告一個(gè)語法錯(cuò)誤。這個(gè)限制在簡單查詢協(xié)議中并不存在,它只存在于擴(kuò)展協(xié)議中,因?yàn)樵试S預(yù)備語句或者入口包含多個(gè)命令將導(dǎo)致協(xié)議過度地復(fù)雜。

如果成功創(chuàng)建了一個(gè)命名的預(yù)備語句對象,那么它將持續(xù)到當(dāng)前會話結(jié)束, 除非被明確地刪除。一個(gè)未命名的預(yù)備語句只持續(xù)到下一個(gè)聲明未命名語句的Parse語句發(fā)出為止(請注意一個(gè)簡單的查詢消息也會銷毀未命名語句)。命名預(yù)備語句必須被明確地關(guān)閉,然后才能用一個(gè)Parse消息重新定義,但是未命名語句并不要求這個(gè)動作。命名預(yù)備語句也可以在SQL命令級別創(chuàng)建和訪問,方法是使用PREPAREEXECUTE。

一旦一個(gè)預(yù)備語句存在,就可以使用Bind消息使之進(jìn)入執(zhí)行狀態(tài)。Bind消息給出源預(yù)備語句的名字(空字符串表示未命名預(yù)備語句)、目標(biāo)入口的名字(空字符串表示未命名的入口)及用于那些在預(yù)備語句中出現(xiàn)的所有參數(shù)占位符的值。提供的參數(shù)集必須匹配那些預(yù)備語句所需要的參數(shù)(如果你在Parse消息里聲明任何void參數(shù),那么在Bind消息里給它們傳遞NULL值)。Bind還指定被查詢返回的數(shù)據(jù)的格式;格式可以在總體上聲明,也可以對每個(gè)列進(jìn)行聲明。響應(yīng)是BindComplete或ErrorResponse。

注意

輸出的格式是文本還是二進(jìn)制是由Bind里給出的格式代碼決定的,而不管涉及的是什么SQL命令。在使用擴(kuò)展查詢協(xié)議的時(shí)候,游標(biāo)聲明里的BINARY屬性是無關(guān)的。

當(dāng)Bind消息被處理時(shí)通常會進(jìn)行查詢規(guī)劃。如果預(yù)備語句沒有參數(shù)或者是被反復(fù)執(zhí)行,服務(wù)器可能會保存創(chuàng)建好的計(jì)劃并在后續(xù)對同一個(gè)預(yù)備語句的Bind消息中重用之。不過,當(dāng)它發(fā)現(xiàn)可以創(chuàng)建一個(gè)效率比依賴指定參數(shù)值的計(jì)劃不低很多的一般性計(jì)劃時(shí),它仍然會進(jìn)行查詢規(guī)劃。但是這對于協(xié)議所關(guān)注的來說是透明的。

如果成功創(chuàng)建了一個(gè)命名入口對象,那么它將持續(xù)到當(dāng)前事務(wù)的結(jié)尾,除非被明確地刪除。一個(gè)未命名入口在事務(wù)的結(jié)尾刪除,或者是在發(fā)出的下一個(gè)Bind語句聲明了一個(gè)未命名入口為止(請注意一個(gè)簡單查詢消息也會刪除這個(gè)未命名入口)。命名入口在可以用一個(gè)Bind消息重新定義之前必須明確地關(guān)閉,但是未命名入口不要求這個(gè)動作。命名入口也可以在SQL命令的級別創(chuàng)建和訪問,方法是使用DECLARE CURSORFETCH

一旦一個(gè)入口存在,那么就可以用一個(gè)Execute消息執(zhí)行它。Execute消息指定入口的名字(空字符串表示未命名入口)和一個(gè)最大的結(jié)果行計(jì)數(shù)(零表示取出所有行)。 結(jié)果行計(jì)數(shù)只對包含返回行集的入口有意義;在其它情況下,該命令總是被執(zhí)行到結(jié)束,而行計(jì)數(shù)會被忽略。Execute消息的可能響應(yīng)和那些通過簡單查詢協(xié)議發(fā)出的查詢一樣,只不過執(zhí)行不會導(dǎo)致后端發(fā)出ReadyForQuery或者RowDescription。

如果Execute在入口的執(zhí)行完成之前終止(因?yàn)檫_(dá)到了一個(gè)非零的結(jié)果行計(jì)數(shù)),它將發(fā)送一個(gè)PortalSuspended消息;這個(gè)消息的出現(xiàn)告訴前端應(yīng)該在同一個(gè)入口上發(fā)出另外一個(gè)Execute消息以完成操作。在入口的執(zhí)行完成之前,不會發(fā)出表示源SQL命令結(jié)束的CommandComplete消息。因此執(zhí)行階段總是由下列消息之一出現(xiàn)標(biāo)志著結(jié)束:CommandComplete、EmptyQueryResponse(如果入口是從一個(gè)空字符串創(chuàng)建出來的)、ErrorResponse或者PortalSuspended。

每個(gè)擴(kuò)展查詢消息序列完成后,前端都應(yīng)該發(fā)出一條Sync消息。這個(gè)無參數(shù)的消息導(dǎo)致后端關(guān)閉當(dāng)前事務(wù)——如果當(dāng)前事務(wù)不是在一個(gè)BEGIN/COMMIT事務(wù)塊中(關(guān)閉的意思就是在沒有錯(cuò)誤的情況下提交, 或者是有錯(cuò)誤的情況下回滾)。然后響應(yīng)一條ReadyForQuery消息。Sync的目的是提供一個(gè)錯(cuò)誤恢復(fù)的重新同步的點(diǎn)。 如果在處理任何擴(kuò)展查詢消息的時(shí)候偵測到任何錯(cuò)誤,那么后端會發(fā)出ErrorResponse,然后讀取并拋棄消息直到一個(gè)Sync到來,然后發(fā)出ReadyForQuery并且返回到正常的消息處理中(但是要注意如果正在處理Sync的時(shí)候發(fā)生了錯(cuò)誤,那么不會忽略任何東西 — 這樣就保證了為每個(gè)Sync發(fā)出一個(gè)并且只是一個(gè)ReadyForQuery)。

注意

Sync并不導(dǎo)致一個(gè)用BEGIN打開的事務(wù)塊關(guān)閉。我們可以偵測到這種情況,因?yàn)镽eadyForQuery消息包含事務(wù)狀態(tài)信息。

除了這些基本的、必須的操作之外,在擴(kuò)展查詢協(xié)議里還有幾種可選的操作可以使用。

Describe消息(入口變體)指定一個(gè)現(xiàn)有的入口的名字(或者一個(gè)空字符串表示未命名入口)。響應(yīng)是一個(gè)RowDescription消息,它描述了執(zhí)行該入口將要返回的行;或者是一個(gè)NoData消息——果入口并不包含會返回行的查詢;或者是一個(gè)ErrorResponse——如果入口不存在。

Describe消息(語句變體)指定一個(gè)現(xiàn)有的預(yù)備語句的名字(或者一個(gè)空字符串表示未命名預(yù)備語句)。 響應(yīng)是一個(gè)描述該語句需要的參數(shù)的ParameterDescription消息,后面跟著一個(gè)描述該語句最終執(zhí)行后返回的行的RowDescription消息(或者是 NoData 消息,如果該語句不返回行)。如果沒有這樣的預(yù)備語句,則返回ErrorResponse。請注意因?yàn)檫€沒有發(fā)出Bind,所以后端還不知道用于返回列的格式;在這種情況下,RowDescription消息里面的格式代碼域?qū)⑹橇恪?/p>

提示

在大多數(shù)情況下,前端在發(fā)出Execute之前應(yīng)該發(fā)出某種Describe的變體,以保證它知道如何解析它將得到的結(jié)果。

Close消息關(guān)閉一個(gè)現(xiàn)有的預(yù)備語句或者入口,并且釋放資源。對一個(gè)不存在的語句或者入口發(fā)出Close不是一個(gè)錯(cuò)誤。響應(yīng)通常是CloseComplete,但如果在釋放資源的時(shí)候遇到了一些困難也可以是ErrorResponse。請注意關(guān)閉一個(gè)預(yù)備語句會隱含地關(guān)閉任何從該語句構(gòu)造出來的打開的入口。

Flush消息并產(chǎn)生任何特定的輸出,但是強(qiáng)制后端發(fā)送任何還在它的輸出緩沖區(qū)中待處理的數(shù)據(jù)。Flush必須在除Sync外的任何擴(kuò)展查詢命令后面發(fā)出——如果前端希望在發(fā)出更多的命令之前檢查該命令的結(jié)果的話。如果沒有Flush,后端返回的消息將組合成盡可能少的數(shù)據(jù)包,以減少網(wǎng)絡(luò)負(fù)荷。

注意

簡單查詢消息大概等于一系列使用未命名預(yù)備語句和無參數(shù)入口對象的Parse、Bind、入口Describe、Execute、Close、Sync。一個(gè)區(qū)別是它會在查詢字符串中接受多個(gè)SQL語句,并連續(xù)地為每個(gè)語句自動執(zhí)行綁定/描述/執(zhí)行序列。另外一個(gè)區(qū)別是它不會返回ParseComplete、Bindcomplete、CloseComplete或者NoData消息。

52.2.4.  函數(shù)調(diào)用

函數(shù)調(diào)用子協(xié)議允許客戶端請求一個(gè)對存在于數(shù)據(jù)庫pg_proc系統(tǒng)表中的任意函數(shù)的直接調(diào)用??蛻舳吮仨氃谠摵瘮?shù)上有執(zhí)行的權(quán)限。

注意

函數(shù)調(diào)用子協(xié)議是一個(gè)遺留的特性,在新代碼里可能最好避免用它。類似的結(jié)果可以通過設(shè)置一個(gè)執(zhí)行SELECT function($1, ...)的預(yù)備語句得到。這樣函數(shù)調(diào)用周期就可以用 Bind/Execute 代替。

一個(gè)函數(shù)調(diào)用周期是由前端向后端發(fā)送一條FunctionCall消息初始化的。然后后端根據(jù)函數(shù)調(diào)用的結(jié)果發(fā)送一條或者更多響應(yīng)消息,并且最后是一條ReadyForQuery響應(yīng)消息。ReadyForQuery通知前端它可以安全地發(fā)送一個(gè)新的查詢或者函數(shù)調(diào)用了。

來自后端的可能的響應(yīng)消息是:

ErrorResponse

發(fā)生了一個(gè)錯(cuò)誤。

FunctionCallResponse

函數(shù)調(diào)用完成并且在消息中返回一個(gè)結(jié)果(請注意函數(shù)調(diào)用協(xié)議只能處理單個(gè)標(biāo)量結(jié)果,不能處理行類型或者集合類型的結(jié)果)。

ReadyForQuery

函數(shù)調(diào)用處理完成。ReadyForQuery將總是被發(fā)送,不管是成功完成處理還是發(fā)生一個(gè)錯(cuò)誤。

NoticeResponse

發(fā)出了一條有關(guān)該函數(shù)調(diào)用的警告信息。通知是附加在其他響應(yīng)上的,也就是說,后端將繼續(xù)處理該命令。

52.2.5. COPY操作

COPY命令允許在服務(wù)器和客戶端之間進(jìn)行高速大批量數(shù)據(jù)傳輸。拷貝入和拷貝出操作每個(gè)都把連接切換到一個(gè)獨(dú)立的子協(xié)議中,并且持續(xù)到操作結(jié)束。

拷貝入模式(數(shù)據(jù)傳輸?shù)椒?wù)器)是在后端執(zhí)行一個(gè)COPY FROM STDIN語句的時(shí)候初始化的。后端發(fā)送一個(gè)CopyInResponse消息給前端。前端應(yīng)該發(fā)送零條或者更多CopyData消息,形成一個(gè)輸出數(shù)據(jù)的流(消息的邊界和行的邊界沒有任何相關(guān)性要求,盡管通常那是合理的選擇)。前端可以通過發(fā)送一個(gè)CopyDone消息來終止拷貝入操作(允許成功終止),也可以發(fā)出一個(gè)CopyFail消息(它將導(dǎo)致COPY語句帶著錯(cuò)誤失?。?。 然后后端就恢復(fù)回它在COPY開始之前的命令處理模式,可能是簡單查詢協(xié)議,也可能是擴(kuò)展查詢協(xié)議。然后它會發(fā)送CommandComplete(如果成功)或者ErrorResponse(如果失敗)。

如果在拷貝入模式下后端檢測到了錯(cuò)誤(包括接受接收到CopyFiail消息, 或者是任何除了CopyData或者CopyDone之外的前端消息),那么后端將發(fā)出一個(gè)ErrorResponse消息。如果COPY命令是通過一個(gè)擴(kuò)展查詢消息發(fā)出的, 那么后端從現(xiàn)在開始將拋棄前端消息,直到一個(gè)Sync消息到達(dá),然后它將發(fā)出ReadyForQuery并且返回到正常的處理中。如果COPY命令是在一個(gè)簡單查詢消息里發(fā)出的,那么該消息剩余部分被丟棄然后發(fā)出ReadyForQuery消息。不管是哪種情況,任何前端發(fā)出的CopyData、CopyDone或者CopyFail消息都將被簡單地拋棄。

在拷貝入模式下,后端將忽略所收到的Flush和Sync消息。收到任何其他非拷貝消息類型都會造成一個(gè)錯(cuò)誤,它將導(dǎo)致上面所描述的拷貝入狀態(tài)中斷(Flush和Sync的例外是為了方便客戶端庫,它們總是在一個(gè)Execute消息之后發(fā)送Flush和Sync,而不檢查被執(zhí)行的命令是否為一個(gè)COPY FROM STDIN)。

拷貝出模式(數(shù)據(jù)從服務(wù)器發(fā)出)是在后端執(zhí)行一個(gè)COPY TO STDOUT語句的時(shí)候初始化的。后端發(fā)出一個(gè)CopyOutResponse消息給前端,后面跟著零或者多個(gè)CopyData消息(總是每行一個(gè)),然后跟著CopyDone。然后后端回退到它在COPY開始之前的命令處理模式,然后發(fā)送CommandComplete。前端不能退出傳輸(除非是關(guān)閉連接或者發(fā)出一個(gè)Cancel請求),但是它可以拋棄不需要的CopyData和CopyDone消息。

在拷貝出模式中,如果后端檢測到錯(cuò)誤,那么它將發(fā)出一個(gè)ErrorResponse消息并且回到正常的處理。前端應(yīng)該把收到ErrorResponse當(dāng)作終止拷貝出模式的標(biāo)志。

在CopyData消息中間可能會散布有NoticeResponse和ParameterStatus消息。前端必須處理這些情況,并且應(yīng)該也為異步消息類型(參見第 52.2.6 節(jié))準(zhǔn)備好。否則任何除CopyData或CopyDone之外的消息類型都會被認(rèn)為是要中止拷貝出模式。

還有另外一種被稱為雙向拷貝的與拷貝相關(guān)的模式,它允許“向”“從”服務(wù)器高速傳輸大批量數(shù)據(jù)。當(dāng)后端處于walsender模式中執(zhí)行一個(gè)START_REPLICATION語句時(shí),它會啟動雙向拷貝模式。后端會發(fā)送一個(gè)CopyBothResponse消息給前端。然后前端和后端都會發(fā)送CopyData消息,然后直到最后發(fā)送一個(gè)CopyDone消息。在客戶端發(fā)送一個(gè)CopyDone消息后,連接將從雙向拷貝模式轉(zhuǎn)換到拷貝出模式,并且客戶端將不能發(fā)送更多CopyData消息。類似的,當(dāng)服務(wù)器發(fā)送了一個(gè)CopyDone消息,連接進(jìn)入到拷貝入模式,并且服務(wù)器將不能發(fā)送更多CopyData消息。在雙方發(fā)送完一個(gè)CopyDone消息后,拷貝模式被中斷,而后端將回到之前的命令處理模式。如果在雙向拷貝模式中出現(xiàn)一個(gè)后端檢測到的錯(cuò)誤,后端將發(fā)出一個(gè)ErrorResponse消息,然后將發(fā)出ReadyForQuery并返回到普通處理。前端將把收到ErrorResponse作為在雙向上中斷拷貝的信號,在這種情況下不會有CopyDone被發(fā)出。關(guān)于在雙向拷貝模式下傳輸?shù)淖訁f(xié)議請參見 第 52.4 節(jié)。

CopyInResponse、CopyOutResponse和CopyBothResponse消息包括域和格式代碼,域告訴前端每行的列數(shù),而格式代碼則用于具體每個(gè)列(就目前的實(shí)現(xiàn)而言,一個(gè)給定COPY操作中的所有列都將使用同樣的格式,但是消息設(shè)計(jì)并不做這個(gè)假設(shè))。

52.2.6.  異步操作

有幾種情況下后端會發(fā)送一些并非由特定前端命令流傳達(dá)的消息。在任何時(shí)候前端都必須準(zhǔn)備處理這些消息,即使它是并未參與一個(gè)查詢。在最低限度下,我們應(yīng)該在開始讀取查詢響應(yīng)之前檢查這些情況。

NoticeResponse消息有可能是因?yàn)橥獠康幕顒佣a(chǎn)生的;比如,如果數(shù)據(jù)庫管理員進(jìn)行一次快速數(shù)據(jù)庫關(guān)閉,那么后端將在關(guān)閉連接之前發(fā)送一個(gè)NoticeResponse來表明這些。相應(yīng)地,前端應(yīng)該總是準(zhǔn)備接受和顯示NoticeResponse消息,即使連接事實(shí)上是空閑的。

如果任何時(shí)候有任何參數(shù)值的活躍值改變且后端認(rèn)為前端應(yīng)該知道這些,那么都會產(chǎn)生ParameterStatus消息。這種情況最常見發(fā)生的情形是對前端執(zhí)行的一個(gè)SET命令進(jìn)行響應(yīng),并且這種情況實(shí)際上是同步的 — 但是也有可能是數(shù)據(jù)庫管理員改變了配置文件然后項(xiàng)服務(wù)器發(fā)出SIGHUP信號導(dǎo)致了參數(shù)狀態(tài)的變化。同樣,如果一個(gè)SET命令回滾,那么也會生成一個(gè)合適的ParameterStatus 消息以報(bào)告當(dāng)前有效值。

目前,系統(tǒng)內(nèi)有一套會生成ParameterStatus消息的寫成硬代碼的參數(shù),它們是: (server_encoding,TimeZone 和 integer_datetimes 在 8.0 版本之前沒有報(bào)告。standard_conforming_strings 在版本 8.1 之前沒有報(bào)告。) 請注意 server_version, server_encoding 和 integer_datetimes 是偽參數(shù),啟動后不能修改。 這些可能在將來改變,或者甚至是變成可以配置的。 因此,前端應(yīng)該簡單地忽略那些它不懂或者不關(guān)心的 ParameterStatus。 server_version、 server_encoding、 client_encoding、 application_nameis_superuser、 session_authorizationDateStyle、 IntervalStyle、 TimeZoneinteger_datetimes以及 standard_conforming_stringsserver_encoding、 TimeZone以及integer_datetimes在版本8.0之前不會被報(bào)告; standard_conforming_strings在版本8.1之前不會被報(bào)告; IntervalStyle在版本8.4之前不會被報(bào)告; application_name在版本9.0之前不會被報(bào)告)。 注意server_version、server_encoding以及integer_datetimes是偽參數(shù),它們不能在啟動之后被改變。這種設(shè)置可能在未來改變,甚至變成可配置的。相應(yīng)地,一個(gè)前端應(yīng)該簡單地忽略那些與它不懂或者不關(guān)心的參數(shù)相關(guān)的ParameterStatus。

如果前端發(fā)出一個(gè)LISTEN命令, 那么無論何時(shí)在為同一個(gè)通道名NOTIFY時(shí),后端將發(fā)送一個(gè)NotificationResponse消息(不要和NoticeResponse搞混?。?。

注意

目前,NotificationResponse只能在一個(gè)事務(wù)外面發(fā)送,因此它將不會在一個(gè)命令響應(yīng)序列中間出現(xiàn),但是它可能正好在ReadyForQuery之前出現(xiàn)。不過,在前端邏輯中做上述假設(shè)是不明智的。好的做法是在協(xié)議的任何點(diǎn)上都可以接受NotificationResponse。

52.2.7.  取消正在處理的請求

在一條查詢正在處理的時(shí)候,前端可以請求取消該查詢。這種取消請求不是直接通過打開的連接發(fā)送給后端的,這么做是因?yàn)閷?shí)現(xiàn)的效率:我們不希望后端在處理查詢的過程中不停地檢查前端來的輸入。 取消請求應(yīng)該相對而言比較少見,所以我們把取消做得稍微笨拙一些,以便不影響正常狀況的性能。

要發(fā)出一條取消請求,前端打開一個(gè)與服務(wù)器的新連接并且發(fā)送一條CancelRequest消息, 而不是通常在新連接中經(jīng)常發(fā)送的StartupMessage消息。服務(wù)器將處理這個(gè)請求然后關(guān)閉連接。 出于安全原因,對取消請求消息不做直接的響應(yīng)。

除非CancelRequest消息包含在連接啟動過程中傳遞給前端的相同的關(guān)鍵數(shù)據(jù)(PID和密鑰),否則它將被忽略。如果該請求匹配當(dāng)前運(yùn)行著的后端的PID和密鑰, 則退出當(dāng)前查詢的處理(目前的實(shí)現(xiàn)里采用的方法是向正在處理該查詢的后端進(jìn)程發(fā)送一個(gè)特殊的信號)。

取消信號可能產(chǎn)生或者不產(chǎn)生效果 — 例如,如果它在后端完成查詢的處理后到達(dá),那么它就沒有做用。如果取消起作用了,會導(dǎo)致當(dāng)前命令伴隨著一個(gè)錯(cuò)誤消息提前退出。

這么做是對安全性和有效性通盤考慮的結(jié)果,前端沒有直接的方法獲知一個(gè)取消請求是否成功。它必須繼續(xù)等待后端對查詢響應(yīng)。發(fā)出一個(gè)取消僅僅是增加了當(dāng)前查詢快些結(jié)束的可能性, 同時(shí)也增加了當(dāng)前查詢會伴隨著一條錯(cuò)誤消息失敗而不是成功執(zhí)行的可能性。

因?yàn)槿∠埱笫峭ㄟ^新的聯(lián)接發(fā)送給服務(wù)器而不是通過平常的前端/后端通訊鏈接,所以取消請求可能被任意進(jìn)程發(fā)出的,而不僅僅是要取消查詢的前端。 這樣可能對創(chuàng)建多進(jìn)程應(yīng)用提供了更多的靈活性。同時(shí)這樣也帶來了安全風(fēng)險(xiǎn),因?yàn)槿魏我粋€(gè)非授權(quán)用戶都可能試圖取消查詢。這個(gè)安全風(fēng)險(xiǎn)通過要求在取消請求中提供一個(gè)動態(tài)生成的密鑰來解決。

52.2.8. 終止

通常優(yōu)雅的終止過程是前端發(fā)送一條Terminate消息并且立刻關(guān)閉連接。一旦收到消息,后端馬上關(guān)閉連接并且終止。

在少數(shù)情況下(比如一個(gè)管理員命令數(shù)據(jù)庫關(guān)閉),后端可能在沒有任何前端請求的情況下斷開連接。在這種情況下,后端將在它斷開連接之前嘗試發(fā)送一個(gè)錯(cuò)誤或者通知消息給出斷開的原因。

其它終止的情況發(fā)生在各種失敗的場合,比如某一方的內(nèi)核轉(zhuǎn)儲、失去通訊鏈路、丟失了消息邊界同步等。不管是前端還是后端看到了一個(gè)意外的連接關(guān)閉,那么它應(yīng)該清理現(xiàn)場并且終止。 如果前端不想終止自己,那么它有一個(gè)選項(xiàng)是重連服務(wù)器的方法啟動一個(gè)新的后端。如果收到了一個(gè)無法識別的消息類型,那么我們也建議關(guān)閉連接,因?yàn)槌霈F(xiàn)這種情況可能意味著是丟失了消息邊界的同步。

不管是正常還是不正常的終止,任何打開的事務(wù)都會回滾而不是提交。不過,我們應(yīng)該注意的是如果一個(gè)前端在一個(gè)非SELECT查詢正在處理的時(shí)候斷開, 那么后端很可能在發(fā)現(xiàn)斷開之前先完成查詢的處理。如果查詢處于任何事務(wù)塊之外(BEGIN ... COMMIT序列),那么其結(jié)果很可能在得知斷開之前被提交。

52.2.9. SSL會話加密

如果編譯PostgreSQL的時(shí)候打開了SSL支持,那么前后端通訊就可以用SSL加密。 這樣就提供了一種在攻擊者可能捕獲會話通訊數(shù)據(jù)包的環(huán)境下保證通訊安全的方法。有關(guān)使用SSL加密PostgreSQL會話的更多信息, 請參閱第 18.9 節(jié)。

要開始一次SSL加密連接,前端先是發(fā)送一個(gè)SSLRequest消息,而不是StartupMessage。然后服務(wù)器以一個(gè)包含SN的字節(jié)響應(yīng),分別表示它愿意還是不愿意進(jìn)行SSL。如果此時(shí)前端對響應(yīng)不滿意, 那么它可以關(guān)閉連接。要在S之后繼續(xù),那么先進(jìn)行與服務(wù)器的 SSL啟動握手(沒有在這里描述,是SSL規(guī)范的一部分)。 如果這些成功了,那么繼續(xù)發(fā)送普通的StartupMessage。這種情況下,StartupMessage和所有隨后的數(shù)據(jù)都將由SSL加密。要在N之后繼續(xù),則發(fā)送普通的StartupMessage并不適用加密繼續(xù)處理。

前端應(yīng)該也準(zhǔn)備處理一個(gè)來自服務(wù)器的給SSLRequest的ErrorMessage響應(yīng)。這種情況只在服務(wù)器早于PostgreSQLSSL支持的情況下才會出現(xiàn)(這種服務(wù)器現(xiàn)在非常古老,并且可能不再存在了)。在這種情況下,連接必需關(guān)閉,但是前端可以選擇打開一個(gè)新的連接然后不使用SSL進(jìn)行連接。

一個(gè)初始化的 SSLRequest 也可以用于打開來用于發(fā)送一條 CancelRequest 消息的聯(lián)接中。

如果協(xié)議本身并未提供某種方法強(qiáng)制SSL加密,那么管理員可以把服務(wù)器配置為拒絕未加密的會話,這是認(rèn)證檢查的一個(gè)副產(chǎn)品。

52.2.10. GSSAPI 會話加密

如果PostgreSQL是使用GSSAPI支持構(gòu)建的,則可以使用GSSAPI加密前端/后端之間的通信。 這為攻擊者可能捕獲會話流量的環(huán)境中提供了通信安全性。有關(guān)使用GSSAPI加密PostgreSQL會話的詳細(xì)信息,請參閱 第 18.10 節(jié)。

要啟動GSSAPI加密連接,前端最初會發(fā)送 GSSENCRequest消息,而不是StartupMessage。 然后,服務(wù)器使用包含GN的單個(gè)字節(jié)進(jìn)行響應(yīng),分別表示愿意或不愿意執(zhí)行GSSAPI加密。 如果前端對響應(yīng)不滿意,則此時(shí)可能會關(guān)閉連接。 要在G之后繼續(xù),使用 RFC2744 或等效部分討論的 GSSAPI C 綁定,通過在循環(huán)中調(diào)用 gss_init_sec_context()并將結(jié)果發(fā)送到服務(wù)器執(zhí)行GSSAPI初始化,從空輸入開始然后從服務(wù)器返回每個(gè)結(jié)果,直到返回?zé)o輸出。 在向服務(wù)器發(fā)送gss_init_sec_context()的結(jié)果時(shí),按照網(wǎng)絡(luò)字節(jié)順序,將消息的長度預(yù)置為四個(gè)字節(jié)的整數(shù)。 如果成功,則使用 gss_wrap()加密通常的 StartupMessage 和所有后續(xù)數(shù)據(jù),將 gss_wrap()的結(jié)果長度作為網(wǎng)絡(luò)字節(jié)順序中的四個(gè)字節(jié)整數(shù)預(yù)置到實(shí)際加密的有效負(fù)載。 請注意,服務(wù)器僅接受來自小于 16kB 的客戶端的加密數(shù)據(jù)包;gss_wrap_size_limit()應(yīng)由客戶端用于確定將適合此限制的未加密消息的大小,并且較大的消息將分解為多個(gè) gss_wrap()調(diào)用。 典型段為8kB未加密數(shù)據(jù),導(dǎo)致加密數(shù)據(jù)包略大于 8kB,但遠(yuǎn)低于 16kB 最大值。 服務(wù)器可以預(yù)期不會向客戶端發(fā)送大于 16kB 的加密數(shù)據(jù)包。 要在N之后繼續(xù),請發(fā)送常用的StartupMessage,然后繼續(xù)而不進(jìn)行加密。

前端還應(yīng)準(zhǔn)備好處理來自服務(wù)器的 GSSENCRequest的ErrorMessage響應(yīng)。 僅在服務(wù)器將GSSAPI加密支持添加到PostgreSQL之前,才會發(fā)生這種情況。 在這種情況下連接必須關(guān)閉,但前端可能會選擇打開新的連接,并在不請求 GSSAPI加密的情況下繼續(xù)。 考慮到前面指定的長度限制,ErrorMessage不能與來自服務(wù)器的具有適當(dāng)長度的正常響應(yīng)相混淆。

初始 GSSENCRequest也可用于正在打開的連接中發(fā)送CancelRequest消息。

雖然協(xié)議本身不為服務(wù)器提供強(qiáng)制GSSAPI加密的方法,但管理員可以將服務(wù)器配置為拒絕未加密的會話,作為身份驗(yàn)證檢查的副產(chǎn)品。


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號