Put back error test for DECLARE CURSOR outside a transaction block ...
authorTom Lane <[email protected]>
Mon, 18 Nov 2002 01:17:50 +0000 (01:17 +0000)
committerTom Lane <[email protected]>
Mon, 18 Nov 2002 01:17:50 +0000 (01:17 +0000)
but do it correctly now.

src/backend/access/transam/xact.c
src/backend/tcop/pquery.c
src/include/access/xact.h

index 04e2103a76c57ca66991ef331ea015af1d13a1aa..6e1d41e53370606f5af40957caa6a02daca5fec4 100644 (file)
@@ -1497,6 +1497,50 @@ PreventTransactionChain(void *stmtNode, const char *stmtType)
        }
 }
 
+/* --------------------------------
+ *     RequireTransactionChain
+ *
+ *     This routine is to be called by statements that must run inside
+ *     a transaction block, because they have no effects that persist past
+ *     transaction end (and so calling them outside a transaction block
+ *     is presumably an error).  DECLARE CURSOR is an example.
+ *
+ *     If we appear to be running inside a user-defined function, we do not
+ *     issue an error, since the function could issue more commands that make
+ *     use of the current statement's results.  Thus this is an inverse for
+ *     PreventTransactionChain.
+ *
+ *     stmtNode: pointer to parameter block for statement; this is used in
+ *     a very klugy way to determine whether we are inside a function.
+ *     stmtType: statement type name for error messages.
+ * --------------------------------
+ */
+void
+RequireTransactionChain(void *stmtNode, const char *stmtType)
+{
+       /*
+        * xact block already started?
+        */
+       if (IsTransactionBlock())
+               return;
+       /*
+        * Are we inside a function call?  If the statement's parameter block
+        * was allocated in QueryContext, assume it is an interactive command.
+        * Otherwise assume it is coming from a function.
+        */
+       if (!MemoryContextContains(QueryContext, stmtNode))
+               return;
+       /*
+        * If we are in autocommit-off mode then it's okay, because this
+        * statement will itself start a transaction block.
+        */
+       if (!autocommit && !suppressChain)
+               return;
+       /* translator: %s represents an SQL statement name */
+       elog(ERROR, "%s may only be used in begin/end transaction blocks",
+                stmtType);
+}
+
 
 /* ----------------------------------------------------------------
  *                                        transaction block support
index 9227b1b2fc9209cba2929c03092c2bded93a7a57..2d871de1d51eee61b364796537c362d6c4f6430b 100644 (file)
@@ -161,6 +161,8 @@ ProcessQuery(Query *parsetree,
                        /* If binary portal, switch to alternate output format */
                        if (dest == Remote && parsetree->isBinary)
                                dest = RemoteInternal;
+                       /* Check for invalid context (must be in transaction block) */
+                       RequireTransactionChain((void *) parsetree, "DECLARE CURSOR");
                }
                else if (parsetree->into != NULL)
                {
index 4fca596a6d28e0a4bacb1b5c33c66ee86379236f..1e1e1b613cf12bfd623b2c79e4565a5abf87ed5b 100644 (file)
@@ -114,6 +114,7 @@ extern bool IsTransactionBlock(void);
 extern void UserAbortTransactionBlock(void);
 extern void AbortOutOfAnyTransaction(void);
 extern void PreventTransactionChain(void *stmtNode, const char *stmtType);
+extern void RequireTransactionChain(void *stmtNode, const char *stmtType);
 
 extern void RecordTransactionCommit(void);