Skip to content

Commit a54a873

Browse files
committed
Ensure that plpgsql cleans up cleanly during parallel-worker exit.
plpgsql_xact_cb ought to treat events XACT_EVENT_PARALLEL_COMMIT and XACT_EVENT_PARALLEL_ABORT like XACT_EVENT_COMMIT and XACT_EVENT_ABORT respectively, since its goal is to do process-local cleanup. This oversight caused plpgsql's end-of-transaction cleanup to not get done in parallel workers. Since a parallel worker will exit just after the transaction cleanup, the effects of this are limited. I couldn't find any case in the core code with user-visible effects, but perhaps there are some in extensions. In any case it's wrong, so let's fix it before it bites us not after. In passing, add some comments around the handling of expression evaluation resources in DO blocks. There's no live bug there, but it's quite unobvious what's happening; at least I thought so. This isn't related to the other issue, except that I found both things while poking at expression-evaluation performance. Back-patch the plpgsql_xact_cb fix to 9.5 where those event types were introduced, and the DO-block commentary to v11 where DO blocks gained the ability to issue COMMIT/ROLLBACK. Discussion: https://postgr.es/m/10353.1585247879@sss.pgh.pa.us
1 parent 223e9c7 commit a54a873

File tree

2 files changed

+37
-7
lines changed

2 files changed

+37
-7
lines changed

src/pl/plpgsql/src/pl_exec.c

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,13 @@ typedef struct
8484
* has its own simple-expression EState, which is cleaned up at exit from
8585
* plpgsql_inline_handler(). DO blocks still use the simple_econtext_stack,
8686
* though, so that subxact abort cleanup does the right thing.
87+
*
88+
* (However, if a DO block executes COMMIT or ROLLBACK, then exec_stmt_commit
89+
* or exec_stmt_rollback will unlink it from the DO's simple-expression EState
90+
* and create a new shared EState that will be used thenceforth. The original
91+
* EState will be cleaned up when we get back to plpgsql_inline_handler. This
92+
* is a bit ugly, but it isn't worth doing better, since scenarios like this
93+
* can't result in indefinite accumulation of state trees.)
8794
*/
8895
typedef struct SimpleEcontextStackEntry
8996
{
@@ -2317,8 +2324,8 @@ exec_stmt_call(PLpgSQL_execstate *estate, PLpgSQL_stmt_call *stmt)
23172324
else
23182325
{
23192326
/*
2320-
* If we are in a new transaction after the call, we need to reset
2321-
* some internal state.
2327+
* If we are in a new transaction after the call, we need to build new
2328+
* simple-expression infrastructure.
23222329
*/
23232330
estate->simple_eval_estate = NULL;
23242331
plpgsql_create_econtext(estate);
@@ -4827,6 +4834,10 @@ exec_stmt_commit(PLpgSQL_execstate *estate, PLpgSQL_stmt_commit *stmt)
48274834
SPI_start_transaction();
48284835
}
48294836

4837+
/*
4838+
* We need to build new simple-expression infrastructure, since the old
4839+
* data structures are gone.
4840+
*/
48304841
estate->simple_eval_estate = NULL;
48314842
plpgsql_create_econtext(estate);
48324843

@@ -4849,6 +4860,10 @@ exec_stmt_rollback(PLpgSQL_execstate *estate, PLpgSQL_stmt_rollback *stmt)
48494860
SPI_start_transaction();
48504861
}
48514862

4863+
/*
4864+
* We need to build new simple-expression infrastructure, since the old
4865+
* data structures are gone.
4866+
*/
48524867
estate->simple_eval_estate = NULL;
48534868
plpgsql_create_econtext(estate);
48544869

@@ -8201,8 +8216,13 @@ plpgsql_create_econtext(PLpgSQL_execstate *estate)
82018216
* one already in the current transaction. The EState is made a child of
82028217
* TopTransactionContext so it will have the right lifespan.
82038218
*
8204-
* Note that this path is never taken when executing a DO block; the
8205-
* required EState was already made by plpgsql_inline_handler.
8219+
* Note that this path is never taken when beginning a DO block; the
8220+
* required EState was already made by plpgsql_inline_handler. However,
8221+
* if the DO block executes COMMIT or ROLLBACK, then we'll come here and
8222+
* make a shared EState to use for the rest of the DO block. That's OK;
8223+
* see the comments for shared_simple_eval_estate. (Note also that a DO
8224+
* block will continue to use its private cast hash table for the rest of
8225+
* the block. That's okay for now, but it might cause problems someday.)
82068226
*/
82078227
if (estate->simple_eval_estate == NULL)
82088228
{
@@ -8274,15 +8294,18 @@ plpgsql_xact_cb(XactEvent event, void *arg)
82748294
* expect the regular abort recovery procedures to release everything of
82758295
* interest.
82768296
*/
8277-
if (event == XACT_EVENT_COMMIT || event == XACT_EVENT_PREPARE)
8297+
if (event == XACT_EVENT_COMMIT ||
8298+
event == XACT_EVENT_PARALLEL_COMMIT ||
8299+
event == XACT_EVENT_PREPARE)
82788300
{
82798301
simple_econtext_stack = NULL;
82808302

82818303
if (shared_simple_eval_estate)
82828304
FreeExecutorState(shared_simple_eval_estate);
82838305
shared_simple_eval_estate = NULL;
82848306
}
8285-
else if (event == XACT_EVENT_ABORT)
8307+
else if (event == XACT_EVENT_ABORT ||
8308+
event == XACT_EVENT_PARALLEL_ABORT)
82868309
{
82878310
simple_econtext_stack = NULL;
82888311
shared_simple_eval_estate = NULL;

src/pl/plpgsql/src/pl_handler.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,14 @@ plpgsql_inline_handler(PG_FUNCTION_ARGS)
330330
flinfo.fn_oid = InvalidOid;
331331
flinfo.fn_mcxt = CurrentMemoryContext;
332332

333-
/* Create a private EState for simple-expression execution */
333+
/*
334+
* Create a private EState for simple-expression execution. Notice that
335+
* this is NOT tied to transaction-level resources; it must survive any
336+
* COMMIT/ROLLBACK the DO block executes, since we will unconditionally
337+
* try to clean it up below. (Hence, be wary of adding anything that
338+
* could fail between here and the PG_TRY block.) See the comments for
339+
* shared_simple_eval_estate.
340+
*/
334341
simple_eval_estate = CreateExecutorState();
335342

336343
/* And run the function */

0 commit comments

Comments
 (0)