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