inline_code_with_params2.patch

application/octet-stream

Filename: inline_code_with_params2.patch
Type: application/octet-stream
Part: 0
Message: Re: 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 125 1
src/backend/executor/spi.c 120 0
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/executor/spi.h 2 0
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..1007611 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -48,7 +48,11 @@
 #include "catalog/pg_type_fn.h"
 #include "commands/defrem.h"
 #include "commands/proclang.h"
+#include "executor/executor.h"
+#include "executor/execdesc.h"
+#include "executor/spi.h"
 #include "miscadmin.h"
+#include "optimizer/planmain.h"
 #include "optimizer/var.h"
 #include "parser/parse_coerce.h"
 #include "parser/parse_collate.h"
@@ -57,6 +61,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 +1916,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 +2008,125 @@ ExecuteDoStmt(DoStmt *stmt)
 
 	ReleaseSysCache(languageTuple);
 
+	/* evaluate parameters if exists */
+	if (stmt->params != NULL)
+	{
+		ListCell	*current_param;
+		ListCell	*current_expr;
+		int	i;
+		int		nparams;
+		List	*exprsList;
+		SelectStmt *s;
+		MemoryContext oldctx = CurrentMemoryContext;
+
+		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));
+
+		exprsList = NIL;
+
+		/* prepare expression list to evaluation and take names */
+		for (i = 0; i < nparams; i++)
+		{
+			FunctionParameter *fp = (FunctionParameter *) lfirst(current_param);
+			TypeCast *tc;
+
+			if (current_expr == NULL)
+				ereport(ERROR,
+					(errcode(ERRCODE_SYNTAX_ERROR),
+					  errmsg("too few parameters specified for inline code")));
+
+			codeblock->names[i] = fp->name;
+
+			/* forward expected type to expression */
+			tc = makeNode(TypeCast);
+			tc->arg = lfirst(current_expr);
+			tc->typeName = fp->argType;
+			tc->location = -1;
+			exprsList = lappend(exprsList, tc);
+
+			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")));
+
+		if (SPI_connect() != SPI_OK_CONNECT)
+			elog(ERROR, "SPI_connect failed");
+
+		/* Evaluate parameters */
+		s = makeNode(SelectStmt);
+		s->valuesLists = list_make1(exprsList);
+
+		SPI_execute_pt((Node *) s, queryString, true, 0);
+
+		if (SPI_processed != 1)
+			elog(ERROR, "expected one row");
+
+		/* get back context before copy result variables */
+		MemoryContextSwitchTo(oldctx);
+
+		for (i = 0; i < nparams; i++)
+		{
+			Datum	datum;
+			bool	isnull;
+			Oid		typid;
+			int16		typlen;
+			bool		typbyval;
+
+			typid = SPI_gettypeid(SPI_tuptable->tupdesc, i + 1);
+
+			/* Get info needed about result datatype */
+			get_typlenbyval(typid, &typlen, &typbyval);
+
+			datum = SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, i + 1, &isnull);
+
+			if (!isnull)
+			{
+				codeblock->nulls[i] = false;
+				codeblock->dvalues[i] = datumCopy(datum, typbyval, typlen);
+			}
+			else
+			{
+				codeblock->nulls[i] = true;
+				codeblock->dvalues[i] = (Datum) 0;
+			}
+
+			codeblock->typoids[i] = typid;
+		}
+
+		SPI_freetuptable(SPI_tuptable);
+
+		SPI_finish();
+	}
+	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/executor/spi.c b/src/backend/executor/spi.c
index e222365..e8b68d6 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -50,6 +50,8 @@ static Portal SPI_cursor_open_internal(const char *name, SPIPlanPtr plan,
 
 static void _SPI_prepare_plan(const char *src, SPIPlanPtr plan,
 				  ParamListInfo boundParams);
+static void
+_SPI_prepare_plan_pt(Node *raw_parsetree_list, const char *queryString, SPIPlanPtr plan);
 
 static int _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
 				  Snapshot snapshot, Snapshot crosscheck_snapshot,
@@ -371,6 +373,36 @@ SPI_exec(const char *src, long tcount)
 	return SPI_execute(src, false, tcount);
 }
 
+/* analyze, plan and execute a query with parse tree */
+int
+SPI_execute_pt(Node *parsetree, const char *queryString, bool read_only, long tcount)
+{
+	_SPI_plan	plan;
+	int			res;
+
+	if (parsetree == NULL || tcount < 0)
+		return SPI_ERROR_ARGUMENT;
+
+	res = _SPI_begin_call(true);
+	if (res < 0)
+		return res;
+
+	memset(&plan, 0, sizeof(_SPI_plan));
+	plan.magic = _SPI_PLAN_MAGIC;
+	plan.cursor_options = 0;
+
+
+	_SPI_prepare_plan_pt(parsetree, queryString, &plan);
+
+
+	res = _SPI_execute_plan(&plan, NULL,
+							InvalidSnapshot, InvalidSnapshot,
+							read_only, true, tcount);
+
+	_SPI_end_call(true);
+	return res;
+}
+
 /* Execute a previously prepared plan */
 int
 SPI_execute_plan(SPIPlanPtr plan, Datum *Values, const char *Nulls,
@@ -1736,6 +1768,94 @@ _SPI_prepare_plan(const char *src, SPIPlanPtr plan, ParamListInfo boundParams)
 }
 
 /*
+ * Analyze a querystring.
+ *
+ * At entry, plan->argtypes and plan->nargs (or alternatively plan->parserSetup
+ * and plan->parserSetupArg) must be valid, as must plan->cursor_options.
+ * If boundParams isn't NULL then it represents parameter values that are made
+ * available to the planner (as either estimates or hard values depending on
+ * their PARAM_FLAG_CONST marking).  The boundParams had better match the
+ * param type information embedded in the plan!
+ *
+ * Results are stored into *plan (specifically, plan->plancache_list).
+ * Note that the result data is all in CurrentMemoryContext or child contexts
+ * thereof; in practice this means it is in the SPI executor context, and
+ * what we are creating is a "temporary" SPIPlan.  Cruft generated during
+ * parsing is also left in CurrentMemoryContext.
+ */
+static void
+_SPI_prepare_plan_pt(Node *parsetree, const char *queryString, SPIPlanPtr plan)
+{
+	List	   *plancache_list;
+	ErrorContextCallback spierrcontext;
+	int			cursor_options = plan->cursor_options;
+	List	   *stmt_list;
+	CachedPlanSource *plansource;
+
+	/*
+	 * Setup error traceback support for ereport()
+	 */
+	spierrcontext.callback = _SPI_error_callback;
+	spierrcontext.arg = (void *) queryString;
+	spierrcontext.previous = error_context_stack;
+	error_context_stack = &spierrcontext;
+
+	/*
+	 * Do parse analysis and rule rewrite for each raw parsetree, storing the
+	 * results into unsaved plancache entries.
+	 */
+	plancache_list = NIL;
+
+	/*
+	 * Create the CachedPlanSource before we do parse analysis, since it
+	 * needs to see the unmodified raw parse tree.
+	 */
+	plansource = CreateCachedPlan(parsetree,
+								  queryString,
+								  CreateCommandTag(parsetree));
+
+	/*
+	 * Parameter datatypes are driven by parserSetup hook if provided,
+	 * otherwise we use the fixed parameter list.
+	 */
+	if (plan->parserSetup != NULL)
+	{
+		Assert(plan->nargs == 0);
+		stmt_list = pg_analyze_and_rewrite_params(parsetree,
+												  queryString,
+												  plan->parserSetup,
+												  plan->parserSetupArg);
+	}
+	else
+	{
+		stmt_list = pg_analyze_and_rewrite(parsetree,
+										   queryString,
+										   plan->argtypes,
+										   plan->nargs);
+	}
+
+	/* Finish filling in the CachedPlanSource */
+	CompleteCachedPlan(plansource,
+					   stmt_list,
+					   NULL,
+					   plan->argtypes,
+					   plan->nargs,
+					   plan->parserSetup,
+					   plan->parserSetupArg,
+					   cursor_options,
+					   false);		/* not fixed result */
+
+	plancache_list = lappend(plancache_list, plansource);
+
+	plan->plancache_list = plancache_list;
+
+	/*
+	 * Pop the error context stack
+	 */
+	error_context_stack = spierrcontext.previous;
+}
+
+/*
  * Execute the given plan with the given parameter values
  *
  * snapshot: query snapshot to use, or InvalidSnapshot for the normal
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/executor/spi.h b/src/include/executor/spi.h
index cfbaa14..2c1b11a 100644
--- a/src/include/executor/spi.h
+++ b/src/include/executor/spi.h
@@ -140,4 +140,6 @@ extern void SPI_cursor_close(Portal portal);
 extern void AtEOXact_SPI(bool isCommit);
 extern void AtEOSubXact_SPI(bool isCommit, SubTransactionId mySubid);
 
+extern int SPI_execute_pt(Node *parsetree, const char *queryString, bool read_only, long tcount);
+
 #endif   /* SPI_H */
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,