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:
parent
1ca0874faa
commit
03b0a589d1
|
@ -458,6 +458,17 @@ this step.)
|
||||||
</para></step>
|
</para></step>
|
||||||
<step performance="required">
|
<step performance="required">
|
||||||
<para>
|
<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.
|
Look for the best match.
|
||||||
</para>
|
</para>
|
||||||
<substeps>
|
<substeps>
|
||||||
|
@ -519,17 +530,6 @@ then fail.
|
||||||
</step>
|
</step>
|
||||||
</substeps>
|
</substeps>
|
||||||
</step>
|
</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>
|
</procedure>
|
||||||
|
|
||||||
<bridgehead renderas="sect2">Examples</bridgehead>
|
<bridgehead renderas="sect2">Examples</bridgehead>
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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;
|
List *arglist;
|
||||||
int nargs = 0;
|
int nargs = 0;
|
||||||
int i;
|
int i;
|
||||||
|
FuncDetailCode fdresult;
|
||||||
Oid funcid;
|
Oid funcid;
|
||||||
Oid rettype;
|
Oid rettype;
|
||||||
bool retset;
|
bool retset;
|
||||||
|
@ -282,9 +283,18 @@ FuncIndexArgs(IndexInfo *indexInfo,
|
||||||
* that. So, check to make sure that the selected function has
|
* that. So, check to make sure that the selected function has
|
||||||
* exact-match or binary-compatible input types.
|
* exact-match or binary-compatible input types.
|
||||||
*/
|
*/
|
||||||
if (!func_get_detail(funcIndex->name, nargs, argTypes,
|
fdresult = func_get_detail(funcIndex->name, funcIndex->args,
|
||||||
&funcid, &rettype, &retset, &true_typeids))
|
nargs, argTypes,
|
||||||
func_error("DefineIndex", funcIndex->name, nargs, argTypes, NULL);
|
&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)
|
if (retset)
|
||||||
elog(ERROR, "DefineIndex: cannot index on a function returning a set");
|
elog(ERROR, "DefineIndex: cannot index on a function returning a set");
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* Is it a set, or a function?
|
||||||
* 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.
|
|
||||||
*/
|
*/
|
||||||
if (attisset)
|
if (attisset)
|
||||||
{ /* we know all of these fields already */
|
{ /* we know all of these fields already */
|
||||||
|
@ -454,61 +449,29 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
bool exists;
|
FuncDetailCode fdresult;
|
||||||
|
|
||||||
exists = func_get_detail(funcname, nargs, oid_array, &funcid,
|
/*
|
||||||
&rettype, &retset, &true_oid_array);
|
* func_get_detail looks up the function in the catalogs, does
|
||||||
if (!exists)
|
* 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
|
* We can do it as a trivial coercion.
|
||||||
* function), see if this is really a type-coercion request:
|
* coerce_type can handle these cases, so why duplicate code...
|
||||||
* 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!
|
|
||||||
*/
|
*/
|
||||||
if (nargs == 1)
|
return coerce_type(pstate, lfirst(fargs),
|
||||||
{
|
oid_array[0], rettype, -1);
|
||||||
Oid targetType;
|
}
|
||||||
|
if (fdresult != FUNCDETAIL_NORMAL)
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Oops. Time to die.
|
* Oops. Time to die.
|
||||||
*
|
*
|
||||||
|
@ -1130,6 +1093,7 @@ func_select_candidate(int nargs,
|
||||||
|
|
||||||
|
|
||||||
/* func_get_detail()
|
/* func_get_detail()
|
||||||
|
*
|
||||||
* Find the named function in the system catalogs.
|
* Find the named function in the system catalogs.
|
||||||
*
|
*
|
||||||
* Attempt to find the named function in the system catalogs with
|
* 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.
|
* (exact match) is as quick as possible.
|
||||||
*
|
*
|
||||||
* If an exact match isn't found:
|
* 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
|
* 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
|
* 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
|
* a) find how many of the function arg type arrays from list #2
|
||||||
* it can be coerced to
|
* it can be coerced to
|
||||||
* b) if the answer is one, we have our function
|
* b) if the answer is one, we have our function
|
||||||
* c) if the answer is more than one, attempt to resolve the conflict
|
* 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
|
* d) if the answer is zero, try the next array from vector #1
|
||||||
*/
|
*/
|
||||||
bool
|
FuncDetailCode
|
||||||
func_get_detail(char *funcname,
|
func_get_detail(char *funcname,
|
||||||
|
List *fargs,
|
||||||
int nargs,
|
int nargs,
|
||||||
Oid *argtypes,
|
Oid *argtypes,
|
||||||
Oid *funcid, /* return value */
|
Oid *funcid, /* return value */
|
||||||
|
@ -1158,6 +1124,7 @@ func_get_detail(char *funcname,
|
||||||
Oid **true_typeids) /* return value */
|
Oid **true_typeids) /* return value */
|
||||||
{
|
{
|
||||||
HeapTuple ftup;
|
HeapTuple ftup;
|
||||||
|
CandidateList function_typeids;
|
||||||
|
|
||||||
/* attempt to find with arguments exactly as specified... */
|
/* attempt to find with arguments exactly as specified... */
|
||||||
ftup = SearchSysCache(PROCNAME,
|
ftup = SearchSysCache(PROCNAME,
|
||||||
|
@ -1173,12 +1140,59 @@ func_get_detail(char *funcname,
|
||||||
}
|
}
|
||||||
else
|
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
|
* didn't find an exact match, so now try to match up
|
||||||
* candidates...
|
* candidates...
|
||||||
*/
|
*/
|
||||||
CandidateList function_typeids;
|
|
||||||
|
|
||||||
function_typeids = func_get_candidates(funcname, nargs);
|
function_typeids = func_get_candidates(funcname, nargs);
|
||||||
|
|
||||||
|
@ -1268,9 +1282,10 @@ func_get_detail(char *funcname,
|
||||||
*rettype = pform->prorettype;
|
*rettype = pform->prorettype;
|
||||||
*retset = pform->proretset;
|
*retset = pform->proretset;
|
||||||
ReleaseSysCache(ftup);
|
ReleaseSysCache(ftup);
|
||||||
return true;
|
return FUNCDETAIL_NORMAL;
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
|
return FUNCDETAIL_NOTFOUND;
|
||||||
} /* func_get_detail() */
|
} /* func_get_detail() */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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;
|
struct _CandidateList *next;
|
||||||
} *CandidateList;
|
} *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,
|
extern Node *ParseNestedFuncOrColumn(ParseState *pstate, Attr *attr,
|
||||||
int precedence);
|
int precedence);
|
||||||
extern Node *ParseFuncOrColumn(ParseState *pstate,
|
extern Node *ParseFuncOrColumn(ParseState *pstate,
|
||||||
|
@ -45,9 +54,10 @@ extern Node *ParseFuncOrColumn(ParseState *pstate,
|
||||||
bool agg_star, bool agg_distinct,
|
bool agg_star, bool agg_distinct,
|
||||||
int precedence);
|
int precedence);
|
||||||
|
|
||||||
extern bool func_get_detail(char *funcname, int nargs, Oid *argtypes,
|
extern FuncDetailCode func_get_detail(char *funcname, List *fargs,
|
||||||
Oid *funcid, Oid *rettype,
|
int nargs, Oid *argtypes,
|
||||||
bool *retset, Oid **true_typeids);
|
Oid *funcid, Oid *rettype,
|
||||||
|
bool *retset, Oid **true_typeids);
|
||||||
|
|
||||||
extern bool typeInheritsFrom(Oid subclassTypeId, Oid superclassTypeId);
|
extern bool typeInheritsFrom(Oid subclassTypeId, Oid superclassTypeId);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue