The current CALL implementation doesn't support parameters, which was a
bad oversight.  This patch fixes that.

-- 
Peter Eisentraut              http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
From f3e78c7d9ef2fe21102cc8a08d244eda69aaa141 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <pete...@gmx.net>
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

Reply via email to