Arrange for an explicit cast applied to an ARRAY[] constructor to be applied
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 20 Mar 2008 21:42:48 +0000 (21:42 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Thu, 20 Mar 2008 21:42:48 +0000 (21:42 +0000)
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
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/nodes/outfuncs.c
src/backend/parser/gram.y
src/backend/parser/parse_expr.c
src/backend/parser/parse_target.c
src/include/nodes/nodes.h
src/include/nodes/parsenodes.h
src/test/regress/expected/arrays.out
src/test/regress/sql/arrays.sql

index 8761b6e1da71c987b93d618426ed7cba37a4696a..e677d80d5cec7c902d1f5647a87f340b20d5eef0 100644 (file)
@@ -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
index e9df49bab9cb7a65718cb297cc9bc8c726329866..d943253266613809b10c59f4331d59799c9db863 100644 (file)
@@ -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;
index 57c51b2c73f6e6edef063732b981034514e027c1..a92911dc27e9ec817cb0ba5b3343ad64a346b8df 100644 (file)
@@ -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;
index c54cbc9d024e29e71619a7148a92c2f5317dc54a..ceb0eb607b7cbaae9005f527ae88ad3479a77b30 100644 (file)
@@ -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;
index 6d4df81a8a3b40a5094091cc279c7e2cfe385e6d..01cc50428baf603f15c249bd0ebc516e1d001658 100644 (file)
@@ -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.
index d43d38c494d91d4daea78a6094c47b2048640a18..29c058aa40b3315ffd4261b2e61f843a8d7ff846 100644 (file)
@@ -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;
index bd3153548a504817edfbd0532050bc27059f77fe..e1d69fec42121ca92a6d3da021df0e578698daf9 100644 (file)
@@ -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;
index 5a6745a2141d911ff4b82a6ee51683a10d84637d..79d679b5be48fa0be6db85e4dcfa4bab13f61cb2 100644 (file)
@@ -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,
index c973eea729dbb2d3a5503e591feb245a78d3fde5..ff014383313cafa52d2b94eab8b60c016bfe6135 100644 (file)
@@ -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)
index 0f611bf7d7821488209fa2568e59eba7da674ab7..c82cd3919b949fbfa91ca1e312815b6525682872 100644 (file)
@@ -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[]);
index a60bf560fa88b9a13aaa1cb913e485faa301aadb..192648a39b6f3e52feb65da3a7b4963fd5387082 100644 (file)
@@ -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