aboutsummaryrefslogtreecommitdiff
path: root/src/backend/executor/spi.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/executor/spi.c')
-rw-r--r--src/backend/executor/spi.c96
1 files changed, 55 insertions, 41 deletions
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index 0865e3ebef2..a717a0deead 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -1768,7 +1768,7 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
Oid my_lastoid = InvalidOid;
SPITupleTable *my_tuptable = NULL;
int res = 0;
- bool have_active_snap = ActiveSnapshotSet();
+ bool pushed_active_snap = false;
ErrorContextCallback spierrcontext;
CachedPlan *cplan = NULL;
ListCell *lc1;
@@ -1781,6 +1781,40 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
spierrcontext.previous = error_context_stack;
error_context_stack = &spierrcontext;
+ /*
+ * We support four distinct snapshot management behaviors:
+ *
+ * snapshot != InvalidSnapshot, read_only = true: use exactly the given
+ * snapshot.
+ *
+ * snapshot != InvalidSnapshot, read_only = false: use the given
+ * snapshot, modified by advancing its command ID before each querytree.
+ *
+ * snapshot == InvalidSnapshot, read_only = true: use the entry-time
+ * ActiveSnapshot, if any (if there isn't one, we run with no snapshot).
+ *
+ * snapshot == InvalidSnapshot, read_only = false: take a full new
+ * snapshot for each user command, and advance its command ID before each
+ * querytree within the command.
+ *
+ * In the first two cases, we can just push the snap onto the stack
+ * once for the whole plan list.
+ */
+ if (snapshot != InvalidSnapshot)
+ {
+ if (read_only)
+ {
+ PushActiveSnapshot(snapshot);
+ pushed_active_snap = true;
+ }
+ else
+ {
+ /* Make sure we have a private copy of the snapshot to modify */
+ PushCopiedSnapshot(snapshot);
+ pushed_active_snap = true;
+ }
+ }
+
foreach(lc1, plan->plancache_list)
{
CachedPlanSource *plansource = (CachedPlanSource *) lfirst(lc1);
@@ -1802,12 +1836,23 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
stmt_list = plansource->plan->stmt_list;
}
+ /*
+ * In the default non-read-only case, get a new snapshot, replacing
+ * any that we pushed in a previous cycle.
+ */
+ if (snapshot == InvalidSnapshot && !read_only)
+ {
+ if (pushed_active_snap)
+ PopActiveSnapshot();
+ PushActiveSnapshot(GetTransactionSnapshot());
+ pushed_active_snap = true;
+ }
+
foreach(lc2, stmt_list)
{
Node *stmt = (Node *) lfirst(lc2);
bool canSetTag;
DestReceiver *dest;
- bool pushed_active_snap = false;
_SPI_current->processed = 0;
_SPI_current->lastoid = InvalidOid;
@@ -1848,48 +1893,16 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
/*
* If not read-only mode, advance the command counter before each
- * command.
+ * command and update the snapshot.
*/
if (!read_only)
+ {
CommandCounterIncrement();
+ UpdateActiveSnapshotCommandId();
+ }
dest = CreateDestReceiver(canSetTag ? DestSPI : DestNone);
- if (snapshot == InvalidSnapshot)
- {
- /*
- * Default read_only behavior is to use the entry-time
- * ActiveSnapshot, if any; if read-write, grab a full new
- * snap.
- */
- if (read_only)
- {
- if (have_active_snap)
- {
- PushActiveSnapshot(GetActiveSnapshot());
- pushed_active_snap = true;
- }
- }
- else
- {
- PushActiveSnapshot(GetTransactionSnapshot());
- pushed_active_snap = true;
- }
- }
- else
- {
- /*
- * We interpret read_only with a specified snapshot to be
- * exactly that snapshot, but read-write means use the snap
- * with advancing of command ID.
- */
- if (read_only)
- PushActiveSnapshot(snapshot);
- else
- PushUpdatedSnapshot(snapshot);
- pushed_active_snap = true;
- }
-
if (IsA(stmt, PlannedStmt) &&
((PlannedStmt *) stmt)->utilityStmt == NULL)
{
@@ -1925,9 +1938,6 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
res = SPI_OK_UTILITY;
}
- if (pushed_active_snap)
- PopActiveSnapshot();
-
/*
* The last canSetTag query sets the status values returned to the
* caller. Be careful to free any tuptables not returned, to
@@ -1970,6 +1980,10 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
fail:
+ /* Pop the snapshot off the stack if we pushed one */
+ if (pushed_active_snap)
+ PopActiveSnapshot();
+
/* We no longer need the cached plan refcount, if any */
if (cplan)
ReleaseCachedPlan(cplan, true);