diff --git a/doc/src/sgml/typeconv.sgml b/doc/src/sgml/typeconv.sgml index c5373aa753..7cf3efdb1a 100644 --- a/doc/src/sgml/typeconv.sgml +++ b/doc/src/sgml/typeconv.sgml @@ -1,4 +1,4 @@ - + Type Conversion @@ -843,11 +843,19 @@ data type. Type Resolution for <literal>UNION</literal>, <literal>CASE</literal>, and Related Constructs + + +If all inputs are of the same type, and it is not unknown, +resolve as that type. Otherwise, replace any domain types in the list with +their underlying base types. + + + If all inputs are of type unknown, resolve as type text (the preferred type of the string category). -Otherwise, ignore the unknown inputs while choosing the result type. +Otherwise, the unknown inputs will be ignored. @@ -860,14 +868,23 @@ If the non-unknown inputs are not all of the same type category, fail. Choose the first non-unknown input type which is a preferred type in -that category or allows all the non-unknown inputs to be implicitly -converted to it. +that category, if there is one. -Convert all inputs to the selected type. +Otherwise, choose the last non-unknown input type that allows all the +preceding non-unknown inputs to be implicitly converted to it. (There +always is such a type, since at least the first type in the list must +satisfy this condition.) + + + + + +Convert all inputs to the selected type. Fail if there is not a +conversion from a given input to the selected type. diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c index 98b9aba238..aeda66fc26 100644 --- a/src/backend/parser/parse_coerce.c +++ b/src/backend/parser/parse_coerce.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.158 2007/11/15 21:14:37 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.159 2007/11/26 16:46:50 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -958,7 +958,7 @@ coerce_to_specific_type(ParseState *pstate, Node *node, * This is used for determining the output type of CASE and UNION * constructs. * - * typeids is a nonempty list of type OIDs. Note that earlier items + * 'typeids' is a nonempty list of type OIDs. Note that earlier items * in the list will be preferred if there is doubt. * 'context' is a phrase to use in the error message if we fail to select * a usable type. @@ -971,7 +971,28 @@ select_common_type(List *typeids, const char *context) ListCell *type_item; Assert(typeids != NIL); - ptype = getBaseType(linitial_oid(typeids)); + ptype = linitial_oid(typeids); + + /* + * If all input types are valid and exactly the same, just pick that type. + * This is the only way that we will resolve the result as being a domain + * type; otherwise domains are smashed to their base types for comparison. + */ + if (ptype != UNKNOWNOID) + { + for_each_cell(type_item, lnext(list_head(typeids))) + { + Oid ntype = lfirst_oid(type_item); + + if (ntype != ptype) + break; + } + if (type_item == NULL) /* got to the end of the list? */ + return ptype; + } + + /* Nope, so set up for the full algorithm */ + ptype = getBaseType(ptype); pcategory = TypeCategory(ptype); for_each_cell(type_item, lnext(list_head(typeids))) @@ -979,11 +1000,11 @@ select_common_type(List *typeids, const char *context) Oid ntype = getBaseType(lfirst_oid(type_item)); /* move on to next one if no new information... */ - if ((ntype != InvalidOid) && (ntype != UNKNOWNOID) && (ntype != ptype)) + if (ntype != UNKNOWNOID && ntype != ptype) { - if ((ptype == InvalidOid) || ptype == UNKNOWNOID) + if (ptype == UNKNOWNOID) { - /* so far, only nulls so take anything... */ + /* so far, only unknowns so take anything... */ ptype = ntype; pcategory = TypeCategory(ptype); } diff --git a/src/test/regress/expected/domain.out b/src/test/regress/expected/domain.out index b951ce8caa..a80eae7d59 100644 --- a/src/test/regress/expected/domain.out +++ b/src/test/regress/expected/domain.out @@ -69,6 +69,25 @@ from basictest; | (3 rows) +-- check that union/case/coalesce type resolution handles domains properly +select coalesce(4::domainint4, 7) is of (int4) as t; + t +--- + t +(1 row) + +select coalesce(4::domainint4, 7) is of (domainint4) as f; + f +--- + f +(1 row) + +select coalesce(4::domainint4, 7::domainint4) is of (domainint4) as t; + t +--- + t +(1 row) + drop table basictest; drop domain domainvarchar restrict; drop domain domainnumeric restrict; diff --git a/src/test/regress/sql/domain.sql b/src/test/regress/sql/domain.sql index 7a4ec383d2..1e5295899b 100644 --- a/src/test/regress/sql/domain.sql +++ b/src/test/regress/sql/domain.sql @@ -58,6 +58,11 @@ select * from basictest; select testtext || testvarchar as concat, testnumeric + 42 as sum from basictest; +-- check that union/case/coalesce type resolution handles domains properly +select coalesce(4::domainint4, 7) is of (int4) as t; +select coalesce(4::domainint4, 7) is of (domainint4) as f; +select coalesce(4::domainint4, 7::domainint4) is of (domainint4) as t; + drop table basictest; drop domain domainvarchar restrict; drop domain domainnumeric restrict;