Add the ability for the core grammar to have more than one parse target.
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 4 Jan 2021 16:03:22 +0000 (11:03 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 4 Jan 2021 16:03:22 +0000 (11:03 -0500)
This patch essentially allows gram.y to implement a family of related
syntax trees, rather than necessarily always parsing a list of SQL
statements.  raw_parser() gains a new argument, enum RawParseMode,
to say what to do.  As proof of concept, add a mode that just parses
a TypeName without any other decoration, and use that to greatly
simplify typeStringToTypeName().

In addition, invent a new SPI entry point SPI_prepare_extended() to
allow SPI users (particularly plpgsql) to get at this new functionality.
In hopes of making this the last variant of SPI_prepare(), set up its
additional arguments as a struct rather than direct arguments, and
promise that future additions to the struct can default to zero.
SPI_prepare_cursor() and SPI_prepare_params() can perhaps go away at
some point.

Discussion: https://postgr.es/m/4165684.1607707277@sss.pgh.pa.us

14 files changed:
doc/src/sgml/spi.sgml
src/backend/commands/tablecmds.c
src/backend/executor/spi.c
src/backend/parser/gram.y
src/backend/parser/parse_coerce.c
src/backend/parser/parse_type.c
src/backend/parser/parser.c
src/backend/tcop/postgres.c
src/include/executor/spi.h
src/include/executor/spi_priv.h
src/include/parser/parser.h
src/interfaces/ecpg/preproc/parse.pl
src/pl/plpgsql/src/pl_exec.c
src/pl/plpgsql/src/pl_gram.y

index 6e92e15ca3b3fbdba908a4cad109b9c0175ed2c1..f5e0a35da0645e6b20c342d5aa5a68ff1dbebec4 100644 (file)
@@ -1105,6 +1105,11 @@ SPIPlanPtr SPI_prepare_cursor(const char * <parameter>command</parameter>, int <
    for the <structfield>options</structfield> field of <structname>DeclareCursorStmt</structname>.
    <function>SPI_prepare</function> always takes the cursor options as zero.
   </para>
+
+  <para>
+   This function is now deprecated in favor
+   of <function>SPI_prepare_extended</function>.
+  </para>
  </refsect1>
 
  <refsect1>
@@ -1176,6 +1181,122 @@ SPIPlanPtr SPI_prepare_cursor(const char * <parameter>command</parameter>, int <
 
 <!-- *********************************************** -->
 
+<refentry id="spi-spi-prepare-extended">
+ <indexterm><primary>SPI_prepare_extended</primary></indexterm>
+
+ <refmeta>
+  <refentrytitle>SPI_prepare_extended</refentrytitle>
+  <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+  <refname>SPI_prepare_extended</refname>
+  <refpurpose>prepare a statement, without executing it yet</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+SPIPlanPtr SPI_prepare_extended(const char * <parameter>command</parameter>,
+                                const SPIPrepareOptions * <parameter>options</parameter>)
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+  <title>Description</title>
+
+  <para>
+   <function>SPI_prepare_extended</function> creates and returns a prepared
+   statement for the specified command, but doesn't execute the command.
+   This function is equivalent to <function>SPI_prepare</function>,
+   with the addition that the caller can specify options to control
+   the parsing of external parameter references, as well as other facets
+   of query parsing and planning.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Arguments</title>
+
+  <variablelist>
+   <varlistentry>
+    <term><literal>const char * <parameter>command</parameter></literal></term>
+    <listitem>
+     <para>
+      command string
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>const SPIPrepareOptions * <parameter>options</parameter></literal></term>
+    <listitem>
+     <para>
+      struct containing optional arguments
+     </para>
+    </listitem>
+   </varlistentry>
+  </variablelist>
+
+  <para>
+   Callers should always zero out the entire <parameter>options</parameter>
+   struct, then fill whichever fields they want to set.  This ensures forward
+   compatibility of code, since any fields that are added to the struct in
+   future will be defined to behave backwards-compatibly if they are zero.
+   The currently available <parameter>options</parameter> fields are:
+  </para>
+
+  <variablelist>
+   <varlistentry>
+    <term><literal>ParserSetupHook <parameter>parserSetup</parameter></literal></term>
+    <listitem>
+     <para>
+      Parser hook setup function
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>void * <parameter>parserSetupArg</parameter></literal></term>
+    <listitem>
+     <para>
+      pass-through argument for <parameter>parserSetup</parameter>
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>RawParseMode <parameter>parseMode</parameter></literal></term>
+    <listitem>
+     <para>
+      mode for raw parsing; <literal>RAW_PARSE_DEFAULT</literal> (zero)
+      produces default behavior
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>int <parameter>cursorOptions</parameter></literal></term>
+    <listitem>
+     <para>
+      integer bit mask of cursor options; zero produces default behavior
+     </para>
+    </listitem>
+   </varlistentry>
+  </variablelist>
+ </refsect1>
+
+ <refsect1>
+  <title>Return Value</title>
+
+  <para>
+   <function>SPI_prepare_extended</function> has the same return conventions as
+   <function>SPI_prepare</function>.
+  </para>
+ </refsect1>
+</refentry>
+
+<!-- *********************************************** -->
+
 <refentry id="spi-spi-prepare-params">
  <indexterm><primary>SPI_prepare_params</primary></indexterm>
 
@@ -1208,6 +1329,11 @@ SPIPlanPtr SPI_prepare_params(const char * <parameter>command</parameter>,
    with the addition that the caller can specify parser hook functions
    to control the parsing of external parameter references.
   </para>
+
+  <para>
+   This function is now deprecated in favor
+   of <function>SPI_prepare_extended</function>.
+  </para>
  </refsect1>
 
  <refsect1>
index 11dae782fd2f5bdbb7eeec0b0bdadbb2d815f552..993da56d437c9fcd394fd5d4c23b2ff3109435dc 100644 (file)
@@ -12095,7 +12095,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
         * 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_parsetree_list = raw_parser(cmd, RAW_PARSE_DEFAULT);
        querytree_list = NIL;
        foreach(list_item, raw_parsetree_list)
        {
index 8368ead1ef6c17ed6eff59f8c44a807ac00ab2b4..6c0593686a9573ac7caf68460b1a851c9d15c362 100644 (file)
@@ -508,6 +508,7 @@ SPI_execute(const char *src, bool read_only, long tcount)
 
        memset(&plan, 0, sizeof(_SPI_plan));
        plan.magic = _SPI_PLAN_MAGIC;
+       plan.parse_mode = RAW_PARSE_DEFAULT;
        plan.cursor_options = CURSOR_OPT_PARALLEL_OK;
 
        _SPI_prepare_oneshot_plan(src, &plan);
@@ -681,6 +682,7 @@ SPI_execute_with_args(const char *src,
 
        memset(&plan, 0, sizeof(_SPI_plan));
        plan.magic = _SPI_PLAN_MAGIC;
+       plan.parse_mode = RAW_PARSE_DEFAULT;
        plan.cursor_options = CURSOR_OPT_PARALLEL_OK;
        plan.nargs = nargs;
        plan.argtypes = argtypes;
@@ -726,6 +728,7 @@ SPI_execute_with_receiver(const char *src,
 
        memset(&plan, 0, sizeof(_SPI_plan));
        plan.magic = _SPI_PLAN_MAGIC;
+       plan.parse_mode = RAW_PARSE_DEFAULT;
        plan.cursor_options = CURSOR_OPT_PARALLEL_OK;
        if (params)
        {
@@ -768,6 +771,7 @@ SPI_prepare_cursor(const char *src, int nargs, Oid *argtypes,
 
        memset(&plan, 0, sizeof(_SPI_plan));
        plan.magic = _SPI_PLAN_MAGIC;
+       plan.parse_mode = RAW_PARSE_DEFAULT;
        plan.cursor_options = cursorOptions;
        plan.nargs = nargs;
        plan.argtypes = argtypes;
@@ -784,6 +788,42 @@ SPI_prepare_cursor(const char *src, int nargs, Oid *argtypes,
        return result;
 }
 
+SPIPlanPtr
+SPI_prepare_extended(const char *src,
+                                        const SPIPrepareOptions *options)
+{
+       _SPI_plan       plan;
+       SPIPlanPtr      result;
+
+       if (src == NULL || options == NULL)
+       {
+               SPI_result = SPI_ERROR_ARGUMENT;
+               return NULL;
+       }
+
+       SPI_result = _SPI_begin_call(true);
+       if (SPI_result < 0)
+               return NULL;
+
+       memset(&plan, 0, sizeof(_SPI_plan));
+       plan.magic = _SPI_PLAN_MAGIC;
+       plan.parse_mode = options->parseMode;
+       plan.cursor_options = options->cursorOptions;
+       plan.nargs = 0;
+       plan.argtypes = NULL;
+       plan.parserSetup = options->parserSetup;
+       plan.parserSetupArg = options->parserSetupArg;
+
+       _SPI_prepare_plan(src, &plan);
+
+       /* copy plan to procedure context */
+       result = _SPI_make_plan_non_temp(&plan);
+
+       _SPI_end_call(true);
+
+       return result;
+}
+
 SPIPlanPtr
 SPI_prepare_params(const char *src,
                                   ParserSetupHook parserSetup,
@@ -805,6 +845,7 @@ SPI_prepare_params(const char *src,
 
        memset(&plan, 0, sizeof(_SPI_plan));
        plan.magic = _SPI_PLAN_MAGIC;
+       plan.parse_mode = RAW_PARSE_DEFAULT;
        plan.cursor_options = cursorOptions;
        plan.nargs = 0;
        plan.argtypes = NULL;
@@ -1340,6 +1381,7 @@ SPI_cursor_open_with_args(const char *name,
 
        memset(&plan, 0, sizeof(_SPI_plan));
        plan.magic = _SPI_PLAN_MAGIC;
+       plan.parse_mode = RAW_PARSE_DEFAULT;
        plan.cursor_options = cursorOptions;
        plan.nargs = nargs;
        plan.argtypes = argtypes;
@@ -1400,6 +1442,7 @@ SPI_cursor_parse_open_with_paramlist(const char *name,
 
        memset(&plan, 0, sizeof(_SPI_plan));
        plan.magic = _SPI_PLAN_MAGIC;
+       plan.parse_mode = RAW_PARSE_DEFAULT;
        plan.cursor_options = cursorOptions;
        if (params)
        {
@@ -2036,7 +2079,8 @@ spi_printtup(TupleTableSlot *slot, DestReceiver *self)
  * Parse and analyze a querystring.
  *
  * At entry, plan->argtypes and plan->nargs (or alternatively plan->parserSetup
- * and plan->parserSetupArg) must be valid, as must plan->cursor_options.
+ * and plan->parserSetupArg) must be valid, as must plan->parse_mode and
+ * plan->cursor_options.
  *
  * Results are stored into *plan (specifically, plan->plancache_list).
  * Note that the result data is all in CurrentMemoryContext or child contexts
@@ -2063,7 +2107,7 @@ _SPI_prepare_plan(const char *src, SPIPlanPtr plan)
        /*
         * Parse the request string into a list of raw parse trees.
         */
-       raw_parsetree_list = pg_parse_query(src);
+       raw_parsetree_list = raw_parser(src, plan->parse_mode);
 
        /*
         * Do parse analysis and rule rewrite for each raw parsetree, storing the
@@ -2168,7 +2212,7 @@ _SPI_prepare_oneshot_plan(const char *src, SPIPlanPtr plan)
        /*
         * Parse the request string into a list of raw parse trees.
         */
-       raw_parsetree_list = pg_parse_query(src);
+       raw_parsetree_list = raw_parser(src, plan->parse_mode);
 
        /*
         * Construct plancache entries, but don't do parse analysis yet.
@@ -2866,6 +2910,7 @@ _SPI_make_plan_non_temp(SPIPlanPtr plan)
        newplan = (SPIPlanPtr) palloc0(sizeof(_SPI_plan));
        newplan->magic = _SPI_PLAN_MAGIC;
        newplan->plancxt = plancxt;
+       newplan->parse_mode = plan->parse_mode;
        newplan->cursor_options = plan->cursor_options;
        newplan->nargs = plan->nargs;
        if (plan->nargs > 0)
@@ -2930,6 +2975,7 @@ _SPI_save_plan(SPIPlanPtr plan)
        newplan = (SPIPlanPtr) palloc0(sizeof(_SPI_plan));
        newplan->magic = _SPI_PLAN_MAGIC;
        newplan->plancxt = plancxt;
+       newplan->parse_mode = plan->parse_mode;
        newplan->cursor_options = plan->cursor_options;
        newplan->nargs = plan->nargs;
        if (plan->nargs > 0)
index 18e181d5005dfd88544fbed61d4a86066ee94694..fb025f08a4eb415e3c60620845f4cb34a588af30 100644 (file)
@@ -384,7 +384,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 %type <node>   vacuum_relation
 %type <selectlimit> opt_select_limit select_limit limit_clause
 
-%type <list>   stmtblock stmtmulti
+%type <list>   parse_toplevel stmtmulti
                                OptTableElementList TableElementList OptInherit definition
                                OptTypedTableElementList TypedTableElementList
                                reloptions opt_reloptions
@@ -723,6 +723,15 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
  */
 %token         NOT_LA NULLS_LA WITH_LA
 
+/*
+ * The grammar likewise thinks these tokens are keywords, but they are never
+ * generated by the scanner.  Rather, they can be injected by parser.c as
+ * the initial token of the string (using the lookahead-token mechanism
+ * implemented there).  This provides a way to tell the grammar to parse
+ * something other than the usual list of SQL commands.
+ */
+%token         MODE_TYPE_NAME
+
 
 /* Precedence: lowest to highest */
 %nonassoc      SET                             /* see relation_expr_opt_alias */
@@ -787,11 +796,20 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 
 /*
  *     The target production for the whole parse.
+ *
+ * Ordinarily we parse a list of statements, but if we see one of the
+ * special MODE_XXX symbols as first token, we parse something else.
+ * The options here correspond to enum RawParseMode, which see for details.
  */
-stmtblock:     stmtmulti
+parse_toplevel:
+                       stmtmulti
                        {
                                pg_yyget_extra(yyscanner)->parsetree = $1;
                        }
+                       | MODE_TYPE_NAME Typename
+                       {
+                               pg_yyget_extra(yyscanner)->parsetree = list_make1($2);
+                       }
                ;
 
 /*
index 8d01fca6d21783aaf65bf5b03ddc4e1d95f90d15..74eb39c0e4d8d5bce67e45b7a9282006cd1e5892 100644 (file)
@@ -1541,7 +1541,7 @@ select_common_typmod(ParseState *pstate, List *exprs, Oid common_type)
 
        foreach(lc, exprs)
        {
-               Node   *expr = (Node *) lfirst(lc);
+               Node       *expr = (Node *) lfirst(lc);
 
                /* Types must match */
                if (exprType(expr) != common_type)
@@ -2380,7 +2380,8 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
                        if (!OidIsValid(elem_typeid))
                        {
                                /*
-                                * if we don't have an element type yet, use the one we just got
+                                * if we don't have an element type yet, use the one we just
+                                * got
                                 */
                                elem_typeid = range_typelem;
                        }
index 717125ad87388fbdc3551c53ea554627677f3c45..abe131ebebfc2d4f71d17dfc4d7f31900229c804 100644 (file)
@@ -719,13 +719,6 @@ pts_error_callback(void *arg)
        const char *str = (const char *) arg;
 
        errcontext("invalid type name \"%s\"", str);
-
-       /*
-        * Currently we just suppress any syntax error position report, rather
-        * than transforming to an "internal query" error.  It's unlikely that a
-        * type name is complex enough to need positioning.
-        */
-       errposition(0);
 }
 
 /*
@@ -737,11 +730,7 @@ pts_error_callback(void *arg)
 TypeName *
 typeStringToTypeName(const char *str)
 {
-       StringInfoData buf;
        List       *raw_parsetree_list;
-       SelectStmt *stmt;
-       ResTarget  *restarget;
-       Typ