mirror of
https://github.com/postgres/postgres.git
synced 2025-06-07 11:02:12 +03:00
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
This commit is contained in:
parent
b49154b3b7
commit
844fe9f159
@ -1105,6 +1105,11 @@ SPIPlanPtr SPI_prepare_cursor(const char * <parameter>command</parameter>, int <
|
|||||||
for the <structfield>options</structfield> field of <structname>DeclareCursorStmt</structname>.
|
for the <structfield>options</structfield> field of <structname>DeclareCursorStmt</structname>.
|
||||||
<function>SPI_prepare</function> always takes the cursor options as zero.
|
<function>SPI_prepare</function> always takes the cursor options as zero.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
This function is now deprecated in favor
|
||||||
|
of <function>SPI_prepare_extended</function>.
|
||||||
|
</para>
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
|
||||||
<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">
|
<refentry id="spi-spi-prepare-params">
|
||||||
<indexterm><primary>SPI_prepare_params</primary></indexterm>
|
<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
|
with the addition that the caller can specify parser hook functions
|
||||||
to control the parsing of external parameter references.
|
to control the parsing of external parameter references.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
This function is now deprecated in favor
|
||||||
|
of <function>SPI_prepare_extended</function>.
|
||||||
|
</para>
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
|
||||||
<refsect1>
|
<refsect1>
|
||||||
|
@ -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
|
* parse_analyze() or the rewriter, but instead we need to pass them
|
||||||
* through parse_utilcmd.c to make them ready for execution.
|
* 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;
|
querytree_list = NIL;
|
||||||
foreach(list_item, raw_parsetree_list)
|
foreach(list_item, raw_parsetree_list)
|
||||||
{
|
{
|
||||||
|
@ -508,6 +508,7 @@ SPI_execute(const char *src, bool read_only, long tcount)
|
|||||||
|
|
||||||
memset(&plan, 0, sizeof(_SPI_plan));
|
memset(&plan, 0, sizeof(_SPI_plan));
|
||||||
plan.magic = _SPI_PLAN_MAGIC;
|
plan.magic = _SPI_PLAN_MAGIC;
|
||||||
|
plan.parse_mode = RAW_PARSE_DEFAULT;
|
||||||
plan.cursor_options = CURSOR_OPT_PARALLEL_OK;
|
plan.cursor_options = CURSOR_OPT_PARALLEL_OK;
|
||||||
|
|
||||||
_SPI_prepare_oneshot_plan(src, &plan);
|
_SPI_prepare_oneshot_plan(src, &plan);
|
||||||
@ -681,6 +682,7 @@ SPI_execute_with_args(const char *src,
|
|||||||
|
|
||||||
memset(&plan, 0, sizeof(_SPI_plan));
|
memset(&plan, 0, sizeof(_SPI_plan));
|
||||||
plan.magic = _SPI_PLAN_MAGIC;
|
plan.magic = _SPI_PLAN_MAGIC;
|
||||||
|
plan.parse_mode = RAW_PARSE_DEFAULT;
|
||||||
plan.cursor_options = CURSOR_OPT_PARALLEL_OK;
|
plan.cursor_options = CURSOR_OPT_PARALLEL_OK;
|
||||||
plan.nargs = nargs;
|
plan.nargs = nargs;
|
||||||
plan.argtypes = argtypes;
|
plan.argtypes = argtypes;
|
||||||
@ -726,6 +728,7 @@ SPI_execute_with_receiver(const char *src,
|
|||||||
|
|
||||||
memset(&plan, 0, sizeof(_SPI_plan));
|
memset(&plan, 0, sizeof(_SPI_plan));
|
||||||
plan.magic = _SPI_PLAN_MAGIC;
|
plan.magic = _SPI_PLAN_MAGIC;
|
||||||
|
plan.parse_mode = RAW_PARSE_DEFAULT;
|
||||||
plan.cursor_options = CURSOR_OPT_PARALLEL_OK;
|
plan.cursor_options = CURSOR_OPT_PARALLEL_OK;
|
||||||
if (params)
|
if (params)
|
||||||
{
|
{
|
||||||
@ -768,6 +771,7 @@ SPI_prepare_cursor(const char *src, int nargs, Oid *argtypes,
|
|||||||
|
|
||||||
memset(&plan, 0, sizeof(_SPI_plan));
|
memset(&plan, 0, sizeof(_SPI_plan));
|
||||||
plan.magic = _SPI_PLAN_MAGIC;
|
plan.magic = _SPI_PLAN_MAGIC;
|
||||||
|
plan.parse_mode = RAW_PARSE_DEFAULT;
|
||||||
plan.cursor_options = cursorOptions;
|
plan.cursor_options = cursorOptions;
|
||||||
plan.nargs = nargs;
|
plan.nargs = nargs;
|
||||||
plan.argtypes = argtypes;
|
plan.argtypes = argtypes;
|
||||||
@ -784,6 +788,42 @@ SPI_prepare_cursor(const char *src, int nargs, Oid *argtypes,
|
|||||||
return result;
|
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
|
SPIPlanPtr
|
||||||
SPI_prepare_params(const char *src,
|
SPI_prepare_params(const char *src,
|
||||||
ParserSetupHook parserSetup,
|
ParserSetupHook parserSetup,
|
||||||
@ -805,6 +845,7 @@ SPI_prepare_params(const char *src,
|
|||||||
|
|
||||||
memset(&plan, 0, sizeof(_SPI_plan));
|
memset(&plan, 0, sizeof(_SPI_plan));
|
||||||
plan.magic = _SPI_PLAN_MAGIC;
|
plan.magic = _SPI_PLAN_MAGIC;
|
||||||
|
plan.parse_mode = RAW_PARSE_DEFAULT;
|
||||||
plan.cursor_options = cursorOptions;
|
plan.cursor_options = cursorOptions;
|
||||||
plan.nargs = 0;
|
plan.nargs = 0;
|
||||||
plan.argtypes = NULL;
|
plan.argtypes = NULL;
|
||||||
@ -1340,6 +1381,7 @@ SPI_cursor_open_with_args(const char *name,
|
|||||||
|
|
||||||
memset(&plan, 0, sizeof(_SPI_plan));
|
memset(&plan, 0, sizeof(_SPI_plan));
|
||||||
plan.magic = _SPI_PLAN_MAGIC;
|
plan.magic = _SPI_PLAN_MAGIC;
|
||||||
|
plan.parse_mode = RAW_PARSE_DEFAULT;
|
||||||
plan.cursor_options = cursorOptions;
|
plan.cursor_options = cursorOptions;
|
||||||
plan.nargs = nargs;
|
plan.nargs = nargs;
|
||||||
plan.argtypes = argtypes;
|
plan.argtypes = argtypes;
|
||||||
@ -1400,6 +1442,7 @@ SPI_cursor_parse_open_with_paramlist(const char *name,
|
|||||||
|
|
||||||
memset(&plan, 0, sizeof(_SPI_plan));
|
memset(&plan, 0, sizeof(_SPI_plan));
|
||||||
plan.magic = _SPI_PLAN_MAGIC;
|
plan.magic = _SPI_PLAN_MAGIC;
|
||||||
|
plan.parse_mode = RAW_PARSE_DEFAULT;
|
||||||
plan.cursor_options = cursorOptions;
|
plan.cursor_options = cursorOptions;
|
||||||
if (params)
|
if (params)
|
||||||
{
|
{
|
||||||
@ -2036,7 +2079,8 @@ spi_printtup(TupleTableSlot *slot, DestReceiver *self)
|
|||||||
* Parse and analyze a querystring.
|
* Parse and analyze a querystring.
|
||||||
*
|
*
|
||||||
* At entry, plan->argtypes and plan->nargs (or alternatively plan->parserSetup
|
* 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).
|
* Results are stored into *plan (specifically, plan->plancache_list).
|
||||||
* Note that the result data is all in CurrentMemoryContext or child contexts
|
* 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.
|
* 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
|
* 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.
|
* 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.
|
* 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 = (SPIPlanPtr) palloc0(sizeof(_SPI_plan));
|
||||||
newplan->magic = _SPI_PLAN_MAGIC;
|
newplan->magic = _SPI_PLAN_MAGIC;
|
||||||
newplan->plancxt = plancxt;
|
newplan->plancxt = plancxt;
|
||||||
|
newplan->parse_mode = plan->parse_mode;
|
||||||
newplan->cursor_options = plan->cursor_options;
|
newplan->cursor_options = plan->cursor_options;
|
||||||
newplan->nargs = plan->nargs;
|
newplan->nargs = plan->nargs;
|
||||||
if (plan->nargs > 0)
|
if (plan->nargs > 0)
|
||||||
@ -2930,6 +2975,7 @@ _SPI_save_plan(SPIPlanPtr plan)
|
|||||||
newplan = (SPIPlanPtr) palloc0(sizeof(_SPI_plan));
|
newplan = (SPIPlanPtr) palloc0(sizeof(_SPI_plan));
|
||||||
newplan->magic = _SPI_PLAN_MAGIC;
|
newplan->magic = _SPI_PLAN_MAGIC;
|
||||||
newplan->plancxt = plancxt;
|
newplan->plancxt = plancxt;
|
||||||
|
newplan->parse_mode = plan->parse_mode;
|
||||||
newplan->cursor_options = plan->cursor_options;
|
newplan->cursor_options = plan->cursor_options;
|
||||||
newplan->nargs = plan->nargs;
|
newplan->nargs = plan->nargs;
|
||||||
if (plan->nargs > 0)
|
if (plan->nargs > 0)
|
||||||
|
@ -384,7 +384,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
|
|||||||
%type <node> vacuum_relation
|
%type <node> vacuum_relation
|
||||||
%type <selectlimit> opt_select_limit select_limit limit_clause
|
%type <selectlimit> opt_select_limit select_limit limit_clause
|
||||||
|
|
||||||
%type <list> stmtblock stmtmulti
|
%type <list> parse_toplevel stmtmulti
|
||||||
OptTableElementList TableElementList OptInherit definition
|
OptTableElementList TableElementList OptInherit definition
|
||||||
OptTypedTableElementList TypedTableElementList
|
OptTypedTableElementList TypedTableElementList
|
||||||
reloptions opt_reloptions
|
reloptions opt_reloptions
|
||||||
@ -723,6 +723,15 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
|
|||||||
*/
|
*/
|
||||||
%token NOT_LA NULLS_LA WITH_LA
|
%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 */
|
/* Precedence: lowest to highest */
|
||||||
%nonassoc SET /* see relation_expr_opt_alias */
|
%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.
|
* 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;
|
pg_yyget_extra(yyscanner)->parsetree = $1;
|
||||||
}
|
}
|
||||||
|
| MODE_TYPE_NAME Typename
|
||||||
|
{
|
||||||
|
pg_yyget_extra(yyscanner)->parsetree = list_make1($2);
|
||||||
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -2380,7 +2380,8 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
|
|||||||
if (!OidIsValid(elem_typeid))
|
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;
|
elem_typeid = range_typelem;
|
||||||
}
|
}
|
||||||
|
@ -719,13 +719,6 @@ pts_error_callback(void *arg)
|
|||||||
const char *str = (const char *) arg;
|
const char *str = (const char *) arg;
|
||||||
|
|
||||||
errcontext("invalid type name \"%s\"", str);
|
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 *
|
TypeName *
|
||||||
typeStringToTypeName(const char *str)
|
typeStringToTypeName(const char *str)
|
||||||
{
|
{
|
||||||
StringInfoData buf;
|
|
||||||
List *raw_parsetree_list;
|
List *raw_parsetree_list;
|
||||||
SelectStmt *stmt;
|
|
||||||
ResTarget *restarget;
|
|
||||||
TypeCast *typecast;
|
|
||||||
TypeName *typeName;
|
TypeName *typeName;
|
||||||
ErrorContextCallback ptserrcontext;
|
ErrorContextCallback ptserrcontext;
|
||||||
|
|
||||||
@ -749,9 +738,6 @@ typeStringToTypeName(const char *str)
|
|||||||
if (strspn(str, " \t\n\r\f") == strlen(str))
|
if (strspn(str, " \t\n\r\f") == strlen(str))
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
initStringInfo(&buf);
|
|
||||||
appendStringInfo(&buf, "SELECT NULL::%s", str);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Setup error traceback support in case of ereport() during parse
|
* Setup error traceback support in case of ereport() during parse
|
||||||
*/
|
*/
|
||||||
@ -760,58 +746,18 @@ typeStringToTypeName(const char *str)
|
|||||||
ptserrcontext.previous = error_context_stack;
|
ptserrcontext.previous = error_context_stack;
|
||||||
error_context_stack = &ptserrcontext;
|
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;
|
error_context_stack = ptserrcontext.previous;
|
||||||
|
|
||||||
/*
|
/* We should get back exactly one TypeName node. */
|
||||||
* Make sure we got back exactly what we expected and no more; paranoia is
|
Assert(list_length(raw_parsetree_list) == 1);
|
||||||
* justified since the string might contain anything.
|
typeName = linitial_node(TypeName, raw_parsetree_list);
|
||||||
*/
|
|
||||||
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;
|
|
||||||
|
|
||||||
typeName = typecast->typeName;
|
/* The grammar allows SETOF in TypeName, but we don't want that here. */
|
||||||
if (typeName == NULL ||
|
|
||||||
!IsA(typeName, TypeName))
|
|
||||||
goto fail;
|
|
||||||
if (typeName->setof)
|
if (typeName->setof)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
pfree(buf.data);
|
|
||||||
|
|
||||||
return typeName;
|
return typeName;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
|
@ -35,11 +35,11 @@ static char *str_udeescape(const char *str, char escape,
|
|||||||
* raw_parser
|
* raw_parser
|
||||||
* Given a query in string form, do lexical and grammatical analysis.
|
* Given a query in string form, do lexical and grammatical analysis.
|
||||||
*
|
*
|
||||||
* Returns a list of raw (un-analyzed) parse trees. The immediate elements
|
* Returns a list of raw (un-analyzed) parse trees. The contents of the
|
||||||
* of the list are always RawStmt nodes.
|
* list have the form required by the specified RawParseMode.
|
||||||
*/
|
*/
|
||||||
List *
|
List *
|
||||||
raw_parser(const char *str)
|
raw_parser(const char *str, RawParseMode mode)
|
||||||
{
|
{
|
||||||
core_yyscan_t yyscanner;
|
core_yyscan_t yyscanner;
|
||||||
base_yy_extra_type yyextra;
|
base_yy_extra_type yyextra;
|
||||||
@ -49,8 +49,22 @@ raw_parser(const char *str)
|
|||||||
yyscanner = scanner_init(str, &yyextra.core_yy_extra,
|
yyscanner = scanner_init(str, &yyextra.core_yy_extra,
|
||||||
&ScanKeywords, ScanKeywordTokens);
|
&ScanKeywords, ScanKeywordTokens);
|
||||||
|
|
||||||
/* base_yylex() only needs this much initialization */
|
/* base_yylex() only needs us to initialize the lookahead token, if any */
|
||||||
|
if (mode == RAW_PARSE_DEFAULT)
|
||||||
yyextra.have_lookahead = false;
|
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 */
|
/* initialize the bison parser */
|
||||||
parser_init(&yyextra);
|
parser_init(&yyextra);
|
||||||
@ -104,6 +118,7 @@ base_yylex(YYSTYPE *lvalp, YYLTYPE *llocp, core_yyscan_t yyscanner)
|
|||||||
cur_token = yyextra->lookahead_token;
|
cur_token = yyextra->lookahead_token;
|
||||||
lvalp->core_yystype = yyextra->lookahead_yylval;
|
lvalp->core_yystype = yyextra->lookahead_yylval;
|
||||||
*llocp = yyextra->lookahead_yylloc;
|
*llocp = yyextra->lookahead_yylloc;
|
||||||
|
if (yyextra->lookahead_end)
|
||||||
*(yyextra->lookahead_end) = yyextra->lookahead_hold_char;
|
*(yyextra->lookahead_end) = yyextra->lookahead_hold_char;
|
||||||
yyextra->have_lookahead = false;
|
yyextra->have_lookahead = false;
|
||||||
}
|
}
|
||||||
|
@ -635,7 +635,7 @@ pg_parse_query(const char *query_string)
|
|||||||
if (log_parser_stats)
|
if (log_parser_stats)
|
||||||
ResetUsage();
|
ResetUsage();
|
||||||
|
|
||||||
raw_parsetree_list = raw_parser(query_string);
|
raw_parsetree_list = raw_parser(query_string, RAW_PARSE_DEFAULT);
|
||||||
|
|
||||||
if (log_parser_stats)
|
if (log_parser_stats)
|
||||||
ShowUsage("PARSER STATISTICS");
|
ShowUsage("PARSER STATISTICS");
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
|
|
||||||
#include "commands/trigger.h"
|
#include "commands/trigger.h"
|
||||||
#include "lib/ilist.h"
|
#include "lib/ilist.h"
|
||||||
#include "nodes/parsenodes.h"
|
#include "parser/parser.h"
|
||||||
#include "utils/portal.h"
|
#include "utils/portal.h"
|
||||||
|
|
||||||
|
|
||||||
@ -33,6 +33,15 @@ typedef struct SPITupleTable
|
|||||||
SubTransactionId subid; /* subxact in which tuptable was created */
|
SubTransactionId subid; /* subxact in which tuptable was created */
|
||||||
} SPITupleTable;
|
} 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 */
|
/* Plans are opaque structs for standard users of SPI */
|
||||||
typedef struct _SPI_plan *SPIPlanPtr;
|
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(const char *src, int nargs, Oid *argtypes);
|
||||||
extern SPIPlanPtr SPI_prepare_cursor(const char *src, int nargs, Oid *argtypes,
|
extern SPIPlanPtr SPI_prepare_cursor(const char *src, int nargs, Oid *argtypes,
|
||||||
int cursorOptions);
|
int cursorOptions);
|
||||||
|
extern SPIPlanPtr SPI_prepare_extended(const char *src,
|
||||||
|
const SPIPrepareOptions *options);
|
||||||
extern SPIPlanPtr SPI_prepare_params(const char *src,
|
extern SPIPlanPtr SPI_prepare_params(const char *src,
|
||||||
ParserSetupHook parserSetup,
|
ParserSetupHook parserSetup,
|
||||||
void *parserSetupArg,
|
void *parserSetupArg,
|
||||||
|
@ -95,6 +95,7 @@ typedef struct _SPI_plan
|
|||||||
bool no_snapshots; /* let the caller handle the snapshots */
|
bool no_snapshots; /* let the caller handle the snapshots */
|
||||||
List *plancache_list; /* one CachedPlanSource per parsetree */
|
List *plancache_list; /* one CachedPlanSource per parsetree */
|
||||||
MemoryContext plancxt; /* Context containing _SPI_plan and data */
|
MemoryContext plancxt; /* Context containing _SPI_plan and data */
|
||||||
|
RawParseMode parse_mode; /* raw_parser() mode */
|
||||||
int cursor_options; /* Cursor options used for planning */
|
int cursor_options; /* Cursor options used for planning */
|
||||||
int nargs; /* number of plan arguments */
|
int nargs; /* number of plan arguments */
|
||||||
Oid *argtypes; /* Argument types (NULL if nargs is 0) */
|
Oid *argtypes; /* Argument types (NULL if nargs is 0) */
|
||||||
|
@ -18,6 +18,24 @@
|
|||||||
#include "nodes/parsenodes.h"
|
#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
|
typedef enum
|
||||||
{
|
{
|
||||||
BACKSLASH_QUOTE_OFF,
|
BACKSLASH_QUOTE_OFF,
|
||||||
@ -32,7 +50,7 @@ extern PGDLLIMPORT bool standard_conforming_strings;
|
|||||||
|
|
||||||
|
|
||||||
/* Primary entry point for the raw parsing functions */
|
/* 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) */
|
/* Utility functions exported by gram.y (perhaps these should be elsewhere) */
|
||||||
extern List *SystemFuncName(char *name);
|
extern List *SystemFuncName(char *name);
|
||||||
|
@ -63,7 +63,7 @@ my %replace_types = (
|
|||||||
'opt_array_bounds' => '<index>',
|
'opt_array_bounds' => '<index>',
|
||||||
|
|
||||||
# "ignore" means: do not create type and rules for this non-term-id
|
# "ignore" means: do not create type and rules for this non-term-id
|
||||||
'stmtblock' => 'ignore',
|
'parse_toplevel' => 'ignore',
|
||||||
'stmtmulti' => 'ignore',
|
'stmtmulti' => 'ignore',
|
||||||
'CreateAsStmt' => 'ignore',
|
'CreateAsStmt' => 'ignore',
|
||||||
'DeallocateStmt' => 'ignore',
|
'DeallocateStmt' => 'ignore',
|
||||||
|
@ -4168,6 +4168,7 @@ exec_prepare_plan(PLpgSQL_execstate *estate,
|
|||||||
bool keepplan)
|
bool keepplan)
|
||||||
{
|
{
|
||||||
SPIPlanPtr plan;
|
SPIPlanPtr plan;
|
||||||
|
SPIPrepareOptions options;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The grammar can't conveniently set expr->func while building the parse
|
* 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
|
* Generate and save the plan
|
||||||
*/
|
*/
|
||||||
plan = SPI_prepare_params(expr->query,
|
memset(&options, 0, sizeof(options));
|
||||||
(ParserSetupHook) plpgsql_parser_setup,
|
options.parserSetup = (ParserSetupHook) plpgsql_parser_setup;
|
||||||
(void *) expr,
|
options.parserSetupArg = (void *) expr;
|
||||||
cursorOptions);
|
options.parseMode = RAW_PARSE_DEFAULT;
|
||||||
|
options.cursorOptions = cursorOptions;
|
||||||
|
plan = SPI_prepare_extended(expr->query, &options);
|
||||||
if (plan == NULL)
|
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));
|
expr->query, SPI_result_code_string(SPI_result));
|
||||||
if (keepplan)
|
if (keepplan)
|
||||||
SPI_keepplan(plan);
|
SPI_keepplan(plan);
|
||||||
|
@ -3661,7 +3661,7 @@ check_sql_expr(const char *stmt, int location, int leaderlen)
|
|||||||
error_context_stack = &syntax_errcontext;
|
error_context_stack = &syntax_errcontext;
|
||||||
|
|
||||||
oldCxt = MemoryContextSwitchTo(plpgsql_compile_tmp_cxt);
|
oldCxt = MemoryContextSwitchTo(plpgsql_compile_tmp_cxt);
|
||||||
(void) raw_parser(stmt);
|
(void) raw_parser(stmt, RAW_PARSE_DEFAULT);
|
||||||
MemoryContextSwitchTo(oldCxt);
|
MemoryContextSwitchTo(oldCxt);
|
||||||
|
|
||||||
/* Restore former ereport callback */
|
/* Restore former ereport callback */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user