]> granicus.if.org Git - postgresql/commitdiff
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 b5b5542d51aedf5f33db356afcef085b6919a420..e3b6be4d97b277f8009f7cb04512699d07fd430c 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/queries.sgml,v 1.44 2007/02/01 19:10:24 momjian Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/queries.sgml,v 1.45 2008/02/15 22:17:06 tgl Exp $ -->
 
 <chapter id="queries">
  <title>Queries</title>
@@ -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 6e3d9ff2c1fad9bb1790b01a504606dd86fb8dcc..89eaf264192b445ceb3b6cdbd0935072634a22d9 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/delete.sgml,v 1.32 2007/11/28 15:42:31 petere Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/delete.sgml,v 1.33 2008/02/15 22:17:06 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -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 5dc1f6d7863ccb2c5832592a68efd502ea313c03..52a4a0e93788527c0c8cff64b0af30dfedee153e 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/insert.sgml,v 1.35 2007/01/31 23:26:04 momjian Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/insert.sgml,v 1.36 2008/02/15 22:17:06 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -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 2624630699f48ca54b5fbb4b2e7c30615d249d05..000b5614dd2854431bb57e5af2312ae98b8dd491 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/select.sgml,v 1.102 2007/11/28 15:42:31 petere Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/select.sgml,v 1.103 2008/02/15 22:17:06 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -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 8780771201f06cf12c67cc2a487482aa005ad85b..915e859ea988ce87c2daee540594561362b33ed4 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/select_into.sgml,v 1.39 2007/01/09 02:14:10 tgl Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/select_into.sgml,v 1.40 2008/02/15 22:17:06 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -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 ce05150073a6ccb284bc3fdc273aca8f2ea6d386..2c8fed2a2f88a4afb916c705cb454c01b1056dea 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/update.sgml,v 1.45 2007/11/28 15:42:31 petere Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/update.sgml,v 1.46 2008/02/15 22:17:06 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -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 98ed6e1331148e0f435e158ac0b00b6bb4dc2c2a..6c63708b0e80e34ebc7a88cc089198fdc86d8fe1 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/sql.sgml,v 1.46 2007/02/16 03:50:29 momjian Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/sql.sgml,v 1.47 2008/02/15 22:17:06 tgl Exp $ -->
 
  <chapter id="sql-intro">
   <title>SQL</title>
@@ -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 2b992fab4a7d06e5f2d9ba659a8b29520ab970bd..4688bc797789ade8ec4b69b872feb5728912b0eb 100644 (file)
@@ -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);
index 6dd32b930b3e9f81039815b22025af21b312195e..43efc431dba1693cd5a4205c29389281228d1344 100644 (file)
@@ -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; }
                | '*'
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