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