Hello

I enhanced DO statement syntax to allowing a parameters. Syntax is
relative simple:

do ([varname] vartype := value, ...) $$ ... $$

It allows to pass a content of psql variables to inline code block to
allows more easy scripting

\set schema 'public'

do(text := :'schema') $$
declare r record;
begin
  for r in
      select * from information_schema.tables where table_schema = $1
  loop
    raise notice '>>> table %', r.table_name;
  end loop;
end $$;
NOTICE:  >>> table t
NOTICE:  >>> table t1
DO

ToDo:

* doesn't allows SubLinks :(

pa...@postgres:5432=# do(text := (SELECT :'schema')) $$ declare r
record; begin for r in select * from information_schema.tables where
table_schema = $1 loop raise notice '>>> table %', r.table_name; end
loop; end $$;
ERROR:  XX000: unrecognized node type: 315
LOCATION:  ExecInitExpr, execQual.c:4868

ideas, notes, comments??

Regards

Pavel Stehule
*** ./src/backend/commands/functioncmds.c.orig	2010-02-26 03:00:39.000000000 +0100
--- ./src/backend/commands/functioncmds.c	2010-07-04 07:50:16.175265641 +0200
***************
*** 47,53 ****
--- 47,55 ----
  #include "catalog/pg_type_fn.h"
  #include "commands/defrem.h"
  #include "commands/proclang.h"
+ #include "executor/executor.h"
  #include "miscadmin.h"
+ #include "optimizer/planmain.h"
  #include "optimizer/var.h"
  #include "parser/parse_coerce.h"
  #include "parser/parse_expr.h"
***************
*** 55,60 ****
--- 57,63 ----
  #include "parser/parse_type.h"
  #include "utils/acl.h"
  #include "utils/builtins.h"
+ #include "utils/datum.h"
  #include "utils/fmgroids.h"
  #include "utils/guc.h"
  #include "utils/lsyscache.h"
***************
*** 1928,1934 ****
   *		Execute inline procedural-language code
   */
  void
! ExecuteDoStmt(DoStmt *stmt)
  {
  	InlineCodeBlock *codeblock = makeNode(InlineCodeBlock);
  	ListCell   *arg;
--- 1931,1937 ----
   *		Execute inline procedural-language code
   */
  void
! ExecuteDoStmt(DoStmt *stmt, const char *queryString)
  {
  	InlineCodeBlock *codeblock = makeNode(InlineCodeBlock);
  	ListCell   *arg;
***************
*** 1939,1944 ****
--- 1942,1948 ----
  	Oid			laninline;
  	HeapTuple	languageTuple;
  	Form_pg_language languageStruct;
+ 	ParseState *pstate;
  
  	/* Process options we got from gram.y */
  	foreach(arg, stmt->args)
***************
*** 1973,1978 ****
--- 1977,2088 ----
  				(errcode(ERRCODE_SYNTAX_ERROR),
  				 errmsg("no inline code specified")));
  
+ 	/* Transform parameters - when are used */
+ 	if (stmt->params != NIL)
+ 	{
+ 		ListCell   *param;
+ 		EState	   *estate;
+ 		int	nparams;
+ 		int		i = 0;
+ 
+ 		nparams = list_length(stmt->params);
+ 		
+ 		codeblock->nparams = nparams;
+ 		codeblock->dvalues = (Datum *) palloc(nparams * sizeof(Datum));
+ 		codeblock->nulls = (bool *) palloc(nparams * sizeof(bool));
+ 		codeblock->names = (char **) palloc(nparams * sizeof(char *));
+ 		codeblock->typoids = (Oid *) palloc(nparams * sizeof(Oid));
+ 
+ 		/* prepare pstate for parse analysis of param exprs */
+ 		pstate = make_parsestate(NULL);
+ 		pstate->p_sourcetext = queryString;
+ 		
+ 		foreach(param, stmt->params)
+ 		{
+ 			FunctionParameter *p = (FunctionParameter *) lfirst(param);
+ 			Oid	toid;
+ 			Type		typtup;
+ 			TypeName	*t = p->argType;
+ 			MemoryContext	   oldcontext;
+ 			ExprState *exprstate;
+ 			Node *expr;
+ 			Datum		const_val;
+ 			bool		const_is_null;
+ 			int16		resultTypLen;
+ 			bool		resultTypByVal;
+ 
+ 			codeblock->names[i] = p->name;
+ 
+ 			if (t->setof)
+ 				ereport(ERROR,
+ 					(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+ 					 errmsg("inline procedural code cannot accept set arguments")));
+ 
+ 			typtup = LookupTypeName(NULL, t, NULL);
+ 			if (typtup)
+ 			{
+ 				if (!((Form_pg_type) GETSTRUCT(typtup))->typisdefined)
+ 					ereport(NOTICE,
+ 							(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ 							 errmsg("argument type %s is only a shell",
+ 									TypeNameToString(t))));
+ 				
+ 				toid = typeTypeId(typtup);
+ 				ReleaseSysCache(typtup);
+ 				codeblock->typoids[i] = toid;
+ 			}
+ 			else
+ 			{
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_UNDEFINED_OBJECT),
+ 						 errmsg("type %s does not exist",
+ 								TypeNameToString(t))));
+ 				toid = InvalidOid;	/* keep compiler quiet */
+ 			}
+ 			
+ 			estate = CreateExecutorState();
+ 			
+ 			expr = transformExpr(pstate, p->defexpr);
+ 			expr  = coerce_to_specific_type(pstate, expr,
+ 									    toid, "DEFAULT");
+ 			
+ 			oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
+ 			
+ 			fix_opfuncids(expr);
+ 			
+ 			/* Prepare expr for execution */
+ 			exprstate = ExecPrepareExpr((Expr *) expr, estate);
+ 			
+ 			/* And evaluaye it */
+ 			const_val = ExecEvalExprSwitchContext(exprstate,
+ 									    GetPerTupleExprContext(estate),
+ 									    &const_is_null, NULL);
+ 			/* Get info needed about result datatype */
+ 			get_typlenbyval(toid, &resultTypLen, &resultTypByVal);
+ 									    
+ 			/* Get back outer memory context */
+ 			MemoryContextSwitchTo(oldcontext);
+ 			
+ 			if (!const_is_null)
+ 				const_val = datumCopy(const_val, resultTypByVal, resultTypLen);
+ 			
+ 			codeblock->nulls[i] = const_is_null;
+ 			codeblock->dvalues[i] = const_val;
+ 			
+ 			FreeExecutorState(estate);
+ 			i += 1;
+ 		}
+ 	}
+ 	else
+ 	{
+ 		codeblock->nparams = 0;
+ 		codeblock->dvalues = NULL;
+ 		codeblock->nulls = NULL;
+ 		codeblock->names = NULL;
+ 		codeblock->typoids = NULL;
+ 	}
+ 
+ 
  	/* if LANGUAGE option wasn't specified, use the default */
  	if (language_item)
  		language = strVal(language_item->arg);
*** ./src/backend/nodes/copyfuncs.c.orig	2010-02-26 03:00:43.000000000 +0100
--- ./src/backend/nodes/copyfuncs.c	2010-06-27 13:45:22.340830586 +0200
***************
*** 2680,2685 ****
--- 2680,2686 ----
  	DoStmt	   *newnode = makeNode(DoStmt);
  
  	COPY_NODE_FIELD(args);
+ 	COPY_NODE_FIELD(params);
  
  	return newnode;
  }
*** ./src/backend/nodes/equalfuncs.c.orig	2010-02-26 03:00:43.000000000 +0100
--- ./src/backend/nodes/equalfuncs.c	2010-06-27 13:46:01.856829383 +0200
***************
*** 1242,1247 ****
--- 1242,1248 ----
  _equalDoStmt(DoStmt *a, DoStmt *b)
  {
  	COMPARE_NODE_FIELD(args);
+ 	COMPARE_NODE_FIELD(params);
  
  	return true;
  }
*** ./src/backend/parser/gram.y.orig	2010-06-13 19:43:12.000000000 +0200
--- ./src/backend/parser/gram.y	2010-07-04 07:55:13.124266470 +0200
***************
*** 437,442 ****
--- 437,444 ----
  				opt_frame_clause frame_extent frame_bound
  %type <str>		opt_existing_window_name
  
+ %type <node>	do_arg
+ %type <list>	do_args
  
  /*
   * Non-keyword token types.  These are hard-wired into the "flex" lexer.
***************
*** 5445,5454 ****
   *
   *****************************************************************************/
  
! DoStmt: DO dostmt_opt_list
  				{
  					DoStmt *n = makeNode(DoStmt);
  					n->args = $2;
  					$$ = (Node *)n;
  				}
  		;
--- 5447,5465 ----
   *
   *****************************************************************************/
  
! DoStmt: 
! 			DO '(' do_args ')' dostmt_opt_list
! 				{
! 					DoStmt *n = makeNode(DoStmt);
! 					n->args = $5;
! 					n->params = $3;
! 					$$ = (Node *)n;
! 				}
! 			| DO dostmt_opt_list
  				{
  					DoStmt *n = makeNode(DoStmt);
  					n->args = $2;
+ 					n->params = NIL;
  					$$ = (Node *)n;
  				}
  		;
***************
*** 5469,5474 ****
--- 5480,5519 ----
  				}
  		;
  
+ do_args:
+ 			do_arg
+ 				{
+ 					$$ = list_make1($1);
+ 				}
+ 			| do_args ',' do_arg
+ 				{
+ 					$$ = lappend($1, $3);
+ 				}
+ 			;
+ 
+ do_arg:
+ 			func_type COLON_EQUALS a_expr
+ 				{
+ 					FunctionParameter *n = makeNode(FunctionParameter);
+ 					n->name = NULL;
+ 					n->argType = $1;
+ 					n->mode = FUNC_PARAM_IN;
+ 					n->defexpr = $3;
+ 					$$ = (Node *) n;
+ 				}
+ 			| param_name func_type COLON_EQUALS a_expr
+ 				{
+ 					FunctionParameter *n = makeNode(FunctionParameter);
+ 					n->name = $1;
+ 					n->argType = $2;
+ 					n->mode = FUNC_PARAM_IN;
+ 					n->defexpr = $4;
+ 					$$ = (Node *) n;
+ 				}
+ 		;
+ 
+ 
+ 
  /*****************************************************************************
   *
   *		CREATE CAST / DROP CAST
*** ./src/backend/tcop/utility.c.orig	2010-02-26 03:01:04.000000000 +0100
--- ./src/backend/tcop/utility.c	2010-06-27 16:20:12.075291309 +0200
***************
*** 930,936 ****
  			break;
  
  		case T_DoStmt:
! 			ExecuteDoStmt((DoStmt *) parsetree);
  			break;
  
  		case T_CreatedbStmt:
--- 930,936 ----
  			break;
  
  		case T_DoStmt:
! 			ExecuteDoStmt((DoStmt *) parsetree, queryString);
  			break;
  
  		case T_CreatedbStmt:
*** ./src/include/commands/defrem.h.orig	2010-02-26 03:01:24.000000000 +0100
--- ./src/include/commands/defrem.h	2010-06-27 16:21:19.119051968 +0200
***************
*** 66,72 ****
  extern void DropCastById(Oid castOid);
  extern void AlterFunctionNamespace(List *name, List *argtypes, bool isagg,
  					   const char *newschema);
! extern void ExecuteDoStmt(DoStmt *stmt);
  
  /* commands/operatorcmds.c */
  extern void DefineOperator(List *names, List *parameters);
--- 66,72 ----
  extern void DropCastById(Oid castOid);
  extern void AlterFunctionNamespace(List *name, List *argtypes, bool isagg,
  					   const char *newschema);
! extern void ExecuteDoStmt(DoStmt *stmt, const char *queryString);
  
  /* commands/operatorcmds.c */
  extern void DefineOperator(List *names, List *parameters);
*** ./src/include/nodes/parsenodes.h.orig	2010-02-26 03:01:25.000000000 +0100
--- ./src/include/nodes/parsenodes.h	2010-07-03 20:56:28.828265897 +0200
***************
*** 1991,1996 ****
--- 1991,1997 ----
  {
  	NodeTag		type;
  	List	   *args;			/* List of DefElem nodes */
+ 	List		*params;		/* List of Function parameters */
  } DoStmt;
  
  typedef struct InlineCodeBlock
***************
*** 1999,2004 ****
--- 2000,2010 ----
  	char	   *source_text;	/* source text of anonymous code block */
  	Oid			langOid;		/* OID of selected language */
  	bool		langIsTrusted;	/* trusted property of the language */
+ 	int		nparams;
+ 	Datum		   *dvalues;		/* Values of parameters if they are */
+ 	bool		   *nulls;		/* nulls of parameters if they are */
+ 	char		   **names;		/* used names for parameters if they are */
+ 	Oid		   *typoids;		/* array of parameter types if they are */
  } InlineCodeBlock;
  
  /* ----------------------
*** ./src/pl/plpgsql/src/pl_comp.c.orig	2010-02-26 03:01:34.000000000 +0100
--- ./src/pl/plpgsql/src/pl_comp.c	2010-07-04 07:58:19.441266296 +0200
***************
*** 727,733 ****
   * ----------
   */
  PLpgSQL_function *
! plpgsql_compile_inline(char *proc_source)
  {
  	char	   *func_name = "inline_code_block";
  	PLpgSQL_function *function;
--- 727,733 ----
   * ----------
   */
  PLpgSQL_function *
! plpgsql_compile_inline(InlineCodeBlock *codeblock)
  {
  	char	   *func_name = "inline_code_block";
  	PLpgSQL_function *function;
***************
*** 737,742 ****
--- 737,744 ----
  	int			parse_rc;
  	MemoryContext func_cxt;
  	int			i;
+ 	char		*proc_source = codeblock->source_text;
+ 	int		*in_arg_varnos = NULL;
  
  	/*
  	 * Setup the scanner input and error info.	We assume that this function
***************
*** 812,818 ****
  								 plpgsql_build_datatype(BOOLOID, -1),
  								 true);
  	function->found_varno = var->dno;
! 
  	/*
  	 * Now parse the function's text
  	 */
--- 814,868 ----
  								 plpgsql_build_datatype(BOOLOID, -1),
  								 true);
  	function->found_varno = var->dno;
! 	
! 	/*
! 	 * Complete the function's info
! 	 */
! 	function->fn_nargs = codeblock->nparams;
! 	in_arg_varnos = (int *) palloc(codeblock->nparams * sizeof(int));
! 	
! 	/* 
! 	 * Create variables for inline outer parameters 
! 	 */
! 	for(i = 0; i < codeblock->nparams; i++)
! 	{
! 		char		buf[32];
! 		PLpgSQL_type *argtype;
! 		PLpgSQL_variable *argvariable;
! 		int		argitemtype;
! 
! 		/* Create $n name for variable */
! 		snprintf(buf, sizeof(buf), "$%d", i + 1);
! 		
! 		/* Create datatype info */
! 		argtype = plpgsql_build_datatype(codeblock->typoids[i], -1);
! 		
! 		/* Disallow pseudotype argument */
! 		if (argtype->ttype != PLPGSQL_TTYPE_SCALAR &&
! 			argtype->ttype != PLPGSQL_TTYPE_ROW)
! 			ereport(ERROR,
! 					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! 					 errmsg("PL/pgSQL inline code cannot accept type %s",
! 							format_type_be(codeblock->typoids[i]))));
! 		
! 		/* Build variable and add to datum list */
! 		argvariable = plpgsql_build_variable(buf, 0,
! 									argtype, false);
! 		if (argvariable->dtype == PLPGSQL_DTYPE_ROW)
! 			argitemtype = PLPGSQL_NSTYPE_VAR;
! 		else
! 			argitemtype = PLPGSQL_NSTYPE_ROW;
! 		
! 		in_arg_varnos[i] = argvariable->dno;
! 		
! 		/* Add to namespace */
! 		plpgsql_ns_additem(argitemtype, argvariable->dno, buf);
! 		
! 		if (codeblock->names[i] != NULL)
! 			plpgsql_ns_additem(argitemtype, argvariable->dno,
! 									    codeblock->names[i]);
! 	}
! 	
  	/*
  	 * Now parse the function's text
  	 */
***************
*** 830,839 ****
  	if (function->fn_rettype == VOIDOID)
  		add_dummy_return(function);
  
! 	/*
! 	 * Complete the function's info
! 	 */
! 	function->fn_nargs = 0;
  	function->ndatums = plpgsql_nDatums;
  	function->datums = palloc(sizeof(PLpgSQL_datum *) * plpgsql_nDatums);
  	for (i = 0; i < plpgsql_nDatums; i++)
--- 880,888 ----
  	if (function->fn_rettype == VOIDOID)
  		add_dummy_return(function);
  
! 	for (i = 0; i < function->fn_nargs; i++)
! 		function->fn_argvarnos[i] = in_arg_varnos[i];
! 
  	function->ndatums = plpgsql_nDatums;
  	function->datums = palloc(sizeof(PLpgSQL_datum *) * plpgsql_nDatums);
  	for (i = 0; i < plpgsql_nDatums; i++)
***************
*** 849,854 ****
--- 898,904 ----
  
  	MemoryContextSwitchTo(compile_tmp_cxt);
  	compile_tmp_cxt = NULL;
+ 
  	return function;
  }
  
*** ./src/pl/plpgsql/src/pl_handler.c.orig	2010-02-26 03:01:35.000000000 +0100
--- ./src/pl/plpgsql/src/pl_handler.c	2010-07-04 07:27:03.842263509 +0200
***************
*** 160,165 ****
--- 160,166 ----
  	FmgrInfo	flinfo;
  	Datum		retval;
  	int			rc;
+ 	int	i;
  
  	Assert(IsA(codeblock, InlineCodeBlock));
  
***************
*** 170,183 ****
  		elog(ERROR, "SPI_connect failed: %s", SPI_result_code_string(rc));
  
  	/* Compile the anonymous code block */
! 	func = plpgsql_compile_inline(codeblock->source_text);
  
  	/*
  	 * Set up a fake fcinfo with just enough info to satisfy
  	 * plpgsql_exec_function().  In particular note that this sets things up
  	 * with no arguments passed.
  	 */
! 	MemSet(&fake_fcinfo, 0, sizeof(fake_fcinfo));
  	MemSet(&flinfo, 0, sizeof(flinfo));
  	fake_fcinfo.flinfo = &flinfo;
  	flinfo.fn_oid = InvalidOid;
--- 171,191 ----
  		elog(ERROR, "SPI_connect failed: %s", SPI_result_code_string(rc));
  
  	/* Compile the anonymous code block */
! 	func = plpgsql_compile_inline(codeblock);
  
  	/*
  	 * Set up a fake fcinfo with just enough info to satisfy
  	 * plpgsql_exec_function().  In particular note that this sets things up
  	 * with no arguments passed.
  	 */
! 	InitFunctionCallInfoData(fake_fcinfo, &flinfo, codeblock->nparams, NULL, NULL);
! 	
! 	for (i = 0; i < codeblock->nparams; i++)
! 	{
! 		fake_fcinfo.arg[i] = codeblock->dvalues[i];
! 		fake_fcinfo.argnull[i] = codeblock->nulls[i];
! 	}
! 	 
  	MemSet(&flinfo, 0, sizeof(flinfo));
  	fake_fcinfo.flinfo = &flinfo;
  	flinfo.fn_oid = InvalidOid;
*** ./src/pl/plpgsql/src/plpgsql.h.orig	2010-02-26 03:01:35.000000000 +0100
--- ./src/pl/plpgsql/src/plpgsql.h	2010-07-03 20:49:40.992266524 +0200
***************
*** 832,838 ****
   */
  extern PLpgSQL_function *plpgsql_compile(FunctionCallInfo fcinfo,
  				bool forValidator);
! extern PLpgSQL_function *plpgsql_compile_inline(char *proc_source);
  extern void plpgsql_parser_setup(struct ParseState *pstate,
  					 PLpgSQL_expr *expr);
  extern bool plpgsql_parse_word(char *word1, const char *yytxt,
--- 832,838 ----
   */
  extern PLpgSQL_function *plpgsql_compile(FunctionCallInfo fcinfo,
  				bool forValidator);
! extern PLpgSQL_function *plpgsql_compile_inline(InlineCodeBlock *codeblock);
  extern void plpgsql_parser_setup(struct ParseState *pstate,
  					 PLpgSQL_expr *expr);
  extern bool plpgsql_parse_word(char *word1, const char *yytxt,
*** ./src/test/regress/expected/plpgsql.out.orig	2010-06-25 18:40:13.000000000 +0200
--- ./src/test/regress/expected/plpgsql.out	2010-07-04 08:21:55.000000000 +0200
***************
*** 4121,4123 ****
--- 4121,4141 ----
  (1 row)
  
  drop function unreserved_test();
+ -- Inline code parametrization
+ do (int := 11, int := 22) $$
+ begin
+   raise notice '%', $1 + $2;
+ end $$;
+ NOTICE:  33
+ do (a int := 11, b int := 22) $$
+ begin
+   raise notice '%', a + b;
+ end $$;
+ NOTICE:  33
+ \set myvar1 'Hello'
+ \set myvar2 'World'
+ do (msg1 text := :'myvar1', msg2 text := :'myvar2') $$
+ begin
+   raise notice '% %', msg1, msg2;
+ end $$;
+ NOTICE:  Hello World
*** ./src/test/regress/sql/plpgsql.sql.orig	2010-06-25 18:40:13.000000000 +0200
--- ./src/test/regress/sql/plpgsql.sql	2010-07-04 08:20:42.244266106 +0200
***************
*** 3268,3270 ****
--- 3268,3289 ----
  select unreserved_test();
  
  drop function unreserved_test();
+ 
+ -- Inline code parametrization
+ do (int := 11, int := 22) $$
+ begin
+   raise notice '%', $1 + $2;
+ end $$;
+ 
+ do (a int := 11, b int := 22) $$
+ begin
+   raise notice '%', a + b;
+ end $$;
+ 
+ \set myvar1 'Hello'
+ \set myvar2 'World'
+ do (msg1 text := :'myvar1', msg2 text := :'myvar2') $$
+ begin
+   raise notice '% %', msg1, msg2;
+ end $$;
+ 
-- 
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