inline_code_with_params.patch

application/octet-stream

Filename: inline_code_with_params.patch
Type: application/octet-stream
Part: 0
Message: patch: inline code with params

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: unified
File+
src/backend/commands/functioncmds.c 129 1
src/backend/nodes/copyfuncs.c 2 0
src/backend/nodes/equalfuncs.c 2 0
src/backend/parser/gram.y 43 5
src/backend/tcop/utility.c 1 1
src/include/commands/defrem.h 1 1
src/include/nodes/parsenodes.h 7 0
src/pl/plpgsql/src/pl_comp.c 54 2
src/pl/plpgsql/src/pl_handler.c 11 2
src/pl/plpgsql/src/plpgsql.h 1 1
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index 9ba6dd8..2968225 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -48,7 +48,9 @@
 #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_collate.h"
@@ -57,6 +59,7 @@
 #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"
@@ -1911,7 +1914,7 @@ AlterFunctionNamespace_oid(Oid procOid, Oid nspOid)
  *		Execute inline procedural-language code
  */
 void
-ExecuteDoStmt(DoStmt *stmt)
+ExecuteDoStmt(DoStmt *stmt, const char *queryString)
 {
 	InlineCodeBlock *codeblock = makeNode(InlineCodeBlock);
 	ListCell   *arg;
@@ -2003,6 +2006,131 @@ ExecuteDoStmt(DoStmt *stmt)
 
 	ReleaseSysCache(languageTuple);
 
+	/* evaluate parameters if exists */
+	if (stmt->params != NULL)
+	{
+		ListCell	*current_param;
+		ListCell	*current_expr;
+		ParseState *pstate;
+		EState	   *estate;
+		int	i;
+		int		nparams;
+
+		current_param = list_head(stmt->params);
+		current_expr = list_head(stmt->expr_list);
+
+		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;
+
+		for (i = 0; i < nparams; i++)
+		{
+			FunctionParameter *fp = (FunctionParameter *) lfirst(current_param);
+			Type		typtup;
+			Oid		typid;
+			Node	*expr;
+			ExprState *exprstate;
+			MemoryContext	   oldcontext;
+			Datum		const_val;
+			bool		const_is_null;
+			int16		resultTypLen;
+			bool		resultTypByVal;
+
+			if (current_expr == NULL)
+				ereport(ERROR,
+					(errcode(ERRCODE_SYNTAX_ERROR),
+					  errmsg("too few parameters specified for inline code")));
+
+			codeblock->names[i] = fp->name;
+
+			typtup = LookupTypeName(NULL, fp->argType, 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(fp->argType))));
+
+				typid = typeTypeId(typtup);
+				ReleaseSysCache(typtup);
+				codeblock->typoids[i] = typid;
+			}
+			else
+			{
+				ereport(ERROR,
+						(errcode(ERRCODE_UNDEFINED_OBJECT),
+						 errmsg("type %s does not exist",
+								TypeNameToString(fp->argType))));
+				typid = InvalidOid;	/* keep compiler quiet */
+			}
+
+			/* evaluate expression */
+			expr = lfirst(current_expr);
+			expr = transformExpr(pstate, expr);
+			expr = coerce_to_specific_type(pstate, expr, typid, "DEFAULT");
+
+			estate = CreateExecutorState();
+			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(typid, &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);
+
+			current_param = lnext(current_param);
+			current_expr = lnext(current_expr);
+		}
+
+		/*
+		 * If more parameters were specified than were required to process
+		 * throw an error.
+		 */
+		if (current_expr != NULL)
+			ereport(ERROR,
+					(errcode(ERRCODE_SYNTAX_ERROR),
+					 errmsg("too many parameters specified for inline code")));
+	}
+	else
+	{
+		if (stmt->expr_list != NIL)
+			ereport(ERROR,
+				(errcode(ERRCODE_SYNTAX_ERROR),
+				 errmsg("using USING clause without parameters")));
+
+		codeblock->nparams = 0;
+		codeblock->dvalues = NULL;
+		codeblock->nulls = NULL;
+		codeblock->names = NULL;
+		codeblock->typoids = NULL;
+	}
+
 	/* execute the inline handler */
 	OidFunctionCall1(laninline, PointerGetDatum(codeblock));
 }
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 1743b8f..a812c6c 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2883,6 +2883,8 @@ _copyDoStmt(const DoStmt *from)
 	DoStmt	   *newnode = makeNode(DoStmt);
 
 	COPY_NODE_FIELD(args);
+	COPY_NODE_FIELD(params);
+	COPY_NODE_FIELD(expr_list);
 
 	return newnode;
 }
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index f19ad77..a3ecc22 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1299,6 +1299,8 @@ static bool
 _equalDoStmt(const DoStmt *a, const DoStmt *b)
 {
 	COMPARE_NODE_FIELD(args);
+	COMPARE_NODE_FIELD(params);
+	COMPARE_NODE_FIELD(expr_list);
 
 	return true;
 }
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7e6ceed..714687f 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -319,7 +319,8 @@ static void processCASbits(int cas_bits, int location, const char *constrType,
 				execute_param_clause using_clause returning_clause
 				opt_enum_val_list enum_val_list table_func_column_list
 				create_generic_options alter_generic_options
-				relation_expr_list dostmt_opt_list
+				relation_expr_list dostmt_opt_list do_args_opt do_args_list
+				do_expr_list_opt
 
 %type <list>	opt_fdw_options fdw_options
 %type <defelt>	fdw_option
@@ -328,7 +329,7 @@ static void processCASbits(int cas_bits, int location, const char *constrType,
 %type <into>	into_clause create_as_target
 
 %type <defelt>	createfunc_opt_item common_func_opt_item dostmt_opt_item
-%type <fun_param> func_arg func_arg_with_default table_func_column
+%type <fun_param> func_arg func_arg_with_default table_func_column do_arg
 %type <fun_param_mode> arg_class
 %type <typnam>	func_return func_type
 
@@ -463,7 +464,6 @@ static void processCASbits(int cas_bits, int location, const char *constrType,
 				opt_frame_clause frame_extent frame_bound
 %type <str>		opt_existing_window_name
 
-
 /*
  * Non-keyword token types.  These are hard-wired into the "flex" lexer.
  * They must be listed first so that their numeric codes do not depend on
@@ -6403,10 +6403,12 @@ any_operator:
  *
  *****************************************************************************/
 
-DoStmt: DO dostmt_opt_list
+DoStmt: DO do_args_opt dostmt_opt_list do_expr_list_opt
 				{
 					DoStmt *n = makeNode(DoStmt);
-					n->args = $2;
+					n->args = $3;
+					n->params = $2;
+					n->expr_list = $4;
 					$$ = (Node *)n;
 				}
 		;
@@ -6427,6 +6429,42 @@ dostmt_opt_item:
 				}
 		;
 
+do_expr_list_opt:
+			USING expr_list			{ $$ = $2; }
+			| /*EMPTY*/			{ $$ = NIL; }
+		;
+
+do_args_opt:
+			'(' do_args_list ')'			{ $$ = $2; }
+			| /*EMPTY*/				{ $$ = NIL; }
+		;
+
+do_args_list:
+			do_arg				{ $$ = list_make1($1); }
+			| do_args_list ',' do_arg		{ $$ = lappend($1, $3); }
+		;
+
+do_arg:
+			param_name Typename
+				{
+					FunctionParameter *n = makeNode(FunctionParameter);
+					n->name = $1;
+					n->argType = $2;
+					n->mode = FUNC_PARAM_IN;
+					n->defexpr = NULL;
+					$$ = n;
+				}
+			| Typename
+				{
+					FunctionParameter *n = makeNode(FunctionParameter);
+					n->name = NULL;
+					n->argType = $1;
+					n->mode = FUNC_PARAM_IN;
+					n->defexpr = NULL;
+					$$ = n;
+				}
+		;
+
 /*****************************************************************************
  *
  *		CREATE CAST / DROP CAST
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 33b292e..4c41d91 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -957,7 +957,7 @@ standard_ProcessUtility(Node *parsetree,
 			break;
 
 		case T_DoStmt:
-			ExecuteDoStmt((DoStmt *) parsetree);
+			ExecuteDoStmt((DoStmt *) parsetree, queryString);
 			break;
 
 		case T_CreatedbStmt:
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index 8f3d2c3..5c07d89 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -73,7 +73,7 @@ extern void DropCastById(Oid castOid);
 extern void AlterFunctionNamespace(List *name, List *argtypes, bool isagg,
 					   const char *newschema);
 extern Oid	AlterFunctionNamespace_oid(Oid procOid, Oid nspOid);
-extern void ExecuteDoStmt(DoStmt *stmt);
+extern void ExecuteDoStmt(DoStmt *stmt, const char *queryString);
 extern Oid	get_cast_oid(Oid sourcetypeid, Oid targettypeid, bool missing_ok);
 
 /* commands/operatorcmds.c */
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 50111cb..03f927c 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2096,6 +2096,8 @@ typedef struct DoStmt
 {
 	NodeTag		type;
 	List	   *args;			/* List of DefElem nodes */
+	List		*params;		/* List of FunctionParameter nodes */
+	List		*expr_list;		/* List of expressions */
 } DoStmt;
 
 typedef struct InlineCodeBlock
@@ -2104,6 +2106,11 @@ typedef struct InlineCodeBlock
 	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;
 
 /* ----------------------
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index 5d2f818..d9e457c 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -755,7 +755,7 @@ do_compile(FunctionCallInfo fcinfo,
  * ----------
  */
 PLpgSQL_function *
-plpgsql_compile_inline(char *proc_source)
+plpgsql_compile_inline(InlineCodeBlock *codeblock)
 {
 	char	   *func_name = "inline_code_block";
 	PLpgSQL_function *function;
@@ -765,6 +765,8 @@ plpgsql_compile_inline(char *proc_source)
 	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
@@ -845,6 +847,54 @@ plpgsql_compile_inline(char *proc_source)
 	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 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, InvalidOid);
+
+		/* 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
 	 */
 	parse_rc = plpgsql_yyparse();
@@ -864,7 +914,9 @@ plpgsql_compile_inline(char *proc_source)
 	/*
 	 * Complete the function's info
 	 */
-	function->fn_nargs = 0;
+	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++)
diff --git a/src/pl/plpgsql/src/pl_handler.c b/src/pl/plpgsql/src/pl_handler.c
index 022ec3f..b3b4860 100644
--- a/src/pl/plpgsql/src/pl_handler.c
+++ b/src/pl/plpgsql/src/pl_handler.c
@@ -160,6 +160,7 @@ plpgsql_inline_handler(PG_FUNCTION_ARGS)
 	FmgrInfo	flinfo;
 	Datum		retval;
 	int			rc;
+	int	i;
 
 	Assert(IsA(codeblock, InlineCodeBlock));
 
@@ -170,7 +171,7 @@ plpgsql_inline_handler(PG_FUNCTION_ARGS)
 		elog(ERROR, "SPI_connect failed: %s", SPI_result_code_string(rc));
 
 	/* Compile the anonymous code block */
-	func = plpgsql_compile_inline(codeblock->source_text);
+	func = plpgsql_compile_inline(codeblock);
 
 	/* Mark the function as busy, just pro forma */
 	func->use_count++;
@@ -180,7 +181,15 @@ plpgsql_inline_handler(PG_FUNCTION_ARGS)
 	 * plpgsql_exec_function().  In particular note that this sets things up
 	 * with no arguments passed.
 	 */
-	MemSet(&fake_fcinfo, 0, sizeof(fake_fcinfo));
+	InitFunctionCallInfoData(fake_fcinfo, &flinfo, codeblock->nparams,
+							InvalidOid, 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;
diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h
index b63f336..a6db279 100644
--- a/src/pl/plpgsql/src/plpgsql.h
+++ b/src/pl/plpgsql/src/plpgsql.h
@@ -876,7 +876,7 @@ extern PLpgSQL_plugin **plugin_ptr;
  */
 extern PLpgSQL_function *plpgsql_compile(FunctionCallInfo fcinfo,
 				bool forValidator);
-extern PLpgSQL_function *plpgsql_compile_inline(char *proc_source);
+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,