aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2011-06-08 12:52:12 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2011-06-08 12:52:58 -0400
commitb7e8feb33e5d948c66720643fe32cfb06980c3d1 (patch)
treece857d48d94ccb16746978b718ebd16fa0e9529e /src
parent8f9622bbb3c02b06176760c3ca2d33c5b5f629a7 (diff)
downloadpostgresql-b7e8feb33e5d948c66720643fe32cfb06980c3d1.tar.gz
postgresql-b7e8feb33e5d948c66720643fe32cfb06980c3d1.zip
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.
Diffstat (limited to 'src')
-rw-r--r--src/backend/parser/parse_coerce.c87
-rw-r--r--src/test/regress/expected/domain.out36
-rw-r--r--src/test/regress/sql/domain.sql7
3 files changed, 110 insertions, 20 deletions
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index 0418972517e..3c5be6bc888 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 7d72791e5ef..2586b07241e 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 545af626220..1cc447b8a24 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);