I have found some of the parse/analyze API calls confusing one too many times, so here I'm proposing some renaming and refactoring.

Notionally, there are three parallel ways to call the parse/analyze phase: with fixed parameters (for example, used by SPI), with variable parameters (for example, used by PREPARE), and with a parser callback (for example, used to parse the body of SQL functions). Some of the involved functions were confusingly named and made this API structure more confusing.

For example, at the top level there are pg_analyze_and_rewrite() and pg_analyze_and_rewrite_params(). You'd think the first one doesn't take parameters and the second one takes parameters. But the truth is, the first one takes fixed parameters and the second one takes a parser callback. The parser callback can be used to parse parameters, but also other things. There isn't any variant that takes variable parameters; that code is sprinkled around other places altogether.

One level below that, there is parse_analyze() (for fixed parameters) and parse_analyze_varparams() (good name). But there is no analogous function for the callback variant; that code is spread out in pg_analyze_and_rewrite_params().

And then there are parse_fixed_parameters() and parse_variable_parameters(). But they don't do any parsing at all. They just set up callbacks for the parsing to follow.

This doesn't need to be so confusing. With the attached patch set, the calls end up:

pg_analyze_and_rewrite_fixedparams()
  -> parse_analyze_fixedparams()
       -> setup_parse_fixed_parameters()

pg_analyze_and_rewrite_varparams() [new]
  -> parse_analyze_varparams()
       -> setup_parse_variable_parameters()

pg_analyze_and_rewrite_withcb()
  -> parse_analyze_withcb() [new]
       -> (nothing needed here)

(The "withcb" naming maybe isn't great; better ideas welcome.)

Not included in this patch set, but food for further thought: The pg_analyze_and_rewrite_*() functions aren't all that useful (anymore). One might as well write

    pg_rewrite_query(parse_analyze_xxx(...))

The only things that pg_analyze_and_rewrite_*() do in addition to that is handle log_parser_stats, which could be moved into parse_analyze_*(), and TRACE_POSTGRESQL_QUERY_REWRITE_*(), which IMO doesn't make sense to begin with and should be in pg_rewrite_query().
From 9a72ceadebd6b9309fe0157c1cd181b29ee8bd52 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <pe...@eisentraut.org>
Date: Tue, 28 Dec 2021 09:01:45 +0100
Subject: [PATCH v1 1/3] Parse/analyze function renaming

There are three parallel ways to call parse/analyze: with fixed
parameters, with variable parameters, and by supplying your own parser
callback.  Some of the involved functions were confusingly named and
made this API structure more confusing.  This patch renames some functions to
make this clearer:

parse_analyze() -> parse_analyze_fixedparams()
pg_analyze_and_rewrite() -> pg_analyze_and_rewrite_fixedparams()

(Otherwise one might think this variant doesn't accept parameters, but
in fact all three ways accept parameters.)

pg_analyze_and_rewrite_params() -> pg_analyze_and_rewrite_withcb()

(Before, and also when considering pg_analyze_and_rewrite(), one might
think this is the only way to pass parameters.  Moreover, the parser
callback doesn't necessarily need to parse only parameters, it's just
one of the things it could do.)

parse_fixed_parameters() -> setup_parse_fixed_parameters()
parse_variable_parameters() -> setup_parse_variable_parameters()

(These functions don't actually do any parsing, they just set up
callbacks to use during parsing later.)

This patch also adds some const decorations to the fixed-parameters
API, so the distinction from the variable-parameters API is more
clear.
---
 src/backend/catalog/pg_proc.c        |  2 +-
 src/backend/commands/copyto.c        |  2 +-
 src/backend/commands/extension.c     |  2 +-
 src/backend/commands/schemacmds.c    |  2 +-
 src/backend/commands/tablecmds.c     |  2 +-
 src/backend/commands/view.c          |  2 +-
 src/backend/executor/functions.c     |  2 +-
 src/backend/executor/spi.c           |  8 ++++----
 src/backend/optimizer/util/clauses.c |  2 +-
 src/backend/parser/analyze.c         | 10 +++++-----
 src/backend/parser/parse_param.c     |  8 ++++----
 src/backend/parser/parse_utilcmd.c   |  2 +-
 src/backend/tcop/postgres.c          | 17 +++++++++--------
 src/backend/utils/cache/plancache.c  |  4 ++--
 src/include/parser/analyze.h         |  4 ++--
 src/include/parser/parse_param.h     |  6 +++---
 src/include/tcop/tcopprot.h          |  6 +++---
 17 files changed, 41 insertions(+), 40 deletions(-)

diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index 25d35230d0..8dffcb3a19 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -947,7 +947,7 @@ fmgr_sql_validator(PG_FUNCTION_ARGS)
                                        RawStmt    *parsetree = 
lfirst_node(RawStmt, lc);
                                        List       *querytree_sublist;
 
-                                       querytree_sublist = 
pg_analyze_and_rewrite_params(parsetree,
+                                       querytree_sublist = 
pg_analyze_and_rewrite_withcb(parsetree,
                                                                                
                                                          prosrc,
                                                                                
                                                          (ParserSetupHook) 
sql_fn_parser_setup,
                                                                                
                                                          pinfo,
diff --git a/src/backend/commands/copyto.c b/src/backend/commands/copyto.c
index b6eacd5baa..378f2b232d 100644
--- a/src/backend/commands/copyto.c
+++ b/src/backend/commands/copyto.c
@@ -439,7 +439,7 @@ BeginCopyTo(ParseState *pstate,
                 * Run parse analysis and rewrite.  Note this also acquires 
sufficient
                 * locks on the source table(s).
                 */
-               rewritten = pg_analyze_and_rewrite(raw_query,
+               rewritten = pg_analyze_and_rewrite_fixedparams(raw_query,
                                                                                
   pstate->p_sourcetext, NULL, 0,
                                                                                
   NULL);
 
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index eaa76af47b..46b8ca9a00 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -746,7 +746,7 @@ execute_sql_string(const char *sql)
                /* Be sure parser can see any DDL done so far */
                CommandCounterIncrement();
 
-               stmt_list = pg_analyze_and_rewrite(parsetree,
+               stmt_list = pg_analyze_and_rewrite_fixedparams(parsetree,
                                                                                
   sql,
                                                                                
   NULL,
                                                                                
   0,
diff --git a/src/backend/commands/schemacmds.c 
b/src/backend/commands/schemacmds.c
index 6c6ab9ee34..0c376e070d 100644
--- a/src/backend/commands/schemacmds.c
+++ b/src/backend/commands/schemacmds.c
@@ -172,7 +172,7 @@ CreateSchemaCommand(CreateSchemaStmt *stmt, const char 
*queryString,
        /*
         * Execute each command contained in the CREATE SCHEMA.  Since the 
grammar
         * allows only utility commands in CREATE SCHEMA, there is no need to 
pass
-        * them through parse_analyze() or the rewriter; we can just hand them
+        * them through parse_analyze_*() or the rewriter; we can just hand them
         * straight to ProcessUtility.
         */
        foreach(parsetree_item, parsetree_list)
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 45e59e3d5c..41b0eac97a 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -12920,7 +12920,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid 
refRelId, char *cmd,
        /*
         * We expect that we will get only ALTER TABLE and CREATE INDEX
         * statements. Hence, there is no need to pass them through
-        * parse_analyze() or the rewriter, but instead we need to pass them
+        * parse_analyze_*() or the rewriter, but instead we need to pass them
         * through parse_utilcmd.c to make them ready for execution.
         */
        raw_parsetree_list = raw_parser(cmd, RAW_PARSE_DEFAULT);
diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c
index 4df05a0b33..8b6254cfce 100644
--- a/src/backend/commands/view.c
+++ b/src/backend/commands/view.c
@@ -423,7 +423,7 @@ DefineView(ViewStmt *stmt, const char *queryString,
        rawstmt->stmt_location = stmt_location;
        rawstmt->stmt_len = stmt_len;
 
-       viewParse = parse_analyze(rawstmt, queryString, NULL, 0, NULL);
+       viewParse = parse_analyze_fixedparams(rawstmt, queryString, NULL, 0, 
NULL);
 
        /*
         * The grammar should ensure that the result is a single SELECT Query.
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index 296e54e60a..fd878555ee 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -718,7 +718,7 @@ init_sql_fcache(FunctionCallInfo fcinfo, Oid collation, 
bool lazyEvalOK)
                        RawStmt    *parsetree = lfirst_node(RawStmt, lc);
                        List       *queryTree_sublist;
 
-                       queryTree_sublist = 
pg_analyze_and_rewrite_params(parsetree,
+                       queryTree_sublist = 
pg_analyze_and_rewrite_withcb(parsetree,
                                                                                
                                          fcache->src,
                                                                                
                                          (ParserSetupHook) sql_fn_parser_setup,
                                                                                
                                          fcache->pinfo,
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index 0568ae123f..95989f237d 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -2165,7 +2165,7 @@ _SPI_prepare_plan(const char *src, SPIPlanPtr plan)
                if (plan->parserSetup != NULL)
                {
                        Assert(plan->nargs == 0);
-                       stmt_list = pg_analyze_and_rewrite_params(parsetree,
+                       stmt_list = pg_analyze_and_rewrite_withcb(parsetree,
                                                                                
                          src,
                                                                                
                          plan->parserSetup,
                                                                                
                          plan->parserSetupArg,
@@ -2173,7 +2173,7 @@ _SPI_prepare_plan(const char *src, SPIPlanPtr plan)
                }
                else
                {
-                       stmt_list = pg_analyze_and_rewrite(parsetree,
+                       stmt_list = 
pg_analyze_and_rewrite_fixedparams(parsetree,
                                                                                
           src,
                                                                                
           plan->argtypes,
                                                                                
           plan->nargs,
@@ -2402,7 +2402,7 @@ _SPI_execute_plan(SPIPlanPtr plan, const 
SPIExecuteOptions *options,
                        else if (plan->parserSetup != NULL)
                        {
                                Assert(plan->nargs == 0);
-                               stmt_list = 
pg_analyze_and_rewrite_params(parsetree,
+                               stmt_list = 
pg_analyze_and_rewrite_withcb(parsetree,
                                                                                
                                  src,
                                                                                
                                  plan->parserSetup,
                                                                                
                                  plan->parserSetupArg,
@@ -2410,7 +2410,7 @@ _SPI_execute_plan(SPIPlanPtr plan, const 
SPIExecuteOptions *options,
                        }
                        else
                        {
-                               stmt_list = pg_analyze_and_rewrite(parsetree,
+                               stmt_list = 
pg_analyze_and_rewrite_fixedparams(parsetree,
                                                                                
                   src,
                                                                                
                   plan->argtypes,
                                                                                
                   plan->nargs,
diff --git a/src/backend/optimizer/util/clauses.c 
b/src/backend/optimizer/util/clauses.c
index 873e43bfe6..94735cf0e8 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -5057,7 +5057,7 @@ inline_set_returning_function(PlannerInfo *root, 
RangeTblEntry *rte)
                if (list_length(raw_parsetree_list) != 1)
                        goto fail;
 
-               querytree_list = 
pg_analyze_and_rewrite_params(linitial(raw_parsetree_list),
+               querytree_list = 
pg_analyze_and_rewrite_withcb(linitial(raw_parsetree_list),
                                                                                
                           src,
                                                                                
                           (ParserSetupHook) sql_fn_parser_setup,
                                                                                
                           pinfo, NULL);
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 146ee8dd1e..56c58d263d 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -96,7 +96,7 @@ static bool test_raw_expression_coverage(Node *node, void 
*context);
 
 
 /*
- * parse_analyze
+ * parse_analyze_fixedparams
  *             Analyze a raw parse tree and transform it to Query form.
  *
  * Optionally, information about $n parameter types can be supplied.
@@ -107,8 +107,8 @@ static bool test_raw_expression_coverage(Node *node, void 
*context);
  * a dummy CMD_UTILITY Query node.
  */
 Query *
-parse_analyze(RawStmt *parseTree, const char *sourceText,
-                         Oid *paramTypes, int numParams,
+parse_analyze_fixedparams(RawStmt *parseTree, const char *sourceText,
+                         const Oid *paramTypes, int numParams,
                          QueryEnvironment *queryEnv)
 {
        ParseState *pstate = make_parsestate(NULL);
@@ -120,7 +120,7 @@ parse_analyze(RawStmt *parseTree, const char *sourceText,
        pstate->p_sourcetext = sourceText;
 
        if (numParams > 0)
-               parse_fixed_parameters(pstate, paramTypes, numParams);
+               setup_parse_fixed_parameters(pstate, paramTypes, numParams);
 
        pstate->p_queryEnv = queryEnv;
 
@@ -158,7 +158,7 @@ parse_analyze_varparams(RawStmt *parseTree, const char 
*sourceText,
 
        pstate->p_sourcetext = sourceText;
 
-       parse_variable_parameters(pstate, paramTypes, numParams);
+       setup_parse_variable_parameters(pstate, paramTypes, numParams);
 
        query = transformTopLevelStmt(pstate, parseTree);
 
diff --git a/src/backend/parser/parse_param.c b/src/backend/parser/parse_param.c
index 68a5534393..b9726e0757 100644
--- a/src/backend/parser/parse_param.c
+++ b/src/backend/parser/parse_param.c
@@ -35,7 +35,7 @@
 
 typedef struct FixedParamState
 {
-       Oid                *paramTypes;         /* array of parameter type OIDs 
*/
+       const Oid  *paramTypes;         /* array of parameter type OIDs */
        int                     numParams;              /* number of array 
entries */
 } FixedParamState;
 
@@ -64,8 +64,8 @@ static bool query_contains_extern_params_walker(Node *node, 
void *context);
  * Set up to process a query containing references to fixed parameters.
  */
 void
-parse_fixed_parameters(ParseState *pstate,
-                                          Oid *paramTypes, int numParams)
+setup_parse_fixed_parameters(ParseState *pstate,
+                                          const Oid *paramTypes, int numParams)
 {
        FixedParamState *parstate = palloc(sizeof(FixedParamState));
 
@@ -80,7 +80,7 @@ parse_fixed_parameters(ParseState *pstate,
  * Set up to process a query containing references to variable parameters.
  */
 void
-parse_variable_parameters(ParseState *pstate,
+setup_parse_variable_parameters(ParseState *pstate,
                                                  Oid **paramTypes, int 
*numParams)
 {
        VarParamState *parstate = palloc(sizeof(VarParamState));
diff --git a/src/backend/parser/parse_utilcmd.c 
b/src/backend/parser/parse_utilcmd.c
index 2d857a301b..464dc6c31b 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3,7 +3,7 @@
  * parse_utilcmd.c
  *       Perform parse analysis work for various utility commands
  *
- * Formerly we did this work during parse_analyze() in analyze.c.  However
+ * Formerly we did this work during parse_analyze_*() in analyze.c.  However
  * that is fairly unsafe in the presence of querytree caching, since any
  * database state that we depend on in making the transformations might be
  * obsolete by the time the utility command is executed; and utility commands
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 82de01cdc6..e86275c781 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -640,8 +640,8 @@ pg_parse_query(const char *query_string)
  * NOTE: for reasons mentioned above, this must be separate from raw parsing.
  */
 List *
-pg_analyze_and_rewrite(RawStmt *parsetree, const char *query_string,
-                                          Oid *paramTypes, int numParams,
+pg_analyze_and_rewrite_fixedparams(RawStmt *parsetree, const char 
*query_string,
+                                          const Oid *paramTypes, int numParams,
                                           QueryEnvironment *queryEnv)
 {
        Query      *query;
@@ -655,7 +655,7 @@ pg_analyze_and_rewrite(RawStmt *parsetree, const char 
*query_string,
        if (log_parser_stats)
                ResetUsage();
 
-       query = parse_analyze(parsetree, query_string, paramTypes, numParams,
+       query = parse_analyze_fixedparams(parsetree, query_string, paramTypes, 
numParams,
                                                  queryEnv);
 
        if (log_parser_stats)
@@ -672,12 +672,13 @@ pg_analyze_and_rewrite(RawStmt *parsetree, const char 
*query_string,
 }
 
 /*
- * Do parse analysis and rewriting.  This is the same as pg_analyze_and_rewrite
- * except that external-parameter resolution is determined by parser callback
- * hooks instead of a fixed list of parameter datatypes.
+ * Do parse analysis and rewriting.  This is the same as
+ * pg_analyze_and_rewrite_* except that, instead of a fixed list of parameter
+ * datatypes, a parser callback is supplied that can do external-parameter
+ * resolution and possibly other things.
  */
 List *
-pg_analyze_and_rewrite_params(RawStmt *parsetree,
+pg_analyze_and_rewrite_withcb(RawStmt *parsetree,
                                                          const char 
*query_string,
                                                          ParserSetupHook 
parserSetup,
                                                          void *parserSetupArg,
@@ -1128,7 +1129,7 @@ exec_simple_query(const char *query_string)
                else
                        oldcontext = MemoryContextSwitchTo(MessageContext);
 
-               querytree_list = pg_analyze_and_rewrite(parsetree, query_string,
+               querytree_list = pg_analyze_and_rewrite_fixedparams(parsetree, 
query_string,
                                                                                
                NULL, 0, NULL);
 
                plantree_list = pg_plan_queries(querytree_list, query_string,
diff --git a/src/backend/utils/cache/plancache.c 
b/src/backend/utils/cache/plancache.c
index 6767eae8f2..28871f0176 100644
--- a/src/backend/utils/cache/plancache.c
+++ b/src/backend/utils/cache/plancache.c
@@ -682,13 +682,13 @@ RevalidateCachedQuery(CachedPlanSource *plansource,
        if (rawtree == NULL)
                tlist = NIL;
        else if (plansource->parserSetup != NULL)
-               tlist = pg_analyze_and_rewrite_params(rawtree,
+               tlist = pg_analyze_and_rewrite_withcb(rawtree,
                                                                                
          plansource->query_string,
                                                                                
          plansource->parserSetup,
                                                                                
          plansource->parserSetupArg,
                                                                                
          queryEnv);
        else
-               tlist = pg_analyze_and_rewrite(rawtree,
+               tlist = pg_analyze_and_rewrite_fixedparams(rawtree,
                                                                           
plansource->query_string,
                                                                           
plansource->param_types,
                                                                           
plansource->num_params,
diff --git a/src/include/parser/analyze.h b/src/include/parser/analyze.h
index a0f0bd38d7..e019bc9b1e 100644
--- a/src/include/parser/analyze.h
+++ b/src/include/parser/analyze.h
@@ -24,8 +24,8 @@ typedef void (*post_parse_analyze_hook_type) (ParseState 
*pstate,
 extern PGDLLIMPORT post_parse_analyze_hook_type post_parse_analyze_hook;
 
 
-extern Query *parse_analyze(RawStmt *parseTree, const char *sourceText,
-                                                       Oid *paramTypes, int 
numParams, QueryEnvironment *queryEnv);
+extern Query *parse_analyze_fixedparams(RawStmt *parseTree, const char 
*sourceText,
+                                                       const Oid *paramTypes, 
int numParams, QueryEnvironment *queryEnv);
 extern Query *parse_analyze_varparams(RawStmt *parseTree, const char 
*sourceText,
                                                                          Oid 
**paramTypes, int *numParams);
 
diff --git a/src/include/parser/parse_param.h b/src/include/parser/parse_param.h
index b42fff296c..e7069df6ac 100644
--- a/src/include/parser/parse_param.h
+++ b/src/include/parser/parse_param.h
@@ -15,9 +15,9 @@
 
 #include "parser/parse_node.h"
 
-extern void parse_fixed_parameters(ParseState *pstate,
-                                                                  Oid 
*paramTypes, int numParams);
-extern void parse_variable_parameters(ParseState *pstate,
+extern void setup_parse_fixed_parameters(ParseState *pstate,
+                                                                  const Oid 
*paramTypes, int numParams);
+extern void setup_parse_variable_parameters(ParseState *pstate,
                                                                          Oid 
**paramTypes, int *numParams);
 extern void check_variable_parameters(ParseState *pstate, Query *query);
 extern bool query_contains_extern_params(Query *query);
diff --git a/src/include/tcop/tcopprot.h b/src/include/tcop/tcopprot.h
index 5c77075aed..af056fe43a 100644
--- a/src/include/tcop/tcopprot.h
+++ b/src/include/tcop/tcopprot.h
@@ -45,11 +45,11 @@ extern PGDLLIMPORT int log_statement;
 
 extern List *pg_parse_query(const char *query_string);
 extern List *pg_rewrite_query(Query *query);
-extern List *pg_analyze_and_rewrite(RawStmt *parsetree,
+extern List *pg_analyze_and_rewrite_fixedparams(RawStmt *parsetree,
                                                                        const 
char *query_string,
-                                                                       Oid 
*paramTypes, int numParams,
+                                                                       const 
Oid *paramTypes, int numParams,
                                                                        
QueryEnvironment *queryEnv);
-extern List *pg_analyze_and_rewrite_params(RawStmt *parsetree,
+extern List *pg_analyze_and_rewrite_withcb(RawStmt *parsetree,
                                                                                
   const char *query_string,
                                                                                
   ParserSetupHook parserSetup,
                                                                                
   void *parserSetupArg,

base-commit: cab5b9ab2c066ba904f13de2681872dcda31e207
-- 
2.34.1

From b1e36d4b49cc37138cecbbd9c1f128f9c1df65f4 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <pe...@eisentraut.org>
Date: Tue, 28 Dec 2021 09:01:45 +0100
Subject: [PATCH v1 2/3] Add pg_analyze_and_rewrite_varparams()

This new function extracts common code from PrepareQuery() and
exec_parse_message().  It is then exactly analogous to the existing
pg_analyze_and_rewrite_fixedparams() and
pg_analyze_and_rewrite_withcb().

To unify these two code paths, this makes PrepareQuery() now subject
to log_parser_stats.  Also, both paths now invoke
TRACE_POSTGRESQL_QUERY_REWRITE_START().  PrepareQuery() no longer
checks whether a utility statement was specified.  The grammar doesn't
allow that anyway, and exec_parse_message() supports it, so
restricting it doesn't seem necessary.

This also adds QueryEnvironment support to the *varparams functions,
for consistency with its cousins, even though it is not used right
now.
---
 src/backend/commands/prepare.c | 43 ++-------------
 src/backend/parser/analyze.c   |  5 +-
 src/backend/tcop/postgres.c    | 95 +++++++++++++++++++++++-----------
 src/include/parser/analyze.h   |  2 +-
 src/include/tcop/tcopprot.h    |  5 ++
 5 files changed, 78 insertions(+), 72 deletions(-)

diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c
index 5e03c7c5aa..9d0d27184c 100644
--- a/src/backend/commands/prepare.c
+++ b/src/backend/commands/prepare.c
@@ -62,9 +62,7 @@ PrepareQuery(ParseState *pstate, PrepareStmt *stmt,
        CachedPlanSource *plansource;
        Oid                *argtypes = NULL;
        int                     nargs;
-       Query      *query;
        List       *query_list;
-       int                     i;
 
        /*
         * Disallow empty-string statement name (conflicts with protocol-level
@@ -96,6 +94,7 @@ PrepareQuery(ParseState *pstate, PrepareStmt *stmt,
 
        if (nargs)
        {
+               int                     i;
                ListCell   *l;
 
                argtypes = (Oid *) palloc(nargs * sizeof(Oid));
@@ -114,44 +113,10 @@ PrepareQuery(ParseState *pstate, PrepareStmt *stmt,
         * Analyze the statement using these parameter types (any parameters
         * passed in from above us will not be visible to it), allowing
         * information about unknown parameters to be deduced from context.
+        * Rewrite the query. The result could be 0, 1, or many queries.
         */
-       query = parse_analyze_varparams(rawstmt, pstate->p_sourcetext,
-                                                                       
&argtypes, &nargs);
-
-       /*
-        * Check that all parameter types were determined.
-        */
-       for (i = 0; i < nargs; i++)
-       {
-               Oid                     argtype = argtypes[i];
-
-               if (argtype == InvalidOid || argtype == UNKNOWNOID)
-                       ereport(ERROR,
-                                       
(errcode(ERRCODE_INDETERMINATE_DATATYPE),
-                                        errmsg("could not determine data type 
of parameter $%d",
-                                                       i + 1)));
-       }
-
-       /*
-        * grammar only allows PreparableStmt, so this check should be redundant
-        */
-       switch (query->commandType)
-       {
-               case CMD_SELECT:
-               case CMD_INSERT:
-               case CMD_UPDATE:
-               case CMD_DELETE:
-                       /* OK */
-                       break;
-               default:
-                       ereport(ERROR,
-                                       
(errcode(ERRCODE_INVALID_PSTATEMENT_DEFINITION),
-                                        errmsg("utility statements cannot be 
prepared")));
-                       break;
-       }
-
-       /* Rewrite the query. The result could be 0, 1, or many queries. */
-       query_list = QueryRewrite(query);
+       query_list = pg_analyze_and_rewrite_varparams(rawstmt, 
pstate->p_sourcetext,
+                                                                               
                  &argtypes, &nargs, NULL);
 
        /* Finish filling in the CachedPlanSource */
        CompleteCachedPlan(plansource,
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 56c58d263d..4a9d212b22 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -148,7 +148,8 @@ parse_analyze_fixedparams(RawStmt *parseTree, const char 
*sourceText,
  */
 Query *
 parse_analyze_varparams(RawStmt *parseTree, const char *sourceText,
-                                               Oid **paramTypes, int 
*numParams)
+                                               Oid **paramTypes, int 
*numParams,
+                                               QueryEnvironment *queryEnv)
 {
        ParseState *pstate = make_parsestate(NULL);
        Query      *query;
@@ -160,6 +161,8 @@ parse_analyze_varparams(RawStmt *parseTree, const char 
*sourceText,
 
        setup_parse_variable_parameters(pstate, paramTypes, numParams);
 
+       pstate->p_queryEnv = queryEnv;
+
        query = transformTopLevelStmt(pstate, parseTree);
 
        /* make sure all is well with parameter types */
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index e86275c781..8ade13994f 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -640,9 +640,11 @@ pg_parse_query(const char *query_string)
  * NOTE: for reasons mentioned above, this must be separate from raw parsing.
  */
 List *
-pg_analyze_and_rewrite_fixedparams(RawStmt *parsetree, const char 
*query_string,
-                                          const Oid *paramTypes, int numParams,
-                                          QueryEnvironment *queryEnv)
+pg_analyze_and_rewrite_fixedparams(RawStmt *parsetree,
+                                                                  const char 
*query_string,
+                                                                  const Oid 
*paramTypes,
+                                                                  int 
numParams,
+                                                                  
QueryEnvironment *queryEnv)
 {
        Query      *query;
        List       *querytree_list;
@@ -671,6 +673,59 @@ pg_analyze_and_rewrite_fixedparams(RawStmt *parsetree, 
const char *query_string,
        return querytree_list;
 }
 
+/*
+ * Do parse analysis and rewriting.  This is the same as
+ * pg_analyze_and_rewrite_fixedparams() except that it's okay to deduce
+ * information about $n symbol datatypes from context.
+ */
+List *
+pg_analyze_and_rewrite_varparams(RawStmt *parsetree,
+                                                                const char 
*query_string,
+                                                                Oid 
**paramTypes,
+                                                                int *numParams,
+                                                                
QueryEnvironment *queryEnv)
+{
+       Query      *query;
+       List       *querytree_list;
+
+       TRACE_POSTGRESQL_QUERY_REWRITE_START(query_string);
+
+       /*
+        * (1) Perform parse analysis.
+        */
+       if (log_parser_stats)
+               ResetUsage();
+
+       query = parse_analyze_varparams(parsetree, query_string, paramTypes, 
numParams,
+                                                 queryEnv);
+
+       /*
+        * Check all parameter types got determined.
+        */
+       for (int i = 0; i < *numParams; i++)
+       {
+               Oid                     ptype = (*paramTypes)[i];
+
+               if (ptype == InvalidOid || ptype == UNKNOWNOID)
+                       ereport(ERROR,
+                                       
(errcode(ERRCODE_INDETERMINATE_DATATYPE),
+                                        errmsg("could not determine data type 
of parameter $%d",
+                                                       i + 1)));
+       }
+
+       if (log_parser_stats)
+               ShowUsage("PARSE ANALYSIS STATISTICS");
+
+       /*
+        * (2) Rewrite the queries, as necessary
+        */
+       querytree_list = pg_rewrite_query(query);
+
+       TRACE_POSTGRESQL_QUERY_REWRITE_DONE(query_string);
+
+       return querytree_list;
+}
+
 /*
  * Do parse analysis and rewriting.  This is the same as
  * pg_analyze_and_rewrite_* except that, instead of a fixed list of parameter
@@ -1412,7 +1467,6 @@ exec_parse_message(const char *query_string,      /* 
string to execute */
 
        if (parsetree_list != NIL)
        {
-               Query      *query;
                bool            snapshot_set = false;
 
                raw_parse_tree = linitial_node(RawStmt, parsetree_list);
@@ -1452,34 +1506,13 @@ exec_parse_message(const char *query_string,    /* 
string to execute */
                /*
                 * Analyze and rewrite the query.  Note that the originally 
specified
                 * parameter set is not required to be complete, so we have to 
use
-                * parse_analyze_varparams().
-                */
-               if (log_parser_stats)
-                       ResetUsage();
-
-               query = parse_analyze_varparams(raw_parse_tree,
-                                                                               
query_string,
-                                                                               
&paramTypes,
-                                                                               
&numParams);
-
-               /*
-                * Check all parameter types got determined.
+                * pg_analyze_and_rewrite_varparams().
                 */
-               for (int i = 0; i < numParams; i++)
-               {
-                       Oid                     ptype = paramTypes[i];
-
-                       if (ptype == InvalidOid || ptype == UNKNOWNOID)
-                               ereport(ERROR,
-                                               
(errcode(ERRCODE_INDETERMINATE_DATATYPE),
-                                                errmsg("could not determine 
data type of parameter $%d",
-                                                               i + 1)));
-               }
-
-               if (log_parser_stats)
-                       ShowUsage("PARSE ANALYSIS STATISTICS");
-
-               querytree_list = pg_rewrite_query(query);
+               querytree_list = 
pg_analyze_and_rewrite_varparams(raw_parse_tree,
+                                                                               
                                  query_string,
+                                                                               
                                  &paramTypes,
+                                                                               
                                  &numParams,
+                                                                               
                                  NULL);
 
                /* Done with the snapshot used for parsing */
                if (snapshot_set)
diff --git a/src/include/parser/analyze.h b/src/include/parser/analyze.h
index e019bc9b1e..b1c592c6c6 100644
--- a/src/include/parser/analyze.h
+++ b/src/include/parser/analyze.h
@@ -27,7 +27,7 @@ extern PGDLLIMPORT post_parse_analyze_hook_type 
post_parse_analyze_hook;
 extern Query *parse_analyze_fixedparams(RawStmt *parseTree, const char 
*sourceText,
                                                        const Oid *paramTypes, 
int numParams, QueryEnvironment *queryEnv);
 extern Query *parse_analyze_varparams(RawStmt *parseTree, const char 
*sourceText,
-                                                                         Oid 
**paramTypes, int *numParams);
+                                                                         Oid 
**paramTypes, int *numParams, QueryEnvironment *queryEnv);
 
 extern Query *parse_sub_analyze(Node *parseTree, ParseState *parentParseState,
                                                                CommonTableExpr 
*parentCTE,
diff --git a/src/include/tcop/tcopprot.h b/src/include/tcop/tcopprot.h
index af056fe43a..37bce8cdb5 100644
--- a/src/include/tcop/tcopprot.h
+++ b/src/include/tcop/tcopprot.h
@@ -49,6 +49,11 @@ extern List *pg_analyze_and_rewrite_fixedparams(RawStmt 
*parsetree,
                                                                        const 
char *query_string,
                                                                        const 
Oid *paramTypes, int numParams,
                                                                        
QueryEnvironment *queryEnv);
+extern List *pg_analyze_and_rewrite_varparams(RawStmt *parsetree,
+                                                                               
          const char *query_string,
+                                                                               
          Oid **paramTypes,
+                                                                               
          int *numParams,
+                                                                               
          QueryEnvironment *queryEnv);
 extern List *pg_analyze_and_rewrite_withcb(RawStmt *parsetree,
                                                                                
   const char *query_string,
                                                                                
   ParserSetupHook parserSetup,
-- 
2.34.1

From 9baad5def3d9a4b442a591b9f6286044c354f89d Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <pe...@eisentraut.org>
Date: Tue, 28 Dec 2021 09:01:45 +0100
Subject: [PATCH v1 3/3] Add parse_analyze_withcb()

This extracts code from pg_analyze_and_rewrite_withcb() into a
separate function that mirrors the existing
parse_analyze_fixedparams() and parse_analyze_varparams().
---
 src/backend/parser/analyze.c | 38 ++++++++++++++++++++++++++++++++++++
 src/backend/tcop/postgres.c  | 22 ++-------------------
 src/include/parser/analyze.h |  5 +++++
 3 files changed, 45 insertions(+), 20 deletions(-)

diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 4a9d212b22..6f4cc1b9db 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -181,6 +181,44 @@ parse_analyze_varparams(RawStmt *parseTree, const char 
*sourceText,
        return query;
 }
 
+/*
+ * parse_analyze_withcb
+ *
+ * This variant is used when the caller supplies their own parser callback to
+ * resolve parameters and possibly other things.
+ */
+Query *
+parse_analyze_withcb(RawStmt *parseTree, const char *sourceText,
+                                        ParserSetupHook parserSetup,
+                                        void *parserSetupArg,
+                                        QueryEnvironment *queryEnv)
+{
+       ParseState *pstate = make_parsestate(NULL);
+       Query      *query;
+       JumbleState *jstate = NULL;
+
+       Assert(sourceText != NULL); /* required as of 8.4 */
+
+       pstate->p_sourcetext = sourceText;
+       pstate->p_queryEnv = queryEnv;
+       (*parserSetup) (pstate, parserSetupArg);
+
+       query = transformTopLevelStmt(pstate, parseTree);
+
+       if (IsQueryIdEnabled())
+               jstate = JumbleQuery(query, sourceText);
+
+       if (post_parse_analyze_hook)
+               (*post_parse_analyze_hook) (pstate, query, jstate);
+
+       free_parsestate(pstate);
+
+       pgstat_report_query_id(query->queryId, false);
+
+       return query;
+}
+
+
 /*
  * parse_sub_analyze
  *             Entry point for recursively analyzing a sub-statement.
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 8ade13994f..a646e34d4c 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -739,12 +739,8 @@ pg_analyze_and_rewrite_withcb(RawStmt *parsetree,
                                                          void *parserSetupArg,
                                                          QueryEnvironment 
*queryEnv)
 {
-       ParseState *pstate;
        Query      *query;
        List       *querytree_list;
-       JumbleState *jstate = NULL;
-
-       Assert(query_string != NULL);   /* required as of 8.4 */
 
        TRACE_POSTGRESQL_QUERY_REWRITE_START(query_string);
 
@@ -754,22 +750,8 @@ pg_analyze_and_rewrite_withcb(RawStmt *parsetree,
        if (log_parser_stats)
                ResetUsage();
 
-       pstate = make_parsestate(NULL);
-       pstate->p_sourcetext = query_string;
-       pstate->p_queryEnv = queryEnv;
-       (*parserSetup) (pstate, parserSetupArg);
-
-       query = transformTopLevelStmt(pstate, parsetree);
-
-       if (IsQueryIdEnabled())
-               jstate = JumbleQuery(query, query_string);
-
-       if (post_parse_analyze_hook)
-               (*post_parse_analyze_hook) (pstate, query, jstate);
-
-       free_parsestate(pstate);
-
-       pgstat_report_query_id(query->queryId, false);
+       query = parse_analyze_withcb(parsetree, query_string, parserSetup, 
parserSetupArg,
+                                                                queryEnv);
 
        if (log_parser_stats)
                ShowUsage("PARSE ANALYSIS STATISTICS");
diff --git a/src/include/parser/analyze.h b/src/include/parser/analyze.h
index b1c592c6c6..423b956869 100644
--- a/src/include/parser/analyze.h
+++ b/src/include/parser/analyze.h
@@ -14,6 +14,7 @@
 #ifndef ANALYZE_H
 #define ANALYZE_H
 
+#include "nodes/params.h"
 #include "parser/parse_node.h"
 #include "utils/queryjumble.h"
 
@@ -28,6 +29,10 @@ extern Query *parse_analyze_fixedparams(RawStmt *parseTree, 
const char *sourceTe
                                                        const Oid *paramTypes, 
int numParams, QueryEnvironment *queryEnv);
 extern Query *parse_analyze_varparams(RawStmt *parseTree, const char 
*sourceText,
                                                                          Oid 
**paramTypes, int *numParams, QueryEnvironment *queryEnv);
+extern Query *parse_analyze_withcb(RawStmt *parseTree, const char *sourceText,
+                                                                  
ParserSetupHook parserSetup,
+                                                                  void 
*parserSetupArg,
+                                                                  
QueryEnvironment *queryEnv);
 
 extern Query *parse_sub_analyze(Node *parseTree, ParseState *parentParseState,
                                                                CommonTableExpr 
*parentCTE,
-- 
2.34.1

Reply via email to