From 6b0706ac33ab5da815980c642a9cde9a4cd86b6b Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Thu, 20 Mar 2008 21:42:48 +0000
Subject: [PATCH] Arrange for an explicit cast applied to an ARRAY[]
 constructor to be applied directly to all the member expressions, instead of
 the previous implementation where the ARRAY[] constructor would infer a
 common element type and then we'd coerce the finished array after the fact. 
 This has a number of benefits, one being that we can allow an empty ARRAY[]
 construct so long as its element type is specified by such a cast.

Brendan Jurd, minor fixes by me.
---
 doc/src/sgml/syntax.sgml             |  42 +++++-
 src/backend/nodes/copyfuncs.c        |  15 +-
 src/backend/nodes/equalfuncs.c       |  13 +-
 src/backend/nodes/outfuncs.c         |  13 +-
 src/backend/parser/gram.y            |  85 +++++------
 src/backend/parser/parse_expr.c      | 207 +++++++++++++++++++++------
 src/backend/parser/parse_target.c    |   4 +-
 src/include/nodes/nodes.h            |   3 +-
 src/include/nodes/parsenodes.h       |  17 ++-
 src/test/regress/expected/arrays.out |   9 ++
 src/test/regress/sql/arrays.sql      |   2 +
 11 files changed, 311 insertions(+), 99 deletions(-)

diff --git a/doc/src/sgml/syntax.sgml b/doc/src/sgml/syntax.sgml
index 8761b6e1da7..e677d80d5ce 100644
--- a/doc/src/sgml/syntax.sgml
+++ b/doc/src/sgml/syntax.sgml
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/syntax.sgml,v 1.121 2008/01/23 19:51:29 tgl Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/syntax.sgml,v 1.122 2008/03/20 21:42:47 tgl Exp $ -->
 
 <chapter id="sql-syntax">
  <title>SQL Syntax</title>
@@ -1305,7 +1305,7 @@ sqrt(2)
 
     where <replaceable>aggregate_name</replaceable> is a previously
     defined aggregate (possibly qualified with a schema name), and
-    <replaceable>expression</replaceable> is 
+    <replaceable>expression</replaceable> is
     any value expression that does not itself contain an aggregate
     expression.
    </para>
@@ -1335,7 +1335,7 @@ sqrt(2)
    <para>
     The predefined aggregate functions are described in <xref
     linkend="functions-aggregate">.  Other aggregate functions can be added
-    by the user. 
+    by the user.
    </para>
 
    <para>
@@ -1495,9 +1495,9 @@ SELECT name, (SELECT max(pop) FROM cities WHERE cities.state = states.name)
    <para>
     An array constructor is an expression that builds an
     array value from values for its member elements.  A simple array
-    constructor 
+    constructor
     consists of the key word <literal>ARRAY</literal>, a left square bracket
-    <literal>[</>, one or more expressions (separated by commas) for the
+    <literal>[</>, a list of expressions (separated by commas) for the
     array element values, and finally a right square bracket <literal>]</>.
     For example:
 <programlisting>
@@ -1507,9 +1507,22 @@ SELECT ARRAY[1,2,3+4];
  {1,2,7}
 (1 row)
 </programlisting>
-    The array element type is the common type of the member expressions,
+    By default,
+    the array element type is the common type of the member expressions,
     determined using the same rules as for <literal>UNION</> or
-    <literal>CASE</> constructs (see <xref linkend="typeconv-union-case">). 
+    <literal>CASE</> constructs (see <xref linkend="typeconv-union-case">).
+    You can override this by explicitly casting the array constructor to the
+    desired type, for example:
+<programlisting>
+SELECT ARRAY[1,2,22.7]::integer[];
+  array
+----------
+ {1,2,23}
+(1 row)
+</programlisting>
+    This has the same effect as casting each expression to the array
+    element type individually.
+    For more on casting, see <xref linkend="sql-syntax-type-casts">.
    </para>
 
    <para>
@@ -1534,6 +1547,8 @@ SELECT ARRAY[[1,2],[3,4]];
 
     Since multidimensional arrays must be rectangular, inner constructors
     at the same level must produce sub-arrays of identical dimensions.
+    Any cast applied to the outer <literal>ARRAY</> constructor propagates
+    automatically to all the inner constructors.
   </para>
 
   <para>
@@ -1553,6 +1568,19 @@ SELECT ARRAY[f1, f2, '{{9,10},{11,12}}'::int[]] FROM arr;
 </programlisting>
   </para>
 
+  <para>
+   You can construct an empty array, but since it's impossible to have an
+   array with no type, you must explicitly cast your empty array to the
+   desired type.  For example:
+<programlisting>
+SELECT ARRAY[]::integer[];
+ array
+-------
+ {}
+(1 row)
+</programlisting>
+  </para>
+
   <para>
    It is also possible to construct an array from the results of a
    subquery.  In this form, the array constructor is written with the
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index e9df49bab9c..d9432532666 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -15,7 +15,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.388 2008/02/07 20:19:47 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.389 2008/03/20 21:42:47 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1684,6 +1684,16 @@ _copyA_Indirection(A_Indirection *from)
 	return newnode;
 }
 
+static A_ArrayExpr *
+_copyA_ArrayExpr(A_ArrayExpr *from)
+{
+	A_ArrayExpr  *newnode = makeNode(A_ArrayExpr);
+
+	COPY_NODE_FIELD(elements);
+
+	return newnode;
+}
+
 static ResTarget *
 _copyResTarget(ResTarget *from)
 {
@@ -3543,6 +3553,9 @@ copyObject(void *from)
 		case T_A_Indirection:
 			retval = _copyA_Indirection(from);
 			break;
+		case T_A_ArrayExpr:
+			retval = _copyA_ArrayExpr(from);
+			break;
 		case T_ResTarget:
 			retval = _copyResTarget(from);
 			break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 57c51b2c73f..a92911dc27e 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -18,7 +18,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.318 2008/02/07 20:19:47 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.319 2008/03/20 21:42:48 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1729,6 +1729,14 @@ _equalA_Indirection(A_Indirection *a, A_Indirection *b)
 	return true;
 }
 
+static bool
+_equalA_ArrayExpr(A_ArrayExpr *a, A_ArrayExpr *b)
+{
+	COMPARE_NODE_FIELD(elements);
+
+	return true;
+}
+
 static bool
 _equalResTarget(ResTarget *a, ResTarget *b)
 {
@@ -2469,6 +2477,9 @@ equal(void *a, void *b)
 		case T_A_Indirection:
 			retval = _equalA_Indirection(a, b);
 			break;
+		case T_A_ArrayExpr:
+			retval = _equalA_ArrayExpr(a, b);
+			break;
 		case T_ResTarget:
 			retval = _equalResTarget(a, b);
 			break;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index c54cbc9d024..ceb0eb607b7 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.322 2008/01/09 08:46:44 neilc Exp $
+ *	  $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.323 2008/03/20 21:42:48 tgl Exp $
  *
  * NOTES
  *	  Every node type that can appear in stored rules' parsetrees *must*
@@ -1971,6 +1971,14 @@ _outA_Indirection(StringInfo str, A_Indirection *node)
 	WRITE_NODE_FIELD(indirection);
 }
 
+static void
+_outA_ArrayExpr(StringInfo str, A_ArrayExpr *node)
+{
+	WRITE_NODE_TYPE("A_ARRAYEXPR");
+
+	WRITE_NODE_FIELD(elements);
+}
+
 static void
 _outResTarget(StringInfo str, ResTarget *node)
 {
@@ -2417,6 +2425,9 @@ _outNode(StringInfo str, void *obj)
 			case T_A_Indirection:
 				_outA_Indirection(str, obj);
 				break;
+			case T_A_ArrayExpr:
+				_outA_ArrayExpr(str, obj);
+				break;
 			case T_ResTarget:
 				_outResTarget(str, obj);
 				break;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 6d4df81a8a3..01cc50428ba 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.608 2008/03/19 18:38:30 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.609 2008/03/20 21:42:48 tgl Exp $
  *
  * HISTORY
  *	  AUTHOR			DATE			MAJOR EVENT
@@ -107,6 +107,7 @@ static void insertSelectOptions(SelectStmt *stmt,
 static Node *makeSetOp(SetOperation op, bool all, Node *larg, Node *rarg);
 static Node *doNegate(Node *n, int location);
 static void doNegateFloat(Value *v);
+static Node *makeAArrayExpr(List *elements);
 static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args);
 
 %}
@@ -8429,45 +8430,43 @@ expr_list:	a_expr
 				}
 		;
 
-extract_list:
-			extract_arg FROM a_expr
-				{
-					A_Const *n = makeNode(A_Const);
-					n->val.type = T_String;
-					n->val.val.str = $1;
-					$$ = list_make2((Node *) n, $3);
-				}
-			| /*EMPTY*/								{ $$ = NIL; }
-		;
-
 type_list:	Typename								{ $$ = list_make1($1); }
 			| type_list ',' Typename				{ $$ = lappend($1, $3); }
 		;
 
-array_expr_list: array_expr
-				{	$$ = list_make1($1);		}
-			| array_expr_list ',' array_expr
-				{	$$ = lappend($1, $3);	}
-		;
-
 array_expr: '[' expr_list ']'
 				{
-					ArrayExpr *n = makeNode(ArrayExpr);
-					n->elements = $2;
-					$$ = (Node *)n;
+					$$ = makeAArrayExpr($2);
 				}
 			| '[' array_expr_list ']'
 				{
-					ArrayExpr *n = makeNode(ArrayExpr);
-					n->elements = $2;
-					$$ = (Node *)n;
+					$$ = makeAArrayExpr($2);
+				}
+			| '[' ']'
+				{
+					$$ = makeAArrayExpr(NIL);
+				}
+		;
+
+array_expr_list: array_expr							{ $$ = list_make1($1); }
+			| array_expr_list ',' array_expr		{ $$ = lappend($1, $3); }
+		;
+
+
+extract_list:
+			extract_arg FROM a_expr
+				{
+					A_Const *n = makeNode(A_Const);
+					n->val.type = T_String;
+					n->val.val.str = $1;
+					$$ = list_make2((Node *) n, $3);
 				}
+			| /*EMPTY*/								{ $$ = NIL; }
 		;
 
 /* Allow delimited string SCONST in extract_arg as an SQL extension.
  * - thomas 2001-04-12
  */
-
 extract_arg:
 			IDENT									{ $$ = $1; }
 			| YEAR_P								{ $$ = "year"; }
@@ -9502,13 +9501,6 @@ makeColumnRef(char *relname, List *indirection, int location)
 static Node *
 makeTypeCast(Node *arg, TypeName *typename)
 {
-	/*
-	 * Simply generate a TypeCast node.
-	 *
-	 * Earlier we would determine whether an A_Const would
-	 * be acceptable, however Domains require coerce_type()
-	 * to process them -- applying constraints as required.
-	 */
 	TypeCast *n = makeNode(TypeCast);
 	n->arg = arg;
 	n->typename = typename;
@@ -9582,7 +9574,7 @@ makeBoolAConst(bool state)
 {
 	A_Const *n = makeNode(A_Const);
 	n->val.type = T_String;
-	n->val.val.str = (state? "t": "f");
+	n->val.val.str = (state ? "t" : "f");
 	n->typename = SystemTypeName("bool");
 	return n;
 }
@@ -9763,15 +9755,6 @@ SystemTypeName(char *name)
 											   makeString(name)));
 }
 
-/* parser_init()
- * Initialize to parse one query string
- */
-void
-parser_init(void)
-{
-	QueryIsRule = FALSE;
-}
-
 /* doNegate()
  * Handle negation of a numeric constant.
  *
@@ -9827,6 +9810,15 @@ doNegateFloat(Value *v)
 	}
 }
 
+static Node *
+makeAArrayExpr(List *elements)
+{
+	A_ArrayExpr *n = makeNode(A_ArrayExpr);
+
+	n->elements = elements;
+	return (Node *) n;
+}
+
 static Node *
 makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args)
 {
@@ -9844,6 +9836,15 @@ makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args)
 	return (Node *) x;
 }
 
+/* parser_init()
+ * Initialize to parse one query string
+ */
+void
+parser_init(void)
+{
+	QueryIsRule = FALSE;
+}
+
 /*
  * Must undefine base_yylex before including scan.c, since we want it
  * to create the function base_yylex not filtered_base_yylex.
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index d43d38c494d..29c058aa40b 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.226 2008/01/01 19:45:50 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.227 2008/03/20 21:42:48 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -52,7 +52,8 @@ static Node *transformAExprIn(ParseState *pstate, A_Expr *a);
 static Node *transformFuncCall(ParseState *pstate, FuncCall *fn);
 static Node *transformCaseExpr(ParseState *pstate, CaseExpr *c);
 static Node *transformSubLink(ParseState *pstate, SubLink *sublink);
-static Node *transformArrayExpr(ParseState *pstate, ArrayExpr *a);
+static Node *transformArrayExpr(ParseState *pstate, A_ArrayExpr *a,
+				   Oid array_type, Oid element_type, int32 typmod);
 static Node *transformRowExpr(ParseState *pstate, RowExpr *r);
 static Node *transformCoalesceExpr(ParseState *pstate, CoalesceExpr *c);
 static Node *transformMinMaxExpr(ParseState *pstate, MinMaxExpr *m);
@@ -142,11 +143,49 @@ transformExpr(ParseState *pstate, Node *expr)
 				break;
 			}
 
+		case T_A_ArrayExpr:
+			result = transformArrayExpr(pstate, (A_ArrayExpr *) expr,
+										InvalidOid, InvalidOid, -1);
+			break;
+
 		case T_TypeCast:
 			{
 				TypeCast   *tc = (TypeCast *) expr;
-				Node	   *arg = transformExpr(pstate, tc->arg);
+				Node	   *arg;
+
+				/*
+				 * If the subject of the typecast is an ARRAY[] construct
+				 * and the target type is an array type, we invoke
+				 * transformArrayExpr() directly so that we can pass down
+				 * the type information.  This avoids some cases where
+				 * transformArrayExpr() might not infer the correct type.
+				 */
+				if (IsA(tc->arg, A_ArrayExpr))
+				{
+					Oid			targetType;
+					Oid			elementType;
+					int32		targetTypmod;
+
+					targetType = typenameTypeId(pstate, tc->typename,
+												&targetTypmod);
+					elementType = get_element_type(targetType);
+					if (OidIsValid(elementType))
+					{
+						result = transformArrayExpr(pstate,
+													(A_ArrayExpr *) tc->arg,
+													targetType,
+													elementType,
+													targetTypmod);
+						break;
+					}
 
+					/*
+					 * Corner case: ARRAY[] cast to a non-array type.
+					 * Fall through to do it the standard way.
+					 */
+				}
+
+				arg = transformExpr(pstate, tc->arg);
 				result = typecast_expression(pstate, arg, tc->typename);
 				break;
 			}
@@ -205,10 +244,6 @@ transformExpr(ParseState *pstate, Node *expr)
 			result = transformCaseExpr(pstate, (CaseExpr *) expr);
 			break;
 
-		case T_ArrayExpr:
-			result = transformArrayExpr(pstate, (ArrayExpr *) expr);
-			break;
-
 		case T_RowExpr:
 			result = transformRowExpr(pstate, (RowExpr *) expr);
 			break;
@@ -1255,64 +1290,156 @@ transformSubLink(ParseState *pstate, SubLink *sublink)
 	return result;
 }
 
+/*
+ * transformArrayExpr
+ *
+ * If the caller specifies the target type, the resulting array will
+ * be of exactly that type.  Otherwise we try to infer a common type
+ * for the elements using select_common_type().
+ */
 static Node *
-transformArrayExpr(ParseState *pstate, ArrayExpr *a)
+transformArrayExpr(ParseState *pstate, A_ArrayExpr *a,
+				   Oid array_type, Oid element_type, int32 typmod)
 {
 	ArrayExpr  *newa = makeNode(ArrayExpr);
 	List	   *newelems = NIL;
 	List	   *newcoercedelems = NIL;
 	List	   *typeids = NIL;
 	ListCell   *element;
-	Oid			array_type;
-	Oid			element_type;
+	Oid			coerce_type;
+	bool		coerce_hard;
 
-	/* Transform the element expressions */
+	/* 
+	 * Transform the element expressions 
+	 *
+	 * Assume that the array is one-dimensional unless we find an
+	 * array-type element expression.
+	 */ 
+	newa->multidims = false;
 	foreach(element, a->elements)
 	{
 		Node	   *e = (Node *) lfirst(element);
 		Node	   *newe;
+		Oid			newe_type;
+
+		/*
+		 * If an element is itself an A_ArrayExpr, recurse directly so that
+		 * we can pass down any target type we were given.
+		 */
+		if (IsA(e, A_ArrayExpr))
+		{
+			newe = transformArrayExpr(pstate,
+									  (A_ArrayExpr *) e,
+									  array_type,
+									  element_type,
+									  typmod);
+			newe_type = exprType(newe);
+			/* we certainly have an array here */
+			Assert(array_type == InvalidOid || array_type == newe_type);
+			newa->multidims = true;
+		}
+		else
+		{
+			newe = transformExpr(pstate, e);
+			newe_type = exprType(newe);
+			/*
+			 * Check for sub-array expressions, if we haven't already
+			 * found one.
+			 */
+			if (!newa->multidims && type_is_array(newe_type))
+				newa->multidims = true;
+		}
 
-		newe = transformExpr(pstate, e);
 		newelems = lappend(newelems, newe);
-		typeids = lappend_oid(typeids, exprType(newe));
+		typeids = lappend_oid(typeids, newe_type);
 	}
 
-	/* Select a common type for the elements */
-	element_type = select_common_type(typeids, "ARRAY");
+	/* 
+	 * Select a target type for the elements.
+	 *
+	 * If we haven't been given a target array type, we must try to deduce a
+	 * common type based on the types of the individual elements present.
+	 */
+	if (OidIsValid(array_type))
+	{
+		/* Caller must ensure array_type matches element_type */
+		Assert(OidIsValid(element_type));
+		coerce_type = (newa->multidims ? array_type : element_type);
+		coerce_hard = true;
+	}
+	else
+	{
+		/* Can't handle an empty array without a target type */
+		if (typeids == NIL)
+			ereport(ERROR,
+					(errcode(ERRCODE_INDETERMINATE_DATATYPE),
+					 errmsg("cannot determine type of empty array"),
+					 errhint("Explicitly cast to the desired type, "
+							 "for example ARRAY[]::integer[].")));
+
+		/* Select a common type for the elements */
+		coerce_type = select_common_type(typeids, "ARRAY");
+
+		if (newa->multidims)
+		{
+			array_type = coerce_type;
+			element_type = get_element_type(array_type);
+			if (!OidIsValid(element_type))
+				ereport(ERROR,
+						(errcode(ERRCODE_UNDEFINED_OBJECT),
+						 errmsg("could not find element type for data type %s",
+								format_type_be(array_type))));
+		}
+		else
+		{
+			element_type = coerce_type;
+			array_type = get_array_type(element_type);
+			if (!OidIsValid(array_type))
+				ereport(ERROR,
+						(errcode(ERRCODE_UNDEFINED_OBJECT),
+						 errmsg("could not find array type for data type %s",
+								format_type_be(element_type))));
+		}
+		coerce_hard = false;
+	}
 
-	/* Coerce arguments to common type if necessary */
+	/*
+	 * Coerce elements to target type 
+	 *
+	 * If the array has been explicitly cast, then the elements are in turn
+	 * explicitly coerced.
+	 *
+	 * If the array's type was merely derived from the common type of its
+	 * elements, then the elements are implicitly coerced to the common type.
+	 * This is consistent with other uses of select_common_type().
+	 */ 
 	foreach(element, newelems)
 	{
 		Node	   *e = (Node *) lfirst(element);
 		Node	   *newe;
 
-		newe = coerce_to_common_type(pstate, e,
-									 element_type,
-									 "ARRAY");
+		if (coerce_hard)
+		{
+			newe = coerce_to_target_type(pstate, e, 
+										 exprType(e),
+										 coerce_type, 
+										 typmod,
+										 COERCION_EXPLICIT,
+										 COERCE_EXPLICIT_CAST);
+			if (newe == NULL)
+				ereport(ERROR,
+						(errcode(ERRCODE_CANNOT_COERCE),
+						 errmsg("cannot cast type %s to %s",
+								format_type_be(exprType(e)),
+								format_type_be(coerce_type))));
+		}
+		else
+			newe = coerce_to_common_type(pstate, e,
+										 coerce_type,
+										 "ARRAY");
 		newcoercedelems = lappend(newcoercedelems, newe);
 	}
 
-	/* Do we have an array type to use? */
-	array_type = get_array_type(element_type);
-	if (array_type != InvalidOid)
-	{
-		/* Elements are presumably of scalar type */
-		newa->multidims = false;
-	}
-	else
-	{
-		/* Must be nested array expressions */
-		newa->multidims = true;
-
-		array_type = element_type;
-		element_type = get_element_type(array_type);
-		if (!OidIsValid(element_type))
-			ereport(ERROR,
-					(errcode(ERRCODE_UNDEFINED_OBJECT),
-					 errmsg("could not find array type for data type %s",
-							format_type_be(array_type))));
-	}
-
 	newa->array_typeid = array_type;
 	newa->element_typeid = element_type;
 	newa->elements = newcoercedelems;
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index bd3153548a5..e1d69fec421 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.158 2008/01/01 19:45:51 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.159 2008/03/20 21:42:48 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1294,7 +1294,7 @@ FigureColnameInternal(Node *node, char **name)
 				return 1;
 			}
 			break;
-		case T_ArrayExpr:
+		case T_A_ArrayExpr:
 			/* make ARRAY[] act like a function */
 			*name = "array";
 			return 2;
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 5a6745a2141..79d679b5be4 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.205 2008/01/01 19:45:58 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.206 2008/03/20 21:42:48 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -324,6 +324,7 @@ typedef enum NodeTag
 	T_FuncCall,
 	T_A_Indices,
 	T_A_Indirection,
+	T_A_ArrayExpr,
 	T_ResTarget,
 	T_TypeCast,
 	T_SortBy,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index c973eea729d..ff014383313 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.359 2008/02/07 17:09:51 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.360 2008/03/20 21:42:48 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -242,9 +242,9 @@ typedef struct A_Const
  * TypeCast - a CAST expression
  *
  * NOTE: for mostly historical reasons, A_Const parsenodes contain
- * room for a TypeName; we only generate a separate TypeCast node if the
- * argument to be casted is not a constant.  In theory either representation
- * would work, but the combined representation saves a bit of code in many
+ * room for a TypeName, allowing a constant to be marked as being of a given
+ * type without a separate TypeCast node.  Either representation will work,
+ * but the combined representation saves a bit of code in many
  * productions in gram.y.
  */
 typedef struct TypeCast
@@ -304,6 +304,15 @@ typedef struct A_Indirection
 	List	   *indirection;	/* subscripts and/or field names */
 } A_Indirection;
 
+/*
+ * A_ArrayExpr - an ARRAY[] construct
+ */
+typedef struct A_ArrayExpr
+{
+	NodeTag		type;
+	List	   *elements;		/* array element expressions */
+} A_ArrayExpr;
+
 /*
  * ResTarget -
  *	  result target (used in target list of pre-transformed parse trees)
diff --git a/src/test/regress/expected/arrays.out b/src/test/regress/expected/arrays.out
index 0f611bf7d78..c82cd3919b9 100644
--- a/src/test/regress/expected/arrays.out
+++ b/src/test/regress/expected/arrays.out
@@ -785,6 +785,9 @@ select '{}}'::text[];
 ERROR:  malformed array literal: "{}}"
 select '{ }}'::text[];
 ERROR:  malformed array literal: "{ }}"
+select array[];
+ERROR:  cannot determine type of empty array
+HINT:  Explicitly cast to the desired type, for example ARRAY[]::integer[].
 -- none of the above should be accepted
 -- all of the following should be accepted
 select '{}'::text[];
@@ -826,6 +829,12 @@ select '{
  {"@ 0","@ 1 hour 42 mins 20 secs"}
 (1 row)
 
+select array[]::text[];
+ array 
+-------
+ {}
+(1 row)
+
 -- all of the above should be accepted
 -- tests for array aggregates
 CREATE TEMP TABLE arraggtest ( f1 INT[], f2 TEXT[][], f3 FLOAT[]);
diff --git a/src/test/regress/sql/arrays.sql b/src/test/regress/sql/arrays.sql
index a60bf560fa8..192648a39b6 100644
--- a/src/test/regress/sql/arrays.sql
+++ b/src/test/regress/sql/arrays.sql
@@ -280,6 +280,7 @@ select E'{{1,2},\\{2,3}}'::text[];
 select '{{"1 2" x},{3}}'::text[];
 select '{}}'::text[];
 select '{ }}'::text[];
+select array[];
 -- none of the above should be accepted
 
 -- all of the following should be accepted
@@ -292,6 +293,7 @@ select '{
            0 second,
            @ 1 hour @ 42 minutes @ 20 seconds
          }'::interval[];
+select array[]::text[];
 -- all of the above should be accepted
 
 -- tests for array aggregates
-- 
GitLab