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;
-   TypeCast   *typecast;
    TypeName   *typeName;
    ErrorContextCallback ptserrcontext;
 
@@ -749,9 +738,6 @@ typeStringToTypeName(const char *str)
    if (strspn(str, " \t\n\r\f") == strlen(str))
        goto fail;
 
-   initStringInfo(&buf);
-   appendStringInfo(&buf, "SELECT NULL::%s", str);
-
    /*
     * Setup error traceback support in case of ereport() during parse
     */
@@ -760,58 +746,18 @@ typeStringToTypeName(const char *str)
    ptserrcontext.previous = error_context_stack;
    error_context_stack = &ptserrcontext;
 
-   raw_parsetree_list = raw_parser(buf.data);
+   raw_parsetree_list = raw_parser(str, RAW_PARSE_TYPE_NAME);
 
    error_context_stack = ptserrcontext.previous;
 
-   /*
-    * Make sure we got back exactly what we expected and no more; paranoia is
-    * justified since the string might contain anything.
-    */
-   if (list_length(raw_parsetree_list) != 1)
-       goto fail;
-   stmt = (SelectStmt *) linitial_node(RawStmt, raw_parsetree_list)->stmt;
-   if (stmt == NULL ||
-       !IsA(stmt, SelectStmt) ||
-       stmt->distinctClause != NIL ||
-       stmt->intoClause != NULL ||
-       stmt->fromClause != NIL ||
-       stmt->whereClause != NULL ||
-       stmt->groupClause != NIL ||
-       stmt->havingClause != NULL ||
-       stmt->windowClause != NIL ||
-       stmt->valuesLists != NIL ||
-       stmt->sortClause != NIL ||
-       stmt->limitOffset != NULL ||
-       stmt->limitCount != NULL ||
-       stmt->lockingClause != NIL ||
-       stmt->withClause != NULL ||
-       stmt->op != SETOP_NONE)
-       goto fail;
-   if (list_length(stmt->targetList) != 1)
-       goto fail;
-   restarget = (ResTarget *) linitial(stmt->targetList);
-   if (restarget == NULL ||
-       !IsA(restarget, ResTarget) ||
-       restarget->name != NULL ||
-       restarget->indirection != NIL)
-       goto fail;
-   typecast = (TypeCast *) restarget->val;
-   if (typecast == NULL ||
-       !IsA(typecast, TypeCast) ||
-       typecast->arg == NULL ||
-       !IsA(typecast->arg, A_Const))
-       goto fail;
+   /* We should get back exactly one TypeName node. */
+   Assert(list_length(raw_parsetree_list) == 1);
+   typeName = linitial_node(TypeName, raw_parsetree_list);
 
-   typeName = typecast->typeName;
-   if (typeName == NULL ||
-       !IsA(typeName, TypeName))
-       goto fail;
+   /* The grammar allows SETOF in TypeName, but we don't want that here. */
    if (typeName->setof)
        goto fail;
 
-   pfree(buf.data);
-
    return typeName;
 
 fail:
index b897a5160a22cba3c5e80a010f7c94d592067b97..8eb8feb372ee652bfd60aa3a43fe8d464737ffce 100644 (file)
@@ -35,11 +35,11 @@ static char *str_udeescape(const char *str, char escape,
  * raw_parser
  *     Given a query in string form, do lexical and grammatical analysis.
  *
- * Returns a list of raw (un-analyzed) parse trees.  The immediate elements
- * of the list are always RawStmt nodes.
+ * Returns a list of raw (un-analyzed) parse trees.  The contents of the
+ * list have the form required by the specified RawParseMode.
  */
 List *
-raw_parser(const char *str)
+raw_parser(const char *str, RawParseMode mode)
 {
    core_yyscan_t yyscanner;
    base_yy_extra_type yyextra;
@@ -49,8 +49,22 @@ raw_parser(const char *str)
    yyscanner = scanner_init(str, &yyextra.core_yy_extra,
                             &ScanKeywords, ScanKeywordTokens);
 
-   /* base_yylex() only needs this much initialization */
-   yyextra.have_lookahead = false;
+   /* base_yylex() only needs us to initialize the lookahead token, if any */
+   if (mode == RAW_PARSE_DEFAULT)
+       yyextra.have_lookahead = false;
+   else
+   {
+       /* this array is indexed by RawParseMode enum */
+       static const int mode_token[] = {
+           0,                  /* RAW_PARSE_DEFAULT */
+           MODE_TYPE_NAME      /* RAW_PARSE_TYPE_NAME */
+       };
+
+       yyextra.have_lookahead = true;
+       yyextra.lookahead_token = mode_token[mode];
+       yyextra.lookahead_yylloc = 0;
+       yyextra.lookahead_end = NULL;
+   }
 
    /* initialize the bison parser */
    parser_init(&yyextra);
@@ -104,7 +118,8 @@ base_yylex(YYSTYPE *lvalp, YYLTYPE *llocp, core_yyscan_t yyscanner)
        cur_token = yyextra->lookahead_token;
        lvalp->core_yystype = yyextra->lookahead_yylval;
        *llocp = yyextra->lookahead_yylloc;
-       *(yyextra->lookahead_end) = yyextra->lookahead_hold_char;
+       if (yyextra->lookahead_end)
+           *(yyextra->lookahead_end) = yyextra->lookahead_hold_char;
        yyextra->have_lookahead = false;
    }
    else
index dfa0d685a837a67f23ec801a8ece6c4487747638..f5c14249d13db156f68b6db433458a202a29dced 100644 (file)
@@ -635,7 +635,7 @@ pg_parse_query(const char *query_string)
    if (log_parser_stats)
        ResetUsage();
 
-   raw_parsetree_list = raw_parser(query_string);
+   raw_parsetree_list = raw_parser(query_string, RAW_PARSE_DEFAULT);
 
    if (log_parser_stats)
        ShowUsage("PARSER STATISTICS");
index 6e603d007d73615ab663e5e6198f735b0ec92659..9c70603434a20ad0604aa066555188d7678bf506 100644 (file)
@@ -15,7 +15,7 @@
 
 #include "commands/trigger.h"
 #include "lib/ilist.h"
-#include "nodes/parsenodes.h"
+#include "parser/parser.h"
 #include "utils/portal.h"
 
 
@@ -33,6 +33,15 @@ typedef struct SPITupleTable
    SubTransactionId subid;     /* subxact in which tuptable was created */
 } SPITupleTable;
 
+/* Optional arguments for SPI_prepare_extended */
+typedef struct SPIPrepareOptions
+{
+   ParserSetupHook parserSetup;
+   void       *parserSetupArg;
+   RawParseMode parseMode;
+   int         cursorOptions;
+} SPIPrepareOptions;
+
 /* Plans are opaque structs for standard users of SPI */
 typedef struct _SPI_plan *SPIPlanPtr;
 
@@ -113,6 +122,8 @@ extern int  SPI_execute_with_receiver(const char *src,
 extern SPIPlanPtr SPI_prepare(const char *src, int nargs, Oid *argtypes);
 extern SPIPlanPtr SPI_prepare_cursor(const char *src, int nargs, Oid *argtypes,
                                     int cursorOptions);
+extern SPIPlanPtr SPI_prepare_extended(const char *src,
+                                      const SPIPrepareOptions *options);
 extern SPIPlanPtr SPI_prepare_params(const char *src,
                                     ParserSetupHook parserSetup,
                                     void *parserSetupArg,
index 29a77781a9d5715dee9ca804745cf866774e99a0..ce0f58ce687b075a6995d5a5fe53d23164503dee 100644 (file)
@@ -95,6 +95,7 @@ typedef struct _SPI_plan
    bool        no_snapshots;   /* let the caller handle the snapshots */
    List       *plancache_list; /* one CachedPlanSource per parsetree */
    MemoryContext plancxt;      /* Context containing _SPI_plan and data */
+   RawParseMode parse_mode;    /* raw_parser() mode */
    int         cursor_options; /* Cursor options used for planning */
    int         nargs;          /* number of plan arguments */
    Oid        *argtypes;       /* Argument types (NULL if nargs is 0) */
index 0973003044498287dd84bd9b6b31af3d31459269..80d90027cc47b6978fd2f7f619970ba1c77a7937 100644 (file)
 #include "nodes/parsenodes.h"
 
 
+/*
+ * RawParseMode determines the form of the string that raw_parser() accepts:
+ *
+ * RAW_PARSE_DEFAULT: parse a semicolon-separated list of SQL commands,
+ * and return a List of RawStmt nodes.
+ *
+ * RAW_PARSE_TYPE_NAME: parse a type name, and return a one-element List
+ * containing a TypeName node.
+ *
+ * ... more to come ...
+ */
+typedef enum
+{
+   RAW_PARSE_DEFAULT = 0,
+   RAW_PARSE_TYPE_NAME
+} RawParseMode;
+
+/* Values for the backslash_quote GUC */
 typedef enum
 {
    BACKSLASH_QUOTE_OFF,
@@ -32,7 +50,7 @@ extern PGDLLIMPORT bool standard_conforming_strings;
 
 
 /* Primary entry point for the raw parsing functions */
-extern List *raw_parser(const char *str);
+extern List *raw_parser(const char *str, RawParseMode mode);
 
 /* Utility functions exported by gram.y (perhaps these should be elsewhere) */
 extern List *SystemFuncName(char *name);
index f2731ea873a43f6a8ea29cceab879447146e8a07..7f9be85eb66edd19f6d4a5edb9c48c9ba85a66f7 100644 (file)
@@ -63,7 +63,7 @@ my %replace_types = (
    'opt_array_bounds' => '<index>',
 
    # "ignore" means: do not create type and rules for this non-term-id
-   'stmtblock'          => 'ignore',
+   'parse_toplevel'     => 'ignore',
    'stmtmulti'          => 'ignore',
    'CreateAsStmt'       => 'ignore',
    'DeallocateStmt'     => 'ignore',
index f966ddf0b5e37f20cd13ba9754ae890fb56c23b8..4a51fb6d9f101d2d12c8b046a8781bd811768faf 100644 (file)
@@ -4168,6 +4168,7 @@ exec_prepare_plan(PLpgSQL_execstate *estate,
                  bool keepplan)
 {
    SPIPlanPtr  plan;
+   SPIPrepareOptions options;
 
    /*
     * The grammar can't conveniently set expr->func while building the parse
@@ -4178,12 +4179,14 @@ exec_prepare_plan(PLpgSQL_execstate *estate,
    /*
     * Generate and save the plan
     */
-   plan = SPI_prepare_params(expr->query,
-                             (ParserSetupHook) plpgsql_parser_setup,
-                             (void *) expr,
-                             cursorOptions);
+   memset(&options, 0, sizeof(options));
+   options.parserSetup = (ParserSetupHook) plpgsql_parser_setup;
+   options.parserSetupArg = (void *) expr;
+   options.parseMode = RAW_PARSE_DEFAULT;
+   options.cursorOptions = cursorOptions;
+   plan = SPI_prepare_extended(expr->query, &options);
    if (plan == NULL)
-       elog(ERROR, "SPI_prepare_params failed for \"%s\": %s",
+       elog(ERROR, "SPI_prepare_extended failed for \"%s\": %s",
             expr->query, SPI_result_code_string(SPI_result));
    if (keepplan)
        SPI_keepplan(plan);
index a154b9841a65c3fb896e6ba817f84609011d69a6..c09576efff501058277b4338758e74978b9c7117 100644 (file)
@@ -3661,7 +3661,7 @@ check_sql_expr(const char *stmt, int location, int leaderlen)
    error_context_stack = &syntax_errcontext;
 
    oldCxt = MemoryContextSwitchTo(plpgsql_compile_tmp_cxt);
-   (void) raw_parser(stmt);
+   (void) raw_parser(stmt, RAW_PARSE_DEFAULT);
    MemoryContextSwitchTo(oldCxt);
 
    /* Restore former ereport callback */