Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit7893462

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 tliststhat contain resjunk target items, and improve error messages. Thisaddresses bug reported by Krupnikov 6-July-00.
1 parent469673f commit7893462

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/2117:22:35 tgl Exp $
11+
* $Header: /cvsroot/pgsql/src/backend/catalog/pg_proc.c,v 1.48 2000/08/2120: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+
staticvoidcheckretval(Oidrettype,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
returnretval;
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+
staticvoid
351+
checkretval(Oidrettype,List*queryTreeList)
352+
{
353+
Query*parse;
354+
intcmd;
355+
List*tlist;
356+
List*tlistitem;
357+
inttlistlen;
358+
Typetyp;
359+
Resdom*resnode;
360+
Relationreln;
361+
Oidrelid;
362+
intrelnatts;
363+
inti;
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+
Oidtletype;
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
returnresult;
14451445
}
14461446

1447+
/*
1448+
* Number of items in a tlist (including any resjunk items!)
1449+
*/
14471450
int
14481451
ExecTargetListLength(List*targetlist)
14491452
{
1450-
intlen;
1453+
intlen=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
returnlen;
14651466
}
14661467

1468+
/*
1469+
* Number of items in a tlist, not including any resjunk items
1470+
*/
1471+
int
1472+
ExecCleanTargetListLength(List*targetlist)
1473+
{
1474+
intlen=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+
returnlen;
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(Oidrettype,List*queryTreeList)
888-
{
889-
Query*parse;
890-
List*tlist;
891-
List*rt;
892-
intcmd;
893-
Typetyp;
894-
Resdom*resnode;
895-
Relationreln;
896-
Oidrelid;
897-
intrelnatts;
898-
inti;
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-
Oidtletype=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)

[8]ページ先頭

©2009-2025 Movatter.jp