Skip to content

Commit 8efbe30

Browse files
committed
check_sql_fn_retval has always thought that we supported doing
'SELECT foo()' in a SQL function returning a rowtype, to simply pass back the results of another function returning the same rowtype. However, that hasn't actually worked in many years. Now it works again.
1 parent 3dd1ca0 commit 8efbe30

File tree

4 files changed

+68
-41
lines changed

4 files changed

+68
-41
lines changed

src/backend/catalog/pg_proc.c

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.114 2004/04/01 21:28:44 tgl Exp $
11+
* $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.115 2004/04/02 23:14:05 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -362,8 +362,13 @@ create_parameternames_array(int parameterCount, const char *parameterNames[])
362362
* function execution startup. The rettype is then the actual resolved
363363
* output type of the function, rather than the declared type. (Therefore,
364364
* we should never see ANYARRAY or ANYELEMENT as rettype.)
365+
*
366+
* The return value is true if the function returns the entire tuple result
367+
* of its final SELECT, and false otherwise. Note that because we allow
368+
* "SELECT rowtype_expression", this may be false even when the declared
369+
* function return type is a rowtype.
365370
*/
366-
void
371+
bool
367372
check_sql_fn_retval(Oid rettype, char fn_typtype, List *queryTreeList)
368373
{
369374
Query *parse;
@@ -387,7 +392,7 @@ check_sql_fn_retval(Oid rettype, char fn_typtype, List *queryTreeList)
387392
errmsg("return type mismatch in function declared to return %s",
388393
format_type_be(rettype)),
389394
errdetail("Function's final statement must be a SELECT.")));
390-
return;
395+
return false;
391396
}
392397

393398
/* find the final query */
@@ -408,7 +413,7 @@ check_sql_fn_retval(Oid rettype, char fn_typtype, List *queryTreeList)
408413
errmsg("return type mismatch in function declared to return %s",
409414
format_type_be(rettype)),
410415
errdetail("Function's final statement must not be a SELECT.")));
411-
return;
416+
return false;
412417
}
413418

414419
/* by here, the function is declared to return some type */
@@ -468,7 +473,7 @@ check_sql_fn_retval(Oid rettype, char fn_typtype, List *queryTreeList)
468473
{
469474
restype = ((TargetEntry *) lfirst(tlist))->resdom->restype;
470475
if (IsBinaryCoercible(restype, rettype))
471-
return;
476+
return false; /* NOT returning whole tuple */
472477
}
473478

474479
/*
@@ -536,16 +541,31 @@ check_sql_fn_retval(Oid rettype, char fn_typtype, List *queryTreeList)
536541
errdetail("Final SELECT returns too few columns.")));
537542

538543
relation_close(reln, AccessShareLock);
544+
545+
/* Report that we are returning entire tuple result */
546+
return true;
539547
}
540548
else if (rettype == RECORDOID)
541549
{
542-
/* Shouldn't have a typerelid */
543-
Assert(typerelid == InvalidOid);
550+
/*
551+
* If the target list is of length 1, and the type of the varnode
552+
* in the target list matches the declared return type, this is
553+
* okay. This can happen, for example, where the body of the
554+
* function is 'SELECT func2()', where func2 has the same return
555+
* type as the function that's calling it.
556+
*/
557+
if (tlistlen == 1)
558+
{
559+
restype = ((TargetEntry *) lfirst(tlist))->resdom->restype;
560+
if (IsBinaryCoercible(restype, rettype))
561+
return false; /* NOT returning whole tuple */
562+
}
544563

545564
/*
546-
* For RECORD return type, defer this check until we get the first
547-
* tuple.
565+
* Otherwise assume we are returning the whole tuple. Crosschecking
566+
* against what the caller expects will happen at runtime.
548567
*/
568+
return true;
549569
}
550570
else if (rettype == ANYARRAYOID || rettype == ANYELEMENTOID)
551571
{
@@ -560,6 +580,8 @@ check_sql_fn_retval(Oid rettype, char fn_typtype, List *queryTreeList)
560580
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
561581
errmsg("return type %s is not supported for SQL functions",
562582
format_type_be(rettype))));
583+
584+
return false;
563585
}
564586

565587

@@ -751,7 +773,8 @@ fmgr_sql_validator(PG_FUNCTION_ARGS)
751773
querytree_list = pg_parse_and_rewrite(prosrc,
752774
proc->proargtypes,
753775
proc->pronargs);
754-
check_sql_fn_retval(proc->prorettype, functyptype, querytree_list);
776+
(void) check_sql_fn_retval(proc->prorettype, functyptype,
777+
querytree_list);
755778
}
756779
else
757780
querytree_list = pg_parse_query(prosrc);

src/backend/executor/functions.c

Lines changed: 30 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.79 2004/04/01 21:28:44 tgl Exp $
11+
* $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.80 2004/04/02 23:14:08 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -73,9 +73,7 @@ typedef SQLFunctionCache *SQLFunctionCachePtr;
7373

7474

7575
/* non-export function prototypes */
76-
static execution_state *init_execution_state(char *src,
77-
Oid *argOidVect, int nargs,
78-
Oid rettype, bool haspolyarg);
76+
static execution_state *init_execution_state(List *queryTree_list);
7977
static void init_sql_fcache(FmgrInfo *finfo);
8078
static void postquel_start(execution_state *es, SQLFunctionCachePtr fcache);
8179
static TupleTableSlot *postquel_getnext(execution_state *es);
@@ -90,25 +88,11 @@ static void ShutdownSQLFunction(Datum arg);
9088

9189

9290
static execution_state *
93-
init_execution_state(char *src, Oid *argOidVect, int nargs,
94-
Oid rettype, bool haspolyarg)
91+
init_execution_state(List *queryTree_list)
9592
{
96-
execution_state *firstes;
97-
execution_state *preves;
98-
List *queryTree_list,
99-
*qtl_item;
100-
101-
queryTree_list = pg_parse_and_rewrite(src, argOidVect, nargs);
102-
103-
/*
104-
* If the function has any arguments declared as polymorphic types,
105-
* then it wasn't type-checked at definition time; must do so now.
106-
*/
107-
if (haspolyarg)
108-
check_sql_fn_retval(rettype, get_typtype(rettype), queryTree_list);
109-
110-
firstes = NULL;
111-
preves = NULL;
93+
execution_state *firstes = NULL;
94+
execution_state *preves = NULL;
95+
List *qtl_item;
11296

11397
foreach(qtl_item, queryTree_list)
11498
{
@@ -151,6 +135,7 @@ init_sql_fcache(FmgrInfo *finfo)
151135
bool haspolyarg;
152136
char *src;
153137
int nargs;
138+
List *queryTree_list;
154139
Datum tmp;
155140
bool isNull;
156141

@@ -191,15 +176,17 @@ init_sql_fcache(FmgrInfo *finfo)
191176
typeStruct = (Form_pg_type) GETSTRUCT(typeTuple);
192177

193178
/*
194-
* get the type length and by-value flag from the type tuple
179+
* get the type length and by-value flag from the type tuple; also
180+
* do a preliminary check for returnsTuple (this may prove inaccurate,
181+
* see below).
195182
*/
196183
fcache->typlen = typeStruct->typlen;
197184
fcache->typbyval = typeStruct->typbyval;
198185
fcache->returnsTuple = (typeStruct->typtype == 'c' ||
199186
rettype == RECORDOID);
200187

201188
/*
202-
* Parse and plan the queries. We need the argument type info to pass
189+
* Parse and rewrite the queries. We need the argument type info to pass
203190
* to the parser.
204191
*/
205192
nargs = procedureStruct->pronargs;
@@ -242,8 +229,25 @@ init_sql_fcache(FmgrInfo *finfo)
242229
elog(ERROR, "null prosrc for function %u", foid);
243230
src = DatumGetCString(DirectFunctionCall1(textout, tmp));
244231

245-
fcache->func_state = init_execution_state(src, argOidVect, nargs,
246-
rettype, haspolyarg);
232+
queryTree_list = pg_parse_and_rewrite(src, argOidVect, nargs);
233+
234+
/*
235+
* If the function has any arguments declared as polymorphic types,
236+
* then it wasn't type-checked at definition time; must do so now.
237+
*
238+
* Also, force a type-check if the declared return type is a rowtype;
239+
* we need to find out whether we are actually returning the whole
240+
* tuple result, or just regurgitating a rowtype expression result.
241+
* In the latter case we clear returnsTuple because we need not act
242+
* different from the scalar result case.
243+
*/
244+
if (haspolyarg || fcache->returnsTuple)
245+
fcache->returnsTuple = check_sql_fn_retval(rettype,
246+
get_typtype(rettype),
247+
queryTree_list);
248+
249+
/* Finally, plan the queries */
250+
fcache->func_state = init_execution_state(queryTree_list);
247251

248252
pfree(src);
249253

src/backend/optimizer/util/clauses.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.168 2004/04/02 19:06:57 tgl Exp $
11+
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.169 2004/04/02 23:14:08 tgl Exp $
1212
*
1313
* HISTORY
1414
* AUTHOR DATE MAJOR EVENT
@@ -1992,8 +1992,8 @@ inline_function(Oid funcid, Oid result_type, List *args,
19921992
* probably not important, but let's be careful.)
19931993
*/
19941994
if (polymorphic)
1995-
check_sql_fn_retval(result_type, get_typtype(result_type),
1996-
querytree_list);
1995+
(void) check_sql_fn_retval(result_type, get_typtype(result_type),
1996+
querytree_list);
19971997

19981998
/*
19991999
* Additional validity checks on the expression. It mustn't return a

src/include/catalog/pg_proc.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
88
* Portions Copyright (c) 1994, Regents of the University of California
99
*
10-
* $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.323 2004/04/01 21:28:45 tgl Exp $
10+
* $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.324 2004/04/02 23:14:08 tgl Exp $
1111
*
1212
* NOTES
1313
* The script catalog/genbki.sh reads this file and generates .bki
@@ -3560,7 +3560,7 @@ extern Oid ProcedureCreate(const char *procedureName,
35603560
const Oid *parameterTypes,
35613561
const char *parameterNames[]);
35623562

3563-
extern void check_sql_fn_retval(Oid rettype, char fn_typtype,
3563+
extern bool check_sql_fn_retval(Oid rettype, char fn_typtype,
35643564
List *queryTreeList);
35653565

35663566
extern bool function_parse_error_transpose(const char *prosrc);

0 commit comments

Comments
 (0)