Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 03b0a58

Browse files
committed
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.
1 parent 1ca0874 commit 03b0a58

File tree

4 files changed

+119
-84
lines changed

4 files changed

+119
-84
lines changed

doc/src/sgml/typeconv.sgml

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,17 @@ this step.)
458458
</para></step>
459459
<step performance="required">
460460
<para>
461+
If no exact match appears in the catalog, see whether the function call appears
462+
to be a trivial type coercion request. This happens if the function call
463+
has just one argument and the function name is the same as the (internal)
464+
name of some data type. Furthermore, the function argument must be either
465+
an unknown-type literal or a type that is binary-compatible with the named
466+
data type. When these conditions are met, the function argument is coerced
467+
to the named data type without any explicit function call.
468+
</para>
469+
</step>
470+
<step performance="required">
471+
<para>
461472
Look for the best match.
462473
</para>
463474
<substeps>
@@ -519,17 +530,6 @@ then fail.
519530
</step>
520531
</substeps>
521532
</step>
522-
<step performance="required">
523-
<para>
524-
If no best match could be identified, see whether the function call appears
525-
to be a trivial type coercion request. This happens if the function call
526-
has just one argument and the function name is the same as the (internal)
527-
name of some data type. Furthermore, the function argument must be either
528-
an unknown-type literal or a type that is binary-compatible with the named
529-
data type. When these conditions are met, the function argument is coerced
530-
to the named data type.
531-
</para>
532-
</step>
533533
</procedure>
534534

535535
<bridgehead renderas="sect2">Examples</bridgehead>

src/backend/commands/indexcmds.c

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.57 2001/08/21 16:36:02 tgl Exp $
11+
* $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.58 2001/10/04 22:06:46 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -240,6 +240,7 @@ FuncIndexArgs(IndexInfo *indexInfo,
240240
List *arglist;
241241
int nargs = 0;
242242
int i;
243+
FuncDetailCode fdresult;
243244
Oid funcid;
244245
Oid rettype;
245246
bool retset;
@@ -282,9 +283,18 @@ FuncIndexArgs(IndexInfo *indexInfo,
282283
* that. So, check to make sure that the selected function has
283284
* exact-match or binary-compatible input types.
284285
*/
285-
if (!func_get_detail(funcIndex->name, nargs, argTypes,
286-
&funcid, &rettype, &retset, &true_typeids))
287-
func_error("DefineIndex", funcIndex->name, nargs, argTypes, NULL);
286+
fdresult = func_get_detail(funcIndex->name, funcIndex->args,
287+
nargs, argTypes,
288+
&funcid, &rettype, &retset,
289+
&true_typeids);
290+
if (fdresult != FUNCDETAIL_NORMAL)
291+
{
292+
if (fdresult == FUNCDETAIL_COERCION)
293+
elog(ERROR, "DefineIndex: functional index must use a real function, not a type coercion"
294+
"\n\tTry specifying the index opclass you want to use, instead");
295+
else
296+
func_error("DefineIndex", funcIndex->name, nargs, argTypes, NULL);
297+
}
288298

289299
if (retset)
290300
elog(ERROR, "DefineIndex: cannot index on a function returning a set");

src/backend/parser/parse_func.c

Lines changed: 80 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.110 2001/08/09 18:28:18 petere Exp $
11+
* $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.111 2001/10/04 22:06:46 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -427,12 +427,7 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
427427
}
428428

429429
/*
430-
* func_get_detail looks up the function in the catalogs, does
431-
* disambiguation for polymorphic functions, handles inheritance, and
432-
* returns the funcid and type and set or singleton status of the
433-
* function's return value. it also returns the true argument types
434-
* to the function. if func_get_detail returns true, the function
435-
* exists. otherwise, there was an error.
430+
* Is it a set, or a function?
436431
*/
437432
if (attisset)
438433
{ /* we know all of these fields already */
@@ -454,61 +449,29 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
454449
}
455450
else
456451
{
457-
bool exists;
452+
FuncDetailCode fdresult;
458453

459-
exists = func_get_detail(funcname, nargs, oid_array, &funcid,
460-
&rettype, &retset, &true_oid_array);
461-
if (!exists)
454+
/*
455+
* func_get_detail looks up the function in the catalogs, does
456+
* disambiguation for polymorphic functions, handles inheritance, and
457+
* returns the funcid and type and set or singleton status of the
458+
* function's return value. it also returns the true argument types
459+
* to the function.
460+
*/
461+
fdresult = func_get_detail(funcname, fargs, nargs, oid_array,
462+
&funcid, &rettype, &retset,
463+
&true_oid_array);
464+
if (fdresult == FUNCDETAIL_COERCION)
462465
{
463-
464466
/*
465-
* If we can't find a function (or can't find a unique
466-
* function), see if this is really a type-coercion request:
467-
* single-argument function call where the function name is a
468-
* type name. If so, and if we can do the coercion trivially,
469-
* just go ahead and do it without requiring there to be a
470-
* real function for it.
471-
*
472-
* "Trivial" coercions are ones that involve binary-compatible
473-
* types and ones that are coercing a previously-unknown-type
474-
* literal constant to a specific type.
475-
*
476-
* DO NOT try to generalize this code to nontrivial coercions,
477-
* because you'll just set up an infinite recursion between
478-
* this routine and coerce_type! We have already failed to
479-
* find a suitable "real" coercion function, so we have to
480-
* fail unless this is a coercion that coerce_type can handle
481-
* by itself. Make sure this code stays in sync with what
482-
* coerce_type does!
467+
* We can do it as a trivial coercion.
468+
* coerce_type can handle these cases, so why duplicate code...
483469
*/
484-
if (nargs == 1)
485-
{
486-
Oid targetType;
487-
488-
targetType = GetSysCacheOid(TYPENAME,
489-
PointerGetDatum(funcname),
490-
0, 0, 0);
491-
if (OidIsValid(targetType))
492-
{
493-
Oid sourceType = oid_array[0];
494-
Node *arg1 = lfirst(fargs);
495-
496-
if ((sourceType == UNKNOWNOID && IsA(arg1, Const)) ||
497-
sourceType == targetType ||
498-
IS_BINARY_COMPATIBLE(sourceType, targetType))
499-
{
500-
501-
/*
502-
* Ah-hah, we can do it as a trivial coercion.
503-
* coerce_type can handle these cases, so why
504-
* duplicate code...
505-
*/
506-
return coerce_type(pstate, arg1,
507-
sourceType, targetType, -1);
508-
}
509-
}
510-
}
511-
470+
return coerce_type(pstate, lfirst(fargs),
471+
oid_array[0], rettype, -1);
472+
}
473+
if (fdresult != FUNCDETAIL_NORMAL)
474+
{
512475
/*
513476
* Oops. Time to die.
514477
*
@@ -1130,26 +1093,29 @@ func_select_candidate(int nargs,
11301093

11311094

11321095
/* func_get_detail()
1096+
*
11331097
* Find the named function in the system catalogs.
11341098
*
11351099
* Attempt to find the named function in the system catalogs with
11361100
* arguments exactly as specified, so that the normal case
11371101
* (exact match) is as quick as possible.
11381102
*
11391103
* If an exact match isn't found:
1140-
* 1) get a vector of all possible input arg type arrays constructed
1104+
* 1) check for possible interpretation as a trivial type coercion
1105+
* 2) get a vector of all possible input arg type arrays constructed
11411106
* from the superclasses of the original input arg types
1142-
* 2) get a list of all possible argument type arrays to the function
1107+
* 3) get a list of all possible argument type arrays to the function
11431108
* with given name and number of arguments
1144-
* 3) for each input arg type array from vector #1:
1109+
* 4) for each input arg type array from vector #1:
11451110
* a) find how many of the function arg type arrays from list #2
11461111
* it can be coerced to
11471112
* b) if the answer is one, we have our function
11481113
* c) if the answer is more than one, attempt to resolve the conflict
11491114
* d) if the answer is zero, try the next array from vector #1
11501115
*/
1151-
bool
1116+
FuncDetailCode
11521117
func_get_detail(char *funcname,
1118+
List *fargs,
11531119
int nargs,
11541120
Oid *argtypes,
11551121
Oid *funcid, /* return value */
@@ -1158,6 +1124,7 @@ func_get_detail(char *funcname,
11581124
Oid **true_typeids) /* return value */
11591125
{
11601126
HeapTuple ftup;
1127+
CandidateList function_typeids;
11611128

11621129
/* attempt to find with arguments exactly as specified... */
11631130
ftup = SearchSysCache(PROCNAME,
@@ -1173,12 +1140,59 @@ func_get_detail(char *funcname,
11731140
}
11741141
else
11751142
{
1143+
/*
1144+
* If we didn't find an exact match, next consider the possibility
1145+
* that this is really a type-coercion request: a single-argument
1146+
* function call where the function name is a type name. If so,
1147+
* and if we can do the coercion trivially (no run-time function
1148+
* call needed), then go ahead and treat the "function call" as
1149+
* a coercion. This interpretation needs to be given higher
1150+
* priority than interpretations involving a type coercion followed
1151+
* by a function call, otherwise we can produce surprising results.
1152+
* For example, we want "text(varchar)" to be interpreted as a
1153+
* trivial coercion, not as "text(name(varchar))" which the code
1154+
* below this point is entirely capable of selecting.
1155+
*
1156+
* "Trivial" coercions are ones that involve binary-compatible
1157+
* types and ones that are coercing a previously-unknown-type
1158+
* literal constant to a specific type.
1159+
*
1160+
* NB: it's important that this code stays in sync with what
1161+
* coerce_type can do, because the caller will try to apply
1162+
* coerce_type if we return FUNCDETAIL_COERCION. If we return
1163+
* that result for something coerce_type can't handle, we'll
1164+
* cause infinite recursion between this module and coerce_type!
1165+
*/
1166+
if (nargs == 1)
1167+
{
1168+
Oid targetType;
1169+
1170+
targetType = GetSysCacheOid(TYPENAME,
1171+
PointerGetDatum(funcname),
1172+
0, 0, 0);
1173+
if (OidIsValid(targetType))
1174+
{
1175+
Oid sourceType = argtypes[0];
1176+
Node *arg1 = lfirst(fargs);
1177+
1178+
if ((sourceType == UNKNOWNOID && IsA(arg1, Const)) ||
1179+
sourceType == targetType ||
1180+
IS_BINARY_COMPATIBLE(sourceType, targetType))
1181+
{
1182+
/* Yup, it's a type coercion */
1183+
*funcid = InvalidOid;
1184+
*rettype = targetType;
1185+
*retset = false;
1186+
*true_typeids = argtypes;
1187+
return FUNCDETAIL_COERCION;
1188+
}
1189+
}
1190+
}
11761191

11771192
/*
11781193
* didn't find an exact match, so now try to match up
11791194
* candidates...
11801195
*/
1181-
CandidateList function_typeids;
11821196

11831197
function_typeids = func_get_candidates(funcname, nargs);
11841198

@@ -1268,9 +1282,10 @@ func_get_detail(char *funcname,
12681282
*rettype = pform->prorettype;
12691283
*retset = pform->proretset;
12701284
ReleaseSysCache(ftup);
1271-
return true;
1285+
return FUNCDETAIL_NORMAL;
12721286
}
1273-
return false;
1287+
1288+
return FUNCDETAIL_NOTFOUND;
12741289
} /* func_get_detail() */
12751290

12761291
/*

src/include/parser/parse_func.h

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
88
* Portions Copyright (c) 1994, Regents of the University of California
99
*
10-
* $Id: parse_func.h,v 1.31 2001/05/19 00:33:20 momjian Exp $
10+
* $Id: parse_func.h,v 1.32 2001/10/04 22:06:46 tgl Exp $
1111
*
1212
*-------------------------------------------------------------------------
1313
*/
@@ -38,16 +38,26 @@ typedef struct _CandidateList
3838
struct _CandidateList *next;
3939
} *CandidateList;
4040

41+
/* Result codes for func_get_detail */
42+
typedef enum
43+
{
44+
FUNCDETAIL_NOTFOUND, /* no suitable interpretation */
45+
FUNCDETAIL_NORMAL, /* found a matching function */
46+
FUNCDETAIL_COERCION /* it's a type coercion request */
47+
} FuncDetailCode;
48+
49+
4150
extern Node *ParseNestedFuncOrColumn(ParseState *pstate, Attr *attr,
4251
int precedence);
4352
extern Node *ParseFuncOrColumn(ParseState *pstate,
4453
char *funcname, List *fargs,
4554
bool agg_star, bool agg_distinct,
4655
int precedence);
4756

48-
extern bool func_get_detail(char *funcname, int nargs, Oid *argtypes,
49-
Oid *funcid, Oid *rettype,
50-
bool *retset, Oid **true_typeids);
57+
extern FuncDetailCode func_get_detail(char *funcname, List *fargs,
58+
int nargs, Oid *argtypes,
59+
Oid *funcid, Oid *rettype,
60+
bool *retset, Oid **true_typeids);
5161

5262
extern bool typeInheritsFrom(Oid subclassTypeId, Oid superclassTypeId);
5363

0 commit comments

Comments
 (0)