Hello,

this small patch add missing USING clause to OPEN FOR EXECUTE statement
+ cleaning part of exec_stmt_open function


see http://archives.postgresql.org/pgsql-hackers/2009-11/msg00713.php

Regards
Pavel Stehule
*** ./doc/src/sgml/plpgsql.sgml.orig	2009-11-13 23:43:39.000000000 +0100
--- ./doc/src/sgml/plpgsql.sgml	2009-11-17 20:30:10.656208300 +0100
***************
*** 2488,2494 ****
       <title><command>OPEN FOR EXECUTE</command></title>
  
  <synopsis>
! OPEN <replaceable>unbound_cursorvar</replaceable> <optional> <optional> NO </optional> SCROLL </optional> FOR EXECUTE <replaceable class="command">query_string</replaceable>;
  </synopsis>
  
           <para>
--- 2488,2494 ----
       <title><command>OPEN FOR EXECUTE</command></title>
  
  <synopsis>
! OPEN <replaceable>unbound_cursorvar</replaceable> <optional> <optional> NO </optional> SCROLL </optional> FOR EXECUTE <replaceable class="command">query_string</replaceable> <optional> USING <replaceable>expression</replaceable> <optional>, ...</optional> </optional>;
  </synopsis>
  
           <para>
***************
*** 2500,2506 ****
            command.  As usual, this gives flexibility so the query plan can vary
            from one run to the next (see <xref linkend="plpgsql-plan-caching">),
            and it also means that variable substitution is not done on the
!           command string.
            The <literal>SCROLL</> and
            <literal>NO SCROLL</> options have the same meanings as for a bound
            cursor.
--- 2500,2507 ----
            command.  As usual, this gives flexibility so the query plan can vary
            from one run to the next (see <xref linkend="plpgsql-plan-caching">),
            and it also means that variable substitution is not done on the
!           command string. As with <command>EXECUTE</command>, parameter values 
!           can be inserted into the dynamic command via <literal>USING</>.
            The <literal>SCROLL</> and
            <literal>NO SCROLL</> options have the same meanings as for a bound
            cursor.
***************
*** 2509,2515 ****
         <para>
          An example:
  <programlisting>
! OPEN curs1 FOR EXECUTE 'SELECT * FROM ' || quote_ident($1);
  </programlisting>
         </para>
       </sect3>
--- 2510,2516 ----
         <para>
          An example:
  <programlisting>
! OPEN curs1 FOR EXECUTE 'SELECT * FROM ' || quote_ident($1) ' WHERE col1 = $1' USING var1;
  </programlisting>
         </para>
       </sect3>
*** ./src/pl/plpgsql/src/gram.y.orig	2009-11-13 23:43:40.000000000 +0100
--- ./src/pl/plpgsql/src/gram.y	2009-11-17 14:37:33.927208178 +0100
***************
*** 1686,1692 ****
  							tok = yylex();
  							if (tok == K_EXECUTE)
  							{
! 								new->dynquery = read_sql_stmt("SELECT ");
  							}
  							else
  							{
--- 1686,1712 ----
  							tok = yylex();
  							if (tok == K_EXECUTE)
  							{
! 								int endtoken;
! 
! 								new->dynquery = read_sql_construct(K_USING, ';', 0,
! 														    "USING or ;",
! 														    "SELECT ",
! 														    true, true,
! 														    NULL, &endtoken);
! 														    
! 								/* If we found "USING", collect the argument(s) */
! 								if (endtoken == K_USING)
! 								{
! 									PLpgSQL_expr *expr;
! 									
! 									do
! 									{
! 										expr = read_sql_expression2(',', ';',
! 															", or ;",
! 															&endtoken);
! 										new->params = lappend(new->params, expr);
! 									} while (endtoken == ',');
! 								}
  							}
  							else
  							{
*** ./src/pl/plpgsql/src/pl_exec.c.orig	2009-11-09 01:26:55.000000000 +0100
--- ./src/pl/plpgsql/src/pl_exec.c	2009-11-17 19:48:47.209207349 +0100
***************
*** 199,206 ****
  static PreparedParamsData *exec_eval_using_params(PLpgSQL_execstate *estate,
  					   List *params);
  static void free_params_data(PreparedParamsData *ppd);
! static Portal exec_dynquery_with_params(PLpgSQL_execstate *estate,
! 						  PLpgSQL_expr *query, List *params);
  
  
  /* ----------
--- 199,206 ----
  static PreparedParamsData *exec_eval_using_params(PLpgSQL_execstate *estate,
  					   List *params);
  static void free_params_data(PreparedParamsData *ppd);
! static Portal exec_dynquery_with_params(PLpgSQL_execstate *estate, char *posrtalname,
! 						  PLpgSQL_expr *query, List *params, int cursorOption);
  
  
  /* ----------
***************
*** 2343,2350 ****
  	{
  		/* RETURN QUERY EXECUTE */
  		Assert(stmt->dynquery != NULL);
! 		portal = exec_dynquery_with_params(estate, stmt->dynquery,
! 										   stmt->params);
  	}
  
  	tupmap = convert_tuples_by_position(portal->tupDesc,
--- 2343,2350 ----
  	{
  		/* RETURN QUERY EXECUTE */
  		Assert(stmt->dynquery != NULL);
! 		portal = exec_dynquery_with_params(estate, NULL, stmt->dynquery,
! 										   stmt->params, 0);
  	}
  
  	tupmap = convert_tuples_by_position(portal->tupDesc,
***************
*** 3123,3129 ****
  	Portal		portal;
  	int			rc;
  
! 	portal = exec_dynquery_with_params(estate, stmt->query, stmt->params);
  
  	/*
  	 * Execute the loop
--- 3123,3129 ----
  	Portal		portal;
  	int			rc;
  
! 	portal = exec_dynquery_with_params(estate, NULL, stmt->query, stmt->params, 0);
  
  	/*
  	 * Execute the loop
***************
*** 3191,3234 ****
  		 * This is an OPEN refcursor FOR EXECUTE ...
  		 * ----------
  		 */
! 		Datum		queryD;
! 		Oid			restype;
! 		char	   *querystr;
! 		SPIPlanPtr	curplan;
! 
! 		/* ----------
! 		 * We evaluate the string expression after the
! 		 * EXECUTE keyword. It's result is the querystring we have
! 		 * to execute.
! 		 * ----------
! 		 */
! 		queryD = exec_eval_expr(estate, stmt->dynquery, &isnull, &restype);
! 		if (isnull)
! 			ereport(ERROR,
! 					(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
! 					 errmsg("query string argument of EXECUTE is null")));
! 
! 		/* Get the C-String representation */
! 		querystr = convert_value_to_string(queryD, restype);
! 
! 		exec_eval_cleanup(estate);
! 
! 		/* ----------
! 		 * Now we prepare a query plan for it and open a cursor
! 		 * ----------
! 		 */
! 		curplan = SPI_prepare_cursor(querystr, 0, NULL, stmt->cursor_options);
! 		if (curplan == NULL)
! 			elog(ERROR, "SPI_prepare_cursor failed for \"%s\": %s",
! 				 querystr, SPI_result_code_string(SPI_result));
! 		portal = SPI_cursor_open(curname, curplan, NULL, NULL,
! 								 estate->readonly_func);
! 		if (portal == NULL)
! 			elog(ERROR, "could not open cursor for query \"%s\": %s",
! 				 querystr, SPI_result_code_string(SPI_result));
! 		pfree(querystr);
! 		SPI_freeplan(curplan);
! 
  		/*
  		 * If cursor variable was NULL, store the generated portal name in it
  		 */
--- 3191,3201 ----
  		 * This is an OPEN refcursor FOR EXECUTE ...
  		 * ----------
  		 */
! 		portal = exec_dynquery_with_params(estate, curname,
! 								stmt->dynquery,
! 								stmt->params,
! 								stmt->cursor_options);
! 								
  		/*
  		 * If cursor variable was NULL, store the generated portal name in it
  		 */
***************
*** 5520,5527 ****
   * Open portal for dynamic query
   */
  static Portal
! exec_dynquery_with_params(PLpgSQL_execstate *estate, PLpgSQL_expr *dynquery,
! 						  List *params)
  {
  	Portal		portal;
  	Datum		query;
--- 5487,5496 ----
   * Open portal for dynamic query
   */
  static Portal
! exec_dynquery_with_params(PLpgSQL_execstate *estate, char *portalname, 
! 						  PLpgSQL_expr *dynquery,
! 						  List *params,
! 						  int cursorOption)
  {
  	Portal		portal;
  	Datum		query;
***************
*** 5554,5573 ****
  		PreparedParamsData *ppd;
  
  		ppd = exec_eval_using_params(estate, params);
! 		portal = SPI_cursor_open_with_args(NULL,
  										   querystr,
  										   ppd->nargs, ppd->types,
  										   ppd->values, ppd->nulls,
! 										   estate->readonly_func, 0);
  		free_params_data(ppd);
  	}
  	else
  	{
! 		portal = SPI_cursor_open_with_args(NULL,
  										   querystr,
  										   0, NULL,
  										   NULL, NULL,
! 										   estate->readonly_func, 0);
  	}
  
  	if (portal == NULL)
--- 5523,5544 ----
  		PreparedParamsData *ppd;
  
  		ppd = exec_eval_using_params(estate, params);
! 		portal = SPI_cursor_open_with_args(portalname,
  										   querystr,
  										   ppd->nargs, ppd->types,
  										   ppd->values, ppd->nulls,
! 										   estate->readonly_func, 
! 										   cursorOption);
  		free_params_data(ppd);
  	}
  	else
  	{
! 		portal = SPI_cursor_open_with_args(portalname,
  										   querystr,
  										   0, NULL,
  										   NULL, NULL,
! 										   estate->readonly_func, 
! 										   cursorOption);
  	}
  
  	if (portal == NULL)
*** ./src/pl/plpgsql/src/pl_funcs.c.orig	2009-11-12 01:13:00.000000000 +0100
--- ./src/pl/plpgsql/src/pl_funcs.c	2009-11-17 21:03:12.172210054 +0100
***************
*** 619,627 ****
  		printf("  execute = '");
  		dump_expr(stmt->dynquery);
  		printf("'\n");
  	}
  	dump_indent -= 2;
- 
  }
  
  static void
--- 619,646 ----
  		printf("  execute = '");
  		dump_expr(stmt->dynquery);
  		printf("'\n");
+ 
+ 		if (stmt->params != NIL)
+ 		{
+ 			ListCell   *lc;
+ 			int			i;
+ 
+ 			dump_indent += 2;
+ 			dump_ind();
+ 			printf("    USING\n");
+ 			dump_indent += 2;
+ 			i = 1;
+ 			foreach(lc, stmt->params)
+ 			{
+ 				dump_ind();
+ 				printf("    parameter $%d: ", i++);
+ 				dump_expr((PLpgSQL_expr *) lfirst(lc));
+ 				printf("\n");
+ 			}
+ 			dump_indent -= 4;
+ 		}
  	}
  	dump_indent -= 2;
  }
  
  static void
*** ./src/pl/plpgsql/src/plpgsql.h.orig	2009-11-13 23:43:42.000000000 +0100
--- ./src/pl/plpgsql/src/plpgsql.h	2009-11-17 16:35:35.075209934 +0100
***************
*** 503,508 ****
--- 503,509 ----
  	PLpgSQL_expr *argquery;
  	PLpgSQL_expr *query;
  	PLpgSQL_expr *dynquery;
+ 	List	   *params;			/* USING expressions */
  } PLpgSQL_stmt_open;
  
  
*** ./src/test/regress/expected/plpgsql.out.orig	2009-11-13 23:43:42.000000000 +0100
--- ./src/test/regress/expected/plpgsql.out	2009-11-17 20:11:14.000000000 +0100
***************
*** 3189,3194 ****
--- 3189,3223 ----
          26
  (1 row)
  
+ drop function exc_using(int, text);
+ create or replace function exc_using(int) returns void as $$
+ declare 
+   c refcursor;
+   i int;
+ begin
+   open c for execute 'select * from generate_series(1,$1)' using $1+1;
+   loop
+     fetch c into i;
+     exit when not found;
+     raise notice '%', i;
+   end loop;
+   close c;
+   return;  
+ end;
+ $$ language plpgsql;
+ select exc_using(5);
+ NOTICE:  1
+ NOTICE:  2
+ NOTICE:  3
+ NOTICE:  4
+ NOTICE:  5
+ NOTICE:  6
+  exc_using 
+ -----------
+  
+ (1 row)
+ 
+ drop function exc_using(int);
  -- test FOR-over-cursor
  create or replace function forc01() returns void as $$
  declare
*** ./src/test/regress/sql/plpgsql.sql.orig	2009-11-13 23:43:42.000000000 +0100
--- ./src/test/regress/sql/plpgsql.sql	2009-11-17 20:09:57.030208080 +0100
***************
*** 2629,2634 ****
--- 2629,2656 ----
  
  select exc_using(5, 'foobar');
  
+ drop function exc_using(int, text);
+ 
+ create or replace function exc_using(int) returns void as $$
+ declare 
+   c refcursor;
+   i int;
+ begin
+   open c for execute 'select * from generate_series(1,$1)' using $1+1;
+   loop
+     fetch c into i;
+     exit when not found;
+     raise notice '%', i;
+   end loop;
+   close c;
+   return;  
+ end;
+ $$ language plpgsql;
+ 
+ select exc_using(5);
+ 
+ drop function exc_using(int);
+ 
  -- test FOR-over-cursor
  
  create or replace function forc01() returns void as $$
-- 
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