Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
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

+10-4
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

+20
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

+17
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)