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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy