Skip to content

Commit 7893462

Browse files
committed
Move pg_checkretval out of the planner (where it never belonged) into
pg_proc.c (where it's actually used). Fix it to correctly handle tlists that contain resjunk target items, and improve error messages. This addresses bug reported by Krupnikov 6-July-00.
1 parent 469673f commit 7893462

File tree

5 files changed

+164
-149
lines changed

5 files changed

+164
-149
lines changed

src/backend/catalog/pg_proc.c

Lines changed: 127 additions & 3 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.47 2000/08/21 17:22:35 tgl Exp $
11+
* $Header: /cvsroot/pgsql/src/backend/catalog/pg_proc.c,v 1.48 2000/08/21 20:55:31 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -20,8 +20,9 @@
2020
#include "catalog/pg_language.h"
2121
#include "catalog/pg_proc.h"
2222
#include "catalog/pg_type.h"
23+
#include "executor/executor.h"
2324
#include "miscadmin.h"
24-
#include "optimizer/planner.h"
25+
#include "parser/parse_expr.h"
2526
#include "parser/parse_type.h"
2627
#include "tcop/tcopprot.h"
2728
#include "utils/builtins.h"
@@ -30,6 +31,9 @@
3031
#include "utils/syscache.h"
3132

3233

34+
static void checkretval(Oid rettype, List *queryTreeList);
35+
36+
3337
/* ----------------------------------------------------------------
3438
* ProcedureCreate
3539
* ----------------------------------------------------------------
@@ -220,7 +224,7 @@ ProcedureCreate(char *procedureName,
220224
{
221225
querytree_list = pg_parse_and_rewrite(prosrc, typev, parameterCount);
222226
/* typecheck return value */
223-
pg_checkretval(typeObjectId, querytree_list);
227+
checkretval(typeObjectId, querytree_list);
224228
}
225229

226230
/*
@@ -334,3 +338,123 @@ ProcedureCreate(char *procedureName,
334338
heap_freetuple(tup);
335339
return retval;
336340
}
341+
342+
/*
343+
* checkretval() -- check return value of a list of sql parse trees.
344+
*
345+
* The return value of a sql function is the value returned by
346+
* the final query in the function. We do some ad-hoc define-time
347+
* type checking here to be sure that the user is returning the
348+
* type he claims.
349+
*/
350+
static void
351+
checkretval(Oid rettype, List *queryTreeList)
352+
{
353+
Query *parse;
354+
int cmd;
355+
List *tlist;
356+
List *tlistitem;
357+
int tlistlen;
358+
Type typ;
359+
Resdom *resnode;
360+
Relation reln;
361+
Oid relid;
362+
int relnatts;
363+
int i;
364+
365+
/* find the final query */
366+
parse = (Query *) nth(length(queryTreeList) - 1, queryTreeList);
367+
368+
cmd = parse->commandType;
369+
tlist = parse->targetList;
370+
371+
/*
372+
* The last query must be a SELECT if and only if there is a return type.
373+
*/
374+
if (rettype == InvalidOid)
375+
{
376+
if (cmd == CMD_SELECT)
377+
elog(ERROR, "function declared with no return type, but final query is a SELECT");
378+
return;
379+
}
380+
381+
/* by here, the function is declared to return some type */
382+
if ((typ = typeidType(rettype)) == NULL)
383+
elog(ERROR, "can't find return type %u for function", rettype);
384+
385+
if (cmd != CMD_SELECT)
386+
elog(ERROR, "function declared to return %s, but final query is not a SELECT", typeTypeName(typ));
387+
388+
/*
389+
* Count the non-junk entries in the result targetlist.
390+
*/
391+
tlistlen = ExecCleanTargetListLength(tlist);
392+
393+
/*
394+
* For base-type returns, the target list should have exactly one entry,
395+
* and its type should agree with what the user declared.
396+
*/
397+
if (typeTypeRelid(typ) == InvalidOid)
398+
{
399+
if (tlistlen != 1)
400+
elog(ERROR, "function declared to return %s returns multiple columns in final SELECT", typeTypeName(typ));
401+
402+
resnode = (Resdom *) ((TargetEntry *) lfirst(tlist))->resdom;
403+
if (resnode->restype != rettype)
404+
elog(ERROR, "return type mismatch in function: declared to return %s, returns %s", typeTypeName(typ), typeidTypeName(resnode->restype));
405+
406+
return;
407+
}
408+
409+
/*
410+
* If the target list is of length 1, and the type of the varnode in
411+
* the target list is the same as the declared return type, this is
412+
* okay. This can happen, for example, where the body of the function
413+
* is 'SELECT (x = func2())', where func2 has the same return type
414+
* as the function that's calling it.
415+
*/
416+
if (tlistlen == 1)
417+
{
418+
resnode = (Resdom *) ((TargetEntry *) lfirst(tlist))->resdom;
419+
if (resnode->restype == rettype)
420+
return;
421+
}
422+
423+
/*
424+
* By here, the procedure returns a tuple or set of tuples. This part of
425+
* the typechecking is a hack. We look up the relation that is the
426+
* declared return type, and be sure that attributes 1 .. n in the target
427+
* list match the declared types.
428+
*/
429+
reln = heap_open(typeTypeRelid(typ), AccessShareLock);
430+
relid = reln->rd_id;
431+
relnatts = reln->rd_rel->relnatts;
432+
433+
if (tlistlen != relnatts)
434+
elog(ERROR, "function declared to return %s does not SELECT the right number of columns (%d)", typeTypeName(typ), relnatts);
435+
436+
/* expect attributes 1 .. n in order */
437+
i = 0;
438+
foreach(tlistitem, tlist)
439+
{
440+
TargetEntry *tle = (TargetEntry *) lfirst(tlistitem);
441+
Oid tletype;
442+
443+
if (tle->resdom->resjunk)
444+
continue;
445+
tletype = exprType(tle->expr);
446+
if (tletype != reln->rd_att->attrs[i]->atttypid)
447+
elog(ERROR, "function declared to return %s returns %s instead of %s at column %d",
448+
typeTypeName(typ),
449+
typeidTypeName(tletype),
450+
typeidTypeName(reln->rd_att->attrs[i]->atttypid),
451+
i+1);
452+
i++;
453+
}
454+
455+
/* this shouldn't happen, but let's just check... */
456+
if (i != relnatts)
457+
elog(ERROR, "function declared to return %s does not SELECT the right number of columns (%d)", typeTypeName(typ), relnatts);
458+
459+
heap_close(reln, AccessShareLock);
460+
}

src/backend/executor/execQual.c

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.77 2000/08/08 15:41:22 tgl Exp $
11+
* $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.78 2000/08/21 20:55:30 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -1444,17 +1444,18 @@ ExecQual(List *qual, ExprContext *econtext, bool resultForNull)
14441444
return result;
14451445
}
14461446

1447+
/*
1448+
* Number of items in a tlist (including any resjunk items!)
1449+
*/
14471450
int
14481451
ExecTargetListLength(List *targetlist)
14491452
{
1450-
int len;
1453+
int len = 0;
14511454
List *tl;
1452-
TargetEntry *curTle;
14531455

1454-
len = 0;
14551456
foreach(tl, targetlist)
14561457
{
1457-
curTle = lfirst(tl);
1458+
TargetEntry *curTle = (TargetEntry *) lfirst(tl);
14581459

14591460
if (curTle->resdom != NULL)
14601461
len++;
@@ -1464,6 +1465,32 @@ ExecTargetListLength(List *targetlist)
14641465
return len;
14651466
}
14661467

1468+
/*
1469+
* Number of items in a tlist, not including any resjunk items
1470+
*/
1471+
int
1472+
ExecCleanTargetListLength(List *targetlist)
1473+
{
1474+
int len = 0;
1475+
List *tl;
1476+
1477+
foreach(tl, targetlist)
1478+
{
1479+
TargetEntry *curTle = (TargetEntry *) lfirst(tl);
1480+
1481+
if (curTle->resdom != NULL)
1482+
{
1483+
if (! curTle->resdom->resjunk)
1484+
len++;
1485+
}
1486+
else
1487+
{
1488+
len += curTle->fjoin->fj_nNodes;
1489+
}
1490+
}
1491+
return len;
1492+
}
1493+
14671494
/* ----------------------------------------------------------------
14681495
* ExecTargetList
14691496
*

src/backend/optimizer/plan/planner.c

Lines changed: 1 addition & 135 deletions
Original file line numberDiff line numberDiff line change
@@ -8,29 +8,24 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.87 2000/08/08 15:41:38 tgl Exp $
11+
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.88 2000/08/21 20:55:29 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
15-
#include <sys/types.h>
1615

1716
#include "postgres.h"
1817

19-
#include "access/heapam.h"
2018
#include "catalog/pg_type.h"
21-
#include "executor/executor.h"
2219
#include "nodes/makefuncs.h"
2320
#include "optimizer/clauses.h"
2421
#include "optimizer/paths.h"
25-
#include "optimizer/plancat.h"
2622
#include "optimizer/planmain.h"
2723
#include "optimizer/planner.h"
2824
#include "optimizer/prep.h"
2925
#include "optimizer/subselect.h"
3026
#include "optimizer/tlist.h"
3127
#include "optimizer/var.h"
3228
#include "parser/parse_expr.h"
33-
#include "parser/parse_type.h"
3429
#include "utils/lsyscache.h"
3530

3631

@@ -871,132 +866,3 @@ make_sortplan(List *tlist, Plan *plannode, List *sortcls)
871866

872867
return (Plan *) make_sort(sort_tlist, plannode, keyno);
873868
}
874-
875-
/*
876-
* pg_checkretval() -- check return value of a list of sql parse
877-
* trees.
878-
*
879-
* The return value of a sql function is the value returned by
880-
* the final query in the function. We do some ad-hoc define-time
881-
* type checking here to be sure that the user is returning the
882-
* type he claims.
883-
*
884-
* XXX Why is this function in this module?
885-
*/
886-
void
887-
pg_checkretval(Oid rettype, List *queryTreeList)
888-
{
889-
Query *parse;
890-
List *tlist;
891-
List *rt;
892-
int cmd;
893-
Type typ;
894-
Resdom *resnode;
895-
Relation reln;
896-
Oid relid;
897-
int relnatts;
898-
int i;
899-
900-
/* find the final query */
901-
parse = (Query *) nth(length(queryTreeList) - 1, queryTreeList);
902-
903-
/*
904-
* test 1: if the last query is a utility invocation, then there had
905-
* better not be a return value declared.
906-
*/
907-
if (parse->commandType == CMD_UTILITY)
908-
{
909-
if (rettype == InvalidOid)
910-
return;
911-
else
912-
elog(ERROR, "return type mismatch in function decl: final query is a catalog utility");
913-
}
914-
915-
/* okay, it's an ordinary query */
916-
tlist = parse->targetList;
917-
rt = parse->rtable;
918-
cmd = parse->commandType;
919-
920-
/*
921-
* test 2: if the function is declared to return no value, then the
922-
* final query had better not be a retrieve.
923-
*/
924-
if (rettype == InvalidOid)
925-
{
926-
if (cmd == CMD_SELECT)
927-
elog(ERROR,
928-
"function declared with no return type, but final query is a retrieve");
929-
else
930-
return;
931-
}
932-
933-
/* by here, the function is declared to return some type */
934-
if ((typ = typeidType(rettype)) == NULL)
935-
elog(ERROR, "can't find return type %u for function\n", rettype);
936-
937-
/*
938-
* test 3: if the function is declared to return a value, then the
939-
* final query had better be a retrieve.
940-
*/
941-
if (cmd != CMD_SELECT)
942-
elog(ERROR, "function declared to return type %s, but final query is not a retrieve", typeTypeName(typ));
943-
944-
/*
945-
* test 4: for base type returns, the target list should have exactly
946-
* one entry, and its type should agree with what the user declared.
947-
*/
948-
949-
if (typeTypeRelid(typ) == InvalidOid)
950-
{
951-
if (ExecTargetListLength(tlist) > 1)
952-
elog(ERROR, "function declared to return %s returns multiple values in final retrieve", typeTypeName(typ));
953-
954-
resnode = (Resdom *) ((TargetEntry *) lfirst(tlist))->resdom;
955-
if (resnode->restype != rettype)
956-
elog(ERROR, "return type mismatch in function: declared to return %s, returns %s", typeTypeName(typ), typeidTypeName(resnode->restype));
957-
958-
/* by here, base return types match */
959-
return;
960-
}
961-
962-
/*
963-
* If the target list is of length 1, and the type of the varnode in
964-
* the target list is the same as the declared return type, this is
965-
* okay. This can happen, for example, where the body of the function
966-
* is 'retrieve (x = func2())', where func2 has the same return type
967-
* as the function that's calling it.
968-
*/
969-
if (ExecTargetListLength(tlist) == 1)
970-
{
971-
resnode = (Resdom *) ((TargetEntry *) lfirst(tlist))->resdom;
972-
if (resnode->restype == rettype)
973-
return;
974-
}
975-
976-
/*
977-
* By here, the procedure returns a (set of) tuples. This part of the
978-
* typechecking is a hack. We look up the relation that is the
979-
* declared return type, and be sure that attributes 1 .. n in the
980-
* target list match the declared types.
981-
*/
982-
reln = heap_open(typeTypeRelid(typ), AccessShareLock);
983-
relid = reln->rd_id;
984-
relnatts = reln->rd_rel->relnatts;
985-
986-
if (ExecTargetListLength(tlist) != relnatts)
987-
elog(ERROR, "function declared to return type %s does not retrieve (%s.*)", typeTypeName(typ), typeTypeName(typ));
988-
989-
/* expect attributes 1 .. n in order */
990-
for (i = 1; i <= relnatts; i++)
991-
{
992-
TargetEntry *tle = lfirst(tlist);
993-
Node *thenode = tle->expr;
994-
Oid tletype = exprType(thenode);
995-
996-
if (tletype != reln->rd_att->attrs[i - 1]->atttypid)
997-
elog(ERROR, "function declared to return type %s does not retrieve (%s.all)", typeTypeName(typ), typeTypeName(typ));
998-
tlist = lnext(tlist);
999-
}
1000-
1001-
heap_close(reln, AccessShareLock);
1002-
}

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