Continuing the discussion in [0], here is a patch that allows parameter references in the arguments of the EXECUTE command. The main purpose is submitting protocol-level parameters, but the added regression test case shows another way to exercise it.

What's confusing is that the code already contains a reference that indicates that this should be possible:

    /* Evaluate parameters, if any */
    if (entry->plansource->num_params > 0)
    {
        /*
* Need an EState to evaluate parameters; must not delete it till end * of query, in case parameters are pass-by-reference. Note that the
         * passed-in "params" could possibly be referenced in the parameter
         * expressions.
         */
        estate = CreateExecutorState();
        estate->es_param_list_info = params;
        paramLI = EvaluateParams(pstate, entry, stmt->params, estate);
    }

I'm not sure what this is supposed to do without my patch on top of it. If I remove the estate->es_param_list_info assignment, no tests fail (except the one I added). Either this is a leftover from previous variants of this code (as discussed in [0]), or there is something I haven't understood.


[0]: https://www.postgresql.org/message-id/flat/6e7aa4a1-be6a-1a75-b1f9-83a678e5184a%402ndquadrant.com

--
Peter Eisentraut              http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
From 9c1ba747f5c8b424ca8a1e4c0feb4c859312203e Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <pe...@eisentraut.org>
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 = &paramInfo->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

Reply via email to