提交 681ed4e2 编写于 作者: T Tom Lane

Code cleanups: make non-implicit WITHOUT FUNCTION casts work, avoid

redundant pg_cast searches, fix obsolete comments.
上级 a03f9adb
......@@ -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;
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册