1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-07 00:36:50 +03:00

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.
This commit is contained in:
Tom Lane
2000-08-21 20:55:31 +00:00
parent 469673f966
commit 7893462e44
5 changed files with 164 additions and 149 deletions

View File

@ -8,21 +8,17 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.87 2000/08/08 15:41:38 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.88 2000/08/21 20:55:29 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include <sys/types.h>
#include "postgres.h"
#include "access/heapam.h"
#include "catalog/pg_type.h"
#include "executor/executor.h"
#include "nodes/makefuncs.h"
#include "optimizer/clauses.h"
#include "optimizer/paths.h"
#include "optimizer/plancat.h"
#include "optimizer/planmain.h"
#include "optimizer/planner.h"
#include "optimizer/prep.h"
@ -30,7 +26,6 @@
#include "optimizer/tlist.h"
#include "optimizer/var.h"
#include "parser/parse_expr.h"
#include "parser/parse_type.h"
#include "utils/lsyscache.h"
@ -871,132 +866,3 @@ make_sortplan(List *tlist, Plan *plannode, List *sortcls)
return (Plan *) make_sort(sort_tlist, plannode, keyno);
}
/*
* pg_checkretval() -- check return value of a list of sql parse
* trees.
*
* The return value of a sql function is the value returned by
* the final query in the function. We do some ad-hoc define-time
* type checking here to be sure that the user is returning the
* type he claims.
*
* XXX Why is this function in this module?
*/
void
pg_checkretval(Oid rettype, List *queryTreeList)
{
Query *parse;
List *tlist;
List *rt;
int cmd;
Type typ;
Resdom *resnode;
Relation reln;
Oid relid;
int relnatts;
int i;
/* find the final query */
parse = (Query *) nth(length(queryTreeList) - 1, queryTreeList);
/*
* test 1: if the last query is a utility invocation, then there had
* better not be a return value declared.
*/
if (parse->commandType == CMD_UTILITY)
{
if (rettype == InvalidOid)
return;
else
elog(ERROR, "return type mismatch in function decl: final query is a catalog utility");
}
/* okay, it's an ordinary query */
tlist = parse->targetList;
rt = parse->rtable;
cmd = parse->commandType;
/*
* test 2: if the function is declared to return no value, then the
* final query had better not be a retrieve.
*/
if (rettype == InvalidOid)
{
if (cmd == CMD_SELECT)
elog(ERROR,
"function declared with no return type, but final query is a retrieve");
else
return;
}
/* by here, the function is declared to return some type */
if ((typ = typeidType(rettype)) == NULL)
elog(ERROR, "can't find return type %u for function\n", rettype);
/*
* test 3: if the function is declared to return a value, then the
* final query had better be a retrieve.
*/
if (cmd != CMD_SELECT)
elog(ERROR, "function declared to return type %s, but final query is not a retrieve", typeTypeName(typ));
/*
* test 4: for base type returns, the target list should have exactly
* one entry, and its type should agree with what the user declared.
*/
if (typeTypeRelid(typ) == InvalidOid)
{
if (ExecTargetListLength(tlist) > 1)
elog(ERROR, "function declared to return %s returns multiple values in final retrieve", typeTypeName(typ));
resnode = (Resdom *) ((TargetEntry *) lfirst(tlist))->resdom;
if (resnode->restype != rettype)
elog(ERROR, "return type mismatch in function: declared to return %s, returns %s", typeTypeName(typ), typeidTypeName(resnode->restype));
/* by here, base return types match */
return;
}
/*
* If the target list is of length 1, and the type of the varnode in
* the target list is the same as the declared return type, this is
* okay. This can happen, for example, where the body of the function
* is 'retrieve (x = func2())', where func2 has the same return type
* as the function that's calling it.
*/
if (ExecTargetListLength(tlist) == 1)
{
resnode = (Resdom *) ((TargetEntry *) lfirst(tlist))->resdom;
if (resnode->restype == rettype)
return;
}
/*
* By here, the procedure returns a (set of) tuples. This part of the
* typechecking is a hack. We look up the relation that is the
* declared return type, and be sure that attributes 1 .. n in the
* target list match the declared types.
*/
reln = heap_open(typeTypeRelid(typ), AccessShareLock);
relid = reln->rd_id;
relnatts = reln->rd_rel->relnatts;
if (ExecTargetListLength(tlist) != relnatts)
elog(ERROR, "function declared to return type %s does not retrieve (%s.*)", typeTypeName(typ), typeTypeName(typ));
/* expect attributes 1 .. n in order */
for (i = 1; i <= relnatts; i++)
{
TargetEntry *tle = lfirst(tlist);
Node *thenode = tle->expr;
Oid tletype = exprType(thenode);
if (tletype != reln->rd_att->attrs[i - 1]->atttypid)
elog(ERROR, "function declared to return type %s does not retrieve (%s.all)", typeTypeName(typ), typeTypeName(typ));
tlist = lnext(tlist);
}
heap_close(reln, AccessShareLock);
}