summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/backend/utils/mmgr/portalmem.c44
-rw-r--r--src/include/utils/portal.h5
-rw-r--r--src/pl/plpgsql/src/pl_exec.c16
3 files changed, 58 insertions, 7 deletions
diff --git a/src/backend/utils/mmgr/portalmem.c b/src/backend/utils/mmgr/portalmem.c
index 00bf3e23d23..00c4cf6a108 100644
--- a/src/backend/utils/mmgr/portalmem.c
+++ b/src/backend/utils/mmgr/portalmem.c
@@ -12,7 +12,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/mmgr/portalmem.c,v 1.62 2003/08/24 21:02:43 petere Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/mmgr/portalmem.c,v 1.62.2.1 2010/07/05 09:27:56 heikki Exp $
*
*-------------------------------------------------------------------------
*/
@@ -289,6 +289,27 @@ PortalCreateHoldStore(Portal portal)
}
/*
+ * PinPortal
+ * Protect a portal from dropping.
+ */
+void
+PinPortal(Portal portal)
+{
+ if (!portal->portalReady)
+ elog(ERROR, "cannot pin portal that's not in ready state");
+
+ portal->portalPinned = true;
+}
+
+void
+UnpinPortal(Portal portal)
+{
+ if (!portal->portalPinned)
+ elog(ERROR, "portal not pinned");
+ portal->portalPinned = false;
+}
+
+/*
* PortalDrop
* Destroy the portal.
*
@@ -300,9 +321,17 @@ PortalDrop(Portal portal, bool isError)
{
AssertArg(PortalIsValid(portal));
- /* Not sure if this case can validly happen or not... */
- if (portal->portalActive)
- elog(ERROR, "cannot drop active portal");
+ /*
+ * Don't allow dropping a pinned portal, it's still needed by whoever
+ * pinned it. Unless we're doing post-abort cleanup; whoever pinned the
+ * portal is going to go away at transaction abort anyway.
+ *
+ * Not sure if the portalActive case can validly happen or not...
+ */
+ if ((portal->portalPinned && !isError) || portal->portalActive)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_CURSOR_STATE),
+ errmsg("cannot drop active portal \"%s\"", portal->name)));
/*
* Remove portal from hash table. Because we do this first, we will
@@ -400,6 +429,13 @@ AtCommit_Portals(void)
if (portal->portalActive)
continue;
+ /*
+ * There should be no pinned portals anymore. Complain if someone
+ * leaked one.
+ */
+ if (portal->portalPinned)
+ elog(ERROR, "cannot commit while a portal is pinned");
+
if (portal->cursorOptions & CURSOR_OPT_HOLD)
{
/*
diff --git a/src/include/utils/portal.h b/src/include/utils/portal.h
index 3faf3ec562f..2006c4ce970 100644
--- a/src/include/utils/portal.h
+++ b/src/include/utils/portal.h
@@ -39,7 +39,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: portal.h,v 1.47 2003/08/08 21:42:55 momjian Exp $
+ * $Id: portal.h,v 1.47.4.1 2010/07/05 09:27:56 heikki Exp $
*
*-------------------------------------------------------------------------
*/
@@ -117,6 +117,7 @@ typedef struct PortalData
bool portalUtilReady; /* PortalRunUtility complete? */
bool portalActive; /* portal is running (can't delete it) */
bool portalDone; /* portal is finished (don't re-run it) */
+ bool portalPinned; /* portal is pinned (can't delete it) */
/* If not NULL, Executor is active; call ExecutorEnd eventually: */
QueryDesc *queryDesc; /* info needed for executor invocation */
@@ -169,6 +170,8 @@ extern void AtAbort_Portals(void);
extern void AtCleanup_Portals(void);
extern Portal CreatePortal(const char *name, bool allowDup, bool dupSilent);
extern Portal CreateNewPortal(void);
+extern void PinPortal(Portal portal);
+extern void UnpinPortal(Portal portal);
extern void PortalDrop(Portal portal, bool isError);
extern void DropDependentPortals(MemoryContext queryContext);
extern Portal GetPortalByName(const char *name);
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 8cba7101a37..c58fba0b568 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -3,7 +3,7 @@
* procedural language
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.93.2.3 2007/02/08 18:38:19 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.93.2.4 2010/07/05 09:27:57 heikki Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@@ -1384,9 +1384,11 @@ exec_stmt_fors(PLpgSQL_execstate * estate, PLpgSQL_stmt_fors * stmt)
/*
* Open the implicit cursor for the statement and fetch the initial 10
- * rows.
+ * rows. Pin the portal to make sure it doesn't get closed by the user
+ * statements we execute.
*/
exec_run_select(estate, stmt->query, 0, &portal);
+ PinPortal(portal);
SPI_cursor_fetch(portal, true, 10);
tuptab = SPI_tuptable;
@@ -1425,6 +1427,7 @@ exec_stmt_fors(PLpgSQL_execstate * estate, PLpgSQL_stmt_fors * stmt)
* (This code should match the code after the loop.)
*/
SPI_freetuptable(tuptab);
+ UnpinPortal(portal);
SPI_cursor_close(portal);
exec_set_found(estate, found);
@@ -1471,6 +1474,7 @@ exec_stmt_fors(PLpgSQL_execstate * estate, PLpgSQL_stmt_fors * stmt)
/*
* Close the implicit cursor
*/
+ UnpinPortal(portal);
SPI_cursor_close(portal);
/*
@@ -2228,6 +2232,12 @@ exec_stmt_dynfors(PLpgSQL_execstate * estate, PLpgSQL_stmt_dynfors * stmt)
SPI_freeplan(plan);
/*
+ * Make sure the portal doesn't get closed by the user statements
+ * we execute.
+ */
+ PinPortal(portal);
+
+ /*
* Fetch the initial 10 tuples
*/
SPI_cursor_fetch(portal, true, 10);
@@ -2267,6 +2277,7 @@ exec_stmt_dynfors(PLpgSQL_execstate * estate, PLpgSQL_stmt_dynfors * stmt)
* (This code should match the code after the loop.)
*/
SPI_freetuptable(tuptab);
+ UnpinPortal(portal);
SPI_cursor_close(portal);
exec_set_found(estate, found);
@@ -2313,6 +2324,7 @@ exec_stmt_dynfors(PLpgSQL_execstate * estate, PLpgSQL_stmt_dynfors * stmt)
/*
* Close the implicit cursor
*/
+ UnpinPortal(portal);
SPI_cursor_close(portal);
/*