From e67867b26cf052e166b39c6457ee04861cb81ead Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Fri, 15 Feb 2008 22:17:06 +0000
Subject: [PATCH] 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.

---
 doc/src/sgml/queries.sgml             | 29 +++++++--
 doc/src/sgml/ref/delete.sgml          |  4 +-
 doc/src/sgml/ref/insert.sgml          |  4 +-
 doc/src/sgml/ref/select.sgml          | 87 ++++++++++++++++++---------
 doc/src/sgml/ref/select_into.sgml     |  4 +-
 doc/src/sgml/ref/update.sgml          |  4 +-
 doc/src/sgml/sql.sgml                 |  4 +-
 src/backend/parser/gram.y             | 20 +++++-
 src/interfaces/ecpg/preproc/preproc.y | 10 +--
 src/test/regress/expected/plpgsql.out |  4 +-
 10 files changed, 119 insertions(+), 51 deletions(-)

diff --git a/doc/src/sgml/queries.sgml b/doc/src/sgml/queries.sgml
index b5b5542d51a..e3b6be4d97b 100644
--- a/doc/src/sgml/queries.sgml
+++ b/doc/src/sgml/queries.sgml
@@ -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
diff --git a/doc/src/sgml/ref/delete.sgml b/doc/src/sgml/ref/delete.sgml
index 6e3d9ff2c1f..89eaf264192 100644
--- a/doc/src/sgml/ref/delete.sgml
+++ b/doc/src/sgml/ref/delete.sgml
@@ -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>
 
diff --git a/doc/src/sgml/ref/insert.sgml b/doc/src/sgml/ref/insert.sgml
index 5dc1f6d7863..52a4a0e9378 100644
--- a/doc/src/sgml/ref/insert.sgml
+++ b/doc/src/sgml/ref/insert.sgml
@@ -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>
 
diff --git a/doc/src/sgml/ref/select.sgml b/doc/src/sgml/ref/select.sgml
index 2624630699f..000b5614dd2 100644
--- a/doc/src/sgml/ref/select.sgml
+++ b/doc/src/sgml/ref/select.sgml
@@ -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>
diff --git a/doc/src/sgml/ref/select_into.sgml b/doc/src/sgml/ref/select_into.sgml
index 8780771201f..915e859ea98 100644
--- a/doc/src/sgml/ref/select_into.sgml
+++ b/doc/src/sgml/ref/select_into.sgml
@@ -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> ]
diff --git a/doc/src/sgml/ref/update.sgml b/doc/src/sgml/ref/update.sgml
index ce05150073a..2c8fed2a2f8 100644
--- a/doc/src/sgml/ref/update.sgml
+++ b/doc/src/sgml/ref/update.sgml
@@ -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>
 
diff --git a/doc/src/sgml/sql.sgml b/doc/src/sgml/sql.sgml
index 98ed6e13311..6c63708b0e8 100644
--- a/doc/src/sgml/sql.sgml
+++ b/doc/src/sgml/sql.sgml
@@ -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> ]
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 2b992fab4a7..4688bc79778 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 6dd32b930b3..43efc431dba 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 78466426f1c..03204b66e6b 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
-- 
GitLab