-<!-- $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>
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>
<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>
<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>
{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>
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>
</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
* 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 $
*
*-------------------------------------------------------------------------
*/
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)
{
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;
* 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 $
*
*-------------------------------------------------------------------------
*/
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)
{
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;
*
*
* 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*
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)
{
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;
*
*
* 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
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);
%}
}
;
-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"; }
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;
{
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;
}
makeString(name)));
}
-/* parser_init()
- * Initialize to parse one query string
- */
-void
-parser_init(void)
-{
- QueryIsRule = FALSE;
-}
-
/* doNegate()
* Handle negation of a numeric constant.
*
}
}
+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)
{
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.
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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);
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;
}
result = transformCaseExpr(pstate, (CaseExpr *) expr);
break;
- case T_ArrayExpr:
- result = transformArrayExpr(pstate, (ArrayExpr *) expr);
- break;
-
case T_RowExpr:
result = transformRowExpr(pstate, (RowExpr *) expr);
break;
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;
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
return 1;
}
break;
- case T_ArrayExpr:
+ case T_A_ArrayExpr:
/* make ARRAY[] act like a function */
*name = "array";
return 2;
* 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 $
*
*-------------------------------------------------------------------------
*/
T_FuncCall,
T_A_Indices,
T_A_Indirection,
+ T_A_ArrayExpr,
T_ResTarget,
T_TypeCast,
T_SortBy,
* 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 $
*
*-------------------------------------------------------------------------
*/
* 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
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)
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[];
{"@ 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[]);
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
0 second,
@ 1 hour @ 42 minutes @ 20 seconds
}'::interval[];
+select array[]::text[];
-- all of the above should be accepted
-- tests for array aggregates