1
0
mirror of https://github.com/postgres/postgres.git synced 2025-05-21 15:54:08 +03:00
postgres/src/backend/parser/parse_coerce.c
Tom Lane 2489d76c49 Make Vars be outer-join-aware.
Traditionally we used the same Var struct to represent the value
of a table column everywhere in parse and plan trees.  This choice
predates our support for SQL outer joins, and it's really a pretty
bad idea with outer joins, because the Var's value can depend on
where it is in the tree: it might go to NULL above an outer join.
So expression nodes that are equal() per equalfuncs.c might not
represent the same value, which is a huge correctness hazard for
the planner.

To improve this, decorate Var nodes with a bitmapset showing
which outer joins (identified by RTE indexes) may have nulled
them at the point in the parse tree where the Var appears.
This allows us to trust that equal() Vars represent the same value.
A certain amount of klugery is still needed to cope with cases
where we re-order two outer joins, but it's possible to make it
work without sacrificing that core principle.  PlaceHolderVars
receive similar decoration for the same reason.

In the planner, we include these outer join bitmapsets into the relids
that an expression is considered to depend on, and in consequence also
add outer-join relids to the relids of join RelOptInfos.  This allows
us to correctly perceive whether an expression can be calculated above
or below a particular outer join.

This change affects FDWs that want to plan foreign joins.  They *must*
follow suit when labeling foreign joins in order to match with the
core planner, but for many purposes (if postgres_fdw is any guide)
they'd prefer to consider only base relations within the join.
To support both requirements, redefine ForeignScan.fs_relids as
base+OJ relids, and add a new field fs_base_relids that's set up by
the core planner.

Large though it is, this commit just does the minimum necessary to
install the new mechanisms and get check-world passing again.
Follow-up patches will perform some cleanup.  (The README additions
and comments mention some stuff that will appear in the follow-up.)

Patch by me; thanks to Richard Guo for review.

Discussion: https://postgr.es/m/830269.1656693747@sss.pgh.pa.us
2023-01-30 13:16:20 -05:00

3368 lines
107 KiB
C

/*-------------------------------------------------------------------------
*
* parse_coerce.c
* handle type coercions/conversions for parser
*
* Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* src/backend/parser/parse_coerce.c
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "catalog/pg_cast.h"
#include "catalog/pg_class.h"
#include "catalog/pg_inherits.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "parser/parse_coerce.h"
#include "parser/parse_relation.h"
#include "parser/parse_type.h"
#include "utils/builtins.h"
#include "utils/datum.h" /* needed for datumIsEqual() */
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
#include "utils/typcache.h"
static Node *coerce_type_typmod(Node *node,
Oid targetTypeId, int32 targetTypMod,
CoercionContext ccontext, CoercionForm cformat,
int location,
bool hideInputCoercion);
static void hide_coercion_node(Node *node);
static Node *build_coercion_expression(Node *node,
CoercionPathType pathtype,
Oid funcId,
Oid targetTypeId, int32 targetTypMod,
CoercionContext ccontext, CoercionForm cformat,
int location);
static Node *coerce_record_to_complex(ParseState *pstate, Node *node,
Oid targetTypeId,
CoercionContext ccontext,
CoercionForm cformat,
int location);
static bool is_complex_array(Oid typid);
static bool typeIsOfTypedTable(Oid reltypeId, Oid reloftypeId);
/*
* coerce_to_target_type()
* Convert an expression to a target type and typmod.
*
* This is the general-purpose entry point for arbitrary type coercion
* operations. Direct use of the component operations can_coerce_type,
* coerce_type, and coerce_type_typmod should be restricted to special
* cases (eg, when the conversion is expected to succeed).
*
* Returns the possibly-transformed expression tree, or NULL if the type
* conversion is not possible. (We do this, rather than ereport'ing directly,
* so that callers can generate custom error messages indicating context.)
*
* pstate - parse state (can be NULL, see coerce_type)
* expr - input expression tree (already transformed by transformExpr)
* exprtype - result type of expr
* targettype - desired result type
* targettypmod - desired result typmod
* ccontext, cformat - context indicators to control coercions
* location - parse location of the coercion request, or -1 if unknown/implicit
*/
Node *
coerce_to_target_type(ParseState *pstate, Node *expr, Oid exprtype,
Oid targettype, int32 targettypmod,
CoercionContext ccontext,
CoercionForm cformat,
int location)
{
Node *result;
Node *origexpr;
if (!can_coerce_type(1, &exprtype, &targettype, ccontext))
return NULL;
/*
* If the input has a CollateExpr at the top, strip it off, perform the
* coercion, and put a new one back on. This is annoying since it
* duplicates logic in coerce_type, but if we don't do this then it's too
* hard to tell whether coerce_type actually changed anything, and we
* *must* know that to avoid possibly calling hide_coercion_node on
* something that wasn't generated by coerce_type. Note that if there are
* multiple stacked CollateExprs, we just discard all but the topmost.
* Also, if the target type isn't collatable, we discard the CollateExpr.
*/
origexpr = expr;
while (expr && IsA(expr, CollateExpr))
expr = (Node *) ((CollateExpr *) expr)->arg;
result = coerce_type(pstate, expr, exprtype,
targettype, targettypmod,
ccontext, cformat, location);
/*
* If the target is a fixed-length type, it may need a length coercion as
* well as a type coercion. If we find ourselves adding both, force the
* inner coercion node to implicit display form.
*/
result = coerce_type_typmod(result,
targettype, targettypmod,
ccontext, cformat, location,
(result != expr && !IsA(result, Const)));
if (expr != origexpr && type_is_collatable(targettype))
{
/* Reinstall top CollateExpr */
CollateExpr *coll = (CollateExpr *) origexpr;
CollateExpr *newcoll = makeNode(CollateExpr);
newcoll->arg = (Expr *) result;
newcoll->collOid = coll->collOid;
newcoll->location = coll->location;
result = (Node *) newcoll;
}
return result;
}
/*
* coerce_type()
* Convert an expression to a different type.
*
* The caller should already have determined that the coercion is possible;
* see can_coerce_type.
*
* Normally, no coercion to a typmod (length) is performed here. The caller
* must call coerce_type_typmod as well, if a typmod constraint is wanted.
* (But if the target type is a domain, it may internally contain a
* typmod constraint, which will be applied inside coerce_to_domain.)
* In some cases pg_cast specifies a type coercion function that also
* applies length conversion, and in those cases only, the result will
* already be properly coerced to the specified typmod.
*
* pstate is only used in the case that we are able to resolve the type of
* a previously UNKNOWN Param. It is okay to pass pstate = NULL if the
* caller does not want type information updated for Params.
*
* Note: this function must not modify the given expression tree, only add
* decoration on top of it. See transformSetOperationTree, for example.
*/
Node *
coerce_type(ParseState *pstate, Node *node,
Oid inputTypeId, Oid targetTypeId, int32 targetTypeMod,
CoercionContext ccontext, CoercionForm cformat, int location)
{
Node *result;
CoercionPathType pathtype;
Oid funcId;
if (targetTypeId == inputTypeId ||
node == NULL)
{
/* no conversion needed */
return node;
}
if (targetTypeId == ANYOID ||
targetTypeId == ANYELEMENTOID ||
targetTypeId == ANYNONARRAYOID ||
targetTypeId == ANYCOMPATIBLEOID ||
targetTypeId == ANYCOMPATIBLENONARRAYOID)
{
/*
* Assume can_coerce_type verified that implicit coercion is okay.
*
* Note: by returning the unmodified node here, we are saying that
* it's OK to treat an UNKNOWN constant as a valid input for a
* function accepting one of these pseudotypes. This should be all
* right, since an UNKNOWN value is still a perfectly valid Datum.
*
* NB: we do NOT want a RelabelType here: the exposed type of the
* function argument must be its actual type, not the polymorphic
* pseudotype.
*/
return node;
}
if (targetTypeId == ANYARRAYOID ||
targetTypeId == ANYENUMOID ||
targetTypeId == ANYRANGEOID ||
targetTypeId == ANYMULTIRANGEOID ||
targetTypeId == ANYCOMPATIBLEARRAYOID ||
targetTypeId == ANYCOMPATIBLERANGEOID ||
targetTypeId == ANYCOMPATIBLEMULTIRANGEOID)
{
/*
* Assume can_coerce_type verified that implicit coercion is okay.
*
* These cases are unlike the ones above because the exposed type of
* the argument must be an actual array, enum, range, or multirange
* type. In particular the argument must *not* be an UNKNOWN
* constant. If it is, we just fall through; below, we'll call the
* pseudotype's input function, which will produce an error. Also, if
* what we have is a domain over array, enum, range, or multirange, we
* have to relabel it to its base type.
*
* Note: currently, we can't actually see a domain-over-enum here,
* since the other functions in this file will not match such a
* parameter to ANYENUM. But that should get changed eventually.
*/
if (inputTypeId != UNKNOWNOID)
{
Oid baseTypeId = getBaseType(inputTypeId);
if (baseTypeId != inputTypeId)
{
RelabelType *r = makeRelabelType((Expr *) node,
baseTypeId, -1,
InvalidOid,
cformat);
r->location = location;
return (Node *) r;
}
/* Not a domain type, so return it as-is */
return node;
}
}
if (inputTypeId == UNKNOWNOID && IsA(node, Const))
{
/*
* Input is a string constant with previously undetermined type. Apply
* the target type's typinput function to it to produce a constant of
* the target type.
*
* NOTE: this case cannot be folded together with the other
* constant-input case, since the typinput function does not
* necessarily behave the same as a type conversion function. For
* example, int4's typinput function will reject "1.2", whereas
* float-to-int type conversion will round to integer.
*
* XXX if the typinput function is not immutable, we really ought to
* postpone evaluation of the function call until runtime. But there
* is no way to represent a typinput function call as an expression
* tree, because C-string values are not Datums. (XXX This *is*
* possible as of 7.3, do we want to do it?)
*/
Const *con = (Const *) node;
Const *newcon = makeNode(Const);
Oid baseTypeId;
int32 baseTypeMod;
int32 inputTypeMod;
Type baseType;
ParseCallbackState pcbstate;
/*
* If the target type is a domain, we want to call its base type's
* input routine, not domain_in(). This is to avoid premature failure
* when the domain applies a typmod: existing input routines follow
* implicit-coercion semantics for length checks, which is not always
* what we want here. The needed check will be applied properly
* inside coerce_to_domain().
*/
baseTypeMod = targetTypeMod;
baseTypeId = getBaseTypeAndTypmod(targetTypeId, &baseTypeMod);
/*
* For most types we pass typmod -1 to the input routine, because
* existing input routines follow implicit-coercion semantics for
* length checks, which is not always what we want here. Any length
* constraint will be applied later by our caller. An exception
* however is the INTERVAL type, for which we *must* pass the typmod
* or it won't be able to obey the bizarre SQL-spec input rules. (Ugly
* as sin, but so is this part of the spec...)
*/
if (baseTypeId == INTERVALOID)
inputTypeMod = baseTypeMod;
else
inputTypeMod = -1;
baseType = typeidType(baseTypeId);
newcon->consttype = baseTypeId;
newcon->consttypmod = inputTypeMod;
newcon->constcollid = typeTypeCollation(baseType);
newcon->constlen = typeLen(baseType);
newcon->constbyval = typeByVal(baseType);
newcon->constisnull = con->constisnull;
/*
* We use the original literal's location regardless of the position
* of the coercion. This is a change from pre-9.2 behavior, meant to
* simplify life for pg_stat_statements.
*/
newcon->location = con->location;
/*
* Set up to point at the constant's text if the input routine throws
* an error.
*/
setup_parser_errposition_callback(&pcbstate, pstate, con->location);
/*
* We assume here that UNKNOWN's internal representation is the same
* as CSTRING.
*/
if (!con->constisnull)
newcon->constvalue = stringTypeDatum(baseType,
DatumGetCString(con->constvalue),
inputTypeMod);
else
newcon->constvalue = stringTypeDatum(baseType,
NULL,
inputTypeMod);
/*
* If it's a varlena value, force it to be in non-expanded
* (non-toasted) format; this avoids any possible dependency on
* external values and improves consistency of representation.
*/
if (!con->constisnull && newcon->constlen == -1)
newcon->constvalue =
PointerGetDatum(PG_DETOAST_DATUM(newcon->constvalue));
#ifdef RANDOMIZE_ALLOCATED_MEMORY
/*
* For pass-by-reference data types, repeat the conversion to see if
* the input function leaves any uninitialized bytes in the result. We
* can only detect that reliably if RANDOMIZE_ALLOCATED_MEMORY is
* enabled, so we don't bother testing otherwise. The reason we don't
* want any instability in the input function is that comparison of
* Const nodes relies on bytewise comparison of the datums, so if the
* input function leaves garbage then subexpressions that should be
* identical may not get recognized as such. See pgsql-hackers
* discussion of 2008-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);
ReleaseSysCache(baseType);
return result;
}
if (IsA(node, Param) &&
pstate != NULL && pstate->p_coerce_param_hook != NULL)
{
/*
* Allow the CoerceParamHook to decide what happens. It can return a
* transformed node (very possibly the same Param node), or return
* NULL to indicate we should proceed with normal coercion.
*/
result = pstate->p_coerce_param_hook(pstate,
(Param *) node,
targetTypeId,
targetTypeMod,
location);
if (result)
return result;
}
if (IsA(node, CollateExpr))
{
/*
* If we have a COLLATE clause, we have to push the coercion
* underneath the COLLATE; or discard the COLLATE if the target type
* isn't collatable. This is really ugly, but there is little choice
* because the above hacks on Consts and Params wouldn't happen
* otherwise. This kluge has consequences in coerce_to_target_type.
*/
CollateExpr *coll = (CollateExpr *) node;
result = coerce_type(pstate, (Node *) coll->arg,
inputTypeId, targetTypeId, targetTypeMod,
ccontext, cformat, location);
if (type_is_collatable(targetTypeId))
{
CollateExpr *newcoll = makeNode(CollateExpr);
newcoll->arg = (Expr *) result;
newcoll->collOid = coll->collOid;
newcoll->location = coll->location;
result = (Node *) newcoll;
}
return result;
}
pathtype = find_coercion_pathway(targetTypeId, inputTypeId, ccontext,
&funcId);
if (pathtype != COERCION_PATH_NONE)
{
if (pathtype != COERCION_PATH_RELABELTYPE)
{
/*
* Generate an expression tree representing run-time application
* of the conversion function. If we are dealing with a domain
* target type, the conversion function will yield the base type,
* and we need to extract the correct typmod to use from the
* domain's typtypmod.
*/
Oid baseTypeId;
int32 baseTypeMod;
baseTypeMod = targetTypeMod;
baseTypeId = getBaseTypeAndTypmod(targetTypeId, &baseTypeMod);
result = build_coercion_expression(node, pathtype, funcId,
baseTypeId, baseTypeMod,
ccontext, cformat, location);
/*
* If domain, coerce to the domain type and relabel with domain
* type ID, hiding the previous coercion node.
*/
if (targetTypeId != baseTypeId)
result = coerce_to_domain(result, baseTypeId, baseTypeMod,
targetTypeId,
ccontext, cformat, location,
true);
}
else
{
/*
* We don't need to do a physical conversion, but we do need to
* attach a RelabelType node so that the expression will be seen
* to have the intended type when inspected by higher-level code.
*
* Also, domains may have value restrictions beyond the base type
* that must be accounted for. If the destination is a domain
* then we won't need a RelabelType node.
*/
result = coerce_to_domain(node, InvalidOid, -1, targetTypeId,
ccontext, cformat, location,
false);
if (result == node)
{
/*
* XXX could we label result with exprTypmod(node) instead of
* default -1 typmod, to save a possible length-coercion
* later? Would work if both types have same interpretation of
* typmod, which is likely but not certain.
*/
RelabelType *r = makeRelabelType((Expr *) result,
targetTypeId, -1,
InvalidOid,
cformat);
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))
{
/*
* Input class type is a subclass of target, so generate an
* appropriate runtime conversion (removing unneeded columns and
* possibly rearranging the ones that are wanted).
*
* We will also get here when the input is a domain over a subclass of
* the target type. To keep life simple for the executor, we define
* ConvertRowtypeExpr as only working between regular composite types;
* therefore, in such cases insert a RelabelType to smash the input
* expression down to its base type.
*/
Oid baseTypeId = getBaseType(inputTypeId);
ConvertRowtypeExpr *r = makeNode(ConvertRowtypeExpr);
if (baseTypeId != inputTypeId)
{
RelabelType *rt = makeRelabelType((Expr *) node,
baseTypeId, -1,
InvalidOid,
COERCE_IMPLICIT_CAST);
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()
* Can input_typeids be coerced to target_typeids?
*
* We must be told the context (CAST construct, assignment, implicit coercion)
* as this determines the set of available casts.
*/
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;
}
/*
* If input is an untyped string constant, assume we can convert it to
* anything.
*/
if (inputTypeId == UNKNOWNOID)
continue;
/*
* If pg_cast shows that we can coerce, accept. This test now covers
* both binary-compatible and coercion-function cases.
*/
pathtype = find_coercion_pathway(targetTypeId, inputTypeId, ccontext,
&funcId);
if (pathtype != COERCION_PATH_NONE)
continue;
/*
* If input is RECORD and target is a composite type, assume we can
* coerce (may need tighter checking here)
*/
if (inputTypeId == RECORDOID &&
ISCOMPLEX(targetTypeId))
continue;
/*
* If input is a composite type and target is RECORD, accept
*/
if (targetTypeId == RECORDOID &&
ISCOMPLEX(inputTypeId))
continue;
#ifdef NOT_USED /* not implemented yet */
/*
* If input is record[] and target is a composite array type, assume
* we can coerce (may need tighter checking here)
*/
if (inputTypeId == RECORDARRAYOID &&
is_complex_array(targetTypeId))
continue;
#endif
/*
* If input is a composite array type and target is record[], accept
*/
if (targetTypeId == RECORDARRAYOID &&
is_complex_array(inputTypeId))
continue;
/*
* If input is a class type that inherits from target, accept
*/
if (typeInheritsFrom(inputTypeId, targetTypeId)
|| typeIsOfTypedTable(inputTypeId, targetTypeId))
continue;
/*
* Else, cannot coerce at this argument position
*/
return false;
}
/* If we found any generic argument types, cross-check them */
if (have_generics)
{
if (!check_generic_type_consistency(input_typeids, target_typeids,
nargs))
return false;
}
return true;
}
/*
* Create an expression tree to represent coercion to a domain type.
*
* 'arg': input expression
* 'baseTypeId': base type of domain, if known (pass InvalidOid if caller
* has not bothered to look this up)
* 'baseTypeMod': base type typmod of domain, if known (pass -1 if caller
* has not bothered to look this up)
* 'typeId': target type to coerce to
* 'ccontext': context indicator to control coercions
* 'cformat': coercion display format
* 'location': coercion request location
* 'hideInputCoercion': if true, hide the input coercion under this one.
*
* If the target type isn't a domain, the given 'arg' is returned as-is.
*/
Node *
coerce_to_domain(Node *arg, Oid baseTypeId, int32 baseTypeMod, Oid typeId,
CoercionContext ccontext, CoercionForm cformat, int location,
bool hideInputCoercion)
{
CoerceToDomain *result;
/* Get the base type if it hasn't been supplied */
if (baseTypeId == InvalidOid)
baseTypeId = getBaseTypeAndTypmod(typeId, &baseTypeMod);
/* If it isn't a domain, return the node as it was passed in */
if (baseTypeId == typeId)
return arg;
/* Suppress display of nested coercion steps */
if (hideInputCoercion)
hide_coercion_node(arg);
/*
* If the domain applies a typmod to its base type, build the appropriate
* coercion step. Mark it implicit for display purposes, because we don't
* want it shown separately by ruleutils.c; but the isExplicit flag passed
* to the conversion function depends on the manner in which the domain
* coercion is invoked, so that the semantics of implicit and explicit
* coercion differ. (Is that really the behavior we want?)
*
* NOTE: because we apply this as part of the fixed expression structure,
* ALTER DOMAIN cannot alter the typtypmod. But it's unclear that that
* would be safe to do anyway, without lots of knowledge about what the
* base type thinks the typmod means.
*/
arg = coerce_type_typmod(arg, baseTypeId, baseTypeMod,
ccontext, COERCE_IMPLICIT_CAST, location,
false);
/*
* Now build the domain coercion node. This represents run-time checking
* of any constraints currently attached to the domain. This also ensures
* that the expression is properly labeled as to result type.
*/
result = makeNode(CoerceToDomain);
result->arg = (Expr *) arg;
result->resulttype = typeId;
result->resulttypmod = -1; /* currently, always -1 for domains */
/* resultcollid will be set by parse_collate.c */
result->coercionformat = cformat;
result->location = location;
return (Node *) result;
}
/*
* coerce_type_typmod()
* Force a value to a particular typmod, if meaningful and possible.
*
* This is applied to values that are going to be stored in a relation
* (where we have an atttypmod for the column) as well as values being
* explicitly CASTed (where the typmod comes from the target type spec).
*
* The caller must have already ensured that the value is of the correct
* type, typically by applying coerce_type.
*
* ccontext may affect semantics, depending on whether the length coercion
* function pays attention to the isExplicit flag it's passed.
*
* cformat determines the display properties of the generated node (if any).
*
* If hideInputCoercion is true *and* we generate a node, the input node is
* forced to IMPLICIT display form, so that only the typmod coercion node will
* be visible when displaying the expression.
*
* NOTE: this does not need to work on domain types, because any typmod
* coercion for a domain is considered to be part of the type coercion
* needed to produce the domain value in the first place. So, no getBaseType.
*/
static Node *
coerce_type_typmod(Node *node, Oid targetTypeId, int32 targetTypMod,
CoercionContext ccontext, CoercionForm cformat,
int location,
bool hideInputCoercion)
{
CoercionPathType pathtype;
Oid funcId;
/* Skip coercion if already done */
if (targetTypMod == exprTypmod(node))
return node;
/* Suppress display of nested coercion steps */
if (hideInputCoercion)
hide_coercion_node(node);
/*
* A negative typmod means that no actual coercion is needed, but we still
* want a RelabelType to ensure that the expression exposes the intended
* typmod.
*/
if (targetTypMod < 0)
pathtype = COERCION_PATH_NONE;
else
pathtype = find_typmod_coercion_function(targetTypeId, &funcId);
if (pathtype != COERCION_PATH_NONE)
{
node = build_coercion_expression(node, pathtype, funcId,
targetTypeId, targetTypMod,
ccontext, cformat, location);
}
else
{
/*
* We don't need to perform any actual coercion step, but we should
* apply a RelabelType to ensure that the expression exposes the
* intended typmod.
*/
node = applyRelabelType(node, targetTypeId, targetTypMod,
exprCollation(node),
cformat, location, false);
}
return node;
}
/*
* Mark a coercion node as IMPLICIT so it will never be displayed by
* ruleutils.c. We use this when we generate a nest of coercion nodes
* to implement what is logically one conversion; the inner nodes are
* forced to IMPLICIT_CAST format. This does not change their semantics,
* only display behavior.
*
* It is caller error to call this on something that doesn't have a
* CoercionForm field.
*/
static void
hide_coercion_node(Node *node)
{
if (IsA(node, FuncExpr))
((FuncExpr *) node)->funcformat = COERCE_IMPLICIT_CAST;
else if (IsA(node, RelabelType))
((RelabelType *) node)->relabelformat = COERCE_IMPLICIT_CAST;
else if (IsA(node, CoerceViaIO))
((CoerceViaIO *) node)->coerceformat = COERCE_IMPLICIT_CAST;
else if (IsA(node, ArrayCoerceExpr))
((ArrayCoerceExpr *) node)->coerceformat = COERCE_IMPLICIT_CAST;
else if (IsA(node, ConvertRowtypeExpr))
((ConvertRowtypeExpr *) node)->convertformat = COERCE_IMPLICIT_CAST;
else if (IsA(node, RowExpr))
((RowExpr *) node)->row_format = COERCE_IMPLICIT_CAST;
else if (IsA(node, CoerceToDomain))
((CoerceToDomain *) node)->coercionformat = COERCE_IMPLICIT_CAST;
else
elog(ERROR, "unsupported node type: %d", (int) nodeTag(node));
}
/*
* build_coercion_expression()
* Construct an expression tree for applying a pg_cast entry.
*
* This is used for both type-coercion and length-coercion operations,
* since there is no difference in terms of the calling convention.
*/
static Node *
build_coercion_expression(Node *node,
CoercionPathType pathtype,
Oid funcId,
Oid targetTypeId, int32 targetTypMod,
CoercionContext ccontext, CoercionForm cformat,
int location)
{
int nargs = 0;
if (OidIsValid(funcId))
{
HeapTuple tp;
Form_pg_proc procstruct;
tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcId));
if (!HeapTupleIsValid(tp))
elog(ERROR, "cache lookup failed for function %u", funcId);
procstruct = (Form_pg_proc) GETSTRUCT(tp);
/*
* These Asserts essentially check that function is a legal coercion
* function. We can't make the seemingly obvious tests on prorettype
* and proargtypes[0], even in the COERCION_PATH_FUNC case, because of
* various binary-compatibility cases.
*/
/* Assert(targetTypeId == procstruct->prorettype); */
Assert(!procstruct->proretset);
Assert(procstruct->prokind == PROKIND_FUNCTION);
nargs = procstruct->pronargs;
Assert(nargs >= 1 && nargs <= 3);
/* Assert(procstruct->proargtypes.values[0] == exprType(node)); */
Assert(nargs < 2 || procstruct->proargtypes.values[1] == INT4OID);
Assert(nargs < 3 || procstruct->proargtypes.values[2] == BOOLOID);
ReleaseSysCache(tp);
}
if (pathtype == COERCION_PATH_FUNC)
{
/* We build an ordinary FuncExpr with special arguments */
FuncExpr *fexpr;
List *args;
Const *cons;
Assert(OidIsValid(funcId));
args = list_make1(node);
if (nargs >= 2)
{
/* Pass target typmod as an int4 constant */
cons = makeConst(INT4OID,
-1,
InvalidOid,
sizeof(int32),
Int32GetDatum(targetTypMod),
false,
true);
args = lappend(args, cons);
}
if (nargs == 3)
{
/* Pass it a boolean isExplicit parameter, too */
cons = makeConst(BOOLOID,
-1,
InvalidOid,
sizeof(bool),
BoolGetDatum(ccontext == COERCION_EXPLICIT),
false,
true);
args = lappend(args, cons);
}
fexpr = makeFuncExpr(funcId, targetTypeId, args,
InvalidOid, InvalidOid, cformat);
fexpr->location = location;
return (Node *) fexpr;
}
else if (pathtype == COERCION_PATH_ARRAYCOERCE)
{
/* We need to build an ArrayCoerceExpr */
ArrayCoerceExpr *acoerce = makeNode(ArrayCoerceExpr);
CaseTestExpr *ctest = makeNode(CaseTestExpr);
Oid sourceBaseTypeId;
int32 sourceBaseTypeMod;
Oid targetElementType;
Node *elemexpr;
/*
* Look through any domain over the source array type. Note we don't
* expect that the target type is a domain; it must be a plain array.
* (To get to a domain target type, we'll do coerce_to_domain later.)
*/
sourceBaseTypeMod = exprTypmod(node);
sourceBaseTypeId = getBaseTypeAndTypmod(exprType(node),
&sourceBaseTypeMod);
/*
* Set up a CaseTestExpr representing one element of the source array.
* This is an abuse of CaseTestExpr, but it's OK as long as there
* can't be any CaseExpr or ArrayCoerceExpr within the completed
* elemexpr.
*/
ctest->typeId = get_element_type(sourceBaseTypeId);
Assert(OidIsValid(ctest->typeId));
ctest->typeMod = sourceBaseTypeMod;
ctest->collation = InvalidOid; /* Assume coercions don't care */
/* And coerce it to the target element type */
targetElementType = get_element_type(targetTypeId);
Assert(OidIsValid(targetElementType));
elemexpr = coerce_to_target_type(NULL,
(Node *) ctest,
ctest->typeId,
targetElementType,
targetTypMod,
ccontext,
cformat,
location);
if (elemexpr == NULL) /* shouldn't happen */
elog(ERROR, "failed to coerce array element type as expected");
acoerce->arg = (Expr *) node;
acoerce->elemexpr = (Expr *) elemexpr;
acoerce->resulttype = targetTypeId;
/*
* Label the output as having a particular element typmod only if we
* ended up with a per-element expression that is labeled that way.
*/
acoerce->resulttypmod = exprTypmod(elemexpr);
/* resultcollid will be set by parse_collate.c */
acoerce->coerceformat = cformat;
acoerce->location = location;
return (Node *) acoerce;
}
else if (pathtype == COERCION_PATH_COERCEVIAIO)
{
/* We need to build a CoerceViaIO node */
CoerceViaIO *iocoerce = makeNode(CoerceViaIO);
Assert(!OidIsValid(funcId));
iocoerce->arg = (Expr *) node;
iocoerce->resulttype = targetTypeId;
/* resultcollid will be set by parse_collate.c */
iocoerce->coerceformat = cformat;
iocoerce->location = location;
return (Node *) iocoerce;
}
else
{
elog(ERROR, "unsupported pathtype %d in build_coercion_expression",
(int) pathtype);
return NULL; /* keep compiler quiet */
}
}
/*
* coerce_record_to_complex
* Coerce a RECORD to a specific composite type.
*
* Currently we only support this for inputs that are RowExprs or whole-row
* Vars.
*/
static Node *
coerce_record_to_complex(ParseState *pstate, Node *node,
Oid targetTypeId,
CoercionContext ccontext,
CoercionForm cformat,
int location)
{
RowExpr *rowexpr;
Oid baseTypeId;
int32 baseTypeMod = -1;
TupleDesc tupdesc;
List *args = NIL;
List *newargs;
int i;
int ucolno;
ListCell *arg;
if (node && IsA(node, RowExpr))
{
/*
* Since the RowExpr must be of type RECORD, we needn't worry about it
* containing any dropped columns.
*/
args = ((RowExpr *) node)->args;
}
else if (node && IsA(node, Var) &&
((Var *) node)->varattno == InvalidAttrNumber)
{
int rtindex = ((Var *) node)->varno;
int sublevels_up = ((Var *) node)->varlevelsup;
int vlocation = ((Var *) node)->location;
ParseNamespaceItem *nsitem;
nsitem = GetNSItemByRangeTablePosn(pstate, rtindex, sublevels_up);
args = expandNSItemVars(pstate, nsitem, sublevels_up, vlocation, NULL);
}
else
ereport(ERROR,
(errcode(ERRCODE_CANNOT_COERCE),
errmsg("cannot cast type %s to %s",
format_type_be(RECORDOID),
format_type_be(targetTypeId)),
parser_coercion_errposition(pstate, location, node)));
/*
* Look up the composite type, accounting for possibility that what we are
* given is a domain over composite.
*/
baseTypeId = getBaseTypeAndTypmod(targetTypeId, &baseTypeMod);
tupdesc = lookup_rowtype_tupdesc(baseTypeId, baseTypeMod);
/* 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't use atttypid here, but it doesn't really matter what type
* the Const claims to be.
*/
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);
}
return (Node *) rowexpr;
}
/*
* coerce_to_boolean()
* Coerce an argument of a construct that requires boolean input
* (AND, OR, NOT, etc). Also check that input is not a set.
*
* Returns the possibly-transformed node tree.
*
* As with coerce_type, pstate may be NULL if no special unknown-Param
* processing is wanted.
*/
Node *
coerce_to_boolean(ParseState *pstate, Node *node,
const char *constructName)
{
Oid inputTypeId = exprType(node);
if (inputTypeId != BOOLOID)
{
Node *newnode;
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))));
return node;
}
/*
* coerce_to_specific_type_typmod()
* Coerce an argument of a construct that requires a specific data type,
* with a specific typmod. Also check that input is not a set.
*
* Returns the possibly-transformed node tree.
*
* As with coerce_type, pstate may be NULL if no special unknown-Param
* processing is wanted.
*/
Node *
coerce_to_specific_type_typmod(ParseState *pstate, Node *node,
Oid targetTypeId, int32 targetTypmod,
const char *constructName)
{
Oid inputTypeId = exprType(node);
if (inputTypeId != targetTypeId)
{
Node *newnode;
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))));
return node;
}
/*
* coerce_to_specific_type()
* Coerce an argument of a construct that requires a specific data type.
* Also check that input is not a set.
*
* Returns the possibly-transformed node tree.
*
* As with coerce_type, pstate may be NULL if no special unknown-Param
* processing is wanted.
*/
Node *
coerce_to_specific_type(ParseState *pstate, Node *node,
Oid targetTypeId,
const char *constructName)
{
return coerce_to_specific_type_typmod(pstate, node,
targetTypeId, -1,
constructName);
}
/*
* parser_coercion_errposition - report coercion error location, if possible
*
* We prefer to point at the coercion request (CAST, ::, etc) if possible;
* but there may be no such location in the case of an implicit coercion.
* In that case point at the input expression.
*
* XXX possibly this is more generally useful than coercion errors;
* if so, should rename and place with parser_errposition.
*/
int
parser_coercion_errposition(ParseState *pstate,
int coerce_location,
Node *input_expr)
{
if (coerce_location >= 0)
return parser_errposition(pstate, coerce_location);
else
return parser_errposition(pstate, exprLocation(input_expr));
}
/*
* select_common_type()
* Determine the common supertype of a list of input expressions.
* This is used for determining the output type of CASE, UNION,
* and similar constructs.
*
* 'exprs' is a *nonempty* list of expressions. Note that earlier items
* in the list will be preferred if there is doubt.
* 'context' is a phrase to use in the error message if we fail to select
* a usable type. Pass NULL to have the routine return InvalidOid
* rather than throwing an error on failure.
* 'which_expr': if not NULL, receives a pointer to the particular input
* expression from which the result type was taken.
*
* Caution: "failure" just means that there were inputs of different type
* categories. It is not guaranteed that all the inputs are coercible to the
* selected type; caller must check that (see verify_common_type).
*/
Oid
select_common_type(ParseState *pstate, List *exprs, const char *context,
Node **which_expr)
{
Node *pexpr;
Oid ptype;
TYPCATEGORY pcategory;
bool pispreferred;
ListCell *lc;
Assert(exprs != NIL);
pexpr = (Node *) linitial(exprs);
lc = list_second_cell(exprs);
ptype = exprType(pexpr);
/*
* If all input types are valid and exactly the same, just pick that type.
* This is the only way that we will resolve the result as being a domain
* type; otherwise domains are smashed to their base types for comparison.
*/
if (ptype != UNKNOWNOID)
{
for_each_cell(lc, exprs, lc)
{
Node *nexpr = (Node *) lfirst(lc);
Oid ntype = exprType(nexpr);
if (ntype != ptype)
break;
}
if (lc == NULL) /* got to the end of the list? */
{
if (which_expr)
*which_expr = pexpr;
return ptype;
}
}
/*
* Nope, so set up for the full algorithm. Note that at this point, lc
* points to the first list item with type different from pexpr's; we need
* not re-examine any items the previous loop advanced over.
*/
ptype = getBaseType(ptype);
get_type_category_preferred(ptype, &pcategory, &pispreferred);
for_each_cell(lc, exprs, lc)
{
Node *nexpr = (Node *) lfirst(lc);
Oid ntype = getBaseType(exprType(nexpr));
/* move on to next one if no new information... */
if (ntype != UNKNOWNOID && ntype != ptype)
{
TYPCATEGORY ncategory;
bool nispreferred;
get_type_category_preferred(ntype, &ncategory, &nispreferred);
if (ptype == UNKNOWNOID)
{
/* so far, only unknowns so take anything... */
pexpr = nexpr;
ptype = ntype;
pcategory = ncategory;
pispreferred = nispreferred;
}
else if (ncategory != pcategory)
{
/*
* both types in different categories? then not much hope...
*/
if (context == NULL)
return InvalidOid;
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
/*------
translator: first %s is name of a SQL construct, eg CASE */
errmsg("%s types %s and %s cannot be matched",
context,
format_type_be(ptype),
format_type_be(ntype)),
parser_errposition(pstate, exprLocation(nexpr))));
}
else if (!pispreferred &&
can_coerce_type(1, &ptype, &ntype, COERCION_IMPLICIT) &&
!can_coerce_type(1, &ntype, &ptype, COERCION_IMPLICIT))
{
/*
* take new type if can coerce to it implicitly but not the
* other way; but if we have a preferred type, stay on it.
*/
pexpr = nexpr;
ptype = ntype;
pcategory = ncategory;
pispreferred = nispreferred;
}
}
}
/*
* If all the inputs were UNKNOWN type --- ie, unknown-type literals ---
* then resolve as type TEXT. This situation comes up with constructs
* like SELECT (CASE WHEN foo THEN 'bar' ELSE 'baz' END); SELECT 'foo'
* UNION SELECT 'bar'; It might seem desirable to leave the construct's
* output type as UNKNOWN, but that really doesn't work, because we'd
* probably end up needing a runtime coercion from UNKNOWN to something
* else, and we usually won't have it. We need to coerce the unknown
* literals while they are still literals, so a decision has to be made
* now.
*/
if (ptype == UNKNOWNOID)
ptype = TEXTOID;
if (which_expr)
*which_expr = pexpr;
return ptype;
}
/*
* select_common_type_from_oids()
* Determine the common supertype of an array of type OIDs.
*
* This is the same logic as select_common_type(), but working from
* an array of type OIDs not a list of expressions. As in that function,
* earlier entries in the array have some preference over later ones.
* On failure, return InvalidOid if noerror is true, else throw an error.
*
* Caution: "failure" just means that there were inputs of different type
* categories. It is not guaranteed that all the inputs are coercible to the
* selected type; caller must check that (see verify_common_type_from_oids).
*
* Note: neither caller will pass any UNKNOWNOID entries, so the tests
* for that in this function are dead code. However, they don't cost much,
* and it seems better to keep this logic as close to select_common_type()
* as possible.
*/
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;
}
/*
* Nope, so set up for the full algorithm. Note that at this point, we
* can skip array entries before "i"; they are all equal to ptype.
*/
ptype = getBaseType(ptype);
get_type_category_preferred(ptype, &pcategory, &pispreferred);
for (; i < nargs; i++)
{
Oid ntype = getBaseType(typeids[i]);
/* move on to next one if no new information... */
if (ntype != UNKNOWNOID && ntype != ptype)
{
TYPCATEGORY ncategory;
bool nispreferred;
get_type_category_preferred(ntype, &ncategory, &nispreferred);
if (ptype == UNKNOWNOID)
{
/* so far, only unknowns so take anything... */
ptype = ntype;
pcategory = ncategory;
pispreferred = nispreferred;
}
else if (ncategory != pcategory)
{
/*
* both types in different categories? then not much hope...
*/
if (noerror)
return InvalidOid;
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("argument types %s and %s cannot be matched",
format_type_be(ptype),
format_type_be(ntype))));
}
else if (!pispreferred &&
can_coerce_type(1, &ptype, &ntype, COERCION_IMPLICIT) &&
!can_coerce_type(1, &ntype, &ptype, COERCION_IMPLICIT))
{
/*
* take new type if can coerce to it implicitly but not the
* other way; but if we have a preferred type, stay on it.
*/
ptype = ntype;
pcategory = ncategory;
pispreferred = nispreferred;
}
}
}
/* Like select_common_type(), choose TEXT if all inputs were UNKNOWN */
if (ptype == UNKNOWNOID)
ptype = TEXTOID;
return ptype;
}
/*
* coerce_to_common_type()
* Coerce an expression to the given type.
*
* This is used following select_common_type() to coerce the individual
* expressions to the desired type. 'context' is a phrase to use in the
* error message if we fail to coerce.
*
* As with coerce_type, pstate may be NULL if no special unknown-Param
* processing is wanted.
*/
Node *
coerce_to_common_type(ParseState *pstate, Node *node,
Oid targetTypeId, const char *context)
{
Oid inputTypeId = exprType(node);
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;
}
/*
* verify_common_type()
* Verify that all input types can be coerced to a proposed common type.
* Return true if so, false if not all coercions are possible.
*
* Most callers of select_common_type() don't need to do this explicitly
* because the checks will happen while trying to convert input expressions
* to the right type, e.g. in coerce_to_common_type(). However, if a separate
* check step is needed to validate the applicability of the common type, call
* this.
*/
bool
verify_common_type(Oid common_type, List *exprs)
{
ListCell *lc;
foreach(lc, exprs)
{
Node *nexpr = (Node *) lfirst(lc);
Oid ntype = exprType(nexpr);
if (!can_coerce_type(1, &ntype, &common_type, COERCION_IMPLICIT))
return false;
}
return true;
}
/*
* verify_common_type_from_oids()
* As above, but work from an array of type OIDs.
*/
static bool
verify_common_type_from_oids(Oid common_type, int nargs, const Oid *typeids)
{
for (int i = 0; i < nargs; i++)
{
if (!can_coerce_type(1, &typeids[i], &common_type, COERCION_IMPLICIT))
return false;
}
return true;
}
/*
* select_common_typmod()
* Determine the common typmod of a list of input expressions.
*
* common_type is the selected common type of the expressions, typically
* computed using select_common_type().
*/
int32
select_common_typmod(ParseState *pstate, List *exprs, Oid common_type)
{
ListCell *lc;
bool first = true;
int32 result = -1;
foreach(lc, exprs)
{
Node *expr = (Node *) lfirst(lc);
/* Types must match */
if (exprType(expr) != common_type)
return -1;
else if (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()
* Are the actual arguments potentially compatible with a
* polymorphic function?
*
* The argument consistency rules are:
*
* 1) All arguments declared ANYELEMENT must have the same datatype.
* 2) All arguments declared ANYARRAY must have the same datatype,
* which must be a varlena array type.
* 3) All arguments declared ANYRANGE must be the same range type.
* Similarly, all arguments declared ANYMULTIRANGE must be the same
* multirange type; and if both of these appear, the ANYRANGE type
* must be the element type of the ANYMULTIRANGE type.
* 4) If there are arguments of more than one of these polymorphic types,
* the array element type and/or range subtype must be the same as each
* other and the same as the ANYELEMENT type.
* 5) ANYENUM is treated the same as ANYELEMENT except that if it is used
* (alone or in combination with plain ANYELEMENT), we add the extra
* condition that the ANYELEMENT type must be an enum.
* 6) ANYNONARRAY is treated the same as ANYELEMENT except that if it is used,
* we add the extra condition that the ANYELEMENT type must not be an array.
* (This is a no-op if used in combination with ANYARRAY or ANYENUM, but
* is an extra restriction if not.)
* 7) All arguments declared ANYCOMPATIBLE must be implicitly castable
* to a common supertype (chosen as per select_common_type's rules).
* ANYCOMPATIBLENONARRAY works like ANYCOMPATIBLE but also requires the
* common supertype to not be an array. If there are ANYCOMPATIBLEARRAY
* or ANYCOMPATIBLERANGE or ANYCOMPATIBLEMULTIRANGE arguments, their element
* types or subtypes are included while making the choice of common supertype.
* 8) The resolved type of ANYCOMPATIBLEARRAY arguments will be the array
* type over the common supertype (which might not be the same array type
* as any of the original arrays).
* 9) All ANYCOMPATIBLERANGE arguments must be the exact same range type
* (after domain flattening), since we have no preference rule that would
* let us choose one over another. Furthermore, that range's subtype
* must exactly match the common supertype chosen by rule 7.
* 10) All ANYCOMPATIBLEMULTIRANGE arguments must be the exact same multirange
* type (after domain flattening), since we have no preference rule that
* would let us choose one over another. Furthermore, if ANYCOMPATIBLERANGE
* also appears, that range type must be the multirange's element type;
* otherwise, the multirange's range's subtype must exactly match the
* common supertype chosen by rule 7.
*
* Domains over arrays match ANYARRAY, and are immediately flattened to their
* base type. (Thus, for example, we will consider it a match if one ANYARRAY
* argument is a domain over int4[] while another one is just int4[].) Also
* notice that such a domain does *not* match ANYNONARRAY. The same goes
* for ANYCOMPATIBLEARRAY and ANYCOMPATIBLENONARRAY.
*
* Similarly, domains over ranges match ANYRANGE or ANYCOMPATIBLERANGE,
* and are immediately flattened to their base type. Likewise, domains
* over multiranges match ANYMULTIRANGE or ANYCOMPATIBLEMULTIRANGE and are
* immediately flattened to their base type.
*
* Note that domains aren't currently considered to match ANYENUM,
* even if their base type would match.
*
* If we have UNKNOWN input (ie, an untyped literal) for any polymorphic
* argument, assume it is okay.
*
* We do not ereport here, but just return false if a rule is violated.
*/
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];
/*
* Loop through the arguments to see if we have any that are polymorphic.
* If so, require the actual types to be consistent.
*/
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)
{
if (decl_type == ANYNONARRAYOID)
have_anynonarray = true;
else if (decl_type == ANYENUMOID)
have_anyenum = true;
if (actual_type == UNKNOWNOID)
continue;
if (OidIsValid(elem_typeid) && actual_type != elem_typeid)
return false;
elem_typeid = actual_type;
}
else if (decl_type == ANYARRAYOID)
{
if (actual_type == UNKNOWNOID)
continue;
actual_type = getBaseType(actual_type); /* flatten domains */
if (OidIsValid(array_typeid) && actual_type != array_typeid)
return false;
array_typeid = actual_type;
}
else if (decl_type == ANYRANGEOID)
{
if (actual_type == UNKNOWNOID)
continue;
actual_type = getBaseType(actual_type); /* flatten domains */
if (OidIsValid(range_typeid) && actual_type != range_typeid)
return false;
range_typeid = actual_type;
}
else if (decl_type == ANYMULTIRANGEOID)
{
if (actual_type == UNKNOWNOID)
continue;
actual_type = getBaseType(actual_type); /* flatten domains */
if (OidIsValid(multirange_typeid) && actual_type != multirange_typeid)
return false;
multirange_typeid = actual_type;
}
else if (decl_type == ANYCOMPATIBLEOID ||
decl_type == ANYCOMPATIBLENONARRAYOID)
{
if (decl_type == ANYCOMPATIBLENONARRAYOID)
have_anycompatible_nonarray = true;
if (actual_type == UNKNOWNOID)
continue;
/* collect the actual types of non-unknown COMPATIBLE args */
anycompatible_actual_types[n_anycompatible_args++] = actual_type;
}
else if (decl_type == ANYCOMPATIBLEARRAYOID)
{
Oid elem_type;
if (actual_type == UNKNOWNOID)
continue;
actual_type = getBaseType(actual_type); /* flatten domains */
elem_type = get_element_type(actual_type);
if (!OidIsValid(elem_type))
return false; /* not an array */
/* collect the element type for common-supertype choice */
anycompatible_actual_types[n_anycompatible_args++] = elem_type;
}
else if (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)
return false;
}
else
{
anycompatible_range_typeid = actual_type;
anycompatible_range_typelem = get_range_subtype(actual_type);
if (!OidIsValid(anycompatible_range_typelem))
return false; /* not a range type */
/* collect the subtype for common-supertype choice */
anycompatible_actual_types[n_anycompatible_args++] = anycompatible_range_typelem;
}
}
else if (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)
return false;
}
else
{
anycompatible_multirange_typeid = actual_type;
anycompatible_multirange_typelem = get_multirange_range(actual_type);
if (!OidIsValid(anycompatible_multirange_typelem))
return false; /* 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)
{
/*
* Special case for matching ANYARRAY input to an ANYARRAY
* argument: allow it for now. enforce_generic_type_consistency()
* might complain later, depending on the presence of other
* polymorphic arguments or results, but it will deliver a less
* surprising error message than "function does not exist".
*
* (If you think to change this, note that can_coerce_type will
* consider such a situation as a match, so that we might not even
* get here.)
*/
}
else
{
Oid array_typelem;
array_typelem = get_element_type(array_typeid);
if (!OidIsValid(array_typelem))
return false; /* should be an array, but isn't */
if (!OidIsValid(elem_typeid))
{
/*
* if we don't have an element type yet, use the one we just
* got
*/
elem_typeid = array_typelem;
}
else if (array_typelem != elem_typeid)
{
/* otherwise, they better match */
return false;
}
}
}
/* 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))
return false; /* 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))
return false; /* should be a range, but isn't */
}
else if (multirange_typelem != range_typeid)
{
/* otherwise, they better match */
return false;
}
}
/* 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))
return false; /* should be a range, but isn't */
if (!OidIsValid(elem_typeid))
{
/*
* If we don't have an element type yet, use the one we just got
*/
elem_typeid = range_typelem;
}
else if (range_typelem != elem_typeid)
{
/* otherwise, they better match */
return false;
}
}
if (have_anynonarray)
{
/* require the element type to not be an array or domain over array */
if (type_is_array_domain(elem_typeid))
return false;
}
if (have_anyenum)
{
/* require the element type to be an enum */
if (!type_is_enum(elem_typeid))
return false;
}
/* 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)
return false;
}
else
{
anycompatible_range_typeid = anycompatible_multirange_typelem;
anycompatible_range_typelem = get_range_subtype(anycompatible_range_typeid);
if (!OidIsValid(anycompatible_range_typelem))
return false; /* 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;
anycompatible_typeid =
select_common_type_from_oids(n_anycompatible_args,
anycompatible_actual_types,
true);
if (!OidIsValid(anycompatible_typeid))
return false; /* 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))
return false;
if (have_anycompatible_nonarray)
{
/*
* require the anycompatible type to not be an array or domain
* over array
*/
if (type_is_array_domain(anycompatible_typeid))
return false;
}
/*
* The anycompatible type must exactly match the range element type,
* if we were able to identify one. This checks compatibility for
* anycompatiblemultirange too since that also sets
* anycompatible_range_typelem above.
*/
if (OidIsValid(anycompatible_range_typelem) &&
anycompatible_range_typelem != anycompatible_typeid)
return false;
}
/* Looks valid */
return true;
}
/*
* enforce_generic_type_consistency()
* Make sure a polymorphic function is legally callable, and
* deduce actual argument and result types.
*
* If any polymorphic pseudotype is used in a function's arguments or
* return type, we make sure the actual data types are consistent with
* each other. The argument consistency rules are shown above for
* check_generic_type_consistency().
*
* If we have UNKNOWN input (ie, an untyped literal) for any polymorphic
* argument, we attempt to deduce the actual type it should have. If
* successful, we alter that position of declared_arg_types[] so that
* make_fn_arguments will coerce the literal to the right thing.
*
* If we have polymorphic arguments of the ANYCOMPATIBLE family,
* we similarly alter declared_arg_types[] entries to show the resolved
* common supertype, so that make_fn_arguments will coerce the actual
* arguments to the proper type.
*
* Rules are applied to the function's return type (possibly altering it)
* if it is declared as a polymorphic type and there is at least one
* polymorphic argument type:
*
* 1) If return type is ANYELEMENT, and any argument is ANYELEMENT, use the
* argument's actual type as the function's return type.
* 2) If return type is ANYARRAY, and any argument is ANYARRAY, use the
* argument's actual type as the function's return type.
* 3) Similarly, if return type is ANYRANGE or ANYMULTIRANGE, and any
* argument is ANYRANGE or ANYMULTIRANGE, use that argument's actual type
* (or the corresponding range or multirange type) as the function's return
* type.
* 4) Otherwise, if return type is ANYELEMENT or ANYARRAY, and there is
* at least one ANYELEMENT, ANYARRAY, ANYRANGE, or ANYMULTIRANGE input,
* deduce the return type from those inputs, or throw error if we can't.
* 5) Otherwise, if return type is ANYRANGE or ANYMULTIRANGE, throw error.
* (We have no way to select a specific range type if the arguments don't
* include ANYRANGE or ANYMULTIRANGE.)
* 6) ANYENUM is treated the same as ANYELEMENT except that if it is used
* (alone or in combination with plain ANYELEMENT), we add the extra
* condition that the ANYELEMENT type must be an enum.
* 7) ANYNONARRAY is treated the same as ANYELEMENT except that if it is used,
* we add the extra condition that the ANYELEMENT type must not be an array.
* (This is a no-op if used in combination with ANYARRAY or ANYENUM, but
* is an extra restriction if not.)
* 8) ANYCOMPATIBLE, ANYCOMPATIBLEARRAY, and ANYCOMPATIBLENONARRAY are handled
* by resolving the common supertype of those arguments (or their element
* types, for array inputs), and then coercing all those arguments to the
* common supertype, or the array type over the common supertype for
* ANYCOMPATIBLEARRAY.
* 9) For ANYCOMPATIBLERANGE and ANYCOMPATIBLEMULTIRANGE, there must be at
* least one non-UNKNOWN input matching those arguments, and all such
* inputs must be the same range type (or its multirange type, as
* appropriate), since we cannot deduce a range type from non-range types.
* Furthermore, the range type's subtype is included while choosing the
* common supertype for ANYCOMPATIBLE et al, and it must exactly match
* that common supertype.
*
* Domains over arrays or ranges match ANYARRAY or ANYRANGE arguments,
* respectively, and are immediately flattened to their base type. (In
* particular, if the return type is also ANYARRAY or ANYRANGE, we'll set
* it to the base type not the domain type.) The same is true for
* ANYMULTIRANGE, ANYCOMPATIBLEARRAY, ANYCOMPATIBLERANGE, and
* ANYCOMPATIBLEMULTIRANGE.
*
* When allow_poly is false, we are not expecting any of the actual_arg_types
* to be polymorphic, and we should not return a polymorphic result type
* either. When allow_poly is true, it is okay to have polymorphic "actual"
* arg types, and we can return a matching polymorphic type as the result.
* (This case is currently used only to check compatibility of an aggregate's
* declaration with the underlying transfn.)
*
* A special case is that we could see ANYARRAY as an actual_arg_type even
* when allow_poly is false (this is possible only because pg_statistic has
* columns shown as anyarray in the catalogs). We allow this to match a
* declared ANYARRAY argument, but only if there is no other polymorphic
* argument that we would need to match it with, and no need to determine
* the element type to infer the result type. Note this means that functions
* taking ANYARRAY had better behave sanely if applied to the pg_statistic
* columns; they can't just assume that successive inputs are of the same
* actual element type. There is no similar logic for ANYCOMPATIBLEARRAY;
* there isn't a need for it since there are no catalog columns of that type,
* so we won't see it as input. We could consider matching an actual ANYARRAY
* input to an ANYCOMPATIBLEARRAY argument, but at present that seems useless
* as well, since there's no value in using ANYCOMPATIBLEARRAY unless there's
* at least one other ANYCOMPATIBLE-family argument or result.
*
* Also, if there are no arguments declared to be of polymorphic types,
* we'll return the rettype unmodified even if it's polymorphic. This should
* never occur for user-declared functions, because CREATE FUNCTION prevents
* it. But it does happen for some built-in functions, such as array_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];
/*
* Loop through the arguments to see if we have any that are polymorphic.
* If so, require the actual types to be consistent.
*/
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;
else if (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;
}
else if (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;
}
else if (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;
}
else if (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;
}
else if (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;
}
else if (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;
}
else if (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;
}
}
else if (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 */
}
}
}
/*
* Fast Track: if none of the arguments are polymorphic, return the
* unmodified rettype. Not our job to resolve it if it's polymorphic.
*/
if (n_poly_args == 0 && !have_poly_anycompatible)
return rettype;
/* 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)
{
/*
* Special case for matching ANYARRAY input to an ANYARRAY
* argument: allow it iff no other arguments are family-1
* polymorphics (otherwise we couldn't be sure whether the
* array element type matches up) and the result type doesn't
* require us to infer a specific element type.
*/
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))
{
/*
* if we don't have an element type yet, use the one we just
* got
*/
elem_typeid = array_typelem;
}
else if (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;
}
else if (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))));
}
}
else if (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))
{
/*
* if we don't have an element type yet, use the one we just
* got
*/
elem_typeid = range_typelem;
}
else if (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
{
/*
* Only way to get here is if all the family-1 polymorphic
* arguments have UNKNOWN inputs.
*/
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("could not determine polymorphic type because input has type %s",
"unknown")));
}
}
if (have_anynonarray && elem_typeid != ANYELEMENTOID)
{
/*
* require the element type to not be an array or domain over
* array
*/
if (type_is_array_domain(elem_typeid))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("type matched to anynonarray is an array type: %s",
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;
get_type_category_preferred(type, &typcategory, &typispreferred);
Assert(typcategory != TYPCATEGORY_INVALID);
return (TYPCATEGORY) typcategory;
}
/* 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;
get_type_category_preferred(type, &typcategory, &typispreferred);
if (category == typcategory || category == TYPCATEGORY_INVALID)
return typispreferred;
else
return false;
}
/* 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;
return IsBinaryCoercibleWithCast(srctype, targettype, &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;
targetType = typeidType(typeId);
typeForm = (Form_pg_type) GETSTRUCT(targetType);
/* 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);
*funcid = castForm->castfunc;
ReleaseSysCache(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);
return (OidIsValid(elemtype) && ISCOMPLEX(elemtype));
}
/*
* 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;
ReleaseSysCache(tp);
}
return result;
}