diff options
Diffstat (limited to 'src/backend/parser/parse_coerce.c')
-rw-r--r-- | src/backend/parser/parse_coerce.c | 114 |
1 files changed, 86 insertions, 28 deletions
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c index fbc83870a5c..c232dee4a58 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.152 2007/03/27 23:21:10 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.153 2007/04/02 03:49:38 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -132,7 +132,8 @@ coerce_type(ParseState *pstate, Node *node, } if (targetTypeId == ANYOID || targetTypeId == ANYELEMENTOID || - (targetTypeId == ANYARRAYOID && inputTypeId != UNKNOWNOID)) + (targetTypeId == ANYARRAYOID && inputTypeId != UNKNOWNOID) || + (targetTypeId == ANYENUMOID && inputTypeId != UNKNOWNOID)) { /* * Assume can_coerce_type verified that implicit coercion is okay. @@ -143,7 +144,8 @@ coerce_type(ParseState *pstate, Node *node, * 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.) + * which will produce an error.) Likewise, UNKNOWN input is no good + * for ANYENUM. * * NB: we do NOT want a RelabelType here. */ @@ -406,9 +408,8 @@ can_coerce_type(int nargs, Oid *input_typeids, Oid *target_typeids, if (targetTypeId == ANYOID) continue; - /* accept if target is ANYARRAY or ANYELEMENT, for now */ - if (targetTypeId == ANYARRAYOID || - targetTypeId == ANYELEMENTOID) + /* accept if target is polymorphic, for now */ + if (IsPolymorphicType(targetTypeId)) { have_generics = true; /* do more checking later */ continue; @@ -1048,6 +1049,9 @@ coerce_to_common_type(ParseState *pstate, Node *node, * 3) If there are arguments of both ANYELEMENT and ANYARRAY, make sure * the actual ANYELEMENT datatype is in fact the element type for * the actual ANYARRAY datatype. + * 4) ANYENUM is treated the same as ANYELEMENT except that if it is used + * (alone or in combination with plain ANYELEMENT), we add the extra + * condition that the ANYELEMENT type must be an enum. * * If we have UNKNOWN input (ie, an untyped literal) for any ANYELEMENT * or ANYARRAY argument, assume it is okay. @@ -1070,6 +1074,7 @@ check_generic_type_consistency(Oid *actual_arg_types, Oid array_typeid = InvalidOid; Oid array_typelem; bool have_anyelement = false; + bool have_anyenum = false; /* * Loop through the arguments to see if we have any that are ANYARRAY or @@ -1079,9 +1084,12 @@ check_generic_type_consistency(Oid *actual_arg_types, { Oid actual_type = actual_arg_types[j]; - if (declared_arg_types[j] == ANYELEMENTOID) + if (declared_arg_types[j] == ANYELEMENTOID || + declared_arg_types[j] == ANYENUMOID) { have_anyelement = true; + if (declared_arg_types[j] == ANYENUMOID) + have_anyenum = true; if (actual_type == UNKNOWNOID) continue; if (OidIsValid(elem_typeid) && actual_type != elem_typeid) @@ -1127,6 +1135,13 @@ check_generic_type_consistency(Oid *actual_arg_types, } } + if (have_anyenum) + { + /* require the element type to be an enum */ + if (!type_is_enum(elem_typeid)) + return false; + } + /* Looks valid */ return true; } @@ -1136,18 +1151,18 @@ check_generic_type_consistency(Oid *actual_arg_types, * Make sure a polymorphic function is legally callable, and * deduce actual argument and result types. * - * If ANYARRAY or ANYELEMENT is used for a function's arguments or + * If ANYARRAY, ANYELEMENT, or ANYENUM is used for a function's arguments or * return type, we make sure the actual data types are consistent with * each other. The argument consistency rules are shown above for * check_generic_type_consistency(). * - * If we have UNKNOWN input (ie, an untyped literal) for any ANYELEMENT - * or ANYARRAY argument, we attempt to deduce the actual type it should - * have. If successful, we alter that position of declared_arg_types[] - * so that make_fn_arguments will coerce the literal to the right thing. + * If we have UNKNOWN input (ie, an untyped literal) for any polymorphic + * argument, we attempt to deduce the actual type it should have. If + * successful, we alter that position of declared_arg_types[] so that + * make_fn_arguments will coerce the literal to the right thing. * * Rules are applied to the function's return type (possibly altering it) - * if it is declared ANYARRAY or ANYELEMENT: + * if it is declared as a polymorphic type: * * 1) If return type is ANYARRAY, and any argument is ANYARRAY, use the * argument's actual type as the function's return type. @@ -1167,6 +1182,9 @@ check_generic_type_consistency(Oid *actual_arg_types, * 6) If return type is ANYELEMENT, no argument is ANYARRAY or ANYELEMENT, * generate an ERROR. This condition is prevented by CREATE FUNCTION * and is therefore not expected here. + * 7) ANYENUM is treated the same as ANYELEMENT except that if it is used + * (alone or in combination with plain ANYELEMENT), we add the extra + * condition that the ANYELEMENT type must be an enum. */ Oid enforce_generic_type_consistency(Oid *actual_arg_types, @@ -1180,7 +1198,9 @@ enforce_generic_type_consistency(Oid *actual_arg_types, Oid elem_typeid = InvalidOid; Oid array_typeid = InvalidOid; Oid array_typelem; - bool have_anyelement = (rettype == ANYELEMENTOID); + bool have_anyelement = (rettype == ANYELEMENTOID || + rettype == ANYENUMOID); + bool have_anyenum = (rettype == ANYENUMOID); /* * Loop through the arguments to see if we have any that are ANYARRAY or @@ -1190,9 +1210,12 @@ enforce_generic_type_consistency(Oid *actual_arg_types, { Oid actual_type = actual_arg_types[j]; - if (declared_arg_types[j] == ANYELEMENTOID) + if (declared_arg_types[j] == ANYELEMENTOID || + declared_arg_types[j] == ANYENUMOID) { have_generics = have_anyelement = true; + if (declared_arg_types[j] == ANYENUMOID) + have_anyenum = true; if (actual_type == UNKNOWNOID) { have_unknowns = true; @@ -1227,8 +1250,8 @@ enforce_generic_type_consistency(Oid *actual_arg_types, } /* - * Fast Track: if none of the arguments are ANYARRAY or ANYELEMENT, return - * the unmodified rettype. + * Fast Track: if none of the arguments are polymorphic, return the + * unmodified rettype. We assume it can't be polymorphic either. */ if (!have_generics) return rettype; @@ -1274,7 +1297,17 @@ enforce_generic_type_consistency(Oid *actual_arg_types, /* Only way to get here is if all the generic args are UNKNOWN */ ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("could not determine anyarray/anyelement type because input has type \"unknown\""))); + errmsg("could not determine polymorphic type because input has type \"unknown\""))); + } + + if (have_anyenum) + { + /* require the element type to be an enum */ + if (!type_is_enum(elem_typeid)) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("type matched to anyenum is not an enum type: %s", + format_type_be(elem_typeid)))); } /* @@ -1289,7 +1322,8 @@ enforce_generic_type_consistency(Oid *actual_arg_types, if (actual_type != UNKNOWNOID) continue; - if (declared_arg_types[j] == ANYELEMENTOID) + if (declared_arg_types[j] == ANYELEMENTOID || + declared_arg_types[j] == ANYENUMOID) declared_arg_types[j] = elem_typeid; else if (declared_arg_types[j] == ANYARRAYOID) { @@ -1307,7 +1341,7 @@ enforce_generic_type_consistency(Oid *actual_arg_types, } } - /* if we return ANYARRAYOID use the appropriate argument type */ + /* if we return ANYARRAY use the appropriate argument type */ if (rettype == ANYARRAYOID) { if (!OidIsValid(array_typeid)) @@ -1322,8 +1356,8 @@ enforce_generic_type_consistency(Oid *actual_arg_types, return array_typeid; } - /* if we return ANYELEMENTOID use the appropriate argument type */ - if (rettype == ANYELEMENTOID) + /* if we return ANYELEMENT use the appropriate argument type */ + if (rettype == ANYELEMENTOID || rettype == ANYENUMOID) return elem_typeid; /* we don't return a generic type; send back the original return type */ @@ -1333,7 +1367,7 @@ enforce_generic_type_consistency(Oid *actual_arg_types, /* * resolve_generic_type() * Deduce an individual actual datatype on the assumption that - * the rules for ANYARRAY/ANYELEMENT are being followed. + * the rules for polymorphic types are being followed. * * declared_type is the declared datatype we want to resolve. * context_actual_type is the actual input datatype to some argument @@ -1362,7 +1396,8 @@ resolve_generic_type(Oid declared_type, format_type_be(context_actual_type)))); return context_actual_type; } - else if (context_declared_type == ANYELEMENTOID) + else if (context_declared_type == ANYELEMENTOID || + context_declared_type == ANYENUMOID) { /* Use the array type corresponding to actual type */ Oid array_typeid = get_array_type(context_actual_type); @@ -1375,7 +1410,7 @@ resolve_generic_type(Oid declared_type, return array_typeid; } } - else if (declared_type == ANYELEMENTOID) + else if (declared_type == ANYELEMENTOID || declared_type == ANYENUMOID) { if (context_declared_type == ANYARRAYOID) { @@ -1389,7 +1424,8 @@ resolve_generic_type(Oid declared_type, format_type_be(context_actual_type)))); return array_typelem; } - else if (context_declared_type == ANYELEMENTOID) + else if (context_declared_type == ANYELEMENTOID || + context_declared_type == ANYENUMOID) { /* Use the actual type; it doesn't matter if array or not */ return context_actual_type; @@ -1402,7 +1438,7 @@ resolve_generic_type(Oid declared_type, } /* If we get here, declared_type is polymorphic and context isn't */ /* NB: this is a calling-code logic error, not a user error */ - elog(ERROR, "could not determine ANYARRAY/ANYELEMENT type because context isn't polymorphic"); + elog(ERROR, "could not determine polymorphic type because context isn't polymorphic"); return InvalidOid; /* keep compiler quiet */ } @@ -1502,6 +1538,7 @@ TypeCategory(Oid inType) case (INTERNALOID): case (OPAQUEOID): case (ANYELEMENTOID): + case (ANYENUMOID): result = GENERIC_TYPE; break; @@ -1647,6 +1684,11 @@ IsBinaryCoercible(Oid srctype, Oid targettype) if (get_element_type(srctype) != InvalidOid) return true; + /* Also accept any enum type as coercible to ANYENUM */ + if (targettype == ANYENUMOID) + if (type_is_enum(srctype)) + return true; + /* Else look in pg_cast */ tuple = SearchSysCache(CASTSOURCETARGET, ObjectIdGetDatum(srctype), @@ -1777,6 +1819,22 @@ find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId, result = true; } } + + /* + * If we still haven't found a possibility, check for enums, + * and retry looking for a cast to or from ANYENUM. But don't + * mistakenly conclude that ANYENUM-to-some-enum-type is a + * trivial cast. + */ + if (!result) + { + if (type_is_enum(sourceTypeId)) + result = find_coercion_pathway(targetTypeId, ANYENUMOID, + ccontext, funcid, arrayCoerce); + else if (sourceTypeId != ANYENUMOID && type_is_enum(targetTypeId)) + result = find_coercion_pathway(ANYENUMOID, sourceTypeId, + ccontext, funcid, arrayCoerce); + } } return result; @@ -1813,7 +1871,7 @@ find_typmod_coercion_function(Oid typeId, /* Check for a varlena array type (and not a domain) */ if (typeForm->typelem != InvalidOid && typeForm->typlen == -1 && - typeForm->typtype != 'd') + typeForm->typtype != TYPTYPE_DOMAIN) { /* Yes, switch our attention to the element type */ typeId = typeForm->typelem; |