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.
This commit is contained in:
Peter Eisentraut 2018-02-20 18:03:31 -05:00
parent a6a80134e3
commit 76b6aa41f4
11 changed files with 124 additions and 24 deletions

View 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;

View File

@ -3231,6 +3231,7 @@ _copyCallStmt(const CallStmt *from)
CallStmt *newnode = makeNode(CallStmt);
COPY_NODE_FIELD(funccall);
COPY_NODE_FIELD(funcexpr);
return newnode;
}

View File

@ -1206,6 +1206,7 @@ static bool
_equalCallStmt(const CallStmt *a, const CallStmt *b)
{
COMPARE_NODE_FIELD(funccall);
COMPARE_NODE_FIELD(funcexpr);
return true;
}

View 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.

View 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;

View 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,

View 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

View 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;

View 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;

View 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

View 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