aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2021-01-04 11:03:22 -0500
committerTom Lane <tgl@sss.pgh.pa.us>2021-01-04 11:03:22 -0500
commit844fe9f159a948377907a63d0ef3fb16dc51ce50 (patch)
tree5f2ac3f159f7a4795a01330044fd76049ed5bff6
parentb49154b3b7a45523ce4081fdae8d65049342fcec (diff)
downloadpostgresql-844fe9f159a948377907a63d0ef3fb16dc51ce50.tar.gz
postgresql-844fe9f159a948377907a63d0ef3fb16dc51ce50.zip
Add the ability for the core grammar to have more than one parse target.
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
-rw-r--r--doc/src/sgml/spi.sgml126
-rw-r--r--src/backend/commands/tablecmds.c2
-rw-r--r--src/backend/executor/spi.c52
-rw-r--r--src/backend/parser/gram.y22
-rw-r--r--src/backend/parser/parse_coerce.c5
-rw-r--r--src/backend/parser/parse_type.c64
-rw-r--r--src/backend/parser/parser.c27
-rw-r--r--src/backend/tcop/postgres.c2
-rw-r--r--src/include/executor/spi.h13
-rw-r--r--src/include/executor/spi_priv.h1
-rw-r--r--src/include/parser/parser.h20
-rw-r--r--src/interfaces/ecpg/preproc/parse.pl2
-rw-r--r--src/pl/plpgsql/src/pl_exec.c13
-rw-r--r--src/pl/plpgsql/src/pl_gram.y2
14 files changed, 268 insertions, 83 deletions
diff --git a/doc/src/sgml/spi.sgml b/doc/src/sgml/spi.sgml
index 6e92e15ca3b..f5e0a35da06 100644
--- a/doc/src/sgml/spi.sgml
+++ b/doc/src/sgml/spi.sgml
@@ -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>
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 11dae782fd2..993da56d437 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -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)
{
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index 8368ead1ef6..6c0593686a9 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -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;
@@ -785,6 +789,42 @@ SPI_prepare_cursor(const char *src, int nargs, Oid *argtypes,
}
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,
void *parserSetupArg,
@@ -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)
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 18e181d5005..fb025f08a4e 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -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);
+ }
;
/*
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index 8d01fca6d21..74eb39c0e4d 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -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;
}
diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c
index 717125ad873..abe131ebebf 100644
--- a/src/backend/parser/parse_type.c
+++ b/src/backend/parser/parse_type.c
@@ -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:
diff --git a/src/backend/parser/parser.c b/src/backend/parser/parser.c
index b897a5160a2..8eb8feb372e 100644
--- a/src/backend/parser/parser.c
+++ b/src/backend/parser/parser.c
@@ -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
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index dfa0d685a83..f5c14249d13 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -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");
diff --git a/src/include/executor/spi.h b/src/include/executor/spi.h
index 6e603d007d7..9c70603434a 100644
--- a/src/include/executor/spi.h
+++ b/src/include/executor/spi.h
@@ -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,
diff --git a/src/include/executor/spi_priv.h b/src/include/executor/spi_priv.h
index 29a77781a9d..ce0f58ce687 100644
--- a/src/include/executor/spi_priv.h
+++ b/src/include/executor/spi_priv.h
@@ -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) */
diff --git a/src/include/parser/parser.h b/src/include/parser/parser.h
index 09730030444..80d90027cc4 100644
--- a/src/include/parser/parser.h
+++ b/src/include/parser/parser.h
@@ -18,6 +18,24 @@
#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);
diff --git a/src/interfaces/ecpg/preproc/parse.pl b/src/interfaces/ecpg/preproc/parse.pl
index f2731ea873a..7f9be85eb66 100644
--- a/src/interfaces/ecpg/preproc/parse.pl
+++ b/src/interfaces/ecpg/preproc/parse.pl
@@ -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',
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index f966ddf0b5e..4a51fb6d9f1 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -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);
diff --git a/src/pl/plpgsql/src/pl_gram.y b/src/pl/plpgsql/src/pl_gram.y
index a154b9841a6..c09576efff5 100644
--- a/src/pl/plpgsql/src/pl_gram.y
+++ b/src/pl/plpgsql/src/pl_gram.y
@@ -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 */