From caa4cfa3697472a6673eb817eb34681684cba14f Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Fri, 2 Oct 2009 17:57:30 +0000
Subject: [PATCH] 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.
---
 src/backend/commands/portalcmds.c     | 13 +++++++++++--
 src/backend/executor/spi.c            |  7 ++-----
 src/backend/utils/time/snapmgr.c      |  5 ++---
 src/include/utils/snapmgr.h           |  3 ++-
 src/test/regress/expected/portals.out | 15 +++++++++++++++
 src/test/regress/sql/portals.sql      | 12 ++++++++++++
 6 files changed, 44 insertions(+), 11 deletions(-)

diff --git a/src/backend/commands/portalcmds.c b/src/backend/commands/portalcmds.c
index 17192341739..3ffdbff4695 100644
--- a/src/backend/commands/portalcmds.c
+++ b/src/backend/commands/portalcmds.c
@@ -14,7 +14,7 @@
  *
  *
  * 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;
 	Portal		portal;
 	MemoryContext oldContext;
+	Snapshot	snapshot;
 
 	if (cstmt == NULL || !IsA(cstmt, DeclareCursorStmt))
 		elog(ERROR, "PerformCursorOpen called for non-cursor query");
@@ -118,10 +119,18 @@ PerformCursorOpen(PlannedStmt *stmt, ParamListInfo params,
 			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.
 	 */
-	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 40f43ee011b..8fc45360414 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -8,7 +8,7 @@
  *
  *
  * 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.	(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 73d597d7b37..f8531276a47 100644
--- a/src/backend/utils/time/snapmgr.c
+++ b/src/backend/utils/time/snapmgr.c
@@ -19,7 +19,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * 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 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 979a52c6224..a82e53bb17a 100644
--- a/src/include/utils/snapmgr.h
+++ b/src/include/utils/snapmgr.h
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * 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 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 95dcea5a1d9..be7348d6b2a 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
+-- 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;
diff --git a/src/test/regress/sql/portals.sql b/src/test/regress/sql/portals.sql
index 4265aaa43cf..585a7c25ea7 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
+-- 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;
-- 
GitLab