Movatterモバイル変換


[0]ホーム

URL:



Facebook
Postgres Pro
Facebook
Downloads
33.14. Event System
Prev UpChapter 33. libpq — C LibraryHome Next

33.14. Event System#

libpq's event system is designed to notify registered event handlers about interestinglibpq events, such as the creation or destruction ofPGconn andPGresult objects. A principal use case is that this allows applications to associate their own data with aPGconn orPGresult and ensure that that data is freed at an appropriate time.

Each registered event handler is associated with two pieces of data, known tolibpq only as opaquevoid * pointers. There is apass-through pointer that is provided by the application when the event handler is registered with aPGconn. The pass-through pointer never changes for the life of thePGconn and allPGresults generated from it; so if used, it must point to long-lived data. In addition there is aninstance data pointer, which starts outNULL in everyPGconn andPGresult. This pointer can be manipulated using thePQinstanceData,PQsetInstanceData,PQresultInstanceData andPQresultSetInstanceData functions. Note that unlike the pass-through pointer, instance data of aPGconn is not automatically inherited byPGresults created from it.libpq does not know what pass-through and instance data pointers point to (if anything) and will never attempt to free them — that is the responsibility of the event handler.

33.14.1. Event Types#

The enumPGEventId names the types of events handled by the event system. All its values have names beginning withPGEVT. For each event type, there is a corresponding event info structure that carries the parameters passed to the event handlers. The event types are:

PGEVT_REGISTER#

The register event occurs whenPQregisterEventProc is called. It is the ideal time to initialize anyinstanceData an event procedure may need. Only one register event will be fired per event handler per connection. If the event procedure fails (returns zero), the registration is cancelled.

typedef struct{    PGconn *conn;} PGEventRegister;

When aPGEVT_REGISTER event is received, theevtInfo pointer should be cast to aPGEventRegister *. This structure contains aPGconn that should be in theCONNECTION_OK status; guaranteed if one callsPQregisterEventProc right after obtaining a goodPGconn. When returning a failure code, all cleanup must be performed as noPGEVT_CONNDESTROY event will be sent.

PGEVT_CONNRESET#

The connection reset event is fired on completion ofPQreset orPQresetPoll. In both cases, the event is only fired if the reset was successful. The return value of the event procedure is ignored inPostgres Pro v15 and later. With earlier versions, however, it's important to return success (nonzero) or the connection will be aborted.

typedef struct{    PGconn *conn;} PGEventConnReset;

When aPGEVT_CONNRESET event is received, theevtInfo pointer should be cast to aPGEventConnReset *. Although the containedPGconn was just reset, all event data remains unchanged. This event should be used to reset/reload/requery any associatedinstanceData. Note that even if the event procedure fails to processPGEVT_CONNRESET, it will still receive aPGEVT_CONNDESTROY event when the connection is closed.

PGEVT_CONNDESTROY#

The connection destroy event is fired in response toPQfinish. It is the event procedure's responsibility to properly clean up its event data as libpq has no ability to manage this memory. Failure to clean up will lead to memory leaks.

typedef struct{    PGconn *conn;} PGEventConnDestroy;

When aPGEVT_CONNDESTROY event is received, theevtInfo pointer should be cast to aPGEventConnDestroy *. This event is fired prior toPQfinish performing any other cleanup. The return value of the event procedure is ignored since there is no way of indicating a failure fromPQfinish. Also, an event procedure failure should not abort the process of cleaning up unwanted memory.

PGEVT_RESULTCREATE#

The result creation event is fired in response to any query execution function that generates a result, includingPQgetResult. This event will only be fired after the result has been created successfully.

typedef struct{    PGconn *conn;    PGresult *result;} PGEventResultCreate;

When aPGEVT_RESULTCREATE event is received, theevtInfo pointer should be cast to aPGEventResultCreate *. Theconn is the connection used to generate the result. This is the ideal place to initialize anyinstanceData that needs to be associated with the result. If an event procedure fails (returns zero), that event procedure will be ignored for the remaining lifetime of the result; that is, it will not receivePGEVT_RESULTCOPY orPGEVT_RESULTDESTROY events for this result or results copied from it.

PGEVT_RESULTCOPY#

The result copy event is fired in response toPQcopyResult. This event will only be fired after the copy is complete. Only event procedures that have successfully handled thePGEVT_RESULTCREATE orPGEVT_RESULTCOPY event for the source result will receivePGEVT_RESULTCOPY events.

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

When aPGEVT_RESULTCOPY event is received, theevtInfo pointer should be cast to aPGEventResultCopy *. Thesrc result is what was copied while thedest result is the copy destination. This event can be used to provide a deep copy ofinstanceData, sincePQcopyResult cannot do that. If an event procedure fails (returns zero), that event procedure will be ignored for the remaining lifetime of the new result; that is, it will not receivePGEVT_RESULTCOPY orPGEVT_RESULTDESTROY events for that result or results copied from it.

PGEVT_RESULTDESTROY#

The result destroy event is fired in response to aPQclear. It is the event procedure's responsibility to properly clean up its event data as libpq has no ability to manage this memory. Failure to clean up will lead to memory leaks.

typedef struct{    PGresult *result;} PGEventResultDestroy;

When aPGEVT_RESULTDESTROY event is received, theevtInfo pointer should be cast to aPGEventResultDestroy *. This event is fired prior toPQclear performing any other cleanup. The return value of the event procedure is ignored since there is no way of indicating a failure fromPQclear. Also, an event procedure failure should not abort the process of cleaning up unwanted memory.

33.14.2. Event Callback Procedure#

PGEventProc#

PGEventProc is a typedef for a pointer to an event procedure, that is, the user callback function that receives events from libpq. The signature of an event procedure must be

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

TheevtId parameter indicates whichPGEVT event occurred. TheevtInfo pointer must be cast to the appropriate structure type to obtain further information about the event. ThepassThrough parameter is the pointer provided toPQregisterEventProc when the event procedure was registered. The function should return a non-zero value if it succeeds and zero if it fails.

A particular event procedure can be registered only once in anyPGconn. This is because the address of the procedure is used as a lookup key to identify the associated instance data.

Caution

On Windows, functions can have two different addresses: one visible from outside a DLL and another visible from inside the DLL. One should be careful that only one of these addresses is used withlibpq's event-procedure functions, else confusion will result. The simplest rule for writing code that will work is to ensure that event procedures are declaredstatic. If the procedure's address must be available outside its own source file, expose a separate function to return the address.

33.14.3. Event Support Functions#

PQregisterEventProc#

Registers an event callback procedure with libpq.

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

An event procedure must be registered once on eachPGconn you want to receive events about. There is no limit, other than memory, on the number of event procedures that can be registered with a connection. The function returns a non-zero value if it succeeds and zero if it fails.

Theproc argument will be called when a libpq event is fired. Its memory address is also used to lookupinstanceData. Thename argument is used to refer to the event procedure in error messages. This value cannot beNULL or a zero-length string. The name string is copied into thePGconn, so what is passed need not be long-lived. ThepassThrough pointer is passed to theproc whenever an event occurs. This argument can beNULL.

PQsetInstanceData#

Sets the connectionconn'sinstanceData for procedureproc todata. This returns non-zero for success and zero for failure. (Failure is only possible ifproc has not been properly registered inconn.)

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

PQinstanceData#

Returns the connectionconn'sinstanceData associated with procedureproc, orNULL if there is none.

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

PQresultSetInstanceData#

Sets the result'sinstanceData forproc todata. This returns non-zero for success and zero for failure. (Failure is only possible ifproc has not been properly registered in the result.)

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

Beware that any storage represented bydata will not be accounted for byPQresultMemorySize, unless it is allocated usingPQresultAlloc. (Doing so is recommendable because it eliminates the need to free such storage explicitly when the result is destroyed.)

PQresultInstanceData#

Returns the result'sinstanceData associated withproc, orNULL if there is none.

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

33.14.4. Event Example#

Here is a skeleton example of managing private data associated with libpq connections and results.

/* 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);intmain(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 intmyEventProc(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 */}

Prev Up Next
33.13. Notice Processing Home 33.15. Environment Variables
pdfepub
Go to Postgres Pro Standard 16
By continuing to browse this website, you agree to the use of cookies. Go toPrivacy Policy.

[8]ページ先頭

©2009-2025 Movatter.jp