diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c index b5797042ab..425837d340 100644 --- a/src/backend/tcop/pquery.c +++ b/src/backend/tcop/pquery.c @@ -472,9 +472,9 @@ PortalStart(Portal portal, ParamListInfo params, /* Must set snapshot before starting executor. */ if (snapshot) - PushActiveSnapshot(snapshot); + PushActiveSnapshotWithLevel(snapshot, portal->createSubid); else - PushActiveSnapshot(GetTransactionSnapshot()); + PushActiveSnapshotWithLevel(GetTransactionSnapshot(), portal->createSubid); /* * We could remember the snapshot in portal->portalSnapshot, @@ -915,7 +915,7 @@ PortalRunSelect(Portal portal, nprocessed = RunFromStore(portal, direction, (uint64) count, dest); else { - PushActiveSnapshot(queryDesc->snapshot); + PushActiveSnapshotWithLevel(queryDesc->snapshot, portal->createSubid); ExecutorRun(queryDesc, direction, (uint64) count, portal->run_once); nprocessed = queryDesc->estate->es_processed; @@ -955,7 +955,7 @@ PortalRunSelect(Portal portal, nprocessed = RunFromStore(portal, direction, (uint64) count, dest); else { - PushActiveSnapshot(queryDesc->snapshot); + PushActiveSnapshotWithLevel(queryDesc->snapshot, portal->createSubid); ExecutorRun(queryDesc, direction, (uint64) count, portal->run_once); nprocessed = queryDesc->estate->es_processed; @@ -1137,8 +1137,8 @@ PortalRunUtility(Portal portal, PlannedStmt *pstmt, portal->holdSnapshot = snapshot; } /* In any case, make the snapshot active and remember it in portal */ - PushActiveSnapshot(snapshot); - /* PushActiveSnapshot might have copied the snapshot */ + PushActiveSnapshotWithLevel(snapshot, portal->createSubid); + /* PushActiveSnapshotWithLevel might have copied the snapshot */ portal->portalSnapshot = GetActiveSnapshot(); } else @@ -1704,7 +1704,7 @@ DoPortalRewind(Portal portal) queryDesc = portal->queryDesc; if (queryDesc) { - PushActiveSnapshot(queryDesc->snapshot); + PushActiveSnapshotWithLevel(queryDesc->snapshot, portal->createSubid); ExecutorRewind(queryDesc); PopActiveSnapshot(); } @@ -1785,7 +1785,7 @@ EnsurePortalSnapshotExists(void) Assert(portal->portalSnapshot == NULL); /* Create a new snapshot and make it active */ - PushActiveSnapshot(GetTransactionSnapshot()); - /* PushActiveSnapshot might have copied the snapshot */ + PushActiveSnapshotWithLevel(GetTransactionSnapshot(), portal->createSubid); + /* PushActiveSnapshotWithLevel might have copied the snapshot */ portal->portalSnapshot = GetActiveSnapshot(); } diff --git a/src/backend/utils/time/snapmgr.c b/src/backend/utils/time/snapmgr.c index 2968c7f7b7..dbf322d0d5 100644 --- a/src/backend/utils/time/snapmgr.c +++ b/src/backend/utils/time/snapmgr.c @@ -704,6 +704,45 @@ PushActiveSnapshot(Snapshot snap) OldestActiveSnapshot = ActiveSnapshot; } +/* + * PushActiveSnapshotWithLevel + * Set the given snapshot as the current active snapshot + * Set the given as_level to the given snapshot + * + * If the passed snapshot is a statically-allocated one, or it is possibly + * subject to a future command counter update, create a new long-lived copy + * with active refcount=1. Otherwise, only increment the refcount. + */ +void +PushActiveSnapshotWithLevel(Snapshot snap, int as_level) +{ + ActiveSnapshotElt *newactive; + + Assert(snap != InvalidSnapshot); + if (ActiveSnapshot != NULL && ActiveSnapshot->as_next != NULL) + Assert(as_level >= ActiveSnapshot->as_next->as_level); + + newactive = MemoryContextAlloc(TopTransactionContext, sizeof(ActiveSnapshotElt)); + + /* + * Checking SecondarySnapshot is probably useless here, but it seems + * better to be sure. + */ + if (snap == CurrentSnapshot || snap == SecondarySnapshot || !snap->copied) + newactive->as_snap = CopySnapshot(snap); + else + newactive->as_snap = snap; + + newactive->as_next = ActiveSnapshot; + newactive->as_level = as_level; + + newactive->as_snap->active_count++; + + ActiveSnapshot = newactive; + if (OldestActiveSnapshot == NULL) + OldestActiveSnapshot = ActiveSnapshot; +} + /* * PushCopiedSnapshot * As above, except forcibly copy the presented snapshot. diff --git a/src/include/utils/snapmgr.h b/src/include/utils/snapmgr.h index 44539fe15a..142326950e 100644 --- a/src/include/utils/snapmgr.h +++ b/src/include/utils/snapmgr.h @@ -114,6 +114,7 @@ extern void InvalidateCatalogSnapshot(void); extern void InvalidateCatalogSnapshotConditionally(void); extern void PushActiveSnapshot(Snapshot snapshot); +extern void PushActiveSnapshotWithLevel(Snapshot snapshot, int as_level); extern void PushCopiedSnapshot(Snapshot snapshot); extern void UpdateActiveSnapshotCommandId(void); extern void PopActiveSnapshot(void); diff --git a/src/test/regress/expected/portals.out b/src/test/regress/expected/portals.out index 9da74876e1..55c06bb32d 100644 --- a/src/test/regress/expected/portals.out +++ b/src/test/regress/expected/portals.out @@ -1536,3 +1536,21 @@ fetch backward all in c2; (3 rows) rollback; +-- check re-establishing the portal snapshot with the correct as_level +create table tst1 (a int primary key); +insert into tst1 values (1),(2); +create table tst2 (a int); +insert into tst2 values (1); +DO $$ +BEGIN + FOR i IN 1..2 LOOP + BEGIN + INSERT INTO tst1 (a) VALUES (i); + exception when unique_violation then update tst2 set a = i; + COMMIT; + END; + END LOOP; +END; +$$; +drop table tst1; +drop table tst2; diff --git a/src/test/regress/sql/portals.sql b/src/test/regress/sql/portals.sql index eadf6ed942..0a967546a4 100644 --- a/src/test/regress/sql/portals.sql +++ b/src/test/regress/sql/portals.sql @@ -581,3 +581,24 @@ declare c2 scroll cursor for select generate_series(1,3) as g; fetch all in c2; fetch backward all in c2; rollback; + +-- check re-establishing the portal snapshot with the correct as_level +create table tst1 (a int primary key); +insert into tst1 values (1),(2); +create table tst2 (a int); +insert into tst2 values (1); + +DO $$ +BEGIN + FOR i IN 1..2 LOOP + BEGIN + INSERT INTO tst1 (a) VALUES (i); + exception when unique_violation then update tst2 set a = i; + COMMIT; + END; + END LOOP; +END; +$$; + +drop table tst1; +drop table tst2;