Boszormenyi Zoltan írta:
> Tom Lane írta:
>   
>> Boszormenyi Zoltan <z...@cybertec.at> writes:
>>   
>>     
>>> Tom Lane írta:
>>>     
>>>       
>>>> I'd look at requiring from_in as being the least-bad alternative.
>>>>       
>>>>         
>>   
>>     
>>> Hm. "FETCH FORWARD variable" can only be a rowcount var
>>> only if there's something afterwards, no? With the proposed
>>> change in fetch_direction (moving FORWARD and BACKWARD
>>> without the rowcount upper to the parent rules) now the parser is
>>> able to look behind "FORWARD variable"...
>>>     
>>>       
>> The fundamental reason that there's a problem here is that ecpg has
>> decided to accept a syntax that the backend doesn't (ie, FETCH with a
>> fetch direction but no FROM/IN).  I think that that's basically a bad
>> idea: it's not helpful to users to be inconsistent, and it requires ugly
>> hacks in ecpg, and now ugly hacks in the core grammar as well.  We
>> should resolve it either by taking out that syntax from ecpg, or by
>> making the backend accept it too.  Not by uglifying the grammars some
>> more in order to keep them inconsistent.
>>
>> If we were going to allow it in the core, I think moving the cursor
>> name into the fetch_direction production might work, ie, change
>> fetch_direction to fetch_args and make it cover everything that
>> FETCH and MOVE share.  Probably from_in could become opt_from_in,
>> since the alternatives for it are fully reserved words already, and we
>> wouldn't need to double up any of the fetch_direction productions.
>>
>>                      regards, tom lane
>>   
>>     
>
> Your guess about making from_in into opt_from_in
> seems good, mostly. I tried doing exactly that and simply
> adding an empty match into from_in, I got shift/reduce conflicts
> only in "opt_from_in cursor_name". So, this rule has to be
> unrolled into 3 rules, or keeping a separate "from_in" just for
> having a separate "cursor_name" and "from_in cursor_name".
> I decided that I use the second method, it's shorter.
>   

OK, here's the WIP patch for the unified core/ecpg grammar,
with opt_from_in. But I am still getting the 2 shift/reduce
conflicts exactly for the FORWARD and BACKWARD rules
that I was getting originally. Can you look at this patch and
point me to the right direction in solving it? Thanks in advance.

> Best regards,
> Zoltán Böszörményi
>
>   


-- 
Bible has answers for everything. Proof:
"But let your communication be, Yea, yea; Nay, nay: for whatsoever is more
than these cometh of evil." (Matthew 5:37) - basics of digital technology.
"May your kingdom come" - superficial description of plate tectonics

----------------------------------
Zoltán Böszörményi
Cybertec Schönig & Schönig GmbH
http://www.postgresql.at/

diff -dcrpN pgsql.orig/src/backend/parser/gram.y pgsql/src/backend/parser/gram.y
*** pgsql.orig/src/backend/parser/gram.y	2009-08-03 10:38:28.000000000 +0200
--- pgsql/src/backend/parser/gram.y	2009-08-09 17:48:36.000000000 +0200
*************** static TypeName *TableFuncTypeName(List 
*** 253,259 ****
  
  %type <str>		relation_name copy_file_name
  				database_name access_method_clause access_method attr_name
! 				index_name name file_name cluster_index_specification
  
  %type <list>	func_name handler_name qual_Op qual_all_Op subquery_Op
  				opt_class opt_validator validator_clause
--- 253,259 ----
  
  %type <str>		relation_name copy_file_name
  				database_name access_method_clause access_method attr_name
! 				index_name name cursor_name file_name cluster_index_specification
  
  %type <list>	func_name handler_name qual_Op qual_all_Op subquery_Op
  				opt_class opt_validator validator_clause
*************** static TypeName *TableFuncTypeName(List 
*** 331,337 ****
  %type <ival>	opt_column event cursor_options opt_hold opt_set_data
  %type <objtype>	reindex_type drop_type comment_type
  
! %type <node>	fetch_direction select_limit_value select_offset_value
  				select_offset_value2 opt_select_fetch_first_value
  %type <ival>	row_or_rows first_or_next
  
--- 331,337 ----
  %type <ival>	opt_column event cursor_options opt_hold opt_set_data
  %type <objtype>	reindex_type drop_type comment_type
  
! %type <node>	fetch_args select_limit_value select_offset_value
  				select_offset_value2 opt_select_fetch_first_value
  %type <ival>	row_or_rows first_or_next
  
*************** reloption_elem:	
*** 1915,1921 ****
   *****************************************************************************/
  
  ClosePortalStmt:
! 			CLOSE name
  				{
  					ClosePortalStmt *n = makeNode(ClosePortalStmt);
  					n->portalname = $2;
--- 1915,1921 ----
   *****************************************************************************/
  
  ClosePortalStmt:
! 			CLOSE cursor_name
  				{
  					ClosePortalStmt *n = makeNode(ClosePortalStmt);
  					n->portalname = $2;
*************** comment_text:
*** 4082,4223 ****
   *
   *****************************************************************************/
  
! FetchStmt:	FETCH fetch_direction from_in name
  				{
  					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
  				{
  					FetchStmt *n = (FetchStmt *) $2;
- 					n->portalname = $4;
  					n->ismove = TRUE;
  					$$ = (Node *)n;
  				}
! 			| MOVE name
  				{
  					FetchStmt *n = makeNode(FetchStmt);
  					n->direction = FETCH_FORWARD;
  					n->howMany = 1;
- 					n->portalname = $2;
- 					n->ismove = TRUE;
  					$$ = (Node *)n;
  				}
! 		;
! 
! fetch_direction:
! 			/*EMPTY*/
  				{
  					FetchStmt *n = makeNode(FetchStmt);
  					n->direction = FETCH_FORWARD;
  					n->howMany = 1;
  					$$ = (Node *)n;
  				}
! 			| NEXT
  				{
  					FetchStmt *n = makeNode(FetchStmt);
  					n->direction = FETCH_FORWARD;
  					n->howMany = 1;
  					$$ = (Node *)n;
  				}
! 			| PRIOR
  				{
  					FetchStmt *n = makeNode(FetchStmt);
  					n->direction = FETCH_BACKWARD;
  					n->howMany = 1;
  					$$ = (Node *)n;
  				}
! 			| FIRST_P
  				{
  					FetchStmt *n = makeNode(FetchStmt);
  					n->direction = FETCH_ABSOLUTE;
  					n->howMany = 1;
  					$$ = (Node *)n;
  				}
! 			| LAST_P
  				{
  					FetchStmt *n = makeNode(FetchStmt);
  					n->direction = FETCH_ABSOLUTE;
  					n->howMany = -1;
  					$$ = (Node *)n;
  				}
! 			| ABSOLUTE_P SignedIconst
  				{
  					FetchStmt *n = makeNode(FetchStmt);
  					n->direction = FETCH_ABSOLUTE;
  					n->howMany = $2;
  					$$ = (Node *)n;
  				}
! 			| RELATIVE_P SignedIconst
  				{
  					FetchStmt *n = makeNode(FetchStmt);
  					n->direction = FETCH_RELATIVE;
  					n->howMany = $2;
  					$$ = (Node *)n;
  				}
! 			| SignedIconst
  				{
  					FetchStmt *n = makeNode(FetchStmt);
  					n->direction = FETCH_FORWARD;
  					n->howMany = $1;
  					$$ = (Node *)n;
  				}
! 			| ALL
  				{
  					FetchStmt *n = makeNode(FetchStmt);
  					n->direction = FETCH_FORWARD;
  					n->howMany = FETCH_ALL;
  					$$ = (Node *)n;
  				}
! 			| FORWARD
  				{
  					FetchStmt *n = makeNode(FetchStmt);
  					n->direction = FETCH_FORWARD;
  					n->howMany = 1;
  					$$ = (Node *)n;
  				}
! 			| FORWARD SignedIconst
  				{
  					FetchStmt *n = makeNode(FetchStmt);
  					n->direction = FETCH_FORWARD;
  					n->howMany = $2;
  					$$ = (Node *)n;
  				}
! 			| FORWARD ALL
  				{
  					FetchStmt *n = makeNode(FetchStmt);
  					n->direction = FETCH_FORWARD;
  					n->howMany = FETCH_ALL;
  					$$ = (Node *)n;
  				}
! 			| BACKWARD
  				{
  					FetchStmt *n = makeNode(FetchStmt);
  					n->direction = FETCH_BACKWARD;
  					n->howMany = 1;
  					$$ = (Node *)n;
  				}
! 			| BACKWARD SignedIconst
  				{
  					FetchStmt *n = makeNode(FetchStmt);
  					n->direction = FETCH_BACKWARD;
  					n->howMany = $2;
  					$$ = (Node *)n;
  				}
! 			| BACKWARD ALL
  				{
  					FetchStmt *n = makeNode(FetchStmt);
  					n->direction = FETCH_BACKWARD;
  					n->howMany = FETCH_ALL;
  					$$ = (Node *)n;
--- 4082,4226 ----
   *
   *****************************************************************************/
  
! FetchStmt:	FETCH fetch_args
  				{
  					FetchStmt *n = (FetchStmt *) $2;
  					n->ismove = FALSE;
  					$$ = (Node *)n;
  				}
! 			| MOVE fetch_args
  				{
  					FetchStmt *n = (FetchStmt *) $2;
  					n->ismove = TRUE;
  					$$ = (Node *)n;
  				}
! 		;
! 
! fetch_args:
! 			cursor_name
  				{
  					FetchStmt *n = makeNode(FetchStmt);
+ 					n->portalname = $1;
  					n->direction = FETCH_FORWARD;
  					n->howMany = 1;
  					$$ = (Node *)n;
  				}
! 			| from_in cursor_name
  				{
  					FetchStmt *n = makeNode(FetchStmt);
+ 					n->portalname = $2;
  					n->direction = FETCH_FORWARD;
  					n->howMany = 1;
  					$$ = (Node *)n;
  				}
! 			| NEXT opt_from_in cursor_name
  				{
  					FetchStmt *n = makeNode(FetchStmt);
+ 					n->portalname = $3;
  					n->direction = FETCH_FORWARD;
  					n->howMany = 1;
  					$$ = (Node *)n;
  				}
! 			| PRIOR opt_from_in cursor_name
  				{
  					FetchStmt *n = makeNode(FetchStmt);
+ 					n->portalname = $3;
  					n->direction = FETCH_BACKWARD;
  					n->howMany = 1;
  					$$ = (Node *)n;
  				}
! 			| FIRST_P opt_from_in cursor_name
  				{
  					FetchStmt *n = makeNode(FetchStmt);
+ 					n->portalname = $3;
  					n->direction = FETCH_ABSOLUTE;
  					n->howMany = 1;
  					$$ = (Node *)n;
  				}
! 			| LAST_P opt_from_in cursor_name
  				{
  					FetchStmt *n = makeNode(FetchStmt);
+ 					n->portalname = $3;
  					n->direction = FETCH_ABSOLUTE;
  					n->howMany = -1;
  					$$ = (Node *)n;
  				}
! 			| ABSOLUTE_P SignedIconst opt_from_in cursor_name
  				{
  					FetchStmt *n = makeNode(FetchStmt);
+ 					n->portalname = $4;
  					n->direction = FETCH_ABSOLUTE;
  					n->howMany = $2;
  					$$ = (Node *)n;
  				}
! 			| RELATIVE_P SignedIconst opt_from_in cursor_name
  				{
  					FetchStmt *n = makeNode(FetchStmt);
+ 					n->portalname = $4;
  					n->direction = FETCH_RELATIVE;
  					n->howMany = $2;
  					$$ = (Node *)n;
  				}
! 			| SignedIconst opt_from_in cursor_name
  				{
  					FetchStmt *n = makeNode(FetchStmt);
+ 					n->portalname = $3;
  					n->direction = FETCH_FORWARD;
  					n->howMany = $1;
  					$$ = (Node *)n;
  				}
! 			| ALL opt_from_in cursor_name
  				{
  					FetchStmt *n = makeNode(FetchStmt);
+ 					n->portalname = $3;
  					n->direction = FETCH_FORWARD;
  					n->howMany = FETCH_ALL;
  					$$ = (Node *)n;
  				}
! 			| FORWARD opt_from_in cursor_name
  				{
  					FetchStmt *n = makeNode(FetchStmt);
+ 					n->portalname = $3;
  					n->direction = FETCH_FORWARD;
  					n->howMany = 1;
  					$$ = (Node *)n;
  				}
! 			| FORWARD SignedIconst opt_from_in cursor_name
  				{
  					FetchStmt *n = makeNode(FetchStmt);
+ 					n->portalname = $4;
  					n->direction = FETCH_FORWARD;
  					n->howMany = $2;
  					$$ = (Node *)n;
  				}
! 			| FORWARD ALL opt_from_in cursor_name
  				{
  					FetchStmt *n = makeNode(FetchStmt);
+ 					n->portalname = $4;
  					n->direction = FETCH_FORWARD;
  					n->howMany = FETCH_ALL;
  					$$ = (Node *)n;
  				}
! 			| BACKWARD opt_from_in cursor_name
  				{
  					FetchStmt *n = makeNode(FetchStmt);
+ 					n->portalname = $3;
  					n->direction = FETCH_BACKWARD;
  					n->howMany = 1;
  					$$ = (Node *)n;
  				}
! 			| BACKWARD SignedIconst opt_from_in cursor_name
  				{
  					FetchStmt *n = makeNode(FetchStmt);
+ 					n->portalname = $4;
  					n->direction = FETCH_BACKWARD;
  					n->howMany = $2;
  					$$ = (Node *)n;
  				}
! 			| BACKWARD ALL opt_from_in cursor_name
  				{
  					FetchStmt *n = makeNode(FetchStmt);
+ 					n->portalname = $4;
  					n->direction = FETCH_BACKWARD;
  					n->howMany = FETCH_ALL;
  					$$ = (Node *)n;
*************** from_in:	FROM									{}
*** 4228,4233 ****
--- 4231,4241 ----
  			| IN_P									{}
  		;
  
+ opt_from_in:	FROM									{}
+ 			| IN_P									{}
+ 			| /* EMPTY */								{}
+ 		;
+ 
  
  /*****************************************************************************
   *
*************** set_target_list:
*** 6847,6853 ****
   *				CURSOR STATEMENTS
   *
   *****************************************************************************/
! DeclareCursorStmt: DECLARE name cursor_options CURSOR opt_hold FOR SelectStmt
  				{
  					DeclareCursorStmt *n = makeNode(DeclareCursorStmt);
  					n->portalname = $2;
--- 6855,6861 ----
   *				CURSOR STATEMENTS
   *
   *****************************************************************************/
! DeclareCursorStmt: DECLARE cursor_name cursor_options CURSOR opt_hold FOR SelectStmt
  				{
  					DeclareCursorStmt *n = makeNode(DeclareCursorStmt);
  					n->portalname = $2;
*************** DeclareCursorStmt: DECLARE name cursor_o
*** 6858,6863 ****
--- 6866,6874 ----
  				}
  		;
  
+ cursor_name:	name						{ $$ = $1; }
+ 		;
+ 
  cursor_options: /*EMPTY*/					{ $$ = 0; }
  			| cursor_options NO SCROLL		{ $$ = $1 | CURSOR_OPT_NO_SCROLL; }
  			| cursor_options SCROLL			{ $$ = $1 | CURSOR_OPT_SCROLL; }
diff -dcrpN pgsql.orig/src/interfaces/ecpg/preproc/ecpg.addons pgsql/src/interfaces/ecpg/preproc/ecpg.addons
*** pgsql.orig/src/interfaces/ecpg/preproc/ecpg.addons	2009-01-30 17:28:46.000000000 +0100
--- pgsql/src/interfaces/ecpg/preproc/ecpg.addons	2009-08-09 19:50:07.000000000 +0200
*************** ECPG: ConstraintAttributeSpecConstraintT
*** 206,226 ****
  			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
  		if ($2[0] == '$')
  		{
  			free($2);
  			$2 = make_str("$0");
  		}
  ECPG: PrepareStmtPREPAREprepared_nameprep_type_clauseASPreparableStmt block
  	{
  		$$.name = $2;
--- 206,287 ----
  			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
  		if ($1[0] == '$')
  		{
  			free($1);
  			$1 = make_str("$0");
  		}
! ECPG: fetch_argscursor_name addon
! 		current_cursor = mm_strdup($1);
! 		if ($1[0] == ':')
! 		{
! 			free($1);
! 			$1 = make_str("$0");
! 		}
! ECPG: fetch_argsfrom_incursor_name addon
! 		current_cursor = mm_strdup($2);
! 		if ($2[0] == ':')
! 		{
! 			free($2);
! 			$2 = make_str("$0");
! 		}
! ECPG: fetch_argsNEXTopt_from_incursor_name addon
! ECPG: fetch_argsPRIORopt_from_incursor_name addon
! ECPG: fetch_argsFIRST_Popt_from_incursor_name addon
! ECPG: fetch_argsLAST_Popt_from_incursor_name addon
! ECPG: fetch_argsALLopt_from_incursor_name addon
! ECPG: fetch_argsFORWARDopt_from_incursor_name addon
! ECPG: fetch_argsBACKWARDopt_from_incursor_name addon
! 		current_cursor = mm_strdup($3);
! 		if ($3[0] == ':')
! 		{
! 			free($3);
! 			$3 = make_str("$0");
! 		}
! ECPG: fetch_argsSignedIconstopt_from_incursor_name addon
! 		if ($1[0] == '$')
! 		{
! 			free($1);
! 			$1 = make_str("$0");
! 		}
! 		current_cursor = mm_strdup($3);
! 		if ($3[0] == ':')
! 		{
! 			free($3);
! 			$3 = make_str("$0");
! 		}
! ECPG: fetch_argsFORWARDALLopt_from_incursor_name addon
! ECPG: fetch_argsBACKWARDALLopt_from_incursor_name addon
! 		current_cursor = mm_strdup($4);
! 		if ($4[0] == ':')
! 		{
! 			free($4);
! 			$4 = make_str("$0");
! 		}
! ECPG: fetch_argsABSOLUTE_PSignedIconstopt_from_incursor_name addon
! ECPG: fetch_argsRELATIVE_PSignedIconstopt_from_incursor_name addon
! ECPG: fetch_argsFORWARDSignedIconstopt_from_incursor_name addon
! ECPG: fetch_argsBACKWARDSignedIconstopt_from_incursor_name addon
  		if ($2[0] == '$')
  		{
  			free($2);
  			$2 = make_str("$0");
  		}
+ 		current_cursor = mm_strdup($4);
+ 		if ($4[0] == ':')
+ 		{
+ 			free($4);
+ 			$4 = make_str("$0");
+ 		}
+ ECPG: cursor_namename rule
+ 	| char_civar
+ 		{
+ 			char *curname = mm_alloc(strlen($1) + 2);
+ 			sprintf(curname, ":%s", $1);
+ 			free($1);
+ 			$1 = curname;
+ 			$$ = $1;
+ 		}
  ECPG: PrepareStmtPREPAREprepared_nameprep_type_clauseASPreparableStmt block
  	{
  		$$.name = $2;
*************** ECPG: PrepareStmtPREPAREprepared_namepre
*** 235,243 ****
  	}
  ECPG: ExecuteStmtEXECUTEprepared_nameexecute_param_clauseexecute_rest block
  	{ $$ = $2; }
! ECPG: DeclareCursorStmtDECLAREnamecursor_optionsCURSORopt_holdFORSelectStmt block
  	{
  		struct cursor *ptr, *this;
  
  		for (ptr = cur; ptr != NULL; ptr = ptr->next)
  		{
--- 296,305 ----
  	}
  ECPG: ExecuteStmtEXECUTEprepared_nameexecute_param_clauseexecute_rest block
  	{ $$ = $2; }
! ECPG: DeclareCursorStmtDECLAREcursor_namecursor_optionsCURSORopt_holdFORSelectStmt block
  	{
  		struct cursor *ptr, *this;
+ 		char *cursor_marker = $2[0] == ':' ? make_str("$0") : mm_strdup($2);
  
  		for (ptr = cur; ptr != NULL; ptr = ptr->next)
  		{
*************** ECPG: DeclareCursorStmtDECLAREnamecursor
*** 251,257 ****
  		this->name = $2;
  		this->connection = connection;
  		this->opened = false;
! 		this->command =  cat_str(7, make_str("declare"), mm_strdup($2), $3, make_str("cursor"), $5, make_str("for"), $7);
  		this->argsinsert = argsinsert;
  		this->argsresult = argsresult;
  		argsinsert = argsresult = NULL;
--- 313,319 ----
  		this->name = $2;
  		this->connection = connection;
  		this->opened = false;
! 		this->command =  cat_str(7, make_str("declare"), cursor_marker, $3, make_str("cursor"), $5, make_str("for"), $7);
  		this->argsinsert = argsinsert;
  		this->argsresult = argsresult;
  		argsinsert = argsresult = NULL;
*************** ECPG: DeclareCursorStmtDECLAREnamecursor
*** 262,267 ****
--- 324,334 ----
  		else
  			$$ = cat_str(3, make_str("/*"), mm_strdup(this->command), make_str("*/"));
  	}
+ ECPG: ClosePortalStmtCLOSEcursor_name block
+ 	{
+ 		char *cursor_marker = $2[0] == ':' ? make_str("$0") : $2;
+ 		$$ = cat2_str(make_str("close"), cursor_marker);
+ 	}
  ECPG: opt_hold block
  	{
  		if (compat == ECPG_COMPAT_INFORMIX_SE && autocommit == true)
*************** ECPG: VariableShowStmtSHOWALL block
*** 326,372 ****
  		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
  	{
! 		add_additional_variables($2, false);
! 		$$ = cat_str(2, 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: SpecialRuleRelationOLD addon
  		if (!QueryIsRule)
  			mmerror(PARSE_ERROR, ET_ERROR, "OLD used in query that is not in a rule");
--- 393,412 ----
  		mmerror(PARSE_ERROR, ET_ERROR, "SHOW ALL is not implemented");
  		$$ = EMPTY;
  	}
! ECPG: FetchStmtFETCHfetch_args block
  	{
! 		add_additional_variables(current_cursor, false);
! 		free(current_cursor);
! 		current_cursor = NULL;
! 		$$ = cat2_str(2, make_str("fetch"), $2);
  	}
! ECPG: FetchStmtMOVEfetch_args block
  	{
! 		add_additional_variables(current_cursor, false);
! 		free(current_cursor);
! 		current_cursor = NULL;
! 		$$ = cat2_str(2, make_str("move"), $2);
  	}
  ECPG: SpecialRuleRelationOLD addon
  		if (!QueryIsRule)
  			mmerror(PARSE_ERROR, ET_ERROR, "OLD used in query that is not in a rule");
diff -dcrpN pgsql.orig/src/interfaces/ecpg/preproc/ecpg.c pgsql/src/interfaces/ecpg/preproc/ecpg.c
*** pgsql.orig/src/interfaces/ecpg/preproc/ecpg.c	2009-08-07 13:06:28.000000000 +0200
--- pgsql/src/interfaces/ecpg/preproc/ecpg.c	2009-08-09 18:13:08.000000000 +0200
*************** enum COMPAT_MODE compat = ECPG_COMPAT_PG
*** 26,31 ****
--- 26,32 ----
  
  struct _include_path *include_paths = NULL;
  struct cursor *cur = NULL;
+ char *current_cursor = NULL;
  struct typedefs *types = NULL;
  struct _defines *defines = NULL;
  
diff -dcrpN pgsql.orig/src/interfaces/ecpg/preproc/ecpg.trailer pgsql/src/interfaces/ecpg/preproc/ecpg.trailer
*** pgsql.orig/src/interfaces/ecpg/preproc/ecpg.trailer	2009-08-07 13:06:28.000000000 +0200
--- pgsql/src/interfaces/ecpg/preproc/ecpg.trailer	2009-08-09 15:42:06.000000000 +0200
*************** prepared_name: name             {
*** 285,293 ****
   * Declare a prepared cursor. The syntax is different from the standard
   * declare statement, so we create a new rule.
   */
! ECPGCursorStmt:  DECLARE name cursor_options CURSOR opt_hold FOR prepared_name
  		{
  			struct cursor *ptr, *this;
  			struct variable *thisquery = (struct variable *)mm_alloc(sizeof(struct variable));
  			const char *con = connection ? connection : "NULL";
  
--- 285,294 ----
   * Declare a prepared cursor. The syntax is different from the standard
   * declare statement, so we create a new rule.
   */
! ECPGCursorStmt:  DECLARE cursor_name cursor_options CURSOR opt_hold FOR prepared_name
  		{
  			struct cursor *ptr, *this;
+ 			char *cursor_marker = $2[0] == ':' ? make_str("$0") : mm_strdup($2);
  			struct variable *thisquery = (struct variable *)mm_alloc(sizeof(struct variable));
  			const char *con = connection ? connection : "NULL";
  
*************** ECPGCursorStmt:  DECLARE name cursor_opt
*** 304,310 ****
  			this->next = cur;
  			this->name = $2;
  			this->connection = connection;
! 			this->command =  cat_str(6, make_str("declare"), mm_strdup($2), $3, make_str("cursor"), $5, make_str("for $1"));
  			this->argsresult = NULL;
  
  			thisquery->type = &ecpg_query;
--- 305,311 ----
  			this->next = cur;
  			this->name = $2;
  			this->connection = connection;
! 			this->command =  cat_str(6, make_str("declare"), cursor_marker, $3, make_str("cursor"), $5, make_str("for $1"));
  			this->argsresult = NULL;
  
  			thisquery->type = &ecpg_query;
*************** ECPGCursorStmt:  DECLARE name cursor_opt
*** 314,319 ****
--- 315,326 ----
  			sprintf(thisquery->name, "ECPGprepared_statement(%s, %s, __LINE__)", con, $7);
  
  			this->argsinsert = NULL;
+ 			if ($2[0] == ':')
+ 			{
+ 				struct variable *var = find_variable($2 + 1);
+ 				remove_variable_from_list(&argsinsert, var);
+ 				add_variable_to_head(&(this->argsinsert), var, &no_indicator);
+ 			}
  			add_variable_to_head(&(this->argsinsert), thisquery, &no_indicator);
  
  			cur = this;
*************** ECPGFree:	SQL_FREE name	{ $$ = $2; }
*** 954,960 ****
  /*
   * open is an open cursor, at the moment this has to be removed
   */
! ECPGOpen: SQL_OPEN name opt_ecpg_using { $$ = $2; };
  
  opt_ecpg_using: /*EMPTY*/	{ $$ = EMPTY; }
  		| ecpg_using		{ $$ = $1; }
--- 961,976 ----
  /*
   * open is an open cursor, at the moment this has to be removed
   */
! ECPGOpen: SQL_OPEN cursor_name opt_ecpg_using
! 		{
! 			if ($2[0] == ':')
! 			{
! 				struct variable *var = find_variable($2 + 1);
! 				remove_variable_from_list(&argsinsert, var);
! 			}
! 			$$ = $2;
! 		}
! 		;
  
  opt_ecpg_using: /*EMPTY*/	{ $$ = EMPTY; }
  		| ecpg_using		{ $$ = $1; }
*************** civarind: cvariable indicator
*** 1779,1784 ****
--- 1795,1807 ----
  		}
  		;
  
+ char_civar: char_variable
+ 		{
+ 			add_variable_to_head(&argsinsert, find_variable($1), &no_indicator);
+ 			$$ = $1;
+ 		}
+ 		;
+ 
  civar: cvariable
  		{
  			add_variable_to_head(&argsinsert, find_variable($1), &no_indicator);
diff -dcrpN pgsql.orig/src/interfaces/ecpg/preproc/ecpg.type pgsql/src/interfaces/ecpg/preproc/ecpg.type
*** pgsql.orig/src/interfaces/ecpg/preproc/ecpg.type	2008-11-14 11:03:33.000000000 +0100
--- pgsql/src/interfaces/ecpg/preproc/ecpg.type	2009-08-09 15:42:06.000000000 +0200
***************
*** 43,48 ****
--- 43,49 ----
  %type <str> c_term
  %type <str> c_thing
  %type <str> char_variable
+ %type <str> char_civar
  %type <str> civar
  %type <str> civarind
  %type <str> ColLabel
diff -dcrpN pgsql.orig/src/interfaces/ecpg/preproc/extern.h pgsql/src/interfaces/ecpg/preproc/extern.h
*** pgsql.orig/src/interfaces/ecpg/preproc/extern.h	2009-07-17 07:50:56.000000000 +0200
--- pgsql/src/interfaces/ecpg/preproc/extern.h	2009-08-09 18:13:31.000000000 +0200
*************** extern char *output_filename;
*** 47,52 ****
--- 47,53 ----
  
  extern struct _include_path *include_paths;
  extern struct cursor *cur;
+ extern char *current_cursor;
  extern struct typedefs *types;
  extern struct _defines *defines;
  extern struct ECPGtype ecpg_no_indicator;
*************** extern struct descriptor *lookup_descrip
*** 91,96 ****
--- 92,98 ----
  extern struct variable *descriptor_variable(const char *name, int input);
  extern void add_variable_to_head(struct arguments **, struct variable *, struct variable *);
  extern void add_variable_to_tail(struct arguments **, struct variable *, struct variable *);
+ extern void remove_variable_from_list(struct arguments ** list, struct variable * var);
  extern void dump_variables(struct arguments *, int);
  extern struct typedefs *get_typedef(char *);
  extern void adjust_array(enum ECPGttype, char **, char **, char *, char *, int, bool);
diff -dcrpN pgsql.orig/src/interfaces/ecpg/preproc/parse.pl pgsql/src/interfaces/ecpg/preproc/parse.pl
*** pgsql.orig/src/interfaces/ecpg/preproc/parse.pl	2009-01-30 17:28:46.000000000 +0100
--- pgsql/src/interfaces/ecpg/preproc/parse.pl	2009-08-09 17:53:11.000000000 +0200
*************** $replace_types{'unreserved_keyword'} = '
*** 57,63 ****
  $replace_types{'Sconst'} = 'ignore';
  
  # some production rules have to be ignored or replaced
! $replace_line{'fetch_direction'} = 'ignore';
  $replace_line{"opt_array_boundsopt_array_bounds'['Iconst']'"} = 'ignore';
  $replace_line{'col_name_keywordCHAR_P'} = 'ignore';
  $replace_line{'col_name_keywordINT_P'} = 'ignore';
--- 57,63 ----
  $replace_types{'Sconst'} = 'ignore';
  
  # some production rules have to be ignored or replaced
! $replace_line{'fetch_args'} = 'ignore';
  $replace_line{"opt_array_boundsopt_array_bounds'['Iconst']'"} = 'ignore';
  $replace_line{'col_name_keywordCHAR_P'} = 'ignore';
  $replace_line{'col_name_keywordINT_P'} = 'ignore';
diff -dcrpN pgsql.orig/src/interfaces/ecpg/preproc/variable.c pgsql/src/interfaces/ecpg/preproc/variable.c
*** pgsql.orig/src/interfaces/ecpg/preproc/variable.c	2009-08-07 13:06:28.000000000 +0200
--- pgsql/src/interfaces/ecpg/preproc/variable.c	2009-08-09 15:42:06.000000000 +0200
*************** add_variable_to_tail(struct arguments **
*** 401,406 ****
--- 401,430 ----
  		*list = new;
  }
  
+ void
+ remove_variable_from_list(struct arguments ** list, struct variable * var)
+ {
+ 	struct arguments *p, *prev = NULL;
+ 	bool found = false;
+ 
+ 	for (p = *list; p; p = p->next)
+ 	{
+ 		if (p->variable == var)
+ 		{
+ 			found = true;
+ 			break;
+ 		}
+ 		prev = p;
+ 	}
+ 	if (found)
+ 	{
+ 		if (prev)
+ 			prev->next = p->next;
+ 		else
+ 			*list = p->next;
+ 	}
+ }
+ 
  /* Dump out a list of all the variable on this list.
     This is a recursive function that works from the end of the list and
     deletes the list as we go on.
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to