]> granicus.if.org Git - postgresql/commitdiff
Support parameters in CALL
authorPeter Eisentraut <peter_e@gmx.net>
Tue, 20 Feb 2018 23:03:31 +0000 (18:03 -0500)
committerPeter Eisentraut <peter_e@gmx.net>
Fri, 23 Feb 2018 02:36:48 +0000 (21:36 -0500)
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
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/parser/analyze.c
src/backend/tcop/utility.c
src/include/commands/defrem.h
src/include/nodes/parsenodes.h
src/pl/plpgsql/src/expected/plpgsql_call.out
src/pl/plpgsql/src/sql/plpgsql_call.sql
src/test/regress/expected/create_procedure.out
src/test/regress/sql/create_procedure.sql

index a027b19744aa1af004f752cfcfe7a57ae0de2aeb..abdfa249c01ae3b4197b47de14ba5fdc3ed91e2e 100644 (file)
@@ -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;
index 82255b0d1dae1420fdc8600ca67f3301d87c0167..266a3ef8ef6d6af84197bd7d8c94737a8f160784 100644 (file)
@@ -3231,6 +3231,7 @@ _copyCallStmt(const CallStmt *from)
        CallStmt   *newnode = makeNode(CallStmt);
 
        COPY_NODE_FIELD(funccall);
+       COPY_NODE_FIELD(funcexpr);
 
        return newnode;
 }
index b9bc8e38d74d64097200a59d6b8e4daa155d2a5d..bbffc878423630b9d8bf52f2bac6d29cd919bd3e 100644 (file)
@@ -1206,6 +1206,7 @@ static bool
 _equalCallStmt(const CallStmt *a, const CallStmt *b)
 {
        COMPARE_NODE_FIELD(funccall);
+       COMPARE_NODE_FIELD(funcexpr);
 
        return true;
 }
index 5b3a610cf9f4b2f654f125cf987efba070ced593..c3a9617f67fd3ca2e5e3875d420365f42206433c 100644 (file)
@@ -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.
index 8c23ee53e2baaaaa38d07538baa23196491bc512..f78efdf359a40e212bacbd1ee406424b8909f6d6 100644 (file)
@@ -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;
 
index f510f40945b77feb927db71cef72f31c1f9ef13a..c829abfea7e60cef754620d761157f94b7b49491 100644 (file)
@@ -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,
index c7a43b8933f5642022c76699b896aac6b97f809c..ac292bc6e7a8b13c127518223db6e538975a14fa 100644 (file)
@@ -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
index d0f35163bce3613e524bf08b268a01a57cb4b38f..e2442c603cd1f925919dcb2b0909d1209c4741f2 100644 (file)
@@ -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;
index 38fd220e8faee302b1c2f392f52a41da09ee3ced..321ed43af8eab7d82a2b985d591480ef853f2043 100644 (file)
@@ -40,8 +40,26 @@ CALL test_proc3(55);
 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;
index e7bede24faafa389c5a780ed415f22ac69de9cd7..182b325ea1cf2b42565f69b21e88e242d4ca0ae5 100644 (file)
@@ -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
index 774c12ee34b1af0d1b14f78829d3562fa99590ba..52318bf2a697d74c321b48a320bf590ccd9f3efa 100644 (file)
@@ -31,6 +31,21 @@ $$;
 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