|
|
|
|
@ -8,7 +8,7 @@ |
|
|
|
|
* |
|
|
|
|
* |
|
|
|
|
* IDENTIFICATION |
|
|
|
|
* $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.81 2002/08/31 22:10:46 tgl Exp $ |
|
|
|
|
* $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.82 2002/09/01 02:27:32 tgl Exp $ |
|
|
|
|
* |
|
|
|
|
*------------------------------------------------------------------------- |
|
|
|
|
*/ |
|
|
|
|
@ -27,24 +27,27 @@ |
|
|
|
|
#include "utils/syscache.h" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Oid DemoteType(Oid inType); |
|
|
|
|
Oid PromoteTypeToNext(Oid inType); |
|
|
|
|
|
|
|
|
|
static Oid PreferredType(CATEGORY category, Oid type); |
|
|
|
|
static Node *build_func_call(Oid funcid, Oid rettype, List *args); |
|
|
|
|
static Oid find_coercion_function(Oid targetTypeId, Oid sourceTypeId, |
|
|
|
|
bool isExplicit); |
|
|
|
|
static bool find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId, |
|
|
|
|
bool isExplicit, |
|
|
|
|
Oid *funcid); |
|
|
|
|
static Oid find_typmod_coercion_function(Oid typeId); |
|
|
|
|
static Node *build_func_call(Oid funcid, Oid rettype, List *args); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* coerce_type()
|
|
|
|
|
* Convert a function argument to a different type. |
|
|
|
|
/*
|
|
|
|
|
* coerce_type() |
|
|
|
|
* Convert a function argument to a different type. |
|
|
|
|
* |
|
|
|
|
* The caller should already have determined that the coercion is possible; |
|
|
|
|
* see can_coerce_type. |
|
|
|
|
*/ |
|
|
|
|
Node * |
|
|
|
|
coerce_type(ParseState *pstate, Node *node, Oid inputTypeId, |
|
|
|
|
Oid targetTypeId, int32 atttypmod, bool isExplicit) |
|
|
|
|
{ |
|
|
|
|
Node *result; |
|
|
|
|
Oid funcId; |
|
|
|
|
|
|
|
|
|
if (targetTypeId == inputTypeId || |
|
|
|
|
node == NULL) |
|
|
|
|
@ -118,25 +121,71 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId, |
|
|
|
|
/* assume can_coerce_type verified that implicit coercion is okay */ |
|
|
|
|
result = node; |
|
|
|
|
} |
|
|
|
|
else if (IsBinaryCompatible(inputTypeId, targetTypeId)) |
|
|
|
|
else if (find_coercion_pathway(targetTypeId, inputTypeId, isExplicit, |
|
|
|
|
&funcId)) |
|
|
|
|
{ |
|
|
|
|
/*
|
|
|
|
|
* We don't really need to do a 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. |
|
|
|
|
*/ |
|
|
|
|
result = coerce_type_constraints(pstate, node, targetTypeId, true); |
|
|
|
|
/*
|
|
|
|
|
* 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 (wrong if target is a domain, |
|
|
|
|
* in any case). |
|
|
|
|
*/ |
|
|
|
|
result = (Node *) makeRelabelType(result, targetTypeId, -1); |
|
|
|
|
if (OidIsValid(funcId)) |
|
|
|
|
{ |
|
|
|
|
/*
|
|
|
|
|
* 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. |
|
|
|
|
*/ |
|
|
|
|
Oid baseTypeId = getBaseType(targetTypeId); |
|
|
|
|
|
|
|
|
|
result = build_func_call(funcId, baseTypeId, makeList1(node)); |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* If domain, test against domain constraints and relabel with |
|
|
|
|
* domain type ID |
|
|
|
|
*/ |
|
|
|
|
if (targetTypeId != baseTypeId) |
|
|
|
|
{ |
|
|
|
|
result = coerce_type_constraints(pstate, result, |
|
|
|
|
targetTypeId, true); |
|
|
|
|
result = (Node *) makeRelabelType(result, targetTypeId, -1); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* If the input is a constant, apply the type conversion function |
|
|
|
|
* now instead of delaying to runtime. (We could, of course, just |
|
|
|
|
* leave this to be done during planning/optimization; but it's a |
|
|
|
|
* very frequent special case, and we save cycles in the rewriter |
|
|
|
|
* if we fold the expression now.) |
|
|
|
|
* |
|
|
|
|
* Note that no folding will occur if the conversion function is |
|
|
|
|
* not marked 'immutable'. |
|
|
|
|
* |
|
|
|
|
* HACK: if constant is NULL, don't fold it here. This is needed |
|
|
|
|
* by make_subplan(), which calls this routine on placeholder |
|
|
|
|
* Const nodes that mustn't be collapsed. (It'd be a lot cleaner |
|
|
|
|
* to make a separate node type for that purpose...) |
|
|
|
|
*/ |
|
|
|
|
if (IsA(node, Const) && |
|
|
|
|
!((Const *) node)->constisnull) |
|
|
|
|
result = eval_const_expressions(result); |
|
|
|
|
} |
|
|
|
|
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. |
|
|
|
|
*/ |
|
|
|
|
result = coerce_type_constraints(pstate, node, |
|
|
|
|
targetTypeId, true); |
|
|
|
|
/*
|
|
|
|
|
* 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 (wrong if target is a domain, |
|
|
|
|
* in any case). |
|
|
|
|
*/ |
|
|
|
|
result = (Node *) makeRelabelType(result, targetTypeId, -1); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
else if (typeInheritsFrom(inputTypeId, targetTypeId)) |
|
|
|
|
{ |
|
|
|
|
@ -149,74 +198,23 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId, |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
/*
|
|
|
|
|
* Otherwise, find the appropriate type conversion function |
|
|
|
|
* (caller should have determined that there is one), and generate |
|
|
|
|
* an expression tree representing run-time application of the |
|
|
|
|
* conversion function. |
|
|
|
|
* |
|
|
|
|
* For domains, we use the coercion function for the base type. |
|
|
|
|
*/ |
|
|
|
|
Oid baseTypeId = getBaseType(targetTypeId); |
|
|
|
|
Oid funcId; |
|
|
|
|
|
|
|
|
|
funcId = find_coercion_function(baseTypeId, |
|
|
|
|
getBaseType(inputTypeId), |
|
|
|
|
isExplicit); |
|
|
|
|
if (!OidIsValid(funcId)) |
|
|
|
|
elog(ERROR, "coerce_type: no conversion function from '%s' to '%s'", |
|
|
|
|
format_type_be(inputTypeId), format_type_be(targetTypeId)); |
|
|
|
|
|
|
|
|
|
result = build_func_call(funcId, baseTypeId, makeList1(node)); |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* If domain, test against domain constraints and relabel with |
|
|
|
|
* domain type ID |
|
|
|
|
*/ |
|
|
|
|
if (targetTypeId != baseTypeId) |
|
|
|
|
{ |
|
|
|
|
result = coerce_type_constraints(pstate, result, targetTypeId, |
|
|
|
|
true); |
|
|
|
|
result = (Node *) makeRelabelType(result, targetTypeId, -1); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* If the input is a constant, apply the type conversion function |
|
|
|
|
* now instead of delaying to runtime. (We could, of course, just |
|
|
|
|
* leave this to be done during planning/optimization; but it's a |
|
|
|
|
* very frequent special case, and we save cycles in the rewriter |
|
|
|
|
* if we fold the expression now.) |
|
|
|
|
* |
|
|
|
|
* Note that no folding will occur if the conversion function is not |
|
|
|
|
* marked 'iscachable'. |
|
|
|
|
* |
|
|
|
|
* HACK: if constant is NULL, don't fold it here. This is needed by |
|
|
|
|
* make_subplan(), which calls this routine on placeholder Const |
|
|
|
|
* nodes that mustn't be collapsed. (It'd be a lot cleaner to |
|
|
|
|
* make a separate node type for that purpose...) |
|
|
|
|
*/ |
|
|
|
|
if (IsA(node, Const) && |
|
|
|
|
!((Const *) node)->constisnull) |
|
|
|
|
result = eval_const_expressions(result); |
|
|
|
|
/* If we get here, caller blew it */ |
|
|
|
|
elog(ERROR, "coerce_type: no conversion function from %s to %s", |
|
|
|
|
format_type_be(inputTypeId), format_type_be(targetTypeId)); |
|
|
|
|
result = NULL; /* keep compiler quiet */ |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return result; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* can_coerce_type()
|
|
|
|
|
* Can input_typeids be coerced to func_typeids? |
|
|
|
|
* |
|
|
|
|
* There are a few types which are known apriori to be convertible. |
|
|
|
|
* We will check for those cases first, and then look for possible |
|
|
|
|
* conversion functions. |
|
|
|
|
/*
|
|
|
|
|
* can_coerce_type() |
|
|
|
|
* Can input_typeids be coerced to func_typeids? |
|
|
|
|
* |
|
|
|
|
* We must be told whether this is an implicit or explicit coercion |
|
|
|
|
* (explicit being a CAST construct, explicit function call, etc). |
|
|
|
|
* We will accept a wider set of coercion cases for an explicit coercion. |
|
|
|
|
* |
|
|
|
|
* Notes: |
|
|
|
|
* This uses the same mechanism as the CAST() SQL construct in gram.y. |
|
|
|
|
*/ |
|
|
|
|
bool |
|
|
|
|
can_coerce_type(int nargs, Oid *input_typeids, Oid *func_typeids, |
|
|
|
|
@ -278,35 +276,101 @@ can_coerce_type(int nargs, Oid *input_typeids, Oid *func_typeids, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* one of the known-good transparent conversions? then drop |
|
|
|
|
* through... |
|
|
|
|
* If pg_cast shows that we can coerce, accept. This test now |
|
|
|
|
* covers both binary-compatible and coercion-function cases. |
|
|
|
|
*/ |
|
|
|
|
if (IsBinaryCompatible(inputTypeId, targetTypeId)) |
|
|
|
|
if (find_coercion_pathway(targetTypeId, inputTypeId, isExplicit, |
|
|
|
|
&funcId)) |
|
|
|
|
continue; |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* If input is a class type that inherits from target, no problem |
|
|
|
|
* If input is a class type that inherits from target, accept |
|
|
|
|
*/ |
|
|
|
|
if (typeInheritsFrom(inputTypeId, targetTypeId)) |
|
|
|
|
continue; |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Else, try for run-time conversion using functions: look for a |
|
|
|
|
* single-argument function named with the target type name and |
|
|
|
|
* accepting the source type. |
|
|
|
|
* |
|
|
|
|
* If either type is a domain, use its base type instead. |
|
|
|
|
* Else, cannot coerce at this argument position |
|
|
|
|
*/ |
|
|
|
|
funcId = find_coercion_function(getBaseType(targetTypeId), |
|
|
|
|
getBaseType(inputTypeId), |
|
|
|
|
isExplicit); |
|
|
|
|
if (!OidIsValid(funcId)) |
|
|
|
|
return false; |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Create an expression tree to enforce the constraints (if any) |
|
|
|
|
* that should be applied by the type. Currently this is only |
|
|
|
|
* interesting for domain types. |
|
|
|
|
*/ |
|
|
|
|
Node * |
|
|
|
|
coerce_type_constraints(ParseState *pstate, Node *arg, |
|
|
|
|
Oid typeId, bool applyTypmod) |
|
|
|
|
{ |
|
|
|
|
char *notNull = NULL; |
|
|
|
|
int32 typmod = -1; |
|
|
|
|
|
|
|
|
|
for (;;) |
|
|
|
|
{ |
|
|
|
|
HeapTuple tup; |
|
|
|
|
Form_pg_type typTup; |
|
|
|
|
|
|
|
|
|
tup = SearchSysCache(TYPEOID, |
|
|
|
|
ObjectIdGetDatum(typeId), |
|
|
|
|
0, 0, 0); |
|
|
|
|
if (!HeapTupleIsValid(tup)) |
|
|
|
|
elog(ERROR, "coerce_type_constraints: failed to lookup type %u", |
|
|
|
|
typeId); |
|
|
|
|
typTup = (Form_pg_type) GETSTRUCT(tup); |
|
|
|
|
|
|
|
|
|
/* Test for NOT NULL Constraint */ |
|
|
|
|
if (typTup->typnotnull && notNull == NULL) |
|
|
|
|
notNull = pstrdup(NameStr(typTup->typname)); |
|
|
|
|
|
|
|
|
|
/* TODO: Add CHECK Constraints to domains */ |
|
|
|
|
|
|
|
|
|
if (typTup->typtype != 'd') |
|
|
|
|
{ |
|
|
|
|
/* Not a domain, so done */ |
|
|
|
|
ReleaseSysCache(tup); |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Assert(typmod < 0); |
|
|
|
|
|
|
|
|
|
typeId = typTup->typbasetype; |
|
|
|
|
typmod = typTup->typtypmod; |
|
|
|
|
ReleaseSysCache(tup); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* If domain applies a typmod to its base type, do length coercion. |
|
|
|
|
*/ |
|
|
|
|
if (applyTypmod && typmod >= 0) |
|
|
|
|
arg = coerce_type_typmod(pstate, arg, typeId, typmod); |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Only need to add one NOT NULL check regardless of how many
|
|
|
|
|
* domains in the stack request it. The topmost domain that |
|
|
|
|
* requested it is used as the constraint name. |
|
|
|
|
*/ |
|
|
|
|
if (notNull) |
|
|
|
|
{ |
|
|
|
|
ConstraintTest *r = makeNode(ConstraintTest); |
|
|
|
|
|
|
|
|
|
r->arg = arg; |
|
|
|
|
r->testtype = CONSTR_TEST_NOTNULL; |
|
|
|
|
r->name = notNull; |
|
|
|
|
r->check_expr = NULL; |
|
|
|
|
|
|
|
|
|
arg = (Node *) r; |
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return arg; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* coerce_type_typmod()
|
|
|
|
|
* Force a value to a particular typmod, if meaningful and possible. |
|
|
|
|
* |
|
|
|
|
@ -317,21 +381,9 @@ can_coerce_type(int nargs, Oid *input_typeids, Oid *func_typeids, |
|
|
|
|
* The caller must have already ensured that the value is of the correct |
|
|
|
|
* type, typically by applying coerce_type. |
|
|
|
|
* |
|
|
|
|
* If the target column type possesses a function named for the type |
|
|
|
|
* and having parameter signature (columntype, int4), we assume that |
|
|
|
|
* the type requires coercion to its own length and that the said |
|
|
|
|
* function should be invoked to do that. |
|
|
|
|
* |
|
|
|
|
* "bpchar" (ie, char(N)) and "numeric" are examples of such types. |
|
|
|
|
* |
|
|
|
|
* This mechanism may seem pretty grotty and in need of replacement by |
|
|
|
|
* something in pg_cast, but since typmod is only interesting for datatypes |
|
|
|
|
* that have special handling in the grammar, there's not really much |
|
|
|
|
* percentage in making it any easier to apply such coercions ... |
|
|
|
|
* |
|
|
|
|
* 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. |
|
|
|
|
* needed to produce the domain value in the first place. So, no getBaseType. |
|
|
|
|
*/ |
|
|
|
|
Node * |
|
|
|
|
coerce_type_typmod(ParseState *pstate, Node *node, |
|
|
|
|
@ -600,100 +652,6 @@ TypeCategory(Oid inType) |
|
|
|
|
} /* TypeCategory() */ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* IsBinaryCompatible()
|
|
|
|
|
* Check if two types are binary-compatible. |
|
|
|
|
* |
|
|
|
|
* This notion allows us to cheat and directly exchange values without |
|
|
|
|
* going through the trouble of calling a conversion function. |
|
|
|
|
* |
|
|
|
|
* XXX This should be moved to system catalog lookups |
|
|
|
|
* to allow for better type extensibility. |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
#define TypeIsTextGroup(t) \ |
|
|
|
|
((t) == TEXTOID || \
|
|
|
|
|
(t) == BPCHAROID || \
|
|
|
|
|
(t) == VARCHAROID) |
|
|
|
|
|
|
|
|
|
/* Notice OidGroup is a subset of Int4GroupA */ |
|
|
|
|
#define TypeIsOidGroup(t) \ |
|
|
|
|
((t) == OIDOID || \
|
|
|
|
|
(t) == REGPROCOID || \
|
|
|
|
|
(t) == REGPROCEDUREOID || \
|
|
|
|
|
(t) == REGOPEROID || \
|
|
|
|
|
(t) == REGOPERATOROID || \
|
|
|
|
|
(t) == REGCLASSOID || \
|
|
|
|
|
(t) == REGTYPEOID) |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* INT4 is binary-compatible with many types, but we don't want to allow |
|
|
|
|
* implicit coercion directly between, say, OID and AbsTime. So we subdivide |
|
|
|
|
* the categories. |
|
|
|
|
*/ |
|
|
|
|
#define TypeIsInt4GroupA(t) \ |
|
|
|
|
((t) == INT4OID || \
|
|
|
|
|
TypeIsOidGroup(t)) |
|
|
|
|
|
|
|
|
|
#define TypeIsInt4GroupB(t) \ |
|
|
|
|
((t) == INT4OID || \
|
|
|
|
|
(t) == ABSTIMEOID) |
|
|
|
|
|
|
|
|
|
#define TypeIsInt4GroupC(t) \ |
|
|
|
|
((t) == INT4OID || \
|
|
|
|
|
(t) == RELTIMEOID) |
|
|
|
|
|
|
|
|
|
#define TypeIsInetGroup(t) \ |
|
|
|
|
((t) == INETOID || \
|
|
|
|
|
(t) == CIDROID) |
|
|
|
|
|
|
|
|
|
#define TypeIsBitGroup(t) \ |
|
|
|
|
((t) == BITOID || \
|
|
|
|
|
(t) == VARBITOID) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static bool |
|
|
|
|
DirectlyBinaryCompatible(Oid type1, Oid type2) |
|
|
|
|
{ |
|
|
|
|
HeapTuple tuple; |
|
|
|
|
bool result; |
|
|
|
|
|
|
|
|
|
if (type1 == type2) |
|
|
|
|
return true; |
|
|
|
|
|
|
|
|
|
tuple = SearchSysCache(CASTSOURCETARGET, type1, type2, 0, 0); |
|
|
|
|
if (HeapTupleIsValid(tuple)) |
|
|
|
|
{ |
|
|
|
|
Form_pg_cast caststruct; |
|
|
|
|
|
|
|
|
|
caststruct = (Form_pg_cast) GETSTRUCT(tuple); |
|
|
|
|
result = caststruct->castfunc == InvalidOid && caststruct->castimplicit; |
|
|
|
|
ReleaseSysCache(tuple); |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
result = false; |
|
|
|
|
|
|
|
|
|
return result; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool |
|
|
|
|
IsBinaryCompatible(Oid type1, Oid type2) |
|
|
|
|
{ |
|
|
|
|
if (DirectlyBinaryCompatible(type1, type2)) |
|
|
|
|
return true; |
|
|
|
|
/*
|
|
|
|
|
* Perhaps the types are domains; if so, look at their base types |
|
|
|
|
*/ |
|
|
|
|
if (OidIsValid(type1)) |
|
|
|
|
type1 = getBaseType(type1); |
|
|
|
|
if (OidIsValid(type2)) |
|
|
|
|
type2 = getBaseType(type2); |
|
|
|
|
if (DirectlyBinaryCompatible(type1, type2)) |
|
|
|
|
return true; |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* IsPreferredType()
|
|
|
|
|
* Check if this type is a preferred type. |
|
|
|
|
* XXX This should be moved to system catalog lookups |
|
|
|
|
@ -733,7 +691,13 @@ PreferredType(CATEGORY category, Oid type) |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case (NUMERIC_TYPE): |
|
|
|
|
if (TypeIsOidGroup(type)) |
|
|
|
|
if (type == OIDOID || |
|
|
|
|
type == REGPROCOID || |
|
|
|
|
type == REGPROCEDUREOID || |
|
|
|
|
type == REGOPEROID || |
|
|
|
|
type == REGOPERATOROID || |
|
|
|
|
type == REGCLASSOID || |
|
|
|
|
type == REGTYPEOID) |
|
|
|
|
result = OIDOID; |
|
|
|
|
else if (type == NUMERICOID) |
|
|
|
|
result = NUMERICOID; |
|
|
|
|
@ -768,30 +732,85 @@ PreferredType(CATEGORY category, Oid type) |
|
|
|
|
return result; |
|
|
|
|
} /* PreferredType() */ |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* find_coercion_function |
|
|
|
|
* Look for a coercion function between two types. |
|
|
|
|
|
|
|
|
|
/* IsBinaryCompatible()
|
|
|
|
|
* Check if two types are binary-compatible. |
|
|
|
|
* |
|
|
|
|
* A coercion function must be named after (the internal name of) its |
|
|
|
|
* result type, and must accept exactly the specified input type. We |
|
|
|
|
* also require it to be defined in the same namespace as its result type. |
|
|
|
|
* Furthermore, unless we are doing explicit coercion the function must |
|
|
|
|
* be marked as usable for implicit coercion --- this allows coercion |
|
|
|
|
* functions to be provided that aren't implicitly invokable. |
|
|
|
|
* This notion allows us to cheat and directly exchange values without |
|
|
|
|
* going through the trouble of calling a conversion function. |
|
|
|
|
* |
|
|
|
|
* This routine is also used to look for length-coercion functions, which |
|
|
|
|
* are similar but accept a second argument. secondArgType is the type |
|
|
|
|
* of the second argument (normally INT4OID), or InvalidOid if we are |
|
|
|
|
* looking for a regular coercion function. |
|
|
|
|
* As of 7.3, binary compatibility isn't hardwired into the code anymore. |
|
|
|
|
* We consider two types binary-compatible if there is an implicit, |
|
|
|
|
* no-function-needed pg_cast entry. NOTE that we assume that such |
|
|
|
|
* entries are symmetric, ie, it doesn't matter which type we consider |
|
|
|
|
* source and which target. (cf. checks in opr_sanity regression test) |
|
|
|
|
*/ |
|
|
|
|
bool |
|
|
|
|
IsBinaryCompatible(Oid type1, Oid type2) |
|
|
|
|
{ |
|
|
|
|
HeapTuple tuple; |
|
|
|
|
Form_pg_cast castForm; |
|
|
|
|
bool result; |
|
|
|
|
|
|
|
|
|
/* Fast path if same type */ |
|
|
|
|
if (type1 == type2) |
|
|
|
|
return true; |
|
|
|
|
|
|
|
|
|
/* Perhaps the types are domains; if so, look at their base types */ |
|
|
|
|
if (OidIsValid(type1)) |
|
|
|
|
type1 = getBaseType(type1); |
|
|
|
|
if (OidIsValid(type2)) |
|
|
|
|
type2 = getBaseType(type2); |
|
|
|
|
|
|
|
|
|
/* Somewhat-fast path if same base type */ |
|
|
|
|
if (type1 == type2) |
|
|
|
|
return true; |
|
|
|
|
|
|
|
|
|
/* Else look in pg_cast */ |
|
|
|
|
tuple = SearchSysCache(CASTSOURCETARGET, |
|
|
|
|
ObjectIdGetDatum(type1), |
|
|
|
|
ObjectIdGetDatum(type2), |
|
|
|
|
0, 0); |
|
|
|
|
if (!HeapTupleIsValid(tuple)) |
|
|
|
|
return false; /* no cast */ |
|
|
|
|
castForm = (Form_pg_cast) GETSTRUCT(tuple); |
|
|
|
|
|
|
|
|
|
result = (castForm->castfunc == InvalidOid) && castForm->castimplicit; |
|
|
|
|
|
|
|
|
|
ReleaseSysCache(tuple); |
|
|
|
|
|
|
|
|
|
return result; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* find_coercion_pathway |
|
|
|
|
* Look for a coercion pathway between two types. |
|
|
|
|
* |
|
|
|
|
* If a function is found, return its pg_proc OID; else return InvalidOid. |
|
|
|
|
* If we find a matching entry in pg_cast, return TRUE, and set *funcid |
|
|
|
|
* to the castfunc value (which may be InvalidOid for a binary-compatible |
|
|
|
|
* coercion). |
|
|
|
|
*/ |
|
|
|
|
static Oid |
|
|
|
|
find_coercion_function(Oid targetTypeId, Oid sourceTypeId, bool isExplicit) |
|
|
|
|
static bool |
|
|
|
|
find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId, bool isExplicit, |
|
|
|
|
Oid *funcid) |
|
|
|
|
{ |
|
|
|
|
Oid funcid = InvalidOid; |
|
|
|
|
bool result = false; |
|
|
|
|
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 automatically binary-compatible with their base type */ |
|
|
|
|
if (sourceTypeId == targetTypeId) |
|
|
|
|
return true; |
|
|
|
|
|
|
|
|
|
/* Else look in pg_cast */ |
|
|
|
|
tuple = SearchSysCache(CASTSOURCETARGET, |
|
|
|
|
ObjectIdGetDatum(sourceTypeId), |
|
|
|
|
ObjectIdGetDatum(targetTypeId), |
|
|
|
|
@ -799,18 +818,36 @@ find_coercion_function(Oid targetTypeId, Oid sourceTypeId, bool isExplicit) |
|
|
|
|
|
|
|
|
|
if (HeapTupleIsValid(tuple)) |
|
|
|
|
{ |
|
|
|
|
Form_pg_cast cform = (Form_pg_cast) GETSTRUCT(tuple); |
|
|
|
|
Form_pg_cast castForm = (Form_pg_cast) GETSTRUCT(tuple); |
|
|
|
|
|
|
|
|
|
if (isExplicit || cform->castimplicit) |
|
|
|
|
funcid = cform->castfunc; |
|
|
|
|
if (isExplicit || castForm->castimplicit) |
|
|
|
|
{ |
|
|
|
|
*funcid = castForm->castfunc; |
|
|
|
|
result = true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
ReleaseSysCache(tuple); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return funcid; |
|
|
|
|
return result; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* find_typmod_coercion_function -- does the given type need length coercion? |
|
|
|
|
* |
|
|
|
|
* If the target type possesses a function named for the type |
|
|
|
|
* and having parameter signature (targettype, int4), we assume that |
|
|
|
|
* the type requires coercion to its own length and that the said |
|
|
|
|
* function should be invoked to do that. |
|
|
|
|
* |
|
|
|
|
* "bpchar" (ie, char(N)) and "numeric" are examples of such types. |
|
|
|
|
* |
|
|
|
|
* This mechanism may seem pretty grotty and in need of replacement by |
|
|
|
|
* something in pg_cast, but since typmod is only interesting for datatypes |
|
|
|
|
* that have special handling in the grammar, there's not really much |
|
|
|
|
* percentage in making it any easier to apply such coercions ... |
|
|
|
|
*/ |
|
|
|
|
static Oid |
|
|
|
|
find_typmod_coercion_function(Oid typeId) |
|
|
|
|
{ |
|
|
|
|
@ -849,6 +886,7 @@ find_typmod_coercion_function(Oid typeId) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
ReleaseSysCache(targetType); |
|
|
|
|
|
|
|
|
|
return funcid; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -877,74 +915,3 @@ build_func_call(Oid funcid, Oid rettype, List *args) |
|
|
|
|
|
|
|
|
|
return (Node *) expr; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Create an expression tree to enforce the constraints (if any) |
|
|
|
|
* that should be applied by the type. Currently this is only |
|
|
|
|
* interesting for domain types. |
|
|
|
|
*/ |
|
|
|
|
Node * |
|
|
|
|
coerce_type_constraints(ParseState *pstate, Node *arg, |
|
|
|
|
Oid typeId, bool applyTypmod) |
|
|
|
|
{ |
|
|
|
|
char *notNull = NULL; |
|
|
|
|
int32 typmod = -1; |
|
|
|
|
|
|
|
|
|
for (;;) |
|
|
|
|
{ |
|
|
|
|
HeapTuple tup; |
|
|
|
|
Form_pg_type typTup; |
|
|
|
|
|
|
|
|
|
tup = SearchSysCache(TYPEOID, |
|
|
|
|
ObjectIdGetDatum(typeId), |
|
|
|
|
0, 0, 0); |
|
|
|
|
if (!HeapTupleIsValid(tup)) |
|
|
|
|
elog(ERROR, "coerce_type_constraints: failed to lookup type %u", |
|
|
|
|
typeId); |
|
|
|
|
typTup = (Form_pg_type) GETSTRUCT(tup); |
|
|
|
|
|
|
|
|
|
/* Test for NOT NULL Constraint */ |
|
|
|
|
if (typTup->typnotnull && notNull == NULL) |
|
|
|
|
notNull = pstrdup(NameStr(typTup->typname)); |
|
|
|
|
|
|
|
|
|
/* TODO: Add CHECK Constraints to domains */ |
|
|
|
|
|
|
|
|
|
if (typTup->typtype != 'd') |
|
|
|
|
{ |
|
|
|
|
/* Not a domain, so done */ |
|
|
|
|
ReleaseSysCache(tup); |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Assert(typmod < 0); |
|
|
|
|
|
|
|
|
|
typeId = typTup->typbasetype; |
|
|
|
|
typmod = typTup->typtypmod; |
|
|
|
|
ReleaseSysCache(tup); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* If domain applies a typmod to its base type, do length coercion. |
|
|
|
|
*/ |
|
|
|
|
if (applyTypmod && typmod >= 0) |
|
|
|
|
arg = coerce_type_typmod(pstate, arg, typeId, typmod); |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Only need to add one NOT NULL check regardless of how many
|
|
|
|
|
* domains in the stack request it. The topmost domain that |
|
|
|
|
* requested it is used as the constraint name. |
|
|
|
|
*/ |
|
|
|
|
if (notNull) |
|
|
|
|
{ |
|
|
|
|
ConstraintTest *r = makeNode(ConstraintTest); |
|
|
|
|
|
|
|
|
|
r->arg = arg; |
|
|
|
|
r->testtype = CONSTR_TEST_NOTNULL; |
|
|
|
|
r->name = notNull; |
|
|
|
|
r->check_expr = NULL; |
|
|
|
|
|
|
|
|
|
arg = (Node *) r; |
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return arg; |
|
|
|
|
} |
|
|
|
|
|