From 17ac74797a12e85741436d27cd4ac6337a806c44 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Mon, 18 Nov 2002 01:17:39 +0000
Subject: [PATCH] Put back error test for DECLARE CURSOR outside a transaction
 block ... but do it correctly now.

---
 src/backend/access/transam/xact.c | 46 ++++++++++++++++++++++++++++++-
 src/backend/tcop/pquery.c         |  4 ++-
 src/include/access/xact.h         |  3 +-
 3 files changed, 50 insertions(+), 3 deletions(-)

diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index 4711c091c7..607a47f124 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/access/transam/xact.c,v 1.138 2002/11/13 03:12:05 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/access/transam/xact.c,v 1.139 2002/11/18 01:17:39 tgl Exp $
  *
  * NOTES
  *		Transaction aborts can now occur two ways:
@@ -1488,6 +1488,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
diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c
index 29909295f5..0919b03f45 100644
--- a/src/backend/tcop/pquery.c
+++ b/src/backend/tcop/pquery.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/tcop/pquery.c,v 1.55 2002/09/04 20:31:26 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/tcop/pquery.c,v 1.56 2002/11/18 01:17:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -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)
 		{
diff --git a/src/include/access/xact.h b/src/include/access/xact.h
index dd609f19a1..fa30c3303b 100644
--- a/src/include/access/xact.h
+++ b/src/include/access/xact.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: xact.h,v 1.47 2002/11/13 03:12:05 momjian Exp $
+ * $Id: xact.h,v 1.48 2002/11/18 01:17:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -115,6 +115,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);