Skip to content

Commit 844fe9f

Browse files
committed
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/[email protected]
1 parent b49154b commit 844fe9f

File tree

14 files changed

+268
-83
lines changed

14 files changed

+268
-83
lines changed

doc/src/sgml/spi.sgml

+126
Original file line numberDiff line numberDiff line change
@@ -1105,6 +1105,11 @@ SPIPlanPtr SPI_prepare_cursor(const char * <parameter>command</parameter>, int <
11051105
for the <structfield>options</structfield> field of <structname>DeclareCursorStmt</structname>.
11061106
<function>SPI_prepare</function> always takes the cursor options as zero.
11071107
</para>
1108+
1109+
<para>
1110+
This function is now deprecated in favor
1111+
of <function>SPI_prepare_extended</function>.
1112+
</para>
11081113
</refsect1>
11091114

11101115
<refsect1>
@@ -1176,6 +1181,122 @@ SPIPlanPtr SPI_prepare_cursor(const char * <parameter>command</parameter>, int <
11761181

11771182
<!-- *********************************************** -->
11781183

1184+
<refentry id="spi-spi-prepare-extended">
1185+
<indexterm><primary>SPI_prepare_extended</primary></indexterm>
1186+
1187+
<refmeta>
1188+
<refentrytitle>SPI_prepare_extended</refentrytitle>
1189+
<manvolnum>3</manvolnum>
1190+
</refmeta>
1191+
1192+
<refnamediv>
1193+
<refname>SPI_prepare_extended</refname>
1194+
<refpurpose>prepare a statement, without executing it yet</refpurpose>
1195+
</refnamediv>
1196+
1197+
<refsynopsisdiv>
1198+
<synopsis>
1199+
SPIPlanPtr SPI_prepare_extended(const char * <parameter>command</parameter>,
1200+
const SPIPrepareOptions * <parameter>options</parameter>)
1201+
</synopsis>
1202+
</refsynopsisdiv>
1203+
1204+
<refsect1>
1205+
<title>Description</title>
1206+
1207+
<para>
1208+
<function>SPI_prepare_extended</function> creates and returns a prepared
1209+
statement for the specified command, but doesn't execute the command.
1210+
This function is equivalent to <function>SPI_prepare</function>,
1211+
with the addition that the caller can specify options to control
1212+
the parsing of external parameter references, as well as other facets
1213+
of query parsing and planning.
1214+
</para>
1215+
</refsect1>
1216+
1217+
<refsect1>
1218+
<title>Arguments</title>
1219+
1220+
<variablelist>
1221+
<varlistentry>
1222+
<term><literal>const char * <parameter>command</parameter></literal></term>
1223+
<listitem>
1224+
<para>
1225+
command string
1226+
</para>
1227+
</listitem>
1228+
</varlistentry>
1229+
1230+
<varlistentry>
1231+
<term><literal>const SPIPrepareOptions * <parameter>options</parameter></literal></term>
1232+
<listitem>
1233+
<para>
1234+
struct containing optional arguments
1235+
</para>
1236+
</listitem>
1237+
</varlistentry>
1238+
</variablelist>
1239+
1240+
<para>
1241+
Callers should always zero out the entire <parameter>options</parameter>
1242+
struct, then fill whichever fields they want to set. This ensures forward
1243+
compatibility of code, since any fields that are added to the struct in
1244+
future will be defined to behave backwards-compatibly if they are zero.
1245+
The currently available <parameter>options</parameter> fields are:
1246+
</para>
1247+
1248+
<variablelist>
1249+
<varlistentry>
1250+
<term><literal>ParserSetupHook <parameter>parserSetup</parameter></literal></term>
1251+
<listitem>
1252+
<para>
1253+
Parser hook setup function
1254+
</para>
1255+
</listitem>
1256+
</varlistentry>
1257+
1258+
<varlistentry>
1259+
<term><literal>void * <parameter>parserSetupArg</parameter></literal></term>
1260+
<listitem>
1261+
<para>
1262+
pass-through argument for <parameter>parserSetup</parameter>
1263+
</para>
1264+
</listitem>
1265+
</varlistentry>
1266+
1267+
<varlistentry>
1268+
<term><literal>RawParseMode <parameter>parseMode</parameter></literal></term>
1269+
<listitem>
1270+
<para>
1271+
mode for raw parsing; <literal>RAW_PARSE_DEFAULT</literal> (zero)
1272+
produces default behavior
1273+
</para>
1274+
</listitem>
1275+
</varlistentry>
1276+
1277+
<varlistentry>
1278+
<term><literal>int <parameter>cursorOptions</parameter></literal></term>
1279+
<listitem>
1280+
<para>
1281+
integer bit mask of cursor options; zero produces default behavior
1282+
</para>
1283+
</listitem>
1284+
</varlistentry>
1285+
</variablelist>
1286+
</refsect1>
1287+
1288+
<refsect1>
1289+
<title>Return Value</title>
1290+
1291+
<para>
1292+
<function>SPI_prepare_extended</function> has the same return conventions as
1293+
<function>SPI_prepare</function>.
1294+
</para>
1295+
</refsect1>
1296+
</refentry>
1297+
1298+
<!-- *********************************************** -->
1299+
11791300
<refentry id="spi-spi-prepare-params">
11801301
<indexterm><primary>SPI_prepare_params</primary></indexterm>
11811302

@@ -1208,6 +1329,11 @@ SPIPlanPtr SPI_prepare_params(const char * <parameter>command</parameter>,
12081329
with the addition that the caller can specify parser hook functions
12091330
to control the parsing of external parameter references.
12101331
</para>
1332+
1333+
<para>
1334+
This function is now deprecated in favor
1335+
of <function>SPI_prepare_extended</function>.
1336+
</para>
12111337
</refsect1>
12121338

12131339
<refsect1>

src/backend/commands/tablecmds.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -12095,7 +12095,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
1209512095
* parse_analyze() or the rewriter, but instead we need to pass them
1209612096
* through parse_utilcmd.c to make them ready for execution.
1209712097
*/
12098-
raw_parsetree_list = raw_parser(cmd);
12098+
raw_parsetree_list = raw_parser(cmd, RAW_PARSE_DEFAULT);
1209912099
querytree_list = NIL;
1210012100
foreach(list_item, raw_parsetree_list)
1210112101
{

src/backend/executor/spi.c

+49-3
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,7 @@ SPI_execute(const char *src, bool read_only, long tcount)
508508

509509
memset(&plan, 0, sizeof(_SPI_plan));
510510
plan.magic = _SPI_PLAN_MAGIC;
511+
plan.parse_mode = RAW_PARSE_DEFAULT;
511512
plan.cursor_options = CURSOR_OPT_PARALLEL_OK;
512513

513514
_SPI_prepare_oneshot_plan(src, &plan);
@@ -681,6 +682,7 @@ SPI_execute_with_args(const char *src,
681682

682683
memset(&plan, 0, sizeof(_SPI_plan));
683684
plan.magic = _SPI_PLAN_MAGIC;
685+
plan.parse_mode = RAW_PARSE_DEFAULT;
684686
plan.cursor_options = CURSOR_OPT_PARALLEL_OK;
685687
plan.nargs = nargs;
686688
plan.argtypes = argtypes;
@@ -726,6 +728,7 @@ SPI_execute_with_receiver(const char *src,
726728

727729
memset(&plan, 0, sizeof(_SPI_plan));
728730
plan.magic = _SPI_PLAN_MAGIC;
731+
plan.parse_mode = RAW_PARSE_DEFAULT;
729732
plan.cursor_options = CURSOR_OPT_PARALLEL_OK;
730733
if (params)
731734
{
@@ -768,6 +771,7 @@ SPI_prepare_cursor(const char *src, int nargs, Oid *argtypes,
768771

769772
memset(&plan, 0, sizeof(_SPI_plan));
770773
plan.magic = _SPI_PLAN_MAGIC;
774+
plan.parse_mode = RAW_PARSE_DEFAULT;
771775
plan.cursor_options = cursorOptions;
772776
plan.nargs = nargs;
773777
plan.argtypes = argtypes;
@@ -784,6 +788,42 @@ SPI_prepare_cursor(const char *src, int nargs, Oid *argtypes,
784788
return result;
785789
}
786790

791+
SPIPlanPtr
792+
SPI_prepare_extended(const char *src,
793+
const SPIPrepareOptions *options)
794+
{
795+
_SPI_plan plan;
796+
SPIPlanPtr result;
797+
798+
if (src == NULL || options == NULL)
799+
{
800+
SPI_result = SPI_ERROR_ARGUMENT;
801+
return NULL;
802+
}
803+
804+
SPI_result = _SPI_begin_call(true);
805+
if (SPI_result < 0)
806+
return NULL;
807+
808+
memset(&plan, 0, sizeof(_SPI_plan));
809+
plan.magic = _SPI_PLAN_MAGIC;
810+
plan.parse_mode = options->parseMode;
811+
plan.cursor_options = options->cursorOptions;
812+
plan.nargs = 0;
813+
plan.argtypes = NULL;
814+
plan.parserSetup = options->parserSetup;
815+
plan.parserSetupArg = options->parserSetupArg;
816+
817+
_SPI_prepare_plan(src, &plan);
818+
819+
/* copy plan to procedure context */
820+
result = _SPI_make_plan_non_temp(&plan);
821+
822+
_SPI_end_call(true);
823+
824+
return result;
825+
}
826+
787827
SPIPlanPtr
788828
SPI_prepare_params(const char *src,
789829
ParserSetupHook parserSetup,
@@ -805,6 +845,7 @@ SPI_prepare_params(const char *src,
805845

806846
memset(&plan, 0, sizeof(_SPI_plan));
807847
plan.magic = _SPI_PLAN_MAGIC;
848+
plan.parse_mode = RAW_PARSE_DEFAULT;
808849
plan.cursor_options = cursorOptions;
809850
plan.nargs = 0;
810851
plan.argtypes = NULL;
@@ -1340,6 +1381,7 @@ SPI_cursor_open_with_args(const char *name,
13401381

13411382
memset(&plan, 0, sizeof(_SPI_plan));
13421383
plan.magic = _SPI_PLAN_MAGIC;
1384+
plan.parse_mode = RAW_PARSE_DEFAULT;
13431385
plan.cursor_options = cursorOptions;
13441386
plan.nargs = nargs;
13451387
plan.argtypes = argtypes;
@@ -1400,6 +1442,7 @@ SPI_cursor_parse_open_with_paramlist(const char *name,
14001442

14011443
memset(&plan, 0, sizeof(_SPI_plan));
14021444
plan.magic = _SPI_PLAN_MAGIC;
1445+
plan.parse_mode = RAW_PARSE_DEFAULT;
14031446
plan.cursor_options = cursorOptions;
14041447
if (params)
14051448
{
@@ -2036,7 +2079,8 @@ spi_printtup(TupleTableSlot *slot, DestReceiver *self)
20362079
* Parse and analyze a querystring.
20372080
*
20382081
* At entry, plan->argtypes and plan->nargs (or alternatively plan->parserSetup
2039-
* and plan->parserSetupArg) must be valid, as must plan->cursor_options.
2082+
* and plan->parserSetupArg) must be valid, as must plan->parse_mode and
2083+
* plan->cursor_options.
20402084
*
20412085
* Results are stored into *plan (specifically, plan->plancache_list).
20422086
* Note that the result data is all in CurrentMemoryContext or child contexts
@@ -2063,7 +2107,7 @@ _SPI_prepare_plan(const char *src, SPIPlanPtr plan)
20632107
/*
20642108
* Parse the request string into a list of raw parse trees.
20652109
*/
2066-
raw_parsetree_list = pg_parse_query(src);
2110+
raw_parsetree_list = raw_parser(src, plan->parse_mode);
20672111

20682112
/*
20692113
* 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)
21682212
/*
21692213
* Parse the request string into a list of raw parse trees.
21702214
*/
2171-
raw_parsetree_list = pg_parse_query(src);
2215+
raw_parsetree_list = raw_parser(src, plan->parse_mode);
21722216

21732217
/*
21742218
* Construct plancache entries, but don't do parse analysis yet.
@@ -2866,6 +2910,7 @@ _SPI_make_plan_non_temp(SPIPlanPtr plan)
28662910
newplan = (SPIPlanPtr) palloc0(sizeof(_SPI_plan));
28672911
newplan->magic = _SPI_PLAN_MAGIC;
28682912
newplan->plancxt = plancxt;
2913+
newplan->parse_mode = plan->parse_mode;
28692914
newplan->cursor_options = plan->cursor_options;
28702915
newplan->nargs = plan->nargs;
28712916
if (plan->nargs > 0)
@@ -2930,6 +2975,7 @@ _SPI_save_plan(SPIPlanPtr plan)
29302975
newplan = (SPIPlanPtr) palloc0(sizeof(_SPI_plan));
29312976
newplan->magic = _SPI_PLAN_MAGIC;
29322977
newplan->plancxt = plancxt;
2978+
newplan->parse_mode = plan->parse_mode;
29332979
newplan->cursor_options = plan->cursor_options;
29342980
newplan->nargs = plan->nargs;
29352981
if (plan->nargs > 0)

src/backend/parser/gram.y

+20-2
Original file line numberDiff line numberDiff line change
@@ -384,7 +384,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
384384
%type <node> vacuum_relation
385385
%type <selectlimit> opt_select_limit select_limit limit_clause
386386

387-
%type <list> stmtblock stmtmulti
387+
%type <list> parse_toplevel stmtmulti
388388
OptTableElementList TableElementList OptInherit definition
389389
OptTypedTableElementList TypedTableElementList
390390
reloptions opt_reloptions
@@ -723,6 +723,15 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
723723
*/
724724
%token NOT_LA NULLS_LA WITH_LA
725725

726+
/*
727+
* The grammar likewise thinks these tokens are keywords, but they are never
728+
* generated by the scanner. Rather, they can be injected by parser.c as
729+
* the initial token of the string (using the lookahead-token mechanism
730+
* implemented there). This provides a way to tell the grammar to parse
731+
* something other than the usual list of SQL commands.
732+
*/
733+
%token MODE_TYPE_NAME
734+
726735

727736
/* Precedence: lowest to highest */
728737
%nonassoc SET /* see relation_expr_opt_alias */
@@ -787,11 +796,20 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
787796

788797
/*
789798
* The target production for the whole parse.
799+
*
800+
* Ordinarily we parse a list of statements, but if we see one of the
801+
* special MODE_XXX symbols as first token, we parse something else.
802+
* The options here correspond to enum RawParseMode, which see for details.
790803
*/
791-
stmtblock: stmtmulti
804+
parse_toplevel:
805+
stmtmulti
792806
{
793807
pg_yyget_extra(yyscanner)->parsetree = $1;
794808
}
809+
| MODE_TYPE_NAME Typename
810+
{
811+
pg_yyget_extra(yyscanner)->parsetree = list_make1($2);
812+
}
795813
;
796814

797815
/*

src/backend/parser/parse_coerce.c

+3-2
Original file line numberDiff line numberDiff line change
@@ -1541,7 +1541,7 @@ select_common_typmod(ParseState *pstate, List *exprs, Oid common_type)
15411541

15421542
foreach(lc, exprs)
15431543
{
1544-
Node *expr = (Node *) lfirst(lc);
1544+
Node *expr = (Node *) lfirst(lc);
15451545

15461546
/* Types must match */
15471547
if (exprType(expr) != common_type)
@@ -2380,7 +2380,8 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
23802380
if (!OidIsValid(elem_typeid))
23812381
{
23822382
/*
2383-
* if we don't have an element type yet, use the one we just got
2383+
* if we don't have an element type yet, use the one we just
2384+
* got
23842385
*/
23852386
elem_typeid = range_typelem;
23862387
}

0 commit comments

Comments
 (0)