Code review for holdable-cursors patch. Fix error recovery, memory
authorTom Lane
Tue, 29 Apr 2003 03:21:30 +0000 (03:21 +0000)
committerTom Lane
Tue, 29 Apr 2003 03:21:30 +0000 (03:21 +0000)
context sloppiness, some other things.  Includes Neil's mopup patch
of 22-Apr.

18 files changed:
doc/src/sgml/ref/declare.sgml
src/backend/catalog/sql_features.txt
src/backend/commands/portalcmds.c
src/backend/executor/spi.c
src/backend/executor/tstoreReceiver.c
src/backend/parser/analyze.c
src/backend/parser/gram.y
src/backend/storage/file/buffile.c
src/backend/storage/file/fd.c
src/backend/utils/mmgr/portalmem.c
src/backend/utils/sort/tuplestore.c
src/include/commands/portalcmds.h
src/include/storage/buffile.h
src/include/storage/fd.h
src/include/utils/portal.h
src/include/utils/tuplestore.h
src/test/regress/expected/portals.out
src/test/regress/sql/portals.sql

index 9658c7b7da0fff4492b4bc0b487a698b3672e22d..49d380e238b1c30583a5c75f7c0ca19d13349111 100644 (file)
@@ -1,5 +1,5 @@
 
 
@@ -70,8 +70,8 @@ DECLARE cursorname [ BINARY ] [ INS
       NO SCROLL
       
        
-    Specifies that the cursor cannot be used to retrieve rows in a
-    nonsequential fashion (e.g., backward).
+        Specifies that the cursor cannot be used to retrieve rows in a
+        nonsequential fashion (e.g., backward).
        
       
      
@@ -83,7 +83,7 @@ DECLARE cursorname [ BINARY ] [ INS
    Specifies that the cursor may be used to retrieve rows in a
    nonsequential fashion (e.g., backward). Depending upon the
    complexity of the query's execution plan, specifying
-   SCROLL may impose a slight performance penalty
+   SCROLL may impose a performance penalty
    on the query's execution time.
        
       
@@ -96,7 +96,7 @@ DECLARE cursorname [ BINARY ] [ INS
         Specifies that the cursor cannot be used outside of the
         transaction that created it. If neither WITHOUT
         HOLD nor WITH HOLD is specified,
-        WITH HOLD is the default.
+        WITHOUT HOLD is the default.
        
       
      
@@ -105,8 +105,8 @@ DECLARE cursorname [ BINARY ] [ INS
       WITH HOLD
       
        
-        Specifies that the cursor may be used after the transaction
-        that creates it successfully commits.
+        Specifies that the cursor may continue to be used after the
+   transaction that creates it successfully commits.
        
       
      
@@ -163,7 +163,7 @@ DECLARE cursorname [ BINARY ] [ INS
 
    
     The BINARYINSENSITIVE,
-    SCROLL keywords may appear in any order.
+    and SCROLL keywords may appear in any order.
    
   
 
@@ -296,11 +296,14 @@ ERROR:  DECLARE CURSOR may only be used in begin/end transaction blocks
    
     If WITH HOLD is specified and the transaction
     that created the cursor successfully commits, the cursor can be
-    accessed outside the creating transaction. If the creating
-    transaction is aborted, the cursor is removed. A cursor created
+    continue to be accessed by subsequent transactions in the same session.
+    (But if the creating
+    transaction is aborted, the cursor is removed.) A cursor created
     with WITH HOLD is closed when an explicit
-    CLOSE command is issued on it, or the client
-    connection is terminated.
+    CLOSE command is issued on it, or when the client
+    connection is terminated.  In the current implementation, the rows
+    represented by a held cursor are copied into a temporary file or
+    memory area so that they remain available for subsequent transactions.
    
 
    
@@ -312,7 +315,8 @@ ERROR:  DECLARE CURSOR may only be used in begin/end transaction blocks
     plan is simple enough that no extra overhead is needed to support
     it. However, application developers are advised not to rely on
     using backward fetches from a cursor that has not been created
-    with SCROLL.
+    with SCROLL.  If NO SCROLL is specified,
+    then backward fetches are disallowed in any case.
    
 
    
index 8c1f0bbf639e321be775bcdf8d6db8367e248147..33c80918f5868680c3baf0232c420eac31022acd 100644 (file)
@@ -95,7 +95,7 @@ E121  Basic cursor support    06  Positioned UPDATE statement NO
 E121   Basic cursor support    07  Positioned DELETE statement NO  
 E121   Basic cursor support    08  CLOSE statement YES 
 E121   Basic cursor support    10  FETCH statement implicit NEXT   YES 
-E121   Basic cursor support    17  WITH HOLD cursors   NO  
+E121   Basic cursor support    17  WITH HOLD cursors   YES 
 E131   Null value support (nulls in lieu of values)            YES 
 E141   Basic integrity constraints         YES 
 E141   Basic integrity constraints 01  NOT NULL constraints    YES 
@@ -214,12 +214,12 @@ F401  Extended joined table   03  UNION JOIN  YES
 F401   Extended joined table   04  CROSS JOIN  YES 
 F411   Time zone specification         YES 
 F421   National character          YES 
-F431   Read-only scrollable cursors            NO  
+F431   Read-only scrollable cursors            YES 
 F431   Read-only scrollable cursors    01  FETCH with explicit NEXT    YES 
-F431   Read-only scrollable cursors    02  FETCH FIRST NO  
-F431   Read-only scrollable cursors    03  FETCH LAST  NO  
+F431   Read-only scrollable cursors    02  FETCH FIRST YES 
+F431   Read-only scrollable cursors    03  FETCH LAST  YES 
 F431   Read-only scrollable cursors    04  FETCH PRIOR YES 
-F431   Read-only scrollable cursors    05  FETCH ABSOLUTE  NO  
+F431   Read-only scrollable cursors    05  FETCH ABSOLUTE  YES 
 F431   Read-only scrollable cursors    06  FETCH RELATIVE  YES 
 F441   Extended set function support           YES 
 F451   Character set definition            NO  
@@ -319,7 +319,7 @@ T211    Basic trigger capability    04  FOR EACH ROW triggers   YES
 T211   Basic trigger capability    05  Ability to specify a search condition that must be true before the trigger is invoked   NO  
 T211   Basic trigger capability    06  Support for run-time rules for the interaction of triggers and constraints  NO  
 T211   Basic trigger capability    07  TRIGGER privilege   YES 
-T211   Basic trigger capability    08  Multiple triggers for the same the event are executed in the order in which they were created   NO  
+T211   Basic trigger capability    08  Multiple triggers for the same event are executed in the order in which they were created   NO  intentionally omitted
 T212   Enhanced trigger capability         YES 
 T231   SENSITIVE cursors           YES 
 T241   START TRANSACTION statement         YES 
index 7eabc58d49599c55cef10035f068cf0c567027f6..9dc7583f06c70bbaa1eab90e4895cbcacefcdef4 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/commands/portalcmds.c,v 1.11 2003/03/27 16:51:27 momjian Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/commands/portalcmds.c,v 1.12 2003/04/29 03:21:29 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "rewrite/rewriteHandler.h"
 #include "utils/memutils.h"
 
+
 static long DoRelativeFetch(Portal portal,
                            bool forward,
                            long count,
                            CommandDest dest);
-static long DoRelativeStoreFetch(Portal portal,
-                                bool forward,
-                                long count,
-                                CommandDest dest);
+static uint32 RunFromStore(Portal portal, ScanDirection direction, long count);
 static void DoPortalRewind(Portal portal);
 static Portal PreparePortal(DeclareCursorStmt *stmt);
 
@@ -52,11 +50,9 @@ PerformCursorOpen(DeclareCursorStmt *stmt, CommandDest dest)
    QueryDesc  *queryDesc;
 
    /*
-    * If this is a non-holdable cursor, we ensure that this statement
+    * If this is a non-holdable cursor, we require that this statement
     * has been executed inside a transaction block (or else, it would
     * have no user-visible effect).
-    *
-    * XXX: surely there is a better way to check this?
     */
    if (!(stmt->options & CURSOR_OPT_HOLD))
        RequireTransactionChain((void *) stmt, "DECLARE CURSOR");
@@ -106,7 +102,7 @@ PerformCursorOpen(DeclareCursorStmt *stmt, CommandDest dest)
    ExecutorStart(queryDesc);
 
    /* Arrange to shut down the executor if portal is dropped */
-   PortalSetQuery(portal, queryDesc, PortalCleanup);
+   PortalSetQuery(portal, queryDesc);
 
    /*
     * We're done; the query won't actually be run until PerformPortalFetch
@@ -352,15 +348,11 @@ DoRelativeFetch(Portal portal,
                CommandDest dest)
 {
    QueryDesc  *queryDesc;
-   EState     *estate;
-   ScanDirection direction;
    QueryDesc   temp_queryDesc;
-
-   if (portal->holdStore)
-       return DoRelativeStoreFetch(portal, forward, count, dest);
+   ScanDirection direction;
+   uint32      nprocessed;
 
    queryDesc = PortalGetQueryDesc(portal);
-   estate = queryDesc->estate;
 
    /*
     * If the requested destination is not the same as the query's
@@ -403,19 +395,26 @@ DoRelativeFetch(Portal portal,
        if (count == FETCH_ALL)
            count = 0;
 
-       ExecutorRun(queryDesc, direction, count);
+       if (portal->holdStore)
+           nprocessed = RunFromStore(portal, direction, count);
+       else
+       {
+           Assert(portal->executorRunning);
+           ExecutorRun(queryDesc, direction, count);
+           nprocessed = queryDesc->estate->es_processed;
+       }
 
        if (direction != NoMovementScanDirection)
        {
            long    oldPos;
 
-           if (estate->es_processed > 0)
+           if (nprocessed > 0)
                portal->atStart = false;    /* OK to go backward now */
            if (count == 0 ||
-               (unsigned long) estate->es_processed < (unsigned long) count)
+               (unsigned long) nprocessed < (unsigned long) count)
                portal->atEnd = true;       /* we retrieved 'em all */
            oldPos = portal->portalPos;
-           portal->portalPos += estate->es_processed;
+           portal->portalPos += nprocessed;
            /* portalPos doesn't advance when we fall off the end */
            if (portal->portalPos < oldPos)
                portal->posOverflow = true;
@@ -436,17 +435,24 @@ DoRelativeFetch(Portal portal,
        if (count == FETCH_ALL)
            count = 0;
 
-       ExecutorRun(queryDesc, direction, count);
+       if (portal->holdStore)
+           nprocessed = RunFromStore(portal, direction, count);
+       else
+       {
+           Assert(portal->executorRunning);
+           ExecutorRun(queryDesc, direction, count);
+           nprocessed = queryDesc->estate->es_processed;
+       }
 
        if (direction != NoMovementScanDirection)
        {
-           if (estate->es_processed > 0 && portal->atEnd)
+           if (nprocessed > 0 && portal->atEnd)
            {
                portal->atEnd = false;      /* OK to go forward now */
                portal->portalPos++;        /* adjust for endpoint case */
            }
            if (count == 0 ||
-               (unsigned long) estate->es_processed < (unsigned long) count)
+               (unsigned long) nprocessed < (unsigned long) count)
            {
                portal->atStart = true;     /* we retrieved 'em all */
                portal->portalPos = 0;
@@ -457,7 +463,7 @@ DoRelativeFetch(Portal portal,
                long    oldPos;
 
                oldPos = portal->portalPos;
-               portal->portalPos -= estate->es_processed;
+               portal->portalPos -= nprocessed;
                if (portal->portalPos > oldPos ||
                    portal->portalPos <= 0)
                    portal->posOverflow = true;
@@ -465,76 +471,75 @@ DoRelativeFetch(Portal portal,
        }
    }
 
-   return estate->es_processed;
+   return nprocessed;
 }
 
 /*
- * DoRelativeStoreFetch
- *     Do fetch for a simple N-rows-forward-or-backward case, getting
- *     the results from the portal's tuple store.
+ * RunFromStore
+ *     Fetch tuples from the portal's tuple store.
+ *
+ * Calling conventions are similar to ExecutorRun, except that we
+ * do not depend on having an estate, and therefore return the number
+ * of tuples processed as the result, not in estate->es_processed.
+ *
+ * One difference from ExecutorRun is that the destination receiver functions
+ * are run in the caller's memory context (since we have no estate).  Watch
+ * out for memory leaks.
  */
-static long
-DoRelativeStoreFetch(Portal portal,
-                    bool forward,
-                    long count,
-                    CommandDest dest)
+static uint32
+RunFromStore(Portal portal, ScanDirection direction, long count)
 {
+   QueryDesc *queryDesc = PortalGetQueryDesc(portal);
    DestReceiver *destfunc;
-   QueryDesc *queryDesc = portal->queryDesc;
-   long rows_fetched = 0;
+   long        current_tuple_count = 0;
 
-   if (!forward && portal->scrollType == DISABLE_SCROLL)
-           elog(ERROR, "Cursor can only scan forward"
-                "\n\tDeclare it with SCROLL option to enable backward scan");
-
-   destfunc = DestToFunction(dest);
+   destfunc = DestToFunction(queryDesc->dest);
    (*destfunc->setup) (destfunc, queryDesc->operation,
-                       portal->name, queryDesc->tupDesc);
+                       queryDesc->portalName, queryDesc->tupDesc);
 
-   for (;;)
+   if (direction == NoMovementScanDirection)
    {
-       HeapTuple tup;
-       bool should_free;
+       /* do nothing except start/stop the destination */
+   }
+   else
+   {
+       bool    forward = (direction == ForwardScanDirection);
 
-       if (rows_fetched >= count)
-           break;
-       if (portal->atEnd && forward)
-           break;
-       if (portal->atStart && !forward)
-           break;
+       for (;;)
+       {
+           MemoryContext oldcontext;
+           HeapTuple tup;
+           bool should_free;
 
-       tup = tuplestore_getheaptuple(portal->holdStore, forward, &should_free);
+           oldcontext = MemoryContextSwitchTo(portal->holdContext);
 
-       if (tup == NULL)
-       {
-           if (forward)
-               portal->atEnd = true;
-           else
-               portal->atStart = true;
+           tup = tuplestore_getheaptuple(portal->holdStore, forward,
+                                         &should_free);
 
-           break;
-       }
+           MemoryContextSwitchTo(oldcontext);
 
-       (*destfunc->receiveTuple) (tup, queryDesc->tupDesc, destfunc);
+           if (tup == NULL)
+               break;
 
-       rows_fetched++;
-       if (forward)
-           portal->portalPos++;
-       else
-           portal->portalPos--;
+           (*destfunc->receiveTuple) (tup, queryDesc->tupDesc, destfunc);
 
-       if (forward && portal->atStart)
-           portal->atStart = false;
-       if (!forward && portal->atEnd)
-           portal->atEnd = false;
+           if (should_free)
+               pfree(tup);
 
-       if (should_free)
-           pfree(tup);
+           /*
+            * check our tuple count.. if we've processed the proper number
+            * then quit, else loop again and process more tuples.  Zero
+            * count means no limit.
+            */
+           current_tuple_count++;
+           if (count && count == current_tuple_count)
+               break;
+       }
    }
 
    (*destfunc->cleanup) (destfunc);
 
-   return rows_fetched;
+   return (uint32) current_tuple_count;
 }
 
 /*
@@ -544,9 +549,17 @@ static void
 DoPortalRewind(Portal portal)
 {
    if (portal->holdStore)
+   {
+       MemoryContext oldcontext;
+
+       oldcontext = MemoryContextSwitchTo(portal->holdContext);
        tuplestore_rescan(portal->holdStore);
-   else
+       MemoryContextSwitchTo(oldcontext);
+   }
+   if (portal->executorRunning)
+   {
        ExecutorRewind(PortalGetQueryDesc(portal));
+   }
 
    portal->atStart = true;
    portal->atEnd = false;
@@ -630,12 +643,11 @@ PreparePortal(DeclareCursorStmt *stmt)
 /*
  * PortalCleanup
  *
- * Clean up a portal when it's dropped.  Since this mainly exists to run
- * ExecutorEnd(), it should not be set as the cleanup hook until we have
- * called ExecutorStart() on the portal's query.
+ * Clean up a portal when it's dropped.  This is the standard cleanup hook
+ * for portals.
  */
 void
-PortalCleanup(Portal portal)
+PortalCleanup(Portal portal, bool isError)
 {
    /*
     * sanity checks
@@ -643,11 +655,31 @@ PortalCleanup(Portal portal)
    AssertArg(PortalIsValid(portal));
    AssertArg(portal->cleanup == PortalCleanup);
 
+   /*
+    * Delete tuplestore if present.  (Note: portalmem.c is responsible
+    * for removing holdContext.)  We should do this even under error
+    * conditions.
+    */
    if (portal->holdStore)
-       tuplestore_end(portal->holdStore);
-   else
-       ExecutorEnd(PortalGetQueryDesc(portal));
+   {
+       MemoryContext oldcontext;
 
+       oldcontext = MemoryContextSwitchTo(portal->holdContext);
+       tuplestore_end(portal->holdStore);
+       MemoryContextSwitchTo(oldcontext);
+       portal->holdStore = NULL;
+   }
+   /*
+    * Shut down executor, if still running.  We skip this during error
+    * abort, since other mechanisms will take care of releasing executor
+    * resources, and we can't be sure that ExecutorEnd itself wouldn't fail.
+    */
+   if (portal->executorRunning)
+   {
+       portal->executorRunning = false;
+       if (!isError)
+           ExecutorEnd(PortalGetQueryDesc(portal));
+   }
 }
 
 /*
@@ -661,8 +693,10 @@ PortalCleanup(Portal portal)
 void
 PersistHoldablePortal(Portal portal)
 {
-   MemoryContext oldcxt;
    QueryDesc *queryDesc = PortalGetQueryDesc(portal);
+   MemoryContext oldcxt;
+   CommandDest olddest;
+   TupleDesc   tupdesc;
 
    /*
     * If we're preserving a holdable portal, we had better be
@@ -672,17 +706,15 @@ PersistHoldablePortal(Portal portal)
    Assert(portal->holdStore == NULL);
 
    /*
-    * This context is used to store portal data that needs to persist
-    * between transactions.
+    * This context is used to store the tuple set.
+    * Caller must have created it already.
     */
+   Assert(portal->holdContext != NULL);
    oldcxt = MemoryContextSwitchTo(portal->holdContext);
 
    /* XXX: Should SortMem be used for this? */
    portal->holdStore = tuplestore_begin_heap(true, true, SortMem);
 
-   /* Set the destination to output to the tuplestore */
-   queryDesc->dest = Tuplestore;
-
    /*
     * Rewind the executor: we need to store the entire result set in
     * the tuplestore, so that subsequent backward FETCHs can be
@@ -690,8 +722,29 @@ PersistHoldablePortal(Portal portal)
     */
    ExecutorRewind(queryDesc);
 
+   /* Set the destination to output to the tuplestore */
+   olddest = queryDesc->dest;
+   queryDesc->dest = Tuplestore;
+
    /* Fetch the result set into the tuplestore */
-   ExecutorRun(queryDesc, ForwardScanDirection, 0);
+   ExecutorRun(queryDesc, ForwardScanDirection, 0L);
+
+   queryDesc->dest = olddest;
+
+   /*
+    * Before closing down the executor, we must copy the tupdesc, since
+    * it was created in executor memory.
+    */
+   tupdesc = CreateTupleDescCopy(queryDesc->tupDesc);
+
+   /*
+    * Now shut down the inner executor.
+    */
+   portal->executorRunning = false;
+   ExecutorEnd(queryDesc);
+
+   /* ExecutorEnd clears this, so must wait to save copied pointer */
+   queryDesc->tupDesc = tupdesc;
 
    /*
     * Reset the position in the result set: ideally, this could be
@@ -702,69 +755,26 @@ PersistHoldablePortal(Portal portal)
     */
    if (!portal->atEnd)
    {
-       int store_pos = 0;
-       bool should_free;
+       long    store_pos;
 
        tuplestore_rescan(portal->holdStore);
 
-       while (store_pos < portal->portalPos)
+       for (store_pos = 0; store_pos < portal->portalPos; store_pos++)
        {
-           HeapTuple tmp = tuplestore_gettuple(portal->holdStore,
-                                               true, &should_free);
+           HeapTuple tup;
+           bool should_free;
 
-           if (tmp == NULL)
+           tup = tuplestore_gettuple(portal->holdStore, true,
+                                     &should_free);
+
+           if (tup == NULL)
                elog(ERROR,
                     "PersistHoldablePortal: unexpected end of tuple stream");
 
-           store_pos++;
-
-           /*
-            * This could probably be optimized by creating and then
-            * deleting a separate memory context for this series of
-            * operations.
-            */
            if (should_free)
-               pfree(tmp);
+               pfree(tup);
        }
    }
 
-   /*
-    * The current Portal structure contains some data that will be
-    * needed by the holdable cursor, but it has been allocated in a
-    * memory context that is not sufficiently long-lived: we need to
-    * copy it into the portal's long-term memory context.
-    */
-   {
-       TupleDesc tupDescCopy;
-       QueryDesc *queryDescCopy;
-
-       /*
-        * We need to use this order as ExecutorEnd invalidates the
-        * queryDesc's tuple descriptor
-        */
-       tupDescCopy = CreateTupleDescCopy(queryDesc->tupDesc);
-
-       ExecutorEnd(queryDesc);
-
-       queryDescCopy = palloc(sizeof(*queryDescCopy));
-
-       /*
-        * This doesn't copy all the dependant data in the QueryDesc,
-        * but that's okay -- the only complex field we need to keep is
-        * the query's tupledesc, which we've copied ourselves.
-        */
-       memcpy(queryDescCopy, queryDesc, sizeof(*queryDesc));
-
-       FreeQueryDesc(queryDesc);
-
-       queryDescCopy->tupDesc = tupDescCopy;
-       portal->queryDesc = queryDescCopy;
-   }
-
-   /*
-    * We no longer need the portal's short-term memory context.
-    */
-   MemoryContextDelete(PortalGetHeapMemory(portal));
-
-   PortalGetHeapMemory(portal) = NULL;
+   MemoryContextSwitchTo(oldcxt);
 }
index 11ed2914de5f9502d39d278c7f99868394147c68..218b56a601315088f8d339fa44118c5fedca6f15 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.91 2003/04/27 20:09:43 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.92 2003/04/29 03:21:29 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -809,7 +809,7 @@ SPI_cursor_open(const char *name, void *plan, Datum *Values, const char *Nulls)
    ExecutorStart(queryDesc);
 
    /* Arrange to shut down the executor if portal is dropped */
-   PortalSetQuery(portal, queryDesc, PortalCleanup);
+   PortalSetQuery(portal, queryDesc);
 
    /* Switch back to the callers memory context */
    MemoryContextSwitchTo(oldcontext);
index fe82cc8c92c8c60b54d316fd8723012b91c207f4..01fffed8168168d6b31f9509ba38c411169b132c 100644 (file)
@@ -9,7 +9,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/executor/tstoreReceiver.c,v 1.1 2003/03/27 16:53:15 momjian Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/executor/tstoreReceiver.c,v 1.2 2003/04/29 03:21:29 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -27,8 +27,9 @@ typedef struct
    MemoryContext       cxt;
 } TStoreState;
 
+
 /*
- * Receive a tuple from the executor and store it in the tuplestore.
+ * Prepare to receive tuples from executor.
  *
  * XXX: As currently implemented, this routine is a hack: there should
  * be no tie between this code and the portal system. Instead, the
@@ -56,6 +57,9 @@ tstoreSetupReceiver(DestReceiver *self, int operation,
    myState->cxt = portal->holdContext;
 }
 
+/*
+ * Receive a tuple from the executor and store it in the tuplestore.
+ */
 static void
 tstoreReceiveTuple(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
 {
@@ -67,12 +71,18 @@ tstoreReceiveTuple(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
    MemoryContextSwitchTo(oldcxt);
 }
 
+/*
+ * Clean up
+ */
 static void
 tstoreCleanupReceiver(DestReceiver *self)
 {
-   /* do nothing */
+   /* do nothing */
 }
 
+/*
+ * Initially create a DestReceiver object.
+ */
 DestReceiver *
 tstoreReceiverCreateDR(void)
 {
index 64cd5603acdc549351d6896b69b5289bc025cf7a..5dc3d82bea8f695dfb01818356f02d0355854024 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.266 2003/03/27 16:51:28 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.267 2003/04/29 03:21:29 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -2299,7 +2299,7 @@ transformDeclareCursorStmt(ParseState *pstate, DeclareCursorStmt *stmt)
     */
    if ((stmt->options & CURSOR_OPT_SCROLL) &&
        (stmt->options & CURSOR_OPT_NO_SCROLL))
-       elog(ERROR, "Both SCROLL and NO SCROLL cannot be specified.");
+       elog(ERROR, "Cannot specify both SCROLL and NO SCROLL");
 
    stmt->query = (Node *) transformStmt(pstate, stmt->query,
                                         &extras_before, &extras_after);
index 5e98788765806f58e4306b9516c5adf6c34615b7..bd67dae811f720193c4dd6264f8b131011880015 100644 (file)
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.411 2003/04/08 23:20:01 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.412 2003/04/29 03:21:29 tgl Exp $
  *
  * HISTORY
  *   AUTHOR            DATE            MAJOR EVENT
@@ -4236,10 +4236,8 @@ DeclareCursorStmt: DECLARE name cursor_options CURSOR opt_hold FOR SelectStmt
                    n->portalname = $2;
                    n->options = $3;
                    n->query = $7;
-
                    if ($5)
                        n->options |= CURSOR_OPT_HOLD;
-
                    $$ = (Node *)n;
                }
        ;
@@ -7191,6 +7189,7 @@ unreserved_keyword:
            | FUNCTION
            | GLOBAL
            | HANDLER
+           | HOLD
            | HOUR_P
            | IMMEDIATE
            | IMMUTABLE
index 90b185cd5dec24169b6012980b5acccafe5443e2..e479bfe22b78fee19623e2fa5be87b03479a13d1 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/storage/file/buffile.c,v 1.15 2003/03/27 16:51:29 momjian Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/storage/file/buffile.c,v 1.16 2003/04/29 03:21:29 tgl Exp $
  *
  * NOTES:
  *
@@ -64,7 +64,7 @@ struct BufFile
     */
 
    bool        isTemp;         /* can only add files if this is TRUE */
-   bool        isInterTxn;     /* keep open over transactions? */
+   bool        isInterXact;    /* keep open over transactions? */
    bool        dirty;          /* does buffer need to be written? */
 
    /*
@@ -119,7 +119,7 @@ extendBufFile(BufFile *file)
    File        pfile;
 
    Assert(file->isTemp);
-   pfile = OpenTemporaryFile(file->isInterTxn);
+   pfile = OpenTemporaryFile(file->isInterXact);
    Assert(pfile >= 0);
 
    file->files = (File *) repalloc(file->files,
@@ -135,19 +135,22 @@ extendBufFile(BufFile *file)
  * Create a BufFile for a new temporary file (which will expand to become
  * multiple temporary files if more than MAX_PHYSICAL_FILESIZE bytes are
  * written to it).
+ *
+ * Note: if interXact is true, the caller had better be calling us in a
+ * memory context that will survive across transaction boundaries.
  */
 BufFile *
-BufFileCreateTemp(bool interTxn)
+BufFileCreateTemp(bool interXact)
 {
    BufFile    *file;
    File        pfile;
 
-   pfile = OpenTemporaryFile(interTxn);
+   pfile = OpenTemporaryFile(interXact);
    Assert(pfile >= 0);
 
    file = makeBufFile(pfile);
    file->isTemp = true;
-   file->isInterTxn = interTxn;
+   file->isInterXact = interXact;
 
    return file;
 }
index b84e2cb82d1c41a388cbb7c7ea5be7c944ac675e..5552a043399321faf2809e4894558cb65e1da81f 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/storage/file/fd.c,v 1.97 2003/04/04 20:42:12 momjian Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/storage/file/fd.c,v 1.98 2003/04/29 03:21:29 tgl Exp $
  *
  * NOTES:
  *
@@ -113,8 +113,8 @@ int         max_files_per_process = 1000;
 #define FileUnknownPos (-1L)
 
 /* these are the assigned bits in fdstate below: */
-#define FD_TEMPORARY       (1 << 0)
-#define FD_TXN_TEMPORARY   (1 << 1)
+#define FD_TEMPORARY       (1 << 0)        /* T = delete when closed */
+#define FD_XACT_TEMPORARY  (1 << 1)        /* T = delete at eoXact */
 
 typedef struct vfd
 {
@@ -156,7 +156,7 @@ static int  numAllocatedFiles = 0;
 static FILE *allocatedFiles[MAX_ALLOCATED_FILES];
 
 /*
- * Number of temporary files opened during the current transaction;
+ * Number of temporary files opened during the current session;
  * this is used in generation of tempfile names.
  */
 static long tempFileCounter = 0;
@@ -205,6 +205,9 @@ static int  FileAccess(File file);
 static File fileNameOpenFile(FileName fileName, int fileFlags, int fileMode);
 static char *filepath(const char *filename);
 static long pg_nofile(void);
+static void AtProcExit_Files(void);
+static void CleanupTempFiles(bool isProcExit);
+
 
 /*
  * pg_fsync --- same as fsync except does nothing if enableFsync is off
@@ -522,7 +525,7 @@ AllocateVfd(void)
         * register proc-exit call to ensure temp files are dropped at
         * exit
         */
-       on_proc_exit(AtEOXact_Files, 0);
+       on_proc_exit(AtProcExit_Files, 0);
    }
 
    if (VfdCache[0].nextFree == 0)
@@ -751,21 +754,21 @@ PathNameOpenFile(FileName fileName, int fileFlags, int fileMode)
  * There's no need to pass in fileFlags or fileMode either, since only
  * one setting makes any sense for a temp file.
  *
- * keepOverTxn: if true, don't close the file at end-of-transaction. In
+ * interXact: if true, don't close the file at end-of-transaction. In
  * most cases, you don't want temporary files to outlive the transaction
  * that created them, so this should be false -- but if you need
  * "somewhat" temporary storage, this might be useful. In either case,
- * the file is removed when the File is explicitely closed.
+ * the file is removed when the File is explicitly closed.
  */
 File
-OpenTemporaryFile(bool keepOverTxn)
+OpenTemporaryFile(bool interXact)
 {
-   char        tempfilepath[128];
+   char        tempfilepath[MAXPGPATH];
    File        file;
 
    /*
-    * Generate a tempfile name that's unique within the current
-    * transaction and database instance.
+    * Generate a tempfile name that should be unique within the current
+    * database instance.
     */
    snprintf(tempfilepath, sizeof(tempfilepath),
             "%s/%s%d.%ld", PG_TEMP_FILES_DIR, PG_TEMP_FILE_PREFIX,
@@ -798,15 +801,16 @@ OpenTemporaryFile(bool keepOverTxn)
                                O_RDWR | O_CREAT | O_TRUNC | PG_BINARY,
                                0600);
        if (file <= 0)
-           elog(ERROR, "Failed to create temporary file %s", tempfilepath);
+           elog(ERROR, "Failed to create temporary file %s: %m",
+                tempfilepath);
    }
 
    /* Mark it for deletion at close */
    VfdCache[file].fdstate |= FD_TEMPORARY;
 
    /* Mark it for deletion at EOXact */
-   if (!keepOverTxn)
-       VfdCache[file].fdstate |= FD_TXN_TEMPORARY;
+   if (!interXact)
+       VfdCache[file].fdstate |= FD_XACT_TEMPORARY;
 
    return file;
 }
@@ -1108,44 +1112,79 @@ closeAllVfds(void)
 /*
  * AtEOXact_Files
  *
- * This routine is called during transaction commit or abort or backend
- * exit (it doesn't particularly care which).  All still-open temporary-file
- * VFDs are closed, which also causes the underlying files to be deleted.
- * Furthermore, all "allocated" stdio files are closed.
+ * This routine is called during transaction commit or abort (it doesn't
+ * particularly care which).  All still-open per-transaction temporary file
+ * VFDs are closed, which also causes the underlying files to be
+ * deleted. Furthermore, all "allocated" stdio files are closed.
  */
 void
 AtEOXact_Files(void)
 {
-   Index       i;
+   CleanupTempFiles(false);
+}
+
+/*
+ * AtProcExit_Files
+ *
+ * on_proc_exit hook to clean up temp files during backend shutdown.
+ * Here, we want to clean up *all* temp files including interXact ones.
+ */
+static void
+AtProcExit_Files(void)
+{
+   CleanupTempFiles(true);
+}
+
+/*
+ * Close temporary files and delete their underlying files.
+ *
+ * isProcExit: if true, this is being called as the backend process is
+ * exiting. If that's the case, we should remove all temporary files; if
+ * that's not the case, we are being called for transaction commit/abort
+ * and should only remove transaction-local temp files.  In either case,
+ * also clean up "allocated" stdio files.
+ */
+static void
+CleanupTempFiles(bool isProcExit)
+{
+   Index i;
 
    if (SizeVfdCache > 0)
    {
        Assert(FileIsNotOpen(0));       /* Make sure ring not corrupted */
        for (i = 1; i < SizeVfdCache; i++)
        {
-           if ((VfdCache[i].fdstate & FD_TEMPORARY) &&
-               (VfdCache[i].fdstate & FD_TXN_TEMPORARY) &&
-               VfdCache[i].fileName != NULL)
-               FileClose(i);
+           unsigned short fdstate = VfdCache[i].fdstate;
+
+           if ((fdstate & FD_TEMPORARY) && VfdCache[i].fileName != NULL)
+           {
+               /*
+                * If we're in the process of exiting a backend process,
+                * close all temporary files. Otherwise, only close
+                * temporary files local to the current transaction.
+                */
+               if (isProcExit || (fdstate & FD_XACT_TEMPORARY))
+                   FileClose(i);
+           }
        }
    }
 
    while (numAllocatedFiles > 0)
        FreeFile(allocatedFiles[0]);
-
-   /*
-    * Reset the tempfile name counter to 0; not really necessary, but
-    * helps keep the names from growing unreasonably long.
-    */
-   tempFileCounter = 0;
 }
 
 
 /*
- * Remove old temporary files
+ * Remove temporary files left over from a prior postmaster session
  *
  * This should be called during postmaster startup.  It will forcibly
  * remove any leftover files created by OpenTemporaryFile.
+ *
+ * NOTE: we could, but don't, call this during a post-backend-crash restart
+ * cycle.  The argument for not doing it is that someone might want to examine
+ * the temp files for debugging purposes.  This does however mean that
+ * OpenTemporaryFile had better allow for collision with an existing temp
+ * file name.
  */
 void
 RemovePgTempFiles(void)
@@ -1194,15 +1233,9 @@ RemovePgTempFiles(void)
                                strlen(PG_TEMP_FILE_PREFIX)) == 0)
                        unlink(rm_path);
                    else
-                   {
-                       /*
-                        * would prefer to use elog here, but it's not up
-                        * and running during postmaster startup...
-                        */
-                       fprintf(stderr,
-                               "Unexpected file found in temporary-files directory: %s\n",
-                               rm_path);
-                   }
+                       elog(LOG,
+                            "Unexpected file found in temporary-files directory: %s",
+                            rm_path);
                }
                closedir(temp_dir);
            }
index e6486d4b16dcc89957f6b437b3e0f4b3944712e2..0b093b2613c492213f64d7d8137cbdb566145deb 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/utils/mmgr/portalmem.c,v 1.54 2003/03/27 16:51:29 momjian Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/utils/mmgr/portalmem.c,v 1.55 2003/04/29 03:21:29 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -33,6 +33,7 @@
 
 #include "postgres.h"
 
+#include "commands/portalcmds.h"
 #include "executor/executor.h"
 #include "utils/hsearch.h"
 #include "utils/memutils.h"
@@ -156,14 +157,14 @@ GetPortalByName(const char *name)
 
 /*
  * PortalSetQuery
- *     Attaches a "query" to the specified portal. Note that in the
- *     case of DECLARE CURSOR, some Portal options have already been
- *     set based upon the parsetree of the original DECLARE statement.
+ *     Attaches a QueryDesc to the specified portal.  This should be
+ *     called only after successfully doing ExecutorStart for the query.
+ *
+ * Note that in the case of DECLARE CURSOR, some Portal options have
+ * already been set in portalcmds.c's PreparePortal().  This is grotty.
  */
 void
-PortalSetQuery(Portal portal,
-              QueryDesc *queryDesc,
-              void (*cleanup) (Portal portal))
+PortalSetQuery(Portal portal, QueryDesc *queryDesc)
 {
    AssertArg(PortalIsValid(portal));
 
@@ -174,18 +175,15 @@ PortalSetQuery(Portal portal,
     */
    if (portal->scrollType == DEFAULT_SCROLL)
    {
-       bool backwardPlan;
-
-       backwardPlan = ExecSupportsBackwardScan(queryDesc->plantree);
-
-       if (backwardPlan)
+       if (ExecSupportsBackwardScan(queryDesc->plantree))
            portal->scrollType = ENABLE_SCROLL;
        else
            portal->scrollType = DISABLE_SCROLL;
    }
 
    portal->queryDesc = queryDesc;
-   portal->cleanup = cleanup;
+   portal->executorRunning = true; /* now need to shut down executor */
+
    portal->atStart = true;
    portal->atEnd = false;      /* allow fetches */
    portal->portalPos = 0;
@@ -228,12 +226,13 @@ CreatePortal(const char *name)
 
    /* initialize portal query */
    portal->queryDesc = NULL;
-   portal->cleanup = NULL;
+   portal->cleanup = PortalCleanup;
    portal->scrollType = DEFAULT_SCROLL;
+   portal->executorRunning = false;
    portal->holdOpen = false;
+   portal->createXact = GetCurrentTransactionId();
    portal->holdStore = NULL;
    portal->holdContext = NULL;
-   portal->createXact = GetCurrentTransactionId();
    portal->atStart = true;
    portal->atEnd = true;       /* disallow fetches until query is set */
    portal->portalPos = 0;
@@ -249,67 +248,34 @@ CreatePortal(const char *name)
  * PortalDrop
  *     Destroy the portal.
  *
- *     keepHoldable: if true, holdable portals should not be removed by
- *     this function. More specifically, invoking this function with
- *     keepHoldable = true on a holdable portal prepares the portal for
- *     access outside of its creating transaction.
+ *     isError: if true, we are destroying portals at the end of a failed
+ *     transaction.  (This causes PortalCleanup to skip unneeded steps.)
  */
 void
-PortalDrop(Portal portal, bool persistHoldable)
+PortalDrop(Portal portal, bool isError)
 {
    AssertArg(PortalIsValid(portal));
 
-   if (portal->holdOpen && persistHoldable)
-   {
-       /*
-        * We're "dropping" a holdable portal, but what we really need
-        * to do is prepare the portal for access outside of its
-        * creating transaction.
-        */
-
-       /*
-        * Create the memory context that is used for storage of
-        * long-term (cross transaction) data needed by the holdable
-        * portal.
-        */
-       portal->holdContext =
-           AllocSetContextCreate(PortalMemory,
-                                 "PortalHeapMemory",
-                                 ALLOCSET_DEFAULT_MINSIZE,
-                                 ALLOCSET_DEFAULT_INITSIZE,
-                                 ALLOCSET_DEFAULT_MAXSIZE);
-
-       /*
-        * Note that PersistHoldablePortal() releases any resources used
-        * by the portal that are local to the creating txn.
-        */
-       PersistHoldablePortal(portal);
-
-       return;
-   }
-
-   /* remove portal from hash table */
+   /*
+    * Remove portal from hash table.  Because we do this first, we will
+    * not come back to try to remove the portal again if there's any error
+    * in the subsequent steps.  Better to leak a little memory than to get
+    * into an infinite error-recovery loop.
+    */
    PortalHashTableDelete(portal);
 
-   /* reset portal */
+   /* let portalcmds.c clean up the state it knows about */
    if (PointerIsValid(portal->cleanup))
-       (*portal->cleanup) (portal);
+       (*portal->cleanup) (portal, isError);
 
-   /*
-    * delete short-term memory context; in the case of a holdable
-    * portal, this has already been done
-    */
-   if (PortalGetHeapMemory(portal))
-       MemoryContextDelete(PortalGetHeapMemory(portal));
-
-   /*
-    * delete long-term memory context; in the case of a non-holdable
-    * portal, this context has never been created, so we don't need to
-    * do anything
-    */
+   /* delete tuplestore storage, if any */
    if (portal->holdContext)
        MemoryContextDelete(portal->holdContext);
 
+   /* release subsidiary storage */
+   if (PortalGetHeapMemory(portal))
+       MemoryContextDelete(PortalGetHeapMemory(portal));
+
    /* release name and portal data (both are in PortalMemory) */
    pfree(portal->name);
    pfree(portal);
@@ -320,8 +286,8 @@ PortalDrop(Portal portal, bool persistHoldable)
  * transaction was aborted, all the portals created in this transaction
  * should be removed. If the transaction was successfully committed, any
  * holdable cursors created in this transaction need to be kept
- * open. Only cursors created in the current transaction should be
- * removed in this fashion.
+ * open. In any case, portals remaining from prior transactions should
+ * be left untouched.
  *
  * XXX This assumes that portals can be deleted in a random order, ie,
  * no portal has a reference to any other (at least not one that will be
@@ -340,7 +306,42 @@ AtEOXact_portals(bool isCommit)
 
    while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
    {
-       if (hentry->portal->createXact == xact)
-           PortalDrop(hentry->portal, isCommit);
+       Portal portal = hentry->portal;
+
+       if (portal->createXact != xact)
+           continue;
+
+       if (portal->holdOpen && isCommit)
+       {
+           /*
+            * We are exiting the transaction that created a holdable
+            * cursor.  Instead of dropping the portal, prepare it for
+            * access by later transactions.
+            */
+
+           /*
+            * Create the memory context that is used for storage of
+            * the held cursor's tuple set.
+            */
+           portal->holdContext =
+               AllocSetContextCreate(PortalMemory,
+                                     "PortalHeapMemory",
+                                     ALLOCSET_DEFAULT_MINSIZE,
+                                     ALLOCSET_DEFAULT_INITSIZE,
+                                     ALLOCSET_DEFAULT_MAXSIZE);
+
+           /*
+            * Transfer data into the held tuplestore.
+            *
+            * Note that PersistHoldablePortal() must release all
+            * resources used by the portal that are local to the creating
+            * transaction.
+            */
+           PersistHoldablePortal(portal);
+       }
+       else
+       {
+           PortalDrop(portal, !isCommit);
+       }
    }
 }
index 89970b2495b3fedfd28fd8fa1b3971fdaac363ce..b67090abad78fe39e8c3b29eef2a6518627e863e 100644 (file)
@@ -36,7 +36,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/utils/sort/tuplestore.c,v 1.12 2003/03/27 16:51:29 momjian Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/utils/sort/tuplestore.c,v 1.13 2003/04/29 03:21:29 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -65,7 +65,7 @@ struct Tuplestorestate
 {
    TupStoreStatus status;      /* enumerated value as shown above */
    bool        randomAccess;   /* did caller request random access? */
-   bool        interTxn;       /* keep open through transactions? */
+   bool        interXact;      /* keep open through transactions? */
    long        availMem;       /* remaining memory available, in bytes */
    BufFile    *myfile;         /* underlying file, or NULL if none */
 
@@ -191,7 +191,7 @@ struct Tuplestorestate
 
 
 static Tuplestorestate *tuplestore_begin_common(bool randomAccess,
-                                               bool interTxn,
+                                               bool interXact,
                                                int maxKBytes);
 static void dumptuples(Tuplestorestate *state);
 static unsigned int getlen(Tuplestorestate *state, bool eofOK);
@@ -205,9 +205,8 @@ static void *readtup_heap(Tuplestorestate *state, unsigned int len);
  *
  * Initialize for a tuple store operation.
  */
-
 static Tuplestorestate *
-tuplestore_begin_common(bool randomAccess, bool interTxn, int maxKBytes)
+tuplestore_begin_common(bool randomAccess, bool interXact, int maxKBytes)
 {
    Tuplestorestate *state;
 
@@ -215,7 +214,7 @@ tuplestore_begin_common(bool randomAccess, bool interTxn, int maxKBytes)
 
    state->status = TSS_INMEM;
    state->randomAccess = randomAccess;
-   state->interTxn = interTxn;
+   state->interXact = interXact;
    state->availMem = maxKBytes * 1024L;
    state->myfile = NULL;
 
@@ -244,17 +243,21 @@ tuplestore_begin_common(bool randomAccess, bool interTxn, int maxKBytes)
  * randomAccess: if true, both forward and backward accesses to the
  * tuple store are allowed.
  *
- * interTxn: if true, the files used by on-disk storage persist beyond
- * the end of the current transaction.
+ * interXact: if true, the files used for on-disk storage persist beyond the
+ * end of the current transaction.  NOTE: It's the caller's responsibility to
+ * create such a tuplestore in a memory context that will also survive
+ * transaction boundaries, and to ensure the tuplestore is closed when it's
+ * no longer wanted.
  *
  * maxKBytes: how much data to store in memory (any data beyond this
  * amount is paged to disk).
  */
 Tuplestorestate *
-tuplestore_begin_heap(bool randomAccess, bool interTxn, int maxKBytes)
+tuplestore_begin_heap(bool randomAccess, bool interXact, int maxKBytes)
 {
    Tuplestorestate *state = tuplestore_begin_common(randomAccess,
-                                                    interTxn, maxKBytes);
+                                                    interXact,
+                                                    maxKBytes);
 
    state->copytup = copytup_heap;
    state->writetup = writetup_heap;
@@ -341,7 +344,7 @@ tuplestore_puttuple(Tuplestorestate *state, void *tuple)
            /*
             * Nope; time to switch to tape-based operation.
             */
-           state->myfile = BufFileCreateTemp(state->interTxn);
+           state->myfile = BufFileCreateTemp(state->interXact);
            state->status = TSS_WRITEFILE;
            dumptuples(state);
            break;
index 74855ddf60545c2d8f29bd03f99c9725ac3bcfff..66bfca2b8b6b81c549ae8ddcfd1b22716d03f8cf 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: portalcmds.h,v 1.6 2003/03/11 19:40:23 tgl Exp $
+ * $Id: portalcmds.h,v 1.7 2003/04/29 03:21:30 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -29,6 +29,6 @@ extern long DoPortalFetch(Portal portal,
 
 extern void PerformPortalClose(char *name);
 
-extern void PortalCleanup(Portal portal);
+extern void PortalCleanup(Portal portal, bool isError);
 
 #endif   /* PORTALCMDS_H */
index c456b74d0f6ad4f40d2152f3d31a189420396b49..85f8ff2203e7883cb88d7dce9b5d81de60d04079 100644 (file)
@@ -18,7 +18,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: buffile.h,v 1.13 2003/03/27 16:51:29 momjian Exp $
+ * $Id: buffile.h,v 1.14 2003/04/29 03:21:30 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -34,7 +34,7 @@ typedef struct BufFile BufFile;
  * prototypes for functions in buffile.c
  */
 
-extern BufFile *BufFileCreateTemp(bool interTxn);
+extern BufFile *BufFileCreateTemp(bool interXact);
 extern void BufFileClose(BufFile *file);
 extern size_t BufFileRead(BufFile *file, void *ptr, size_t size);
 extern size_t BufFileWrite(BufFile *file, void *ptr, size_t size);
index 261e6314e91a9a60b613a6df4032330973ffc93f..0cec593032de751154bf28b4725b18c95383b748 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: fd.h,v 1.37 2003/03/27 16:51:29 momjian Exp $
+ * $Id: fd.h,v 1.38 2003/04/29 03:21:30 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -55,7 +55,7 @@ extern int    max_files_per_process;
 /* Operations on virtual Files --- equivalent to Unix kernel file ops */
 extern File FileNameOpenFile(FileName fileName, int fileFlags, int fileMode);
 extern File PathNameOpenFile(FileName fileName, int fileFlags, int fileMode);
-extern File OpenTemporaryFile(bool keepOverTxn);
+extern File OpenTemporaryFile(bool interXact);
 extern void FileClose(File file);
 extern void FileUnlink(File file);
 extern int FileRead(File file, char *buffer, int amount);
index 0201b0684c378c2f21402525fb35edaa36ec378b..2615db0490d56d623be7359483c9c82dfdc53c53 100644 (file)
@@ -9,7 +9,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: portal.h,v 1.40 2003/03/27 16:51:29 momjian Exp $
+ * $Id: portal.h,v 1.41 2003/04/29 03:21:30 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -48,14 +48,15 @@ typedef struct PortalData *Portal;
 typedef struct PortalData
 {
    char       *name;           /* Portal's name */
-   MemoryContext heap;         /* memory for storing short-term data */
+   MemoryContext heap;         /* subsidiary memory */
    QueryDesc  *queryDesc;      /* Info about query associated with portal */
-   void        (*cleanup) (Portal);    /* Cleanup routine (optional) */
+   void        (*cleanup) (Portal portal, bool isError);   /* Cleanup hook */
    ScrollType  scrollType;     /* Allow backward fetches? */
-   bool        holdOpen;       /* hold open after txn ends? */
-   TransactionId createXact;   /* the xid of the creating txn */
+   bool        executorRunning;    /* T if we need to call ExecutorEnd */
+   bool        holdOpen;       /* hold open after xact ends? */
+   TransactionId createXact;   /* the xid of the creating xact */
    Tuplestorestate *holdStore; /* store for holdable cursors */
-   MemoryContext holdContext;  /* memory for long-term data */
+   MemoryContext holdContext;  /* memory containing holdStore */
 
    /*
     * atStart, atEnd and portalPos indicate the current cursor position.
@@ -88,10 +89,9 @@ typedef struct PortalData
 extern void EnablePortalManager(void);
 extern void AtEOXact_portals(bool isCommit);
 extern Portal CreatePortal(const char *name);
-extern void PortalDrop(Portal portal, bool persistHoldable);
+extern void PortalDrop(Portal portal, bool isError);
 extern Portal GetPortalByName(const char *name);
-extern void PortalSetQuery(Portal portal, QueryDesc *queryDesc,
-                          void (*cleanup) (Portal portal));
+extern void PortalSetQuery(Portal portal, QueryDesc *queryDesc);
 extern void PersistHoldablePortal(Portal portal);
 
 #endif   /* PORTAL_H */
index dbc47ef29d61d87862c8b92850e74e80b5c03771..6a021ba52fd3dd3af293a8c3c35c32778b5b5b89 100644 (file)
@@ -17,7 +17,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: tuplestore.h,v 1.10 2003/03/27 16:51:29 momjian Exp $
+ * $Id: tuplestore.h,v 1.11 2003/04/29 03:21:30 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -37,7 +37,7 @@ typedef struct Tuplestorestate Tuplestorestate;
  */
 
 extern Tuplestorestate *tuplestore_begin_heap(bool randomAccess,
-                                             bool interTxn,
+                                             bool interXact,
                                              int maxKBytes);
 
 extern void tuplestore_puttuple(Tuplestorestate *state, void *tuple);
index 7520653924e17213d159707fad2defaed72530d3..1e69cff96963be1c710091870910fa18e3c1fe3c 100644 (file)
@@ -675,7 +675,7 @@ CLOSE foo9;
 CLOSE foo10;
 CLOSE foo11;
 CLOSE foo12;
--- is there a reason why we don't close the rest of the open cursors?
+-- leave some cursors open, to test that auto-close works.
 END;
 --
 -- NO SCROLL disallows backward fetching
index 2df820a30a79c28b39c33f6e11db1bc6e76691a9..807c847bc3c1da72e71f7836d079a44092ef4634 100644 (file)
@@ -166,7 +166,7 @@ CLOSE foo11;
 
 CLOSE foo12;
 
--- is there a reason why we don't close the rest of the open cursors?
+-- leave some cursors open, to test that auto-close works.
 
 END;
 
@@ -217,4 +217,4 @@ DECLARE foo26 CURSOR WITH HOLD FOR SELECT * FROM tenk1;
 ROLLBACK;
 
 -- should fail
-FETCH FROM foo26;
\ No newline at end of file
+FETCH FROM foo26;