1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-07 00:36:50 +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:
Tom Lane
2021-01-04 11:03:22 -05:00
parent b49154b3b7
commit 844fe9f159
14 changed files with 268 additions and 83 deletions

View 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)
{

View 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)

View 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);
}
;
/*

View 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;
}

View 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:

View 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

View 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");