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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy