diff options
33 files changed, 297 insertions, 676 deletions
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index e0301094d16..df6c44f5bc5 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -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. @@ -1765,6 +1764,12 @@ cookDefault(ParseState *pstate, 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... */ if (contain_subplans(expr)) diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 79b54aa7a5a..885b760a512 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -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 @@ -2474,6 +2473,14 @@ AlterTableAddConstraint(Oid myrelid, 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. */ diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile index 0a66e1be03e..b875259bc1a 100644 --- a/src/backend/executor/Makefile +++ b/src/backend/executor/Makefile @@ -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 \ diff --git a/src/backend/executor/execFlatten.c b/src/backend/executor/execFlatten.c deleted file mode 100644 index ecbe7b5d189..00000000000 --- a/src/backend/executor/execFlatten.c +++ /dev/null @@ -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 diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index bebb664f299..cc9bebd1fed 100644 --- a/src/backend/executor/execQual.c +++ b/src/backend/executor/execQual.c @@ -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; diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index cfcf00a4d47..3801d409cf1 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -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; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 185a16beb5a..cf203243f11 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -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 */ @@ -539,12 +544,6 @@ _equalJoinInfo(JoinInfo *a, JoinInfo *b) } static bool -_equalIter(Iter *a, Iter *b) -{ - return equal(a->iterexpr, b->iterexpr); -} - -static bool _equalStream(Stream *a, Stream *b) { if (a->clausetype != b->clausetype) @@ -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; diff --git a/src/backend/nodes/makefuncs.c b/src/backend/nodes/makefuncs.c index 65f4ffcca37..54d9e061908 100644 --- a/src/backend/nodes/makefuncs.c +++ b/src/backend/nodes/makefuncs.c @@ -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; } diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 698beb5d999..8ee69d1a702 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -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)); } /* @@ -1247,13 +1250,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) { appendStringInfo(str, @@ -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; diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index 27604dcb4be..98aab14b911 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -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) diff --git a/src/backend/optimizer/README b/src/backend/optimizer/README index 472efbcd9cc..14e39909220 100644 --- a/src/backend/optimizer/README +++ b/src/backend/optimizer/README @@ -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 diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index 1e9074186c2..27b6d110bc9 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -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; diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c index 21599b08bd7..6ec1d5af8fa 100644 --- a/src/backend/optimizer/path/indxpath.c +++ b/src/backend/optimizer/path/indxpath.c @@ -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)); diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c index 60e05ca340f..a35e86375a2 100644 --- a/src/backend/optimizer/path/pathkeys.c +++ b/src/backend/optimizer/path/pathkeys.c @@ -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) diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c index 2410651d683..5d35d1b230a 100644 --- a/src/backend/optimizer/plan/initsplan.c +++ b/src/backend/optimizer/plan/initsplan.c @@ -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); diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 5eb17f3b05c..500297f2155 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -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; /* diff --git a/src/backend/optimizer/prep/prepqual.c b/src/backend/optimizer/prep/prepqual.c index a7773040d79..069563ec18e 100644 --- a/src/backend/optimizer/prep/prepqual.c +++ b/src/backend/optimizer/prep/prepqual.c @@ -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)); } diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index 1a278e9ca2e..972c7f94633 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -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; diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index 19aa688ff94..a41182e9398 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -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; } diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c index 57aad4dbf58..716cbcbb756 100644 --- a/src/backend/parser/parse_coerce.c +++ b/src/backend/parser/parse_coerce.c @@ -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); diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 100bf9a7b49..e29d5ca4b5c 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -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; - b->arg = transformExpr(pstate, b->arg); - - if (!coerce_to_boolean(pstate, &b->arg)) + switch (b->booltesttype) { - const char *clausename; + 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 */ + } - 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); + + b->arg = coerce_to_boolean(b->arg, clausename); - 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; diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c index 339577f39ca..1912ab4e072 100644 --- a/src/backend/parser/parse_func.c +++ b/src/backend/parser/parse_func.c @@ -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; diff --git a/src/backend/parser/parse_node.c b/src/backend/parser/parse_node.c index 2c4467035a5..e4b0766c63f 100644 --- a/src/backend/parser/parse_node.c +++ b/src/backend/parser/parse_node.c @@ -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; diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index b4bd3b2f514..78cb6306e02 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -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; diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c index ca3bd80e8a7..e695f533b02 100644 --- a/src/backend/utils/cache/lsyscache.c +++ b/src/backend/utils/cache/lsyscache.c @@ -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. @@ -613,6 +613,27 @@ get_func_rettype(Oid funcid) } /* + * 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. */ diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 3120fb5fae0..ca61cb51a76 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -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 diff --git a/src/include/executor/execFlatten.h b/src/include/executor/execFlatten.h deleted file mode 100644 index 1795ed1c487..00000000000 --- a/src/include/executor/execFlatten.h +++ /dev/null @@ -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 */ diff --git a/src/include/nodes/makefuncs.h b/src/include/nodes/makefuncs.h index 4096c2d9fb3..e3b6a164377 100644 --- a/src/include/nodes/makefuncs.h +++ b/src/include/nodes/makefuncs.h @@ -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, diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 492619e194d..7de647d1362 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -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, diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h index 6eed89a7042..342ac11bb5b 100644 --- a/src/include/nodes/primnodes.h +++ b/src/include/nodes/primnodes.h @@ -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,13 +151,52 @@ 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 * * Note: during parsing/planning, varnoold/varoattno are always just copies @@ -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 */ diff --git a/src/include/optimizer/clauses.h b/src/include/optimizer/clauses.h index 69596c1779a..376e8cc77aa 100644 --- a/src/include/optimizer/clauses.h +++ b/src/include/optimizer/clauses.h @@ -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); diff --git a/src/include/parser/parse_coerce.h b/src/include/parser/parse_coerce.h index 47ce9b61c16..a7eb56a5092 100644 --- a/src/include/parser/parse_coerce.h +++ b/src/include/parser/parse_coerce.h @@ -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, diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h index 9f812256737..eaff98448d3 100644 --- a/src/include/utils/lsyscache.h +++ b/src/include/utils/lsyscache.h @@ -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); |