?
本文檔使用 php中文網(wǎng)手冊 發(fā)布
libpq事件系統(tǒng)用于通知對libpq事件感興趣的注冊事件處理過程, 如創(chuàng)建或刪除PGconn和PGresult對象。 一個主要的使用原因是,它允許應(yīng)用程序通過一個PGconn或PGresult關(guān)聯(lián)它們自己的數(shù)據(jù), 并且確保數(shù)據(jù)在適當(dāng)?shù)臅r候釋放。
每個注冊的事件處理程序與兩片數(shù)據(jù)相關(guān)聯(lián),已知的libpq只作為不透明的void *指針。
當(dāng)事件處理程序注冊帶有PGconn時,應(yīng)用程序會提供一個passthrough指針。
傳遞指針在由它產(chǎn)生的PGconn和所有的PGresult的生命周期中永遠不會改變,
它指向生命周期長的數(shù)據(jù)。除此之外,還有一個instance data指針,它從
NULL每個PGconn中的NULL和PGresult開始。
這個指針可以與PQinstanceData
,PQsetInstanceData
,PQresultInstanceData
和
PQsetResultInstanceData
函數(shù)一起使用。需要注意的是不同于傳遞指針,一個PGconn的實例數(shù)據(jù)不會被由它產(chǎn)生的
PGresult自動繼承。libpq不知道傳遞和實例數(shù)據(jù)指針指向的是什么,并且
不會嘗試去釋放它們;這對事件處理程序是一種保證。
枚舉PGEventId命名事件系統(tǒng)處理的事件的類型。 所有的命名值都是從PGEVT開始。對每個事件類型來說, 有一個相關(guān)的事件信息結(jié)構(gòu),用于傳送傳遞給事件處理程序的參數(shù)。事件類型如下:
當(dāng)調(diào)用PQregisterEventProc
時,會發(fā)生注冊的事件。
這是一個理想化的時間,用于初始化任意instanceData,可能需要一個事件過程。
每次連接中的每個事件處理程序只會觸發(fā)一個注冊了的事件。
如果事件過程失敗,會終止注冊。
typedef struct { PGconn *conn; } PGEventRegister;
當(dāng)接收到PGEVT_REGISTER時,evtInfo指針
應(yīng)該被轉(zhuǎn)換為一個PGEventRegister *。這個結(jié)構(gòu)包含了一個
CONNECTION_OK狀態(tài)信息;用以保證在獲得一個好的PGconn之后立即
請求調(diào)用PQregisterEventProc
。
當(dāng)返回一個錯誤代碼時,必須執(zhí)行所有的清理,因為沒有PGEVT_CONNDESTROY會被發(fā)送。
PQreset
或PQresetPoll
函數(shù)完成時,觸發(fā)連接復(fù)位事件。
在這兩種情況下,只有重置成功時才會觸發(fā)事件。
如果事件過程失敗,整個連接復(fù)位都會失??;PGconn被置為CONNECTION_BAD狀態(tài)
并且PQresetPoll
將返回PGRES_POLLING_FAILED。
typedef struct { PGconn *conn; } PGEventConnReset;
當(dāng)接收到一個PGEVT_CONNRESET事件時,evtInfo指針 應(yīng)該被轉(zhuǎn)換為一個PGEventConnReset *。 盡管包含的PGconn備重置了,但所有事件數(shù)據(jù)不會改變。 這個事件應(yīng)該用于reset/reload/requery任何關(guān)聯(lián)的instanceData。需要注意的是 即使事件過程在處理PGEVT_CONNRESET時失敗了,當(dāng)連接關(guān)閉時,仍會接收一個PGEVT_CONNDESTROY事件。
在響應(yīng)PQfinish
時會觸發(fā)連接破壞事件。
這是事件過程的職責(zé):合適的清理它的事件數(shù)據(jù),因為libpq沒有能力管理這部分內(nèi)存。
失敗的清理會導(dǎo)致內(nèi)存溢出。
typedef struct { PGconn *conn; } PGEventConnDestroy;
當(dāng)接收到一個PGEVT_CONNDESTROY事件時,evtInfo指針
應(yīng)該被轉(zhuǎn)換為一個PGEventConnDestroy *。
在PQfinish
執(zhí)行清理之前會觸發(fā)該事件。
事件過程的返回值會被忽略,因為沒有很好的方式從PQfinish
指出失敗。
同樣,一個事件過程失敗不應(yīng)該中止清理不需要的內(nèi)存的過程。
回應(yīng)任意產(chǎn)生一個結(jié)果(包括PQgetResult
)的查詢執(zhí)行函數(shù)時,會觸發(fā)結(jié)果創(chuàng)建事件。
這個事件只有在成功創(chuàng)建結(jié)果時才會被觸發(fā)。
typedef struct { PGconn *conn; PGresult *result; } PGEventResultCreate;
當(dāng)接收到一個PGEVT_RESULTCREATE事件時,evtInfo指針
應(yīng)該被轉(zhuǎn)換為一個PGEventResultCreate *。
conn是用于產(chǎn)生結(jié)果的連接。
這是理想的位置,用于初始化任意需要與結(jié)果相關(guān)聯(lián)的instanceData。
如果事件過程失敗,事件過程不應(yīng)該嘗試自己PQclear
結(jié)果對象。
當(dāng)返回一個錯誤代碼時,必須執(zhí)行所有的清理,因為沒有PGEVT_RESULTDESTROY會被發(fā)送。
在響應(yīng)PQcopyResult
時會觸發(fā)結(jié)果拷貝事件。
只有在拷貝完成時,才會觸發(fā)該事件。只有那些為源結(jié)果成功處理PGEVT_RESULTCREATE或PGEVT_RESULTCOPY事件
的時間過程才會收到PGEVT_RESULTCOPY事件。
typedef struct { const PGresult *src; PGresult *dest; } PGEventResultCopy;
當(dāng)接收到一個PGEVT_RESULTCOPY事件時,evtInfo指針 應(yīng)該被轉(zhuǎn)換為一個PGEventResultCopy *。 src結(jié)果是當(dāng)dest為拷貝目標(biāo)時要進行拷貝的。 這個事件用于提供一個instanceData的深度拷貝,因為PQcopyResult做不到。 如果事件過程失敗,整個拷貝過程都將失敗,并且dest結(jié)果也會被清理。 當(dāng)返回一個錯誤代碼時,必須執(zhí)行所有的清理,因為沒有PGEVT_RESULTDESTROY會被發(fā)送。
在回應(yīng)PQclear
時會觸發(fā)結(jié)果破壞事件。
這是事件過程的責(zé)任;合理清理它的事件數(shù)據(jù),因為libpq沒有能力管理這塊內(nèi)存。
清理失敗會導(dǎo)致內(nèi)存溢出。
typedef struct { PGresult *result; } PGEventResultDestroy;
當(dāng)接收到一個PGEVT_RESULTDESTROY事件時,evtInfo指針
應(yīng)該被轉(zhuǎn)換為一個PGEventResultDestroy *。
這個時間會在PQclear
執(zhí)行清理之前被觸發(fā)。
事件過程的返回結(jié)果會被忽略,因為沒有一個方式能夠從PQclear
指出失敗。
同樣,一個事件過程失敗不應(yīng)該中止對不需要內(nèi)存的清理。
PGEventProc是一個事件過程中指針的typedef, 也是,從libpq接收事件的用戶回調(diào)函數(shù)。 事件過程的用法必須如下:
int eventproc(PGEventId evtId,void *evtInfo,void *passThrough)
evtId參數(shù)指出要發(fā)生哪個PGEVT事件。 evtInfo指針必須被轉(zhuǎn)換為合適的結(jié)構(gòu)類型以獲取有關(guān)該事件的進一步信息。 passThrough是當(dāng)事件過程被注冊時,提供給passThrough的指針。 這個函數(shù)應(yīng)該返回一個非0的值,如果成功的話,反之,返回0。
在任意PGconn中,只有一次可以注冊一個特殊的事件過程。 這是因為過程地址被用于作為查詢關(guān)鍵字,以識別相關(guān)的實例數(shù)據(jù)。
Caution |
在Windows上,函數(shù)可以有兩個不同的地址:一個是內(nèi)部DLL可見的,另一個是外部DLL可見的。 需要注意的是,libpq事件過程函數(shù)只會使用其中一個地址,否則會造成混亂。 有效的編寫代碼的簡單規(guī)則是為了保證static聲明的事件過程。 如果過程地址在它的源文件之外是可用的,公開一個單獨的函數(shù)以返回地址。 |
PQregisterEventProc
libpq的注冊事件回調(diào)過程。
int PQregisterEventProc(PGconn *conn,PGEventProc proc, const char *name,void *passThrough);
在每個PGconn中必須注冊一次事件過程,用于希望接受到的事件。 除了內(nèi)存之外,對通一次連接注冊的事件過程個數(shù)沒有限制。 如果成功,則返回一個非0的值,否則返回0。
當(dāng)一個libpq事件被觸發(fā)時,會調(diào)用一個proc參數(shù)。 內(nèi)存地址同樣會被用于查找instanceData。name用于 指出在錯誤信息中的事件過程。這個值不能為NULL,或一個長度為0的字符串。 名字字符串被拷貝到PGconn中,因此被傳遞的不需要擁有很長的生命周期。 passThrough指針被傳遞到proc,無論觸發(fā)事件是何時。 這個參數(shù)可以是NULL。
PQsetInstanceData
為proc到data的過程設(shè)置conn'sinstanceData連接。 成功則返回一個非0值,否則返回0。 只有proc沒有成功在conn注冊時才會發(fā)生失敗。
int PQsetInstanceData(PGconn *conn,PGEventProc proc,void *data);
PQinstanceData
返回與proc過程,或NULL(如果存在空)相關(guān)的conn的instanceData。
void *PQinstanceData(const PGconn *conn,PGEventProc proc);
PQresultSetInstanceData
為proc到data的過程設(shè)置結(jié)果的instanceData。 成功則返回一個非0值,否則返回0。 只有proc沒有成功在結(jié)果注冊時才會發(fā)生失敗。
int PQresultSetInstanceData(PGresult *res,PGEventProc proc,void *data);
PQresultInstanceData
返回與proc過程,或NULL(如果存在空)相關(guān)的結(jié)果的instanceData。
void *PQresultInstanceData(const PGresult *res,PGEventProc proc);
一個管理與libpq連接和結(jié)果相關(guān)的私有數(shù)據(jù)的例子:
/* 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"); if (PQstatus(conn) != CONNECTION_OK) { fprintf(stderr,"Connection to database failed: %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) */ PQsetResultInstanceData(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) */ PQsetResultInstanceData(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 */ }