/* *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;
}
} elseif (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")));
/* *theanycompatibletypemustexactlymatchtherangeelement *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")));
/* *theanycompatibletypemustexactlymatchthemultirange *elementtype
*/ 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)
{ /* *requiretheelementtypetonotbeanarrayordomainover *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
{ /* *Onlywaytogethereisifallthefamily-2polymorphic *argumentshaveUNKNOWNinputs.ResolvetoTEXTas *select_common_type()woulddo.Thatdoesn'tlicenseusto *useTEXTRANGEorTEXTMULTIRANGE,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];
/* *IfwehadanyUNKNOWNinputsforfamily-1polymorphicarguments, *re-scantoassigncorrecttypestothem. * *Note:wedon'thavetoconsiderunknowninputsthatwerematchedto *family-2polymorphicarguments,becauseweforciblyupdatedtheir *declared_arg_types[]positionsjustabove.
*/ 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; elseif (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;
} elseif (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;
} elseif (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() *Isaproposedfunctionsignaturevalidperpolymorphismrules? * *ReturnsNULLifthesignatureisvalid(eitherret_typeisnotpolymorphic, *oritcanbededucedfromthegivendeclaredargumenttypes).Otherwise, *returnsapalloc'd,alreadytranslatederrdetailstringsayingwhynot.
*/ char *
check_valid_polymorphic_signature(Oid ret_type, const Oid *declared_arg_types, int nargs)
{ if (ret_type == ANYRANGEOID || ret_type == ANYMULTIRANGEOID)
{ /* *ANYRANGEandANYMULTIRANGErequireanANYRANGEorANYMULTIRANGE *input,elsewecan'ttellwhichofseveralrangetypeswiththe *sameelementtypetouse.
*/ 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));
} elseif (ret_type == ANYCOMPATIBLERANGEOID || ret_type == ANYCOMPATIBLEMULTIRANGEOID)
{ /* *ANYCOMPATIBLERANGEandANYCOMPATIBLEMULTIRANGErequirean *ANYCOMPATIBLERANGEorANYCOMPATIBLEMULTIRANGEinput,elsewecan't *tellwhichofseveralrangetypeswiththesameelementtypeto *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));
} elseif (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));
} elseif (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() *IsaproposedfunctionsignaturevalidperINTERNALsafetyrules? * *ReturnsNULLifOK,orasuitableerrormessageifret_typeisINTERNALbut *noneofthedeclaredargtypesare.(It'sunsafetocreatesuchafunction *sinceitwouldallowinvocationofINTERNAL-consumingfunctionsdirectly *fromSQL.)It'soverkilltoreturntheerrordetailmessage,sincethere *isonlyonepossibility,butwedoitlikethistokeeptheAPIsimilarto *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 */
}
/* Fast path if same type */ if (srctype == targettype) returntrue;
/* Anything is coercible to ANY or ANYELEMENT or ANYCOMPATIBLE */ if (targettype == ANYOID || targettype == ANYELEMENTOID ||
targettype == ANYCOMPATIBLEOID) returntrue;
/* 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) returntrue;
/* Also accept any array type as coercible to ANY[COMPATIBLE]ARRAY */ if (targettype == ANYARRAYOID || targettype == ANYCOMPATIBLEARRAYOID) if (type_is_array(srctype)) returntrue;
/* Also accept any non-array type as coercible to ANY[COMPATIBLE]NONARRAY */ if (targettype == ANYNONARRAYOID || targettype == ANYCOMPATIBLENONARRAYOID) if (!type_is_array(srctype)) returntrue;
/* Also accept any enum type as coercible to ANYENUM */ if (targettype == ANYENUMOID) if (type_is_enum(srctype)) returntrue;
/* Also accept any range type as coercible to ANY[COMPATIBLE]RANGE */ if (targettype == ANYRANGEOID || targettype == ANYCOMPATIBLERANGEOID) if (type_is_range(srctype)) returntrue;
/* Also, any multirange type is coercible to ANY[COMPATIBLE]MULTIRANGE */ if (targettype == ANYMULTIRANGEOID || targettype == ANYCOMPATIBLEMULTIRANGEOID) if (type_is_multirange(srctype)) returntrue;
/* Also accept any composite type as coercible to RECORD */ if (targettype == RECORDOID) if (ISCOMPLEX(srctype)) returntrue;
/* Also accept any composite array type as coercible to RECORD[] */ if (targettype == RECORDARRAYOID) if (is_complex_array(srctype)) returntrue;
/* Else look in pg_cast */
tuple = SearchSysCache2(CASTSOURCETARGET,
ObjectIdGetDatum(srctype),
ObjectIdGetDatum(targettype)); if (!HeapTupleIsValid(tuple)) returnfalse; /* no cast */
castForm = (Form_pg_cast) GETSTRUCT(tuple);
result = (castForm->castmethod == COERCION_METHOD_BINARY &&
castForm->castcontext == COERCION_CODE_IMPLICIT);
/* 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
{ /* *Ifthere'snopg_castentry,perhapswearedealingwithapairof *arraytypes.Ifso,andiftheirelementtypeshaveaconversion *pathway,reportthatwecancoercewithanArrayCoerceExpr. * *Hack:disallowcoercionstooidvectorandint2vector,which *otherwisetendtocapturecoercionsthatshouldgoto"real"array *types.Wewantthosetypestobeconsidered"real"arraysformany *purposes,butnotthisone.(Also,ArrayCoerceExprisn't *guaranteedtoproduceanoutputthatmeetstherestrictionsof *thesedatatypes,suchasbeing1-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;
}
}
}
/* *Ifwestillhaven'tfoundapossibility,considerautomaticcasting *usingI/Ofunctions.Weallowassignmentcaststostringtypesand *explicitcastsfromstringtypestobehandledthisway.(The *CoerceViaIOmechanismisalotmoregeneralthanthat,butthisis *allwewanttoallowintheabsenceofapg_castentry.)Itwould *probablybebettertoinsistonexplicitcastsinbothdirections, *butthisisacompromisetopreservesomethingofthepre-8.3 *behaviorthatmanytypeshadimplicit(yipes!)caststotext.
*/ if (result == COERCION_PATH_NONE)
{ if (ccontext >= COERCION_ASSIGNMENT &&
TypeCategory(targetTypeId) == TYPCATEGORY_STRING)
result = COERCION_PATH_COERCEVIAIO; elseif (ccontext >= COERCION_EXPLICIT &&
TypeCategory(sourceTypeId) == TYPCATEGORY_STRING)
result = COERCION_PATH_COERCEVIAIO;
}
}
/* *WhenparsingPL/pgSQLassignments,allowanI/Ocasttobeused *whenevernonormalcoercionisavailable.
*/ if (result == COERCION_PATH_NONE &&
ccontext == COERCION_PLPGSQL)
result = COERCION_PATH_COERCEVIAIO;
/* 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);
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.