Hello

this patch add USING clause into plpgsql EXECUTE statements.

Proposal:
http://archives.postgresql.org/pgsql-hackers/2007-10/msg00790.php

I found, so dynamics statements are little bit faster with parameters,
because we don't need call lot of in out/in functions. Mainly it is
barier to SQL injection.

I have question, who will be commiter of plpgsql region? I am quite
irritated from 8.3 process. Bruce's patch queue more or less black
hole, and I have not any idea, if somebody checking my patches or not
and if I have to be in readiness or not.

Patch queue is longer and longer, and I need to know any responsible
person who can be recipient of my reminder request. Really it's
nothing nice, if your work is repeatedly deleted or inserted to
current queue. Nobody can do any plans.

Best regards
Pavel Stehule
*** ./doc/src/sgml/plpgsql.sgml.orig	2007-10-22 10:31:57.000000000 +0200
--- ./doc/src/sgml/plpgsql.sgml	2007-10-22 12:04:52.000000000 +0200
***************
*** 1005,1011 ****
       <command>EXECUTE</command> statement is provided:
  
  <synopsis>
! EXECUTE <replaceable class="command">command-string</replaceable> <optional> INTO <optional>STRICT</optional> <replaceable>target</replaceable> </optional>;
  </synopsis>
  
       where <replaceable>command-string</replaceable> is an expression
--- 1005,1011 ----
       <command>EXECUTE</command> statement is provided:
  
  <synopsis>
! EXECUTE <replaceable class="command">command-string</replaceable> <optional> INTO <optional>STRICT</optional> <replaceable>target</replaceable> </optional> <optional> USING <replaceable class="parameter">expression</replaceable> <optional>, ...</optional> </optional>;
  </synopsis>
  
       where <replaceable>command-string</replaceable> is an expression
***************
*** 1046,1051 ****
--- 1046,1066 ----
       If the <literal>STRICT</> option is given, an error is reported
       unless the query produces exactly one row.
      </para>
+     
+     <para>
+      The <command>EXECUTE</command> statement can take parameters. To refer 
+      to the parameters use $1, $2, $3, etc. Any parameter have to be bind to
+      any variable or any expression with USING clause. You cannot use bind 
+      arguments to pass the names of schema objects to a dynamic SQL statement.
+      The use of arguments is perfect protection from SQL injection.
+ <programlisting>
+ EXECUTE 'SELECT count(*) FROM '
+ 	|| tabname::regclass
+ 	|| ' WHERE inserted_by = $1 AND inserted <= $2'
+    INTO c
+    USING checked_user, checked_date;
+ </programlisting>
+     </para>
  
      <para>
       <command>SELECT INTO</command> is not currently supported within
***************
*** 1924,1930 ****
       rows:
  <synopsis>
  <optional> &lt;&lt;<replaceable>label</replaceable>&gt;&gt; </optional>
! FOR <replaceable>target</replaceable> IN EXECUTE <replaceable>text_expression</replaceable> LOOP 
      <replaceable>statements</replaceable>
  END LOOP <optional> <replaceable>label</replaceable> </optional>;
  </synopsis>
--- 1939,1945 ----
       rows:
  <synopsis>
  <optional> &lt;&lt;<replaceable>label</replaceable>&gt;&gt; </optional>
! FOR <replaceable>target</replaceable> IN EXECUTE <replaceable>text_expression</replaceable> <optional> USING <replaceable class="parameter">expression</replaceable> <optional>, ...</optional> </optional> LOOP 
      <replaceable>statements</replaceable>
  END LOOP <optional> <replaceable>label</replaceable> </optional>;
  </synopsis>
*** ./src/pl/plpgsql/src/gram.y.orig	2007-10-16 10:44:47.000000000 +0200
--- ./src/pl/plpgsql/src/gram.y	2007-10-22 12:07:44.000000000 +0200
***************
*** 21,26 ****
--- 21,27 ----
  
  static PLpgSQL_expr		*read_sql_construct(int until,
  											int until2,
+ 											int until3,
  											const char *expected,
  											const char *sqlstart,
  											bool isexpression,
***************
*** 201,206 ****
--- 202,208 ----
  %token	K_THEN
  %token	K_TO
  %token	K_TYPE
+ %token	K_USING
  %token	K_WARNING
  %token	K_WHEN
  %token	K_WHILE
***************
*** 893,900 ****
  						{
  							PLpgSQL_stmt_dynfors	*new;
  							PLpgSQL_expr			*expr;
  
! 							expr = plpgsql_read_expression(K_LOOP, "LOOP");
  
  							new = palloc0(sizeof(PLpgSQL_stmt_dynfors));
  							new->cmd_type = PLPGSQL_STMT_DYNFORS;
--- 895,908 ----
  						{
  							PLpgSQL_stmt_dynfors	*new;
  							PLpgSQL_expr			*expr;
+ 							int		term;
  
! 							expr = read_sql_construct(K_LOOP, 
! 												K_USING, 
! 												0, 
! 												"LOOP|USING", 
! 												"SELECT ", 
! 												true, true, &term);
  
  							new = palloc0(sizeof(PLpgSQL_stmt_dynfors));
  							new->cmd_type = PLPGSQL_STMT_DYNFORS;
***************
*** 921,926 ****
--- 929,949 ----
  								yyerror("loop variable of loop over rows must be a record or row variable or list of scalar variables");
  							}
  							new->query = expr;
+ 							
+ 							if (term == K_USING)
+ 							{
+ 								for(;;)
+ 								{
+ 									int term;
+ 								
+ 									expr = read_sql_construct(',', K_LOOP, 0, ", or LOOP",
+ 																"SELECT ",
+ 																true, true, &term);
+ 									new->params = lappend(new->params, expr);
+ 									if (term == K_LOOP)
+ 										break;
+ 								}
+ 							}
  
  							$$ = (PLpgSQL_stmt *) new;
  						}
***************
*** 955,960 ****
--- 978,984 ----
  							 */
  							expr1 = read_sql_construct(K_DOTDOT,
  													   K_LOOP,
+ 													   0,
  													   "LOOP",
  													   "SELECT ",
  													   true,
***************
*** 976,981 ****
--- 1000,1006 ----
  								/* Read and check the second one */
  								expr2 = read_sql_construct(K_LOOP,
  														   K_BY,
+ 														   0,
  														   "LOOP",
  														   "SELECT ",
  														   true,
***************
*** 1215,1221 ****
  
  							for (;;)
  							{
! 								expr = read_sql_construct(',', ';', ", or ;",
  														  "SELECT ",
  														  true, true, &term);
  								new->params = lappend(new->params, expr);
--- 1240,1246 ----
  
  							for (;;)
  							{
! 								expr = read_sql_construct(',', ';', 0, ", or ;",
  														  "SELECT ",
  														  true, true, &term);
  								new->params = lappend(new->params, expr);
***************
*** 1300,1309 ****
  						PLpgSQL_expr *expr;
  						int endtoken;
  
! 						expr = read_sql_construct(K_INTO, ';', "INTO|;",
  												  "SELECT ",
  												  true, true, &endtoken);
! 
  						new = palloc(sizeof(PLpgSQL_stmt_dynexecute));
  						new->cmd_type = PLPGSQL_STMT_DYNEXECUTE;
  						new->lineno = $2;
--- 1325,1334 ----
  						PLpgSQL_expr *expr;
  						int endtoken;
  
! 						expr = read_sql_construct(K_INTO, K_USING, ';', "INTO or USING or ;",
  												  "SELECT ",
  												  true, true, &endtoken);
! 												  
  						new = palloc(sizeof(PLpgSQL_stmt_dynexecute));
  						new->cmd_type = PLPGSQL_STMT_DYNEXECUTE;
  						new->lineno = $2;
***************
*** 1312,1327 ****
  						new->strict = false;
  						new->rec = NULL;
  						new->row = NULL;
  
  						/* If we found "INTO", collect the argument */
  						if (endtoken == K_INTO)
  						{
  							new->into = true;
  							read_into_target(&new->rec, &new->row, &new->strict);
! 							if (yylex() != ';')
  								yyerror("syntax error");
  						}
! 
  						$$ = (PLpgSQL_stmt *)new;
  					}
  				;
--- 1337,1368 ----
  						new->strict = false;
  						new->rec = NULL;
  						new->row = NULL;
+ 						new->params = NULL;
  
  						/* If we found "INTO", collect the argument */
  						if (endtoken == K_INTO)
  						{
  							new->into = true;
  							read_into_target(&new->rec, &new->row, &new->strict);
! 					
! 							endtoken = yylex();
! 							if (endtoken != ';' && endtoken != K_USING)
  								yyerror("syntax error");
  						}
! 						
! 						if (endtoken == K_USING)
! 						{							
! 							for(;;)
! 							{
! 								expr = read_sql_construct(',', ';', 0, ", or ;",
! 														"SELECT ",
! 								 						true, true, &endtoken);
! 								new->params = lappend(new->params, expr);
! 								if (endtoken == ';')
! 									break;
! 							}
! 						}
! 						
  						$$ = (PLpgSQL_stmt *)new;
  					}
  				;
***************
*** 1341,1347 ****
  						if ($3->cursor_explicit_expr == NULL)
  						{
  							/* be nice if we could use opt_scrollable here */
! 						    tok = yylex();
  							if (tok == K_NOSCROLL)
  							{
  								new->cursor_options |= CURSOR_OPT_NO_SCROLL;
--- 1382,1388 ----
  						if ($3->cursor_explicit_expr == NULL)
  						{
  							/* be nice if we could use opt_scrollable here */
! 							tok = yylex();
  							if (tok == K_NOSCROLL)
  							{
  								new->cursor_options |= CURSOR_OPT_NO_SCROLL;
***************
*** 1718,1730 ****
  PLpgSQL_expr *
  plpgsql_read_expression(int until, const char *expected)
  {
! 	return read_sql_construct(until, 0, expected, "SELECT ", true, true, NULL);
  }
  
  static PLpgSQL_expr *
  read_sql_stmt(const char *sqlstart)
  {
! 	return read_sql_construct(';', 0, ";", sqlstart, false, true, NULL);
  }
  
  /*
--- 1759,1771 ----
  PLpgSQL_expr *
  plpgsql_read_expression(int until, const char *expected)
  {
! 	return read_sql_construct(until, 0, 0, expected, "SELECT ", true, true, NULL);
  }
  
  static PLpgSQL_expr *
  read_sql_stmt(const char *sqlstart)
  {
! 	return read_sql_construct(';', 0, 0, ";", sqlstart, false, true, NULL);
  }
  
  /*
***************
*** 1732,1737 ****
--- 1773,1779 ----
   *
   * until:		token code for expected terminator
   * until2:		token code for alternate terminator (pass 0 if none)
+  * until3:		token code for alternate terminator (pass 0 if none) 
   * expected:	text to use in complaining that terminator was not found
   * sqlstart:	text to prefix to the accumulated SQL text
   * isexpression: whether to say we're reading an "expression" or a "statement"
***************
*** 1742,1747 ****
--- 1784,1790 ----
  static PLpgSQL_expr *
  read_sql_construct(int until,
  				   int until2,
+ 				   int until3,
  				   const char *expected,
  				   const char *sqlstart,
  				   bool isexpression,
***************
*** 1768,1773 ****
--- 1811,1818 ----
  			break;
  		if (tok == until2 && parenlevel == 0)
  			break;
+ 		if (tok == until3 && parenlevel == 0)
+ 			break;
  		if (tok == '(' || tok == '[')
  			parenlevel++;
  		else if (tok == ')' || tok == ']')
***************
*** 2051,2064 ****
  	else if (pg_strcasecmp(yytext, "absolute") == 0)
  	{
  		fetch->direction = FETCH_ABSOLUTE;
! 		fetch->expr = read_sql_construct(K_FROM, K_IN, "FROM or IN",
  										 "SELECT ", true, true, NULL);
  		check_FROM = false;
  	}
  	else if (pg_strcasecmp(yytext, "relative") == 0)
  	{
  		fetch->direction = FETCH_RELATIVE;
! 		fetch->expr = read_sql_construct(K_FROM, K_IN, "FROM or IN",
  										 "SELECT ", true, true, NULL);
  		check_FROM = false;
  	}
--- 2096,2109 ----
  	else if (pg_strcasecmp(yytext, "absolute") == 0)
  	{
  		fetch->direction = FETCH_ABSOLUTE;
! 		fetch->expr = read_sql_construct(K_FROM, K_IN, 0, "FROM or IN",
  										 "SELECT ", true, true, NULL);
  		check_FROM = false;
  	}
  	else if (pg_strcasecmp(yytext, "relative") == 0)
  	{
  		fetch->direction = FETCH_RELATIVE;
! 		fetch->expr = read_sql_construct(K_FROM, K_IN, 0, "FROM or IN",
  										 "SELECT ", true, true, NULL);
  		check_FROM = false;
  	}
***************
*** 2073,2079 ****
  	else if (tok != T_SCALAR)
  	{
  		plpgsql_push_back_token(tok);
! 		fetch->expr = read_sql_construct(K_FROM, K_IN, "FROM or IN",
  										 "SELECT ", true, true, NULL);
  		check_FROM = false;
  	}
--- 2118,2124 ----
  	else if (tok != T_SCALAR)
  	{
  		plpgsql_push_back_token(tok);
! 		fetch->expr = read_sql_construct(K_FROM, K_IN, 0, "FROM or IN",
  										 "SELECT ", true, true, NULL);
  		check_FROM = false;
  	}
***************
*** 2218,2224 ****
  	new = palloc0(sizeof(PLpgSQL_stmt_return_query));
  	new->cmd_type = PLPGSQL_STMT_RETURN_QUERY;
  	new->lineno = lineno;
! 	new->query = read_sql_construct(';', 0, ")", "", false, true, NULL);
  
  	return (PLpgSQL_stmt *) new;
  }
--- 2263,2269 ----
  	new = palloc0(sizeof(PLpgSQL_stmt_return_query));
  	new->cmd_type = PLPGSQL_STMT_RETURN_QUERY;
  	new->lineno = lineno;
! 	new->query = read_sql_construct(';',0, 0, ")", "", false, true, NULL);
  
  	return (PLpgSQL_stmt *) new;
  }
*** ./src/pl/plpgsql/src/pl_exec.c.orig	2007-10-16 14:50:13.000000000 +0200
--- ./src/pl/plpgsql/src/pl_exec.c	2007-10-21 08:16:04.000000000 +0200
***************
*** 33,38 ****
--- 33,39 ----
  #include "utils/lsyscache.h"
  #include "utils/memutils.h"
  #include "utils/typcache.h"
+ #include "nodes/pg_list.h"
  
  
  static const char *const raise_skip_msg = "RAISE";
***************
*** 65,70 ****
--- 66,81 ----
  static SimpleEstateStackEntry *simple_estate_stack = NULL;
  static long int simple_estate_id_counter = 0;
  
+ typedef struct PreparedParamsData
+ {
+ 	Datum	*values;			/* evaluated arguments */
+ 	bool	*freevals;			/* pfree-able arguments */
+ 	char	*nulls;				/* char array of null info */
+ 	Oid	*types;				/* types of arguments */
+ 	int	nargs;				/* number of arguments */
+ } PreparedParamsData;
+ 
+ 
  /************************************************************
   * Local function forward declarations
   ************************************************************/
***************
*** 177,182 ****
--- 188,196 ----
  static void exec_set_found(PLpgSQL_execstate *estate, bool state);
  static void plpgsql_create_econtext(PLpgSQL_execstate *estate);
  static void free_var(PLpgSQL_var *var);
+ static PreparedParamsData *exec_eval_using_params(PLpgSQL_execstate *estate, 
+ 								List *params);
+ static void free_params_data(PreparedParamsData *evp);
  
  
  /* ----------
***************
*** 2675,2685 ****
  
  	exec_eval_cleanup(estate);
  
! 	/*
! 	 * Call SPI_execute() without preparing a saved plan.
! 	 */
! 	exec_res = SPI_execute(querystr, estate->readonly_func, 0);
  
  	switch (exec_res)
  	{
  		case SPI_OK_SELECT:
--- 2689,2714 ----
  
  	exec_eval_cleanup(estate);
  
! 	/* Evaluate and use using clause params */
! 	if (stmt->params)
! 	{
! 		void *plan;
! 	
! 		PreparedParamsData *up = exec_eval_using_params(estate, stmt->params);
! 		
! 		plan = SPI_prepare(querystr, up->nargs, up->types);
! 		if (plan == NULL)
! 			elog(ERROR, "SPI_prepare failed for \"%s\": %s",
! 				 querystr, SPI_result_code_string(SPI_result));
  
+ 		exec_res = SPI_execute_plan(plan, up->values, up->nulls, estate->readonly_func, 0);
+ 		
+ 		SPI_freeplan(plan);
+ 		free_params_data(up);
+ 	}
+ 	else
+ 		exec_res = SPI_execute(querystr, estate->readonly_func, 0);
+ 		
  	switch (exec_res)
  	{
  		case SPI_OK_SELECT:
***************
*** 2855,2869 ****
  
  	exec_eval_cleanup(estate);
  
! 	/*
! 	 * Prepare a plan and open an implicit cursor for the query
! 	 */
! 	plan = SPI_prepare(querystr, 0, NULL);
! 	if (plan == NULL)
! 		elog(ERROR, "SPI_prepare failed for \"%s\": %s",
! 			 querystr, SPI_result_code_string(SPI_result));
! 	portal = SPI_cursor_open(NULL, plan, NULL, NULL,
  							 estate->readonly_func);
  	if (portal == NULL)
  		elog(ERROR, "could not open implicit cursor for query \"%s\": %s",
  			 querystr, SPI_result_code_string(SPI_result));
--- 2884,2914 ----
  
  	exec_eval_cleanup(estate);
  
! 	/* Evaluate and use using clause params */
! 	if (stmt->params)
! 	{
! 		PreparedParamsData *up = exec_eval_using_params(estate, stmt->params);
! 		
! 		plan = SPI_prepare(querystr, up->nargs, up->types);
! 		if (plan == NULL)
! 			elog(ERROR, "SPI_prepare failed for \"%s\": %s",
! 				 querystr, SPI_result_code_string(SPI_result));
! 
! 		portal = SPI_cursor_open(NULL, plan, up->values, up->nulls,
! 								 estate->readonly_func);
! 		free_params_data(up);
! 	}
! 	else
! 	{
! 		plan = SPI_prepare(querystr, 0, NULL);
! 		if (plan == NULL)
! 			elog(ERROR, "SPI_prepare failed for \"%s\": %s",
! 				 querystr, SPI_result_code_string(SPI_result));
! 
! 		portal = SPI_cursor_open(NULL, plan, NULL, NULL,
  							 estate->readonly_func);
+ 	}
+ 		
  	if (portal == NULL)
  		elog(ERROR, "could not open implicit cursor for query \"%s\": %s",
  			 querystr, SPI_result_code_string(SPI_result));
***************
*** 5070,5072 ****
--- 5115,5194 ----
  		var->freeval = false;
  	}
  }
+ 
+ /*
+  * exec_eval_using_params --- evaluate params of using clause
+  * EXECUTE INTO USING
+  */
+ static PreparedParamsData *
+ exec_eval_using_params(PLpgSQL_execstate *estate, List *params)
+ {
+ 	PreparedParamsData *up; 
+ 	ListCell *current_param;
+ 	int i;
+ 	int nargs; 
+ 	
+ 	
+ 	up = (PreparedParamsData *) palloc(sizeof(PreparedParamsData));
+ 	nargs = list_length(params);
+ 	
+ 	up->types  = (Oid *) palloc(nargs * sizeof(Oid));
+ 	up->values = (Datum *) palloc(nargs * sizeof(Datum));
+ 	up->nulls  = (char *)  palloc(nargs * sizeof(char));
+ 	up->freevals = (bool *) palloc(nargs * sizeof(bool));
+ 	up->nargs = nargs;
+ 	
+ 	current_param = list_head(params);
+ 	for (i = 0; i < nargs; i++)
+ 	{
+ 		bool	isnull;
+ 		
+ 		up->freevals[i] = false;
+ 		up->values[i] = exec_eval_expr(estate, 
+ 						    (PLpgSQL_expr *) lfirst(current_param),
+ 						    &isnull,
+ 						    &up->types[i]);
+ 		up->nulls[i] = isnull ? 'n' : ' ';
+ 
+ 		/* pass-by-ref non null values must be copied into plpgsql context */
+ 		if (!isnull && OidIsValid(up->types[i]))
+ 		{
+ 			int16	typLen;
+ 			bool		typByVal;
+ 			
+ 			get_typlenbyval(up->types[i], &typLen, &typByVal);
+ 						
+ 			if (!typByVal)
+ 			{
+ 				up->values[i] = datumCopy(up->values[i], typByVal, typLen); 
+ 				up->freevals[i] = true;
+ 			} 
+ 		}
+ 
+ 		current_param = lnext(current_param);
+ 		
+ 		exec_eval_cleanup(estate);
+ 	}
+ 	
+ 	return up;
+ }
+ 
+ /*
+  * free_params_data --- pfree all pass-by-reference values used in using clause
+  */
+ static void 
+ free_params_data(PreparedParamsData *up)
+ {
+ 	int 	i;
+ 	
+ 	for (i = 0; i < up->nargs; i++)
+ 		if (up->freevals[i])
+ 			pfree(DatumGetPointer(up->values[i]));
+ 			
+ 	pfree(up->types);
+ 	pfree(up->values);
+ 	pfree(up->nulls);
+ 	pfree(up->freevals);
+ 	
+ 	pfree(up);
+ }
*** ./src/pl/plpgsql/src/pl_funcs.c.orig	2007-10-21 08:17:39.000000000 +0200
--- ./src/pl/plpgsql/src/pl_funcs.c	2007-10-22 00:11:28.000000000 +0200
***************
*** 935,948 ****
  			   stmt->strict ? " STRICT" : "",
  			   stmt->row->rowno, stmt->row->refname);
  	}
- 	if (stmt->
- 
  	dump_indent -= 2;
  }
  
  static void
  dump_dynexecute(PLpgSQL_stmt_dynexecute *stmt)
  {
  	dump_ind();
  	printf("EXECUTE ");
  	dump_expr(stmt->query);
--- 935,949 ----
  			   stmt->strict ? " STRICT" : "",
  			   stmt->row->rowno, stmt->row->refname);
  	}
  	dump_indent -= 2;
  }
  
  static void
  dump_dynexecute(PLpgSQL_stmt_dynexecute *stmt)
  {
+ 	ListCell   *lc;
+ 	int			i = 1;
+ 
  	dump_ind();
  	printf("EXECUTE ");
  	dump_expr(stmt->query);
***************
*** 963,981 ****
  			   stmt->strict ? " STRICT" : "",
  			   stmt->row->rowno, stmt->row->refname);
  	}
  	dump_indent -= 2;
  }
  
  static void
  dump_dynfors(PLpgSQL_stmt_dynfors *stmt)
  {
  	dump_ind();
  	printf("FORS %s EXECUTE ", (stmt->rec != NULL) ? stmt->rec->refname : stmt->row->refname);
  	dump_expr(stmt->query);
  	printf("\n");
! 
  	dump_stmts(stmt->body);
- 
  	dump_ind();
  	printf("    ENDFORS\n");
  }
--- 964,1012 ----
  			   stmt->strict ? " STRICT" : "",
  			   stmt->row->rowno, stmt->row->refname);
  	}
+ 	if (stmt->params != NULL)
+ 	{
+ 		dump_ind();
+ 		printf("    USING\n");
+ 		dump_indent += 2;
+     		foreach(lc, stmt->params)
+ 		{
+ 			dump_ind();
+ 			printf("    parameter %d: ", i++);
+ 			dump_expr((PLpgSQL_expr *) lfirst(lc));
+ 			printf("\n");
+ 		}
+ 		dump_indent -= 2;
+ 	}
  	dump_indent -= 2;
  }
  
  static void
  dump_dynfors(PLpgSQL_stmt_dynfors *stmt)
  {
+ 	ListCell   *lc;
+ 	int			i = 1;
+ 
  	dump_ind();
  	printf("FORS %s EXECUTE ", (stmt->rec != NULL) ? stmt->rec->refname : stmt->row->refname);
  	dump_expr(stmt->query);
  	printf("\n");
! 	if (stmt->params != NULL)
! 	{
! 		dump_indent += 2;
! 		dump_ind();
! 		printf("    USING\n");
! 		dump_indent += 2;
!     		foreach(lc, stmt->params)
! 		{
! 			dump_ind();
! 			printf("    parameter $%d: ", i++);
! 			dump_expr((PLpgSQL_expr *) lfirst(lc));
! 			printf("\n");
! 		}
! 		dump_indent -= 4;
! 	}
  	dump_stmts(stmt->body);
  	dump_ind();
  	printf("    ENDFORS\n");
  }
*** ./src/pl/plpgsql/src/plpgsql.h.orig	2007-10-16 10:45:10.000000000 +0200
--- ./src/pl/plpgsql/src/plpgsql.h	2007-10-16 14:45:04.000000000 +0200
***************
*** 430,435 ****
--- 430,436 ----
  	PLpgSQL_row *row;
  	PLpgSQL_expr *query;
  	List	   *body;			/* List of statements */
+ 	List	 *params;			/* USING params */
  } PLpgSQL_stmt_dynfors;
  
  
***************
*** 534,539 ****
--- 535,541 ----
  	bool		strict;			/* INTO STRICT flag */
  	PLpgSQL_rec *rec;			/* INTO target, if record */
  	PLpgSQL_row *row;			/* INTO target, if row */
+ 	List	 *params;			/* USING params */
  } PLpgSQL_stmt_dynexecute;
  
  
*** ./src/pl/plpgsql/src/scan.l.orig	2007-10-16 10:44:53.000000000 +0200
--- ./src/pl/plpgsql/src/scan.l	2007-10-16 10:48:23.000000000 +0200
***************
*** 163,168 ****
--- 163,169 ----
  then			{ return K_THEN;			}
  to				{ return K_TO;				}
  type			{ return K_TYPE;			}
+ using			{ return K_USING;			}
  warning			{ return K_WARNING;			}
  when			{ return K_WHEN;			}
  while			{ return K_WHILE;			}
*** ./src/test/regress/expected/plpgsql.out.orig	2007-10-21 08:08:37.000000000 +0200
--- ./src/test/regress/expected/plpgsql.out	2007-10-21 08:07:07.000000000 +0200
***************
*** 3128,3130 ****
--- 3128,3177 ----
   c9f0f895fb98ab9159f51fd0297e236d |  8 | t
  (9 rows)
  
+ -- test EXECUTE USING
+ create function exc_using(int)
+ returns int
+ as $$
+ declare i int;
+ begin
+   for i in execute 'select * from ' || 'generate_series' || '(1,$1)' using $1 loop
+     raise notice '%', i;
+   end loop;
+   execute 'select $1+$1*3' into i using $1;
+   return i;
+ end
+ $$ language plpgsql;
+ select exc_using(5);
+ NOTICE:  1
+ NOTICE:  2
+ NOTICE:  3
+ NOTICE:  4
+ NOTICE:  5
+  exc_using 
+ -----------
+         20
+ (1 row)
+ 
+ drop function exc_using(int);
+ -- same for varlena type
+ create function exc_using(varchar)
+ returns setof varchar
+ as $$
+ declare v varchar;
+ begin
+   for v in execute 'select roomno from '|| 'WSlot'::regclass || ' where slotname = $1' using $1 loop
+     return next v;
+   end loop;
+   execute 'select upper($1)' into v using $1;
+   return next v;
+   return;
+ end
+ $$ language plpgsql;
+ select * from exc_using('WS.001.1b');
+  exc_using 
+ -----------
+  001     
+  WS.001.1B
+ (2 rows)
+ 
+ drop function exc_using(varchar);
*** ./src/test/regress/sql/plpgsql.sql.orig	2007-10-22 12:09:17.000000000 +0200
--- ./src/test/regress/sql/plpgsql.sql	2007-10-21 08:06:27.000000000 +0200
***************
*** 2581,2583 ****
--- 2581,2620 ----
  $$ language plpgsql;
  
  select * from ret_query2(8);
+ 
+ -- test EXECUTE USING
+ create function exc_using(int)
+ returns int
+ as $$
+ declare i int;
+ begin
+   for i in execute 'select * from ' || 'generate_series' || '(1,$1)' using $1 loop
+     raise notice '%', i;
+   end loop;
+   execute 'select $1+$1*3' into i using $1;
+   return i;
+ end
+ $$ language plpgsql;
+ 
+ select exc_using(5);
+ 
+ drop function exc_using(int);
+ 
+ -- same for varlena type
+ create function exc_using(varchar)
+ returns setof varchar
+ as $$
+ declare v varchar;
+ begin
+   for v in execute 'select roomno from '|| 'WSlot'::regclass || ' where slotname = $1' using $1 loop
+     return next v;
+   end loop;
+   execute 'select upper($1)' into v using $1;
+   return next v;
+   return;
+ end
+ $$ language plpgsql;
+ 
+ select * from exc_using('WS.001.1b');
+ 
+ drop function exc_using(varchar);
---------------------------(end of broadcast)---------------------------
TIP 9: In versions below 8.0, the planner will ignore your desire to
       choose an index scan if your joining column's datatypes do not
       match

Reply via email to