v1-0001-add-raw-parse-modes.patch
text/x-diff
Filename: v1-0001-add-raw-parse-modes.patch
Type: text/x-diff
Part: 0
Patch
Format: unified
Series: patch v1-0001
| File | + | − |
|---|---|---|
| src/backend/commands/tablecmds.c | 1 | 1 |
| src/backend/parser/gram.y | 16 | 0 |
| src/backend/parser/parser.c | 21 | 6 |
| src/backend/parser/parse_type.c | 5 | 59 |
| src/backend/tcop/postgres.c | 1 | 1 |
| src/include/parser/parser.h | 19 | 1 |
| src/pl/plpgsql/src/pl_gram.y | 1 | 1 |
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 1fa9f19f08..9e7f1590b5 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -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)
{
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8f341ac006..88c76dd985 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -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,18 @@ 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.
*/
stmtblock: stmtmulti
{
pg_yyget_extra(yyscanner)->parsetree = $1;
}
+ | MODE_TYPE_NAME Typename
+ {
+ pg_yyget_extra(yyscanner)->parsetree = list_make1($2);
+ }
;
/*
diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c
index 2709f6f9c7..633774b733 100644
--- a/src/backend/parser/parse_type.c
+++ b/src/backend/parser/parse_type.c
@@ -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:
diff --git a/src/backend/parser/parser.c b/src/backend/parser/parser.c
index be86eb37fe..b04be3bca5 100644
--- a/src/backend/parser/parser.c
+++ b/src/backend/parser/parser.c
@@ -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
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 3679799e50..64c06f615b 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -634,7 +634,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");
diff --git a/src/include/parser/parser.h b/src/include/parser/parser.h
index 3bdeeb8b0b..ebbcb788cc 100644
--- a/src/include/parser/parser.h
+++ b/src/include/parser/parser.h
@@ -18,6 +18,24 @@
#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
{
BACKSLASH_QUOTE_OFF,
@@ -32,7 +50,7 @@ extern PGDLLIMPORT bool standard_conforming_strings;
/* 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) */
extern List *SystemFuncName(char *name);
diff --git a/src/pl/plpgsql/src/pl_gram.y b/src/pl/plpgsql/src/pl_gram.y
index 8227bf0449..26732d875e 100644
--- a/src/pl/plpgsql/src/pl_gram.y
+++ b/src/pl/plpgsql/src/pl_gram.y
@@ -3661,7 +3661,7 @@ check_sql_expr(const char *stmt, int location, int leaderlen)
error_context_stack = &syntax_errcontext;
oldCxt = MemoryContextSwitchTo(plpgsql_compile_tmp_cxt);
- (void) raw_parser(stmt);
+ (void) raw_parser(stmt, RAW_PARSE_DEFAULT);
MemoryContextSwitchTo(oldCxt);
/* Restore former ereport callback */