mirror of
https://github.com/postgres/postgres.git
synced 2025-04-29 13:56:47 +03:00
Improve the recently-added libpq events code to provide more consistent
guarantees about whether event procedures will receive DESTROY events. They no longer need to defend themselves against getting a DESTROY without a successful prior CREATE. Andrew Chernow
This commit is contained in:
parent
7626f2a936
commit
e2b7d0c65c
@ -1,4 +1,4 @@
|
|||||||
<!-- $PostgreSQL: pgsql/doc/src/sgml/libpq.sgml,v 1.261 2008/09/17 04:31:08 tgl Exp $ -->
|
<!-- $PostgreSQL: pgsql/doc/src/sgml/libpq.sgml,v 1.262 2008/09/19 16:40:40 tgl Exp $ -->
|
||||||
|
|
||||||
<chapter id="libpq">
|
<chapter id="libpq">
|
||||||
<title><application>libpq</application> - C Library</title>
|
<title><application>libpq</application> - C Library</title>
|
||||||
@ -4914,7 +4914,9 @@ typedef struct
|
|||||||
<structname>PGconn</structname> that should be in the
|
<structname>PGconn</structname> that should be in the
|
||||||
<literal>CONNECTION_OK</literal> status; guaranteed if one calls
|
<literal>CONNECTION_OK</literal> status; guaranteed if one calls
|
||||||
<function>PQregisterEventProc</function> right after obtaining a good
|
<function>PQregisterEventProc</function> right after obtaining a good
|
||||||
<structname>PGconn</structname>.
|
<structname>PGconn</structname>. When returning a failure code, all
|
||||||
|
cleanup must be performed as no <literal>PGEVT_CONNDESTROY</literal>
|
||||||
|
event will be sent.
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
@ -4944,7 +4946,10 @@ typedef struct
|
|||||||
<structname>PGEventConnReset *</structname>. Although the contained
|
<structname>PGEventConnReset *</structname>. Although the contained
|
||||||
<structname>PGconn</structname> was just reset, all event data remains
|
<structname>PGconn</structname> was just reset, all event data remains
|
||||||
unchanged. This event should be used to reset/reload/requery any
|
unchanged. This event should be used to reset/reload/requery any
|
||||||
associated <literal>instanceData</literal>.
|
associated <literal>instanceData</literal>. Note that even if the
|
||||||
|
event procedure fails to process <literal>PGEVT_CONNRESET</>, it will
|
||||||
|
still receive a <literal>PGEVT_CONNDESTROY</> event when the connection
|
||||||
|
is closed.
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
@ -5003,7 +5008,9 @@ typedef struct
|
|||||||
<literal>instanceData</literal> that needs to be associated with the
|
<literal>instanceData</literal> that needs to be associated with the
|
||||||
result. If the event procedure fails, the result will be cleared and
|
result. If the event procedure fails, the result will be cleared and
|
||||||
the failure will be propagated. The event procedure must not try to
|
the failure will be propagated. The event procedure must not try to
|
||||||
<function>PQclear</> the result object for itself.
|
<function>PQclear</> the result object for itself. When returning a
|
||||||
|
failure code, all cleanup must be performed as no
|
||||||
|
<literal>PGEVT_RESULTDESTROY</literal> event will be sent.
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
@ -5014,7 +5021,10 @@ typedef struct
|
|||||||
<para>
|
<para>
|
||||||
The result copy event is fired in response to
|
The result copy event is fired in response to
|
||||||
<function>PQcopyResult</function>. This event will only be fired after
|
<function>PQcopyResult</function>. This event will only be fired after
|
||||||
the copy is complete.
|
the copy is complete. Only event procedures that have
|
||||||
|
successfully handled the <literal>PGEVT_RESULTCREATE</literal>
|
||||||
|
or <literal>PGEVT_RESULTCOPY</literal> event for the source result
|
||||||
|
will receive <literal>PGEVT_RESULTCOPY</literal> events.
|
||||||
|
|
||||||
<synopsis>
|
<synopsis>
|
||||||
typedef struct
|
typedef struct
|
||||||
@ -5032,7 +5042,10 @@ typedef struct
|
|||||||
can be used to provide a deep copy of <literal>instanceData</literal>,
|
can be used to provide a deep copy of <literal>instanceData</literal>,
|
||||||
since <literal>PQcopyResult</literal> cannot do that. If the event
|
since <literal>PQcopyResult</literal> cannot do that. If the event
|
||||||
procedure fails, the entire copy operation will fail and the
|
procedure fails, the entire copy operation will fail and the
|
||||||
<parameter>dest</parameter> result will be cleared.
|
<parameter>dest</parameter> result will be cleared. When returning a
|
||||||
|
failure code, all cleanup must be performed as no
|
||||||
|
<literal>PGEVT_RESULTDESTROY</literal> event will be sent for the
|
||||||
|
destination result.
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/interfaces/libpq/fe-exec.c,v 1.198 2008/09/17 04:31:08 tgl Exp $
|
* $PostgreSQL: pgsql/src/interfaces/libpq/fe-exec.c,v 1.199 2008/09/19 16:40:40 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -331,10 +331,7 @@ PQcopyResult(const PGresult *src, int flags)
|
|||||||
if (flags & PG_COPYRES_NOTICEHOOKS)
|
if (flags & PG_COPYRES_NOTICEHOOKS)
|
||||||
dest->noticeHooks = src->noticeHooks;
|
dest->noticeHooks = src->noticeHooks;
|
||||||
|
|
||||||
/*
|
/* Wants to copy PGEvents? */
|
||||||
* Wants to copy PGEvents? NB: this should be last, as we don't want
|
|
||||||
* to trigger RESULTDESTROY events on a useless PGresult.
|
|
||||||
*/
|
|
||||||
if ((flags & PG_COPYRES_EVENTS) && src->nEvents > 0)
|
if ((flags & PG_COPYRES_EVENTS) && src->nEvents > 0)
|
||||||
{
|
{
|
||||||
dest->events = dupEvents(src->events, src->nEvents);
|
dest->events = dupEvents(src->events, src->nEvents);
|
||||||
@ -349,15 +346,19 @@ PQcopyResult(const PGresult *src, int flags)
|
|||||||
/* Okay, trigger PGEVT_RESULTCOPY event */
|
/* Okay, trigger PGEVT_RESULTCOPY event */
|
||||||
for (i = 0; i < dest->nEvents; i++)
|
for (i = 0; i < dest->nEvents; i++)
|
||||||
{
|
{
|
||||||
PGEventResultCopy evt;
|
if (src->events[i].resultInitialized)
|
||||||
|
|
||||||
evt.src = src;
|
|
||||||
evt.dest = dest;
|
|
||||||
if (!dest->events[i].proc(PGEVT_RESULTCOPY, &evt,
|
|
||||||
dest->events[i].passThrough))
|
|
||||||
{
|
{
|
||||||
PQclear(dest);
|
PGEventResultCopy evt;
|
||||||
return NULL;
|
|
||||||
|
evt.src = src;
|
||||||
|
evt.dest = dest;
|
||||||
|
if (!dest->events[i].proc(PGEVT_RESULTCOPY, &evt,
|
||||||
|
dest->events[i].passThrough))
|
||||||
|
{
|
||||||
|
PQclear(dest);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
dest->events[i].resultInitialized = TRUE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -365,8 +366,9 @@ PQcopyResult(const PGresult *src, int flags)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copy an array of PGEvents (with no extra space for more)
|
* Copy an array of PGEvents (with no extra space for more).
|
||||||
* Does not duplicate the event instance data, sets this to NULL
|
* Does not duplicate the event instance data, sets this to NULL.
|
||||||
|
* Also, the resultInitialized flags are all cleared.
|
||||||
*/
|
*/
|
||||||
static PGEvent *
|
static PGEvent *
|
||||||
dupEvents(PGEvent *events, int count)
|
dupEvents(PGEvent *events, int count)
|
||||||
@ -381,13 +383,13 @@ dupEvents(PGEvent *events, int count)
|
|||||||
if (!newEvents)
|
if (!newEvents)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
memcpy(newEvents, events, count * sizeof(PGEvent));
|
|
||||||
|
|
||||||
/* NULL out the data pointers and deep copy names */
|
|
||||||
for (i = 0; i < count; i++)
|
for (i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
|
newEvents[i].proc = events[i].proc;
|
||||||
|
newEvents[i].passThrough = events[i].passThrough;
|
||||||
newEvents[i].data = NULL;
|
newEvents[i].data = NULL;
|
||||||
newEvents[i].name = strdup(newEvents[i].name);
|
newEvents[i].resultInitialized = FALSE;
|
||||||
|
newEvents[i].name = strdup(events[i].name);
|
||||||
if (!newEvents[i].name)
|
if (!newEvents[i].name)
|
||||||
{
|
{
|
||||||
while (--i >= 0)
|
while (--i >= 0)
|
||||||
@ -666,11 +668,15 @@ PQclear(PGresult *res)
|
|||||||
|
|
||||||
for (i = 0; i < res->nEvents; i++)
|
for (i = 0; i < res->nEvents; i++)
|
||||||
{
|
{
|
||||||
PGEventResultDestroy evt;
|
/* only send DESTROY to successfully-initialized event procs */
|
||||||
|
if (res->events[i].resultInitialized)
|
||||||
|
{
|
||||||
|
PGEventResultDestroy evt;
|
||||||
|
|
||||||
evt.result = res;
|
evt.result = res;
|
||||||
(void) res->events[i].proc(PGEVT_RESULTDESTROY, &evt,
|
(void) res->events[i].proc(PGEVT_RESULTDESTROY, &evt,
|
||||||
res->events[i].passThrough);
|
res->events[i].passThrough);
|
||||||
|
}
|
||||||
free(res->events[i].name);
|
free(res->events[i].name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1612,6 +1618,7 @@ PQgetResult(PGconn *conn)
|
|||||||
res->resultStatus = PGRES_FATAL_ERROR;
|
res->resultStatus = PGRES_FATAL_ERROR;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
res->events[i].resultInitialized = TRUE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/interfaces/libpq/libpq-events.c,v 1.1 2008/09/17 04:31:08 tgl Exp $
|
* $PostgreSQL: pgsql/src/interfaces/libpq/libpq-events.c,v 1.2 2008/09/19 16:40:40 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -76,6 +76,7 @@ PQregisterEventProc(PGconn *conn, PGEventProc proc,
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
conn->events[conn->nEvents].passThrough = passThrough;
|
conn->events[conn->nEvents].passThrough = passThrough;
|
||||||
conn->events[conn->nEvents].data = NULL;
|
conn->events[conn->nEvents].data = NULL;
|
||||||
|
conn->events[conn->nEvents].resultInitialized = FALSE;
|
||||||
conn->nEvents++;
|
conn->nEvents++;
|
||||||
|
|
||||||
regevt.conn = conn;
|
regevt.conn = conn;
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/interfaces/libpq/libpq-int.h,v 1.132 2008/09/17 04:31:08 tgl Exp $
|
* $PostgreSQL: pgsql/src/interfaces/libpq/libpq-int.h,v 1.133 2008/09/19 16:40:40 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -156,6 +156,7 @@ typedef struct PGEvent
|
|||||||
char *name; /* used only for error messages */
|
char *name; /* used only for error messages */
|
||||||
void *passThrough; /* pointer supplied at registration time */
|
void *passThrough; /* pointer supplied at registration time */
|
||||||
void *data; /* optional state (instance) data */
|
void *data; /* optional state (instance) data */
|
||||||
|
bool resultInitialized; /* T if RESULTCREATE/COPY succeeded */
|
||||||
} PGEvent;
|
} PGEvent;
|
||||||
|
|
||||||
struct pg_result
|
struct pg_result
|
||||||
|
Loading…
x
Reference in New Issue
Block a user