I wrote:
> However, we do have to have a benefit to show those people whose
> queries we break.  Hence my insistence on having a working AS fix
> (or some other benefit) before not after.

I experimented with this a bit more, and came up with the attached.
It's not a working patch, just a set of grammar changes that Bison
is happy with.  (Getting to a working patch would require fixing the
various build infrastructure that knows about the keyword classification,
which seems straightforward but tedious.)

As Robert theorized, it works to move a fairly-small number of unreserved
keywords into a new slightly-reserved category.  However, as the patch
stands, only the remaining fully-unreserved keywords can be used as bare
column labels.  I'd hoped to be able to also use col_name keywords in that
way (which'd make the set of legal bare column labels mostly the same as
ColId).  The col_name keywords that cause problems are, it appears,
only PRECISION, CHARACTER, and CHAR_P.  So in principle we could move
those three into yet another keyword category and then let the remaining
col_name keywords be included in BareColLabel.  I kind of think that
that's more complication than it's worth, though.

                        regards, tom lane

diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index a24b30f..0b034b6 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -542,13 +542,14 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 %type <str>		Sconst comment_text notify_payload
 %type <str>		RoleId opt_boolean_or_string
 %type <list>	var_list
-%type <str>		ColId ColLabel var_name type_function_name param_name
+%type <str>		ColId ColLabel BareColLabel
 %type <str>		NonReservedWord NonReservedWord_or_Sconst
+%type <str>		var_name type_function_name param_name
 %type <str>		createdb_opt_name
 %type <node>	var_value zone_value
 %type <rolespec> auth_ident RoleSpec opt_granted_by
 
-%type <keyword> unreserved_keyword type_func_name_keyword
+%type <keyword> unreserved_keyword non_label_keyword type_func_name_keyword
 %type <keyword> col_name_keyword reserved_keyword
 
 %type <node>	TableConstraint TableLikeClause
@@ -744,7 +745,6 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 %nonassoc	'<' '>' '=' LESS_EQUALS GREATER_EQUALS NOT_EQUALS
 %nonassoc	BETWEEN IN_P LIKE ILIKE SIMILAR NOT_LA
 %nonassoc	ESCAPE			/* ESCAPE must be just above LIKE/ILIKE/SIMILAR */
-%left		POSTFIXOP		/* dummy for postfix Op rules */
 /*
  * To support target_el without AS, we must give IDENT an explicit priority
  * between POSTFIXOP and Op.  We can safely assign the same priority to
@@ -3908,6 +3908,7 @@ PartitionSpec: PARTITION BY part_strategy '(' part_params ')'
 
 part_strategy:	IDENT					{ $$ = $1; }
 				| unreserved_keyword	{ $$ = pstrdup($1); }
+				| non_label_keyword		{ $$ = pstrdup($1); }
 		;
 
 part_params:	part_elem						{ $$ = list_make1($1); }
@@ -13230,8 +13231,6 @@ a_expr:		c_expr									{ $$ = $1; }
 				{ $$ = (Node *) makeA_Expr(AEXPR_OP, $2, $1, $3, @2); }
 			| qual_Op a_expr					%prec Op
 				{ $$ = (Node *) makeA_Expr(AEXPR_OP, $1, NULL, $2, @1); }
-			| a_expr qual_Op					%prec POSTFIXOP
-				{ $$ = (Node *) makeA_Expr(AEXPR_OP, $2, $1, NULL, @2); }
 
 			| a_expr AND a_expr
 				{ $$ = makeAndExpr($1, $3, @2); }
@@ -13645,8 +13644,6 @@ b_expr:		c_expr
 				{ $$ = (Node *) makeA_Expr(AEXPR_OP, $2, $1, $3, @2); }
 			| qual_Op b_expr					%prec Op
 				{ $$ = (Node *) makeA_Expr(AEXPR_OP, $1, NULL, $2, @1); }
-			| b_expr qual_Op					%prec POSTFIXOP
-				{ $$ = (Node *) makeA_Expr(AEXPR_OP, $2, $1, NULL, @2); }
 			| b_expr IS DISTINCT FROM b_expr		%prec IS
 				{
 					$$ = (Node *) makeSimpleA_Expr(AEXPR_DISTINCT, "=", $1, $5, @2);
@@ -14910,7 +14907,7 @@ target_el:	a_expr AS ColLabel
 			 * as an infix expression, which we accomplish by assigning
 			 * IDENT a precedence higher than POSTFIXOP.
 			 */
-			| a_expr IDENT
+			| a_expr BareColLabel
 				{
 					$$ = makeNode(ResTarget);
 					$$->name = $2;
@@ -15228,6 +15225,7 @@ role_list:	RoleSpec
  */
 ColId:		IDENT									{ $$ = $1; }
 			| unreserved_keyword					{ $$ = pstrdup($1); }
+			| non_label_keyword						{ $$ = pstrdup($1); }
 			| col_name_keyword						{ $$ = pstrdup($1); }
 		;
 
@@ -15235,6 +15233,7 @@ ColId:		IDENT									{ $$ = $1; }
  */
 type_function_name:	IDENT							{ $$ = $1; }
 			| unreserved_keyword					{ $$ = pstrdup($1); }
+			| non_label_keyword						{ $$ = pstrdup($1); }
 			| type_func_name_keyword				{ $$ = pstrdup($1); }
 		;
 
@@ -15242,15 +15241,23 @@ type_function_name:	IDENT							{ $$ = $1; }
  */
 NonReservedWord:	IDENT							{ $$ = $1; }
 			| unreserved_keyword					{ $$ = pstrdup($1); }
+			| non_label_keyword						{ $$ = pstrdup($1); }
 			| col_name_keyword						{ $$ = pstrdup($1); }
 			| type_func_name_keyword				{ $$ = pstrdup($1); }
 		;
 
+/* Bare column label --- names that can be column labels without writing "AS".
+ */
+BareColLabel:	IDENT								{ $$ = $1; }
+			| unreserved_keyword					{ $$ = pstrdup($1); }
+		;
+
 /* Column label --- allowed labels in "AS" clauses.
  * This presently includes *all* Postgres keywords.
  */
 ColLabel:	IDENT									{ $$ = $1; }
 			| unreserved_keyword					{ $$ = pstrdup($1); }
+			| non_label_keyword						{ $$ = pstrdup($1); }
 			| col_name_keyword						{ $$ = pstrdup($1); }
 			| type_func_name_keyword				{ $$ = pstrdup($1); }
 			| reserved_keyword						{ $$ = pstrdup($1); }
@@ -15326,7 +15333,6 @@ unreserved_keyword:
 			| CYCLE
 			| DATA_P
 			| DATABASE
-			| DAY_P
 			| DEALLOCATE
 			| DECLARE
 			| DEFAULTS
@@ -15360,7 +15366,6 @@ unreserved_keyword:
 			| EXTENSION
 			| EXTERNAL
 			| FAMILY
-			| FILTER
 			| FIRST_P
 			| FOLLOWING
 			| FORCE
@@ -15374,7 +15379,6 @@ unreserved_keyword:
 			| HANDLER
 			| HEADER_P
 			| HOLD
-			| HOUR_P
 			| IDENTITY_P
 			| IF_P
 			| IMMEDIATE
@@ -15414,10 +15418,8 @@ unreserved_keyword:
 			| MATERIALIZED
 			| MAXVALUE
 			| METHOD
-			| MINUTE_P
 			| MINVALUE
 			| MODE
-			| MONTH_P
 			| MOVE
 			| NAME_P
 			| NAMES
@@ -15443,7 +15445,6 @@ unreserved_keyword:
 			| OPTIONS
 			| ORDINALITY
 			| OTHERS
-			| OVER
 			| OVERRIDING
 			| OWNED
 			| OWNER
@@ -15499,7 +15500,6 @@ unreserved_keyword:
 			| SCHEMAS
 			| SCROLL
 			| SEARCH
-			| SECOND_P
 			| SECURITY
 			| SEQUENCE
 			| SEQUENCES
@@ -15557,23 +15557,36 @@ unreserved_keyword:
 			| VALIDATE
 			| VALIDATOR
 			| VALUE_P
-			| VARYING
 			| VERSION_P
 			| VIEW
 			| VIEWS
 			| VOLATILE
 			| WHITESPACE_P
-			| WITHIN
-			| WITHOUT
 			| WORK
 			| WRAPPER
 			| WRITE
 			| XML_P
-			| YEAR_P
 			| YES_P
 			| ZONE
 		;
 
+/* "Non label" keywords --- cannot be a bare column label in a SELECT list
+ * (you have to write AS in front).  Otherwise usable for anything.
+ */
+non_label_keyword:
+			DAY_P
+			| FILTER
+			| HOUR_P
+			| MINUTE_P
+			| MONTH_P
+			| OVER
+			| SECOND_P
+			| VARYING
+			| WITHIN
+			| WITHOUT
+			| YEAR_P
+		;
+
 /* Column identifier --- keywords that can be column, table, etc names.
  *
  * Many of these keywords will in fact be recognized as type or function

Reply via email to