From 9c1ba747f5c8b424ca8a1e4c0feb4c859312203e Mon Sep 17 00:00:00 2001 From: Peter Eisentraut Date: Tue, 11 Feb 2020 19:55:25 +0100 Subject: [PATCH v1] Support external parameters in EXECUTE command This allows the arguments of the EXECUTE command to be parameters themselves, for example passed as separate parameters on the protocol level. --- src/backend/commands/prepare.c | 40 +++++++++++++++++++++++++++ src/test/regress/expected/prepare.out | 23 +++++++++++++++ src/test/regress/sql/prepare.sql | 21 ++++++++++++++ 3 files changed, 84 insertions(+) diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c index c4e4b6eaec..543de3493d 100644 --- a/src/backend/commands/prepare.c +++ b/src/backend/commands/prepare.c @@ -33,6 +33,7 @@ #include "tcop/pquery.h" #include "tcop/utility.h" #include "utils/builtins.h" +#include "utils/lsyscache.h" #include "utils/snapmgr.h" #include "utils/timestamp.h" @@ -175,6 +176,42 @@ PrepareQuery(ParseState *pstate, PrepareStmt *stmt, true); } +/* + * Parser callback for resolving parameter references from an existing + * ParamListInfo structure. + */ +static Node * +pli_paramref_hook(ParseState *pstate, ParamRef *pref) +{ + ParamListInfo paramInfo = (ParamListInfo) pstate->p_ref_hook_state; + int paramno = pref->number; + ParamExternData *ped; + ParamExternData pedws; + Param *param; + + /* Check parameter number is valid */ + if (paramno <= 0 || paramno > paramInfo->numParams) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_PARAMETER), + errmsg("there is no parameter $%d", paramno), + parser_errposition(pstate, pref->location))); + + if (paramInfo->paramFetch != NULL) + ped = paramInfo->paramFetch(paramInfo, paramno, false, &pedws); + else + ped = ¶mInfo->params[paramno - 1]; + + param = makeNode(Param); + param->paramkind = PARAM_EXTERN; + param->paramid = paramno; + param->paramtype = ped->ptype; + param->paramtypmod = -1; + param->paramcollid = get_typcollation(param->paramtype); + param->location = pref->location; + + return (Node *) param; +} + /* * ExecuteQuery --- implement the 'EXECUTE' utility statement. * @@ -199,6 +236,9 @@ ExecuteQuery(ParseState *pstate, int eflags; long count; + pstate->p_ref_hook_state = (void *) params; + pstate->p_paramref_hook = pli_paramref_hook; + /* Look it up in the hash table */ entry = FetchPreparedStatement(stmt->name, true); diff --git a/src/test/regress/expected/prepare.out b/src/test/regress/expected/prepare.out index 3306c696b1..a0fec13c6b 100644 --- a/src/test/regress/expected/prepare.out +++ b/src/test/regress/expected/prepare.out @@ -187,3 +187,26 @@ SELECT name, statement, parameter_types FROM pg_prepared_statements ------+-----------+----------------- (0 rows) +-- check parameter handling +CREATE TABLE t1 (a int); +PREPARE p1 AS INSERT INTO t1 (a) VALUES ($1); +CREATE FUNCTION f1(x int) RETURNS int +LANGUAGE SQL +AS $$ +EXECUTE p1($1); +SELECT null::int; +$$; +SELECT f1(2); + f1 +---- + +(1 row) + +SELECT * FROM t1; + a +--- + 2 +(1 row) + +DROP FUNCTION f1(int); +DROP TABLE t1; diff --git a/src/test/regress/sql/prepare.sql b/src/test/regress/sql/prepare.sql index 985d0f05c9..6a16858482 100644 --- a/src/test/regress/sql/prepare.sql +++ b/src/test/regress/sql/prepare.sql @@ -78,3 +78,24 @@ CREATE TEMPORARY TABLE q5_prep_nodata AS EXECUTE q5(200, 'DTAAAA') DEALLOCATE ALL; SELECT name, statement, parameter_types FROM pg_prepared_statements ORDER BY name; + + +-- check parameter handling + +CREATE TABLE t1 (a int); + +PREPARE p1 AS INSERT INTO t1 (a) VALUES ($1); + +CREATE FUNCTION f1(x int) RETURNS int +LANGUAGE SQL +AS $$ +EXECUTE p1($1); +SELECT null::int; +$$; + +SELECT f1(2); + +SELECT * FROM t1; + +DROP FUNCTION f1(int); +DROP TABLE t1; -- 2.25.0