libpq 的事件系統旨在通知已註冊的事件處理程式有關 libpq 的重要事件,例如 PGconn 和 PGresult 物件的建立或銷燬。主要用例是,這允許應用程式將自己的資料與 PGconn 或 PGresult 關聯起來,並確保在適當的時間釋放這些資料。
每個已註冊的事件處理程式都與兩部分資料相關聯,在 libpq 中僅作為不透明的 void * 指標。有一個“傳遞”(pass-through)指標,在將事件處理程式註冊到 PGconn 時由應用程式提供。此傳遞指標在 PGconn 的整個生命週期及其生成的所有 PGresult 中都不會改變;因此,如果使用,它必須指向長期存在的資料。此外,還有一個“例項資料”(instance data)指標,在每個 PGconn 和 PGresult 中初始值為 NULL。此指標可以使用 PQinstanceData、PQsetInstanceData、PQresultInstanceData 和 PQresultSetInstanceData 函式進行操作。請注意,與傳遞指標不同,由 PGconn 建立的 PGresult 不會自動繼承 PGconn 的例項資料。 libpq 不知道傳遞指標和例項資料指標(如果它們指向任何東西)指向何處,也不會嘗試釋放它們——這由事件處理程式負責。
列舉 PGEventId 命名了事件系統處理的事件型別。它的所有值都以 PGEVT 開頭。對於每種事件型別,都有一個相應的事件資訊結構,它攜帶傳遞給事件處理程式的引數。事件型別是:
PGEVT_REGISTER #當呼叫 PQregisterEventProc 時會發生註冊事件。這是初始化事件過程可能需要的任何 instanceData 的理想時機。每個連線每個事件處理程式只觸發一次註冊事件。如果事件過程失敗(返回零),則註冊將被取消。
typedef struct
{
PGconn *conn;
} PGEventRegister;
收到 PGEVT_REGISTER 事件時,應將 evtInfo 指標強制轉換為 PGEventRegister *。此結構包含一個 PGconn,它應該處於 CONNECTION_OK 狀態;如果呼叫 PQregisterEventProc 緊接著獲得一個有效的 PGconn,則可以保證這一點。返回失敗程式碼時,必須執行所有清理工作,因為不會發送 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 都必須註冊一次事件過程。可以註冊到連線的事件過程數量沒有限制,除了記憶體限制。如果成功,函式將返回一個非零值,如果失敗,則返回零。
proc 引數將在 libpq 事件觸發時被呼叫。其記憶體地址也用於查詢 instanceData。name 引數用於在錯誤訊息中引用事件過程。此值不能為空 NULL 或零長度字串。名稱字串會被複制到 PGconn 中,因此傳遞的值不必是長期存在的。passThrough 指標在事件發生時會傳遞給 proc。此引數可以是 NULL。
PQsetInstanceData #將連線 conn 的 instanceData 對於過程 proc 設定為 data。此函式返回非零表示成功,零表示失敗。(只有當 proc 未能在 conn 中正確註冊時才可能失敗。)
int PQsetInstanceData(PGconn *conn, PGEventProc proc, void *data);
PQinstanceData #返回連線 conn 的與過程 proc 關聯的 instanceData,如果沒有則返回 NULL。
void *PQinstanceData(const PGconn *conn, PGEventProc proc);
PQresultSetInstanceData #將結果的 instanceData 對於 proc 設定為 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 */
}
如果您在文件中發現任何不正確之處、與您對特定功能的實際體驗不符或需要進一步澄清的地方,請使用 此表格 報告文件問題。