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.
This commit is contained in:
Alvaro Herrera 2009-10-02 17:57:30 +00:00
parent fabf75cffc
commit caa4cfa369
6 changed files with 44 additions and 11 deletions

View File

@ -14,7 +14,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/portalcmds.c,v 1.79 2009/06/11 14:48:56 momjian Exp $ * $PostgreSQL: pgsql/src/backend/commands/portalcmds.c,v 1.80 2009/10/02 17:57:29 alvherre Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -47,6 +47,7 @@ PerformCursorOpen(PlannedStmt *stmt, ParamListInfo params,
DeclareCursorStmt *cstmt = (DeclareCursorStmt *) stmt->utilityStmt; DeclareCursorStmt *cstmt = (DeclareCursorStmt *) stmt->utilityStmt;
Portal portal; Portal portal;
MemoryContext oldContext; MemoryContext oldContext;
Snapshot snapshot;
if (cstmt == NULL || !IsA(cstmt, DeclareCursorStmt)) if (cstmt == NULL || !IsA(cstmt, DeclareCursorStmt))
elog(ERROR, "PerformCursorOpen called for non-cursor query"); elog(ERROR, "PerformCursorOpen called for non-cursor query");
@ -118,10 +119,18 @@ PerformCursorOpen(PlannedStmt *stmt, ParamListInfo params,
portal->cursorOptions |= CURSOR_OPT_NO_SCROLL; portal->cursorOptions |= CURSOR_OPT_NO_SCROLL;
} }
/*
* 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. * Start execution, inserting parameters if any.
*/ */
PortalStart(portal, params, GetActiveSnapshot()); PortalStart(portal, params, snapshot);
Assert(portal->strategy == PORTAL_ONE_SELECT); Assert(portal->strategy == PORTAL_ONE_SELECT);

View File

@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.208 2009/06/11 14:48:57 momjian Exp $ * $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.209 2009/10/02 17:57:30 alvherre Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -1211,10 +1211,7 @@ SPI_cursor_open_internal(const char *name, SPIPlanPtr plan,
} }
} }
/* /* Set up the snapshot to use. */
* Set up the snapshot to use. (PortalStart will do PushActiveSnapshot,
* so we skip that here.)
*/
if (read_only) if (read_only)
snapshot = GetActiveSnapshot(); snapshot = GetActiveSnapshot();
else else

View File

@ -19,7 +19,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/time/snapmgr.c,v 1.10 2009/06/11 14:49:06 momjian Exp $ * $PostgreSQL: pgsql/src/backend/utils/time/snapmgr.c,v 1.11 2009/10/02 17:57:30 alvherre Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -104,7 +104,6 @@ bool FirstSnapshotSet = false;
static bool registered_serializable = false; static bool registered_serializable = false;
static Snapshot CopySnapshot(Snapshot snapshot);
static void FreeSnapshot(Snapshot snapshot); static void FreeSnapshot(Snapshot snapshot);
static void SnapshotResetXmin(void); static void SnapshotResetXmin(void);
@ -192,7 +191,7 @@ SnapshotSetCommandId(CommandId curcid)
* The copy is palloc'd in TopTransactionContext and has initial refcounts set * The copy is palloc'd in TopTransactionContext and has initial refcounts set
* to 0. The returned snapshot has the copied flag set. * to 0. The returned snapshot has the copied flag set.
*/ */
static Snapshot Snapshot
CopySnapshot(Snapshot snapshot) CopySnapshot(Snapshot snapshot)
{ {
Snapshot newsnap; Snapshot newsnap;

View File

@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/utils/snapmgr.h,v 1.5 2009/06/11 14:49:13 momjian Exp $ * $PostgreSQL: pgsql/src/include/utils/snapmgr.h,v 1.6 2009/10/02 17:57:30 alvherre Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -26,6 +26,7 @@ extern TransactionId RecentGlobalXmin;
extern Snapshot GetTransactionSnapshot(void); extern Snapshot GetTransactionSnapshot(void);
extern Snapshot GetLatestSnapshot(void); extern Snapshot GetLatestSnapshot(void);
extern void SnapshotSetCommandId(CommandId curcid); extern void SnapshotSetCommandId(CommandId curcid);
extern Snapshot CopySnapshot(Snapshot snapshot);
extern void PushActiveSnapshot(Snapshot snapshot); extern void PushActiveSnapshot(Snapshot snapshot);
extern void PushUpdatedSnapshot(Snapshot snapshot); extern void PushUpdatedSnapshot(Snapshot snapshot);

View File

@ -1242,3 +1242,18 @@ FETCH FROM c1;
DELETE FROM ucview WHERE CURRENT OF c1; -- fail, views not supported DELETE FROM ucview WHERE CURRENT OF c1; -- fail, views not supported
ERROR: WHERE CURRENT OF on a view is not implemented ERROR: WHERE CURRENT OF on a view is not implemented
ROLLBACK; ROLLBACK;
-- Make sure snapshot management works okay, per bug report in
-- 235395b90909301035v7228ce63q392931f15aa74b31@mail.gmail.com
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;

View File

@ -458,3 +458,15 @@ DECLARE c1 CURSOR FOR SELECT * FROM ucview;
FETCH FROM c1; FETCH FROM c1;
DELETE FROM ucview WHERE CURRENT OF c1; -- fail, views not supported DELETE FROM ucview WHERE CURRENT OF c1; -- fail, views not supported
ROLLBACK; ROLLBACK;
-- Make sure snapshot management works okay, per bug report in
-- 235395b90909301035v7228ce63q392931f15aa74b31@mail.gmail.com
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;