diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 8c00b119ec..d631ac89a9 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -810,7 +810,6 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); /* Precedence: lowest to highest */ -%nonassoc SET /* see relation_expr_opt_alias */ %left UNION EXCEPT %left INTERSECT %left OR @@ -821,18 +820,23 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); %nonassoc BETWEEN IN_P LIKE ILIKE SIMILAR NOT_LA %nonassoc ESCAPE /* ESCAPE must be just above LIKE/ILIKE/SIMILAR */ -/* SQL/JSON related keywords */ -%nonassoc UNIQUE JSON -%nonassoc KEYS OBJECT_P SCALAR VALUE_P -%nonassoc WITH WITHOUT - /* - * To support target_el without AS, it used to be necessary to assign IDENT an - * explicit precedence just less than Op. While that's not really necessary - * since we removed postfix operators, it's still helpful to do so because - * there are some other unreserved keywords that need precedence assignments. - * If those keywords have the same precedence as IDENT then they clearly act - * the same as non-keywords, reducing the risk of unwanted precedence effects. + * Sometimes it is necessary to assign precedence to keywords that are not + * really part of the operator hierarchy, in order to resolve grammar + * ambiguities. It's best to avoid doing so whenever possible, because such + * assignments have global effect and may hide ambiguities besides the one + * you intended to solve. (Attaching a precedence to a single rule with + * %prec is far safer and should be preferred.) If you must give precedence + * to a new keyword, try very hard to give it the same precedence as IDENT. + * If the keyword has IDENT's precedence then it clearly acts the same as + * non-keywords and other similar keywords, thus reducing the risk of + * unexpected precedence effects. + * + * We used to need to assign IDENT an explicit precedence just less than Op, + * to support target_el without AS. While that's not really necessary since + * we removed postfix operators, we continue to do so because it provides a + * reference point for a precedence level that we can assign to other + * keywords that lack a natural precedence level. * * We need to do this for PARTITION, RANGE, ROWS, and GROUPS to support * opt_existing_window_name (see comment there). @@ -850,9 +854,18 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); * an explicit priority lower than '(', so that a rule with CUBE '(' will shift * rather than reducing a conflicting rule that takes CUBE as a function name. * Using the same precedence as IDENT seems right for the reasons given above. + * + * SET is likewise assigned the same precedence as IDENT, to support the + * relation_expr_opt_alias production (see comment there). + * + * KEYS, OBJECT_P, SCALAR, VALUE_P, WITH, and WITHOUT are similarly assigned + * the same precedence as IDENT. This allows resolving conflicts in the + * json_predicate_type_constraint and json_key_uniqueness_constraint_opt + * productions (see comments there). */ %nonassoc UNBOUNDED /* ideally would have same precedence as IDENT */ %nonassoc IDENT PARTITION RANGE ROWS GROUPS PRECEDING FOLLOWING CUBE ROLLUP + SET KEYS OBJECT_P SCALAR VALUE_P WITH WITHOUT %left Op OPERATOR /* multi-character ops and user-defined operators */ %left '+' '-' %left '*' '/' '%' @@ -16519,21 +16532,35 @@ json_returning_clause_opt: | /* EMPTY */ { $$ = NULL; } ; +/* + * We must assign the only-JSON production a precedence less than IDENT in + * order to favor shifting over reduction when JSON is followed by VALUE_P, + * OBJECT_P, or SCALAR. (ARRAY doesn't need that treatment, because it's a + * fully reserved word.) Because json_predicate_type_constraint is always + * followed by json_key_uniqueness_constraint_opt, we also need the only-JSON + * production to have precedence less than WITH and WITHOUT. UNBOUNDED isn't + * really related to this syntax, but it's a convenient choice because it + * already has a precedence less than IDENT for other reasons. + */ json_predicate_type_constraint: - JSON { $$ = JS_TYPE_ANY; } + JSON %prec UNBOUNDED { $$ = JS_TYPE_ANY; } | JSON VALUE_P { $$ = JS_TYPE_ANY; } | JSON ARRAY { $$ = JS_TYPE_ARRAY; } | JSON OBJECT_P { $$ = JS_TYPE_OBJECT; } | JSON SCALAR { $$ = JS_TYPE_SCALAR; } ; -/* KEYS is a noise word here */ +/* + * KEYS is a noise word here. To avoid shift/reduce conflicts, assign the + * KEYS-less productions a precedence less than IDENT (i.e., less than KEYS). + * This prevents reducing them when the next token is KEYS. + */ json_key_uniqueness_constraint_opt: WITH UNIQUE KEYS { $$ = true; } - | WITH UNIQUE { $$ = true; } + | WITH UNIQUE %prec UNBOUNDED { $$ = true; } | WITHOUT UNIQUE KEYS { $$ = false; } - | WITHOUT UNIQUE { $$ = false; } - | /* EMPTY */ %prec KEYS { $$ = false; } + | WITHOUT UNIQUE %prec UNBOUNDED { $$ = false; } + | /* EMPTY */ %prec UNBOUNDED { $$ = false; } ; json_name_and_value_list: