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
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>
<!-- *********************************************** -->
+<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>
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>
* 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)
{
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);
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;
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)
{
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;
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,
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;
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;
memset(&plan, 0, sizeof(_SPI_plan));
plan.magic = _SPI_PLAN_MAGIC;
+ plan.parse_mode = RAW_PARSE_DEFAULT;
plan.cursor_options = cursorOptions;
if (params)
{
* 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
/*
* 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
/*
* 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.
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)
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)
%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
*/
%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 */
/*
* 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);
+ }
;
/*
foreach(lc, exprs)
{
- Node *expr = (Node *) lfirst(lc);
+ Node *expr = (Node *) lfirst(lc);
/* Types must match */
if (exprType(expr) != common_type)
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;
}
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);
}
/*
TypeName *
typeStringToTypeName(const char *str)
{
- StringInfoData buf;
List *raw_parsetree_list;
- SelectStmt *stmt;
- ResTarget *restarget;
- Typ