/* *Forpass-by-referencedatatypes,repeattheconversiontoseeif *theinputfunctionleavesanyuninitializedbytesintheresult.We *canonlydetectthatreliablyifRANDOMIZE_ALLOCATED_MEMORYis *enabled,sowedon'tbothertestingotherwise.Thereasonwedon't *wantanyinstabilityintheinputfunctionisthatcomparisonof *Constnodesreliesonbytewisecomparisonofthedatums,soifthe *inputfunctionleavesgarbagethensubexpressionsthatshouldbe *identicalmaynotgetrecognizedassuch.Seepgsql-hackers *discussionof2008-04-04.
*/ if (!con->constisnull && !newcon->constbyval)
{
Datum val2;
val2 = stringTypeDatum(baseType,
DatumGetCString(con->constvalue),
inputTypeMod); if (newcon->constlen == -1)
val2 = PointerGetDatum(PG_DETOAST_DATUM(val2)); if (!datumIsEqual(newcon->constvalue, val2, false, newcon->constlen))
elog(WARNING, "type %s has unstable input conversion for \"%s\"",
typeTypeName(baseType), DatumGetCString(con->constvalue));
} #endif
cancel_parser_errposition_callback(&pcbstate);
result = (Node *) newcon;
/* If target is a domain, apply constraints. */ if (baseTypeId != targetTypeId)
result = coerce_to_domain(result,
baseTypeId, baseTypeMod,
targetTypeId,
ccontext, cformat, location, false);
r->location = location;
result = (Node *) r;
}
} return result;
} if (inputTypeId == RECORDOID &&
ISCOMPLEX(targetTypeId))
{ /* Coerce a RECORD to a specific complex type */ return coerce_record_to_complex(pstate, node, targetTypeId,
ccontext, cformat, location);
} if (targetTypeId == RECORDOID &&
ISCOMPLEX(inputTypeId))
{ /* Coerce a specific complex type to RECORD */ /* NB: we do NOT want a RelabelType here */ return node;
} #ifdef NOT_USED if (inputTypeId == RECORDARRAYOID &&
is_complex_array(targetTypeId))
{ /* Coerce record[] to a specific complex array type */ /* not implemented yet ... */
} #endif if (targetTypeId == RECORDARRAYOID &&
is_complex_array(inputTypeId))
{ /* Coerce a specific complex array type to record[] */ /* NB: we do NOT want a RelabelType here */ return node;
} if (typeInheritsFrom(inputTypeId, targetTypeId)
|| typeIsOfTypedTable(inputTypeId, targetTypeId))
{ /* *Inputclasstypeisasubclassoftarget,sogeneratean *appropriateruntimeconversion(removingunneededcolumnsand *possiblyrearrangingtheonesthatarewanted). * *Wewillalsogetherewhentheinputisadomainoverasubclassof *thetargettype.Tokeeplifesimplefortheexecutor,wedefine *ConvertRowtypeExprasonlyworkingbetweenregularcompositetypes; *therefore,insuchcasesinsertaRelabelTypetosmashtheinput *expressiondowntoitsbasetype.
*/
Oid baseTypeId = getBaseType(inputTypeId);
ConvertRowtypeExpr *r = makeNode(ConvertRowtypeExpr);
rt->location = location;
node = (Node *) rt;
}
r->arg = (Expr *) node;
r->resulttype = targetTypeId;
r->convertformat = cformat;
r->location = location; return (Node *) r;
} /* If we get here, caller blew it */
elog(ERROR, "failed to find conversion function from %s to %s",
format_type_be(inputTypeId), format_type_be(targetTypeId)); return NULL; /* keep compiler quiet */
}
/* *can_coerce_type() *Caninput_typeidsbecoercedtotarget_typeids? * *Wemustbetoldthecontext(CASTconstruct,assignment,implicitcoercion) *asthisdeterminesthesetofavailablecasts.
*/ bool
can_coerce_type(int nargs, const Oid *input_typeids, const Oid *target_typeids,
CoercionContext ccontext)
{ bool have_generics = false; int i;
/* run through argument list... */ for (i = 0; i < nargs; i++)
{
Oid inputTypeId = input_typeids[i];
Oid targetTypeId = target_typeids[i];
CoercionPathType pathtype;
Oid funcId;
/* no problem if same type */ if (inputTypeId == targetTypeId) continue;
/* accept if target is ANY */ if (targetTypeId == ANYOID) continue;
/* accept if target is polymorphic, for now */ if (IsPolymorphicType(targetTypeId))
{
have_generics = true; /* do more checking later */ continue;
}
/* *Ifinputisanuntypedstringconstant,assumewecanconvertitto *anything.
*/ if (inputTypeId == UNKNOWNOID) continue;
/* If we found any generic argument types, cross-check them */ if (have_generics)
{ if (!check_generic_type_consistency(input_typeids, target_typeids,
nargs)) returnfalse;
}
/* Process the fields */
newargs = NIL;
ucolno = 1;
arg = list_head(args); for (i = 0; i < tupdesc->natts; i++)
{
Node *expr;
Node *cexpr;
Oid exprtype;
Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
/* Fill in NULLs for dropped columns in rowtype */ if (attr->attisdropped)
{ /* *can'tuseatttypidhere,butitdoesn'treallymatterwhattype *theConstclaimstobe.
*/
newargs = lappend(newargs,
makeNullConst(INT4OID, -1, InvalidOid)); continue;
}
if (arg == NULL)
ereport(ERROR,
(errcode(ERRCODE_CANNOT_COERCE),
errmsg("cannot cast type %s to %s",
format_type_be(RECORDOID),
format_type_be(targetTypeId)),
errdetail("Input has too few columns."),
parser_coercion_errposition(pstate, location, node)));
expr = (Node *) lfirst(arg);
exprtype = exprType(expr);
cexpr = coerce_to_target_type(pstate,
expr, exprtype,
attr->atttypid,
attr->atttypmod,
ccontext,
COERCE_IMPLICIT_CAST,
-1); if (cexpr == NULL)
ereport(ERROR,
(errcode(ERRCODE_CANNOT_COERCE),
errmsg("cannot cast type %s to %s",
format_type_be(RECORDOID),
format_type_be(targetTypeId)),
errdetail("Cannot cast type %s to %s in column %d.",
format_type_be(exprtype),
format_type_be(attr->atttypid),
ucolno),
parser_coercion_errposition(pstate, location, expr)));
newargs = lappend(newargs, cexpr);
ucolno++;
arg = lnext(args, arg);
} if (arg != NULL)
ereport(ERROR,
(errcode(ERRCODE_CANNOT_COERCE),
errmsg("cannot cast type %s to %s",
format_type_be(RECORDOID),
format_type_be(targetTypeId)),
errdetail("Input has too many columns."),
parser_coercion_errposition(pstate, location, node)));
ReleaseTupleDesc(tupdesc);
rowexpr = makeNode(RowExpr);
rowexpr->args = newargs;
rowexpr->row_typeid = baseTypeId;
rowexpr->row_format = cformat;
rowexpr->colnames = NIL; /* not needed for named target type */
rowexpr->location = location;
/* If target is a domain, apply constraints */ if (baseTypeId != targetTypeId)
{
rowexpr->row_format = COERCE_IMPLICIT_CAST; return coerce_to_domain((Node *) rowexpr,
baseTypeId, baseTypeMod,
targetTypeId,
ccontext, cformat, location, false);
}
newnode = coerce_to_target_type(pstate, node, inputTypeId,
BOOLOID, -1,
COERCION_ASSIGNMENT,
COERCE_IMPLICIT_CAST,
-1); if (newnode == NULL)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH), /* translator: first %s is name of a SQL construct, eg WHERE */
errmsg("argument of %s must be type %s, not type %s",
constructName, "boolean",
format_type_be(inputTypeId)),
parser_errposition(pstate, exprLocation(node))));
node = newnode;
}
if (expression_returns_set(node))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH), /* translator: %s is name of a SQL construct, eg WHERE */
errmsg("argument of %s must not return a set",
constructName),
parser_errposition(pstate, exprLocation(node))));
newnode = coerce_to_target_type(pstate, node, inputTypeId,
targetTypeId, targetTypmod,
COERCION_ASSIGNMENT,
COERCE_IMPLICIT_CAST,
-1); if (newnode == NULL)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH), /* translator: first %s is name of a SQL construct, eg LIMIT */
errmsg("argument of %s must be type %s, not type %s",
constructName,
format_type_be(targetTypeId),
format_type_be(inputTypeId)),
parser_errposition(pstate, exprLocation(node))));
node = newnode;
}
if (expression_returns_set(node))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH), /* translator: %s is name of a SQL construct, eg LIMIT */
errmsg("argument of %s must not return a set",
constructName),
parser_errposition(pstate, exprLocation(node))));
if (which_expr)
*which_expr = pexpr; return ptype;
}
/* *select_common_type_from_oids() *DeterminethecommonsupertypeofanarrayoftypeOIDs. * *Thisisthesamelogicasselect_common_type(),butworkingfrom *anarrayoftypeOIDsnotalistofexpressions.Asinthatfunction, *earlierentriesinthearrayhavesomepreferenceoverlaterones. *Onfailure,returnInvalidOidifnoerroristrue,elsethrowanerror. * *Caution:"failure"justmeansthattherewereinputsofdifferenttype *categories.Itisnotguaranteedthatalltheinputsarecoercibletothe *selectedtype;callermustcheckthat(seeverify_common_type_from_oids). * *Note:neithercallerwillpassanyUNKNOWNOIDentries,sothetests *forthatinthisfunctionaredeadcode.However,theydon'tcostmuch, *anditseemsbettertokeepthislogicasclosetoselect_common_type() *aspossible.
*/ static Oid
select_common_type_from_oids(int nargs, const Oid *typeids, bool noerror)
{
Oid ptype;
TYPCATEGORY pcategory; bool pispreferred; int i = 1;
Assert(nargs > 0);
ptype = typeids[0];
/* If all input types are valid and exactly the same, pick that type. */ if (ptype != UNKNOWNOID)
{ for (; i < nargs; i++)
{ if (typeids[i] != ptype) break;
} if (i == nargs) return ptype;
}
if (inputTypeId == targetTypeId) return node; /* no work */ if (can_coerce_type(1, &inputTypeId, &targetTypeId, COERCION_IMPLICIT))
node = coerce_type(pstate, node, inputTypeId, targetTypeId, -1,
COERCION_IMPLICIT, COERCE_IMPLICIT_CAST, -1); else
ereport(ERROR,
(errcode(ERRCODE_CANNOT_COERCE), /* translator: first %s is name of a SQL construct, eg CASE */
errmsg("%s could not convert type %s to %s",
context,
format_type_be(inputTypeId),
format_type_be(targetTypeId)),
parser_errposition(pstate, exprLocation(node)))); return node;
}
/* Types must match */ if (exprType(expr) != common_type) return -1; elseif (first)
{
result = exprTypmod(expr);
first = false;
} else
{ /* As soon as we see a non-matching typmod, fall back to -1 */ if (result != exprTypmod(expr)) return -1;
}
}
return result;
}
/* *check_generic_type_consistency() *Aretheactualargumentspotentiallycompatiblewitha *polymorphicfunction? * *Theargumentconsistencyrulesare: * *1)AllargumentsdeclaredANYELEMENTmusthavethesamedatatype. *2)AllargumentsdeclaredANYARRAYmusthavethesamedatatype, *whichmustbeavarlenaarraytype. *3)AllargumentsdeclaredANYRANGEmustbethesamerangetype. *Similarly,allargumentsdeclaredANYMULTIRANGEmustbethesame *multirangetype;andifbothoftheseappear,theANYRANGEtype *mustbetheelementtypeoftheANYMULTIRANGEtype. *4)Ifthereareargumentsofmorethanoneofthesepolymorphictypes, *thearrayelementtypeand/orrangesubtypemustbethesameaseach *otherandthesameastheANYELEMENTtype. *5)ANYENUMistreatedthesameasANYELEMENTexceptthatifitisused *(aloneorincombinationwithplainANYELEMENT),weaddtheextra *conditionthattheANYELEMENTtypemustbeanenum. *6)ANYNONARRAYistreatedthesameasANYELEMENTexceptthatifitisused, *weaddtheextraconditionthattheANYELEMENTtypemustnotbeanarray. *(Thisisano-opifusedincombinationwithANYARRAYorANYENUM,but *isanextrarestrictionifnot.) *7)AllargumentsdeclaredANYCOMPATIBLEmustbeimplicitlycastable *toacommonsupertype(chosenasperselect_common_type'srules). *ANYCOMPATIBLENONARRAYworkslikeANYCOMPATIBLEbutalsorequiresthe *commonsupertypetonotbeanarray.IfthereareANYCOMPATIBLEARRAY *orANYCOMPATIBLERANGEorANYCOMPATIBLEMULTIRANGEarguments,theirelement *typesorsubtypesareincludedwhilemakingthechoiceofcommonsupertype. *8)TheresolvedtypeofANYCOMPATIBLEARRAYargumentswillbethearray *typeoverthecommonsupertype(whichmightnotbethesamearraytype *asanyoftheoriginalarrays). *9)AllANYCOMPATIBLERANGEargumentsmustbetheexactsamerangetype *(afterdomainflattening),sincewehavenopreferencerulethatwould *letuschooseoneoveranother.Furthermore,thatrange'ssubtype *mustexactlymatchthecommonsupertypechosenbyrule7. *10)AllANYCOMPATIBLEMULTIRANGEargumentsmustbetheexactsamemultirange *type(afterdomainflattening),sincewehavenopreferencerulethat *wouldletuschooseoneoveranother.Furthermore,ifANYCOMPATIBLERANGE *alsoappears,thatrangetypemustbethemultirange'selementtype; *otherwise,themultirange'srange'ssubtypemustexactlymatchthe *commonsupertypechosenbyrule7. * *DomainsoverarraysmatchANYARRAY,andareimmediatelyflattenedtotheir *basetype.(Thus,forexample,wewillconsideritamatchifoneANYARRAY *argumentisadomainoverint4[]whileanotheroneisjustint4[].)Also *noticethatsuchadomaindoes*not*matchANYNONARRAY.Thesamegoes *forANYCOMPATIBLEARRAYandANYCOMPATIBLENONARRAY. * *Similarly,domainsoverrangesmatchANYRANGEorANYCOMPATIBLERANGE, *andareimmediatelyflattenedtotheirbasetype.Likewise,domains *overmultirangesmatchANYMULTIRANGEorANYCOMPATIBLEMULTIRANGEandare *immediatelyflattenedtotheirbasetype. * *Notethatdomainsaren'tcurrentlyconsideredtomatchANYENUM, *eveniftheirbasetypewouldmatch. * *IfwehaveUNKNOWNinput(ie,anuntypedliteral)foranypolymorphic *argument,assumeitisokay. * *Wedonotereporthere,butjustreturnfalseifaruleisviolated.
*/ bool
check_generic_type_consistency(const Oid *actual_arg_types, const Oid *declared_arg_types, int nargs)
{
Oid elem_typeid = InvalidOid;
Oid array_typeid = InvalidOid;
Oid range_typeid = InvalidOid;
Oid multirange_typeid = InvalidOid;
Oid anycompatible_range_typeid = InvalidOid;
Oid anycompatible_range_typelem = InvalidOid;
Oid anycompatible_multirange_typeid = InvalidOid;
Oid anycompatible_multirange_typelem = InvalidOid;
Oid range_typelem = InvalidOid; bool have_anynonarray = false; bool have_anyenum = false; bool have_anycompatible_nonarray = false; int n_anycompatible_args = 0;
Oid anycompatible_actual_types[FUNC_MAX_ARGS];
/* *Loopthroughtheargumentstoseeifwehaveanythatarepolymorphic. *Ifso,requiretheactualtypestobeconsistent.
*/
Assert(nargs <= FUNC_MAX_ARGS); for (int j = 0; j < nargs; j++)
{
Oid decl_type = declared_arg_types[j];
Oid actual_type = actual_arg_types[j];
if (actual_type == UNKNOWNOID) continue;
actual_type = getBaseType(actual_type); /* flatten domains */
elem_type = get_element_type(actual_type); if (!OidIsValid(elem_type)) returnfalse; /* not an array */ /* collect the element type for common-supertype choice */
anycompatible_actual_types[n_anycompatible_args++] = elem_type;
} elseif (decl_type == ANYCOMPATIBLERANGEOID)
{ if (actual_type == UNKNOWNOID) continue;
actual_type = getBaseType(actual_type); /* flatten domains */ if (OidIsValid(anycompatible_range_typeid))
{ /* All ANYCOMPATIBLERANGE arguments must be the same type */ if (anycompatible_range_typeid != actual_type) returnfalse;
} else
{
anycompatible_range_typeid = actual_type;
anycompatible_range_typelem = get_range_subtype(actual_type); if (!OidIsValid(anycompatible_range_typelem)) returnfalse; /* not a range type */ /* collect the subtype for common-supertype choice */
anycompatible_actual_types[n_anycompatible_args++] = anycompatible_range_typelem;
}
} elseif (decl_type == ANYCOMPATIBLEMULTIRANGEOID)
{ if (actual_type == UNKNOWNOID) continue;
actual_type = getBaseType(actual_type); /* flatten domains */ if (OidIsValid(anycompatible_multirange_typeid))
{ /* All ANYCOMPATIBLEMULTIRANGE arguments must be the same type */ if (anycompatible_multirange_typeid != actual_type) returnfalse;
} else
{
anycompatible_multirange_typeid = actual_type;
anycompatible_multirange_typelem = get_multirange_range(actual_type); if (!OidIsValid(anycompatible_multirange_typelem)) returnfalse; /* not a multirange type */ /* we'll consider the subtype below */
}
}
}
/* Get the element type based on the array type, if we have one */ if (OidIsValid(array_typeid))
{ if (array_typeid == ANYARRAYOID)
{ /* *SpecialcaseformatchingANYARRAYinputtoanANYARRAY *argument:allowitfornow.enforce_generic_type_consistency() *mightcomplainlater,dependingonthepresenceofother *polymorphicargumentsorresults,butitwilldeliveraless *surprisingerrormessagethan"functiondoesnotexist". * *(Ifyouthinktochangethis,notethatcan_coerce_typewill *considersuchasituationasamatch,sothatwemightnoteven *gethere.)
*/
} else
{
Oid array_typelem;
array_typelem = get_element_type(array_typeid); if (!OidIsValid(array_typelem)) returnfalse; /* should be an array, but isn't */
if (!OidIsValid(elem_typeid))
{ /* *ifwedon'thaveanelementtypeyet,usetheonewejust *got
*/
elem_typeid = array_typelem;
} elseif (array_typelem != elem_typeid)
{ /* otherwise, they better match */ returnfalse;
}
}
}
/* Deduce range type from multirange type, or check that they agree */ if (OidIsValid(multirange_typeid))
{
Oid multirange_typelem;
multirange_typelem = get_multirange_range(multirange_typeid); if (!OidIsValid(multirange_typelem)) returnfalse; /* should be a multirange, but isn't */
if (!OidIsValid(range_typeid))
{ /* If we don't have a range type yet, use the one we just got */
range_typeid = multirange_typelem;
range_typelem = get_range_subtype(multirange_typelem); if (!OidIsValid(range_typelem)) returnfalse; /* should be a range, but isn't */
} elseif (multirange_typelem != range_typeid)
{ /* otherwise, they better match */ returnfalse;
}
}
/* Get the element type based on the range type, if we have one */ if (OidIsValid(range_typeid))
{
range_typelem = get_range_subtype(range_typeid); if (!OidIsValid(range_typelem)) returnfalse; /* should be a range, but isn't */
if (!OidIsValid(elem_typeid))
{ /* *Ifwedon'thaveanelementtypeyet,usetheonewejustgot
*/
elem_typeid = range_typelem;
} elseif (range_typelem != elem_typeid)
{ /* otherwise, they better match */ returnfalse;
}
}
if (have_anynonarray)
{ /* require the element type to not be an array or domain over array */ if (type_is_array_domain(elem_typeid)) returnfalse;
}
if (have_anyenum)
{ /* require the element type to be an enum */ if (!type_is_enum(elem_typeid)) returnfalse;
}
/* Deduce range type from multirange type, or check that they agree */ if (OidIsValid(anycompatible_multirange_typeid))
{ if (OidIsValid(anycompatible_range_typeid))
{ if (anycompatible_multirange_typelem !=
anycompatible_range_typeid) returnfalse;
} else
{
anycompatible_range_typeid = anycompatible_multirange_typelem;
anycompatible_range_typelem = get_range_subtype(anycompatible_range_typeid); if (!OidIsValid(anycompatible_range_typelem)) returnfalse; /* not a range type */ /* collect the subtype for common-supertype choice */
anycompatible_actual_types[n_anycompatible_args++] =
anycompatible_range_typelem;
}
}
/* Check matching of ANYCOMPATIBLE-family arguments, if any */ if (n_anycompatible_args > 0)
{
Oid anycompatible_typeid;
if (!OidIsValid(anycompatible_typeid)) returnfalse; /* there's definitely no common supertype */
/* We have to verify that the selected type actually works */ if (!verify_common_type_from_oids(anycompatible_typeid,
n_anycompatible_args,
anycompatible_actual_types)) returnfalse;
if (have_anycompatible_nonarray)
{ /* *requiretheanycompatibletypetonotbeanarrayordomain *overarray
*/ if (type_is_array_domain(anycompatible_typeid)) returnfalse;
}
/* *enforce_generic_type_consistency() *Makesureapolymorphicfunctionislegallycallable,and *deduceactualargumentandresulttypes. * *Ifanypolymorphicpseudotypeisusedinafunction'sargumentsor *returntype,wemakesuretheactualdatatypesareconsistentwith *eachother.Theargumentconsistencyrulesareshownabovefor *check_generic_type_consistency(). * *IfwehaveUNKNOWNinput(ie,anuntypedliteral)foranypolymorphic *argument,weattempttodeducetheactualtypeitshouldhave.If *successful,wealterthatpositionofdeclared_arg_types[]sothat *make_fn_argumentswillcoercetheliteraltotherightthing. * *IfwehavepolymorphicargumentsoftheANYCOMPATIBLEfamily, *wesimilarlyalterdeclared_arg_types[]entriestoshowtheresolved *commonsupertype,sothatmake_fn_argumentswillcoercetheactual *argumentstothepropertype. * *Rulesareappliedtothefunction'sreturntype(possiblyalteringit) *ifitisdeclaredasapolymorphictypeandthereisatleastone *polymorphicargumenttype: * *1)IfreturntypeisANYELEMENT,andanyargumentisANYELEMENT,usethe *argument'sactualtypeasthefunction'sreturntype. *2)IfreturntypeisANYARRAY,andanyargumentisANYARRAY,usethe *argument'sactualtypeasthefunction'sreturntype. *3)Similarly,ifreturntypeisANYRANGEorANYMULTIRANGE,andany *argumentisANYRANGEorANYMULTIRANGE,usethatargument'sactualtype *(orthecorrespondingrangeormultirangetype)asthefunction'sreturn *type. *4)Otherwise,ifreturntypeisANYELEMENTorANYARRAY,andthereis *atleastoneANYELEMENT,ANYARRAY,ANYRANGE,orANYMULTIRANGEinput, *deducethereturntypefromthoseinputs,orthrowerrorifwecan't. *5)Otherwise,ifreturntypeisANYRANGEorANYMULTIRANGE,throwerror. *(Wehavenowaytoselectaspecificrangetypeiftheargumentsdon't *includeANYRANGEorANYMULTIRANGE.) *6)ANYENUMistreatedthesameasANYELEMENTexceptthatifitisused *(aloneorincombinationwithplainANYELEMENT),weaddtheextra *conditionthattheANYELEMENTtypemustbeanenum. *7)ANYNONARRAYistreatedthesameasANYELEMENTexceptthatifitisused, *weaddtheextraconditionthattheANYELEMENTtypemustnotbeanarray. *(Thisisano-opifusedincombinationwithANYARRAYorANYENUM,but *isanextrarestrictionifnot.) *8)ANYCOMPATIBLE,ANYCOMPATIBLEARRAY,andANYCOMPATIBLENONARRAYarehandled *byresolvingthecommonsupertypeofthosearguments(ortheirelement *types,forarrayinputs),andthencoercingallthoseargumentstothe *commonsupertype,orthearraytypeoverthecommonsupertypefor *ANYCOMPATIBLEARRAY. *9)ForANYCOMPATIBLERANGEandANYCOMPATIBLEMULTIRANGE,theremustbeat *leastonenon-UNKNOWNinputmatchingthosearguments,andallsuch *inputsmustbethesamerangetype(oritsmultirangetype,as *appropriate),sincewecannotdeducearangetypefromnon-rangetypes. *Furthermore,therangetype'ssubtypeisincludedwhilechoosingthe *commonsupertypeforANYCOMPATIBLEetal,anditmustexactlymatch *thatcommonsupertype. * *DomainsoverarraysorrangesmatchANYARRAYorANYRANGEarguments, *respectively,andareimmediatelyflattenedtotheirbasetype.(In *particular,ifthereturntypeisalsoANYARRAYorANYRANGE,we'llset *ittothebasetypenotthedomaintype.)Thesameistruefor *ANYMULTIRANGE,ANYCOMPATIBLEARRAY,ANYCOMPATIBLERANGE,and *ANYCOMPATIBLEMULTIRANGE. * *Whenallow_polyisfalse,wearenotexpectinganyoftheactual_arg_types *tobepolymorphic,andweshouldnotreturnapolymorphicresulttype *either.Whenallow_polyistrue,itisokaytohavepolymorphic"actual" *argtypes,andwecanreturnamatchingpolymorphictypeastheresult. *(Thiscaseiscurrentlyusedonlytocheckcompatibilityofanaggregate's *declarationwiththeunderlyingtransfn.) * *AspecialcaseisthatwecouldseeANYARRAYasanactual_arg_typeeven *whenallow_polyisfalse(thisispossibleonlybecausepg_statistichas *columnsshownasanyarrayinthecatalogs).Weallowthistomatcha *declaredANYARRAYargument,butonlyifthereisnootherpolymorphic *argumentthatwewouldneedtomatchitwith,andnoneedtodetermine *theelementtypetoinfertheresulttype.Notethismeansthatfunctions *takingANYARRAYhadbetterbehavesanelyifappliedtothepg_statistic *columns;theycan'tjustassumethatsuccessiveinputsareofthesame *actualelementtype.ThereisnosimilarlogicforANYCOMPATIBLEARRAY; *thereisn'taneedforitsincetherearenocatalogcolumnsofthattype, *sowewon'tseeitasinput.WecouldconsidermatchinganactualANYARRAY *inputtoanANYCOMPATIBLEARRAYargument,butatpresentthatseemsuseless *aswell,sincethere'snovalueinusingANYCOMPATIBLEARRAYunlessthere's *atleastoneotherANYCOMPATIBLE-familyargumentorresult. * *Also,iftherearenoargumentsdeclaredtobeofpolymorphictypes, *we'llreturntherettypeunmodifiedevenifit'spolymorphic.Thisshould *neveroccurforuser-declaredfunctions,becauseCREATEFUNCTIONprevents *it.Butitdoeshappenforsomebuilt-infunctions,suchasarray_in().
*/
Oid
enforce_generic_type_consistency(const Oid *actual_arg_types,
Oid *declared_arg_types, int nargs,
Oid rettype, bool allow_poly)
{ bool have_poly_anycompatible = false; bool have_poly_unknowns = false;
Oid elem_typeid = InvalidOid;
Oid array_typeid = InvalidOid;
Oid range_typeid = InvalidOid;
Oid multirange_typeid = InvalidOid;
Oid anycompatible_typeid = InvalidOid;
Oid anycompatible_array_typeid = InvalidOid;
Oid anycompatible_range_typeid = InvalidOid;
Oid anycompatible_range_typelem = InvalidOid;
Oid anycompatible_multirange_typeid = InvalidOid;
Oid anycompatible_multirange_typelem = InvalidOid; bool have_anynonarray = (rettype == ANYNONARRAYOID); bool have_anyenum = (rettype == ANYENUMOID); bool have_anymultirange = (rettype == ANYMULTIRANGEOID); bool have_anycompatible_nonarray = (rettype == ANYCOMPATIBLENONARRAYOID); bool have_anycompatible_array = (rettype == ANYCOMPATIBLEARRAYOID); bool have_anycompatible_range = (rettype == ANYCOMPATIBLERANGEOID); bool have_anycompatible_multirange = (rettype == ANYCOMPATIBLEMULTIRANGEOID); int n_poly_args = 0; /* this counts all family-1 arguments */ int n_anycompatible_args = 0; /* this counts only non-unknowns */
Oid anycompatible_actual_types[FUNC_MAX_ARGS];
/* *Loopthroughtheargumentstoseeifwehaveanythatarepolymorphic. *Ifso,requiretheactualtypestobeconsistent.
*/
Assert(nargs <= FUNC_MAX_ARGS); for (int j = 0; j < nargs; j++)
{
Oid decl_type = declared_arg_types[j];
Oid actual_type = actual_arg_types[j];
if (decl_type == ANYELEMENTOID ||
decl_type == ANYNONARRAYOID ||
decl_type == ANYENUMOID)
{
n_poly_args++; if (decl_type == ANYNONARRAYOID)
have_anynonarray = true; elseif (decl_type == ANYENUMOID)
have_anyenum = true; if (actual_type == UNKNOWNOID)
{
have_poly_unknowns = true; continue;
} if (allow_poly && decl_type == actual_type) continue; /* no new information here */ if (OidIsValid(elem_typeid) && actual_type != elem_typeid)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("arguments declared \"%s\" are not all alike", "anyelement"),
errdetail("%s versus %s",
format_type_be(elem_typeid),
format_type_be(actual_type))));
elem_typeid = actual_type;
} elseif (decl_type == ANYARRAYOID)
{
n_poly_args++; if (actual_type == UNKNOWNOID)
{
have_poly_unknowns = true; continue;
} 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),
errmsg("arguments declared \"%s\" are not all alike", "anyarray"),
errdetail("%s versus %s",
format_type_be(array_typeid),
format_type_be(actual_type))));
array_typeid = actual_type;
} elseif (decl_type == ANYRANGEOID)
{
n_poly_args++; if (actual_type == UNKNOWNOID)
{
have_poly_unknowns = true; continue;
} if (allow_poly && decl_type == actual_type) continue; /* no new information here */
actual_type = getBaseType(actual_type); /* flatten domains */ if (OidIsValid(range_typeid) && actual_type != range_typeid)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("arguments declared \"%s\" are not all alike", "anyrange"),
errdetail("%s versus %s",
format_type_be(range_typeid),
format_type_be(actual_type))));
range_typeid = actual_type;
} elseif (decl_type == ANYMULTIRANGEOID)
{
n_poly_args++;
have_anymultirange = true; if (actual_type == UNKNOWNOID)
{
have_poly_unknowns = true; continue;
} if (allow_poly && decl_type == actual_type) continue; /* no new information here */
actual_type = getBaseType(actual_type); /* flatten domains */ if (OidIsValid(multirange_typeid) && actual_type != multirange_typeid)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("arguments declared \"%s\" are not all alike", "anymultirange"),
errdetail("%s versus %s",
format_type_be(multirange_typeid),
format_type_be(actual_type))));
multirange_typeid = actual_type;
} elseif (decl_type == ANYCOMPATIBLEOID ||
decl_type == ANYCOMPATIBLENONARRAYOID)
{
have_poly_anycompatible = true; if (decl_type == ANYCOMPATIBLENONARRAYOID)
have_anycompatible_nonarray = true; if (actual_type == UNKNOWNOID) continue; if (allow_poly && decl_type == actual_type) continue; /* no new information here */ /* collect the actual types of non-unknown COMPATIBLE args */
anycompatible_actual_types[n_anycompatible_args++] = actual_type;
} elseif (decl_type == ANYCOMPATIBLEARRAYOID)
{
Oid anycompatible_elem_type;
have_poly_anycompatible = true;
have_anycompatible_array = true; if (actual_type == UNKNOWNOID) continue; if (allow_poly && decl_type == actual_type) continue; /* no new information here */
actual_type = getBaseType(actual_type); /* flatten domains */
anycompatible_elem_type = get_element_type(actual_type); if (!OidIsValid(anycompatible_elem_type))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("argument declared %s is not an array but type %s", "anycompatiblearray",
format_type_be(actual_type)))); /* collect the element type for common-supertype choice */
anycompatible_actual_types[n_anycompatible_args++] = anycompatible_elem_type;
} elseif (decl_type == ANYCOMPATIBLERANGEOID)
{
have_poly_anycompatible = true;
have_anycompatible_range = true; if (actual_type == UNKNOWNOID) continue; if (allow_poly && decl_type == actual_type) continue; /* no new information here */
actual_type = getBaseType(actual_type); /* flatten domains */ if (OidIsValid(anycompatible_range_typeid))
{ /* All ANYCOMPATIBLERANGE arguments must be the same type */ if (anycompatible_range_typeid != actual_type)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("arguments declared \"%s\" are not all alike", "anycompatiblerange"),
errdetail("%s versus %s",
format_type_be(anycompatible_range_typeid),
format_type_be(actual_type))));
} else
{
anycompatible_range_typeid = actual_type;
anycompatible_range_typelem = get_range_subtype(actual_type); if (!OidIsValid(anycompatible_range_typelem))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("argument declared %s is not a range type but type %s", "anycompatiblerange",
format_type_be(actual_type)))); /* collect the subtype for common-supertype choice */
anycompatible_actual_types[n_anycompatible_args++] = anycompatible_range_typelem;
}
} elseif (decl_type == ANYCOMPATIBLEMULTIRANGEOID)
{
have_poly_anycompatible = true;
have_anycompatible_multirange = true; if (actual_type == UNKNOWNOID) continue; if (allow_poly && decl_type == actual_type) continue; /* no new information here */
actual_type = getBaseType(actual_type); /* flatten domains */ if (OidIsValid(anycompatible_multirange_typeid))
{ /* All ANYCOMPATIBLEMULTIRANGE arguments must be the same type */ if (anycompatible_multirange_typeid != actual_type)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("arguments declared \"%s\" are not all alike", "anycompatiblemultirange"),
errdetail("%s versus %s",
format_type_be(anycompatible_multirange_typeid),
format_type_be(actual_type))));
} else
{
anycompatible_multirange_typeid = actual_type;
anycompatible_multirange_typelem = get_multirange_range(actual_type); if (!OidIsValid(anycompatible_multirange_typelem))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("argument declared %s is not a multirange type but type %s", "anycompatiblemultirange",
format_type_be(actual_type)))); /* we'll consider the subtype below */
}
}
}
/* Check matching of family-1 polymorphic arguments, if any */ if (n_poly_args)
{ /* Get the element type based on the array type, if we have one */ if (OidIsValid(array_typeid))
{
Oid array_typelem;
if (array_typeid == ANYARRAYOID)
{ /* *SpecialcaseformatchingANYARRAYinputtoanANYARRAY *argument:allowitiffnootherargumentsarefamily-1 *polymorphics(otherwisewecouldn'tbesurewhetherthe *arrayelementtypematchesup)andtheresulttypedoesn't *requireustoinferaspecificelementtype.
*/ if (n_poly_args != 1 ||
(rettype != ANYARRAYOID &&
IsPolymorphicTypeFamily1(rettype)))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("cannot determine element type of \"anyarray\" argument")));
array_typelem = ANYELEMENTOID;
} else
{
array_typelem = get_element_type(array_typeid); if (!OidIsValid(array_typelem))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("argument declared %s is not an array but type %s", "anyarray", format_type_be(array_typeid))));
}
if (!OidIsValid(elem_typeid))
{ /* *ifwedon'thaveanelementtypeyet,usetheonewejust *got
*/
elem_typeid = array_typelem;
} elseif (array_typelem != elem_typeid)
{ /* otherwise, they better match */
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("argument declared %s is not consistent with argument declared %s", "anyarray", "anyelement"),
errdetail("%s versus %s",
format_type_be(array_typeid),
format_type_be(elem_typeid))));
}
}
/* Deduce range type from multirange type, or vice versa */ if (OidIsValid(multirange_typeid))
{
Oid multirange_typelem;
multirange_typelem = get_multirange_range(multirange_typeid); if (!OidIsValid(multirange_typelem))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("argument declared %s is not a multirange type but type %s", "anymultirange",
format_type_be(multirange_typeid))));
if (!OidIsValid(range_typeid))
{ /* if we don't have a range type yet, use the one we just got */
range_typeid = multirange_typelem;
} elseif (multirange_typelem != range_typeid)
{ /* otherwise, they better match */
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("argument declared %s is not consistent with argument declared %s", "anymultirange", "anyrange"),
errdetail("%s versus %s",
format_type_be(multirange_typeid),
format_type_be(range_typeid))));
}
} elseif (have_anymultirange && OidIsValid(range_typeid))
{
multirange_typeid = get_range_multirange(range_typeid); /* We'll complain below if that didn't work */
}
/* Get the element type based on the range type, if we have one */ if (OidIsValid(range_typeid))
{
Oid range_typelem;
range_typelem = get_range_subtype(range_typeid); if (!OidIsValid(range_typelem))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("argument declared %s is not a range type but type %s", "anyrange",
format_type_be(range_typeid))));
if (!OidIsValid(elem_typeid))
{ /* *ifwedon'thaveanelementtypeyet,usetheonewejust *got
*/
elem_typeid = range_typelem;
} elseif (range_typelem != elem_typeid)
{ /* otherwise, they better match */
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("argument declared %s is not consistent with argument declared %s", "anyrange", "anyelement"),
errdetail("%s versus %s",
format_type_be(range_typeid),
format_type_be(elem_typeid))));
}
}
if (!OidIsValid(elem_typeid))
{ if (allow_poly)
{
elem_typeid = ANYELEMENTOID;
array_typeid = ANYARRAYOID;
range_typeid = ANYRANGEOID;
multirange_typeid = ANYMULTIRANGEOID;
} else
{ /* *Onlywaytogethereisifallthefamily-1polymorphic *argumentshaveUNKNOWNinputs.
*/
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("could not determine polymorphic type because input has type %s", "unknown")));
}
}
if (have_anynonarray && elem_typeid != ANYELEMENTOID)
{ /* *requiretheelementtypetonotbeanarrayordomainover *array
*/ if (type_is_array_domain(elem_typeid))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("type matched to anynonarray is an array type: %s",
format_type_be(elem_typeid))));
}
if (have_anyenum && elem_typeid != ANYELEMENTOID)
{ /* 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))));
}
}
/* Check matching of family-2 polymorphic arguments, if any */ if (have_poly_anycompatible)
{ /* Deduce range type from multirange type, or vice versa */ if (OidIsValid(anycompatible_multirange_typeid))
{ if (OidIsValid(anycompatible_range_typeid))
{ if (anycompatible_multirange_typelem !=
anycompatible_range_typeid)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("argument declared %s is not consistent with argument declared %s", "anycompatiblemultirange", "anycompatiblerange"),
errdetail("%s versus %s",
format_type_be(anycompatible_multirange_typeid),
format_type_be(anycompatible_range_typeid))));
} else
{
anycompatible_range_typeid = anycompatible_multirange_typelem;
anycompatible_range_typelem = get_range_subtype(anycompatible_range_typeid); if (!OidIsValid(anycompatible_range_typelem))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("argument declared %s is not a multirange type but type %s", "anycompatiblemultirange",
format_type_be(anycompatible_multirange_typeid)))); /* this enables element type matching check below */
have_anycompatible_range = true; /* collect the subtype for common-supertype choice */
anycompatible_actual_types[n_anycompatible_args++] =
anycompatible_range_typelem;
}
}
else if (have_anycompatible_multirange &&
OidIsValid(anycompatible_range_typeid))
{
anycompatible_multirange_typeid = get_range_multirange(anycompatible_range_typeid);
/* We'll complain below if that didn't work */
}
if (n_anycompatible_args > 0)
{
anycompatible_typeid =
select_common_type_from_oids(n_anycompatible_args,
anycompatible_actual_types,
false);
/* We have to verify that the selected type actually works */
if (!verify_common_type_from_oids(anycompatible_typeid,
n_anycompatible_args,
anycompatible_actual_types))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("arguments of anycompatible family cannot be cast to a common type")));
if (have_anycompatible_array)
{
anycompatible_array_typeid = get_array_type(anycompatible_typeid);
if (!OidIsValid(anycompatible_array_typeid))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("could not find array type for data type %s",
format_type_be(anycompatible_typeid))));
}
if (have_anycompatible_range)
{
/* we can't infer a range type from the others */
if (!OidIsValid(anycompatible_range_typeid))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("could not determine polymorphic type %s because input has type %s",
"anycompatiblerange", "unknown")));
/*
* the anycompatible type must exactly match the range element
* type
*/
if (anycompatible_range_typelem != anycompatible_typeid)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("anycompatiblerange type %s does not match anycompatible type %s",
format_type_be(anycompatible_range_typeid),
format_type_be(anycompatible_typeid))));
}
if (have_anycompatible_multirange)
{
/* we can't infer a multirange type from the others */
if (!OidIsValid(anycompatible_multirange_typeid))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("could not determine polymorphic type %s because input has type %s",
"anycompatiblemultirange", "unknown")));
/*
* the anycompatible type must exactly match the multirange
* element type
*/
if (anycompatible_range_typelem != anycompatible_typeid)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("anycompatiblemultirange type %s does not match anycompatible type %s",
format_type_be(anycompatible_multirange_typeid),
format_type_be(anycompatible_typeid))));
}
if (have_anycompatible_nonarray)
{
/*
* require the element type to not be an array or domain over
* array
*/
if (type_is_array_domain(anycompatible_typeid))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("type matched to anycompatiblenonarray is an array type: %s",
format_type_be(anycompatible_typeid))));
}
}
else
{
if (allow_poly)
{
anycompatible_typeid = ANYCOMPATIBLEOID;
anycompatible_array_typeid = ANYCOMPATIBLEARRAYOID;
anycompatible_range_typeid = ANYCOMPATIBLERANGEOID;
anycompatible_multirange_typeid = ANYCOMPATIBLEMULTIRANGEOID;
}
else
{
/*
* Only way to get here is if all the family-2 polymorphic
* arguments have UNKNOWN inputs. Resolve to TEXT as
* select_common_type() would do. That doesn't license us to
* use TEXTRANGE or TEXTMULTIRANGE, though.
*/
anycompatible_typeid = TEXTOID;
anycompatible_array_typeid = TEXTARRAYOID;
if (have_anycompatible_range)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("could not determine polymorphic type %s because input has type %s",
"anycompatiblerange", "unknown")));
if (have_anycompatible_multirange)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("could not determine polymorphic type %s because input has type %s",
"anycompatiblemultirange", "unknown")));
}
}
/* replace family-2 polymorphic types by selected types */
for (int j = 0; j < nargs; j++)
{
Oid decl_type = declared_arg_types[j];
if (decl_type == ANYCOMPATIBLEOID ||
decl_type == ANYCOMPATIBLENONARRAYOID)
declared_arg_types[j] = anycompatible_typeid;
else if (decl_type == ANYCOMPATIBLEARRAYOID)
declared_arg_types[j] = anycompatible_array_typeid;
else if (decl_type == ANYCOMPATIBLERANGEOID)
declared_arg_types[j] = anycompatible_range_typeid;
else if (decl_type == ANYCOMPATIBLEMULTIRANGEOID)
declared_arg_types[j] = anycompatible_multirange_typeid;
}
}
/*
* If we had any UNKNOWN inputs for family-1 polymorphic arguments,
* re-scan to assign correct types to them.
*
* Note: we don't have to consider unknown inputs that were matched to
* family-2 polymorphic arguments, because we forcibly updated their
* declared_arg_types[] positions just above.
*/
if (have_poly_unknowns)
{
for (int j = 0; j < nargs; j++)
{
Oid decl_type = declared_arg_types[j];
Oid actual_type = actual_arg_types[j];
if (actual_type != UNKNOWNOID)
continue;
if (decl_type == ANYELEMENTOID ||
decl_type == ANYNONARRAYOID ||
decl_type == ANYENUMOID)
declared_arg_types[j] = elem_typeid;
else if (decl_type == ANYARRAYOID)
{
if (!OidIsValid(array_typeid))
{
array_typeid = get_array_type(elem_typeid);
if (!OidIsValid(array_typeid))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("could not find array type for data type %s",
format_type_be(elem_typeid))));
}
declared_arg_types[j] = array_typeid;
}
else if (decl_type == ANYRANGEOID)
{
if (!OidIsValid(range_typeid))
{
/* we can't infer a range type from the others */
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("could not determine polymorphic type %s because input has type %s",
"anyrange", "unknown")));
}
declared_arg_types[j] = range_typeid;
}
else if (decl_type == ANYMULTIRANGEOID)
{
if (!OidIsValid(multirange_typeid))
{
/* we can't infer a multirange type from the others */
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("could not determine polymorphic type %s because input has type %s",
"anymultirange", "unknown")));
}
declared_arg_types[j] = multirange_typeid;
}
}
}
/* if we return ANYELEMENT use the appropriate argument type */
if (rettype == ANYELEMENTOID ||
rettype == ANYNONARRAYOID ||
rettype == ANYENUMOID)
return elem_typeid;
/* if we return ANYARRAY use the appropriate argument type */
if (rettype == ANYARRAYOID)
{
if (!OidIsValid(array_typeid))
{
array_typeid = get_array_type(elem_typeid);
if (!OidIsValid(array_typeid))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("could not find array type for data type %s",
format_type_be(elem_typeid))));
}
return array_typeid;
}
/* if we return ANYRANGE use the appropriate argument type */
if (rettype == ANYRANGEOID)
{
/* this error is unreachable if the function signature is valid: */
if (!OidIsValid(range_typeid))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg_internal("could not determine polymorphic type %s because input has type %s",
"anyrange", "unknown")));
return range_typeid;
}
/* if we return ANYMULTIRANGE use the appropriate argument type */
if (rettype == ANYMULTIRANGEOID)
{
/* this error is unreachable if the function signature is valid: */
if (!OidIsValid(multirange_typeid))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg_internal("could not determine polymorphic type %s because input has type %s",
"anymultirange", "unknown")));
return multirange_typeid;
}
/* if we return ANYCOMPATIBLE use the appropriate type */
if (rettype == ANYCOMPATIBLEOID ||
rettype == ANYCOMPATIBLENONARRAYOID)
{
/* this error is unreachable if the function signature is valid: */
if (!OidIsValid(anycompatible_typeid))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg_internal("could not identify anycompatible type")));
return anycompatible_typeid;
}
/* if we return ANYCOMPATIBLEARRAY use the appropriate type */
if (rettype == ANYCOMPATIBLEARRAYOID)
{
/* this error is unreachable if the function signature is valid: */
if (!OidIsValid(anycompatible_array_typeid))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg_internal("could not identify anycompatiblearray type")));
return anycompatible_array_typeid;
}
/* if we return ANYCOMPATIBLERANGE use the appropriate argument type */
if (rettype == ANYCOMPATIBLERANGEOID)
{
/* this error is unreachable if the function signature is valid: */
if (!OidIsValid(anycompatible_range_typeid))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg_internal("could not identify anycompatiblerange type")));
return anycompatible_range_typeid;
}
/* if we return ANYCOMPATIBLEMULTIRANGE use the appropriate argument type */
if (rettype == ANYCOMPATIBLEMULTIRANGEOID)
{
/* this error is unreachable if the function signature is valid: */
if (!OidIsValid(anycompatible_multirange_typeid))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg_internal("could not identify anycompatiblemultirange type")));
return anycompatible_multirange_typeid;
}
/* we don't return a generic type; send back the original return type */
return rettype;
}
/*
* check_valid_polymorphic_signature()
* Is a proposed function signature valid per polymorphism rules?
*
* Returns NULL if the signature is valid (either ret_type is not polymorphic,
* or it can be deduced from the given declared argument types). Otherwise,
* returns a palloc'd, already translated errdetail string saying why not.
*/
char *
check_valid_polymorphic_signature(Oid ret_type,
const Oid *declared_arg_types,
int nargs)
{
if (ret_type == ANYRANGEOID || ret_type == ANYMULTIRANGEOID)
{
/*
* ANYRANGE and ANYMULTIRANGE require an ANYRANGE or ANYMULTIRANGE
* input, else we can't tell which of several range types with the
* same element type to use.
*/
for (int i = 0; i < nargs; i++)
{
if (declared_arg_types[i] == ANYRANGEOID ||
declared_arg_types[i] == ANYMULTIRANGEOID)
return NULL; /* OK */
}
return psprintf(_("A result of type %s requires at least one input of type anyrange or anymultirange."),
format_type_be(ret_type));
}
else if (ret_type == ANYCOMPATIBLERANGEOID || ret_type == ANYCOMPATIBLEMULTIRANGEOID)
{
/*
* ANYCOMPATIBLERANGE and ANYCOMPATIBLEMULTIRANGE require an
* ANYCOMPATIBLERANGE or ANYCOMPATIBLEMULTIRANGE input, else we can't
* tell which of several range types with the same element type to
* use.
*/
for (int i = 0; i < nargs; i++)
{
if (declared_arg_types[i] == ANYCOMPATIBLERANGEOID ||
declared_arg_types[i] == ANYCOMPATIBLEMULTIRANGEOID)
return NULL; /* OK */
}
return psprintf(_("A result of type %s requires at least one input of type anycompatiblerange or anycompatiblemultirange."),
format_type_be(ret_type));
}
else if (IsPolymorphicTypeFamily1(ret_type))
{
/* Otherwise, any family-1 type can be deduced from any other */
for (int i = 0; i < nargs; i++)
{
if (IsPolymorphicTypeFamily1(declared_arg_types[i]))
return NULL; /* OK */
}
/* Keep this list in sync with IsPolymorphicTypeFamily1! */
return psprintf(_("A result of type %s requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange."),
format_type_be(ret_type));
}
else if (IsPolymorphicTypeFamily2(ret_type))
{
/* Otherwise, any family-2 type can be deduced from any other */
for (int i = 0; i < nargs; i++)
{
if (IsPolymorphicTypeFamily2(declared_arg_types[i]))
return NULL; /* OK */
}
/* Keep this list in sync with IsPolymorphicTypeFamily2! */
return psprintf(_("A result of type %s requires at least one input of type anycompatible, anycompatiblearray, anycompatiblenonarray, anycompatiblerange, or anycompatiblemultirange."),
format_type_be(ret_type));
}
else
return NULL; /* OK, ret_type is not polymorphic */
}
/*
* check_valid_internal_signature()
* Is a proposed function signature valid per INTERNAL safety rules?
*
* Returns NULL if OK, or a suitable error message if ret_type is INTERNAL but
* none of the declared arg types are. (It's unsafe to create such a function
* since it would allow invocation of INTERNAL-consuming functions directly
* from SQL.) It's overkill to return the error detail message, since there
* is only one possibility, but we do it like this to keep the API similar to
* check_valid_polymorphic_signature().
*/
char *
check_valid_internal_signature(Oid ret_type,
const Oid *declared_arg_types,
int nargs)
{
if (ret_type == INTERNALOID)
{
for (int i = 0; i < nargs; i++)
{
if (declared_arg_types[i] == ret_type)
return NULL; /* OK */
}
return pstrdup(_("A result of type internal requires at least one input of type internal."));
}
else
return NULL; /* OK, ret_type is not INTERNAL */
}
/* TypeCategory()
* Assign a category to the specified type OID.
*
* NB: this must not return TYPCATEGORY_INVALID.
*/
TYPCATEGORY
TypeCategory(Oid type)
{
char typcategory;
bool typispreferred;
/* IsPreferredType()
* Check if this type is a preferred type for the given category.
*
* If category is TYPCATEGORY_INVALID, then we'll return true for preferred
* types of any category; otherwise, only for preferred types of that
* category.
*/
bool
IsPreferredType(TYPCATEGORY category, Oid type)
{
char typcategory;
bool typispreferred;
/* IsBinaryCoercible()
* Check if srctype is binary-coercible to targettype.
*
* This notion allows us to cheat and directly exchange values without
* going through the trouble of calling a conversion function. Note that
* in general, this should only be an implementation shortcut. Before 7.4,
* this was also used as a heuristic for resolving overloaded functions and
* operators, but that's basically a bad idea.
*
* As of 7.3, binary coercibility isn't hardwired into the code anymore.
* We consider two types binary-coercible if there is an implicitly
* invokable, no-function-needed pg_cast entry. Also, a domain is always
* binary-coercible to its base type, though *not* vice versa (in the other
* direction, one must apply domain constraint checks before accepting the
* value as legitimate). We also need to special-case various polymorphic
* types.
*
* This function replaces IsBinaryCompatible(), which was an inherently
* symmetric test. Since the pg_cast entries aren't necessarily symmetric,
* the order of the operands is now significant.
*/
bool
IsBinaryCoercible(Oid srctype, Oid targettype)
{
Oid castoid;
/* IsBinaryCoercibleWithCast()
* Check if srctype is binary-coercible to targettype.
*
* This variant also returns the OID of the pg_cast entry if one is involved.
* *castoid is set to InvalidOid if no binary-coercible cast exists, or if
* there is a hard-wired rule for it rather than a pg_cast entry.
*/
bool
IsBinaryCoercibleWithCast(Oid srctype, Oid targettype,
Oid *castoid)
{
HeapTuple tuple;
Form_pg_cast castForm;
bool result;
*castoid = InvalidOid;
/* Fast path if same type */
if (srctype == targettype)
return true;
/* Anything is coercible to ANY or ANYELEMENT or ANYCOMPATIBLE */
if (targettype == ANYOID || targettype == ANYELEMENTOID ||
targettype == ANYCOMPATIBLEOID)
return true;
/* If srctype is a domain, reduce to its base type */
if (OidIsValid(srctype))
srctype = getBaseType(srctype);
/* Somewhat-fast path for domain -> base type case */
if (srctype == targettype)
return true;
/* Also accept any array type as coercible to ANY[COMPATIBLE]ARRAY */
if (targettype == ANYARRAYOID || targettype == ANYCOMPATIBLEARRAYOID)
if (type_is_array(srctype))
return true;
/* Also accept any non-array type as coercible to ANY[COMPATIBLE]NONARRAY */
if (targettype == ANYNONARRAYOID || targettype == ANYCOMPATIBLENONARRAYOID)
if (!type_is_array(srctype))
return true;
/* Also accept any enum type as coercible to ANYENUM */
if (targettype == ANYENUMOID)
if (type_is_enum(srctype))
return true;
/* Also accept any range type as coercible to ANY[COMPATIBLE]RANGE */
if (targettype == ANYRANGEOID || targettype == ANYCOMPATIBLERANGEOID)
if (type_is_range(srctype))
return true;
/* Also, any multirange type is coercible to ANY[COMPATIBLE]MULTIRANGE */
if (targettype == ANYMULTIRANGEOID || targettype == ANYCOMPATIBLEMULTIRANGEOID)
if (type_is_multirange(srctype))
return true;
/* Also accept any composite type as coercible to RECORD */
if (targettype == RECORDOID)
if (ISCOMPLEX(srctype))
return true;
/* Also accept any composite array type as coercible to RECORD[] */
if (targettype == RECORDARRAYOID)
if (is_complex_array(srctype))
return true;
/* Else look in pg_cast */
tuple = SearchSysCache2(CASTSOURCETARGET,
ObjectIdGetDatum(srctype),
ObjectIdGetDatum(targettype));
if (!HeapTupleIsValid(tuple))
return false; /* no cast */
castForm = (Form_pg_cast) GETSTRUCT(tuple);
result = (castForm->castmethod == COERCION_METHOD_BINARY &&
castForm->castcontext == COERCION_CODE_IMPLICIT);
if (result)
*castoid = castForm->oid;
ReleaseSysCache(tuple);
return result;
}
/*
* find_coercion_pathway
* Look for a coercion pathway between two types.
*
* Currently, this deals only with scalar-type cases; it does not consider
* polymorphic types nor casts between composite types. (Perhaps fold
* those in someday?)
*
* ccontext determines the set of available casts.
*
* The possible result codes are:
* COERCION_PATH_NONE: failed to find any coercion pathway
* *funcid is set to InvalidOid
* COERCION_PATH_FUNC: apply the coercion function returned in *funcid
* COERCION_PATH_RELABELTYPE: binary-compatible cast, no function needed
* *funcid is set to InvalidOid
* COERCION_PATH_ARRAYCOERCE: need an ArrayCoerceExpr node
* *funcid is set to InvalidOid
* COERCION_PATH_COERCEVIAIO: need a CoerceViaIO node
* *funcid is set to InvalidOid
*
* Note: COERCION_PATH_RELABELTYPE does not necessarily mean that no work is
* needed to do the coercion; if the target is a domain then we may need to
* apply domain constraint checking. If you want to check for a zero-effort
* conversion then use IsBinaryCoercible().
*/
CoercionPathType
find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId,
CoercionContext ccontext,
Oid *funcid)
{
CoercionPathType result = COERCION_PATH_NONE;
HeapTuple tuple;
*funcid = InvalidOid;
/* Perhaps the types are domains; if so, look at their base types */
if (OidIsValid(sourceTypeId))
sourceTypeId = getBaseType(sourceTypeId);
if (OidIsValid(targetTypeId))
targetTypeId = getBaseType(targetTypeId);
/* Domains are always coercible to and from their base type */
if (sourceTypeId == targetTypeId)
return COERCION_PATH_RELABELTYPE;
/* Look in pg_cast */
tuple = SearchSysCache2(CASTSOURCETARGET,
ObjectIdGetDatum(sourceTypeId),
ObjectIdGetDatum(targetTypeId));
if (HeapTupleIsValid(tuple))
{
Form_pg_cast castForm = (Form_pg_cast) GETSTRUCT(tuple);
CoercionContext castcontext;
/* convert char value for castcontext to CoercionContext enum */
switch (castForm->castcontext)
{
case COERCION_CODE_IMPLICIT:
castcontext = COERCION_IMPLICIT;
break;
case COERCION_CODE_ASSIGNMENT:
castcontext = COERCION_ASSIGNMENT;
break;
case COERCION_CODE_EXPLICIT:
castcontext = COERCION_EXPLICIT;
break;
default:
elog(ERROR, "unrecognized castcontext: %d",
(int) castForm->castcontext);
castcontext = 0; /* keep compiler quiet */
break;
}
/* Rely on ordering of enum for correct behavior here */
if (ccontext >= castcontext)
{
switch (castForm->castmethod)
{
case COERCION_METHOD_FUNCTION:
result = COERCION_PATH_FUNC;
*funcid = castForm->castfunc;
break;
case COERCION_METHOD_INOUT:
result = COERCION_PATH_COERCEVIAIO;
break;
case COERCION_METHOD_BINARY:
result = COERCION_PATH_RELABELTYPE;
break;
default:
elog(ERROR, "unrecognized castmethod: %d",
(int) castForm->castmethod);
break;
}
}
ReleaseSysCache(tuple);
}
else
{
/*
* If there's no pg_cast entry, perhaps we are dealing with a pair of
* array types. If so, and if their element types have a conversion
* pathway, report that we can coerce with an ArrayCoerceExpr.
*
* Hack: disallow coercions to oidvector and int2vector, which
* otherwise tend to capture coercions that should go to "real" array
* types. We want those types to be considered "real" arrays for many
* purposes, but not this one. (Also, ArrayCoerceExpr isn't
* guaranteed to produce an output that meets the restrictions of
* these datatypes, such as being 1-dimensional.)
*/
if (targetTypeId != OIDVECTOROID && targetTypeId != INT2VECTOROID)
{
Oid targetElem;
Oid sourceElem;
if ((targetElem = get_element_type(targetTypeId)) != InvalidOid &&
(sourceElem = get_element_type(sourceTypeId)) != InvalidOid)
{
CoercionPathType elempathtype;
Oid elemfuncid;
elempathtype = find_coercion_pathway(targetElem,
sourceElem,
ccontext,
&elemfuncid);
if (elempathtype != COERCION_PATH_NONE)
{
result = COERCION_PATH_ARRAYCOERCE;
}
}
}
/*
* If we still haven't found a possibility, consider automatic casting
* using I/O functions. We allow assignment casts to string types and
* explicit casts from string types to be handled this way. (The
* CoerceViaIO mechanism is a lot more general than that, but this is
* all we want to allow in the absence of a pg_cast entry.) It would
* probably be better to insist on explicit casts in both directions,
* but this is a compromise to preserve something of the pre-8.3
* behavior that many types had implicit (yipes!) casts to text.
*/
if (result == COERCION_PATH_NONE)
{
if (ccontext >= COERCION_ASSIGNMENT &&
TypeCategory(targetTypeId) == TYPCATEGORY_STRING)
result = COERCION_PATH_COERCEVIAIO;
else if (ccontext >= COERCION_EXPLICIT &&
TypeCategory(sourceTypeId) == TYPCATEGORY_STRING)
result = COERCION_PATH_COERCEVIAIO;
}
}
/*
* When parsing PL/pgSQL assignments, allow an I/O cast to be used
* whenever no normal coercion is available.
*/
if (result == COERCION_PATH_NONE &&
ccontext == COERCION_PLPGSQL)
result = COERCION_PATH_COERCEVIAIO;
return result;
}
/*
* find_typmod_coercion_function -- does the given type need length coercion?
*
* If the target type possesses a pg_cast function from itself to itself,
* it must need length coercion.
*
* "bpchar" (ie, char(N)) and "numeric" are examples of such types.
*
* If the given type is a varlena array type, we do not look for a coercion
* function associated directly with the array type, but instead look for
* one associated with the element type. An ArrayCoerceExpr node must be
* used to apply such a function. (Note: currently, it's pointless to
* return the funcid in this case, because it'll just get looked up again
* in the recursive construction of the ArrayCoerceExpr's elemexpr.)
*
* We use the same result enum as find_coercion_pathway, but the only possible
* result codes are:
* COERCION_PATH_NONE: no length coercion needed
* COERCION_PATH_FUNC: apply the function returned in *funcid
* COERCION_PATH_ARRAYCOERCE: apply the function using ArrayCoerceExpr
*/
CoercionPathType
find_typmod_coercion_function(Oid typeId,
Oid *funcid)
{
CoercionPathType result;
Type targetType;
Form_pg_type typeForm;
HeapTuple tuple;
*funcid = InvalidOid;
result = COERCION_PATH_FUNC;
/* Check for a "true" array type */
if (IsTrueArrayType(typeForm))
{
/* Yes, switch our attention to the element type */
typeId = typeForm->typelem;
result = COERCION_PATH_ARRAYCOERCE;
}
ReleaseSysCache(targetType);
/* Look in pg_cast */
tuple = SearchSysCache2(CASTSOURCETARGET,
ObjectIdGetDatum(typeId),
ObjectIdGetDatum(typeId));
if (HeapTupleIsValid(tuple))
{
Form_pg_cast castForm = (Form_pg_cast) GETSTRUCT(tuple);
if (!OidIsValid(*funcid))
result = COERCION_PATH_NONE;
return result;
}
/*
* is_complex_array
* Is this type an array of composite?
*
* Note: this will not return true for record[]; check for RECORDARRAYOID
* separately if needed.
*/
static bool
is_complex_array(Oid typid)
{
Oid elemtype = get_element_type(typid);
/*
* Check whether reltypeId is the row type of a typed table of type
* reloftypeId, or is a domain over such a row type. (This is conceptually
* similar to the subtype relationship checked by typeInheritsFrom().)
*/
static bool
typeIsOfTypedTable(Oid reltypeId, Oid reloftypeId)
{
Oid relid = typeOrDomainTypeRelid(reltypeId);
bool result = false;
if (relid)
{
HeapTuple tp;
Form_pg_class reltup;
tp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
if (!HeapTupleIsValid(tp))
elog(ERROR, "cache lookup failed for relation %u", relid);
reltup = (Form_pg_class) GETSTRUCT(tp);
if (reltup->reloftype == reloftypeId)
result = true;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.