支援的版本:目前 (17) / 16 / 15 / 14 / 13
開發版本:devel
不支援的版本:12 / 11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1 / 9.0 / 8.4

32.14. 事件系統 #

libpq 的事件系統旨在通知已註冊的事件處理器關於有趣的 libpq 事件,例如建立或銷毀 PGconnPGresult 物件。 主要用途是允許應用程式將它們自己的資料與 PGconnPGresult 相關聯,並確保該資料在適當的時間被釋放。

每個已註冊的事件處理器都與兩個資料相關聯,libpq 僅將其視為不透明的 void * 指標。 有一個穿透(pass-through) 指標,該指標在事件處理器向 PGconn 註冊時由應用程式提供。 穿透指標在 PGconn 及其產生之所有 PGresult 的生命週期內永遠不會改變;因此,如果使用它,則必須指向長時間存在的資料。 此外,還有一個實例資料(instance data)指標,在每個 PGconnPGresult 中都以 NULL 開始。 可以使用 PQinstanceDataPQsetInstanceDataPQresultInstanceDataPQresultSetInstanceData 函式來操作此指標。 請注意,與穿透指標不同,PGconn 的實例資料不會自動由從其建立的 PGresult 繼承。libpq 不知道穿透指標和實例資料指標指向什麼(如果有的話),並且永遠不會嘗試釋放它們 — 這是事件處理器的責任。

32.14.1. 事件類型 #

列舉 PGEventId 命名了事件系統處理的事件類型。 所有值都以 PGEVT 開頭。 對於每種事件類型,都有一個對應的事件資訊結構,用於攜帶傳遞給事件處理器的參數。 事件類型為

PGEVT_REGISTER #

當呼叫 PQregisterEventProc 時,會發生註冊事件。 這是初始化事件程序可能需要的任何 instanceData 的理想時機。 每個連線的每個事件處理器只會觸發一個註冊事件。 如果事件程序失敗(傳回零),則註冊將被取消。

typedef struct
{
    PGconn *conn;
} PGEventRegister;

收到 PGEVT_REGISTER 事件時,應將 evtInfo 指標強制轉換為 PGEventRegister *。 此結構包含一個 PGconn,該 PGconn 應處於 CONNECTION_OK 狀態;如果在獲得良好的 PGconn 之後立即呼叫 PQregisterEventProc,則保證可以處於該狀態。 傳回失敗程式碼時,必須執行所有清理,因為不會傳送任何 PGEVT_CONNDESTROY 事件。

PGEVT_CONNRESET #

連線重置事件在 PQresetPQresetPoll 完成時觸發。 在這兩種情況下,僅當重置成功時才會觸發該事件。 在 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_RESULTCOPYPGEVT_RESULTDESTROY 事件。

PGEVT_RESULTCOPY #

結果複製事件會在回應 PQcopyResult 時觸發。此事件只會在複製完成後觸發。只有成功處理了來源結果的 PGEVT_RESULTCREATEPGEVT_RESULTCOPY 事件的事件程序才會收到 PGEVT_RESULTCOPY 事件。

typedef struct
{
    const PGresult *src;
    PGresult *dest;
} PGEventResultCopy;

當收到 PGEVT_RESULTCOPY 事件時,evtInfo 指標應轉換為 PGEventResultCopy * 型別。src 結果是被複製的內容,而 dest 結果是複製目標。此事件可用於提供 instanceData 的深層複製,因為 PQcopyResult 無法做到這一點。如果事件程序失敗(傳回零),則該事件程序將在新結果的剩餘生命週期內被忽略;也就是說,它將不會收到該結果或從其複製的結果的 PGEVT_RESULTCOPYPGEVT_RESULTDESTROY 事件。

PGEVT_RESULTDESTROY #

結果銷毀事件會在回應 PQclear 時觸發。事件程序有責任正確清理其事件資料,因為 libpq 無法管理此記憶體。未能清理將導致記憶體洩漏。

typedef struct
{
    PGresult *result;
} PGEventResultDestroy;

當收到 PGEVT_RESULTDESTROY 事件時,evtInfo 指標應轉換為 PGEventResultDestroy * 型別。此事件會在 PQclear 執行任何其他清理之前觸發。事件程序的傳回值會被忽略,因為無法從 PQclear 指出失敗。此外,事件程序失敗不應中止清理不需要的記憶體的過程。

32.14.2. 事件回呼程序 #

PGEventProc #

PGEventProc 是一個指向事件程序的指標的 typedef,也就是接收來自 libpq 的事件的使用者回呼函式。事件程序的簽章必須是

int eventproc(PGEventId evtId, void *evtInfo, void *passThrough)

evtId 參數指示發生了哪個 PGEVT 事件。evtInfo 指標必須轉換為適當的結構類型,以取得有關該事件的更多資訊。passThrough 參數是註冊事件程序時提供給 PQregisterEventProc 的指標。如果函式成功,則應傳回一個非零值,如果失敗,則傳回零。

一個特定的事件程序在任何 PGconn 中只能註冊一次。這是因為該程序的位址被用作查找金鑰,以識別相關的實例資料。

注意

在 Windows 上,函式可以有兩個不同的位址:一個從 DLL 外部可見,另一個從 DLL 內部可見。應注意,只有其中一個位址與 libpq 的事件程序函式一起使用,否則會導致混淆。編寫能夠運作的程式碼的最簡單規則是確保事件程序宣告為 static。如果程序的位址必須在其自己的原始檔之外可用,則公開一個單獨的函式來傳回該位址。

32.14.3. 事件支援函式 #

PQregisterEventProc #

向 libpq 註冊事件回呼程序。

int PQregisterEventProc(PGconn *conn, PGEventProc proc,
                        const char *name, void *passThrough);

必須在您希望接收事件的每個 PGconn 上註冊一次事件程序。可以向連線註冊的事件程序的數量沒有限制,除了記憶體之外。如果函式成功,則傳回一個非零值,如果失敗,則傳回零。

當觸發 libpq 事件時,將呼叫 proc 引數。它的記憶體位址也用於查找 instanceDataname 引數用於在錯誤訊息中引用事件程序。此值不能為 NULL 或零長度字串。名稱字串被複製到 PGconn 中,因此傳遞的內容不必是長期存在的。passThrough 指標在發生事件時傳遞給 proc。此引數可以為 NULL

PQsetInstanceData #

將連線 conn 的程序 procinstanceData 設為 data。如果成功,則傳回非零值,如果失敗,則傳回零。(只有當 proc 未在 conn 中正確註冊時才可能失敗。)

int PQsetInstanceData(PGconn *conn, PGEventProc proc, void *data);
PQinstanceData #

傳回與程序 proc 相關聯的連線 conninstanceData,如果沒有,則傳回 NULL

void *PQinstanceData(const PGconn *conn, PGEventProc proc);
PQresultSetInstanceData #

將結果的 instanceData 設定為 procdata。成功時傳回非零值,失敗時傳回零。(只有在結果中未正確註冊 proc 時才會失敗。)

int PQresultSetInstanceData(PGresult *res, PGEventProc proc, void *data);

請注意,由 data 表示的任何儲存空間都不會被 PQresultMemorySize 計算在內,除非它使用 PQresultAlloc 進行分配。(建議這樣做,因為它消除了在銷毀結果時顯式釋放此類儲存空間的需求。)

PQresultInstanceData #

傳回與 proc 相關聯的結果的 instanceData,如果沒有,則傳回 NULL

void *PQresultInstanceData(const PGresult *res, PGEventProc proc);

32.14.4. 事件範例 #

以下是一個管理與 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 */
}

提交更正

如果您在文件中看到任何不正確、與您特定功能的經驗不符或需要進一步澄清的地方,請使用此表格回報文件問題。