Allow AS to be omitted when specifying an output column name in SELECT
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 15 Feb 2008 22:17:06 +0000 (22:17 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 15 Feb 2008 22:17:06 +0000 (22:17 +0000)
(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.

doc/src/sgml/queries.sgml
doc/src/sgml/ref/delete.sgml
doc/src/sgml/ref/insert.sgml
doc/src/sgml/ref/select.sgml
doc/src/sgml/ref/select_into.sgml
doc/src/sgml/ref/update.sgml
doc/src/sgml/sql.sgml
src/backend/parser/gram.y
src/interfaces/ecpg/preproc/preproc.y
src/test/regress/expected/plpgsql.out

index 0f347be1eb74142bcc98dd4f78e25b22c2b9646f..0d237abc7e2ea2e087811bfa05400b1b9e95a290 100644 (file)
@@ -491,7 +491,7 @@ FROM <replaceable>table_reference</replaceable> AS <replaceable>alias</replaceab
 <synopsis>
 FROM <replaceable>table_reference</replaceable> <replaceable>alias</replaceable>
 </synopsis>
-     The <literal>AS</literal> key word is noise.
+     The <literal>AS</literal> key word is optional noise.
      <replaceable>alias</replaceable> can be any identifier.
     </para>
 
@@ -1040,13 +1040,32 @@ SELECT a AS value, b + c AS sum FROM ...
    </para>
 
    <para>
-    If no output column name is specified using <literal>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 <literal>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.
    </para>
 
+   <para>
+    The <literal>AS</> keyword is optional, but only if the new column
+    name does not match any
+    <productname>PostgreSQL</productname> keyword (see <xref
+    linkend="sql-keywords-appendix">).  To avoid an accidental match to
+    a keyword, you can double-quote the column name.  For example,
+    <literal>VALUE</> is a keyword, so this does not work:
+<programlisting>
+SELECT a value, b + c AS sum FROM ...
+</programlisting>
+    but this does:
+<programlisting>
+SELECT a "value", b + c AS sum FROM ...
+</programlisting>
+    For protection against possible
+    future keyword additions, it is recommended that you always either
+    write <literal>AS</literal> or double-quote the output column name.
+   </para>
+
    <note>
     <para>
      The naming of output columns here is different from that done in
index ccc959e7ceae1910d926df08a9c490f5d8128b97..f8388143bd466bfa26675fc3a77feddd2f6b6760 100644 (file)
@@ -23,7 +23,7 @@ PostgreSQL documentation
 DELETE FROM [ ONLY ] <replaceable class="PARAMETER">table</replaceable> [ [ AS ] <replaceable class="parameter">alias</replaceable> ]
     [ USING <replaceable class="PARAMETER">usinglist</replaceable> ]
     [ WHERE <replaceable class="PARAMETER">condition</replaceable> | WHERE CURRENT OF <replaceable class="PARAMETER">cursor_name</replaceable> ]
-    [ RETURNING * | <replaceable class="parameter">output_expression</replaceable> [ AS <replaceable class="parameter">output_name</replaceable> ] [, ...] ]
+    [ RETURNING * | <replaceable class="parameter">output_expression</replaceable> [ [ AS ] <replaceable class="parameter">output_name</replaceable> ] [, ...] ]
 </synopsis>
  </refsynopsisdiv>
 
index 154ea9872c32bb73a2d87bf3aa85b4b02c8bf1e1..aa3c26c01817ee1d2950daae2838cecdb4f2bcd9 100644 (file)
@@ -22,7 +22,7 @@ PostgreSQL documentation
 <synopsis>
 INSERT INTO <replaceable class="PARAMETER">table</replaceable> [ ( <replaceable class="PARAMETER">column</replaceable> [, ...] ) ]
     { DEFAULT VALUES | VALUES ( { <replaceable class="PARAMETER">expression</replaceable> | DEFAULT } [, ...] ) [, ...] | <replaceable class="PARAMETER">query</replaceable> }
-    [ RETURNING * | <replaceable class="parameter">output_expression</replaceable> [ AS <replaceable class="parameter">output_name</replaceable> ] [, ...] ]
+    [ RETURNING * | <replaceable class="parameter">output_expression</replaceable> [ [ AS ] <replaceable class="parameter">output_name</replaceable> ] [, ...] ]
 </synopsis>
  </refsynopsisdiv>
 
index 33fc71e8e7ddeb05a13ce2bd19fbbc45e96226d9..e463d15450364b610c335eb0d1297ad8f65f7065 100644 (file)
@@ -21,7 +21,7 @@ PostgreSQL documentation
  <refsynopsisdiv>
 <synopsis>
 SELECT [ ALL | DISTINCT [ ON ( <replaceable class="parameter">expression</replaceable> [, ...] ) ] ]
-    * | <replaceable class="parameter">expression</replaceable> [ AS <replaceable class="parameter">output_name</replaceable> ] [, ...]
+    * | <replaceable class="parameter">expression</replaceable> [ [ AS ] <replaceable class="parameter">output_name</replaceable> ] [, ...]
     [ FROM <replaceable class="parameter">from_item</replaceable> [, ...] ]
     [ WHERE <replaceable class="parameter">condition</replaceable> ]
     [ GROUP BY <replaceable class="parameter">expression</replaceable> [, ...] ]
@@ -477,23 +477,45 @@ HAVING <replaceable class="parameter">condition</replaceable>
     <literal>SELECT</> and <literal>FROM</>) specifies expressions
     that form the output rows of the <command>SELECT</command>
     statement.  The expressions can (and usually do) refer to columns
-    computed in the <literal>FROM</> clause.  Using the clause
-    <literal>AS <replaceable
-    class="parameter">output_name</replaceable></literal>, 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 <literal>ORDER BY</> and
-    <literal>GROUP BY</> clauses, but not in the <literal>WHERE</> or
-    <literal>HAVING</> clauses; there you must write out the
-    expression instead.
+    computed in the <literal>FROM</> clause.
+   </para>
+
+   <para>
+    Just as in a table, every output column of a <command>SELECT</command>
+    has a name.  In a simple <command>SELECT</command> this name is just
+    used to label the column for display, but when the <command>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
+    <literal>AS</> <replaceable class="parameter">output_name</replaceable>
+    after the column's expression.  (You can omit <literal>AS</literal>,
+    but only if the desired output name does not match any
+    <productname>PostgreSQL</productname> keyword (see <xref
+    linkend="sql-keywords-appendix">).  For protection against possible
+    future keyword additions, it is recommended that you always either
+    write <literal>AS</literal> or double-quote the output name.)
+    If you do not specify a column name, a name is chosen automatically
+    by <productname>PostgreSQL</productname>.  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
+    <literal>?column<replaceable>N</>?</literal> is usually chosen.
+   </para>
+
+   <para>
+    An output column's name can be used to refer to the column's value in
+    <literal>ORDER BY</> and <literal>GROUP BY</> clauses, but not in the
+    <literal>WHERE</> or <literal>HAVING</> clauses; there you must write
+    out the expression instead.
    </para>
 
    <para>
     Instead of an expression, <literal>*</literal> can be written in
     the output list as a shorthand for all the columns of the selected
-    rows.  Also, one can write <literal><replaceable
+    rows.  Also, you can write <literal><replaceable
     class="parameter">table_name</replaceable>.*</literal> 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 <literal>AS</>;
+    the output column names will be the same as the table columns' names.
    </para>
   </refsect2>
   
@@ -661,17 +683,17 @@ ORDER BY <replaceable class="parameter">expression</replaceable> [ ASC | DESC |
 
    <para>
     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
     <literal>AS</> clause.
    </para>
     
    <para>
     It is also possible to use arbitrary expressions in the
     <literal>ORDER BY</literal> clause, including columns that do not
-    appear in the <command>SELECT</command> result list.  Thus the
+    appear in the <command>SELECT</command> output list.  Thus the
     following statement is valid:
 <programlisting>
 SELECT name FROM distributors ORDER BY code;
@@ -684,8 +706,8 @@ SELECT name FROM distributors ORDER BY code;
 
    <para>
     If an <literal>ORDER BY</> expression is a simple name that
-    matches both a result column name and an input column name,
-    <literal>ORDER BY</> will interpret it as the result column name.
+    matches both an output column name and an input column name,
+    <literal>ORDER BY</> will interpret it as the output column name.
     This is the opposite of the choice that <literal>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';
   </refsect2>
 
   <refsect2>
-   <title>The <literal>AS</literal> Key Word</title>
+   <title>Omitting the <literal>AS</literal> Key Word</title>
+
+   <para>
+    In the SQL standard, the optional key word <literal>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).  <productname>PostgreSQL</productname> is slightly more
+    restrictive: <literal>AS</> is required if the new column name
+    matches any keyword at all, reserved or not.  Recommended practice is
+    to use <literal>AS</> or double-quote output column names, to prevent
+    any possible conflict against future keyword additions.
+   </para>
 
    <para>
-    In the SQL standard, the optional key word <literal>AS</> is just
-    noise and can be omitted without affecting the meaning.  The
-    <productname>PostgreSQL</productname> parser requires this key
-    word when renaming output columns because the type extensibility
-    features lead to parsing ambiguities without it.
-    <literal>AS</literal> is optional in <literal>FROM</literal>
-    items, however.
+    In <literal>FROM</literal> items, both the standard and
+    <productname>PostgreSQL</productname> allow <literal>AS</> to
+    be omitted before an alias that is an unreserved keyword.  But
+    this is impractical for output column names, because of syntactic
+    ambiguities.
    </para>
   </refsect2>
 
@@ -1153,7 +1184,7 @@ SELECT distributors.* WHERE distributors.name = 'Westward';
 
    <para>
     In the SQL-92 standard, an <literal>ORDER BY</literal> clause can
-    only use result column names or numbers, while a <literal>GROUP
+    only use output column names or numbers, while a <literal>GROUP
     BY</literal> clause can only use expressions based on input column
     names.  <productname>PostgreSQL</productname> 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';
     <productname>PostgreSQL</productname> 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.
    </para>
 
    <para>
index fee61e62d6e07f7eee270f158ce467dcebafdb65..2458d6fd3136a84a9c335d472167aff5f96df558 100644 (file)
@@ -21,7 +21,7 @@ PostgreSQL documentation
  <refsynopsisdiv>
 <synopsis>
 SELECT [ ALL | DISTINCT [ ON ( <replaceable class="PARAMETER">expression</replaceable> [, ...] ) ] ]
-    * | <replaceable class="PARAMETER">expression</replaceable> [ AS <replaceable class="PARAMETER">output_name</replaceable> ] [, ...]
+    * | <replaceable class="PARAMETER">expression</replaceable> [ [ AS ] <replaceable class="PARAMETER">output_name</replaceable> ] [, ...]
     INTO [ TEMPORARY | TEMP ] [ TABLE ] <replaceable class="PARAMETER">new_table</replaceable>
     [ FROM <replaceable class="PARAMETER">from_item</replaceable> [, ...] ]
     [ WHERE <replaceable class="PARAMETER">condition</replaceable> ]
index 9f8e4d1b4cf5c70a4495ba4c3571f9cc451962d5..ec39b0a259379767ef5d83a84e3f03d1831e855a 100644 (file)
@@ -25,7 +25,7 @@ UPDATE [ ONLY ] <replaceable class="PARAMETER">table</replaceable> [ [ AS ] <rep
           ( <replaceable class="PARAMETER">column</replaceable> [, ...] ) = ( { <replaceable class="PARAMETER">expression</replaceable> | DEFAULT } [, ...] ) } [, ...]
     [ FROM <replaceable class="PARAMETER">fromlist</replaceable> ]
     [ WHERE <replaceable class="PARAMETER">condition</replaceable> | WHERE CURRENT OF <replaceable class="PARAMETER">cursor_name</replaceable> ]
-    [ RETURNING * | <replaceable class="parameter">output_expression</replaceable> [ AS <replaceable class="parameter">output_name</replaceable> ] [, ...] ]
+    [ RETURNING * | <replaceable class="parameter">output_expression</replaceable> [ [ AS ] <replaceable class="parameter">output_name</replaceable> ] [, ...] ]
 </synopsis>
  </refsynopsisdiv>
 
index d76f959c8147f111ef95033cb0de36ecb089fe9f..c6b56d05f9cf2f4705f109cddc4fce6e81232900 100644 (file)
@@ -853,7 +853,7 @@ A &lt; B + 3.
 
      <synopsis>
 SELECT [ ALL | DISTINCT [ ON ( <replaceable class="PARAMETER">expression</replaceable> [, ...] ) ] ]
-    * | <replaceable class="PARAMETER">expression</replaceable> [ AS <replaceable class="PARAMETER">output_name</replaceable> ] [, ...]
+    * | <replaceable class="PARAMETER">expression</replaceable> [ [ AS ] <replaceable class="PARAMETER">output_name</replaceable> ] [, ...]
     [ INTO [ TEMPORARY | TEMP ] [ TABLE ] <replaceable class="PARAMETER">new_table</replaceable> ]
     [ FROM <replaceable class="PARAMETER">from_item</replaceable> [, ...] ]
     [ WHERE <replaceable class="PARAMETER">condition</replaceable> ]
index 3dc2c36e211f36b9c0b509561906ec8afd275dcf..0205ef5f32f789d4581cf9d8f395cf0a3b625cd7 100644 (file)
@@ -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);
index d9f5999c99ccb6267a89fd89cdb7cacf27be0eb1..79dd5d6cb6c595cb2f352db779169f64972ac38d 100644 (file)
@@ -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; }
                | '*'
index 78466426f1c00b6ea8c72ded1e896da231a80af8..03204b66e6bc11953bca2d720a2b1e3a9f3dac7c 100644 (file)
@@ -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