Consider interpreting a function call as a trivial (binary-compatible)

type coercion after failing to find an exact match in pg_proc, but before
considering interpretations that involve a function call with one or
more argument type coercions.  This avoids surprises wherein what looks
like a type coercion is interpreted as coercing to some third type and
then to the destination type, as in Dave Blasby's bug report of 3-Oct-01.
See subsequent discussion in pghackers.
This commit is contained in:
Tom Lane 2001-10-04 22:06:46 +00:00
parent 1ca0874faa
commit 03b0a589d1
4 changed files with 119 additions and 84 deletions

View File

@ -458,6 +458,17 @@ this step.)
</para></step>
<step performance="required">
<para>
If no exact match appears in the catalog, see whether the function call appears
to be a trivial type coercion request. This happens if the function call
has just one argument and the function name is the same as the (internal)
name of some data type. Furthermore, the function argument must be either
an unknown-type literal or a type that is binary-compatible with the named
data type. When these conditions are met, the function argument is coerced
to the named data type without any explicit function call.
</para>
</step>
<step performance="required">
<para>
Look for the best match.
</para>
<substeps>
@ -519,17 +530,6 @@ then fail.
</step>
</substeps>
</step>
<step performance="required">
<para>
If no best match could be identified, see whether the function call appears
to be a trivial type coercion request. This happens if the function call
has just one argument and the function name is the same as the (internal)
name of some data type. Furthermore, the function argument must be either
an unknown-type literal or a type that is binary-compatible with the named
data type. When these conditions are met, the function argument is coerced
to the named data type.
</para>
</step>
</procedure>
<bridgehead renderas="sect2">Examples</bridgehead>

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.57 2001/08/21 16:36:02 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.58 2001/10/04 22:06:46 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -240,6 +240,7 @@ FuncIndexArgs(IndexInfo *indexInfo,
List *arglist;
int nargs = 0;
int i;
FuncDetailCode fdresult;
Oid funcid;
Oid rettype;
bool retset;
@ -282,9 +283,18 @@ FuncIndexArgs(IndexInfo *indexInfo,
* that. So, check to make sure that the selected function has
* exact-match or binary-compatible input types.
*/
if (!func_get_detail(funcIndex->name, nargs, argTypes,
&funcid, &rettype, &retset, &true_typeids))
func_error("DefineIndex", funcIndex->name, nargs, argTypes, NULL);
fdresult = func_get_detail(funcIndex->name, funcIndex->args,
nargs, argTypes,
&funcid, &rettype, &retset,
&true_typeids);
if (fdresult != FUNCDETAIL_NORMAL)
{
if (fdresult == FUNCDETAIL_COERCION)
elog(ERROR, "DefineIndex: functional index must use a real function, not a type coercion"
"\n\tTry specifying the index opclass you want to use, instead");
else
func_error("DefineIndex", funcIndex->name, nargs, argTypes, NULL);
}
if (retset)
elog(ERROR, "DefineIndex: cannot index on a function returning a set");

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.110 2001/08/09 18:28:18 petere Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.111 2001/10/04 22:06:46 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -427,12 +427,7 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
}
/*
* func_get_detail looks up the function in the catalogs, does
* disambiguation for polymorphic functions, handles inheritance, and
* returns the funcid and type and set or singleton status of the
* function's return value. it also returns the true argument types
* to the function. if func_get_detail returns true, the function
* exists. otherwise, there was an error.
* Is it a set, or a function?
*/
if (attisset)
{ /* we know all of these fields already */
@ -454,61 +449,29 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
}
else
{
bool exists;
FuncDetailCode fdresult;
exists = func_get_detail(funcname, nargs, oid_array, &funcid,
&rettype, &retset, &true_oid_array);
if (!exists)
/*
* func_get_detail looks up the function in the catalogs, does
* disambiguation for polymorphic functions, handles inheritance, and
* returns the funcid and type and set or singleton status of the
* function's return value. it also returns the true argument types
* to the function.
*/
fdresult = func_get_detail(funcname, fargs, nargs, oid_array,
&funcid, &rettype, &retset,
&true_oid_array);
if (fdresult == FUNCDETAIL_COERCION)
{
/*
* If we can't find a function (or can't find a unique
* function), see if this is really a type-coercion request:
* single-argument function call where the function name is a
* type name. If so, and if we can do the coercion trivially,
* just go ahead and do it without requiring there to be a
* real function for it.
*
* "Trivial" coercions are ones that involve binary-compatible
* types and ones that are coercing a previously-unknown-type
* literal constant to a specific type.
*
* DO NOT try to generalize this code to nontrivial coercions,
* because you'll just set up an infinite recursion between
* this routine and coerce_type! We have already failed to
* find a suitable "real" coercion function, so we have to
* fail unless this is a coercion that coerce_type can handle
* by itself. Make sure this code stays in sync with what
* coerce_type does!
* We can do it as a trivial coercion.
* coerce_type can handle these cases, so why duplicate code...
*/
if (nargs == 1)
{
Oid targetType;
targetType = GetSysCacheOid(TYPENAME,
PointerGetDatum(funcname),
0, 0, 0);
if (OidIsValid(targetType))
{
Oid sourceType = oid_array[0];
Node *arg1 = lfirst(fargs);
if ((sourceType == UNKNOWNOID && IsA(arg1, Const)) ||
sourceType == targetType ||
IS_BINARY_COMPATIBLE(sourceType, targetType))
{
/*
* Ah-hah, we can do it as a trivial coercion.
* coerce_type can handle these cases, so why
* duplicate code...
*/
return coerce_type(pstate, arg1,
sourceType, targetType, -1);
}
}
}
return coerce_type(pstate, lfirst(fargs),
oid_array[0], rettype, -1);
}
if (fdresult != FUNCDETAIL_NORMAL)
{
/*
* Oops. Time to die.
*
@ -1130,6 +1093,7 @@ func_select_candidate(int nargs,
/* func_get_detail()
*
* Find the named function in the system catalogs.
*
* Attempt to find the named function in the system catalogs with
@ -1137,19 +1101,21 @@ func_select_candidate(int nargs,
* (exact match) is as quick as possible.
*
* If an exact match isn't found:
* 1) get a vector of all possible input arg type arrays constructed
* 1) check for possible interpretation as a trivial type coercion
* 2) get a vector of all possible input arg type arrays constructed
* from the superclasses of the original input arg types
* 2) get a list of all possible argument type arrays to the function
* 3) get a list of all possible argument type arrays to the function
* with given name and number of arguments
* 3) for each input arg type array from vector #1:
* 4) for each input arg type array from vector #1:
* a) find how many of the function arg type arrays from list #2
* it can be coerced to
* b) if the answer is one, we have our function
* c) if the answer is more than one, attempt to resolve the conflict
* d) if the answer is zero, try the next array from vector #1
*/
bool
FuncDetailCode
func_get_detail(char *funcname,
List *fargs,
int nargs,
Oid *argtypes,
Oid *funcid, /* return value */
@ -1158,6 +1124,7 @@ func_get_detail(char *funcname,
Oid **true_typeids) /* return value */
{
HeapTuple ftup;
CandidateList function_typeids;
/* attempt to find with arguments exactly as specified... */
ftup = SearchSysCache(PROCNAME,
@ -1173,12 +1140,59 @@ func_get_detail(char *funcname,
}
else
{
/*
* If we didn't find an exact match, next consider the possibility
* that this is really a type-coercion request: a single-argument
* function call where the function name is a type name. If so,
* and if we can do the coercion trivially (no run-time function
* call needed), then go ahead and treat the "function call" as
* a coercion. This interpretation needs to be given higher
* priority than interpretations involving a type coercion followed
* by a function call, otherwise we can produce surprising results.
* For example, we want "text(varchar)" to be interpreted as a
* trivial coercion, not as "text(name(varchar))" which the code
* below this point is entirely capable of selecting.
*
* "Trivial" coercions are ones that involve binary-compatible
* types and ones that are coercing a previously-unknown-type
* literal constant to a specific type.
*
* NB: it's important that this code stays in sync with what
* coerce_type can do, because the caller will try to apply
* coerce_type if we return FUNCDETAIL_COERCION. If we return
* that result for something coerce_type can't handle, we'll
* cause infinite recursion between this module and coerce_type!
*/
if (nargs == 1)
{
Oid targetType;
targetType = GetSysCacheOid(TYPENAME,
PointerGetDatum(funcname),
0, 0, 0);
if (OidIsValid(targetType))
{
Oid sourceType = argtypes[0];
Node *arg1 = lfirst(fargs);
if ((sourceType == UNKNOWNOID && IsA(arg1, Const)) ||
sourceType == targetType ||
IS_BINARY_COMPATIBLE(sourceType, targetType))
{
/* Yup, it's a type coercion */
*funcid = InvalidOid;
*rettype = targetType;
*retset = false;
*true_typeids = argtypes;
return FUNCDETAIL_COERCION;
}
}
}
/*
* didn't find an exact match, so now try to match up
* candidates...
*/
CandidateList function_typeids;
function_typeids = func_get_candidates(funcname, nargs);
@ -1268,9 +1282,10 @@ func_get_detail(char *funcname,
*rettype = pform->prorettype;
*retset = pform->proretset;
ReleaseSysCache(ftup);
return true;
return FUNCDETAIL_NORMAL;
}
return false;
return FUNCDETAIL_NOTFOUND;
} /* func_get_detail() */
/*

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: parse_func.h,v 1.31 2001/05/19 00:33:20 momjian Exp $
* $Id: parse_func.h,v 1.32 2001/10/04 22:06:46 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -38,6 +38,15 @@ typedef struct _CandidateList
struct _CandidateList *next;
} *CandidateList;
/* Result codes for func_get_detail */
typedef enum
{
FUNCDETAIL_NOTFOUND, /* no suitable interpretation */
FUNCDETAIL_NORMAL, /* found a matching function */
FUNCDETAIL_COERCION /* it's a type coercion request */
} FuncDetailCode;
extern Node *ParseNestedFuncOrColumn(ParseState *pstate, Attr *attr,
int precedence);
extern Node *ParseFuncOrColumn(ParseState *pstate,
@ -45,9 +54,10 @@ extern Node *ParseFuncOrColumn(ParseState *pstate,
bool agg_star, bool agg_distinct,
int precedence);
extern bool func_get_detail(char *funcname, int nargs, Oid *argtypes,
Oid *funcid, Oid *rettype,
bool *retset, Oid **true_typeids);
extern FuncDetailCode func_get_detail(char *funcname, List *fargs,
int nargs, Oid *argtypes,
Oid *funcid, Oid *rettype,
bool *retset, Oid **true_typeids);
extern bool typeInheritsFrom(Oid subclassTypeId, Oid superclassTypeId);