*** ./doc/src/sgml/syntax.sgml.orig Fri Nov 30 19:31:29 2007 --- ./doc/src/sgml/syntax.sgml Fri Nov 30 19:32:11 2007 *************** *** 1497,1503 **** array value from values for its member elements. A simple array constructor consists of the key word ARRAY, a left square bracket ! [, one or more expressions (separated by commas) for the array element values, and finally a right square bracket ]. For example: --- 1497,1503 ---- array value from values for its member elements. A simple array constructor consists of the key word ARRAY, a left square bracket ! [, a list of expressions (separated by commas) for the array element values, and finally a right square bracket ]. For example: *************** *** 1507,1515 **** {1,2,7} (1 row) ! The array element type is the common type of the member expressions, ! determined using the same rules as for UNION or ! CASE constructs (see ). --- 1507,1516 ---- {1,2,7} (1 row) ! If the array is not explictly cast to a particular type, the array element ! type is the common type of the member expressions, determined using the ! same rules as for UNION or CASE constructs (see ! ). *************** *** 1554,1559 **** --- 1555,1573 ---- + You can construct an empty array, but since it's impossible to have an array + with no type, you must explictly cast your empty array to the desired type. For example: + + SELECT ARRAY[]::int[]; + int4 + ------ + {} + (1 row) + + For more on casting, see . + + + It is also possible to construct an array from the results of a subquery. In this form, the array constructor is written with the key word ARRAY followed by a parenthesized (not *** ./src/backend/nodes/copyfuncs.c.orig Fri Nov 30 19:29:16 2007 --- ./src/backend/nodes/copyfuncs.c Fri Nov 30 19:32:11 2007 *************** *** 1704,1709 **** --- 1704,1719 ---- 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) { *************** *** 3538,3543 **** --- 3548,3556 ---- case T_A_ArrayExpr: retval = _copyA_ArrayExpr(from); break; + case T_A_ArrayExpr: + retval = _copyA_ArrayExpr(from); + break; case T_ResTarget: retval = _copyResTarget(from); break; *** ./src/backend/nodes/outfuncs.c.orig Fri Nov 30 19:29:16 2007 --- ./src/backend/nodes/outfuncs.c Fri Nov 30 19:32:11 2007 *************** *** 1978,1983 **** --- 1978,1991 ---- } 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) { WRITE_NODE_TYPE("RESTARGET"); *** ./src/backend/parser/gram.y.orig Fri Nov 30 19:31:29 2007 --- ./src/backend/parser/gram.y Fri Nov 30 19:32:11 2007 *************** *** 108,113 **** --- 108,114 ---- static Node *doNegate(Node *n, int location); static void doNegateFloat(Value *v); static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args); + static Node *makeAArrayExpr(List *elements); %} *************** *** 8439,8457 **** { $$ = list_make1($1); } | array_expr_list ',' array_expr { $$ = lappend($1, $3); } ; array_expr: '[' expr_list ']' { ! ArrayExpr *n = makeNode(ArrayExpr); ! n->elements = $2; ! $$ = (Node *)n; } | '[' array_expr_list ']' { ! ArrayExpr *n = makeNode(ArrayExpr); ! n->elements = $2; ! $$ = (Node *)n; } ; --- 8440,8456 ---- { $$ = list_make1($1); } | array_expr_list ',' array_expr { $$ = lappend($1, $3); } + | /* EMPTY */ + { $$ = NIL; } ; array_expr: '[' expr_list ']' { ! $$ = makeAArrayExpr($2); } | '[' array_expr_list ']' { ! $$ = makeAArrayExpr($2); } ; *************** *** 9820,9825 **** --- 9819,9833 ---- return (Node *) x; } + static Node * + makeAArrayExpr(List *elements) + { + A_ArrayExpr *n = makeNode(A_ArrayExpr); + + n->elements = elements; + return (Node *) n; + } + /* * Must undefine base_yylex before including scan.c, since we want it * to create the function base_yylex not filtered_base_yylex. *** ./src/backend/parser/parse_expr.c.orig Fri Nov 30 19:31:29 2007 --- ./src/backend/parser/parse_expr.c Fri Nov 30 19:32:11 2007 *************** *** 52,58 **** 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 *transformRowExpr(ParseState *pstate, RowExpr *r); static Node *transformCoalesceExpr(ParseState *pstate, CoalesceExpr *c); static Node *transformMinMaxExpr(ParseState *pstate, MinMaxExpr *m); --- 52,58 ---- 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, A_ArrayExpr *a, Oid typeid, int32 typmod); static Node *transformRowExpr(ParseState *pstate, RowExpr *r); static Node *transformCoalesceExpr(ParseState *pstate, CoalesceExpr *c); static Node *transformMinMaxExpr(ParseState *pstate, MinMaxExpr *m); *************** *** 145,153 **** case T_TypeCast: { TypeCast *tc = (TypeCast *) expr; ! Node *arg = transformExpr(pstate, tc->arg); ! result = typecast_expression(pstate, arg, tc->typename); break; } --- 145,174 ---- case T_TypeCast: { TypeCast *tc = (TypeCast *) expr; ! int32 typmod; ! Oid typeid = typenameTypeId(pstate, tc->typename, &typmod); ! /* ! * If the subject of the typecast is an array expression, we ! * check whether the target type is an array type. If so, we ! * skip the creation of a typecast expression and instead pass ! * the type down to transformArrayExpr. This allows us to know ! * the type of the array without having to examine the types of ! * all its elements. This in turn allows us to construct ! * viable empty arrays. ! */ ! if (nodeTag(tc->arg) == T_A_ArrayExpr && type_is_array(typeid)) ! { ! result = transformArrayExpr(pstate, ! (A_ArrayExpr *) tc->arg, ! typeid, ! typmod); ! } ! else ! { ! Node *arg = transformExpr(pstate, tc->arg); ! result = typecast_expression(pstate, arg, tc->typename); ! } break; } *************** *** 205,212 **** result = transformCaseExpr(pstate, (CaseExpr *) expr); break; ! case T_ArrayExpr: ! result = transformArrayExpr(pstate, (ArrayExpr *) expr); break; case T_RowExpr: --- 226,233 ---- result = transformCaseExpr(pstate, (CaseExpr *) expr); break; ! case T_A_ArrayExpr: ! result = transformArrayExpr(pstate, (A_ArrayExpr *) expr, InvalidOid, -1); break; case T_RowExpr: *************** *** 1255,1262 **** return result; } static Node * ! transformArrayExpr(ParseState *pstate, ArrayExpr *a) { ArrayExpr *newa = makeNode(ArrayExpr); List *newelems = NIL; --- 1276,1292 ---- return result; } + /* + * transformArrayExpr + * + * If the array is the subject of an explicit cast to an array type, the target + * type should be passed in via the typeid argument. + * + * When typeid is present, rather than examining the elements to determine the + * overall array type, the elements will be coerced to the appropriate type. + */ static Node * ! transformArrayExpr(ParseState *pstate, A_ArrayExpr *a, Oid typeid, int32 typmod) { ArrayExpr *newa = makeNode(ArrayExpr); List *newelems = NIL; *************** *** 1265,1318 **** ListCell *element; Oid array_type; Oid element_type; ! /* Transform the element expressions */ foreach(element, a->elements) { Node *e = (Node *) lfirst(element); Node *newe; newe = transformExpr(pstate, e); newelems = lappend(newelems, newe); - typeids = lappend_oid(typeids, exprType(newe)); - } - - /* Select a common type for the elements */ - element_type = select_common_type(typeids, "ARRAY"); ! /* Coerce arguments to common type if necessary */ ! foreach(element, newelems) ! { ! Node *e = (Node *) lfirst(element); ! Node *newe; ! newe = coerce_to_common_type(pstate, e, ! element_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; --- 1295,1400 ---- ListCell *element; Oid array_type; Oid element_type; + Oid coerce_type; ! /* ! * Transform the element expressions ! * ! * Assume that the array is one-dimensional until we discover otherwise. ! */ ! newa->multidims = false; foreach(element, a->elements) { Node *e = (Node *) lfirst(element); Node *newe; + Oid newe_type; newe = transformExpr(pstate, e); newelems = lappend(newelems, newe); ! newe_type = exprType(newe); ! if (!newa->multidims && OidIsValid(get_element_type(newe_type))) ! /* ! * This element appears to be an array, so presume that we have a ! * multi-dimensional array. ! */ ! newa->multidims = true; ! typeids = lappend_oid(typeids, newe_type); } ! /* ! * Select a target type for the elements. ! * ! * If we haven't been given an array type via the typeid argument, we must ! * try to deduce a common type based on the types of the individual ! * elements present. ! */ ! if (OidIsValid(typeid)) { ! array_type = typeid; ! element_type = get_element_type(array_type); ! coerce_type = (newa->multidims ? array_type : element_type); ! ! if (!OidIsValid(element_type)) ! /* The caller screwed up by passing us a non-array type */ ! ereport(ERROR, ! (errcode(ERRCODE_INTERNAL_ERROR), ! errmsg("invalid target type for array expression"), ! errdetail("%s is not an array type.", ! format_type_be(typeid)))); } else { ! if (newelems == NIL) ! /* Can't have an empty array without a target type */ ! ereport(ERROR, ! (errcode(ERRCODE_INDETERMINATE_DATATYPE), ! errmsg("no target type for empty array"), ! errhint("Empty arrays must be explictly cast to the " ! "desired array type, e.g. ARRAY[]::int[]"))); ! ! coerce_type = select_common_type(typeids, "ARRAY"); ! array_type = (newa->multidims ? ! coerce_type : ! get_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)))); } + /* + * Coerce elements to target type + * + * If the array has been explictly type cast, then the elements are in turn + * explictly coerced. + * + * If the array's type was merely derived from the common type of its + * elements, then the elements are implictly coerced to the common type. + */ + foreach(element, newelems) + { + Node *e = (Node *) lfirst(element); + Node *newe; + + if (OidIsValid(typeid)) + newe = coerce_to_target_type(pstate, e, + exprType(e), + coerce_type, + typmod, + COERCION_EXPLICIT, + COERCE_EXPLICIT_CAST); + else + newe = coerce_to_common_type(pstate, e, + coerce_type, + "ARRAY"); + newcoercedelems = lappend(newcoercedelems, newe); + } + newa->array_typeid = array_type; newa->element_typeid = element_type; newa->elements = newcoercedelems; *** ./src/backend/parser/parse_target.c.orig Fri Nov 30 19:31:30 2007 --- ./src/backend/parser/parse_target.c Fri Nov 30 19:32:11 2007 *************** *** 1294,1299 **** --- 1294,1300 ---- return 1; } break; + case T_A_ArrayExpr: case T_ArrayExpr: /* make ARRAY[] act like a function */ *name = "array"; *** ./src/include/nodes/nodes.h.orig Fri Nov 30 19:31:30 2007 --- ./src/include/nodes/nodes.h Fri Nov 30 19:32:11 2007 *************** *** 347,352 **** --- 347,353 ---- T_LockingClause, T_RowMarkClause, T_XmlSerialize, + T_A_ArrayExpr, /* * TAGS FOR RANDOM OTHER STUFF *** ./src/include/nodes/parsenodes.h.orig Fri Nov 30 19:31:30 2007 --- ./src/include/nodes/parsenodes.h Fri Nov 30 19:32:11 2007 *************** *** 305,310 **** --- 305,319 ---- } A_Indirection; /* + * A_ArrayExpr - an ARRAY[] construction + */ + typedef struct A_ArrayExpr + { + NodeTag type; + List *elements; /* array elements, or NIL if none */ + } A_ArrayExpr; + + /* * ResTarget - * result target (used in target list of pre-transformed parse trees) * *** ./src/test/regress/expected/arrays.out.orig Fri Nov 30 19:31:30 2007 --- ./src/test/regress/expected/arrays.out Fri Nov 30 19:32:11 2007 *************** *** 785,790 **** --- 785,793 ---- ERROR: malformed array literal: "{}}" select '{ }}'::text[]; ERROR: malformed array literal: "{ }}" + select array[]; + ERROR: no target type for empty array + HINT: Empty arrays must be explictly cast to the desired array type, e.g. ARRAY[]::int[] -- none of the above should be accepted -- all of the following should be accepted select '{}'::text[]; *************** *** 826,831 **** --- 829,840 ---- {"@ 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[]); *** ./src/test/regress/sql/arrays.sql.orig Fri Nov 30 19:31:30 2007 --- ./src/test/regress/sql/arrays.sql Fri Nov 30 19:32:11 2007 *************** *** 280,285 **** --- 280,286 ---- 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,297 **** --- 293,299 ---- 0 second, @ 1 hour @ 42 minutes @ 20 seconds }'::interval[]; + select array[]::text[]; -- all of the above should be accepted -- tests for array aggregates