libpq 的事件系統旨在通知已註冊的事件處理常式關於感興趣的 libpq 事件,例如 PGconn
和 PGresult
物件的建立或銷毀。一個主要的用例是,這允許應用程式將它們自己的資料與 PGconn
或 PGresult
相關聯,並確保該資料在適當的時間被釋放。
每個已註冊的事件處理常式都與兩段資料相關聯,libpq 僅將它們視為不透明的 void *
指標。有一個傳遞 (pass-through) 指標,當事件處理常式向 PGconn
註冊時,由應用程式提供。對於 PGconn
及其產生的所有 PGresult
的生命週期而言,傳遞指標永遠不會改變;所以如果使用,它必須指向長存活的資料。此外,還有一個實例資料 (instance data) 指標,它在每個 PGconn
和 PGresult
中都以 NULL
開始。這個指標可以使用 PQinstanceData
、PQsetInstanceData
、PQresultInstanceData
和 PQresultSetInstanceData
函式來操作。請注意,與傳遞指標不同,PGconn
的實例資料不會自動被從它建立的 PGresult
繼承。libpq 不知道傳遞和實例資料指標指向什麼(如果有的話),並且永遠不會嘗試釋放它們 - 這是事件處理常式的責任。
列舉 PGEventId
命名事件系統處理的事件類型。它所有的值都以 PGEVT
開頭。對於每種事件類型,都有一個相應的事件資訊結構,攜帶傳遞給事件處理常式的參數。事件類型為
PGEVT_REGISTER
#當呼叫 PQregisterEventProc
時,會發生註冊事件。這是初始化事件程序可能需要的任何 instanceData
的理想時機。每個連線中,每個事件處理常式只會觸發一個註冊事件。如果事件程序失敗(傳回零),則註冊會被取消。
typedef struct { PGconn *conn; } PGEventRegister;
當收到 PGEVT_REGISTER
事件時,evtInfo
指標應被轉換為 PGEventRegister *
。此結構包含一個 PGconn
,它應處於 CONNECTION_OK
狀態;如果在取得良好的 PGconn
後立即呼叫 PQregisterEventProc
,則可保證。當傳回失敗代碼時,必須執行所有清理,因為不會發送 PGEVT_CONNDESTROY
事件。
PGEVT_CONNRESET
#連線重置事件在 PQreset
或 PQresetPoll
完成時觸發。在這兩種情況下,只有在重置成功時才會觸發該事件。事件程序的傳回值在 PostgreSQL v15 及更高版本中被忽略。但是,在較早版本中,傳回成功(非零)很重要,否則連線將被中止。
typedef struct { PGconn *conn; } PGEventConnReset;
當收到 PGEVT_CONNRESET
事件時,evtInfo
指標應被轉換為 PGEventConnReset *
。雖然包含的 PGconn
剛剛被重置,但所有事件資料保持不變。此事件應用於重置/重新載入/重新查詢任何相關的 instanceData
。請注意,即使事件程序未能處理 PGEVT_CONNRESET
,當連線關閉時,它仍將收到 PGEVT_CONNDESTROY
事件。
PGEVT_CONNDESTROY
#連接銷毀事件會在回應 PQfinish
時觸發。事件程序的職責是正確地清理其事件資料,因為 libpq 無法管理此記憶體。未能清理將導致記憶體洩漏。
typedef struct { PGconn *conn; } PGEventConnDestroy;
當收到 PGEVT_CONNDESTROY
事件時,evtInfo
指標應轉換為 PGEventConnDestroy *
。此事件會在 PQfinish
執行任何其他清理之前觸發。事件程序的傳回值會被忽略,因為無法從 PQfinish
指出失敗。此外,事件程序失敗不應中止清理不需要的記憶體的過程。
PGEVT_RESULTCREATE
#結果建立事件會在回應任何產生結果的查詢執行函數時觸發,包括 PQgetResult
。此事件只會在結果已成功建立後觸發。
typedef struct { PGconn *conn; PGresult *result; } PGEventResultCreate;
當收到 PGEVT_RESULTCREATE
事件時,evtInfo
指標應轉換為 PGEventResultCreate *
。conn
是用於產生結果的連線。這是初始化任何需要與結果相關聯的 instanceData
的理想位置。如果事件程序失敗 (傳回零),則該事件程序將在結果的剩餘生命週期內被忽略;也就是說,它將不會收到此結果或從其複製的結果的 PGEVT_RESULTCOPY
或 PGEVT_RESULTDESTROY
事件。
PGEVT_RESULTCOPY
#結果複製事件會在回應 PQcopyResult
時觸發。此事件只會在複製完成後觸發。只有成功處理了來源結果的 PGEVT_RESULTCREATE
或 PGEVT_RESULTCOPY
事件的事件程序才會收到 PGEVT_RESULTCOPY
事件。
typedef struct { const PGresult *src; PGresult *dest; } PGEventResultCopy;
當收到 PGEVT_RESULTCOPY
事件時,evtInfo
指標應轉換為 PGEventResultCopy *
。src
結果是被複製的內容,而 dest
結果是複製目標。此事件可用於提供 instanceData
的深層複製,因為 PQcopyResult
無法做到這一點。如果事件程序失敗 (傳回零),則該事件程序將在新結果的剩餘生命週期內被忽略;也就是說,它將不會收到該結果或從其複製的結果的 PGEVT_RESULTCOPY
或 PGEVT_RESULTDESTROY
事件。
PGEVT_RESULTDESTROY
#結果銷毀事件會在回應 PQclear
時觸發。事件程序的職責是正確地清理其事件資料,因為 libpq 無法管理此記憶體。未能清理將導致記憶體洩漏。
typedef struct { PGresult *result; } PGEventResultDestroy;
當收到 PGEVT_RESULTDESTROY
事件時,evtInfo
指標應轉換為 PGEventResultDestroy *
。此事件會在 PQclear
執行任何其他清理之前觸發。事件程序的傳回值會被忽略,因為無法從 PQclear
指出失敗。此外,事件程序失敗不應中止清理不需要的記憶體的過程。
PGEventProc
#PGEventProc
是一個 typedef,指向事件程序,也就是接收來自 libpq 的事件的使用者回呼函數。事件程序的簽章必須是
int eventproc(PGEventId evtId, void *evtInfo, void *passThrough)
evtId
參數表示發生了哪個 PGEVT
事件。evtInfo
指標必須轉換為適當的結構類型,以取得有關事件的更多資訊。passThrough
參數是註冊事件程序時提供給 PQregisterEventProc
的指標。如果函數成功,應傳回非零值;如果失敗,則傳回零。
特定的事件程序在任何 PGconn
中只能註冊一次。這是因為程序的位址用作查找金鑰來識別相關的實例資料。
在 Windows 上,函數可以有兩個不同的位址:一個從 DLL 外部可見,另一個從 DLL 內部可見。應注意僅將其中一個位址與 libpq 的事件程序函數一起使用,否則會導致混淆。編寫可運作程式碼的最簡單規則是確保事件程序宣告為 static
。如果程序的位址必須在其自己的原始檔之外可用,則公開一個單獨的函數來傳回該位址。
PQregisterEventProc
#向 libpq 註冊事件回呼程序。
int PQregisterEventProc(PGconn *conn, PGEventProc proc, const char *name, void *passThrough);
您必須在想要接收事件的每個 PGconn
上註冊一次事件程序。除了記憶體之外,可以向連線註冊的事件程序數量沒有限制。如果函數成功,則傳回非零值;如果失敗,則傳回零。
當觸發 libpq 事件時,將呼叫 proc
參數。它的記憶體位址也用於查找 instanceData
。name
參數用於在錯誤訊息中引用事件程序。此值不能為 NULL
或零長度字串。名稱字串被複製到 PGconn
中,因此傳遞的內容不必是長期的。passThrough
指標會在事件發生時傳遞給 proc
。此參數可以為 NULL
。
PQsetInstanceData
#將程序 proc
的 instanceData
設定給連線 conn
。成功時回傳非零值,失敗時回傳零。(只有在 proc
未在 conn
中正確註冊時才會失敗。)
int PQsetInstanceData(PGconn *conn, PGEventProc proc, void *data);
PQinstanceData
#傳回與程序 proc
相關聯的連線 conn
的 instanceData
,如果沒有,則傳回 NULL
。
void *PQinstanceData(const PGconn *conn, PGEventProc proc);
PQresultSetInstanceData
#將 proc
的結果 instanceData
設定為 data
。成功時回傳非零值,失敗時回傳零。(只有在 proc
未在結果中正確註冊時才會失敗。)
int PQresultSetInstanceData(PGresult *res, PGEventProc proc, void *data);
請注意,data
所代表的任何儲存空間都不會被 PQresultMemorySize
計算在內,除非它是使用 PQresultAlloc
分配的。(建議這樣做,因為它消除了在結果被銷毀時明確釋放這些儲存空間的需求。)
PQresultInstanceData
#傳回與 proc
相關聯的結果 instanceData
,如果沒有,則傳回 NULL
。
void *PQresultInstanceData(const PGresult *res, PGEventProc proc);
以下是一個管理與 libpq 連線和結果相關聯的私有資料的骨架範例。
/* required header for libpq events (note: includes libpq-fe.h) */ #include <libpq-events.h> /* The instanceData */ typedef struct { int n; char *str; } mydata; /* PGEventProc */ static int myEventProc(PGEventId evtId, void *evtInfo, void *passThrough); int main(void) { mydata *data; PGresult *res; PGconn *conn = PQconnectdb("dbname=postgres options=-csearch_path="); if (PQstatus(conn) != CONNECTION_OK) { /* PQerrorMessage's result includes a trailing newline */ fprintf(stderr, "%s", PQerrorMessage(conn)); PQfinish(conn); return 1; } /* called once on any connection that should receive events. * Sends a PGEVT_REGISTER to myEventProc. */ if (!PQregisterEventProc(conn, myEventProc, "mydata_proc", NULL)) { fprintf(stderr, "Cannot register PGEventProc\n"); PQfinish(conn); return 1; } /* conn instanceData is available */ data = PQinstanceData(conn, myEventProc); /* Sends a PGEVT_RESULTCREATE to myEventProc */ res = PQexec(conn, "SELECT 1 + 1"); /* result instanceData is available */ data = PQresultInstanceData(res, myEventProc); /* If PG_COPYRES_EVENTS is used, sends a PGEVT_RESULTCOPY to myEventProc */ res_copy = PQcopyResult(res, PG_COPYRES_TUPLES | PG_COPYRES_EVENTS); /* result instanceData is available if PG_COPYRES_EVENTS was * used during the PQcopyResult call. */ data = PQresultInstanceData(res_copy, myEventProc); /* Both clears send a PGEVT_RESULTDESTROY to myEventProc */ PQclear(res); PQclear(res_copy); /* Sends a PGEVT_CONNDESTROY to myEventProc */ PQfinish(conn); return 0; } static int myEventProc(PGEventId evtId, void *evtInfo, void *passThrough) { switch (evtId) { case PGEVT_REGISTER: { PGEventRegister *e = (PGEventRegister *)evtInfo; mydata *data = get_mydata(e->conn); /* associate app specific data with connection */ PQsetInstanceData(e->conn, myEventProc, data); break; } case PGEVT_CONNRESET: { PGEventConnReset *e = (PGEventConnReset *)evtInfo; mydata *data = PQinstanceData(e->conn, myEventProc); if (data) memset(data, 0, sizeof(mydata)); break; } case PGEVT_CONNDESTROY: { PGEventConnDestroy *e = (PGEventConnDestroy *)evtInfo; mydata *data = PQinstanceData(e->conn, myEventProc); /* free instance data because the conn is being destroyed */ if (data) free_mydata(data); break; } case PGEVT_RESULTCREATE: { PGEventResultCreate *e = (PGEventResultCreate *)evtInfo; mydata *conn_data = PQinstanceData(e->conn, myEventProc); mydata *res_data = dup_mydata(conn_data); /* associate app specific data with result (copy it from conn) */ PQresultSetInstanceData(e->result, myEventProc, res_data); break; } case PGEVT_RESULTCOPY: { PGEventResultCopy *e = (PGEventResultCopy *)evtInfo; mydata *src_data = PQresultInstanceData(e->src, myEventProc); mydata *dest_data = dup_mydata(src_data); /* associate app specific data with result (copy it from a result) */ PQresultSetInstanceData(e->dest, myEventProc, dest_data); break; } case PGEVT_RESULTDESTROY: { PGEventResultDestroy *e = (PGEventResultDestroy *)evtInfo; mydata *data = PQresultInstanceData(e->result, myEventProc); /* free instance data because the result is being destroyed */ if (data) free_mydata(data); break; } /* unknown event ID, just return true. */ default: break; } return true; /* event processing succeeded */ }
如果您在文件中看到任何不正確、與您使用特定功能的體驗不符或需要進一步澄清的內容,請使用此表格回報文件問題。