From 2ea179f36102edc8df6be9e728b24bcb264ec52e Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Wed, 11 Nov 2009 19:25:42 +0000
Subject: [PATCH] Support optional FROM/IN in FETCH and MOVE

The main motivation for this is that it's required for Informix compatibility
in ECPG.

This patch makes the ECPG and core grammars a bit closer to one another for
these productions.

Author: Zoltan Boszormenyi
---
 src/backend/parser/gram.y                     | 78 ++++++++++---------
 src/interfaces/ecpg/preproc/ecpg.addons       | 76 ++++++++----------
 .../expected/compat_informix-test_informix.c  |  2 +-
 .../compat_informix-test_informix.stderr      |  6 +-
 4 files changed, 77 insertions(+), 85 deletions(-)

diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4325e4d0ed..38d9764b55 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.690 2009/11/09 18:38:48 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.691 2009/11/11 19:25:40 alvherre Exp $
  *
  * HISTORY
  *	  AUTHOR			DATE			MAJOR EVENT
@@ -332,7 +332,7 @@ static TypeName *TableFuncTypeName(List *columns);
 %type <ival>	opt_column event cursor_options opt_hold opt_set_data
 %type <objtype>	reindex_type drop_type comment_type
 
-%type <node>	fetch_direction limit_clause select_limit_value
+%type <node>	fetch_args limit_clause select_limit_value
 				offset_clause select_offset_value
 				select_offset_value2 opt_select_fetch_first_value
 %type <ival>	row_or_rows first_or_next
@@ -4180,142 +4180,144 @@ comment_text:
  *
  *****************************************************************************/
 
-FetchStmt:	FETCH fetch_direction from_in name
+FetchStmt:	FETCH fetch_args
 				{
 					FetchStmt *n = (FetchStmt *) $2;
-					n->portalname = $4;
-					n->ismove = FALSE;
-					$$ = (Node *)n;
-				}
-			| FETCH name
-				{
-					FetchStmt *n = makeNode(FetchStmt);
-					n->direction = FETCH_FORWARD;
-					n->howMany = 1;
-					n->portalname = $2;
 					n->ismove = FALSE;
 					$$ = (Node *)n;
 				}
-			| MOVE fetch_direction from_in name
+			| MOVE fetch_args
 				{
 					FetchStmt *n = (FetchStmt *) $2;
-					n->portalname = $4;
 					n->ismove = TRUE;
 					$$ = (Node *)n;
 				}
-			| MOVE name
+		;
+
+fetch_args:	name
 				{
 					FetchStmt *n = makeNode(FetchStmt);
+					n->portalname = $1;
 					n->direction = FETCH_FORWARD;
 					n->howMany = 1;
-					n->portalname = $2;
-					n->ismove = TRUE;
 					$$ = (Node *)n;
 				}
-		;
-
-fetch_direction:
-			/*EMPTY*/
+			| from_in name
 				{
 					FetchStmt *n = makeNode(FetchStmt);
+					n->portalname = $2;
 					n->direction = FETCH_FORWARD;
 					n->howMany = 1;
 					$$ = (Node *)n;
 				}
-			| NEXT
+			| NEXT opt_from_in name
 				{
 					FetchStmt *n = makeNode(FetchStmt);
+					n->portalname = $3;
 					n->direction = FETCH_FORWARD;
 					n->howMany = 1;
 					$$ = (Node *)n;
 				}
-			| PRIOR
+			| PRIOR opt_from_in name
 				{
 					FetchStmt *n = makeNode(FetchStmt);
+					n->portalname = $3;
 					n->direction = FETCH_BACKWARD;
 					n->howMany = 1;
 					$$ = (Node *)n;
 				}
-			| FIRST_P
+			| FIRST_P opt_from_in name
 				{
 					FetchStmt *n = makeNode(FetchStmt);
+					n->portalname = $3;
 					n->direction = FETCH_ABSOLUTE;
 					n->howMany = 1;
 					$$ = (Node *)n;
 				}
-			| LAST_P
+			| LAST_P opt_from_in name
 				{
 					FetchStmt *n = makeNode(FetchStmt);
+					n->portalname = $3;
 					n->direction = FETCH_ABSOLUTE;
 					n->howMany = -1;
 					$$ = (Node *)n;
 				}
-			| ABSOLUTE_P SignedIconst
+			| ABSOLUTE_P SignedIconst opt_from_in name
 				{
 					FetchStmt *n = makeNode(FetchStmt);
+					n->portalname = $4;
 					n->direction = FETCH_ABSOLUTE;
 					n->howMany = $2;
 					$$ = (Node *)n;
 				}
-			| RELATIVE_P SignedIconst
+			| RELATIVE_P SignedIconst opt_from_in name
 				{
 					FetchStmt *n = makeNode(FetchStmt);
+					n->portalname = $4;
 					n->direction = FETCH_RELATIVE;
 					n->howMany = $2;
 					$$ = (Node *)n;
 				}
-			| SignedIconst
+			| SignedIconst opt_from_in name
 				{
 					FetchStmt *n = makeNode(FetchStmt);
+					n->portalname = $3;
 					n->direction = FETCH_FORWARD;
 					n->howMany = $1;
 					$$ = (Node *)n;
 				}
-			| ALL
+			| ALL opt_from_in name
 				{
 					FetchStmt *n = makeNode(FetchStmt);
+					n->portalname = $3;
 					n->direction = FETCH_FORWARD;
 					n->howMany = FETCH_ALL;
 					$$ = (Node *)n;
 				}
-			| FORWARD
+			| FORWARD opt_from_in name
 				{
 					FetchStmt *n = makeNode(FetchStmt);
+					n->portalname = $3;
 					n->direction = FETCH_FORWARD;
 					n->howMany = 1;
 					$$ = (Node *)n;
 				}
-			| FORWARD SignedIconst
+			| FORWARD SignedIconst opt_from_in name
 				{
 					FetchStmt *n = makeNode(FetchStmt);
+					n->portalname = $4;
 					n->direction = FETCH_FORWARD;
 					n->howMany = $2;
 					$$ = (Node *)n;
 				}
-			| FORWARD ALL
+			| FORWARD ALL opt_from_in name
 				{
 					FetchStmt *n = makeNode(FetchStmt);
+					n->portalname = $4;
 					n->direction = FETCH_FORWARD;
 					n->howMany = FETCH_ALL;
 					$$ = (Node *)n;
 				}
-			| BACKWARD
+			| BACKWARD opt_from_in name
 				{
 					FetchStmt *n = makeNode(FetchStmt);
+					n->portalname = $3;
 					n->direction = FETCH_BACKWARD;
 					n->howMany = 1;
 					$$ = (Node *)n;
 				}
-			| BACKWARD SignedIconst
+			| BACKWARD SignedIconst opt_from_in name
 				{
 					FetchStmt *n = makeNode(FetchStmt);
+					n->portalname = $4;
 					n->direction = FETCH_BACKWARD;
 					n->howMany = $2;
 					$$ = (Node *)n;
 				}
-			| BACKWARD ALL
+			| BACKWARD ALL opt_from_in name
 				{
 					FetchStmt *n = makeNode(FetchStmt);
+					n->portalname = $4;
 					n->direction = FETCH_BACKWARD;
 					n->howMany = FETCH_ALL;
 					$$ = (Node *)n;
@@ -4326,6 +4328,10 @@ from_in:	FROM									{}
 			| IN_P									{}
 		;
 
+opt_from_in:	from_in								{}
+			| /* EMPTY */							{}
+		;
+
 
 /*****************************************************************************
  *
diff --git a/src/interfaces/ecpg/preproc/ecpg.addons b/src/interfaces/ecpg/preproc/ecpg.addons
index 7550616170..9ef3d19100 100644
--- a/src/interfaces/ecpg/preproc/ecpg.addons
+++ b/src/interfaces/ecpg/preproc/ecpg.addons
@@ -1,4 +1,4 @@
-/* $PostgreSQL: pgsql/src/interfaces/ecpg/preproc/ecpg.addons,v 1.6 2009/11/05 23:24:27 tgl Exp $ */
+/* $PostgreSQL: pgsql/src/interfaces/ecpg/preproc/ecpg.addons,v 1.7 2009/11/11 19:25:40 alvherre Exp $ */
 
 ECPG: stmtClosePortalStmt block
 	{
@@ -206,16 +206,38 @@ ECPG: ConstraintAttributeSpecConstraintTimeSpecConstraintDeferrabilitySpec addon
 			if (strcmp($2, "deferrable") != 0 && strcmp($1, "initially deferrable") == 0 )
 				mmerror(PARSE_ERROR, ET_ERROR, "constraint declared INITIALLY DEFERRED must be DEFERRABLE");
 ECPG: var_valueNumericOnly addon
-ECPG: fetch_directionSignedIconst addon
 		if ($1[0] == '$')
 		{
 			free($1);
 			$1 = make_str("$0");
 		}
-ECPG: fetch_directionABSOLUTE_PSignedIconst addon
-ECPG: fetch_directionRELATIVE_PSignedIconst addon
-ECPG: fetch_directionFORWARDSignedIconst addon
-ECPG: fetch_directionBACKWARDSignedIconst addon
+ECPG: fetch_argsname addon
+		add_additional_variables($1, false);
+ECPG: fetch_argsfrom_inname addon
+		add_additional_variables($2, false);
+ECPG: fetch_argsNEXTopt_from_inname addon
+ECPG: fetch_argsPRIORopt_from_inname addon
+ECPG: fetch_argsFIRST_Popt_from_inname addon
+ECPG: fetch_argsLAST_Popt_from_inname addon
+ECPG: fetch_argsALLopt_from_inname addon
+ECPG: fetch_argsFORWARDopt_from_inname addon
+ECPG: fetch_argsBACKWARDopt_from_inname addon
+		add_additional_variables($3, false);
+ECPG: fetch_argsSignedIconstopt_from_inname addon
+		add_additional_variables($3, false);
+		if ($1[0] == '$')
+		{
+			free($1);
+			$1 = make_str("$0");
+		}
+ECPG: fetch_argsFORWARDALLopt_from_inname addon
+ECPG: fetch_argsBACKWARDALLopt_from_inname addon
+		add_additional_variables($4, false);
+ECPG: fetch_argsABSOLUTE_PSignedIconstopt_from_inname addon
+ECPG: fetch_argsRELATIVE_PSignedIconstopt_from_inname addon
+ECPG: fetch_argsFORWARDSignedIconstopt_from_inname addon
+ECPG: fetch_argsBACKWARDSignedIconstopt_from_inname addon
+		add_additional_variables($4, false);
 		if ($2[0] == '$')
 		{
 			free($2);
@@ -336,47 +358,11 @@ ECPG: VariableShowStmtSHOWALL block
 		mmerror(PARSE_ERROR, ET_ERROR, "SHOW ALL is not implemented");
 		$$ = EMPTY;
 	}
-ECPG: FetchStmtFETCHfetch_directionfrom_inname block 
-	{
-		add_additional_variables($4, false);
-		$$ = cat_str(4, make_str("fetch"), $2, $3, $4);
-	}
-ECPG: FetchStmtFETCHname block
+ECPG: FetchStmtMOVEfetch_args rule
+	| FETCH fetch_args ecpg_into
 	{
-		add_additional_variables($2, false);
-		$$ = cat_str(2, make_str("fetch"), $2);
+		$$ = cat2_str(make_str("fetch"), $2);
 	}
-ECPG: FetchStmtMOVEname rule
-	| FETCH fetch_direction from_in name ecpg_into
-		{
-			add_additional_variables($4, false);
-			$$ = cat_str(4, make_str("fetch"), $2, $3, $4);
-		}
-	| FETCH fetch_direction name ecpg_into
-		{
-			add_additional_variables($3, false);
-			$$ = cat_str(4, make_str("fetch"), $2, make_str("from"), $3);
-		}
-	| FETCH from_in name ecpg_into
-		{
-			add_additional_variables($3, false);
-			$$ = cat_str(3, make_str("fetch"), $2, $3);
-		}
-	| FETCH name ecpg_into
-		{
-			add_additional_variables($2, false);
-			$$ = cat2_str(make_str("fetch"), $2);
-		}
-	| FETCH fetch_direction name
-		{
-			add_additional_variables($3, false);
-			$$ = cat_str(4, make_str("fetch"), $2, make_str("from"), $3);
-		}
-	| FETCH from_in name
-		{
-			add_additional_variables($3, false);
-			$$ = cat_str(3, make_str("fetch"), $2, $3);
-		}
 ECPG: select_limitLIMITselect_limit_value','select_offset_value block
         {
                 mmerror(PARSE_ERROR, ET_WARNING, "no longer supported LIMIT #,# syntax passed to server");
diff --git a/src/interfaces/ecpg/test/expected/compat_informix-test_informix.c b/src/interfaces/ecpg/test/expected/compat_informix-test_informix.c
index 3d48a3a273..0dc8c7f08a 100644
--- a/src/interfaces/ecpg/test/expected/compat_informix-test_informix.c
+++ b/src/interfaces/ecpg/test/expected/compat_informix-test_informix.c
@@ -158,7 +158,7 @@ if (sqlca.sqlcode < 0) dosqlprint ( );}
 
 	while (1)
 	{
-		{ ECPGdo(__LINE__, 1, 1, NULL, 0, ECPGst_normal, "fetch forward from c", ECPGt_EOIT, 
+		{ ECPGdo(__LINE__, 1, 1, NULL, 0, ECPGst_normal, "fetch forward c", ECPGt_EOIT, 
 	ECPGt_int,&(i),(long)1,(long)1,sizeof(int), 
 	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, 
 	ECPGt_decimal,&(j),(long)1,(long)1,sizeof(decimal), 
diff --git a/src/interfaces/ecpg/test/expected/compat_informix-test_informix.stderr b/src/interfaces/ecpg/test/expected/compat_informix-test_informix.stderr
index 89e9483ffb..d678788640 100644
--- a/src/interfaces/ecpg/test/expected/compat_informix-test_informix.stderr
+++ b/src/interfaces/ecpg/test/expected/compat_informix-test_informix.stderr
@@ -63,7 +63,7 @@ DETAIL:  Key (i)=(7) already exists.
 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ecpg_execute on line 95: OK: DECLARE CURSOR
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 57: query: fetch forward from c; with 0 parameter(s) on connection regress1
+[NO_PID]: ecpg_execute on line 57: query: fetch forward c; with 0 parameter(s) on connection regress1
 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ecpg_execute on line 57: using PQexec
 [NO_PID]: sqlca: code: 0, state: 00000
@@ -75,7 +75,7 @@ DETAIL:  Key (i)=(7) already exists.
 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ecpg_get_data on line 57: RESULT: test    offset: -1; array: yes
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 57: query: fetch forward from c; with 0 parameter(s) on connection regress1
+[NO_PID]: ecpg_execute on line 57: query: fetch forward c; with 0 parameter(s) on connection regress1
 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ecpg_execute on line 57: using PQexec
 [NO_PID]: sqlca: code: 0, state: 00000
@@ -87,7 +87,7 @@ DETAIL:  Key (i)=(7) already exists.
 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ecpg_get_data on line 57: RESULT: a       offset: -1; array: yes
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 57: query: fetch forward from c; with 0 parameter(s) on connection regress1
+[NO_PID]: ecpg_execute on line 57: query: fetch forward c; with 0 parameter(s) on connection regress1
 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ecpg_execute on line 57: using PQexec
 [NO_PID]: sqlca: code: 0, state: 00000
-- 
2.40.0