Get rid of long-since-vestigial Iter node type, in favor of adding a

returns-set boolean field in Func and Oper nodes.  This allows cleaner,
more reliable tests for expressions returning sets in the planner and
parser.  For example, a WHERE clause returning a set is now detected
and complained of in the parser, not only at runtime.
This commit is contained in:
Tom Lane 2002-05-12 23:43:04 +00:00
parent f9e4f611a1
commit 3389a110d4
33 changed files with 297 additions and 676 deletions

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.198 2002/05/03 04:11:08 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.199 2002/05/12 23:43:02 tgl Exp $
*
*
* INTERFACE ROUTINES
@ -49,6 +49,7 @@
#include "optimizer/planmain.h"
#include "optimizer/prep.h"
#include "optimizer/var.h"
#include "parser/parse_coerce.h"
#include "parser/parse_expr.h"
#include "parser/parse_relation.h"
#include "parser/parse_target.h"
@ -1626,9 +1627,7 @@ AddRelationRawConstraints(Relation rel,
/*
* Make sure it yields a boolean result.
*/
if (exprType(expr) != BOOLOID)
elog(ERROR, "CHECK constraint expression '%s' does not yield boolean result",
ccname);
expr = coerce_to_boolean(expr, "CHECK");
/*
* Make sure no outside relations are referred to.
@ -1764,6 +1763,12 @@ cookDefault(ParseState *pstate,
if (contain_var_clause(expr))
elog(ERROR, "cannot use column references in DEFAULT clause");
/*
* It can't return a set either.
*/
if (expression_returns_set(expr))
elog(ERROR, "DEFAULT clause must not return a set");
/*
* No subplans or aggregates, either...
*/

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.12 2002/04/27 21:24:34 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.13 2002/05/12 23:43:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -37,6 +37,7 @@
#include "optimizer/planmain.h"
#include "optimizer/prep.h"
#include "parser/parse.h"
#include "parser/parse_coerce.h"
#include "parser/parse_expr.h"
#include "parser/parse_relation.h"
#include "parser/parse_type.h"
@ -2461,9 +2462,7 @@ AlterTableAddConstraint(Oid myrelid,
/*
* Make sure it yields a boolean result.
*/
if (exprType(expr) != BOOLOID)
elog(ERROR, "CHECK '%s' does not yield boolean result",
name);
expr = coerce_to_boolean(expr, "CHECK");
/*
* Make sure no outside relations are
@ -2473,6 +2472,14 @@ AlterTableAddConstraint(Oid myrelid,
elog(ERROR, "Only relation '%s' can be referenced in CHECK",
RelationGetRelationName(rel));
/*
* No subplans or aggregates, either...
*/
if (contain_subplans(expr))
elog(ERROR, "cannot use subselect in CHECK constraint expression");
if (contain_agg_clause(expr))
elog(ERROR, "cannot use aggregate function in CHECK constraint expression");
/*
* Might as well try to reduce any
* constant expressions.

View File

@ -4,7 +4,7 @@
# Makefile for executor
#
# IDENTIFICATION
# $Header: /cvsroot/pgsql/src/backend/executor/Makefile,v 1.18 2002/05/12 20:10:02 tgl Exp $
# $Header: /cvsroot/pgsql/src/backend/executor/Makefile,v 1.19 2002/05/12 23:43:02 tgl Exp $
#
#-------------------------------------------------------------------------
@ -12,7 +12,7 @@ subdir = src/backend/executor
top_builddir = ../../..
include $(top_builddir)/src/Makefile.global
OBJS = execAmi.o execFlatten.o execJunk.o execMain.o \
OBJS = execAmi.o execJunk.o execMain.o \
execProcnode.o execQual.o execScan.o execTuples.o \
execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o nodeHash.o \
nodeHashjoin.o nodeIndexscan.o nodeMaterial.o nodeMergejoin.o \

View File

@ -1,243 +0,0 @@
/*-------------------------------------------------------------------------
*
* execFlatten.c
* This file handles the nodes associated with flattening sets in the
* target list of queries containing functions returning sets.
*
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/Attic/execFlatten.c,v 1.16 2001/10/28 06:25:43 momjian Exp $
*
*-------------------------------------------------------------------------
*/
/*
* ExecEvalIter() -
* Iterate through the all return tuples/base types from a function one
* at time (i.e. one per ExecEvalIter call). Not really needed for
* postquel functions, but for reasons of orthogonality, these nodes
* exist above pq functions as well as c functions.
*
* ExecEvalFjoin() -
* Given N Iter nodes return a vector of all combinations of results
* one at a time (i.e. one result vector per ExecEvalFjoin call). This
* node does the actual flattening work.
*/
#include "postgres.h"
#include "executor/execFlatten.h"
#include "executor/executor.h"
#ifdef SETS_FIXED
static bool FjoinBumpOuterNodes(TargetEntry *tlist, ExprContext *econtext,
DatumPtr results, char *nulls);
#endif
Datum
ExecEvalIter(Iter *iterNode,
ExprContext *econtext,
bool *isNull,
ExprDoneCond *isDone)
{
Node *expression;
expression = iterNode->iterexpr;
/*
* Really Iter nodes are only needed for C functions, postquel
* function by their nature return 1 result at a time. For now we are
* only worrying about postquel functions, c functions will come
* later.
*/
return ExecEvalExpr(expression, econtext, isNull, isDone);
}
void
ExecEvalFjoin(TargetEntry *tlist,
ExprContext *econtext,
bool *isNullVect,
ExprDoneCond *fj_isDone)
{
#ifdef SETS_FIXED
bool isDone;
int curNode;
List *tlistP;
Fjoin *fjNode = tlist->fjoin;
DatumPtr resVect = fjNode->fj_results;
BoolPtr alwaysDone = fjNode->fj_alwaysDone;
if (fj_isDone)
*fj_isDone = ExprMultipleResult;
/*
* For the next tuple produced by the plan, we need to re-initialize
* the Fjoin node.
*/
if (!fjNode->fj_initialized)
{
/*
* Initialize all of the Outer nodes
*/
curNode = 1;
foreach(tlistP, lnext(tlist))
{
TargetEntry *tle = lfirst(tlistP);
resVect[curNode] = ExecEvalIter((Iter *) tle->expr,
econtext,
&isNullVect[curNode],
&isDone);
if (isDone)
isNullVect[curNode] = alwaysDone[curNode] = true;
else
alwaysDone[curNode] = false;
curNode++;
}
/*
* Initialize the inner node
*/
resVect[0] = ExecEvalIter((Iter *) fjNode->fj_innerNode->expr,
econtext,
&isNullVect[0],
&isDone);
if (isDone)
isNullVect[0] = alwaysDone[0] = true;
else
alwaysDone[0] = false;
/*
* Mark the Fjoin as initialized now.
*/
fjNode->fj_initialized = TRUE;
/*
* If the inner node is always done, then we are done for now
*/
if (isDone)
return;
}
else
{
/*
* If we're already initialized, all we need to do is get the next
* inner result and pair it up with the existing outer node result
* vector. Watch out for the degenerate case, where the inner
* node never returns results.
*/
/*
* Fill in nulls for every function that is always done.
*/
for (curNode = fjNode->fj_nNodes - 1; curNode >= 0; curNode--)
isNullVect[curNode] = alwaysDone[curNode];
if (alwaysDone[0] == true)
{
*fj_isDone = FjoinBumpOuterNodes(tlist,
econtext,
resVect,
isNullVect);
return;
}
else
resVect[0] = ExecEvalIter((Iter *) fjNode->fj_innerNode->expr,
econtext,
&isNullVect[0],
&isDone);
}
/*
* if the inner node is done
*/
if (isDone)
{
*fj_isDone = FjoinBumpOuterNodes(tlist,
econtext,
resVect,
isNullVect);
if (*fj_isDone)
return;
resVect[0] = ExecEvalIter((Iter *) fjNode->fj_innerNode->expr,
econtext,
&isNullVect[0],
&isDone);
}
#endif
return;
}
#ifdef SETS_FIXED
static bool
FjoinBumpOuterNodes(TargetEntry *tlist,
ExprContext *econtext,
DatumPtr results,
char *nulls)
{
bool funcIsDone = true;
Fjoin *fjNode = tlist->fjoin;
char *alwaysDone = fjNode->fj_alwaysDone;
List *outerList = lnext(tlist);
List *trailers = lnext(tlist);
int trailNode = 1;
int curNode = 1;
/*
* Run through list of functions until we get to one that isn't yet
* done returning values. Watch out for funcs that are always done.
*/
while ((funcIsDone == true) && (outerList != NIL))
{
TargetEntry *tle = lfirst(outerList);
if (alwaysDone[curNode] == true)
nulls[curNode] = 'n';
else
results[curNode] = ExecEvalIter((Iter) tle->expr,
econtext,
&nulls[curNode],
&funcIsDone);
curNode++;
outerList = lnext(outerList);
}
/*
* If every function is done, then we are done flattening. Mark the
* Fjoin node uninitialized, it is time to get the next tuple from the
* plan and redo all of the flattening.
*/
if (funcIsDone)
{
set_fj_initialized(fjNode, false);
return true;
}
/*
* We found a function that wasn't done. Now re-run every function
* before it. As usual watch out for functions that are always done.
*/
trailNode = 1;
while (trailNode != curNode - 1)
{
TargetEntry *tle = lfirst(trailers);
if (alwaysDone[trailNode] != true)
results[trailNode] = ExecEvalIter((Iter) tle->expr,
econtext,
&nulls[trailNode],
&funcIsDone);
trailNode++;
trailers = lnext(trailers);
}
return false;
}
#endif

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.92 2002/05/12 20:10:02 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.93 2002/05/12 23:43:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -35,7 +35,6 @@
#include "postgres.h"
#include "access/heapam.h"
#include "executor/execFlatten.h"
#include "executor/execdebug.h"
#include "executor/functions.h"
#include "executor/nodeSubplan.h"
@ -1336,12 +1335,6 @@ ExecEvalExpr(Node *expression,
case T_Param:
retDatum = ExecEvalParam((Param *) expression, econtext, isNull);
break;
case T_Iter:
retDatum = ExecEvalIter((Iter *) expression,
econtext,
isNull,
isDone);
break;
case T_Aggref:
retDatum = ExecEvalAggref((Aggref *) expression, econtext, isNull);
break;

View File

@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.183 2002/05/12 20:10:02 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.184 2002/05/12 23:43:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -764,6 +764,7 @@ _copyOper(Oper *from)
newnode->opno = from->opno;
newnode->opid = from->opid;
newnode->opresulttype = from->opresulttype;
newnode->opretset = from->opretset;
/* Do not copy the run-time state, if any */
newnode->op_fcache = NULL;
@ -852,7 +853,8 @@ _copyFunc(Func *from)
* copy remainder of node
*/
newnode->funcid = from->funcid;
newnode->functype = from->functype;
newnode->funcresulttype = from->funcresulttype;
newnode->funcretset = from->funcretset;
/* Do not copy the run-time state, if any */
newnode->func_fcache = NULL;
@ -1433,17 +1435,6 @@ _copyJoinInfo(JoinInfo *from)
return newnode;
}
static Iter *
_copyIter(Iter *from)
{
Iter *newnode = makeNode(Iter);
Node_Copy(from, newnode, iterexpr);
newnode->itertype = from->itertype;
return newnode;
}
static Stream *
_copyStream(Stream *from)
{
@ -2729,9 +2720,6 @@ copyObject(void *from)
case T_ArrayRef:
retval = _copyArrayRef(from);
break;
case T_Iter:
retval = _copyIter(from);
break;
case T_FieldSelect:
retval = _copyFieldSelect(from);
break;

View File

@ -20,7 +20,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.131 2002/05/12 20:10:03 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.132 2002/05/12 23:43:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -130,6 +130,8 @@ _equalOper(Oper *a, Oper *b)
return false;
if (a->opresulttype != b->opresulttype)
return false;
if (a->opretset != b->opretset)
return false;
/*
* We do not examine opid or op_fcache, since these are logically
@ -139,7 +141,8 @@ _equalOper(Oper *a, Oper *b)
* (Besides, op_fcache is executor state, which we don't check --- see
* notes at head of file.)
*
* It's probably not really necessary to check opresulttype either...
* It's probably not really necessary to check opresulttype or opretset,
* either...
*/
return true;
@ -210,7 +213,9 @@ _equalFunc(Func *a, Func *b)
{
if (a->funcid != b->funcid)
return false;
if (a->functype != b->functype)
if (a->funcresulttype != b->funcresulttype)
return false;
if (a->funcretset != b->funcretset)
return false;
/* Note we do not look at func_fcache; see notes for _equalOper */
@ -538,12 +543,6 @@ _equalJoinInfo(JoinInfo *a, JoinInfo *b)
return true;
}
static bool
_equalIter(Iter *a, Iter *b)
{
return equal(a->iterexpr, b->iterexpr);
}
static bool
_equalStream(Stream *a, Stream *b)
{
@ -1884,9 +1883,6 @@ equal(void *a, void *b)
case T_ArrayRef:
retval = _equalArrayRef(a, b);
break;
case T_Iter:
retval = _equalIter(a, b);
break;
case T_RelabelType:
retval = _equalRelabelType(a, b);
break;

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/makefuncs.c,v 1.31 2002/04/16 23:08:10 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/makefuncs.c,v 1.32 2002/05/12 23:43:02 tgl Exp $
*/
#include "postgres.h"
@ -56,13 +56,15 @@ makeSimpleA_Expr(int oper, const char *name,
Oper *
makeOper(Oid opno,
Oid opid,
Oid opresulttype)
Oid opresulttype,
bool opretset)
{
Oper *oper = makeNode(Oper);
oper->opno = opno;
oper->opid = opid;
oper->opresulttype = opresulttype;
oper->opretset = opretset;
oper->op_fcache = NULL;
return oper;
}

View File

@ -5,7 +5,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.158 2002/05/12 20:10:03 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.159 2002/05/12 23:43:02 tgl Exp $
*
* NOTES
* Every (plan) node in POSTGRES has an associated "out" routine which
@ -850,9 +850,11 @@ _outArrayRef(StringInfo str, ArrayRef *node)
static void
_outFunc(StringInfo str, Func *node)
{
appendStringInfo(str, " FUNC :funcid %u :functype %u ",
appendStringInfo(str,
" FUNC :funcid %u :funcresulttype %u :funcretset %s ",
node->funcid,
node->functype);
node->funcresulttype,
booltostr(node->funcretset));
}
/*
@ -862,10 +864,11 @@ static void
_outOper(StringInfo str, Oper *node)
{
appendStringInfo(str,
" OPER :opno %u :opid %u :opresulttype %u ",
" OPER :opno %u :opid %u :opresulttype %u :opretset %s ",
node->opno,
node->opid,
node->opresulttype);
node->opresulttype,
booltostr(node->opretset));
}
/*
@ -1246,13 +1249,6 @@ _outDatum(StringInfo str, Datum value, int typlen, bool typbyval)
}
}
static void
_outIter(StringInfo str, Iter *node)
{
appendStringInfo(str, " ITER :iterexpr ");
_outNode(str, node->iterexpr);
}
static void
_outStream(StringInfo str, Stream *node)
{
@ -1731,9 +1727,6 @@ _outNode(StringInfo str, void *obj)
case T_JoinInfo:
_outJoinInfo(str, obj);
break;
case T_Iter:
_outIter(str, obj);
break;
case T_Stream:
_outStream(str, obj);
break;

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.121 2002/05/12 20:10:03 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.122 2002/05/12 23:43:02 tgl Exp $
*
* NOTES
* Most of the read functions for plan nodes are tested. (In fact, they
@ -1083,9 +1083,13 @@ _readFunc(void)
token = pg_strtok(&length); /* now read it */
local_node->funcid = atooid(token);
token = pg_strtok(&length); /* get :functype */
token = pg_strtok(&length); /* get :funcresulttype */
token = pg_strtok(&length); /* now read it */
local_node->functype = atooid(token);
local_node->funcresulttype = atooid(token);
token = pg_strtok(&length); /* get :funcretset */
token = pg_strtok(&length); /* now read it */
local_node->funcretset = strtobool(token);
local_node->func_fcache = NULL;
@ -1119,6 +1123,10 @@ _readOper(void)
token = pg_strtok(&length); /* now read it */
local_node->opresulttype = atooid(token);
token = pg_strtok(&length); /* get :opretset */
token = pg_strtok(&length); /* now read it */
local_node->opretset = strtobool(token);
local_node->op_fcache = NULL;
return local_node;
@ -1991,26 +1999,6 @@ _readJoinInfo(void)
return local_node;
}
/* ----------------
* _readIter()
*
* ----------------
*/
static Iter *
_readIter(void)
{
Iter *local_node;
char *token;
int length;
local_node = makeNode(Iter);
token = pg_strtok(&length); /* eat :iterexpr */
local_node->iterexpr = nodeRead(true); /* now read it */
return local_node;
}
/* ----------------
* parsePlanString
@ -2124,8 +2112,6 @@ parsePlanString(void)
return_value = _readRestrictInfo();
else if (length == 8 && strncmp(token, "JOININFO", length) == 0)
return_value = _readJoinInfo();
else if (length == 4 && strncmp(token, "ITER", length) == 0)
return_value = _readIter();
else if (length == 5 && strncmp(token, "QUERY", length) == 0)
return_value = _readQuery();
else if (length == 6 && strncmp(token, "NOTIFY", length) == 0)

View File

@ -42,7 +42,8 @@ base rels of the query.
Possible Paths for a primitive table relation include plain old sequential
scan, plus index scans for any indexes that exist on the table. A subquery
base relation just has one Path, a "SubqueryScan" path (which links to the
subplan that was built by a recursive invocation of the planner).
subplan that was built by a recursive invocation of the planner). Likewise
a function-RTE base relation has only one possible Path.
Joins always occur using two RelOptInfos. One is outer, the other inner.
Outers drive lookups of values in the inner. In a nested loop, lookups of

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.84 2002/05/12 20:10:03 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.85 2002/05/12 23:43:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -312,8 +312,8 @@ set_subquery_pathlist(Query *root, RelOptInfo *rel,
* checking that seems more work than it's worth. In any case, a
* plain DISTINCT is safe to push down past.)
*
* 3. If the subquery has any ITER nodes (ie, functions returning sets)
* in its target list, we do not push down any quals, since the quals
* 3. If the subquery has any functions returning sets in its target list,
* we do not push down any quals, since the quals
* might refer to those tlist items, which would mean we'd introduce
* functions-returning-sets into the subquery's WHERE/HAVING quals.
* (It'd be sufficient to not push down quals that refer to those
@ -333,7 +333,7 @@ set_subquery_pathlist(Query *root, RelOptInfo *rel,
subquery->limitOffset == NULL &&
subquery->limitCount == NULL &&
!has_distinct_on_clause(subquery) &&
!contain_iter_clause((Node *) subquery->targetList))
!expression_returns_set((Node *) subquery->targetList))
{
/* OK to consider pushing down individual quals */
List *upperrestrictlist = NIL;

View File

@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.116 2002/04/16 23:08:10 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.117 2002/05/12 23:43:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -1316,7 +1316,8 @@ pred_test_simple_clause(Expr *predicate, Node *clause)
*/
test_oper = makeOper(test_op, /* opno */
InvalidOid, /* opid */
BOOLOID); /* opresulttype */
BOOLOID, /* opresulttype */
false); /* opretset */
replace_opid(test_oper);
test_expr = make_opclause(test_oper,
(Var *) clause_const,
@ -2020,7 +2021,7 @@ prefix_quals(Var *leftop, Oid expr_op,
if (oproid == InvalidOid)
elog(ERROR, "prefix_quals: no = operator for type %u", datatype);
con = string_to_const(prefix, datatype);
op = makeOper(oproid, InvalidOid, BOOLOID);
op = makeOper(oproid, InvalidOid, BOOLOID, false);
expr = make_opclause(op, leftop, (Var *) con);
result = makeList1(expr);
return result;
@ -2035,7 +2036,7 @@ prefix_quals(Var *leftop, Oid expr_op,
if (oproid == InvalidOid)
elog(ERROR, "prefix_quals: no >= operator for type %u", datatype);
con = string_to_const(prefix, datatype);
op = makeOper(oproid, InvalidOid, BOOLOID);
op = makeOper(oproid, InvalidOid, BOOLOID, false);
expr = make_opclause(op, leftop, (Var *) con);
result = makeList1(expr);
@ -2051,7 +2052,7 @@ prefix_quals(Var *leftop, Oid expr_op,
if (oproid == InvalidOid)
elog(ERROR, "prefix_quals: no < operator for type %u", datatype);
con = string_to_const(greaterstr, datatype);
op = makeOper(oproid, InvalidOid, BOOLOID);
op = makeOper(oproid, InvalidOid, BOOLOID, false);
expr = make_opclause(op, leftop, (Var *) con);
result = lappend(result, expr);
pfree(greaterstr);
@ -2116,7 +2117,7 @@ network_prefix_quals(Var *leftop, Oid expr_op, Datum rightop)
opr1right = network_scan_first(rightop);
op = makeOper(opr1oid, InvalidOid, BOOLOID);
op = makeOper(opr1oid, InvalidOid, BOOLOID, false);
expr = make_opclause(op, leftop,
(Var *) makeConst(datatype, -1, opr1right,
false, false, false, false));
@ -2131,7 +2132,7 @@ network_prefix_quals(Var *leftop, Oid expr_op, Datum rightop)
opr2right = network_scan_last(rightop);
op = makeOper(opr2oid, InvalidOid, BOOLOID);
op = makeOper(opr2oid, InvalidOid, BOOLOID, false);
expr = make_opclause(op, leftop,
(Var *) makeConst(datatype, -1, opr2right,
false, false, false, false));

View File

@ -11,7 +11,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.37 2002/03/12 00:51:44 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.38 2002/05/12 23:43:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -518,7 +518,8 @@ build_index_pathkeys(Query *root,
List *funcargs = NIL;
funcnode->funcid = index->indproc;
funcnode->functype = get_func_rettype(index->indproc);
funcnode->funcresulttype = get_func_rettype(index->indproc);
funcnode->funcretset = false; /* can never be a set */
funcnode->func_fcache = NULL;
while (*indexkeys != 0)

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.69 2002/04/28 19:54:28 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.70 2002/05/12 23:43:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -764,9 +764,10 @@ process_implied_equality(Query *root, Node *item1, Node *item2,
clause = makeNode(Expr);
clause->typeOid = BOOLOID;
clause->opType = OP_EXPR;
clause->oper = (Node *) makeOper(oprid(eq_operator), /* opno */
clause->oper = (Node *) makeOper(oprid(eq_operator),/* opno */
InvalidOid, /* opid */
BOOLOID); /* operator result type */
BOOLOID, /* opresulttype */
false); /* opretset */
clause->args = makeList2(item1, item2);
ReleaseSysCache(eq_operator);

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.116 2002/04/28 19:54:28 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.117 2002/05/12 23:43:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -498,7 +498,7 @@ is_simple_subquery(Query *subquery)
* set-returning functions into places where they mustn't go,
* such as quals of higher queries.
*/
if (contain_iter_clause((Node *) subquery->targetList))
if (expression_returns_set((Node *) subquery->targetList))
return false;
/*

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepqual.c,v 1.30 2001/10/25 05:49:33 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepqual.c,v 1.31 2002/05/12 23:43:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -502,7 +502,8 @@ push_nots(Expr *qual)
{
Oper *op = (Oper *) makeOper(negator,
InvalidOid,
oper->opresulttype);
oper->opresulttype,
oper->opretset);
return make_opclause(op, get_leftop(qual), get_rightop(qual));
}

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.98 2002/05/12 20:10:03 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.99 2002/05/12 23:43:03 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@ -47,7 +47,7 @@ typedef struct
static bool contain_agg_clause_walker(Node *node, void *context);
static bool pull_agg_clause_walker(Node *node, List **listptr);
static bool contain_iter_clause_walker(Node *node, void *context);
static bool expression_returns_set_walker(Node *node, void *context);
static bool contain_subplans_walker(Node *node, void *context);
static bool pull_subplans_walker(Node *node, List **listptr);
static bool check_subplans_for_ungrouped_vars_walker(Node *node,
@ -74,7 +74,7 @@ make_clause(int type, Node *oper, List *args)
expr->typeOid = ((Oper *) oper)->opresulttype;
break;
case FUNC_EXPR:
expr->typeOid = ((Func *) oper)->functype;
expr->typeOid = ((Func *) oper)->funcresulttype;
break;
default:
elog(ERROR, "make_clause: unsupported type %d", type);
@ -195,7 +195,7 @@ make_funcclause(Func *func, List *funcargs)
{
Expr *expr = makeNode(Expr);
expr->typeOid = func->functype;
expr->typeOid = func->funcresulttype;
expr->opType = FUNC_EXPR;
expr->oper = (Node *) func;
expr->args = funcargs;
@ -453,36 +453,61 @@ pull_agg_clause_walker(Node *node, List **listptr)
/*****************************************************************************
* Iter clause manipulation
* Support for expressions returning sets
*****************************************************************************/
/*
* contain_iter_clause
* Recursively search for Iter nodes within a clause.
* expression_returns_set
* Test whethe an expression returns a set result.
*
* Returns true if any Iter found.
*
* XXX Iter is a crock. It'd be better to look directly at each function
* or operator to see if it can return a set. However, that would require
* a lot of extra cycles as things presently stand. The return-type info
* for function and operator nodes should be extended to include whether
* the return is a set.
* Because we use expression_tree_walker(), this can also be applied to
* whole targetlists; it'll produce TRUE if any one of the tlist items
* returns a set.
*/
bool
contain_iter_clause(Node *clause)
expression_returns_set(Node *clause)
{
return contain_iter_clause_walker(clause, NULL);
return expression_returns_set_walker(clause, NULL);
}
static bool
contain_iter_clause_walker(Node *node, void *context)
expression_returns_set_walker(Node *node, void *context)
{
if (node == NULL)
return false;
if (IsA(node, Iter))
return true; /* abort the tree traversal and return
* true */
return expression_tree_walker(node, contain_iter_clause_walker, context);
if (IsA(node, Expr))
{
Expr *expr = (Expr *) node;
switch (expr->opType)
{
case OP_EXPR:
if (((Oper *) expr->oper)->opretset)
return true;
/* else fall through to check args */
break;
case FUNC_EXPR:
if (((Func *) expr->oper)->funcretset)
return true;
/* else fall through to check args */
break;
case OR_EXPR:
case AND_EXPR:
case NOT_EXPR:
/* Booleans can't return a set, so no need to recurse */
return false;
case SUBPLAN_EXPR:
/* Subplans can't presently return sets either */
return false;
}
}
/* Avoid recursion for some other cases that can't return a set */
if (IsA(node, Aggref))
return false;
if (IsA(node, SubLink))
return false;
return expression_tree_walker(node, expression_returns_set_walker,
context);
}
/*****************************************************************************
@ -1043,7 +1068,8 @@ CommuteClause(Expr *clause)
commu = makeOper(optup->t_data->t_oid,
commuTup->oprcode,
commuTup->oprresult);
commuTup->oprresult,
((Oper *) clause->oper)->opretset);
ReleaseSysCache(optup);
@ -1073,8 +1099,7 @@ CommuteClause(Expr *clause)
* results even with constant inputs, "nextval()" being the classic
* example. Functions that are not marked "immutable" in pg_proc
* will not be pre-evaluated here, although we will reduce their
* arguments as far as possible. Functions that are the arguments
* of Iter nodes are also not evaluated.
* arguments as far as possible.
*
* We assume that the tree has already been type-checked and contains
* only operators and functions that are reasonable to try to execute.
@ -1398,37 +1423,6 @@ eval_const_expressions_mutator(Node *node, void *context)
newcase->defresult = defresult;
return (Node *) newcase;
}
if (IsA(node, Iter))
{
/*
* The argument of an Iter is normally a function call. We must
* not try to eliminate the function, but we can try to simplify
* its arguments. If, by chance, the arg is NOT a function then
* we go ahead and try to simplify it (by falling into
* expression_tree_mutator). Is that the right thing?
*/
Iter *iter = (Iter *) node;
if (is_funcclause(iter->iterexpr))
{
Expr *func = (Expr *) iter->iterexpr;
Expr *newfunc;
Iter *newiter;
newfunc = makeNode(Expr);
newfunc->typeOid = func->typeOid;
newfunc->opType = func->opType;
newfunc->oper = func->oper;
newfunc->args = (List *)
expression_tree_mutator((Node *) func->args,
eval_const_expressions_mutator,
(void *) context);
newiter = makeNode(Iter);
newiter->iterexpr = (Node *) newfunc;
newiter->itertype = iter->itertype;
return (Node *) newiter;
}
}
/*
* For any node type not handled above, we recurse using
@ -1501,8 +1495,9 @@ simplify_op_or_func(Expr *expr, List *args)
* Get the function procedure's OID and look to see whether it is
* marked immutable.
*
* XXX would it be better to take the result type from the pg_proc tuple,
* rather than the Oper or Func node?
* Note we take the result type from the Oper or Func node, not the
* pg_proc tuple; probably necessary for binary-compatibility cases.
*
*/
if (expr->opType == OP_EXPR)
{
@ -1517,7 +1512,7 @@ simplify_op_or_func(Expr *expr, List *args)
Func *func = (Func *) expr->oper;
funcid = func->funcid;
result_typeid = func->functype;
result_typeid = func->funcresulttype;
}
/*
@ -1747,8 +1742,6 @@ expression_tree_walker(Node *node,
break;
case T_Aggref:
return walker(((Aggref *) node)->target, context);
case T_Iter:
return walker(((Iter *) node)->iterexpr, context);
case T_ArrayRef:
{
ArrayRef *aref = (ArrayRef *) node;
@ -2083,16 +2076,6 @@ expression_tree_mutator(Node *node,
return (Node *) newnode;
}
break;
case T_Iter:
{
Iter *iter = (Iter *) node;
Iter *newnode;
FLATCOPY(newnode, iter, Iter);
MUTATE(newnode->iterexpr, iter->iterexpr, Node *);
return (Node *) newnode;
}
break;
case T_ArrayRef:
{
ArrayRef *arrayref = (ArrayRef *) node;

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.91 2002/05/12 20:10:04 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.92 2002/05/12 23:43:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -285,14 +285,7 @@ transformJoinUsingClause(ParseState *pstate, List *leftVars, List *rightVars)
*/
result = transformExpr(pstate, result);
/*
* We expect the result to yield bool directly, otherwise complain. We
* could try coerce_to_boolean() here, but it seems likely that an "="
* operator that doesn't return bool is wrong anyway.
*/
if (exprType(result) != BOOLOID)
elog(ERROR, "JOIN/USING clause must return type boolean, not type %s",
format_type_be(exprType(result)));
result = coerce_to_boolean(result, "JOIN/USING");
return result;
} /* transformJoinUsingClause() */
@ -326,9 +319,7 @@ transformJoinOnClause(ParseState *pstate, JoinExpr *j,
/* This part is just like transformWhereClause() */
result = transformExpr(pstate, j->quals);
if (!coerce_to_boolean(pstate, &result))
elog(ERROR, "JOIN/ON clause must return type boolean, not type %s",
format_type_be(exprType(result)));
result = coerce_to_boolean(result, "JOIN/ON");
pstate->p_namespace = save_namespace;
@ -486,14 +477,7 @@ transformRangeFunction(ParseState *pstate, RangeFunction *r)
elog(ERROR, "cannot use subselect in FROM function expression");
/*
* Remove any Iter nodes added by parse_func.c. We oughta get rid of
* Iter completely ...
*/
while (funcexpr && IsA(funcexpr, Iter))
funcexpr = ((Iter *) funcexpr)->iterexpr;
/*
* Insist we now have a bare function call (explain.c is the only place
* Insist we have a bare function call (explain.c is the only place
* that depends on this, I think). If this fails, it's probably because
* transformExpr interpreted the function notation as a type coercion.
*/
@ -947,9 +931,7 @@ transformWhereClause(ParseState *pstate, Node *clause)
qual = transformExpr(pstate, clause);
if (!coerce_to_boolean(pstate, &qual))
elog(ERROR, "WHERE clause must return type boolean, not type %s",
format_type_be(exprType(qual)));
qual = coerce_to_boolean(qual, "WHERE");
return qual;
}

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.71 2002/04/25 02:56:55 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.72 2002/05/12 23:43:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -313,26 +313,37 @@ coerce_type_typmod(ParseState *pstate, Node *node,
/* coerce_to_boolean()
* Coerce an argument of a construct that requires boolean input
* (AND, OR, NOT, etc).
* (AND, OR, NOT, etc). Also check that input is not a set.
*
* If successful, update *pnode to be the transformed argument (if any
* transformation is needed), and return TRUE. If fail, return FALSE.
* (The caller must check for FALSE and emit a suitable error message.)
* Returns the possibly-transformed node tree.
*/
bool
coerce_to_boolean(ParseState *pstate, Node **pnode)
Node *
coerce_to_boolean(Node *node, const char *constructName)
{
Oid inputTypeId = exprType(*pnode);
Oid inputTypeId = exprType(node);
Oid targetTypeId;
if (inputTypeId == BOOLOID)
return true; /* no work */
targetTypeId = BOOLOID;
if (!can_coerce_type(1, &inputTypeId, &targetTypeId, false))
return false; /* fail, but let caller choose error msg */
*pnode = coerce_type(pstate, *pnode, inputTypeId, targetTypeId, -1,
false);
return true;
if (inputTypeId != BOOLOID)
{
targetTypeId = BOOLOID;
if (!can_coerce_type(1, &inputTypeId, &targetTypeId, false))
{
/* translator: first %s is name of a SQL construct, eg WHERE */
elog(ERROR, "Argument of %s must be type boolean, not type %s",
constructName, format_type_be(inputTypeId));
}
node = coerce_type(NULL, node, inputTypeId, targetTypeId, -1,
false);
}
if (expression_returns_set(node))
{
/* translator: %s is name of a SQL construct, eg WHERE */
elog(ERROR, "Argument of %s must not be a set function",
constructName);
}
return node;
}
@ -782,7 +793,8 @@ build_func_call(Oid funcid, Oid rettype, List *args)
funcnode = makeNode(Func);
funcnode->funcid = funcid;
funcnode->functype = rettype;
funcnode->funcresulttype = rettype;
funcnode->funcretset = false; /* only possible case here */
funcnode->func_fcache = NULL;
expr = makeNode(Expr);

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.116 2002/04/28 00:49:12 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.117 2002/05/12 23:43:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -31,6 +31,7 @@
#include "parser/parse_target.h"
#include "parser/parse_type.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
@ -230,15 +231,8 @@ transformExpr(ParseState *pstate, Node *expr)
a->rexpr);
Expr *expr = makeNode(Expr);
if (!coerce_to_boolean(pstate, &lexpr))
elog(ERROR, "left-hand side of AND is type '%s', not '%s'",
format_type_be(exprType(lexpr)),
format_type_be(BOOLOID));
if (!coerce_to_boolean(pstate, &rexpr))
elog(ERROR, "right-hand side of AND is type '%s', not '%s'",
format_type_be(exprType(rexpr)),
format_type_be(BOOLOID));
lexpr = coerce_to_boolean(lexpr, "AND");
rexpr = coerce_to_boolean(rexpr, "AND");
expr->typeOid = BOOLOID;
expr->opType = AND_EXPR;
@ -254,15 +248,8 @@ transformExpr(ParseState *pstate, Node *expr)
a->rexpr);
Expr *expr = makeNode(Expr);
if (!coerce_to_boolean(pstate, &lexpr))
elog(ERROR, "left-hand side of OR is type '%s', not '%s'",
format_type_be(exprType(lexpr)),
format_type_be(BOOLOID));
if (!coerce_to_boolean(pstate, &rexpr))
elog(ERROR, "right-hand side of OR is type '%s', not '%s'",
format_type_be(exprType(rexpr)),
format_type_be(BOOLOID));
lexpr = coerce_to_boolean(lexpr, "OR");
rexpr = coerce_to_boolean(rexpr, "OR");
expr->typeOid = BOOLOID;
expr->opType = OR_EXPR;
@ -276,10 +263,7 @@ transformExpr(ParseState *pstate, Node *expr)
a->rexpr);
Expr *expr = makeNode(Expr);
if (!coerce_to_boolean(pstate, &rexpr))
elog(ERROR, "argument to NOT is type '%s', not '%s'",
format_type_be(exprType(rexpr)),
format_type_be(BOOLOID));
rexpr = coerce_to_boolean(rexpr, "NOT");
expr->typeOid = BOOLOID;
expr->opType = NOT_EXPR;
@ -426,9 +410,15 @@ transformExpr(ParseState *pstate, Node *expr)
opname, typeidTypeName(opform->oprresult),
typeidTypeName(BOOLOID));
if (get_func_retset(opform->oprcode))
elog(ERROR, "'%s' must not return a set"
" to be used with quantified predicate subquery",
opname);
newop = makeOper(oprid(optup), /* opno */
InvalidOid, /* opid */
opform->oprresult);
opform->oprresult,
false);
sublink->oper = lappend(sublink->oper, newop);
ReleaseSysCache(optup);
}
@ -467,8 +457,7 @@ transformExpr(ParseState *pstate, Node *expr)
}
neww->expr = transformExpr(pstate, warg);
if (!coerce_to_boolean(pstate, &neww->expr))
elog(ERROR, "WHEN clause must have a boolean result");
neww->expr = coerce_to_boolean(neww->expr, "CASE/WHEN");
/*
* result is NULL for NULLIF() construct - thomas
@ -553,42 +542,38 @@ transformExpr(ParseState *pstate, Node *expr)
case T_BooleanTest:
{
BooleanTest *b = (BooleanTest *) expr;
const char *clausename;
switch (b->booltesttype)
{
case IS_TRUE:
clausename = "IS TRUE";
break;
case IS_NOT_TRUE:
clausename = "IS NOT TRUE";
break;
case IS_FALSE:
clausename = "IS FALSE";
break;
case IS_NOT_FALSE:
clausename = "IS NOT FALSE";
break;
case IS_UNKNOWN:
clausename = "IS UNKNOWN";
break;
case IS_NOT_UNKNOWN:
clausename = "IS NOT UNKNOWN";
break;
default:
elog(ERROR, "transformExpr: unexpected booltesttype %d",
(int) b->booltesttype);
clausename = NULL; /* keep compiler quiet */
}
b->arg = transformExpr(pstate, b->arg);
if (!coerce_to_boolean(pstate, &b->arg))
{
const char *clausename;
b->arg = coerce_to_boolean(b->arg, clausename);
switch (b->booltesttype)
{
case IS_TRUE:
clausename = "IS TRUE";
break;
case IS_NOT_TRUE:
clausename = "IS NOT TRUE";
break;
case IS_FALSE:
clausename = "IS FALSE";
break;
case IS_NOT_FALSE:
clausename = "IS NOT FALSE";
break;
case IS_UNKNOWN:
clausename = "IS UNKNOWN";
break;
case IS_NOT_UNKNOWN:
clausename = "IS NOT UNKNOWN";
break;
default:
elog(ERROR, "transformExpr: unexpected booltesttype %d",
(int) b->booltesttype);
clausename = NULL; /* keep compiler quiet */
}
elog(ERROR, "Argument of %s must be boolean",
clausename);
}
result = expr;
break;
}
@ -833,12 +818,6 @@ exprType(Node *expr)
switch (nodeTag(expr))
{
case T_Func:
type = ((Func *) expr)->functype;
break;
case T_Iter:
type = ((Iter *) expr)->itertype;
break;
case T_Var:
type = ((Var *) expr)->vartype;
break;

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.128 2002/05/12 20:10:04 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.129 2002/05/12 23:43:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -280,7 +280,8 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
Func *funcnode = makeNode(Func);
funcnode->funcid = funcid;
funcnode->functype = rettype;
funcnode->funcresulttype = rettype;
funcnode->funcretset = retset;
funcnode->func_fcache = NULL;
expr->typeOid = rettype;
@ -289,21 +290,6 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
expr->args = fargs;
retval = (Node *) expr;
/*
* if the function returns a set of values, then we need to iterate
* over all the returned values in the executor, so we stick an iter
* node here. if it returns a singleton, then we don't need the iter
* node.
*/
if (retset)
{
Iter *iter = makeNode(Iter);
iter->itertype = rettype;
iter->iterexpr = retval;
retval = (Node *) iter;
}
}
else
{
@ -1186,26 +1172,6 @@ ParseComplexProjection(ParseState *pstate,
*/
switch (nodeTag(first_arg))
{
case T_Iter:
{
Iter *iter = (Iter *) first_arg;
/*
* If it's an Iter, we stick the FieldSelect
* *inside* the Iter --- this is klugy, but necessary
* because ExecTargetList() currently does the right thing
* only when the Iter node is at the top level of a
* targetlist item.
*
* XXX Iter should go away altogether...
*/
fselect = setup_field_select(iter->iterexpr,
funcname, argrelid);
iter->iterexpr = (Node *) fselect;
iter->itertype = fselect->resulttype;
return (Node *) iter;
break;
}
case T_Var:
{
Var *var = (Var *) first_arg;

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.64 2002/05/01 19:26:07 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.65 2002/05/12 23:43:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -135,7 +135,8 @@ make_op(List *opname, Node *ltree, Node *rtree)
newop = makeOper(oprid(tup), /* opno */
InvalidOid, /* opid */
opform->oprresult); /* operator result type */
opform->oprresult, /* opresulttype */
get_func_retset(opform->oprcode)); /* opretset */
result = makeNode(Expr);
result->typeOid = opform->oprresult;

View File

@ -3,7 +3,7 @@
* back to source text
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.103 2002/05/12 20:10:04 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.104 2002/05/12 23:43:03 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@ -1673,10 +1673,6 @@ get_rule_expr(Node *node, deparse_context *context)
get_agg_expr((Aggref *) node, context);
break;
case T_Iter:
get_rule_expr(((Iter *) node)->iterexpr, context);
break;
case T_ArrayRef:
{
ArrayRef *aref = (ArrayRef *) node;

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.72 2002/04/30 01:26:26 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.73 2002/05/12 23:43:03 tgl Exp $
*
* NOTES
* Eventually, the index information should go through here, too.
@ -612,6 +612,27 @@ get_func_rettype(Oid funcid)
return result;
}
/*
* get_func_retset
* Given procedure id, return the function's proretset flag.
*/
bool
get_func_retset(Oid funcid)
{
HeapTuple tp;
bool result;
tp = SearchSysCache(PROCOID,
ObjectIdGetDatum(funcid),
0, 0, 0);
if (!HeapTupleIsValid(tp))
elog(ERROR, "Function OID %u does not exist", funcid);
result = ((Form_pg_proc) GETSTRUCT(tp))->proretset;
ReleaseSysCache(tp);
return result;
}
/*
* func_volatile
* Given procedure id, return the function's provolatile flag.

View File

@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: catversion.h,v 1.130 2002/05/12 20:10:04 tgl Exp $
* $Id: catversion.h,v 1.131 2002/05/12 23:43:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -53,6 +53,6 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 200205121
#define CATALOG_VERSION_NO 200205122
#endif

View File

@ -1,27 +0,0 @@
/*-------------------------------------------------------------------------
*
* execFlatten.h
* prototypes for execFlatten.c.
*
*
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: execFlatten.h,v 1.16 2001/11/05 17:46:33 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#ifndef EXECFLATTEN_H
#define EXECFLATTEN_H
#include "nodes/execnodes.h"
#include "nodes/parsenodes.h"
extern Datum ExecEvalIter(Iter *iterNode, ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone);
extern void ExecEvalFjoin(TargetEntry *tlist, ExprContext *econtext,
bool *isNullVect, ExprDoneCond *fj_isDone);
#endif /* EXECFLATTEN_H */

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: makefuncs.h,v 1.35 2002/04/16 23:08:12 tgl Exp $
* $Id: makefuncs.h,v 1.36 2002/05/12 23:43:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -22,8 +22,9 @@ extern A_Expr *makeSimpleA_Expr(int oper, const char *name,
Node *lexpr, Node *rexpr);
extern Oper *makeOper(Oid opno,
Oid opid,
Oid opresulttype);
Oid opid,
Oid opresulttype,
bool opretset);
extern Var *makeVar(Index varno,
AttrNumber varattno,

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: nodes.h,v 1.106 2002/05/12 20:10:04 tgl Exp $
* $Id: nodes.h,v 1.107 2002/05/12 23:43:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -66,7 +66,6 @@ typedef enum NodeTag
T_Func,
T_FieldSelect,
T_ArrayRef,
T_Iter,
T_RelabelType,
T_RangeTblRef,
T_FromExpr,

View File

@ -10,7 +10,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: primnodes.h,v 1.62 2002/05/12 20:10:05 tgl Exp $
* $Id: primnodes.h,v 1.63 2002/05/12 23:43:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -151,12 +151,51 @@ typedef struct Expr
{
NodeTag type;
Oid typeOid; /* oid of the type of this expression */
OpType opType; /* type of this expression */
OpType opType; /* kind of expression */
Node *oper; /* operator node if needed (Oper, Func, or
* SubPlan) */
List *args; /* arguments to this expression */
} Expr;
/*
* Oper - Expr subnode for an OP_EXPR
*
* NOTE: in the good old days 'opno' used to be both (or either, or
* neither) the pg_operator oid, and/or the pg_proc oid depending
* on the postgres module in question (parser->pg_operator,
* executor->pg_proc, planner->both), the mood of the programmer,
* and the phase of the moon (rumors that it was also depending on the day
* of the week are probably false). To make things even more postgres-like
* (i.e. a mess) some comments were referring to 'opno' using the name
* 'opid'. Anyway, now we have two separate fields, and of course that
* immediately removes all bugs from the code... [ sp :-) ].
*
* Note also that opid is not necessarily filled in immediately on creation
* of the node. The planner makes sure it is valid before passing the node
* tree to the executor, but during parsing/planning opid is typically 0.
*/
typedef struct Oper
{
NodeTag type;
Oid opno; /* PG_OPERATOR OID of the operator */
Oid opid; /* PG_PROC OID of underlying function */
Oid opresulttype; /* PG_TYPE OID of result value */
bool opretset; /* true if operator returns set */
FunctionCachePtr op_fcache; /* runtime state, else NULL */
} Oper;
/*
* Func - Expr subnode for a FUNC_EXPR
*/
typedef struct Func
{
NodeTag type;
Oid funcid; /* PG_PROC OID of the function */
Oid funcresulttype; /* PG_TYPE OID of result value */
bool funcretset; /* true if function returns set */
FunctionCachePtr func_fcache; /* runtime state, or NULL */
} Func;
/*
* Var
*
@ -196,37 +235,6 @@ typedef struct Var
AttrNumber varoattno; /* original value of varattno */
} Var;
/*--------------------
* Oper
*
* NOTE: in the good old days 'opno' used to be both (or either, or
* neither) the pg_operator oid, and/or the pg_proc oid depending
* on the postgres module in question (parser->pg_operator,
* executor->pg_proc, planner->both), the mood of the programmer,
* and the phase of the moon (rumors that it was also depending on the day
* of the week are probably false). To make things even more postgres-like
* (i.e. a mess) some comments were referring to 'opno' using the name
* 'opid'. Anyway, now we have two separate fields, and of course that
* immediately removes all bugs from the code... [ sp :-) ].
*
* Note also that opid is not necessarily filled in immediately on creation
* of the node. The planner makes sure it is valid before passing the node
* tree to the executor, but during parsing/planning opid is typically 0.
*--------------------
*/
typedef struct Oper
{
NodeTag type;
Oid opno; /* PG_OPERATOR OID of the operator */
Oid opid; /* PG_PROC OID for the operator's
* underlying function */
Oid opresulttype;
/* PG_TYPE OID of the operator's return value */
FunctionCachePtr op_fcache;
/* runtime state while running the function */
} Oper;
/*
* Const
*/
@ -283,39 +291,6 @@ typedef struct Param
Oid paramtype; /* PG_TYPE OID of the parameter's value */
} Param;
/*
* Func
*/
typedef struct Func
{
NodeTag type;
Oid funcid; /* PG_PROC OID of the function */
Oid functype; /* PG_TYPE OID of the function's return
* value */
FunctionCachePtr func_fcache;
/*
* runtime state while running this function. Where we are in the
* execution of the function if it returns more than one value, etc.
* See utils/fcache.h
*/
} Func;
/* ----------------
* Iter
* can anyone explain what this is for? Seems to have something to do
* with evaluation of functions that return sets...
* ----------------
*/
typedef struct Iter
{
NodeTag type;
Node *iterexpr;
Oid itertype; /* type of the iter expr (use for type
* checking) */
} Iter;
/*
* Aggref
*/

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: clauses.h,v 1.51 2002/04/05 00:31:35 tgl Exp $
* $Id: clauses.h,v 1.52 2002/05/12 23:43:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -42,7 +42,7 @@ extern List *make_ands_implicit(Expr *clause);
extern bool contain_agg_clause(Node *clause);
extern List *pull_agg_clause(Node *clause);
extern bool contain_iter_clause(Node *clause);
extern bool expression_returns_set(Node *clause);
extern bool contain_subplans(Node *clause);
extern List *pull_subplans(Node *clause);

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: parse_coerce.h,v 1.42 2002/04/11 20:00:15 tgl Exp $
* $Id: parse_coerce.h,v 1.43 2002/05/12 23:43:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -45,7 +45,7 @@ extern Node *coerce_type(ParseState *pstate, Node *node, Oid inputTypeId,
extern Node *coerce_type_typmod(ParseState *pstate, Node *node,
Oid targetTypeId, int32 atttypmod);
extern bool coerce_to_boolean(ParseState *pstate, Node **pnode);
extern Node *coerce_to_boolean(Node *node, const char *constructName);
extern Oid select_common_type(List *typeids, const char *context);
extern Node *coerce_to_common_type(ParseState *pstate, Node *node,

View File

@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: lsyscache.h,v 1.51 2002/04/30 01:26:26 tgl Exp $
* $Id: lsyscache.h,v 1.52 2002/05/12 23:43:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -39,6 +39,7 @@ extern RegProcedure get_oprrest(Oid opno);
extern RegProcedure get_oprjoin(Oid opno);
extern char *get_func_name(Oid funcid);
extern Oid get_func_rettype(Oid funcid);
extern bool get_func_retset(Oid funcid);
extern char func_volatile(Oid funcid);
extern Oid get_relname_relid(const char *relname, Oid relnamespace);
extern char *get_rel_name(Oid relid);