Skip to content

Commit ce1e7a2

Browse files
committed
Don't let libpq "event" procs break the state of PGresult objects.
As currently implemented, failure of a PGEVT_RESULTCREATE callback causes the PGresult to be converted to an error result. This is intellectually inconsistent (shouldn't a failing callback likewise prevent creation of the error result? what about side-effects on the behavior seen by other event procs? why does PQfireResultCreateEvents act differently from PQgetResult?), but more importantly it destroys any promises we might wish to make about the behavior of libpq in nontrivial operating modes, such as pipeline mode. For example, it's not possible to promise that PGRES_PIPELINE_SYNC results will be returned if an event callback fails on those. With this definition, expecting applications to behave sanely in the face of possibly-failing callbacks seems like a very big lift. Hence, redefine the result of a callback failure as being simply that that event procedure won't be called any more for this PGresult (which was true already). Event procedures can still signal failure back to the application through out-of-band mechanisms, for example via their passthrough arguments. Similarly, don't let failure of a PGEVT_RESULTCOPY callback prevent PQcopyResult from succeeding. That definition allowed a misbehaving event proc to break single-row mode (our sole internal use of PQcopyResult), and it probably had equally deleterious effects for outside uses. Discussion: https://postgr.es/m/3185105.1644960083@sss.pgh.pa.us
1 parent de447bb commit ce1e7a2

File tree

3 files changed

+31
-51
lines changed

3 files changed

+31
-51
lines changed

doc/src/sgml/libpq.sgml

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6831,6 +6831,7 @@ PGresult *PQcopyResult(const PGresult *src, int flags);
68316831
<literal>PG_COPYRES_EVENTS</literal> specifies copying the source
68326832
result's events. (But any instance data associated with the source
68336833
is not copied.)
6834+
The event procedures receive <literal>PGEVT_RESULTCOPY</literal> events.
68346835
</para>
68356836
</listitem>
68366837
</varlistentry>
@@ -7126,7 +7127,7 @@ defaultNoticeProcessor(void *arg, const char *message)
71267127
<xref linkend="libpq-PQinstanceData"/>,
71277128
<xref linkend="libpq-PQsetInstanceData"/>,
71287129
<xref linkend="libpq-PQresultInstanceData"/> and
7129-
<function>PQsetResultInstanceData</function> functions. Note that
7130+
<xref linkend="libpq-PQresultSetInstanceData"/> functions. Note that
71307131
unlike the pass-through pointer, instance data of a <structname>PGconn</structname>
71317132
is not automatically inherited by <structname>PGresult</structname>s created from
71327133
it. <application>libpq</application> does not know what pass-through
@@ -7154,7 +7155,7 @@ defaultNoticeProcessor(void *arg, const char *message)
71547155
is called. It is the ideal time to initialize any
71557156
<literal>instanceData</literal> an event procedure may need. Only one
71567157
register event will be fired per event handler per connection. If the
7157-
event procedure fails, the registration is aborted.
7158+
event procedure fails (returns zero), the registration is cancelled.
71587159

71597160
<synopsis>
71607161
typedef struct
@@ -7261,11 +7262,11 @@ typedef struct
72617262
<parameter>conn</parameter> is the connection used to generate the
72627263
result. This is the ideal place to initialize any
72637264
<literal>instanceData</literal> that needs to be associated with the
7264-
result. If the event procedure fails, the result will be cleared and
7265-
the failure will be propagated. The event procedure must not try to
7266-
<xref linkend="libpq-PQclear"/> the result object for itself. When returning a
7267-
failure code, all cleanup must be performed as no
7268-
<literal>PGEVT_RESULTDESTROY</literal> event will be sent.
7265+
result. If an event procedure fails (returns zero), that event
7266+
procedure will be ignored for the remaining lifetime of the result;
7267+
that is, it will not receive <literal>PGEVT_RESULTCOPY</literal>
7268+
or <literal>PGEVT_RESULTDESTROY</literal> events for this result or
7269+
results copied from it.
72697270
</para>
72707271
</listitem>
72717272
</varlistentry>
@@ -7295,12 +7296,12 @@ typedef struct
72957296
<parameter>src</parameter> result is what was copied while the
72967297
<parameter>dest</parameter> result is the copy destination. This event
72977298
can be used to provide a deep copy of <literal>instanceData</literal>,
7298-
since <literal>PQcopyResult</literal> cannot do that. If the event
7299-
procedure fails, the entire copy operation will fail and the
7300-
<parameter>dest</parameter> result will be cleared. When returning a
7301-
failure code, all cleanup must be performed as no
7302-
<literal>PGEVT_RESULTDESTROY</literal> event will be sent for the
7303-
destination result.
7299+
since <literal>PQcopyResult</literal> cannot do that. If an event
7300+
procedure fails (returns zero), that event procedure will be
7301+
ignored for the remaining lifetime of the new result; that is, it
7302+
will not receive <literal>PGEVT_RESULTCOPY</literal>
7303+
or <literal>PGEVT_RESULTDESTROY</literal> events for that result or
7304+
results copied from it.
73047305
</para>
73057306
</listitem>
73067307
</varlistentry>
@@ -7618,7 +7619,7 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
76187619
mydata *res_data = dup_mydata(conn_data);
76197620

76207621
/* associate app specific data with result (copy it from conn) */
7621-
PQsetResultInstanceData(e->result, myEventProc, res_data);
7622+
PQresultSetInstanceData(e->result, myEventProc, res_data);
76227623
break;
76237624
}
76247625

@@ -7629,7 +7630,7 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
76297630
mydata *dest_data = dup_mydata(src_data);
76307631

76317632
/* associate app specific data with result (copy it from a result) */
7632-
PQsetResultInstanceData(e->dest, myEventProc, dest_data);
7633+
PQresultSetInstanceData(e->dest, myEventProc, dest_data);
76337634
break;
76347635
}
76357636

src/interfaces/libpq/fe-exec.c

Lines changed: 7 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -363,19 +363,16 @@ PQcopyResult(const PGresult *src, int flags)
363363
/* Okay, trigger PGEVT_RESULTCOPY event */
364364
for (i = 0; i < dest->nEvents; i++)
365365
{
366+
/* We don't fire events that had some previous failure */
366367
if (src->events[i].resultInitialized)
367368
{
368369
PGEventResultCopy evt;
369370

370371
evt.src = src;
371372
evt.dest = dest;
372-
if (!dest->events[i].proc(PGEVT_RESULTCOPY, &evt,
373-
dest->events[i].passThrough))
374-
{
375-
PQclear(dest);
376-
return NULL;
377-
}
378-
dest->events[i].resultInitialized = true;
373+
if (dest->events[i].proc(PGEVT_RESULTCOPY, &evt,
374+
dest->events[i].passThrough))
375+
dest->events[i].resultInitialized = true;
379376
}
380377
}
381378

@@ -2124,29 +2121,9 @@ PQgetResult(PGconn *conn)
21242121
break;
21252122
}
21262123

2127-
if (res)
2128-
{
2129-
int i;
2130-
2131-
for (i = 0; i < res->nEvents; i++)
2132-
{
2133-
PGEventResultCreate evt;
2134-
2135-
evt.conn = conn;
2136-
evt.result = res;
2137-
if (!res->events[i].proc(PGEVT_RESULTCREATE, &evt,
2138-
res->events[i].passThrough))
2139-
{
2140-
appendPQExpBuffer(&conn->errorMessage,
2141-
libpq_gettext("PGEventProc \"%s\" failed during PGEVT_RESULTCREATE event\n"),
2142-
res->events[i].name);
2143-
pqSetResultError(res, &conn->errorMessage);
2144-
res->resultStatus = PGRES_FATAL_ERROR;
2145-
break;
2146-
}
2147-
res->events[i].resultInitialized = true;
2148-
}
2149-
}
2124+
/* Time to fire PGEVT_RESULTCREATE events, if there are any */
2125+
if (res && res->nEvents > 0)
2126+
(void) PQfireResultCreateEvents(conn, res);
21502127

21512128
return res;
21522129
}

src/interfaces/libpq/libpq-events.c

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -184,26 +184,28 @@ PQresultInstanceData(const PGresult *result, PGEventProc proc)
184184
int
185185
PQfireResultCreateEvents(PGconn *conn, PGresult *res)
186186
{
187+
int result = true;
187188
int i;
188189

189190
if (!res)
190191
return false;
191192

192193
for (i = 0; i < res->nEvents; i++)
193194
{
195+
/* It's possible event was already fired, if so don't repeat it */
194196
if (!res->events[i].resultInitialized)
195197
{
196198
PGEventResultCreate evt;
197199

198200
evt.conn = conn;
199201
evt.result = res;
200-
if (!res->events[i].proc(PGEVT_RESULTCREATE, &evt,
201-
res->events[i].passThrough))
202-
return false;
203-
204-
res->events[i].resultInitialized = true;
202+
if (res->events[i].proc(PGEVT_RESULTCREATE, &evt,
203+
res->events[i].passThrough))
204+
res->events[i].resultInitialized = true;
205+
else
206+
result = false;
205207
}
206208
}
207209

208-
return true;
210+
return result;
209211
}

0 commit comments

Comments
 (0)