mirror of
https://github.com/postgres/postgres.git
synced 2025-07-30 11:03:19 +03:00
Code cleanups: make non-implicit WITHOUT FUNCTION casts work, avoid
redundant pg_cast searches, fix obsolete comments.
This commit is contained in:
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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"
|
#include "utils/syscache.h"
|
||||||
|
|
||||||
|
|
||||||
Oid DemoteType(Oid inType);
|
|
||||||
Oid PromoteTypeToNext(Oid inType);
|
|
||||||
|
|
||||||
static Oid PreferredType(CATEGORY category, Oid type);
|
static Oid PreferredType(CATEGORY category, Oid type);
|
||||||
static Node *build_func_call(Oid funcid, Oid rettype, List *args);
|
static bool find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId,
|
||||||
static Oid find_coercion_function(Oid targetTypeId, Oid sourceTypeId,
|
bool isExplicit,
|
||||||
bool isExplicit);
|
Oid *funcid);
|
||||||
static Oid find_typmod_coercion_function(Oid typeId);
|
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 *
|
Node *
|
||||||
coerce_type(ParseState *pstate, Node *node, Oid inputTypeId,
|
coerce_type(ParseState *pstate, Node *node, Oid inputTypeId,
|
||||||
Oid targetTypeId, int32 atttypmod, bool isExplicit)
|
Oid targetTypeId, int32 atttypmod, bool isExplicit)
|
||||||
{
|
{
|
||||||
Node *result;
|
Node *result;
|
||||||
|
Oid funcId;
|
||||||
|
|
||||||
if (targetTypeId == inputTypeId ||
|
if (targetTypeId == inputTypeId ||
|
||||||
node == NULL)
|
node == NULL)
|
||||||
@ -118,25 +121,71 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId,
|
|||||||
/* assume can_coerce_type verified that implicit coercion is okay */
|
/* assume can_coerce_type verified that implicit coercion is okay */
|
||||||
result = node;
|
result = node;
|
||||||
}
|
}
|
||||||
else if (IsBinaryCompatible(inputTypeId, targetTypeId))
|
else if (find_coercion_pathway(targetTypeId, inputTypeId, isExplicit,
|
||||||
|
&funcId))
|
||||||
{
|
{
|
||||||
/*
|
if (OidIsValid(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.
|
* Generate an expression tree representing run-time application
|
||||||
*
|
* of the conversion function. If we are dealing with a domain
|
||||||
* Also, domains may have value restrictions beyond the base type
|
* target type, the conversion function will yield the base type.
|
||||||
* that must be accounted for.
|
*/
|
||||||
*/
|
Oid baseTypeId = getBaseType(targetTypeId);
|
||||||
result = coerce_type_constraints(pstate, node, targetTypeId, true);
|
|
||||||
/*
|
result = build_func_call(funcId, baseTypeId, makeList1(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,
|
* If domain, test against domain constraints and relabel with
|
||||||
* which is likely but not certain (wrong if target is a domain,
|
* domain type ID
|
||||||
* in any case).
|
*/
|
||||||
*/
|
if (targetTypeId != baseTypeId)
|
||||||
result = (Node *) makeRelabelType(result, targetTypeId, -1);
|
{
|
||||||
|
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))
|
else if (typeInheritsFrom(inputTypeId, targetTypeId))
|
||||||
{
|
{
|
||||||
@ -149,74 +198,23 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId,
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/*
|
/* If we get here, caller blew it */
|
||||||
* Otherwise, find the appropriate type conversion function
|
elog(ERROR, "coerce_type: no conversion function from %s to %s",
|
||||||
* (caller should have determined that there is one), and generate
|
format_type_be(inputTypeId), format_type_be(targetTypeId));
|
||||||
* an expression tree representing run-time application of the
|
result = NULL; /* keep compiler quiet */
|
||||||
* 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* can_coerce_type()
|
/*
|
||||||
* Can input_typeids be coerced to func_typeids?
|
* 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.
|
|
||||||
*
|
*
|
||||||
* We must be told whether this is an implicit or explicit coercion
|
* We must be told whether this is an implicit or explicit coercion
|
||||||
* (explicit being a CAST construct, explicit function call, etc).
|
* (explicit being a CAST construct, explicit function call, etc).
|
||||||
* We will accept a wider set of coercion cases for an explicit coercion.
|
* 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
|
bool
|
||||||
can_coerce_type(int nargs, Oid *input_typeids, Oid *func_typeids,
|
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
|
* If pg_cast shows that we can coerce, accept. This test now
|
||||||
* through...
|
* covers both binary-compatible and coercion-function cases.
|
||||||
*/
|
*/
|
||||||
if (IsBinaryCompatible(inputTypeId, targetTypeId))
|
if (find_coercion_pathway(targetTypeId, inputTypeId, isExplicit,
|
||||||
|
&funcId))
|
||||||
continue;
|
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))
|
if (typeInheritsFrom(inputTypeId, targetTypeId))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Else, try for run-time conversion using functions: look for a
|
* Else, cannot coerce at this argument position
|
||||||
* 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.
|
|
||||||
*/
|
*/
|
||||||
funcId = find_coercion_function(getBaseType(targetTypeId),
|
return false;
|
||||||
getBaseType(inputTypeId),
|
|
||||||
isExplicit);
|
|
||||||
if (!OidIsValid(funcId))
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
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()
|
/* coerce_type_typmod()
|
||||||
* Force a value to a particular typmod, if meaningful and possible.
|
* 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
|
* The caller must have already ensured that the value is of the correct
|
||||||
* type, typically by applying coerce_type.
|
* 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
|
* 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
|
* 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 *
|
Node *
|
||||||
coerce_type_typmod(ParseState *pstate, Node *node,
|
coerce_type_typmod(ParseState *pstate, Node *node,
|
||||||
@ -600,100 +652,6 @@ TypeCategory(Oid inType)
|
|||||||
} /* TypeCategory() */
|
} /* 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()
|
/* IsPreferredType()
|
||||||
* Check if this type is a preferred type.
|
* Check if this type is a preferred type.
|
||||||
* XXX This should be moved to system catalog lookups
|
* XXX This should be moved to system catalog lookups
|
||||||
@ -733,7 +691,13 @@ PreferredType(CATEGORY category, Oid type)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case (NUMERIC_TYPE):
|
case (NUMERIC_TYPE):
|
||||||
if (TypeIsOidGroup(type))
|
if (type == OIDOID ||
|
||||||
|
type == REGPROCOID ||
|
||||||
|
type == REGPROCEDUREOID ||
|
||||||
|
type == REGOPEROID ||
|
||||||
|
type == REGOPERATOROID ||
|
||||||
|
type == REGCLASSOID ||
|
||||||
|
type == REGTYPEOID)
|
||||||
result = OIDOID;
|
result = OIDOID;
|
||||||
else if (type == NUMERICOID)
|
else if (type == NUMERICOID)
|
||||||
result = NUMERICOID;
|
result = NUMERICOID;
|
||||||
@ -768,30 +732,85 @@ PreferredType(CATEGORY category, Oid type)
|
|||||||
return result;
|
return result;
|
||||||
} /* PreferredType() */
|
} /* PreferredType() */
|
||||||
|
|
||||||
/*
|
|
||||||
* find_coercion_function
|
/* IsBinaryCompatible()
|
||||||
* Look for a coercion function between two types.
|
* Check if two types are binary-compatible.
|
||||||
*
|
*
|
||||||
* A coercion function must be named after (the internal name of) its
|
* This notion allows us to cheat and directly exchange values without
|
||||||
* result type, and must accept exactly the specified input type. We
|
* going through the trouble of calling a conversion function.
|
||||||
* 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 routine is also used to look for length-coercion functions, which
|
* As of 7.3, binary compatibility isn't hardwired into the code anymore.
|
||||||
* are similar but accept a second argument. secondArgType is the type
|
* We consider two types binary-compatible if there is an implicit,
|
||||||
* of the second argument (normally INT4OID), or InvalidOid if we are
|
* no-function-needed pg_cast entry. NOTE that we assume that such
|
||||||
* looking for a regular coercion function.
|
* entries are symmetric, ie, it doesn't matter which type we consider
|
||||||
*
|
* source and which target. (cf. checks in opr_sanity regression test)
|
||||||
* If a function is found, return its pg_proc OID; else return InvalidOid.
|
|
||||||
*/
|
*/
|
||||||
static Oid
|
bool
|
||||||
find_coercion_function(Oid targetTypeId, Oid sourceTypeId, bool isExplicit)
|
IsBinaryCompatible(Oid type1, Oid type2)
|
||||||
{
|
{
|
||||||
Oid funcid = InvalidOid;
|
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 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 bool
|
||||||
|
find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId, bool isExplicit,
|
||||||
|
Oid *funcid)
|
||||||
|
{
|
||||||
|
bool result = false;
|
||||||
HeapTuple tuple;
|
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,
|
tuple = SearchSysCache(CASTSOURCETARGET,
|
||||||
ObjectIdGetDatum(sourceTypeId),
|
ObjectIdGetDatum(sourceTypeId),
|
||||||
ObjectIdGetDatum(targetTypeId),
|
ObjectIdGetDatum(targetTypeId),
|
||||||
@ -799,18 +818,36 @@ find_coercion_function(Oid targetTypeId, Oid sourceTypeId, bool isExplicit)
|
|||||||
|
|
||||||
if (HeapTupleIsValid(tuple))
|
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)
|
if (isExplicit || castForm->castimplicit)
|
||||||
funcid = cform->castfunc;
|
{
|
||||||
|
*funcid = castForm->castfunc;
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
|
||||||
ReleaseSysCache(tuple);
|
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
|
static Oid
|
||||||
find_typmod_coercion_function(Oid typeId)
|
find_typmod_coercion_function(Oid typeId)
|
||||||
{
|
{
|
||||||
@ -849,6 +886,7 @@ find_typmod_coercion_function(Oid typeId)
|
|||||||
}
|
}
|
||||||
|
|
||||||
ReleaseSysCache(targetType);
|
ReleaseSysCache(targetType);
|
||||||
|
|
||||||
return funcid;
|
return funcid;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -877,74 +915,3 @@ build_func_call(Oid funcid, Oid rettype, List *args)
|
|||||||
|
|
||||||
return (Node *) expr;
|
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;
|
|
||||||
}
|
|
||||||
|
Reference in New Issue
Block a user