8000 Allow domains over arrays to match ANYARRAY parameters again. · idmf/postgres@b7e8feb · GitHub
[go: up one dir, main page]

Skip to content

Commit b7e8feb

Browse files
committed
Allow domains over arrays to match ANYARRAY parameters again.
This use-case was broken in commit 529cb26 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.
1 parent 8f9622b commit b7e8feb

File tree

3 files changed

+110
-20
lines changed

3 files changed

+110
-20
lines changed

src/backend/parser/parse_coerce.c

Lines changed: 67 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -143,9 +143,7 @@ coerce_type(ParseState *pstate, Node *node,
143143
}
144144
if (targetTypeId == ANYOID ||
145145
targetTypeId == ANYELEMENTOID ||
146-
targetTypeId == ANYNONARRAYOID ||
147-
(targetTypeId == ANYARRAYOID && inputTypeId != UNKNOWNOID) ||
148-
(targetTypeId == ANYENUMOID && inputTypeId != UNKNOWNOID))
146+
targetTypeId == ANYNONARRAYOID)
149147
{
150148
/*
151149
* Assume can_coerce_type verified that implicit coercion is okay.
@@ -154,15 +152,48 @@ coerce_type(ParseState *pstate, Node *node,
154152
* it's OK to treat an UNKNOWN constant as a valid input for a
155153
* function accepting ANY, ANYELEMENT, or ANYNONARRAY. This should be
156154
* all right, since an UNKNOWN value is still a perfectly valid Datum.
157-
* However an UNKNOWN value is definitely *not* an array, and so we
158-
* mustn't accept it for ANYARRAY. (Instead, we will call anyarray_in
159-
* below, which will produce an error.) Likewise, UNKNOWN input is no
160-
* good for ANYENUM.
161155
*
162-
* NB: we do NOT want a RelabelType here.
156+
* NB: we do NOT want a RelabelType here: the exposed type of the
157+
* function argument must be its actual type, not the polymorphic
158+
* pseudotype.
163159
*/
164160
return node;
165161
}
162+
if (targetTypeId == ANYARRAYOID ||
163+
targetTypeId == ANYENUMOID)
164+
{
165+
/*
166+
* Assume can_coerce_type verified that implicit coercion is okay.
167+
*
168+
* These cases are unlike the ones above because the exposed type of
169+
* the argument must be an actual array or enum type. In particular
170+
* the argument must *not* be an UNKNOWN constant. If it is, we just
171+
* fall through; below, we'll call anyarray_in or anyenum_in, which
172+
* will produce an error. Also, if what we have is a domain over
173+
* array or enum, we have to relabel it to its base type.
174+
*
175+
* Note: currently, we can't actually see a domain-over-enum here,
176+
* since the other functions in this file will not match such a
177+
* parameter to ANYENUM. But that should get changed eventually.
178+
*/
179+
if (inputTypeId != UNKNOWNOID)
180+
{
181+
Oid baseTypeId = getBaseType(inputTypeId);
182+
183+
if (baseTypeId != inputTypeId)
184+
{
185+
RelabelType *r = makeRelabelType((Expr *) node,
186+
baseTypeId, -1,
187+
InvalidOid,
188+
cformat);
189+
190+
r->location = location;
191+
return (Node *) r;
192+
}
193+
/* Not a domain type, so return it as-is */
194+
return node;
195+
}
196+
}
166197
if (inputTypeId == UNKNOWNOID && IsA(node, Const))
167198
{
168199
/*
@@ -1257,6 +1288,11 @@ coerce_to_common_type(ParseState *pstate, Node *node,
12571288
* (This is a no-op if used in combination with ANYARRAY or ANYENUM, but
12581289
* is an extra restriction if not.)
12591290
*
1291+
* Domains over arrays match ANYARRAY, and are immediately flattened to their
1292+
* base type. (Thus, for example, we will consider it a match if one ANYARRAY
1293+
* argument is a domain over int4[] while another one is just int4[].) Also
1294+
* notice that such a domain does *not* match ANYNONARRAY.
1295+
*
12601296
* If we have UNKNOWN input (ie, an untyped literal) for any polymorphic
12611297
* argument, assume it is okay.
12621298
*
@@ -1309,6 +1345,7 @@ check_generic_type_consistency(Oid *actual_arg_types,
13091345
{
13101346
if (actual_type == UNKNOWNOID)
13111347
continue;
1348+
actual_type = getBaseType(actual_type); /* flatten domains */
13121349
if (OidIsValid(array_typeid) && actual_type != array_typeid)
13131350
return false;
13141351
array_typeid = actual_type;
@@ -1346,8 +1383,8 @@ check_generic_type_consistency(Oid *actual_arg_types,
13461383

13471384
if (have_anynonarray)
13481385
{
1349-
/* require the element type to not be an array */
1350-
if (type_is_array(elem_typeid))
1386+
/* require the element type to not be an array or domain over array */
1387+
if (type_is_array_domain(elem_typeid))
13511388
return false;
13521389
}
13531390

@@ -1406,6 +1443,10 @@ check_generic_type_consistency(Oid *actual_arg_types,
14061443
* (This is a no-op if used in combination with ANYARRAY or ANYENUM, but
14071444
* is an extra restriction if not.)
14081445
*
1446+
* Domains over arrays match ANYARRAY arguments, and are immediately flattened
1447+
* to their base type. (In particular, if the return type is also ANYARRAY,
1448+
* we'll set it to the base type not the domain type.)
1449+
*
14091450
* When allow_poly is false, we are not expecting any of the actual_arg_types
14101451
* to be polymorphic, and we should not return a polymorphic result type
14111452
* 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,
14851526
}
14861527
if (allow_poly && decl_type == actual_type)
14871528
continue; /* no new information here */
1529+
actual_type = getBaseType(actual_type); /* flatten domains */
14881530
if (OidIsValid(array_typeid) && actual_type != array_typeid)
14891531
ereport(ERROR,
14901532
(errcode(ERRCODE_DATATYPE_MISMATCH),
@@ -1557,8 +1599,8 @@ enforce_generic_type_consistency(Oid *actual_arg_types,
15571599

15581600
if (have_anynonarray && elem_typeid != ANYELEMENTOID)
15591601
{
1560-
/* require the element type to not be an array */
1561-
if (type_is_array(elem_typeid))
1602+
/* require the element type to not be an array or domain over array */
1603+
if (type_is_array_domain(elem_typeid))
15621604
ereport(ERROR,
15631605
(errcode(ERRCODE_DATATYPE_MISMATCH),
15641606
errmsg("type matched to anynonarray is an array type: %s",
@@ -1655,15 +1697,19 @@ resolve_generic_type(Oid declared_type,
16551697
{
16561698
if (context_declared_type == ANYARRAYOID)
16571699
{
1658-
/* Use actual type, but it must be an array */
1659-
Oid array_typelem = get_element_type(context_actual_type);
1700+
/*
1701+
* Use actual type, but it must be an array; or if it's a domain
1702+
* over array, use the base array type.
1703+
*/
1704+
Oid context_base_type = getBaseType(context_actual_type);
1705+
Oid array_typelem = get_element_type(context_base_type);
16601706

16611707
if (!OidIsValid(array_typelem))
16621708
ereport(ERROR,
16631709
(errcode(ERRCODE_DATATYPE_MISMATCH),
16641710
errmsg("argument declared \"anyarray\" is not an array but type %s",
1665-
format_type_be(context_actual_type))));
1666-
return context_actual_type;
1711+
format_type_be(context_base_type))));
1712+
return context_base_type;
16671713
}
16681714
else if (context_declared_type == ANYELEMENTOID ||
16691715
context_declared_type == ANYNONARRAYOID ||
@@ -1687,13 +1733,14 @@ resolve_generic_type(Oid declared_type,
16871733
if (context_declared_type == ANYARRAYOID)
16881734
{
16891735
/* Use the element type corresponding to actual type */
1690-
Oid array_typelem = get_element_type(context_actual_type);
1736+
Oid context_base_type = getBaseType(context_actual_type);
1737+
Oid array_typelem = get_element_type(context_base_type);
16911738

16921739
if (!OidIsValid(array_typelem))
16931740
ereport(ERROR,
16941741
(errcode(ERRCODE_DATATYPE_MISMATCH),
16951742
errmsg("argument declared \"anyarray\" is not an array but type %s",
1696-
format_type_be(context_actual_type))));
1743+
format_type_be(context_base_type))));
16971744
return array_typelem;
16981745
}
16991746
else if (context_declared_type == ANYELEMENTOID ||
@@ -1796,12 +1843,12 @@ IsBinaryCoercible(Oid srctype, Oid targettype)
17961843

17971844
/* Also accept any array type as coercible to ANYARRAY */
17981845
if (targettype == ANYARRAYOID)
1799-
if (type_is_array(srctype))
1846+
if (type_is_array_domain(srctype))
18001847
return true;
18011848

18021849
/* Also accept any non-array type as coercible to ANYNONARRAY */
18031850
if (targettype == ANYNONARRAYOID)
1804-
if (!type_is_array(srctype))
1851+
if (!type_is_array_domain(srctype))
18051852
return true;
18061853

18071854
/* Also accept any enum type as coercible to ANYENUM */

src/test/regress/expected/domain.out

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,16 @@ select testint4arr[1], testchar4arr[2:2] from domarrtest;
127127
| {{d,e,f}}
128128
(5 rows)
129129

130+
select array_dims(testint4arr), array_dims(testchar4arr) from domarrtest;
131+
array_dims | array_dims
132+
------------+------------
133+
[1:2] | [1:2][1:2]
134+
[1:2][1:2] | [1:1][1:2]
135+
[1:2] | [1:3][1:2]
136+
[1:2] | [1:2][1:1]
137+
| [1:2][1:3]
138+
(5 rows)
139+
130140
COPY domarrtest FROM stdin;
131141
COPY domarrtest FROM stdin; -- fail
132142
ERROR: value too long for type character varying(4)
@@ -146,6 +156,32 @@ select * from domarrtest;
146156
drop table domarrtest;
147157
drop domain domainint4arr restrict;
148158
drop domain domainchar4arr restrict;
159+
create domain dia as int[];
160+
select '{1,2,3}'::dia;
161+
dia
162+
---------
163+
{1,2,3}
164+
(1 row)
165+
166+
select array_dims('{1,2,3}'::dia);
167+
array_dims
168+
------------
169+
[1:3]
170+
(1 row)
171+
172+
select pg_typeof('{1,2,3}'::dia);
173+
pg_typeof
174+
-----------
175+
dia
176+
(1 row)
177+
178+
select pg_typeof('{1,2,3}'::dia || 42); -- should be int[] not dia
179+
pg_typeof
180+
-----------
181+
integer[]
182+
(1 row)
183+
184+
drop domain dia;
149185
create domain dnotnull varchar(15) NOT NULL;
150186
create domain dnull varchar(15);
151187
create domain dcheck varchar(15) NOT NULL CHECK (VALUE = 'a' OR VALUE = 'c' OR VALUE = 'd');

src/test/regress/sql/domain.sql

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ INSERT INTO domarrtest values (NULL, '{{"a","b","c"},{"d","e","f"}}');
8787
INSERT INTO domarrtest values (NULL, '{{"toolong","b","c"},{"d","e","f"}}');
8888
select * from domarrtest;
8989
select testint4arr[1], testchar4arr[2:2] from domarrtest;
90+
select array_dims(testint4arr), array_dims(testchar4arr) from domarrtest;
9091

9192
COPY domarrtest FROM stdin;
9293
{3,4} {q,w,e}
@@ -103,6 +104,12 @@ drop table domarrtest;
103104
drop domain domainint4arr restrict;
104105
drop domain domainchar4arr restrict;
105106

107+
create domain dia as int[];
108+
select '{1,2,3}'::dia;
109+
select array_dims('{1,2,3}'::dia);
110+
select pg_typeof('{1,2,3}'::dia);
111+
select pg_typeof('{1,2,3}'::dia || 42); -- should be int[] not dia
112+
drop domain dia;
106113

107114
create domain dnotnull varchar(15) NOT NULL;
108115
create domain dnull varchar(15);

0 commit comments

Comments
 (0)
0