@@ -260,12 +260,8 @@ SPI_commit(void)
260260 /* Start the actual commit */
261261 _SPI_current -> internal_xact = true;
262262
263- /*
264- * Before committing, pop all active snapshots to avoid error about
265- * "snapshot %p still active".
266- */
267- while (ActiveSnapshotSet ())
268- PopActiveSnapshot ();
263+ /* Release snapshots associated with portals */
264+ ForgetPortalSnapshots ();
269265
270266 CommitTransactionCommand ();
271267 MemoryContextSwitchTo (oldcontext );
@@ -300,6 +296,9 @@ SPI_rollback(void)
300296 /* Start the actual rollback */
301297 _SPI_current -> internal_xact = true;
302298
299+ /* Release snapshots associated with portals */
300+ ForgetPortalSnapshots ();
301+
303302 AbortCurrentTransaction ();
304303 MemoryContextSwitchTo (oldcontext );
305304
@@ -2102,6 +2101,7 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
21022101 Oid my_lastoid = InvalidOid ;
21032102 SPITupleTable * my_tuptable = NULL ;
21042103 int res = 0 ;
2104+ bool allow_nonatomic = plan -> no_snapshots ; /* legacy API name */
21052105 bool pushed_active_snap = false;
21062106 ErrorContextCallback spierrcontext ;
21072107 CachedPlan * cplan = NULL ;
@@ -2134,11 +2134,12 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
21342134 * In the first two cases, we can just push the snap onto the stack once
21352135 * for the whole plan list.
21362136 *
2137- * But if the plan has no_snapshots set to true, then don't manage
2138- * snapshots at all. The caller should then take care of that .
2137+ * Note that snapshot != InvalidSnapshot implies an atomic execution
2138+ * context .
21392139 */
2140- if (snapshot != InvalidSnapshot && ! plan -> no_snapshots )
2140+ if (snapshot != InvalidSnapshot )
21412141 {
2142+ Assert (!allow_nonatomic );
21422143 if (read_only )
21432144 {
21442145 PushActiveSnapshot (snapshot );
@@ -2225,15 +2226,39 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
22252226 stmt_list = cplan -> stmt_list ;
22262227
22272228 /*
2228- * In the default non-read-only case, get a new snapshot, replacing
2229- * any that we pushed in a previous cycle .
2229+ * If we weren't given a specific snapshot to use, and the statement
2230+ * list requires a snapshot, set that up .
22302231 */
2231- if (snapshot == InvalidSnapshot && !read_only && !plan -> no_snapshots )
2232+ if (snapshot == InvalidSnapshot &&
2233+ (list_length (stmt_list ) > 1 ||
2234+ (list_length (stmt_list ) == 1 &&
2235+ PlannedStmtRequiresSnapshot (linitial_node (PlannedStmt ,
2236+ stmt_list )))))
22322237 {
2233- if (pushed_active_snap )
2234- PopActiveSnapshot ();
2235- PushActiveSnapshot (GetTransactionSnapshot ());
2236- pushed_active_snap = true;
2238+ /*
2239+ * First, ensure there's a Portal-level snapshot. This back-fills
2240+ * the snapshot stack in case the previous operation was a COMMIT
2241+ * or ROLLBACK inside a procedure or DO block. (We can't put back
2242+ * the Portal snapshot any sooner, or we'd break cases like doing
2243+ * SET or LOCK just after COMMIT.) It's enough to check once per
2244+ * statement list, since COMMIT/ROLLBACK/CALL/DO can't appear
2245+ * within a multi-statement list.
2246+ */
2247+ EnsurePortalSnapshotExists ();
2248+
2249+ /*
2250+ * In the default non-read-only case, get a new per-statement-list
2251+ * snapshot, replacing any that we pushed in a previous cycle.
2252+ * Skip it when doing non-atomic execution, though (we rely
2253+ * entirely on the Portal snapshot in that case).
2254+ */
2255+ if (!read_only && !allow_nonatomic )
2256+ {
2257+ if (pushed_active_snap )
2258+ PopActiveSnapshot ();
2259+ PushActiveSnapshot (GetTransactionSnapshot ());
2260+ pushed_active_snap = true;
2261+ }
22372262 }
22382263
22392264 foreach (lc2 , stmt_list )
@@ -2246,6 +2271,7 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
22462271 _SPI_current -> lastoid = InvalidOid ;
22472272 _SPI_current -> tuptable = NULL ;
22482273
2274+ /* Check for unsupported cases. */
22492275 if (stmt -> utilityStmt )
22502276 {
22512277 if (IsA (stmt -> utilityStmt , CopyStmt ))
@@ -2277,9 +2303,10 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
22772303
22782304 /*
22792305 * If not read-only mode, advance the command counter before each
2280- * command and update the snapshot.
2306+ * command and update the snapshot. (But skip it if the snapshot
2307+ * isn't under our control.)
22812308 */
2282- if (!read_only && ! plan -> no_snapshots )
2309+ if (!read_only && pushed_active_snap )
22832310 {
22842311 CommandCounterIncrement ();
22852312 UpdateActiveSnapshotCommandId ();
@@ -2313,13 +2340,11 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
23132340 ProcessUtilityContext context ;
23142341
23152342 /*
2316- * If the SPI context is atomic, or we are asked to manage
2317- * snapshots, then we are in an atomic execution context.
2318- * Conversely, to propagate a nonatomic execution context, the
2319- * caller must be in a nonatomic SPI context and manage
2320- * snapshots itself.
2343+ * If the SPI context is atomic, or we were not told to allow
2344+ * nonatomic operations, tell ProcessUtility this is an atomic
2345+ * execution context.
23212346 */
2322- if (_SPI_current -> atomic || !plan -> no_snapshots )
2347+ if (_SPI_current -> atomic || !allow_nonatomic )
23232348 context = PROCESS_UTILITY_QUERY ;
23242349 else
23252350 context = PROCESS_UTILITY_QUERY_NONATOMIC ;
0 commit comments