Skip to content

Commit b5eef75

Browse files
committed
Further refine _SPI_execute_plan's rule for atomic execution.
Commit 2dc1dea turns out to have been still a brick shy of a load, because CALL statements executing within a plpgsql exception block could still pass the wrong snapshot to stable functions within the CALL's argument list. That happened because standard_ProcessUtility forces isAtomicContext to true if IsTransactionBlock is true, which it always will be inside a subtransaction. Then ExecuteCallStmt would think it does not need to push a new snapshot --- but _SPI_execute_plan didn't do so either, since it thought it was in nonatomic mode. The best fix for this seems to be for _SPI_execute_plan to operate in atomic execution mode if IsSubTransaction() is true, even when the SPI context as a whole is non-atomic. This makes _SPI_execute_plan have the same rules about when non-atomic execution is allowed as _SPI_commit/_SPI_rollback have about when COMMIT/ROLLBACK are allowed, which seems appropriately symmetric. (If anyone ever tries to allow COMMIT/ROLLBACK inside a subtransaction, this would all need to be rethought ... but I'm unconvinced that such a thing could be logically consistent at all.) For further consistency, also check IsSubTransaction() in SPI_inside_nonatomic_context. That does not matter for its one present-day caller StartTransaction, which can't be reached inside a subtransaction. But if any other callers ever arise, they'd presumably want this definition. Per bug #18656 from Alexander Alehin. Back-patch to all supported branches, like previous fixes in this area. Discussion: https://postgr.es/m/18656-cade1780866ef66c@postgresql.org
1 parent eef9cc4 commit b5eef75

File tree

3 files changed

+47
-4
lines changed

3 files changed

+47
-4
lines changed

src/backend/executor/spi.c

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -334,13 +334,13 @@ _SPI_rollback(bool chain)
334334
MemoryContext oldcontext = CurrentMemoryContext;
335335
SavedTransactionCharacteristics savetc;
336336

337-
/* see under SPI_commit() */
337+
/* see comments in _SPI_commit() */
338338
if (_SPI_current->atomic)
339339
ereport(ERROR,
340340
(errcode(ERRCODE_INVALID_TRANSACTION_TERMINATION),
341341
errmsg("invalid transaction termination")));
342342

343-
/* see under SPI_commit() */
343+
/* see comments in _SPI_commit() */
344344
if (IsSubTransaction())
345345
ereport(ERROR,
346346
(errcode(ERRCODE_INVALID_TRANSACTION_TERMINATION),
@@ -582,8 +582,11 @@ SPI_inside_nonatomic_context(void)
582582
{
583583
if (_SPI_current == NULL)
584584
return false; /* not in any SPI context at all */
585+
/* these tests must match _SPI_commit's opinion of what's atomic: */
585586
if (_SPI_current->atomic)
586587
return false; /* it's atomic (ie function not procedure) */
588+
if (IsSubTransaction())
589+
return false; /* if within subtransaction, it's atomic */
587590
return true;
588591
}
589592

@@ -2411,9 +2414,12 @@ _SPI_execute_plan(SPIPlanPtr plan, const SPIExecuteOptions *options,
24112414

24122415
/*
24132416
* We allow nonatomic behavior only if options->allow_nonatomic is set
2414-
* *and* the SPI_OPT_NONATOMIC flag was given when connecting.
2417+
* *and* the SPI_OPT_NONATOMIC flag was given when connecting and we are
2418+
* not inside a subtransaction. The latter two tests match whether
2419+
* _SPI_commit() would allow a commit; see there for more commentary.
24152420
*/
2416-
allow_nonatomic = options->allow_nonatomic && !_SPI_current->atomic;
2421+
allow_nonatomic = options->allow_nonatomic &&
2422+
!_SPI_current->atomic && !IsSubTransaction();
24172423

24182424
/*
24192425
* Setup error traceback support for ereport()

src/pl/plpgsql/src/expected/plpgsql_call.out

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -597,6 +597,26 @@ NOTICE: f_get_x(1)
597597
NOTICE: f_print_x(1)
598598
NOTICE: f_get_x(2)
599599
NOTICE: f_print_x(2)
600+
-- test in non-atomic context, except exception block is locally atomic
601+
DO $$
602+
BEGIN
603+
BEGIN
604+
UPDATE t_test SET x = x + 1;
605+
RAISE NOTICE 'f_get_x(%)', f_get_x();
606+
CALL f_print_x(f_get_x());
607+
UPDATE t_test SET x = x + 1;
608+
RAISE NOTICE 'f_get_x(%)', f_get_x();
609+
CALL f_print_x(f_get_x());
610+
EXCEPTION WHEN division_by_zero THEN
611+
RAISE NOTICE '%', SQLERRM;
612+
END;
613+
ROLLBACK;
614+
END
615+
$$;
616+
NOTICE: f_get_x(1)
617+
NOTICE: f_print_x(1)
618+
NOTICE: f_get_x(2)
619+
NOTICE: f_print_x(2)
600620
-- test in atomic context
601621
BEGIN;
602622
DO $$

src/pl/plpgsql/src/sql/plpgsql_call.sql

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -557,6 +557,23 @@ BEGIN
557557
END
558558
$$;
559559

560+
-- test in non-atomic context, except exception block is locally atomic
561+
DO $$
562+
BEGIN
563+
BEGIN
564+
UPDATE t_test SET x = x + 1;
565+
RAISE NOTICE 'f_get_x(%)', f_get_x();
566+
CALL f_print_x(f_get_x());
567+
UPDATE t_test SET x = x + 1;
568+
RAISE NOTICE 'f_get_x(%)', f_get_x();
569+
CALL f_print_x(f_get_x());
570+
EXCEPTION WHEN division_by_zero THEN
571+
RAISE NOTICE '%', SQLERRM;
572+
END;
573+
ROLLBACK;
574+
END
575+
$$;
576+
560577
-- test in atomic context
561578
BEGIN;
562579

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy