mirror of
https://github.com/postgres/postgres.git
synced 2025-09-02 04:21:28 +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:
@@ -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);
|
||||
}
|
||||
;
|
||||
|
||||
/*
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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:
|
||||
|
@@ -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
|
||||
|
Reference in New Issue
Block a user