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

Commit d6d07a0

Browse files
committed
SQL functions can have arguments and results declared ANYARRAY or
ANYELEMENT. The effect is to postpone typechecking of the function body until runtime. Documentation is still lacking. Original patch by Joe Conway, modified to postpone type checking by Tom Lane.
1 parent 71e9f3b commit d6d07a0

File tree

8 files changed

+152
-57
lines changed

8 files changed

+152
-57
lines changed

src/backend/catalog/pg_proc.c

Lines changed: 63 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $Header: /cvsroot/pgsql/src/backend/catalog/pg_proc.c,v 1.97 2003/06/15 17:59:10 tgl Exp $
11+
* $Header: /cvsroot/pgsql/src/backend/catalog/pg_proc.c,v 1.98 2003/07/01 00:04:37 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -33,7 +33,6 @@
3333
#include "utils/syscache.h"
3434

3535

36-
static void checkretval(Oid rettype, char fn_typtype, List *queryTreeList);
3736
Datum fmgr_internal_validator(PG_FUNCTION_ARGS);
3837
Datum fmgr_c_validator(PG_FUNCTION_ARGS);
3938
Datum fmgr_sql_validator(PG_FUNCTION_ARGS);
@@ -317,15 +316,20 @@ ProcedureCreate(const char *procedureName,
317316
}
318317

319318
/*
320-
* checkretval() -- check return value of a list of sql parse trees.
319+
* check_sql_fn_retval() -- check return value of a list of sql parse trees.
321320
*
322321
* The return value of a sql function is the value returned by
323-
* the final query in the function. We do some ad-hoc define-time
324-
* type checking here to be sure that the user is returning the
325-
* type he claims.
322+
* the final query in the function. We do some ad-hoc type checking here
323+
* to be sure that the user is returning the type he claims.
324+
*
325+
* This is normally applied during function definition, but in the case
326+
* of a function with polymorphic arguments, we instead apply it during
327+
* function execution startup. The rettype is then the actual resolved
328+
* output type of the function, rather than the declared type. (Therefore,
329+
* we should never see ANYARRAY or ANYELEMENT as rettype.)
326330
*/
327-
static void
328-
checkretval(Oid rettype, char fn_typtype, List *queryTreeList)
331+
void
332+
check_sql_fn_retval(Oid rettype, char fn_typtype, List *queryTreeList)
329333
{
330334
Query *parse;
331335
int cmd;
@@ -472,7 +476,7 @@ checkretval(Oid rettype, char fn_typtype, List *queryTreeList)
472476

473477
relation_close(reln, AccessShareLock);
474478
}
475-
else if (fn_typtype == 'p' && rettype == RECORDOID)
479+
else if (rettype == RECORDOID)
476480
{
477481
/* Shouldn't have a typerelid */
478482
Assert(typerelid == InvalidOid);
@@ -482,6 +486,14 @@ checkretval(Oid rettype, char fn_typtype, List *queryTreeList)
482486
* tuple.
483487
*/
484488
}
489+
else if (rettype == ANYARRAYOID || rettype == ANYELEMENTOID)
490+
{
491+
/*
492+
* This should already have been caught ...
493+
*/
494+
elog(ERROR, "functions returning ANYARRAY or ANYELEMENT must " \
495+
"have at least one argument of either type");
496+
}
485497
else
486498
elog(ERROR, "return type %s is not supported for SQL functions",
487499
format_type_be(rettype));
@@ -505,7 +517,9 @@ fmgr_internal_validator(PG_FUNCTION_ARGS)
505517
Datum tmp;
506518
char *prosrc;
507519

508-
tuple = SearchSysCache(PROCOID, funcoid, 0, 0, 0);
520+
tuple = SearchSysCache(PROCOID,
521+
ObjectIdGetDatum(funcoid),
522+
0, 0, 0);
509523
if (!HeapTupleIsValid(tuple))
510524
elog(ERROR, "cache lookup of function %u failed", funcoid);
511525
proc = (Form_pg_proc) GETSTRUCT(tuple);
@@ -544,7 +558,9 @@ fmgr_c_validator(PG_FUNCTION_ARGS)
544558
char *prosrc;
545559
char *probin;
546560

547-
tuple = SearchSysCache(PROCOID, funcoid, 0, 0, 0);
561+
tuple = SearchSysCache(PROCOID,
562+
ObjectIdGetDatum(funcoid),
563+
0, 0, 0);
548564
if (!HeapTupleIsValid(tuple))
549565
elog(ERROR, "cache lookup of function %u failed", funcoid);
550566
proc = (Form_pg_proc) GETSTRUCT(tuple);
@@ -585,38 +601,62 @@ fmgr_sql_validator(PG_FUNCTION_ARGS)
585601
Datum tmp;
586602
char *prosrc;
587603
char functyptype;
604+
bool haspolyarg;
588605
int i;
589606

590-
tuple = SearchSysCache(PROCOID, funcoid, 0, 0, 0);
607+
tuple = SearchSysCache(PROCOID,
608+
ObjectIdGetDatum(funcoid),
609+
0, 0, 0);
591610
if (!HeapTupleIsValid(tuple))
592611
elog(ERROR, "cache lookup of function %u failed", funcoid);
593612
proc = (Form_pg_proc) GETSTRUCT(tuple);
594613

595614
functyptype = get_typtype(proc->prorettype);
596615

597-
/* Disallow pseudotypes in arguments and result */
598-
/* except that return type can be RECORD or VOID */
616+
/* Disallow pseudotype result */
617+
/* except for RECORD, VOID, ANYARRAY, or ANYELEMENT */
599618
if (functyptype == 'p' &&
600619
proc->prorettype != RECORDOID &&
601-
proc->prorettype != VOIDOID)
620+
proc->prorettype != VOIDOID &&
621+
proc->prorettype != ANYARRAYOID &&
622+
proc->prorettype != ANYELEMENTOID)
602623
elog(ERROR, "SQL functions cannot return type %s",
603624
format_type_be(proc->prorettype));
604625

626+
/* Disallow pseudotypes in arguments */
627+
/* except for ANYARRAY or ANYELEMENT */
628+
haspolyarg = false;
605629
for (i = 0; i < proc->pronargs; i++)
606630
{
607631
if (get_typtype(proc->proargtypes[i]) == 'p')
608-
elog(ERROR, "SQL functions cannot have arguments of type %s",
609-
format_type_be(proc->proargtypes[i]));
632+
{
633+
if (proc->proargtypes[i] == ANYARRAYOID ||
634+
proc->proargtypes[i] == ANYELEMENTOID)
635+
haspolyarg = true;
636+
else
637+
elog(ERROR, "SQL functions cannot have arguments of type %s",
638+
format_type_be(proc->proargtypes[i]));
639+
}
610640
}
611641

612-
tmp = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_prosrc, &isnull);
613-
if (isnull)
614-
elog(ERROR, "null prosrc");
642+
/*
643+
* We can't precheck the function definition if there are any polymorphic
644+
* input types, because actual datatypes of expression results will be
645+
* unresolvable. The check will be done at runtime instead.
646+
*/
647+
if (!haspolyarg)
648+
{
649+
tmp = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_prosrc, &isnull);
650+
if (isnull)
651+
elog(ERROR, "null prosrc");
615652

616-
prosrc = DatumGetCString(DirectFunctionCall1(textout, tmp));
653+
prosrc = DatumGetCString(DirectFunctionCall1(textout, tmp));
617654

618-
querytree_list = pg_parse_and_rewrite(prosrc, proc->proargtypes, proc->pronargs);
619-
checkretval(proc->prorettype, functyptype, querytree_list);
655+
querytree_list = pg_parse_and_rewrite(prosrc,
656+
proc->proargtypes,
657+
proc->pronargs);
658+
check_sql_fn_retval(proc->prorettype, functyptype, querytree_list);
659+
}
620660

621661
ReleaseSysCache(tuple);
622662

src/backend/executor/functions.c

Lines changed: 54 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.66 2003/06/12 17:29:26 tgl Exp $
11+
* $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.67 2003/07/01 00:04:37 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -24,6 +24,7 @@
2424
#include "tcop/tcopprot.h"
2525
#include "tcop/utility.h"
2626
#include "utils/builtins.h"
27+
#include "utils/lsyscache.h"
2728
#include "utils/syscache.h"
2829

2930

@@ -76,7 +77,8 @@ typedef SQLFunctionCache *SQLFunctionCachePtr;
7677

7778
/* non-export function prototypes */
7879
static execution_state *init_execution_state(char *src,
79-
Oid *argOidVect, int nargs);
80+
Oid *argOidVect, int nargs,
81+
Oid rettype, bool haspolyarg);
8082
static void init_sql_fcache(FmgrInfo *finfo);
8183
static void postquel_start(execution_state *es, SQLFunctionCachePtr fcache);
8284
static TupleTableSlot *postquel_getnext(execution_state *es);
@@ -90,7 +92,8 @@ static void ShutdownSQLFunction(Datum arg);
9092

9193

9294
static execution_state *
93-
init_execution_state(char *src, Oid *argOidVect, int nargs)
95+
init_execution_state(char *src, Oid *argOidVect, int nargs,
96+
Oid rettype, bool haspolyarg)
9497
{
9598
execution_state *firstes;
9699
execution_state *preves;
@@ -99,6 +102,13 @@ init_execution_state(char *src, Oid *argOidVect, int nargs)
99102

100103
queryTree_list = pg_parse_and_rewrite(src, argOidVect, nargs);
101104

105+
/*
106+
* If the function has any arguments declared as polymorphic types,
107+
* then it wasn't type-checked at definition time; must do so now.
108+
*/
109+
if (haspolyarg)
110+
check_sql_fn_retval(rettype, get_typtype(rettype), queryTree_list);
111+
102112
firstes = NULL;
103113
preves = NULL;
104114

@@ -133,17 +143,21 @@ static void
133143
init_sql_fcache(FmgrInfo *finfo)
134144
{
135145
Oid foid = finfo->fn_oid;
146+
Oid rettype;
136147
HeapTuple procedureTuple;
137148
HeapTuple typeTuple;
138149
Form_pg_proc procedureStruct;
139150
Form_pg_type typeStruct;
140151
SQLFunctionCachePtr fcache;
141152
Oid *argOidVect;
153+
bool haspolyarg;
142154
char *src;
143155
int nargs;
144156
Datum tmp;
145157
bool isNull;
146158

159+
fcache = (SQLFunctionCachePtr) palloc0(sizeof(SQLFunctionCache));
160+
147161
/*
148162
* get the procedure tuple corresponding to the given function Oid
149163
*/
@@ -153,30 +167,37 @@ init_sql_fcache(FmgrInfo *finfo)
153167
if (!HeapTupleIsValid(procedureTuple))
154168
elog(ERROR, "init_sql_fcache: Cache lookup failed for procedure %u",
155169
foid);
156-
157170
procedureStruct = (Form_pg_proc) GETSTRUCT(procedureTuple);
158171

159172
/*
160-
* get the return type from the procedure tuple
173+
* get the result type from the procedure tuple, and check for
174+
* polymorphic result type; if so, find out the actual result type.
161175
*/
176+
rettype = procedureStruct->prorettype;
177+
178+
if (rettype == ANYARRAYOID || rettype == ANYELEMENTOID)
179+
{
180+
rettype = get_fn_expr_rettype(finfo);
181+
if (rettype == InvalidOid)
182+
elog(ERROR, "could not determine actual result type for function declared %s",
183+
format_type_be(procedureStruct->prorettype));
184+
}
185+
186+
/* Now look up the actual result type */
162187
typeTuple = SearchSysCache(TYPEOID,
163-
ObjectIdGetDatum(procedureStruct->prorettype),
188+
ObjectIdGetDatum(rettype),
164189
0, 0, 0);
165190
if (!HeapTupleIsValid(typeTuple))
166191
elog(ERROR, "init_sql_fcache: Cache lookup failed for type %u",
167-
procedureStruct->prorettype);
168-
192+
rettype);
169193
typeStruct = (Form_pg_type) GETSTRUCT(typeTuple);
170194

171-
fcache = (SQLFunctionCachePtr) palloc0(sizeof(SQLFunctionCache));
172-
173195
/*
174196
* get the type length and by-value flag from the type tuple
175197
*/
176198
fcache->typlen = typeStruct->typlen;
177199

178-
if (typeStruct->typtype != 'c' &&
179-
procedureStruct->prorettype != RECORDOID)
200+
if (typeStruct->typtype != 'c' && rettype != RECORDOID)
180201
{
181202
/* The return type is not a composite type, so just use byval */
182203
fcache->typbyval = typeStruct->typbyval;
@@ -205,17 +226,35 @@ init_sql_fcache(FmgrInfo *finfo)
205226
fcache->funcSlot = NULL;
206227

207228
/*
208-
* Parse and plan the queries. We need the argument info to pass
229+
* Parse and plan the queries. We need the argument type info to pass
209230
* to the parser.
210231
*/
211232
nargs = procedureStruct->pronargs;
233+
haspolyarg = false;
212234

213235
if (nargs > 0)
214236
{
237+
int argnum;
238+
215239
argOidVect = (Oid *) palloc(nargs * sizeof(Oid));
216240
memcpy(argOidVect,
217241
procedureStruct->proargtypes,
218242
nargs * sizeof(Oid));
243+
/* Resolve any polymorphic argument types */
244+
for (argnum = 0; argnum < nargs; argnum++)
245+
{
246+
Oid argtype = argOidVect[argnum];
247+
248+
if (argtype == ANYARRAYOID || argtype == ANYELEMENTOID)
249+
{
250+
argtype = get_fn_expr_argtype(finfo, argnum);
251+
if (argtype == InvalidOid)
252+
elog(ERROR, "could not determine actual type of argument declared %s",
253+
format_type_be(argOidVect[argnum]));
254+
argOidVect[argnum] = argtype;
255+
haspolyarg = true;
256+
}
257+
}
219258
}
220259
else
221260
argOidVect = (Oid *) NULL;
@@ -229,7 +268,8 @@ init_sql_fcache(FmgrInfo *finfo)
229268
foid);
230269
src = DatumGetCString(DirectFunctionCall1(textout, tmp));
231270

232-
fcache->func_state = init_execution_state(src, argOidVect, nargs);
271+
fcache->func_state = init_execution_state(src, argOidVect, nargs,
272+
rettype, haspolyarg);
233273

234274
pfree(src);
235275

src/backend/optimizer/util/clauses.c

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.142 2003/06/29 00:33:43 tgl Exp $
11+
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.143 2003/07/01 00:04:37 tgl Exp $
1212
*
1313
* HISTORY
1414
* AUTHOR DATE MAJOR EVENT
@@ -1731,6 +1731,7 @@ inline_function(Oid funcid, Oid result_type, List *args,
17311731
int *usecounts;
17321732
List *arg;
17331733
int i;
1734+
int j;
17341735

17351736
/*
17361737
* Forget it if the function is not SQL-language or has other
@@ -1742,12 +1743,20 @@ inline_function(Oid funcid, Oid result_type, List *args,
17421743
funcform->pronargs != length(args))
17431744
return NULL;
17441745

1745-
/* Forget it if declared return type is tuple or void */
1746+
/* Forget it if declared return type is not base or domain */
17461747
result_typtype = get_typtype(funcform->prorettype);
17471748
if (result_typtype != 'b' &&
17481749
result_typtype != 'd')
17491750
return NULL;
17501751

1752+
/* Forget it if any declared argument type is polymorphic */
1753+
for (j = 0; j < funcform->pronargs; j++)
1754+
{
1755+
if (funcform->proargtypes[j] == ANYARRAYOID ||
1756+
funcform->proargtypes[j] == ANYELEMENTOID)
1757+
return NULL;
1758+
}
1759+
17511760
/* Check for recursive function, and give up trying to expand if so */
17521761
if (oidMember(funcid, active_fns))
17531762
return NULL;

src/backend/utils/adt/array_userfuncs.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* Copyright (c) 2003, PostgreSQL Global Development Group
77
*
88
* IDENTIFICATION
9-
* $Header: /cvsroot/pgsql/src/backend/utils/adt/array_userfuncs.c,v 1.4 2003/06/27 00:33:25 tgl Exp $
9+
* $Header: /cvsroot/pgsql/src/backend/utils/adt/array_userfuncs.c,v 1.5 2003/07/01 00:04:38 tgl Exp $
1010
*
1111
*-------------------------------------------------------------------------
1212
*/
@@ -37,8 +37,8 @@ array_push(PG_FUNCTION_ARGS)
3737
int16 typlen;
3838
bool typbyval;
3939
char typalign;
40-
Oid arg0_typeid = get_fn_expr_argtype(fcinfo, 0);
41-
Oid arg1_typeid = get_fn_expr_argtype(fcinfo, 1);
40+
Oid arg0_typeid = get_fn_expr_argtype(fcinfo->flinfo, 0);
41+
Oid arg1_typeid = get_fn_expr_argtype(fcinfo->flinfo, 1);
4242
Oid arg0_elemid;
4343
Oid arg1_elemid;
4444
ArrayMetaState *my_extra;

0 commit comments

Comments
 (0)