From f3e78c7d9ef2fe21102cc8a08d244eda69aaa141 Mon Sep 17 00:00:00 2001 From: Peter Eisentraut Date: Tue, 20 Feb 2018 18:03:31 -0500 Subject: [PATCH] Support parameters in CALL To support parameters in CALL, move the parse analysis of the procedure and arguments into the global transformation phase, so that the parser hooks can be applied. And then at execution time pass the parameters from ProcessUtility on to ExecuteCallStmt. --- src/backend/commands/functioncmds.c | 25 +++----------- src/backend/nodes/copyfuncs.c | 1 + src/backend/nodes/equalfuncs.c | 1 + src/backend/parser/analyze.c | 45 ++++++++++++++++++++++++++ src/backend/tcop/utility.c | 2 +- src/include/commands/defrem.h | 3 +- src/include/nodes/parsenodes.h | 3 +- src/pl/plpgsql/src/expected/plpgsql_call.out | 19 +++++++++++ src/pl/plpgsql/src/sql/plpgsql_call.sql | 18 +++++++++++ src/test/regress/expected/create_procedure.out | 16 +++++++++ src/test/regress/sql/create_procedure.sql | 15 +++++++++ 11 files changed, 124 insertions(+), 24 deletions(-) diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c index a027b19744..abdfa249c0 100644 --- a/src/backend/commands/functioncmds.c +++ b/src/backend/commands/functioncmds.c @@ -2212,11 +2212,9 @@ ExecuteDoStmt(DoStmt *stmt, bool atomic) * commits that might occur inside the procedure. */ void -ExecuteCallStmt(ParseState *pstate, CallStmt *stmt, bool atomic) +ExecuteCallStmt(CallStmt *stmt, ParamListInfo params, bool atomic) { - List *targs; ListCell *lc; - Node *node; FuncExpr *fexpr; int nargs; int i; @@ -2228,24 +2226,8 @@ ExecuteCallStmt(ParseState *pstate, CallStmt *stmt, bool atomic) ExprContext *econtext; HeapTuple tp; - /* We need to do parse analysis on the procedure call and its arguments */ - targs = NIL; - foreach(lc, stmt->funccall->args) - { - targs = lappend(targs, transformExpr(pstate, - (Node *) lfirst(lc), - EXPR_KIND_CALL_ARGUMENT)); - } - - node = ParseFuncOrColumn(pstate, - stmt->funccall->funcname, - targs, - pstate->p_last_srf, - stmt->funccall, - true, - stmt->funccall->location); - - fexpr = castNode(FuncExpr, node); + fexpr = stmt->funcexpr; + Assert(fexpr); aclresult = pg_proc_aclcheck(fexpr->funcid, GetUserId(), ACL_EXECUTE); if (aclresult != ACLCHECK_OK) @@ -2289,6 +2271,7 @@ ExecuteCallStmt(ParseState *pstate, CallStmt *stmt, bool atomic) * we can't free this context till the procedure returns. */ estate = CreateExecutorState(); + estate->es_param_list_info = params; econtext = CreateExprContext(estate); i = 0; diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 82255b0d1d..266a3ef8ef 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -3231,6 +3231,7 @@ _copyCallStmt(const CallStmt *from) CallStmt *newnode = makeNode(CallStmt); COPY_NODE_FIELD(funccall); + COPY_NODE_FIELD(funcexpr); return newnode; } diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index b9bc8e38d7..bbffc87842 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -1206,6 +1206,7 @@ static bool _equalCallStmt(const CallStmt *a, const CallStmt *b) { COMPARE_NODE_FIELD(funccall); + COMPARE_NODE_FIELD(funcexpr); return true; } diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 5b3a610cf9..c3a9617f67 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -36,6 +36,8 @@ #include "parser/parse_coerce.h" #include "parser/parse_collate.h" #include "parser/parse_cte.h" +#include "parser/parse_expr.h" +#include "parser/parse_func.h" #include "parser/parse_oper.h" #include "parser/parse_param.h" #include "parser/parse_relation.h" @@ -74,6 +76,8 @@ static Query *transformExplainStmt(ParseState *pstate, ExplainStmt *stmt); static Query *transformCreateTableAsStmt(ParseState *pstate, CreateTableAsStmt *stmt); +static Query *transformCallStmt(ParseState *pstate, + CallStmt *stmt); static void transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, bool pushedDown); #ifdef RAW_EXPRESSION_COVERAGE_TEST @@ -318,6 +322,10 @@ transformStmt(ParseState *pstate, Node *parseTree) (CreateTableAsStmt *) parseTree); break; + case T_CallStmt: + result = transformCallStmt(pstate, + (CallStmt *) parseTree); + default: /* @@ -2571,6 +2579,43 @@ transformCreateTableAsStmt(ParseState *pstate, CreateTableAsStmt *stmt) return result; } +/* + * transform a CallStmt + * + * We need to do parse analysis on the procedure call and its arguments. + */ +static Query * +transformCallStmt(ParseState *pstate, CallStmt *stmt) +{ + List *targs; + ListCell *lc; + Node *node; + Query *result; + + targs = NIL; + foreach(lc, stmt->funccall->args) + { + targs = lappend(targs, transformExpr(pstate, + (Node *) lfirst(lc), + EXPR_KIND_CALL_ARGUMENT)); + } + + node = ParseFuncOrColumn(pstate, + stmt->funccall->funcname, + targs, + pstate->p_last_srf, + stmt->funccall, + true, + stmt->funccall->location); + + stmt->funcexpr = castNode(FuncExpr, node); + + result = makeNode(Query); + result->commandType = CMD_UTILITY; + result->utilityStmt = (Node *) stmt; + + return result; +} /* * Produce a string representation of a LockClauseStrength value. diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index 8c23ee53e2..f78efdf359 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -660,7 +660,7 @@ standard_ProcessUtility(PlannedStmt *pstmt, break; case T_CallStmt: - ExecuteCallStmt(pstate, castNode(CallStmt, parsetree), + ExecuteCallStmt(castNode(CallStmt, parsetree), params, (context != PROCESS_UTILITY_TOPLEVEL || IsTransactionBlock())); break; diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h index f510f40945..c829abfea7 100644 --- a/src/include/commands/defrem.h +++ b/src/include/commands/defrem.h @@ -15,6 +15,7 @@ #define DEFREM_H #include "catalog/objectaddress.h" +#include "nodes/params.h" #include "nodes/parsenodes.h" #include "utils/array.h" @@ -61,7 +62,7 @@ extern void DropTransformById(Oid transformOid); extern void IsThereFunctionInNamespace(const char *proname, int pronargs, oidvector *proargtypes, Oid nspOid); extern void ExecuteDoStmt(DoStmt *stmt, bool atomic); -extern void ExecuteCallStmt(ParseState *pstate, CallStmt *stmt, bool atomic); +extern void ExecuteCallStmt(CallStmt *stmt, ParamListInfo params, bool atomic); extern Oid get_cast_oid(Oid sourcetypeid, Oid targettypeid, bool missing_ok); extern Oid get_transform_oid(Oid type_id, Oid lang_id, bool missing_ok); extern void interpret_function_parameter_list(ParseState *pstate, diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index c7a43b8933..ac292bc6e7 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -2814,7 +2814,8 @@ typedef struct InlineCodeBlock typedef struct CallStmt { NodeTag type; - FuncCall *funccall; + FuncCall *funccall; /* from the parser */ + FuncExpr *funcexpr; /* transformed */ } CallStmt; typedef struct CallContext diff --git a/src/pl/plpgsql/src/expected/plpgsql_call.out b/src/pl/plpgsql/src/expected/plpgsql_call.out index d0f35163bc..e2442c603c 100644 --- a/src/pl/plpgsql/src/expected/plpgsql_call.out +++ b/src/pl/plpgsql/src/expected/plpgsql_call.out @@ -35,7 +35,26 @@ SELECT * FROM test1; 55 (1 row) +-- nested CALL +TRUNCATE TABLE test1; +CREATE PROCEDURE test_proc4(y int) +LANGUAGE plpgsql +AS $$ +BEGIN + CALL test_proc3(y); + CALL test_proc3($1); +END; +$$; +CALL test_proc4(66); +SELECT * FROM test1; + a +---- + 66 + 66 +(2 rows) + DROP PROCEDURE test_proc1; DROP PROCEDURE test_proc2; DROP PROCEDURE test_proc3; +DROP PROCEDURE test_proc4; DROP TABLE test1; diff --git a/src/pl/plpgsql/src/sql/plpgsql_call.sql b/src/pl/plpgsql/src/sql/plpgsql_call.sql index 38fd220e8f..321ed43af8 100644 --- a/src/pl/plpgsql/src/sql/plpgsql_call.sql +++ b/src/pl/plpgsql/src/sql/plpgsql_call.sql @@ -40,8 +40,26 @@ CREATE PROCEDURE test_proc3(x int) SELECT * FROM test1; +-- nested CALL +TRUNCATE TABLE test1; + +CREATE PROCEDURE test_proc4(y int) +LANGUAGE plpgsql +AS $$ +BEGIN + CALL test_proc3(y); + CALL test_proc3($1); +END; +$$; + +CALL test_proc4(66); + +SELECT * FROM test1; + + DROP PROCEDURE test_proc1; DROP PROCEDURE test_proc2; DROP PROCEDURE test_proc3; +DROP PROCEDURE test_proc4; DROP TABLE test1; diff --git a/src/test/regress/expected/create_procedure.out b/src/test/regress/expected/create_procedure.out index e7bede24fa..182b325ea1 100644 --- a/src/test/regress/expected/create_procedure.out +++ b/src/test/regress/expected/create_procedure.out @@ -55,6 +55,22 @@ AS $$ SELECT 5; $$; CALL ptest2(); +-- nested CALL +TRUNCATE cp_test; +CREATE PROCEDURE ptest3(y text) +LANGUAGE SQL +AS $$ +CALL ptest1(y); +CALL ptest1($1); +$$; +CALL ptest3('b'); +SELECT * FROM cp_test; + a | b +---+--- + 1 | b + 1 | b +(2 rows) + -- various error cases CALL version(); -- error: not a procedure ERROR: version() is not a procedure diff --git a/src/test/regress/sql/create_procedure.sql b/src/test/regress/sql/create_procedure.sql index 774c12ee34..52318bf2a6 100644 --- a/src/test/regress/sql/create_procedure.sql +++ b/src/test/regress/sql/create_procedure.sql @@ -31,6 +31,21 @@ CREATE PROCEDURE ptest2() CALL ptest2(); +-- nested CALL +TRUNCATE cp_test; + +CREATE PROCEDURE ptest3(y text) +LANGUAGE SQL +AS $$ +CALL ptest1(y); +CALL ptest1($1); +$$; + +CALL ptest3('b'); + +SELECT * FROM cp_test; + + -- various error cases CALL version(); -- error: not a procedure base-commit: 9a44a26b65d3d36867267624b76d3dea3dc4f6f6 -- 2.16.2