From: Tom Lane Date: Fri, 15 Feb 2008 22:17:06 +0000 (+0000) Subject: Allow AS to be omitted when specifying an output column name in SELECT X-Git-Tag: REL8_4_BETA1~1992 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=e67867b26cf052e166b39c6457ee04861cb81ead;p=postgresql Allow AS to be omitted when specifying an output column name in SELECT (or RETURNING), but only when the output name is not any SQL keyword. This seems as close as we can get to the standard's syntax without a great deal of thrashing. Original patch by Hiroshi Saito, amended by me. --- diff --git a/doc/src/sgml/queries.sgml b/doc/src/sgml/queries.sgml index b5b5542d51..e3b6be4d97 100644 --- a/doc/src/sgml/queries.sgml +++ b/doc/src/sgml/queries.sgml @@ -1,4 +1,4 @@ - + Queries @@ -491,7 +491,7 @@ FROM table_reference AS alias FROM table_reference alias - The AS key word is noise. + The AS key word is optional noise. alias can be any identifier. @@ -1040,13 +1040,32 @@ SELECT a AS value, b + c AS sum FROM ... - If no output column name is specified using AS, the system assigns a - default name. For simple column references, this is the name of the - referenced column. For function + If no output column name is specified using AS, + the system assigns a default column name. For simple column references, + this is the name of the referenced column. For function calls, this is the name of the function. For complex expressions, the system will generate a generic name. + + The AS keyword is optional, but only if the new column + name does not match any + PostgreSQL keyword (see ). To avoid an accidental match to + a keyword, you can double-quote the column name. For example, + VALUE is a keyword, so this does not work: + +SELECT a value, b + c AS sum FROM ... + + but this does: + +SELECT a "value", b + c AS sum FROM ... + + For protection against possible + future keyword additions, it is recommended that you always either + write AS or double-quote the output column name. + + The naming of output columns here is different from that done in diff --git a/doc/src/sgml/ref/delete.sgml b/doc/src/sgml/ref/delete.sgml index 6e3d9ff2c1..89eaf26419 100644 --- a/doc/src/sgml/ref/delete.sgml +++ b/doc/src/sgml/ref/delete.sgml @@ -1,5 +1,5 @@ @@ -23,7 +23,7 @@ PostgreSQL documentation DELETE FROM [ ONLY ] table [ [ AS ] alias ] [ USING usinglist ] [ WHERE condition | WHERE CURRENT OF cursor_name ] - [ RETURNING * | output_expression [ AS output_name ] [, ...] ] + [ RETURNING * | output_expression [ [ AS ] output_name ] [, ...] ] diff --git a/doc/src/sgml/ref/insert.sgml b/doc/src/sgml/ref/insert.sgml index 5dc1f6d786..52a4a0e937 100644 --- a/doc/src/sgml/ref/insert.sgml +++ b/doc/src/sgml/ref/insert.sgml @@ -1,5 +1,5 @@ @@ -22,7 +22,7 @@ PostgreSQL documentation INSERT INTO table [ ( column [, ...] ) ] { DEFAULT VALUES | VALUES ( { expression | DEFAULT } [, ...] ) [, ...] | query } - [ RETURNING * | output_expression [ AS output_name ] [, ...] ] + [ RETURNING * | output_expression [ [ AS ] output_name ] [, ...] ] diff --git a/doc/src/sgml/ref/select.sgml b/doc/src/sgml/ref/select.sgml index 2624630699..000b5614dd 100644 --- a/doc/src/sgml/ref/select.sgml +++ b/doc/src/sgml/ref/select.sgml @@ -1,5 +1,5 @@ @@ -21,7 +21,7 @@ PostgreSQL documentation SELECT [ ALL | DISTINCT [ ON ( expression [, ...] ) ] ] - * | expression [ AS output_name ] [, ...] + * | expression [ [ AS ] output_name ] [, ...] [ FROM from_item [, ...] ] [ WHERE condition ] [ GROUP BY expression [, ...] ] @@ -477,23 +477,45 @@ HAVING condition SELECT and FROM) specifies expressions that form the output rows of the SELECT statement. The expressions can (and usually do) refer to columns - computed in the FROM clause. Using the clause - AS output_name, another - name can be specified for an output column. This name is - primarily used to label the column for display. It can also be - used to refer to the column's value in ORDER BY and - GROUP BY clauses, but not in the WHERE or - HAVING clauses; there you must write out the - expression instead. + computed in the FROM clause. + + + + Just as in a table, every output column of a SELECT + has a name. In a simple SELECT this name is just + used to label the column for display, but when the SELECT + is a sub-query of a larger query, the name is seen by the larger query + as the column name of the virtual table produced by the sub-query. + To specify the name to use for an output column, write + AS output_name + after the column's expression. (You can omit AS, + but only if the desired output name does not match any + PostgreSQL keyword (see ). For protection against possible + future keyword additions, it is recommended that you always either + write AS or double-quote the output name.) + If you do not specify a column name, a name is chosen automatically + by PostgreSQL. If the column's expression + is a simple column reference then the chosen name is the same as that + column's name; in more complex cases a generated name looking like + ?columnN? is usually chosen. + + + + An output column's name can be used to refer to the column's value in + ORDER BY and GROUP BY clauses, but not in the + WHERE or HAVING clauses; there you must write + out the expression instead. Instead of an expression, * can be written in the output list as a shorthand for all the columns of the selected - rows. Also, one can write table_name.* as a - shorthand for the columns coming from just that table. + shorthand for the columns coming from just that table. In these + cases it is not possible to specify new names with AS; + the output column names will be the same as the table columns' names. @@ -661,17 +683,17 @@ ORDER BY expression [ ASC | DESC | The ordinal number refers to the ordinal (left-to-right) position - of the result column. This feature makes it possible to define an + of the output column. This feature makes it possible to define an ordering on the basis of a column that does not have a unique name. This is never absolutely necessary because it is always - possible to assign a name to a result column using the + possible to assign a name to an output column using the AS clause. It is also possible to use arbitrary expressions in the ORDER BY clause, including columns that do not - appear in the SELECT result list. Thus the + appear in the SELECT output list. Thus the following statement is valid: SELECT name FROM distributors ORDER BY code; @@ -684,8 +706,8 @@ SELECT name FROM distributors ORDER BY code; If an ORDER BY expression is a simple name that - matches both a result column name and an input column name, - ORDER BY will interpret it as the result column name. + matches both an output column name and an input column name, + ORDER BY will interpret it as the output column name. This is the opposite of the choice that GROUP BY will make in the same situation. This inconsistency is made to be compatible with the SQL standard. @@ -1135,16 +1157,25 @@ SELECT distributors.* WHERE distributors.name = 'Westward'; - The <literal>AS</literal> Key Word + Omitting the <literal>AS</literal> Key Word + + + In the SQL standard, the optional key word AS can be + omitted before an output column name whenever the new column name + is a valid column name (that is, not the same as any reserved + keyword). PostgreSQL is slightly more + restrictive: AS is required if the new column name + matches any keyword at all, reserved or not. Recommended practice is + to use AS or double-quote output column names, to prevent + any possible conflict against future keyword additions. + - In the SQL standard, the optional key word AS is just - noise and can be omitted without affecting the meaning. The - PostgreSQL parser requires this key - word when renaming output columns because the type extensibility - features lead to parsing ambiguities without it. - AS is optional in FROM - items, however. + In FROM items, both the standard and + PostgreSQL allow AS to + be omitted before an alias that is an unreserved keyword. But + this is impractical for output column names, because of syntactic + ambiguities. @@ -1153,7 +1184,7 @@ SELECT distributors.* WHERE distributors.name = 'Westward'; In the SQL-92 standard, an ORDER BY clause can - only use result column names or numbers, while a GROUP + only use output column names or numbers, while a GROUP BY clause can only use expressions based on input column names. PostgreSQL extends each of these clauses to allow the other choice as well (but it uses the @@ -1161,7 +1192,7 @@ SELECT distributors.* WHERE distributors.name = 'Westward'; PostgreSQL also allows both clauses to specify arbitrary expressions. Note that names appearing in an expression will always be taken as input-column names, not as - result-column names. + output-column names. diff --git a/doc/src/sgml/ref/select_into.sgml b/doc/src/sgml/ref/select_into.sgml index 8780771201..915e859ea9 100644 --- a/doc/src/sgml/ref/select_into.sgml +++ b/doc/src/sgml/ref/select_into.sgml @@ -1,5 +1,5 @@ @@ -21,7 +21,7 @@ PostgreSQL documentation SELECT [ ALL | DISTINCT [ ON ( expression [, ...] ) ] ] - * | expression [ AS output_name ] [, ...] + * | expression [ [ AS ] output_name ] [, ...] INTO [ TEMPORARY | TEMP ] [ TABLE ] new_table [ FROM from_item [, ...] ] [ WHERE condition ] diff --git a/doc/src/sgml/ref/update.sgml b/doc/src/sgml/ref/update.sgml index ce05150073..2c8fed2a2f 100644 --- a/doc/src/sgml/ref/update.sgml +++ b/doc/src/sgml/ref/update.sgml @@ -1,5 +1,5 @@ @@ -25,7 +25,7 @@ UPDATE [ ONLY ] table [ [ AS ] column [, ...] ) = ( { expression | DEFAULT } [, ...] ) } [, ...] [ FROM fromlist ] [ WHERE condition | WHERE CURRENT OF cursor_name ] - [ RETURNING * | output_expression [ AS output_name ] [, ...] ] + [ RETURNING * | output_expression [ [ AS ] output_name ] [, ...] ] diff --git a/doc/src/sgml/sql.sgml b/doc/src/sgml/sql.sgml index 98ed6e1331..6c63708b0e 100644 --- a/doc/src/sgml/sql.sgml +++ b/doc/src/sgml/sql.sgml @@ -1,4 +1,4 @@ - + SQL @@ -853,7 +853,7 @@ A < B + 3. SELECT [ ALL | DISTINCT [ ON ( expression [, ...] ) ] ] - * | expression [ AS output_name ] [, ...] + * | expression [ [ AS ] output_name ] [, ...] [ INTO [ TEMPORARY | TEMP ] [ TABLE ] new_table ] [ FROM from_item [, ...] ] [ WHERE condition ] diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 2b992fab4a..4688bc7977 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -11,7 +11,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.606 2008/02/07 21:07:55 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.607 2008/02/15 22:17:06 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -477,6 +477,7 @@ static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args) %nonassoc BETWEEN %nonassoc IN_P %left POSTFIXOP /* dummy for postfix Op rules */ +%nonassoc IDENT /* to support target_el without AS */ %left Op OPERATOR /* multi-character ops and user-defined operators */ %nonassoc NOTNULL %nonassoc ISNULL @@ -8705,7 +8706,6 @@ target_list: | target_list ',' target_el { $$ = lappend($1, $3); } ; -/* AS is not optional because shift/red conflict with unary ops */ target_el: a_expr AS ColLabel { $$ = makeNode(ResTarget); @@ -8714,6 +8714,22 @@ target_el: a_expr AS ColLabel $$->val = (Node *)$1; $$->location = @1; } + /* + * We support omitting AS only for column labels that aren't + * any known keyword. There is an ambiguity against postfix + * operators: is "a ! b" an infix expression, or a postfix + * expression and a column label? We prefer to resolve this + * as an infix expression, which we accomplish by assigning + * IDENT a precedence higher than POSTFIXOP. + */ + | a_expr IDENT + { + $$ = makeNode(ResTarget); + $$->name = $2; + $$->indirection = NIL; + $$->val = (Node *)$1; + $$->location = @1; + } | a_expr { $$ = makeNode(ResTarget); diff --git a/src/interfaces/ecpg/preproc/preproc.y b/src/interfaces/ecpg/preproc/preproc.y index 6dd32b930b..43efc431db 100644 --- a/src/interfaces/ecpg/preproc/preproc.y +++ b/src/interfaces/ecpg/preproc/preproc.y @@ -1,4 +1,4 @@ -/* $PostgreSQL: pgsql/src/interfaces/ecpg/preproc/preproc.y,v 1.360 2008/02/14 14:54:48 meskes Exp $ */ +/* $PostgreSQL: pgsql/src/interfaces/ecpg/preproc/preproc.y,v 1.361 2008/02/15 22:17:06 tgl Exp $ */ /* Copyright comment */ %{ @@ -521,8 +521,9 @@ add_typedef(char *name, char * dimension, char * length, enum ECPGttype type_enu %nonassoc OVERLAPS %nonassoc BETWEEN %nonassoc IN_P -%left POSTFIXOP /* dummy for postfix Op rules */ -%left Op OPERATOR /* multi-character ops and user-defined operators */ +%left POSTFIXOP /* dummy for postfix Op rules */ +%nonassoc IDENT /* to support target_el without AS */ +%left Op OPERATOR /* multi-character ops and user-defined operators */ %nonassoc NOTNULL %nonassoc ISNULL %nonassoc IS NULL_P TRUE_P FALSE_P UNKNOWN @@ -4695,9 +4696,10 @@ target_list: target_list ',' target_el { $$ = $1; } ; -/* AS is not optional because shift/red conflict with unary ops */ target_el: a_expr AS ColLabel { $$ = cat_str(3, $1, make_str("as"), $3); } + | a_expr IDENT + { $$ = cat_str(3, $1, make_str("as"), $2); } | a_expr { $$ = $1; } | '*' diff --git a/src/test/regress/expected/plpgsql.out b/src/test/regress/expected/plpgsql.out index 78466426f1..03204b66e6 100644 --- a/src/test/regress/expected/plpgsql.out +++ b/src/test/regress/expected/plpgsql.out @@ -2337,9 +2337,9 @@ begin end loop; return 5; end;$$ language plpgsql; -ERROR: syntax error at or near "fought" +ERROR: syntax error at or near "the" LINE 1: select I fought the law, the law won - ^ + ^ QUERY: select I fought the law, the law won CONTEXT: SQL statement in PL/PgSQL function "bad_sql2" near line 3 -- a RETURN expression is mandatory, except for void-returning