From 3576820e7852ab20d552fe5aa7abb2847db62f6f Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Thu, 14 Feb 2002 15:24:10 +0000 Subject: [PATCH] Ensure that a cursor is scanned under the same scanCommandId it was originally created with, so that the set of visible tuples does not change as a result of other activity. This essentially makes PG cursors INSENSITIVE per the SQL92 definition. See bug report of 13-Feb-02. --- src/backend/commands/command.c | 18 +++++++++++++++-- src/backend/executor/spi.c | 31 +++++++++++++++++++++++------- src/backend/utils/mmgr/portalmem.c | 4 +++- src/include/utils/portal.h | 8 +++++--- 4 files changed, 48 insertions(+), 13 deletions(-) diff --git a/src/backend/commands/command.c b/src/backend/commands/command.c index 646511eb18..9b1a33673c 100644 --- a/src/backend/commands/command.c +++ b/src/backend/commands/command.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.152 2002/01/03 23:19:30 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.153 2002/02/14 15:24:06 tgl Exp $ * * NOTES * The PerformAddAttribute() code, like most of the relation @@ -103,6 +103,7 @@ PerformPortalFetch(char *name, QueryDesc *queryDesc; EState *estate; MemoryContext oldcontext; + CommandId savedId; bool temp_desc = false; /* @@ -156,7 +157,7 @@ PerformPortalFetch(char *name, } /* - * tell the destination to prepare to receive some tuples. + * Tell the destination to prepare to receive some tuples. */ BeginCommand(name, queryDesc->operation, @@ -168,6 +169,14 @@ PerformPortalFetch(char *name, tag, queryDesc->dest); + /* + * Restore the scanCommandId that was current when the cursor was + * opened. This ensures that we see the same tuples throughout the + * execution of the cursor. + */ + savedId = GetScanCommandId(); + SetScanCommandId(PortalGetCommandId(portal)); + /* * Determine which direction to go in, and check to see if we're * already at the end of the available tuples in that direction. If @@ -214,6 +223,11 @@ PerformPortalFetch(char *name, } } + /* + * Restore outer command ID. + */ + SetScanCommandId(savedId); + /* * Clean up and switch back to old context. */ diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c index 064b025522..b9ab642265 100644 --- a/src/backend/executor/spi.c +++ b/src/backend/executor/spi.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.64 2002/01/03 20:30:47 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.65 2002/02/14 15:24:08 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -740,9 +740,9 @@ SPI_cursor_open(char *name, void *plan, Datum *Values, char *Nulls) _SPI_current->processed = 0; _SPI_current->tuptable = NULL; - /* Make up a portal name if none given */ if (name == NULL) { + /* Make up a portal name if none given */ for (;;) { unnamed_portal_count++; @@ -755,11 +755,13 @@ SPI_cursor_open(char *name, void *plan, Datum *Values, char *Nulls) name = portalname; } - - /* Ensure the portal doesn't exist already */ - portal = GetPortalByName(name); - if (portal != NULL) - elog(ERROR, "cursor \"%s\" already in use", name); + else + { + /* Ensure the portal doesn't exist already */ + portal = GetPortalByName(name); + if (portal != NULL) + elog(ERROR, "cursor \"%s\" already in use", name); + } /* Create the portal */ portal = CreatePortal(name); @@ -1228,6 +1230,7 @@ _SPI_cursor_operation(Portal portal, bool forward, int count, QueryDesc *querydesc; EState *estate; MemoryContext oldcontext; + CommandId savedId; CommandDest olddest; /* Check that the portal is valid */ @@ -1245,6 +1248,7 @@ _SPI_cursor_operation(Portal portal, bool forward, int count, /* Switch to the portals memory context */ oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal)); + querydesc = PortalGetQueryDesc(portal); estate = PortalGetState(portal); @@ -1253,6 +1257,14 @@ _SPI_cursor_operation(Portal portal, bool forward, int count, olddest = querydesc->dest; querydesc->dest = dest; + /* + * Restore the scanCommandId that was current when the cursor was + * opened. This ensures that we see the same tuples throughout the + * execution of the cursor. + */ + savedId = GetScanCommandId(); + SetScanCommandId(PortalGetCommandId(portal)); + /* Run the executor like PerformPortalFetch and remember states */ if (forward) { @@ -1279,6 +1291,11 @@ _SPI_cursor_operation(Portal portal, bool forward, int count, } } + /* + * Restore outer command ID. + */ + SetScanCommandId(savedId); + /* Restore the old command destination and switch back to callers */ /* memory context */ querydesc->dest = olddest; diff --git a/src/backend/utils/mmgr/portalmem.c b/src/backend/utils/mmgr/portalmem.c index 25171ea7be..7682752922 100644 --- a/src/backend/utils/mmgr/portalmem.c +++ b/src/backend/utils/mmgr/portalmem.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/mmgr/portalmem.c,v 1.44 2001/10/25 05:49:51 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/mmgr/portalmem.c,v 1.45 2002/02/14 15:24:09 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -168,6 +168,7 @@ PortalSetQuery(Portal portal, portal->queryDesc = queryDesc; portal->attinfo = attinfo; + portal->commandId = GetScanCommandId(); portal->state = state; portal->atStart = true; /* Allow fetch forward only */ portal->atEnd = false; @@ -213,6 +214,7 @@ CreatePortal(char *name) /* initialize portal query */ portal->queryDesc = NULL; portal->attinfo = NULL; + portal->commandId = 0; portal->state = NULL; portal->atStart = true; /* disallow fetches until query is set */ portal->atEnd = true; diff --git a/src/include/utils/portal.h b/src/include/utils/portal.h index 395409ad20..9198b21feb 100644 --- a/src/include/utils/portal.h +++ b/src/include/utils/portal.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: portal.h,v 1.31 2001/11/05 17:46:36 momjian Exp $ + * $Id: portal.h,v 1.32 2002/02/14 15:24:10 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -31,6 +31,7 @@ typedef struct PortalData MemoryContext heap; /* subsidiary memory */ QueryDesc *queryDesc; /* Info about query associated with portal */ TupleDesc attinfo; + CommandId commandId; /* Command counter value for query */ EState *state; /* Execution state of query */ bool atStart; /* T => fetch backwards is not allowed */ bool atEnd; /* T => fetch forwards is not allowed */ @@ -48,8 +49,9 @@ typedef struct PortalData */ #define PortalGetQueryDesc(portal) ((portal)->queryDesc) #define PortalGetTupleDesc(portal) ((portal)->attinfo) -#define PortalGetState(portal) ((portal)->state) -#define PortalGetHeapMemory(portal) ((portal)->heap) +#define PortalGetCommandId(portal) ((portal)->commandId) +#define PortalGetState(portal) ((portal)->state) +#define PortalGetHeapMemory(portal) ((portal)->heap) /* * estimate of the maximum number of open portals a user would have,