From: Tom Lane Date: Wed, 8 Jun 2011 16:52:12 +0000 (-0400) Subject: Allow domains over arrays to match ANYARRAY parameters again. X-Git-Tag: REL9_1_BETA2~16 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=b7e8feb33e5d948c66720643fe32cfb06980c3d1;p=postgresql Allow domains over arrays to match ANYARRAY parameters again. This use-case was broken in commit 529cb267a6843a6a8190c86b75d091771d99d6a9 of 2010-10-21, in which I commented "For the moment, we just forbid such matching. We might later wish to insert an automatic downcast to the underlying array type, but such a change should also change matching of domains to ANYELEMENT for consistency". We still lack consensus about what to do with ANYELEMENT; but not matching ANYARRAY is a clear loss of functionality compared to prior releases, so let's go ahead and make that happen. Per complaint from Regina Obe and extensive subsequent discussion. --- diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c index 0418972517..3c5be6bc88 100644 --- a/src/backend/parser/parse_coerce.c +++ b/src/backend/parser/parse_coerce.c @@ -143,9 +143,7 @@ coerce_type(ParseState *pstate, Node *node, } if (targetTypeId == ANYOID || targetTypeId == ANYELEMENTOID || - targetTypeId == ANYNONARRAYOID || - (targetTypeId == ANYARRAYOID && inputTypeId != UNKNOWNOID) || - (targetTypeId == ANYENUMOID && inputTypeId != UNKNOWNOID)) + targetTypeId == ANYNONARRAYOID) { /* * Assume can_coerce_type verified that implicit coercion is okay. @@ -154,15 +152,48 @@ coerce_type(ParseState *pstate, Node *node, * it's OK to treat an UNKNOWN constant as a valid input for a * function accepting ANY, ANYELEMENT, or ANYNONARRAY. This should be * all right, since an UNKNOWN value is still a perfectly valid Datum. - * However an UNKNOWN value is definitely *not* an array, and so we - * mustn't accept it for ANYARRAY. (Instead, we will call anyarray_in - * below, which will produce an error.) Likewise, UNKNOWN input is no - * good for ANYENUM. * - * NB: we do NOT want a RelabelType here. + * NB: we do NOT want a RelabelType here: the exposed type of the + * function argument must be its actual type, not the polymorphic + * pseudotype. */ return node; } + if (targetTypeId == ANYARRAYOID || + targetTypeId == ANYENUMOID) + { + /* + * Assume can_coerce_type verified that implicit coercion is okay. + * + * These cases are unlike the ones above because the exposed type of + * the argument must be an actual array or enum type. In particular + * the argument must *not* be an UNKNOWN constant. If it is, we just + * fall through; below, we'll call anyarray_in or anyenum_in, which + * will produce an error. Also, if what we have is a domain over + * array or enum, we have to relabel it to its base type. + * + * Note: currently, we can't actually see a domain-over-enum here, + * since the other functions in this file will not match such a + * parameter to ANYENUM. But that should get changed eventually. + */ + if (inputTypeId != UNKNOWNOID) + { + Oid baseTypeId = getBaseType(inputTypeId); + + if (baseTypeId != inputTypeId) + { + RelabelType *r = makeRelabelType((Expr *) node, + baseTypeId, -1, + InvalidOid, + cformat); + + r->location = location; + return (Node *) r; + } + /* Not a domain type, so return it as-is */ + return node; + } + } if (inputTypeId == UNKNOWNOID && IsA(node, Const)) { /* @@ -1257,6 +1288,11 @@ coerce_to_common_type(ParseState *pstate, Node *node, * (This is a no-op if used in combination with ANYARRAY or ANYENUM, but * is an extra restriction if not.) * + * Domains over arrays match ANYARRAY, and are immediately flattened to their + * base type. (Thus, for example, we will consider it a match if one ANYARRAY + * argument is a domain over int4[] while another one is just int4[].) Also + * notice that such a domain does *not* match ANYNONARRAY. + * * If we have UNKNOWN input (ie, an untyped literal) for any polymorphic * argument, assume it is okay. * @@ -1309,6 +1345,7 @@ check_generic_type_consistency(Oid *actual_arg_types, { if (actual_type == UNKNOWNOID) continue; + actual_type = getBaseType(actual_type); /* flatten domains */ if (OidIsValid(array_typeid) && actual_type != array_typeid) return false; array_typeid = actual_type; @@ -1346,8 +1383,8 @@ check_generic_type_consistency(Oid *actual_arg_types, if (have_anynonarray) { - /* require the element type to not be an array */ - if (type_is_array(elem_typeid)) + /* require the element type to not be an array or domain over array */ + if (type_is_array_domain(elem_typeid)) return false; } @@ -1406,6 +1443,10 @@ check_generic_type_consistency(Oid *actual_arg_types, * (This is a no-op if used in combination with ANYARRAY or ANYENUM, but * is an extra restriction if not.) * + * Domains over arrays match ANYARRAY arguments, and are immediately flattened + * to their base type. (In particular, if the return type is also ANYARRAY, + * we'll set it to the base type not the domain type.) + * * When allow_poly is false, we are not expecting any of the actual_arg_types * to be polymorphic, and we should not return a polymorphic result type * either. When allow_poly is true, it is okay to have polymorphic "actual" @@ -1485,6 +1526,7 @@ enforce_generic_type_consistency(Oid *actual_arg_types, } if (allow_poly && decl_type == actual_type) continue; /* no new information here */ + actual_type = getBaseType(actual_type); /* flatten domains */ if (OidIsValid(array_typeid) && actual_type != array_typeid) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), @@ -1557,8 +1599,8 @@ enforce_generic_type_consistency(Oid *actual_arg_types, if (have_anynonarray && elem_typeid != ANYELEMENTOID) { - /* require the element type to not be an array */ - if (type_is_array(elem_typeid)) + /* require the element type to not be an array or domain over array */ + if (type_is_array_domain(elem_typeid)) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("type matched to anynonarray is an array type: %s", @@ -1655,15 +1697,19 @@ resolve_generic_type(Oid declared_type, { if (context_declared_type == ANYARRAYOID) { - /* Use actual type, but it must be an array */ - Oid array_typelem = get_element_type(context_actual_type); + /* + * Use actual type, but it must be an array; or if it's a domain + * over array, use the base array type. + */ + Oid context_base_type = getBaseType(context_actual_type); + Oid array_typelem = get_element_type(context_base_type); if (!OidIsValid(array_typelem)) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("argument declared \"anyarray\" is not an array but type %s", - format_type_be(context_actual_type)))); - return context_actual_type; + format_type_be(context_base_type)))); + return context_base_type; } else if (context_declared_type == ANYELEMENTOID || context_declared_type == ANYNONARRAYOID || @@ -1687,13 +1733,14 @@ resolve_generic_type(Oid declared_type, if (context_declared_type == ANYARRAYOID) { /* Use the element type corresponding to actual type */ - Oid array_typelem = get_element_type(context_actual_type); + Oid context_base_type = getBaseType(context_actual_type); + Oid array_typelem = get_element_type(context_base_type); if (!OidIsValid(array_typelem)) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("argument declared \"anyarray\" is not an array but type %s", - format_type_be(context_actual_type)))); + format_type_be(context_base_type)))); return array_typelem; } else if (context_declared_type == ANYELEMENTOID || @@ -1796,12 +1843,12 @@ IsBinaryCoercible(Oid srctype, Oid targettype) /* Also accept any array type as coercible to ANYARRAY */ if (targettype == ANYARRAYOID) - if (type_is_array(srctype)) + if (type_is_array_domain(srctype)) return true; /* Also accept any non-array type as coercible to ANYNONARRAY */ if (targettype == ANYNONARRAYOID) - if (!type_is_array(srctype)) + if (!type_is_array_domain(srctype)) return true; /* Also accept any enum type as coercible to ANYENUM */ diff --git a/src/test/regress/expected/domain.out b/src/test/regress/expected/domain.out index 7d72791e5e..2586b07241 100644 --- a/src/test/regress/expected/domain.out +++ b/src/test/regress/expected/domain.out @@ -127,6 +127,16 @@ select testint4arr[1], testchar4arr[2:2] from domarrtest; | {{d,e,f}} (5 rows) +select array_dims(testint4arr), array_dims(testchar4arr) from domarrtest; + array_dims | array_dims +------------+------------ + [1:2] | [1:2][1:2] + [1:2][1:2] | [1:1][1:2] + [1:2] | [1:3][1:2] + [1:2] | [1:2][1:1] + | [1:2][1:3] +(5 rows) + COPY domarrtest FROM stdin; COPY domarrtest FROM stdin; -- fail ERROR: value too long for type character varying(4) @@ -146,6 +156,32 @@ select * from domarrtest; drop table domarrtest; drop domain domainint4arr restrict; drop domain domainchar4arr restrict; +create domain dia as int[]; +select '{1,2,3}'::dia; + dia +--------- + {1,2,3} +(1 row) + +select array_dims('{1,2,3}'::dia); + array_dims +------------ + [1:3] +(1 row) + +select pg_typeof('{1,2,3}'::dia); + pg_typeof +----------- + dia +(1 row) + +select pg_typeof('{1,2,3}'::dia || 42); -- should be int[] not dia + pg_typeof +----------- + integer[] +(1 row) + +drop domain dia; create domain dnotnull varchar(15) NOT NULL; create domain dnull varchar(15); create domain dcheck varchar(15) NOT NULL CHECK (VALUE = 'a' OR VALUE = 'c' OR VALUE = 'd'); diff --git a/src/test/regress/sql/domain.sql b/src/test/regress/sql/domain.sql index 545af62622..1cc447b8a2 100644 --- a/src/test/regress/sql/domain.sql +++ b/src/test/regress/sql/domain.sql @@ -87,6 +87,7 @@ INSERT INTO domarrtest values (NULL, '{{"a","b","c"},{"d","e","f"}}'); INSERT INTO domarrtest values (NULL, '{{"toolong","b","c"},{"d","e","f"}}'); select * from domarrtest; select testint4arr[1], testchar4arr[2:2] from domarrtest; +select array_dims(testint4arr), array_dims(testchar4arr) from domarrtest; COPY domarrtest FROM stdin; {3,4} {q,w,e} @@ -103,6 +104,12 @@ drop table domarrtest; drop domain domainint4arr restrict; drop domain domainchar4arr restrict; +create domain dia as int[]; +select '{1,2,3}'::dia; +select array_dims('{1,2,3}'::dia); +select pg_typeof('{1,2,3}'::dia); +select pg_typeof('{1,2,3}'::dia || 42); -- should be int[] not dia +drop domain dia; create domain dnotnull varchar(15) NOT NULL; create domain dnull varchar(15);