diff options
author | Alvaro Herrera | 2009-10-02 17:57:30 +0000 |
---|---|---|
committer | Alvaro Herrera | 2009-10-02 17:57:30 +0000 |
commit | a8828c6e8e438bd482ed7936d9c8d29e1681e10b (patch) | |
tree | 4a2e352a8c9ce6c77f700ac1ff29a8bc657c24cd | |
parent | e777fd4ee111311f421a11825ba3c515cba1f95d (diff) |
Ensure that a cursor has an immutable snapshot throughout its lifespan.
The old coding was using a regular snapshot, referenced elsewhere, that was
subject to having its command counter updated. Fix by creating a private copy
of the snapshot exclusively for the cursor.
Backpatch to 8.4, which is when the bug was introduced during the snapshot
management rewrite.
-rw-r--r-- | src/backend/commands/portalcmds.c | 11 | ||||
-rw-r--r-- | src/backend/executor/spi.c | 5 | ||||
-rw-r--r-- | src/backend/utils/time/snapmgr.c | 3 | ||||
-rw-r--r-- | src/include/utils/snapmgr.h | 1 | ||||
-rw-r--r-- | src/test/regress/expected/portals.out | 15 | ||||
-rw-r--r-- | src/test/regress/sql/portals.sql | 12 |
6 files changed, 40 insertions, 7 deletions
diff --git a/src/backend/commands/portalcmds.c b/src/backend/commands/portalcmds.c index 9ca2e84c52..e89e371c37 100644 --- a/src/backend/commands/portalcmds.c +++ b/src/backend/commands/portalcmds.c @@ -47,6 +47,7 @@ PerformCursorOpen(PlannedStmt *stmt, ParamListInfo params, DeclareCursorStmt *cstmt = (DeclareCursorStmt *) stmt->utilityStmt; Portal portal; MemoryContext oldContext; + Snapshot snapshot; if (cstmt == NULL || !IsA(cstmt, DeclareCursorStmt)) elog(ERROR, "PerformCursorOpen called for non-cursor query"); @@ -119,9 +120,17 @@ PerformCursorOpen(PlannedStmt *stmt, ParamListInfo params, } /* + * Set up snapshot for portal. Note that we need a fresh, independent copy + * of the snapshot because we don't want it to be modified by future + * CommandCounterIncrement calls. We do not register it, because + * portalmem.c will take care of that internally. + */ + snapshot = CopySnapshot(GetActiveSnapshot()); + + /* * Start execution, inserting parameters if any. */ - PortalStart(portal, params, GetActiveSnapshot()); + PortalStart(portal, params, snapshot); Assert(portal->strategy == PORTAL_ONE_SELECT); diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c index 12b55d5745..4cd7b0b212 100644 --- a/src/backend/executor/spi.c +++ b/src/backend/executor/spi.c @@ -1211,10 +1211,7 @@ SPI_cursor_open_internal(const char *name, SPIPlanPtr plan, } } - /* - * Set up the snapshot to use. (PortalStart will do PushActiveSnapshot, - * so we skip that here.) - */ + /* Set up the snapshot to use. */ if (read_only) snapshot = GetActiveSnapshot(); else diff --git a/src/backend/utils/time/snapmgr.c b/src/backend/utils/time/snapmgr.c index 85065d3572..95ff4cba27 100644 --- a/src/backend/utils/time/snapmgr.c +++ b/src/backend/utils/time/snapmgr.c @@ -104,7 +104,6 @@ bool FirstSnapshotSet = false; static bool registered_serializable = false; -static Snapshot CopySnapshot(Snapshot snapshot); static void FreeSnapshot(Snapshot snapshot); static void SnapshotResetXmin(void); @@ -192,7 +191,7 @@ SnapshotSetCommandId(CommandId curcid) * The copy is palloc'd in TopTransactionContext and has initial refcounts set * to 0. The returned snapshot has the copied flag set. */ -static Snapshot +Snapshot CopySnapshot(Snapshot snapshot) { Snapshot newsnap; diff --git a/src/include/utils/snapmgr.h b/src/include/utils/snapmgr.h index 6ccb44fd5b..737b3c7117 100644 --- a/src/include/utils/snapmgr.h +++ b/src/include/utils/snapmgr.h @@ -26,6 +26,7 @@ extern TransactionId RecentGlobalXmin; extern Snapshot GetTransactionSnapshot(void); extern Snapshot GetLatestSnapshot(void); extern void SnapshotSetCommandId(CommandId curcid); +extern Snapshot CopySnapshot(Snapshot snapshot); extern void PushActiveSnapshot(Snapshot snapshot); extern void PushUpdatedSnapshot(Snapshot snapshot); diff --git a/src/test/regress/expected/portals.out b/src/test/regress/expected/portals.out index 95dcea5a1d..be7348d6b2 100644 --- a/src/test/regress/expected/portals.out +++ b/src/test/regress/expected/portals.out @@ -1242,3 +1242,18 @@ FETCH FROM c1; DELETE FROM ucview WHERE CURRENT OF c1; -- fail, views not supported ERROR: WHERE CURRENT OF on a view is not implemented ROLLBACK; +-- Make sure snapshot management works okay, per bug report in +BEGIN; +SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; +CREATE TABLE cursor (a int); +INSERT INTO cursor VALUES (1); +DECLARE c1 NO SCROLL CURSOR FOR SELECT * FROM cursor FOR UPDATE; +UPDATE cursor SET a = 2; +FETCH ALL FROM c1; + a +--- +(0 rows) + +COMMIT; +DROP TABLE cursor; diff --git a/src/test/regress/sql/portals.sql b/src/test/regress/sql/portals.sql index 4265aaa43c..585a7c25ea 100644 --- a/src/test/regress/sql/portals.sql +++ b/src/test/regress/sql/portals.sql @@ -458,3 +458,15 @@ DECLARE c1 CURSOR FOR SELECT * FROM ucview; FETCH FROM c1; DELETE FROM ucview WHERE CURRENT OF c1; -- fail, views not supported ROLLBACK; + +-- Make sure snapshot management works okay, per bug report in +BEGIN; +SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; +CREATE TABLE cursor (a int); +INSERT INTO cursor VALUES (1); +DECLARE c1 NO SCROLL CURSOR FOR SELECT * FROM cursor FOR UPDATE; +UPDATE cursor SET a = 2; +FETCH ALL FROM c1; +COMMIT; +DROP TABLE cursor; |