From 31cce21fb089b231bc408bc6e4541280c533b43f Mon Sep 17 00:00:00 2001 From: Bruce Momjian Date: Thu, 18 Feb 1999 00:49:48 +0000 Subject: Fix bushy plans. Cleanup. --- src/backend/nodes/copyfuncs.c | 6 +- src/backend/nodes/equalfuncs.c | 4 +- src/backend/nodes/freefuncs.c | 5 +- src/backend/nodes/outfuncs.c | 8 +- src/backend/nodes/readfuncs.c | 6 +- src/backend/optimizer/path/_deadcode/xfunc.c | 1484 +++++++++++++++++++++++++ src/backend/optimizer/path/allpaths.c | 29 +- src/backend/optimizer/path/indxpath.c | 4 +- src/backend/optimizer/path/joinpath.c | 8 +- src/backend/optimizer/path/joinrels.c | 276 ++--- src/backend/optimizer/path/prune.c | 56 +- src/backend/optimizer/path/xfunc.c | 1485 -------------------------- src/backend/optimizer/plan/initsplan.c | 28 +- src/backend/optimizer/prep/prepunion.c | 10 +- src/backend/optimizer/util/clauses.c | 4 +- src/backend/optimizer/util/joininfo.c | 19 +- src/backend/optimizer/util/pathnode.c | 4 +- src/backend/optimizer/util/relnode.c | 17 +- src/include/nodes/relation.h | 18 +- src/include/optimizer/clauses.h | 5 +- src/include/optimizer/pathnode.h | 6 +- src/include/optimizer/paths.h | 5 +- src/include/optimizer/xfunc.h | 6 +- 23 files changed, 1655 insertions(+), 1838 deletions(-) create mode 100644 src/backend/optimizer/path/_deadcode/xfunc.c delete mode 100644 src/backend/optimizer/path/xfunc.c (limited to 'src') diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 71b1c9269da..c0e072bc359 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.72 1999/02/15 05:21:01 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.73 1999/02/18 00:49:12 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -1066,7 +1066,6 @@ _copyRelOptInfo(RelOptInfo * from) Node_Copy(from, newnode, restrictinfo); Node_Copy(from, newnode, joininfo); Node_Copy(from, newnode, innerjoin); - Node_Copy(from, newnode, superrels); return newnode; } @@ -1428,12 +1427,11 @@ _copyJoinInfo(JoinInfo *from) * copy remainder of node * ---------------- */ - newnode->unjoined_rels = listCopy(from->unjoined_rels); + newnode->unjoined_relids = listCopy(from->unjoined_relids); Node_Copy(from, newnode, jinfo_restrictinfo); newnode->mergejoinable = from->mergejoinable; newnode->hashjoinable = from->hashjoinable; - newnode->bushy_inactive = from->bushy_inactive; return newnode; } diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index d2a4389734b..a5d40ef399d 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.34 1999/02/15 05:21:02 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.35 1999/02/18 00:49:14 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -526,7 +526,7 @@ _equalJoinInfo(JoinInfo *a, JoinInfo *b) { Assert(IsA(a, JoinInfo)); Assert(IsA(b, JoinInfo)); - if (!equal(a->unjoined_rels, b->unjoined_rels)) + if (!equal(a->unjoined_relids, b->unjoined_relids)) return false; if (!equal(a->jinfo_restrictinfo, b->jinfo_restrictinfo)) return false; diff --git a/src/backend/nodes/freefuncs.c b/src/backend/nodes/freefuncs.c index 05e221951e6..0c95880fcd5 100644 --- a/src/backend/nodes/freefuncs.c +++ b/src/backend/nodes/freefuncs.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/Attic/freefuncs.c,v 1.12 1999/02/15 05:21:02 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/Attic/freefuncs.c,v 1.13 1999/02/18 00:49:14 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -740,7 +740,6 @@ _freeRelOptInfo(RelOptInfo *node) freeObject(node->restrictinfo); freeObject(node->joininfo); freeObject(node->innerjoin); - freeObject(node->superrels); pfree(node); } @@ -1024,7 +1023,7 @@ _freeJoinInfo(JoinInfo *node) * free remainder of node * ---------------- */ - freeList(node->unjoined_rels); + freeList(node->unjoined_relids); freeObject(node->jinfo_restrictinfo); pfree(node); diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 8d168e88915..8f1560dac2b 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -5,7 +5,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: outfuncs.c,v 1.74 1999/02/15 05:21:02 momjian Exp $ + * $Id: outfuncs.c,v 1.75 1999/02/18 00:49:14 momjian Exp $ * * NOTES * Every (plan) node in POSTGRES has an associated "out" routine which @@ -61,7 +61,7 @@ _outIntList(StringInfo str, List *list) appendStringInfo(str, "("); foreach(l, list) { - appendStringInfo(str, " %d ", (int) lfirst(l)); + appendStringInfo(str, " %d ", lfirsti(l)); } appendStringInfo(str, ")"); } @@ -1198,8 +1198,8 @@ _outHashInfo(StringInfo str, HashInfo *node) static void _outJoinInfo(StringInfo str, JoinInfo *node) { - appendStringInfo(str, " JINFO :unjoined_rels "); - _outIntList(str, node->unjoined_rels); + appendStringInfo(str, " JINFO :unjoined_relids "); + _outIntList(str, node->unjoined_relids); appendStringInfo(str, " :jinfo_restrictinfo "); _outNode(str, node->jinfo_restrictinfo); diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index 6f252b70e37..f0fa81a4e2e 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.58 1999/02/15 05:21:03 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.59 1999/02/18 00:49:15 momjian Exp $ * * NOTES * Most of the read functions for plan nodes are tested. (In fact, they @@ -1982,8 +1982,8 @@ _readJoinInfo() local_node = makeNode(JoinInfo); - token = lsptok(NULL, &length); /* get :unjoined_rels */ - local_node->unjoined_rels = toIntList(nodeRead(true)); /* now read it */ + token = lsptok(NULL, &length); /* get :unjoined_relids */ + local_node->unjoined_relids = toIntList(nodeRead(true)); /* now read it */ token = lsptok(NULL, &length); /* get :jinfo_restrictinfo */ local_node->jinfo_restrictinfo = nodeRead(true); /* now read it */ diff --git a/src/backend/optimizer/path/_deadcode/xfunc.c b/src/backend/optimizer/path/_deadcode/xfunc.c new file mode 100644 index 00000000000..8aa75468982 --- /dev/null +++ b/src/backend/optimizer/path/_deadcode/xfunc.c @@ -0,0 +1,1484 @@ +/*------------------------------------------------------------------------- + * + * xfunc.c + * Utility routines to handle expensive function optimization. + * Includes xfunc_trypullup(), which attempts early pullup of predicates + * to allow for maximal pruning. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/_deadcode/Attic/xfunc.c,v 1.1 1999/02/18 00:49:24 momjian Exp $ + * + *------------------------------------------------------------------------- + */ +#include /* for MAXFLOAT on most systems */ + +#include /* for MAXFLOAT on SunOS */ +#include + +#include "postgres.h" + +#include "access/heapam.h" +#include "catalog/pg_language.h" +#include "catalog/pg_proc.h" +#include "catalog/pg_type.h" +#include "lib/lispsort.h" +#include "nodes/nodes.h" +#include "nodes/pg_list.h" +#include "nodes/primnodes.h" +#include "nodes/relation.h" +#include "optimizer/clauses.h" +#include "optimizer/cost.h" +#include "optimizer/internal.h" +#include "optimizer/keys.h" +#include "optimizer/pathnode.h" +#include "optimizer/tlist.h" /* for get_expr */ +#include "optimizer/xfunc.h" +#include "storage/buf_internals.h" /* for NBuffers */ +#include "tcop/dest.h" +#include "utils/syscache.h" + +#define ever ; 1 ; + +/* local funcs */ +static int xfunc_card_unreferenced(Query *queryInfo, + Expr *clause, Relids referenced); + +*/ + +/* +** xfunc_trypullup +** Preliminary pullup of predicates, to allow for maximal pruning. +** Given a relation, check each of its paths and see if you can +** pullup clauses from its inner and outer. +*/ + +void +xfunc_trypullup(RelOptInfo rel) +{ + LispValue y; /* list ptr */ + RestrictInfo maxcinfo; /* The RestrictInfo to pull up, as + * calculated by xfunc_shouldpull() */ + JoinPath curpath; /* current path in list */ + int progress; /* has progress been made this time + * through? */ + int clausetype; + + do + { + progress = false; /* no progress yet in this iteration */ + foreach(y, get_pathlist(rel)) + { + curpath = (JoinPath) lfirst(y); + + /* + * * for each operand, attempt to pullup predicates until + * first * failure. + */ + for (ever) + { + /* No, the following should NOT be '==' !! */ + if (clausetype = xfunc_shouldpull((Path) get_innerjoinpath(curpath), + curpath, INNER, &maxcinfo)) + { + + xfunc_pullup((Path) get_innerjoinpath(curpath), + curpath, maxcinfo, INNER, clausetype); + progress = true; + } + else + break; + } + for (ever) + { + + /* No, the following should NOT be '==' !! */ + if (clausetype = xfunc_shouldpull((Path) get_outerjoinpath(curpath), + curpath, OUTER, &maxcinfo)) + { + + xfunc_pullup((Path) get_outerjoinpath(curpath), + curpath, maxcinfo, OUTER, clausetype); + progress = true; + } + else + break; + } + + /* + * * make sure the unpruneable flag bubbles up, i.e. * if + * anywhere below us in the path pruneable is false, * then + * pruneable should be false here + */ + if (get_pruneable(get_parent(curpath)) && + (!get_pruneable(get_parent + ((Path) get_innerjoinpath(curpath))) || + !get_pruneable(get_parent((Path) + get_outerjoinpath(curpath))))) + { + + set_pruneable(get_parent(curpath), false); + progress = true; + } + } + } while (progress); +} + +/* + ** xfunc_shouldpull + ** find clause with highest rank, and decide whether to pull it up + ** from child to parent. Currently we only pullup secondary join clauses + ** that are in the pathrestrictinfo. Secondary hash and sort clauses are + ** left where they are. + ** If we find an expensive function but decide *not* to pull it up, + ** we'd better set the unpruneable flag. -- JMH, 11/11/92 + ** + ** Returns: 0 if nothing left to pullup + ** XFUNC_LOCPRD if a local predicate is to be pulled up + ** XFUNC_JOINPRD if a secondary join predicate is to be pulled up + */ +int +xfunc_shouldpull(Query *queryInfo, + Path childpath, + JoinPath parentpath, + int whichchild, + RestrictInfo *maxcinfopt) /* Out: pointer to clause + * to pullup */ +{ + LispValue clauselist, + tmplist; /* lists of clauses */ + RestrictInfo maxcinfo; /* clause to pullup */ + LispValue primjoinclause /* primary join clause */ + = xfunc_primary_join(parentpath); + Cost tmprank, + maxrank = (-1 * MAXFLOAT); /* ranks of clauses */ + Cost joinselec = 0; /* selectivity of the join predicate */ + Cost joincost = 0; /* join cost + primjoinclause cost */ + int retval = XFUNC_LOCPRD; + + clauselist = get_loc_restrictinfo(childpath); + + if (clauselist != LispNil) + { + /* find local predicate with maximum rank */ + for (tmplist = clauselist, + maxcinfo = (RestrictInfo) lfirst(tmplist), + maxrank = xfunc_rank(get_clause(maxcinfo)); + tmplist != LispNil; + tmplist = lnext(tmplist)) + { + + if ((tmprank = xfunc_rank(get_clause((RestrictInfo) lfirst(tmplist)))) + > maxrank) + { + maxcinfo = (RestrictInfo) lfirst(tmplist); + maxrank = tmprank; + } + } + } + + /* + * * If child is a join path, and there are multiple join clauses, * + * see if any join clause has even higher rank than the highest * + * local predicate + */ + if (is_join(childpath) && xfunc_num_join_clauses((JoinPath) childpath) > 1) + for (tmplist = get_pathrestrictinfo((JoinPath) childpath); + tmplist != LispNil; + tmplist = lnext(tmplist)) + { + + if (tmplist != LispNil && + (tmprank = xfunc_rank(get_clause((RestrictInfo) lfirst(tmplist)))) + > maxrank) + { + maxcinfo = (RestrictInfo) lfirst(tmplist); + maxrank = tmprank; + retval = XFUNC_JOINPRD; + } + } + if (maxrank == (-1 * MAXFLOAT)) /* no expensive clauses */ + return 0; + + /* + * * Pullup over join if clause is higher rank than join, or if * join + * is nested loop and current path is inner child (note that * + * restrictions on the inner of a nested loop don't buy you anything + * -- * you still have to scan the entire inner relation each time). * + * Note that the cost of a secondary join clause is only what's * + * calculated by xfunc_expense(), since the actual joining * (i.e. the + * usual path_cost) is paid for by the primary join clause. + */ + if (primjoinclause != LispNil) + { + joinselec = compute_clause_selec(queryInfo, primjoinclause, LispNil); + joincost = xfunc_join_expense(parentpath, whichchild); + + if (XfuncMode == XFUNC_PULLALL || + (XfuncMode != XFUNC_WAIT && + ((joincost != 0 && + (maxrank = xfunc_rank(get_clause(maxcinfo))) > + ((joinselec - 1.0) / joincost)) + || (joincost == 0 && joinselec < 1) + || (!is_join(childpath) + && (whichchild == INNER) + && IsA(parentpath, NestPath) + &&!IsA(parentpath, HashPath) + &&!IsA(parentpath, MergePath))))) + { + + *maxcinfopt = maxcinfo; + return retval; + + } + else if (maxrank != -(MAXFLOAT)) + { + + /* + * * we've left an expensive restriction below a join. Since * + * we may pullup this restriction in predmig.c, we'd best * + * set the RelOptInfo of this join to be unpruneable + */ + set_pruneable(get_parent(parentpath), false); + /* and fall through */ + } + } + return 0; +} + + +/* + ** xfunc_pullup + ** move clause from child pathnode to parent pathnode. This operation + ** makes the child pathnode produce a larger relation than it used to. + ** This means that we must construct a new RelOptInfo just for the childpath, + ** although this RelOptInfo will not be added to the list of Rels to be joined up + ** in the query; it's merely a parent for the new childpath. + ** We also have to fix up the path costs of the child and parent. + ** + ** Now returns a pointer to the new pulled-up RestrictInfo. -- JMH, 11/18/92 + */ +RestrictInfo +xfunc_pullup(Query *queryInfo, + Path childpath, + JoinPath parentpath, + RestrictInfo cinfo, /* clause to pull up */ + int whichchild, /* whether child is INNER or OUTER of join */ + int clausetype) /* whether clause to pull is join or local */ +{ + Path newkid; + RelOptInfo newrel; + Cost pulled_selec; + Cost cost; + RestrictInfo newinfo; + + /* remove clause from childpath */ + newkid = (Path) copyObject((Node) childpath); + if (clausetype == XFUNC_LOCPRD) + { + set_locrestrictinfo(newkid, + xfunc_LispRemove((LispValue) cinfo, + (List) get_loc_restrictinfo(newkid))); + } + else + { + set_pathrestrictinfo + ((JoinPath) newkid, + xfunc_LispRemove((LispValue) cinfo, + (List) get_pathrestrictinfo((JoinPath) newkid))); + } + + /* + * * give the new child path its own RelOptInfo node that reflects the * + * lack of the pulled-up predicate + */ + pulled_selec = compute_clause_selec(queryInfo, + get_clause(cinfo), LispNil); + xfunc_copyrel(get_parent(newkid), &newrel); + set_parent(newkid, newrel); + set_pathlist(newrel, lcons(newkid, NIL)); + set_unorderedpath(newrel, (PathPtr) newkid); + set_cheapestpath(newrel, (PathPtr) newkid); + set_size(newrel, + (Count) ((Cost) get_size(get_parent(childpath)) / pulled_selec)); + + /* + * * fix up path cost of newkid. To do this we subtract away all the * + * xfunc_costs of childpath, then recompute the xfunc_costs of newkid + */ + cost = get_path_cost(newkid) - xfunc_get_path_cost(childpath); + Assert(cost >= 0); + set_path_cost(newkid, cost); + cost = get_path_cost(newkid) + xfunc_get_path_cost(newkid); + set_path_cost(newkid, cost); + + /* + * * We copy the cinfo, since it may appear in other plans, and we're + * going * to munge it. -- JMH, 7/22/92 + */ + newinfo = (RestrictInfo) copyObject((Node) cinfo); + + /* + * * Fix all vars in the clause * to point to the right varno and + * varattno in parentpath + */ + xfunc_fixvars(get_clause(newinfo), newrel, whichchild); + + /* add clause to parentpath, and fix up its cost. */ + set_locrestrictinfo(parentpath, + lispCons((LispValue) newinfo, + (LispValue) get_loc_restrictinfo(parentpath))); + /* put new childpath into the path tree */ + if (whichchild == INNER) + set_innerjoinpath(parentpath, (pathPtr) newkid); + else + set_outerjoinpath(parentpath, (pathPtr) newkid); + + /* + * * recompute parentpath cost from scratch -- the cost * of the join + * method has changed + */ + cost = xfunc_total_path_cost(parentpath); + set_path_cost(parentpath, cost); + + return newinfo; +} + +/* + ** calculate (selectivity-1)/cost. + */ +Cost +xfunc_rank(Query *queryInfo, LispValue clause) +{ + Cost selec = compute_clause_selec(queryInfo, clause, LispNil); + Cost cost = xfunc_expense(queryInfo, clause); + + if (cost == 0) + if (selec > 1) + return MAXFLOAT; + else + return -(MAXFLOAT); + return (selec - 1) / cost; +} + +/* + ** Find the "global" expense of a clause; i.e. the local expense divided + ** by the cardinalities of all the base relations of the query that are *not* + ** referenced in the clause. + */ +Cost +xfunc_expense(Query *queryInfo, clause) +LispValue clause; +{ + Cost cost = xfunc_local_expense(clause); + + if (cost) + { + Count card = xfunc_card_unreferenced(queryInfo, clause, LispNil); + + if (card) + cost /= card; + } + + return cost; +} + +/* + ** xfunc_join_expense + ** Find global expense of a join clause + */ +Cost +xfunc_join_expense(Query *queryInfo, JoinPath path, int whichchild) +{ + LispValue primjoinclause = xfunc_primary_join(path); + + /* + * * the second argument to xfunc_card_unreferenced reflects all the * + * relations involved in the join clause, i.e. all the relids in the + * RelOptInfo * of the join clause + */ + Count card = 0; + Cost cost = xfunc_expense_per_tuple(path, whichchild); + + card = xfunc_card_unreferenced(queryInfo, + primjoinclause, + get_relids(get_parent(path))); + if (primjoinclause) + cost += xfunc_local_expense(primjoinclause); + + if (card) + cost /= card; + + return cost; +} + +/* + ** Recursively find the per-tuple expense of a clause. See + ** xfunc_func_expense for more discussion. + */ +Cost +xfunc_local_expense(LispValue clause) +{ + Cost cost = 0; /* running expense */ + LispValue tmpclause; + + /* First handle the base case */ + if (IsA(clause, Const) ||IsA(clause, Var) ||IsA(clause, Param)) + return 0; + /* now other stuff */ + else if (IsA(clause, Iter)) + /* Too low. Should multiply by the expected number of iterations. */ + return xfunc_local_expense(get_iterexpr((Iter) clause)); + else if (IsA(clause, ArrayRef)) + return xfunc_local_expense(get_refexpr((ArrayRef) clause)); + else if (fast_is_clause(clause)) + return (xfunc_func_expense((LispValue) get_op(clause), + (LispValue) get_opargs(clause))); + else if (fast_is_funcclause(clause)) + return (xfunc_func_expense((LispValue) get_function(clause), + (LispValue) get_funcargs(clause))); + else if (fast_not_clause(clause)) + return xfunc_local_expense(lsecond(clause)); + else if (fast_or_clause(clause) || fast_and_clause(clause)) + { + /* find cost of evaluating each disjunct */ + for (tmpclause = lnext(clause); tmpclause != LispNil; + tmpclause = lnext(tmpclause)) + cost += xfunc_local_expense(lfirst(tmpclause)); + return cost; + } + else + { + elog(ERROR, "Clause node of undetermined type"); + return -1; + } +} + +/* + ** xfunc_func_expense + ** given a Func or Oper and its args, find its expense. + ** Note: in Stonebraker's SIGMOD '91 paper, he uses a more complicated metric + ** than the one here. We can ignore the expected number of tuples for + ** our calculations; we just need the per-tuple expense. But he also + ** proposes components to take into account the costs of accessing disk and + ** archive. We didn't adopt that scheme here; eventually the vacuum + ** cleaner should be able to tell us what percentage of bytes to find on + ** which storage level, and that should be multiplied in appropriately + ** in the cost function below. Right now we don't model the cost of + ** accessing secondary or tertiary storage, since we don't have sufficient + ** stats to do it right. + */ +Cost +xfunc_func_expense(LispValue node, LispValue args) +{ + HeapTuple tupl; /* the pg_proc tuple for each function */ + Form_pg_proc proc; /* a data structure to hold the pg_proc + * tuple */ + int width = 0; /* byte width of the field referenced by + * each clause */ + RegProcedure funcid; /* ID of function associate with node */ + Cost cost = 0; /* running expense */ + LispValue tmpclause; + LispValue operand; /* one operand of an operator */ + + if (IsA(node, Oper)) + { + /* don't trust the opid in the Oper node. Use the opno. */ + if (!(funcid = get_opcode(get_opno((Oper) node)))) + elog(ERROR, "Oper's function is undefined"); + } + else + funcid = get_funcid((Func) node); + + /* look up tuple in cache */ + tupl = SearchSysCacheTuple(PROOID, + ObjectIdGetDatum(funcid), + 0, 0, 0); + if (!HeapTupleIsValid(tupl)) + elog(ERROR, "Cache lookup failed for procedure %d", funcid); + proc = (Form_pg_proc) GETSTRUCT(tupl); + + /* + * * if it's a Postquel function, its cost is stored in the * + * associated plan. + */ + if (proc->prolang == SQLlanguageId) + { + LispValue tmpplan; + List planlist; + + if (IsA(node, Oper) ||get_func_planlist((Func) node) == LispNil) + { + Oid *argOidVect; /* vector of argtypes */ + char *pq_src; /* text of PQ function */ + int nargs; /* num args to PQ function */ + QueryTreeList *queryTree_list; /* dummy variable */ + + /* + * * plan the function, storing it in the Func node for later * + * use by the executor. + */ + pq_src = (char *) textout(&(proc->prosrc)); + nargs = proc->pronargs; + if (nargs > 0) + argOidVect = proc->proargtypes; + planlist = (List) pg_parse_and_plan(pq_src, argOidVect, nargs, + &parseTree_list, None, FALSE); + if (IsA(node, Func)) + set_func_planlist((Func) node, planlist); + + } + else + { /* plan has been cached inside the Func + * node already */ + planlist = get_func_planlist((Func) node); + } + + /* + * * Return the sum of the costs of the plans (the PQ function * + * may have many queries in its body). + */ + foreach(tmpplan, planlist) + cost += get_cost((Plan) lfirst(tmpplan)); + return cost; + } + else + { /* it's a C function */ + + /* + * * find the cost of evaluating the function's arguments * and + * the width of the operands + */ + for (tmpclause = args; tmpclause != LispNil; + tmpclause = lnext(tmpclause)) + { + + if ((operand = lfirst(tmpclause)) != LispNil) + { + cost += xfunc_local_expense(operand); + width += xfunc_width(operand); + } + } + + /* + * * when stats become available, add in cost of accessing + * secondary * and tertiary storage here. + */ + return (cost + + (Cost) proc->propercall_cpu + + (Cost) proc->properbyte_cpu * (Cost) proc->probyte_pct / 100.00 * + (Cost) width + + /* + * Pct_of_obj_in_mem DISK_COST * proc->probyte_pct/100.00 * width + * Pct_of_obj_on_disk + ARCH_COST * proc->probyte_pct/100.00 * + * width Pct_of_obj_on_arch + */ + ); + } +} + +/* + ** xfunc_width + ** recursively find the width of a expression + */ + +int +xfunc_width(LispValue clause) +{ + Relation rd; /* Relation Descriptor */ + HeapTuple tupl; /* structure to hold a cached tuple */ + Form_pg_type type; /* structure to hold a type tuple */ + int retval = 0; + + if (IsA(clause, Const)) + { + /* base case: width is the width of this constant */ + retval = get_constlen((Const) clause); + goto exit; + } + else if (IsA(clause, ArrayRef)) + { + /* base case: width is width of the refelem within the array */ + retval = get_refelemlength((ArrayRef) clause); + goto exit; + } + else if (IsA(clause, Var)) + { + /* base case: width is width of this attribute */ + tupl = SearchSysCacheTuple(TYPOID, + ObjectIdGetDatum(get_vartype((Var) clause)), + 0, 0, 0); + if (!HeapTupleIsValid(tupl)) + elog(ERROR, "Cache lookup failed for type %d", + get_vartype((Var) clause)); + type = (Form_pg_type) GETSTRUCT(tupl); + if (get_varattno((Var) clause) == 0) + { + /* clause is a tuple. Get its width */ + rd = heap_open(type->typrelid); + retval = xfunc_tuple_width(rd); + heap_close(rd); + } + else + { + /* attribute is a base type */ + retval = type->typlen; + } + goto exit; + } + else if (IsA(clause, Param)) + { + if (typeidTypeRelids(get_paramtype((Param) clause))) + { + /* Param node returns a tuple. Find its width */ + rd = heap_open(typeidTypeRelids(get_paramtype((Param) clause))); + retval = xfunc_tuple_width(rd); + heap_close(rd); + } + else if (get_param_tlist((Param) clause) != LispNil) + { + /* Param node projects a complex type */ + Assert(length(get_param_tlist((Param) clause)) == 1); /* sanity */ + retval = xfunc_width((LispValue) + get_expr(lfirst(get_param_tlist((Param) clause)))); + } + else + { + /* Param node returns a base type */ + retval = typeLen(typeidType(get_paramtype((Param) clause))); + } + goto exit; + } + else if (IsA(clause, Iter)) + { + + /* + * * An Iter returns a setof things, so return the width of a + * single * thing. * Note: THIS MAY NOT WORK RIGHT WHEN AGGS GET + * FIXED, * SINCE AGG FUNCTIONS CHEW ON THE WHOLE SETOF THINGS!!!! * + * This whole Iter business is bogus, anyway. + */ + retval = xfunc_width(get_iterexpr((Iter) clause)); + goto exit; + } + else if (fast_is_clause(clause)) + { + + /* + * * get function associated with this Oper, and treat this as * a + * Func + */ + tupl = SearchSysCacheTuple(OPROID, + ObjectIdGetDatum(get_opno((Oper) get_op(clause))), + 0, 0, 0); + if (!HeapTupleIsValid(tupl)) + elog(ERROR, "Cache lookup failed for procedure %d", + get_opno((Oper) get_op(clause))); + return (xfunc_func_width + ((RegProcedure) (((Form_pg_operator) (GETSTRUCT(tupl)))->oprcode), + (LispValue) get_opargs(clause))); + } + else if (fast_is_funcclause(clause)) + { + Func func = (Func) get_function(clause); + + if (get_func_tlist(func) != LispNil) + { + + /* + * this function has a projection on it. Get the length of + * the projected attribute + */ + Assert(length(get_func_tlist(func)) == 1); /* sanity */ + retval = xfunc_width((LispValue) + get_expr(lfirst(get_func_tlist(func)))); + goto exit; + } + else + { + return (xfunc_func_width((RegProcedure) get_funcid(func), + (LispValue) get_funcargs(clause))); + } + } + else + { + elog(ERROR, "Clause node of undetermined type"); + return -1; + } + +exit: + if (retval == -1) + retval = VARLEN_DEFAULT; + return retval; +} + +/* + ** xfunc_card_unreferenced: + ** find all relations not referenced in clause, and multiply their + ** cardinalities. Ignore relation of cardinality 0. + ** User may pass in referenced list, if they know it (useful + ** for joins). + */ +static Count +xfunc_card_unreferenced(Query *queryInfo, + LispValue clause, Relids referenced) +{ + Relids unreferenced, + allrelids = LispNil; + LispValue temp; + + /* find all relids of base relations referenced in query */ + foreach(temp, queryInfo->base_rel_list) + { + Assert(lnext(get_relids((RelOptInfo) lfirst(temp))) == LispNil); + allrelids = lappend(allrelids, + lfirst(get_relids((RelOptInfo) lfirst(temp)))); + } + + /* find all relids referenced in query but not in clause */ + if (!referenced) + referenced = xfunc_find_references(clause); + unreferenced = set_difference(allrelids, referenced); + + return xfunc_card_product(unreferenced); +} + +/* + ** xfunc_card_product + ** multiple together cardinalities of a list relations. + */ +Count +xfunc_card_product(Query *queryInfo, Relids relids) +{ + LispValue cinfonode; + LispValue temp; + RelOptInfo currel; + Cost tuples; + Count retval = 0; + + foreach(temp, relids) + { + currel = get_rel(lfirst(temp)); + tuples = get_tuples(currel); + + if (tuples) + { /* not of cardinality 0 */ + /* factor in the selectivity of all zero-cost clauses */ + foreach(cinfonode, get_restrictinfo(currel)) + { + if (!xfunc_expense(queryInfo, get_clause((RestrictInfo) lfirst(cinfonode)))) + tuples *= compute_clause_selec(queryInfo, + get_clause((RestrictInfo) lfirst(cinfonode)), + LispNil); + } + + if (retval == 0) + retval = tuples; + else + retval *= tuples; + } + } + if (retval == 0) + retval = 1; /* saves caller from dividing by zero */ + return retval; +} + + +/* + ** xfunc_find_references: + ** Traverse a clause and find all relids referenced in the clause. + */ +List +xfunc_find_references(LispValue clause) +{ + List retval = (List) LispNil; + LispValue tmpclause; + + /* Base cases */ + if (IsA(clause, Var)) + return lispCons(lfirst(get_varid((Var) clause)), LispNil); + else if (IsA(clause, Const) ||IsA(clause, Param)) + return (List) LispNil; + + /* recursion */ + else if (IsA(clause, Iter)) + + /* + * Too low. Should multiply by the expected number of iterations. + * maybe + */ + return xfunc_find_references(get_iterexpr((Iter) clause)); + else if (IsA(clause, ArrayRef)) + return xfunc_find_references(get_refexpr((ArrayRef) clause)); + else if (fast_is_clause(clause)) + { + /* string together result of all operands of Oper */ + for (tmpclause = (LispValue) get_opargs(clause); tmpclause != LispNil; + tmpclause = lnext(tmpclause)) + retval = nconc(retval, xfunc_find_references(lfirst(tmpclause))); + return retval; + } + else if (fast_is_funcclause(clause)) + { + /* string together result of all args of Func */ + for (tmpclause = (LispValue) get_funcargs(clause); + tmpclause != LispNil; + tmpclause = lnext(tmpclause)) + retval = nconc(retval, xfunc_find_references(lfirst(tmpclause))); + return retval; + } + else if (fast_not_clause(clause)) + return xfunc_find_references(lsecond(clause)); + else if (fast_or_clause(clause) || fast_and_clause(clause)) + { + /* string together result of all operands of OR */ + for (tmpclause = lnext(clause); tmpclause != LispNil; + tmpclause = lnext(tmpclause)) + retval = nconc(retval, xfunc_find_references(lfirst(tmpclause))); + return retval; + } + else + { + elog(ERROR, "Clause node of undetermined type"); + return (List) LispNil; + } +} + +/* + ** xfunc_primary_join: + ** Find the primary join clause: for Hash and Merge Joins, this is the + ** min rank Hash or Merge clause, while for Nested Loop it's the + ** min rank pathclause + */ +LispValue +xfunc_primary_join(JoinPath pathnode) +{ + LispValue joinclauselist = get_pathrestrictinfo(pathnode); + RestrictInfo mincinfo; + LispValue tmplist; + LispValue minclause = LispNil; + Cost minrank, + tmprank; + + if (IsA(pathnode, MergePath)) + { + for (tmplist = get_path_mergeclauses((MergePath) pathnode), + minclause = lfirst(tmplist), + minrank = xfunc_rank(minclause); + tmplist != LispNil; + tmplist = lnext(tmplist)) + if ((tmprank = xfunc_rank(lfirst(tmplist))) + < minrank) + { + minrank = tmprank; + minclause = lfirst(tmplist); + } + return minclause; + } + else if (IsA(pathnode, HashPath)) + { + for (tmplist = get_path_hashclauses((HashPath) pathnode), + minclause = lfirst(tmplist), + minrank = xfunc_rank(minclause); + tmplist != LispNil; + tmplist = lnext(tmplist)) + if ((tmprank = xfunc_rank(lfirst(tmplist))) + < minrank) + { + minrank = tmprank; + minclause = lfirst(tmplist); + } + return minclause; + } + + /* if we drop through, it's nested loop join */ + if (joinclauselist == LispNil) + return LispNil; + + for (tmplist = joinclauselist, mincinfo = (RestrictInfo) lfirst(joinclauselist), + minrank = xfunc_rank(get_clause((RestrictInfo) lfirst(tmplist))); + tmplist != LispNil; + tmplist = lnext(tmplist)) + if ((tmprank = xfunc_rank(get_clause((RestrictInfo) lfirst(tmplist)))) + < minrank) + { + minrank = tmprank; + mincinfo = (RestrictInfo) lfirst(tmplist); + } + return (LispValue) get_clause(mincinfo); +} + +/* + ** xfunc_get_path_cost + ** get the expensive function costs of the path + */ +Cost +xfunc_get_path_cost(Query *queryInfo, Path pathnode) +{ + Cost cost = 0; + LispValue tmplist; + Cost selec = 1.0; + + /* + * * first add in the expensive local function costs. * We ensure that + * the clauses are sorted by rank, so that we * know (via + * selectivities) the number of tuples that will be checked * by each + * function. If we're not doing any optimization of expensive * + * functions, we don't sort. + */ + if (XfuncMode != XFUNC_OFF) + set_locrestrictinfo(pathnode, lisp_qsort(get_loc_restrictinfo(pathnode), + xfunc_cinfo_compare)); + for (tmplist = get_loc_restrictinfo(pathnode), selec = 1.0; + tmplist != LispNil; + tmplist = lnext(tmplist)) + { + cost += (Cost) (xfunc_local_expense(get_clause((RestrictInfo) lfirst(tmplist))) + * (Cost) get_tuples(get_parent(pathnode)) * selec); + selec *= compute_clause_selec(queryInfo, + get_clause((RestrictInfo) lfirst(tmplist)), + LispNil); + } + + /* + * * Now add in any node-specific expensive function costs. * Again, + * we must ensure that the clauses are sorted by rank. + */ + if (IsA(pathnode, JoinPath)) + { + if (XfuncMode != XFUNC_OFF) + set_pathrestrictinfo((JoinPath) pathnode, lisp_qsort + (get_pathrestrictinfo((JoinPath) pathnode), + xfunc_cinfo_compare)); + for (tmplist = get_pathrestrictinfo((JoinPath) pathnode), selec = 1.0; + tmplist != LispNil; + tmplist = lnext(tmplist)) + { + cost += (Cost) (xfunc_local_expense(get_clause((RestrictInfo) lfirst(tmplist))) + * (Cost) get_tuples(get_parent(pathnode)) * selec); + selec *= compute_clause_selec(queryInfo, + get_clause((RestrictInfo) lfirst(tmplist)), + LispNil); + } + } + if (IsA(pathnode, HashPath)) + { + if (XfuncMode != XFUNC_OFF) + set_path_hashclauses + ((HashPath) pathnode, + lisp_qsort(get_path_hashclauses((HashPath) pathnode), + xfunc_clause_compare)); + for (tmplist = get_path_hashclauses((HashPath) pathnode), selec = 1.0; + tmplist != LispNil; + tmplist = lnext(tmplist)) + { + cost += (Cost) (xfunc_local_expense(lfirst(tmplist)) + * (Cost) get_tuples(get_parent(pathnode)) * selec); + selec *= compute_clause_selec(queryInfo, + lfirst(tmplist), LispNil); + } + } + if (IsA(pathnode, MergePath)) + { + if (XfuncMode != XFUNC_OFF) + set_path_mergeclauses + ((MergePath) pathnode, + lisp_qsort(get_path_mergeclauses((MergePath) pathnode), + xfunc_clause_compare)); + for (tmplist = get_path_mergeclauses((MergePath) pathnode), selec = 1.0; + tmplist != LispNil; + tmplist = lnext(tmplist)) + { + cost += (Cost) (xfunc_local_expense(lfirst(tmplist)) + * (Cost) get_tuples(get_parent(pathnode)) * selec); + selec *= compute_clause_selec(queryInfo, + lfirst(tmplist), LispNil); + } + } + Assert(cost >= 0); + return cost; +} + +/* + ** Recalculate the cost of a path node. This includes the basic cost of the + ** node, as well as the cost of its expensive functions. + ** We need to do this to the parent after pulling a clause from a child into a + ** parent. Thus we should only be calling this function on JoinPaths. + */ +Cost +xfunc_total_path_cost(JoinPath pathnode) +{ + Cost cost = xfunc_get_path_cost((Path) pathnode); + + Assert(IsA(pathnode, JoinPath)); + if (IsA(pathnode, MergePath)) + { + MergePath mrgnode = (MergePath) pathnode; + + cost += cost_mergejoin(get_path_cost((Path) get_outerjoinpath(mrgnode)), + get_path_cost((Path) get_innerjoinpath(mrgnode)), + get_outersortkeys(mrgnode), + get_innersortkeys(mrgnode), + get_tuples(get_parent((Path) get_outerjoinpath + (mrgnode))), + get_tuples(get_parent((Path) get_innerjoinpath + (mrgnode))), + get_width(get_parent((Path) get_outerjoinpath + (mrgnode))), + get_width(get_parent((Path) get_innerjoinpath + (mrgnode)))); + Assert(cost >= 0); + return cost; + } + else if (IsA(pathnode, HashPath)) + { + HashPath hashnode = (HashPath) pathnode; + + cost += cost_hashjoin(get_path_cost((Path) get_outerjoinpath(hashnode)), + get_path_cost((Path) get_innerjoinpath(hashnode)), + get_outerhashkeys(hashnode), + get_innerhashkeys(hashnode), + get_tuples(get_parent((Path) get_outerjoinpath + (hashnode))), + get_tuples(get_parent((Path) get_innerjoinpath + (hashnode))), + get_width(get_parent((Path) get_outerjoinpath + (hashnode))), + get_width(get_parent((Path) get_innerjoinpath + (hashnode)))); + Assert(cost >= 0); + return cost; + } + else +/* Nested Loop Join */ + { + cost += cost_nestloop(get_path_cost((Path) get_outerjoinpath(pathnode)), + get_path_cost((Path) get_innerjoinpath(pathnode)), + get_tuples(get_parent((Path) get_outerjoinpath + (pathnode))), + get_tuples(get_parent((Path) get_innerjoinpath + (pathnode))), + get_pages(get_parent((Path) get_outerjoinpath + (pathnode))), + IsA(get_innerjoinpath(pathnode), IndexPath)); + Assert(cost >= 0); + return cost; + } +} + + +/* + ** xfunc_expense_per_tuple + ** return the expense of the join *per-tuple* of the input relation. + ** The cost model here is that a join costs + ** k*card(outer)*card(inner) + l*card(outer) + m*card(inner) + n + ** + ** We treat the l and m terms by considering them to be like restrictions + ** constrained to be right under the join. Thus the cost per inner and + ** cost per outer of the join is different, reflecting these virtual nodes. + ** + ** The cost per tuple of outer is k + l/referenced(inner). Cost per tuple + ** of inner is k + m/referenced(outer). + ** The constants k, l, m and n depend on the join method. Measures here are + ** based on the costs in costsize.c, with fudging for HashJoin and Sorts to + ** make it fit our model (the 'q' in HashJoin results in a + ** card(outer)/card(inner) term, and sorting results in a log term. + + */ +Cost +xfunc_expense_per_tuple(JoinPath joinnode, int whichchild) +{ + RelOptInfo outerrel = get_parent((Path) get_outerjoinpath(joinnode)); + RelOptInfo innerrel = get_parent((Path) get_innerjoinpath(joinnode)); + Count outerwidth = get_width(outerrel); + Count outers_per_page = ceil(BLCKSZ / (outerwidth + sizeof(HeapTupleData))); + + if (IsA(joinnode, HashPath)) + { + if (whichchild == INNER) + return (1 + _CPU_PAGE_WEIGHT_) * outers_per_page / NBuffers; + else + return (((1 + _CPU_PAGE_WEIGHT_) * outers_per_page / NBuffers) + + _CPU_PAGE_WEIGHT_ + / xfunc_card_product(get_relids(innerrel))); + } + else if (IsA(joinnode, MergePath)) + { + /* assumes sort exists, and costs one (I/O + CPU) per tuple */ + if (whichchild == INNER) + return ((2 * _CPU_PAGE_WEIGHT_ + 1) + / xfunc_card_product(get_relids(outerrel))); + else + return ((2 * _CPU_PAGE_WEIGHT_ + 1) + / xfunc_card_product(get_relids(innerrel))); + } + else +/* nestloop */ + { + Assert(IsA(joinnode, JoinPath)); + return _CPU_PAGE_WEIGHT_; + } +} + +/* + ** xfunc_fixvars + ** After pulling up a clause, we must walk its expression tree, fixing Var + ** nodes to point to the correct varno (either INNER or OUTER, depending + ** on which child the clause was pulled from), and the right varattno in the + ** target list of the child's former relation. If the target list of the + ** child RelOptInfo does not contain the attribute we need, we add it. + */ +void +xfunc_fixvars(LispValue clause, /* clause being pulled up */ + RelOptInfo rel, /* rel it's being pulled from */ + int varno) /* whether rel is INNER or OUTER of join */ +{ + LispValue tmpclause; /* temporary variable */ + TargetEntry *tle; /* tlist member corresponding to var */ + + + if (IsA(clause, Const) ||IsA(clause, Param)) + return; + else if (IsA(clause, Var)) + { + /* here's the meat */ + tle = tlistentry_member((Var) clause, get_targetlist(rel)); + if (tle == LispNil) + { + + /* + * * The attribute we need is not in the target list, * so we + * have to add it. * + * + */ + add_var_to_tlist(rel, (Var) clause); + tle = tlistentry_member((Var) clause, get_targetlist(rel)); + } + set_varno(((Var) clause), varno); + set_varattno(((Var) clause), get_resno(get_resdom(get_entry(tle)))); + } + else if (IsA(clause, Iter)) + xfunc_fixvars(get_iterexpr((Iter) clause), rel, varno); + else if (fast_is_clause(clause)) + { + xfunc_fixvars(lfirst(lnext(clause)), rel, varno); + xfunc_fixvars(lfirst(lnext(lnext(clause))), rel, varno); + } + else if (fast_is_funcclause(clause)) + for (tmpclause = lnext(clause); tmpclause != LispNil; + tmpclause = lnext(tmpclause)) + xfunc_fixvars(lfirst(tmpclause), rel, varno); + else if (fast_not_clause(clause)) + xfunc_fixvars(lsecond(clause), rel, varno); + else if (fast_or_clause(clause) || fast_and_clause(clause)) + for (tmpclause = lnext(clause); tmpclause != LispNil; + tmpclause = lnext(tmpclause)) + xfunc_fixvars(lfirst(tmpclause), rel, varno); + else + elog(ERROR, "Clause node of undetermined type"); +} + + +/* + ** Comparison function for lisp_qsort() on a list of RestrictInfo's. + ** arg1 and arg2 should really be of type (RestrictInfo *). + */ +int +xfunc_cinfo_compare(void *arg1, void *arg2) +{ + RestrictInfo info1 = *(RestrictInfo *) arg1; + RestrictInfo info2 = *(RestrictInfo *) arg2; + + LispValue clause1 = (LispValue) get_clause(info1), + clause2 = (LispValue) get_clause(info2); + + return xfunc_clause_compare((void *) &clause1, (void *) &clause2); +} + +/* + ** xfunc_clause_compare: comparison function for lisp_qsort() that compares two + ** clauses based on expense/(1 - selectivity) + ** arg1 and arg2 are really pointers to clauses. + */ +int +xfunc_clause_compare(void *arg1, void *arg2) +{ + LispValue clause1 = *(LispValue *) arg1; + LispValue clause2 = *(LispValue *) arg2; + Cost rank1, /* total xfunc rank of clause1 */ + rank2; /* total xfunc rank of clause2 */ + + rank1 = xfunc_rank(clause1); + rank2 = xfunc_rank(clause2); + + if (rank1 < rank2) + return -1; + else if (rank1 == rank2) + return 0; + else + return 1; +} + +/* + ** xfunc_disjunct_sort + ** given a list of clauses, for each clause sort the disjuncts by cost + ** (this assumes the predicates have been converted to Conjunctive NF) + ** Modifies the clause list! + */ +void +xfunc_disjunct_sort(LispValue clause_list) +{ + LispValue temp; + + foreach(temp, clause_list) + if (or_clause(lfirst(temp))) + lnext(lfirst(temp)) = lisp_qsort(lnext(lfirst(temp)), xfunc_disjunct_compare); +} + + +/* + ** xfunc_disjunct_compare: comparison function for qsort() that compares two + ** disjuncts based on cost/selec. + ** arg1 and arg2 are really pointers to disjuncts + */ +int +xfunc_disjunct_compare(Query *queryInfo, void *arg1, void *arg2) +{ + LispValue disjunct1 = *(LispValue *) arg1; + LispValue disjunct2 = *(LispValue *) arg2; + Cost cost1, /* total cost of disjunct1 */ + cost2, /* total cost of disjunct2 */ + selec1, + selec2; + Cost rank1, + rank2; + + cost1 = xfunc_expense(queryInfo, disjunct1); + cost2 = xfunc_expense(queryInfo, disjunct2); + selec1 = compute_clause_selec(queryInfo, + disjunct1, LispNil); + selec2 = compute_clause_selec(queryInfo, + disjunct2, LispNil); + + if (selec1 == 0) + rank1 = MAXFLOAT; + else if (cost1 == 0) + rank1 = 0; + else + rank1 = cost1 / selec1; + + if (selec2 == 0) + rank2 = MAXFLOAT; + else if (cost2 == 0) + rank2 = 0; + else + rank2 = cost2 / selec2; + + if (rank1 < rank2) + return -1; + else if (rank1 == rank2) + return 0; + else + return 1; +} + +/* ------------------------ UTILITY FUNCTIONS ------------------------------- */ +/* + ** xfunc_func_width + ** Given a function OID and operands, find the width of the return value. + */ +int +xfunc_func_width(RegProcedure funcid, LispValue args) +{ + Relation rd; /* Relation Descriptor */ + HeapTuple tupl; /* structure to hold a cached tuple */ + Form_pg_proc proc; /* structure to hold the pg_proc tuple */ + Form_pg_type type; /* structure to hold the pg_type tuple */ + LispValue tmpclause; + int retval; + + /* lookup function and find its return type */ + Assert(RegProcedureIsValid(funcid)); + tupl = SearchSysCacheTuple(PROOID, + ObjectIdGetDatum(funcid), + 0, 0, 0); + if (!HeapTupleIsValid(tupl)) + elog(ERROR, "Cache lookup failed for procedure %d", funcid); + proc = (Form_pg_proc) GETSTRUCT(tupl); + + /* if function returns a tuple, get the width of that */ + if (typeidTypeRelids(proc->prorettype)) + { + rd = heap_open(typeidTypeRelids(proc->prorettype)); + retval = xfunc_tuple_width(rd); + heap_close(rd); + goto exit; + } + else +/* function returns a base type */ + { + tupl = SearchSysCacheTuple(TYPOID, + ObjectIdGetDatum(proc->prorettype), + 0, 0, 0); + if (!HeapTupleIsValid(tupl)) + elog(ERROR, "Cache lookup failed for type %d", proc->prorettype); + type = (Form_pg_type) GETSTRUCT(tupl); + /* if the type length is known, return that */ + if (type->typlen != -1) + { + retval = type->typlen; + goto exit; + } + else +/* estimate the return size */ + { + /* find width of the function's arguments */ + for (tmpclause = args; tmpclause != LispNil; + tmpclause = lnext(tmpclause)) + retval += xfunc_width(lfirst(tmpclause)); + /* multiply by outin_ratio */ + retval = (int) (proc->prooutin_ratio / 100.0 * retval); + goto exit; + } + } +exit: + return retval; +} + +/* + ** xfunc_tuple_width + ** Return the sum of the lengths of all the attributes of a given relation + */ +int +xfunc_tuple_width(Relation rd) +{ + int i; + int retval = 0; + TupleDesc tdesc = RelationGetDescr(rd); + + for (i = 0; i < tdesc->natts; i++) + { + if (tdesc->attrs[i]->attlen != -1) + retval += tdesc->attrs[i]->attlen; + else + retval += VARLEN_DEFAULT; + } + + return retval; +} + +/* + ** xfunc_num_join_clauses + ** Find the number of join clauses associated with this join path + */ +int +xfunc_num_join_clauses(JoinPath path) +{ + int num = length(get_pathrestrictinfo(path)); + + if (IsA(path, MergePath)) + return num + length(get_path_mergeclauses((MergePath) path)); + else if (IsA(path, HashPath)) + return num + length(get_path_hashclauses((HashPath) path)); + else + return num; +} + +/* + ** xfunc_LispRemove + ** Just like LispRemove, but it whines if the item to be removed ain't there + */ +LispValue +xfunc_LispRemove(LispValue foo, List bar) +{ + LispValue temp = LispNil; + LispValue result = LispNil; + int sanity = false; + + for (temp = bar; !null(temp); temp = lnext(temp)) + if (!equal((Node) (foo), (Node) (lfirst(temp)))) + result = lappend(result, lfirst(temp)); + else + sanity = true; /* found a matching item to remove! */ + + if (!sanity) + elog(ERROR, "xfunc_LispRemove: didn't find a match!"); + + return result; +} + +#define Node_Copy(a, b, c, d) \ +do { \ + if (NodeCopy((Node)((a)->d), (Node*)&((b)->d), c) != true) \ + { \ + return false; \ + } \ +} while(0) + +/* + ** xfunc_copyrel + ** Just like _copyRel, but doesn't copy the paths + */ +bool +xfunc_copyrel(RelOptInfo from, RelOptInfo *to) +{ + RelOptInfo newnode; + + Pointer (*alloc) () = palloc; + + /* COPY_CHECKARGS() */ + if (to == NULL) + return false; + + /* COPY_CHECKNULL() */ + if (from == NULL) + { + (*to) = NULL; + return true; + } + + /* COPY_NEW(c) */ + newnode = (RelOptInfo) (*alloc) (classSize(RelOptInfo)); + if (newnode == NULL) + return false; + + /* ---------------- + * copy node superclass fields + * ---------------- + */ + CopyNodeFields((Node) from, (Node) newnode, alloc); + + /* ---------------- + * copy remainder of node + * ---------------- + */ + Node_Copy(from, newnode, alloc, relids); + + newnode->indexed = from->indexed; + newnode->pages = from->pages; + newnode->tuples = from->tuples; + newnode->size = from->size; + newnode->width = from->width; + + Node_Copy(from, newnode, alloc, targetlist); + + /* + * No!!!! Node_Copy(from, newnode, alloc, pathlist); + * Node_Copy(from, newnode, alloc, unorderedpath); Node_Copy(from, + * newnode, alloc, cheapestpath); + */ +#if 0 /* can't use Node_copy now. 2/95 -ay */ + Node_Copy(from, newnode, alloc, classlist); + Node_Copy(from, newnode, alloc, indexkeys); + Node_Copy(from, newnode, alloc, ordering); +#endif + Node_Copy(from, newnode, alloc, restrictinfo); + Node_Copy(from, newnode, alloc, joininfo); + Node_Copy(from, newnode, alloc, innerjoin); + + (*to) = newnode; + return true; +} diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index 90469a63e7d..a4aa7ba8db8 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.40 1999/02/16 00:40:59 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.41 1999/02/18 00:49:17 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -191,6 +191,8 @@ make_one_rel_by_joins(Query *root, List *rels, int levels_needed) merge_rels_with_same_relids(joined_rels); + root->join_rel_list = rels = joined_rels; + #if 0 /* * * for each expensive predicate in each path in each distinct @@ -203,17 +205,6 @@ make_one_rel_by_joins(Query *root, List *rels, int levels_needed) rels_set_cheapest(joined_rels); - if (BushyPlanFlag) - { - /* - * In case of bushy trees if there is still a join between a - * join relation and another relation, add a new joininfo that - * involves the join relation to the joininfo list of the - * other relation - */ - add_rel_to_rel_joininfos(root, joined_rels, rels); - } - foreach(x, joined_rels) { rel = (RelOptInfo *) lfirst(x); @@ -228,20 +219,6 @@ make_one_rel_by_joins(Query *root, List *rels, int levels_needed) #endif } - if (BushyPlanFlag) - { - /* - * prune rels that have been completely incorporated into new - * join rels - */ - joined_rels = del_rels_all_bushy_inactive(rels); - - /* - * merge join rels if then contain the same list of base rels - */ - merge_rels_with_same_relids(joined_rels); - } - root->join_rel_list = rels = joined_rels; } Assert(BushyPlanFlag || length(rels) == 1); diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c index ff8040b4627..630e125c31b 100644 --- a/src/backend/optimizer/path/indxpath.c +++ b/src/backend/optimizer/path/indxpath.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.50 1999/02/15 05:50:00 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.51 1999/02/18 00:49:18 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -1208,7 +1208,7 @@ indexable_joinclauses(RelOptInfo *rel, RelOptInfo *index, { List *clauses = lfirst(clausegroups); - ((RestrictInfo *) lfirst(clauses))->restrictinfojoinid = joininfo->unjoined_rels; + ((RestrictInfo *) lfirst(clauses))->restrictinfojoinid = joininfo->unjoined_relids; } cg_list = nconc(cg_list, clausegroups); } diff --git a/src/backend/optimizer/path/joinpath.c b/src/backend/optimizer/path/joinpath.c index 3577ebfda26..8a62c44b855 100644 --- a/src/backend/optimizer/path/joinpath.c +++ b/src/backend/optimizer/path/joinpath.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinpath.c,v 1.27 1999/02/15 03:22:05 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinpath.c,v 1.28 1999/02/18 00:49:19 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -71,8 +71,8 @@ update_rels_pathlist_for_joins(Query *root, List *joinrels) foreach(j, joinrels) { RelOptInfo *joinrel = (RelOptInfo *) lfirst(j); - List *innerrelids; - List *outerrelids; + Relids innerrelids; + Relids outerrelids; RelOptInfo *innerrel; RelOptInfo *outerrel; Path *bestinnerjoin; @@ -163,7 +163,7 @@ update_rels_pathlist_for_joins(Query *root, List *joinrels) * Returns the pathnode of the selected path. */ static Path * -best_innerjoin(List *join_paths, List *outer_relids) +best_innerjoin(List *join_paths, Relids outer_relids) { Path *cheapest = (Path *) NULL; List *join_path; diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c index 8c1bc06e680..df74130cfcb 100644 --- a/src/backend/optimizer/path/joinrels.c +++ b/src/backend/optimizer/path/joinrels.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinrels.c,v 1.26 1999/02/16 00:41:00 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinrels.c,v 1.27 1999/02/18 00:49:20 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -31,12 +31,11 @@ bool _use_right_sided_plans_ = false; #endif -static List *new_joininfo_list(List *joininfo_list, List *join_relids); -static void add_superrels(RelOptInfo *rel, RelOptInfo *super_rel); -static bool nonoverlap_rels(RelOptInfo *rel1, RelOptInfo *rel2); +static List *new_joininfo_list(List *joininfo_list, Relids join_relids); static bool nonoverlap_sets(List *s1, List *s2); -static void set_joinrel_size(RelOptInfo *joinrel, RelOptInfo *outer_rel, RelOptInfo *inner_rel, - JoinInfo *jinfo); +static bool is_subset(List *s1, List *s2); +static void set_joinrel_size(RelOptInfo *joinrel, RelOptInfo *outer_rel, + RelOptInfo *inner_rel, JoinInfo *jinfo); /* * make_rels_by_joins @@ -100,53 +99,69 @@ make_rels_by_joins(Query *root, List *outer_rels) */ List * make_rels_by_clause_joins(Query *root, RelOptInfo *outer_rel, - List *joininfo_list, List *only_relids) + List *joininfo_list, Relids only_relids) { List *join_list = NIL; List *i = NIL; foreach(i, joininfo_list) - { + { JoinInfo *joininfo = (JoinInfo *) lfirst(i); RelOptInfo *rel; + Relids unjoined_relids = joininfo->unjoined_relids; - if (!joininfo->bushy_inactive) + if (unjoined_relids != NIL) { - List *unjoined_rels = joininfo->unjoined_rels; - - if (unjoined_rels != NIL) + if (length(unjoined_relids) == 1 && + (only_relids == NIL || + /* geqo only wants certain relids to make new rels */ + intMember(lfirsti(unjoined_relids), only_relids))) { - if (length(unjoined_rels) == 1 && - (only_relids == NIL || - /* geqo only wants certain relids to make new rels */ - same(joininfo->unjoined_rels, only_relids))) + rel = make_join_rel(outer_rel, + get_base_rel(root, lfirsti(unjoined_relids)), + joininfo); + join_list = lappend(join_list, rel); + + /* Right-sided plan */ + if (_use_right_sided_plans_ && + length(outer_rel->relids) > 1) { - rel = make_join_rel(outer_rel, - get_base_rel(root, lfirsti(unjoined_rels)), + rel = make_join_rel( + get_base_rel(root, lfirsti(unjoined_relids)), + outer_rel, joininfo); - /* how about right-sided plan ? */ - if (_use_right_sided_plans_ && - length(outer_rel->relids) > 1) + join_list = lappend(join_list, rel); + } + } + + if (BushyPlanFlag && only_relids == NIL) /* no bushy from geqo */ + { + List *r; + + foreach(r, root->join_rel_list) + { + RelOptInfo *join_rel = lfirst(r); + + Assert(length(join_rel->relids) > 1); + if (is_subset(unjoined_relids, join_rel->relids) && + nonoverlap_sets(outer_rel->relids, join_rel->relids)) { - if (rel != NULL) + rel = make_join_rel(outer_rel, + join_rel, + joininfo); + join_list = lappend(join_list, rel); + + /* Right-sided plan */ + if (_use_right_sided_plans_ && + length(outer_rel->relids) > 1) + { + rel = make_join_rel(join_rel, + outer_rel, + joininfo); join_list = lappend(join_list, rel); - rel = make_join_rel(get_base_rel(root, - lfirsti(unjoined_rels)), - outer_rel, - joininfo); + } } } - else if (BushyPlanFlag) - { - rel = make_join_rel(outer_rel, - get_join_rel(root, unjoined_rels), - joininfo); - } - else - rel = NULL; - - if (rel != NULL) - join_list = lappend(join_list, rel); } } } @@ -172,7 +187,7 @@ make_rels_by_clauseless_joins(RelOptInfo *outer_rel, List *inner_rels) foreach(i, inner_rels) { inner_rel = (RelOptInfo *) lfirst(i); - if (nonoverlap_rels(inner_rel, outer_rel)) + if (nonoverlap_sets(inner_rel->relids, outer_rel->relids)) { t_list = lappend(t_list, make_join_rel(outer_rel, @@ -229,11 +244,10 @@ make_join_rel(RelOptInfo *outer_rel, RelOptInfo *inner_rel, JoinInfo *joininfo) joinrel->restrictinfo = NIL; joinrel->joininfo = NULL; joinrel->innerjoin = NIL; - joinrel->superrels = NIL; /* * This function uses a trick to pass inner/outer rels as - * different lists, and then flattens it out later. + * different lists, and then flattens it out later. bjm */ joinrel->relids = lcons(outer_rel->relids, lcons(inner_rel->relids, NIL)); @@ -241,13 +255,10 @@ make_join_rel(RelOptInfo *outer_rel, RelOptInfo *inner_rel, JoinInfo *joininfo) joinrel->targetlist = new_outer_tlist; if (joininfo) - { joinrel->restrictinfo = joininfo->jinfo_restrictinfo; - if (BushyPlanFlag) - joininfo->bushy_inactive = true; - } - joinrel_joininfo_list = new_joininfo_list(append(outer_rel->joininfo, inner_rel->joininfo), + joinrel_joininfo_list = new_joininfo_list(append(outer_rel->joininfo, + inner_rel->joininfo), intAppend(outer_rel->relids, inner_rel->relids)); joinrel->joininfo = joinrel_joininfo_list; @@ -275,7 +286,7 @@ make_join_rel(RelOptInfo *outer_rel, RelOptInfo *inner_rel, JoinInfo *joininfo) */ List * new_join_tlist(List *tlist, - List *other_relids, + Relids other_relids, int first_resdomno) { int resdomno = first_resdomno - 1; @@ -293,8 +304,7 @@ new_join_tlist(List *tlist, if (in_final_tlist) { resdomno += 1; - t_list = lappend(t_list, - create_tl_element(get_expr(xtl), resdomno)); + t_list = lappend(t_list,create_tl_element(get_expr(xtl), resdomno)); } } @@ -320,10 +330,10 @@ new_join_tlist(List *tlist, * Returns a list of joininfo nodes, new and old. */ static List * -new_joininfo_list(List *joininfo_list, List *join_relids) +new_joininfo_list(List *joininfo_list, Relids join_relids) { List *current_joininfo_list = NIL; - List *new_unjoined_rels = NIL; + Relids new_unjoined_relids = NIL; JoinInfo *other_joininfo = (JoinInfo *) NULL; List *xjoininfo = NIL; @@ -332,31 +342,31 @@ new_joininfo_list(List *joininfo_list, List *join_relids) List *or; JoinInfo *joininfo = (JoinInfo *) lfirst(xjoininfo); - new_unjoined_rels = joininfo->unjoined_rels; - foreach(or, new_unjoined_rels) + new_unjoined_relids = joininfo->unjoined_relids; + foreach(or, new_unjoined_relids) { if (intMember(lfirsti(or), join_relids)) - new_unjoined_rels = lremove((void *) lfirst(or), new_unjoined_rels); + new_unjoined_relids = lremove((void *) lfirst(or), new_unjoined_relids); } - joininfo->unjoined_rels = new_unjoined_rels; - if (new_unjoined_rels != NIL) + joininfo->unjoined_relids = new_unjoined_relids; + if (new_unjoined_relids != NIL) { - other_joininfo = joininfo_member(new_unjoined_rels, + other_joininfo = joininfo_member(new_unjoined_relids, current_joininfo_list); if (other_joininfo) { - other_joininfo->jinfo_restrictinfo = (List *) LispUnion(joininfo->jinfo_restrictinfo, - other_joininfo->jinfo_restrictinfo); + other_joininfo->jinfo_restrictinfo = (List *) + LispUnion(joininfo->jinfo_restrictinfo, + other_joininfo->jinfo_restrictinfo); } else { other_joininfo = makeNode(JoinInfo); - other_joininfo->unjoined_rels = joininfo->unjoined_rels; + other_joininfo->unjoined_relids = joininfo->unjoined_relids; other_joininfo->jinfo_restrictinfo = joininfo->jinfo_restrictinfo; other_joininfo->mergejoinable = joininfo->mergejoinable; other_joininfo->hashjoinable = joininfo->hashjoinable; - other_joininfo->bushy_inactive = false; current_joininfo_list = lcons(other_joininfo, current_joininfo_list); @@ -367,105 +377,6 @@ new_joininfo_list(List *joininfo_list, List *join_relids) return current_joininfo_list; } -/* - * add_rel_to_rel_joininfos - * For each new join relation, create new joininfos that - * use the join relation as inner relation, and add - * the new joininfos to those rel nodes that still - * have joins with the join relation. - * - * 'joinrels' is a list of join relations. - * - * Modifies the joininfo field of appropriate rel nodes. - */ -void -add_rel_to_rel_joininfos(Query *root, List *joinrels, List *outerrels) -{ - List *xjoinrel = NIL; - List *xrelid = NIL; - List *xrel = NIL; - List *xjoininfo = NIL; - - foreach(xjoinrel, joinrels) - { - RelOptInfo *joinrel = (RelOptInfo *) lfirst(xjoinrel); - - foreach(xrelid, joinrel->relids) - { - Relid relid = (Relid) lfirst(xrelid); - RelOptInfo *rel = get_join_rel(root, relid); - - add_superrels(rel, joinrel); - } - } - foreach(xjoinrel, joinrels) - { - RelOptInfo *joinrel = (RelOptInfo *) lfirst(xjoinrel); - - foreach(xjoininfo, joinrel->joininfo) - { - JoinInfo *joininfo = (JoinInfo *) lfirst(xjoininfo); - List *unjoined_rels = joininfo->unjoined_rels; - List *restrict_info = joininfo->jinfo_restrictinfo; - bool mergejoinable = joininfo->mergejoinable; - bool hashjoinable = joininfo->hashjoinable; - - foreach(xrelid, unjoined_rels) - { - Relid relid = (Relid) lfirst(xrelid); - RelOptInfo *rel = get_join_rel(root, relid); - List *super_rels = rel->superrels; - List *xsuper_rel = NIL; - JoinInfo *new_joininfo = makeNode(JoinInfo); - - new_joininfo->unjoined_rels = joinrel->relids; - new_joininfo->jinfo_restrictinfo = restrict_info; - new_joininfo->mergejoinable = mergejoinable; - new_joininfo->hashjoinable = hashjoinable; - new_joininfo->bushy_inactive = false; - rel->joininfo = lappend(rel->joininfo, new_joininfo); - - foreach(xsuper_rel, super_rels) - { - RelOptInfo *super_rel = (RelOptInfo *) lfirst(xsuper_rel); - - if (nonoverlap_rels(super_rel, joinrel)) - { - List *new_relids = super_rel->relids; - JoinInfo *other_joininfo = joininfo_member(new_relids, - joinrel->joininfo); - - if (other_joininfo) - { - other_joininfo->jinfo_restrictinfo = (List *) LispUnion(restrict_info, - other_joininfo->jinfo_restrictinfo); - } - else - { - JoinInfo *new_joininfo = makeNode(JoinInfo); - - new_joininfo->unjoined_rels = new_relids; - new_joininfo->jinfo_restrictinfo = restrict_info; - new_joininfo->mergejoinable = mergejoinable; - new_joininfo->hashjoinable = hashjoinable; - new_joininfo->bushy_inactive = false; - joinrel->joininfo = lappend(joinrel->joininfo, - new_joininfo); - } - } - } - } - } - } - - foreach(xrel, outerrels) - { - RelOptInfo *rel = (RelOptInfo *) lfirst(xrel); - - rel->superrels = NIL; - } -} - /* * get_cheapest_complete_rel * Find the join relation that includes all the original @@ -482,8 +393,8 @@ get_cheapest_complete_rel(List *join_rel_list) RelOptInfo *final_rel = NULL; /* - * find the relations that has no further joins, i.e., its joininfos - * all have unjoined_rels nil. + * find the relations that have no further joins, i.e., its joininfos + * all have unjoined_relids nil. */ foreach(xrel, join_rel_list) { @@ -495,7 +406,7 @@ get_cheapest_complete_rel(List *join_rel_list) { JoinInfo *joininfo = (JoinInfo *) lfirst(xjoininfo); - if (joininfo->unjoined_rels != NIL) + if (joininfo->unjoined_relids != NIL) { final = false; break; @@ -510,38 +421,23 @@ get_cheapest_complete_rel(List *join_rel_list) return final_rel; } -/* - * add_superrels - * add rel to the temporary property list superrels. - * - * 'rel' a rel node - * 'super_rel' rel node of a join relation that includes rel - * - * Modifies the superrels field of rel - */ -static void -add_superrels(RelOptInfo *rel, RelOptInfo *super_rel) -{ - rel->superrels = lappend(rel->superrels, super_rel); -} - -/* - * nonoverlap_rels - * test if two join relations overlap, i.e., includes the same - * relation. - * - * 'rel1' and 'rel2' are two join relations - * - * Returns non-nil if rel1 and rel2 do not overlap. - */ static bool -nonoverlap_rels(RelOptInfo *rel1, RelOptInfo *rel2) +nonoverlap_sets(List *s1, List *s2) { - return nonoverlap_sets(rel1->relids, rel2->relids); + List *x = NIL; + + foreach(x, s1) + { + int e = lfirsti(x); + + if (intMember(e, s2)) + return false; + } + return true; } static bool -nonoverlap_sets(List *s1, List *s2) +is_subset(List *s1, List *s2) { List *x = NIL; @@ -549,7 +445,7 @@ nonoverlap_sets(List *s1, List *s2) { int e = lfirsti(x); - if (intMember(e, s2)) + if (!intMember(e, s2)) return false; } return true; diff --git a/src/backend/optimizer/path/prune.c b/src/backend/optimizer/path/prune.c index f0905ae383b..3d60c09f6cc 100644 --- a/src/backend/optimizer/path/prune.c +++ b/src/backend/optimizer/path/prune.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/Attic/prune.c,v 1.36 1999/02/16 00:41:00 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/Attic/prune.c,v 1.37 1999/02/18 00:49:21 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -24,7 +24,7 @@ #include "utils/elog.h" -static List *merge_rel_with_same_relids(RelOptInfo *rel, List *unjoined_rels); +static List *merge_rel_with_same_relids(RelOptInfo *rel, Relids unjoined_relids); /* * merge_rels_with_same_relids @@ -49,7 +49,7 @@ merge_rels_with_same_relids(List *rel_list) /* * merge_rel_with_same_relids - * Prunes those relations from 'unjoined_rels' that are redundant with + * Prunes those relations from 'unjoined_relids' that are redundant with * 'rel'. A relation is redundant if it is built up of the same * relations as 'rel'. Paths for the redundant relation are merged into * the pathlist of 'rel'. @@ -59,12 +59,12 @@ merge_rels_with_same_relids(List *rel_list) * */ static List * -merge_rel_with_same_relids(RelOptInfo *rel, List *unjoined_rels) +merge_rel_with_same_relids(RelOptInfo *rel, Relids unjoined_relids) { List *i = NIL; List *result = NIL; - foreach(i, unjoined_rels) + foreach(i, unjoined_relids) { RelOptInfo *unjoined_rel = (RelOptInfo *) lfirst(i); @@ -105,49 +105,3 @@ rels_set_cheapest(List *rel_list) elog(ERROR, "non JoinPath called"); } } - -/* - * del_rels_all_bushy_inactive - * If all the joininfo's in a rel node are bushy_inactive, - * that means that this node has been joined into - * other nodes in all possible ways, therefore - * this node can be discarded. If not, it will cause - * extra complexity of the optimizer. - * - * old_rels is a list of rel nodes - * - * Returns a new list of rel nodes - */ -List * -del_rels_all_bushy_inactive(List *old_rels) -{ - RelOptInfo *rel; - List *joininfo_list, - *xjoininfo, - *i, - *temp_list = NIL; - - foreach(i, old_rels) - { - rel = (RelOptInfo *) lfirst(i); - joininfo_list = rel->joininfo; - - if (joininfo_list == NIL) - temp_list = lcons(rel, temp_list); - else - { - foreach(xjoininfo, joininfo_list) - { - JoinInfo *joininfo = (JoinInfo *) lfirst(xjoininfo); - - if (!joininfo->bushy_inactive) - { - temp_list = lcons(rel, temp_list); - break; - } - } - } - } - return temp_list; -} - diff --git a/src/backend/optimizer/path/xfunc.c b/src/backend/optimizer/path/xfunc.c deleted file mode 100644 index e722840f30e..00000000000 --- a/src/backend/optimizer/path/xfunc.c +++ /dev/null @@ -1,1485 +0,0 @@ -/*------------------------------------------------------------------------- - * - * xfunc.c - * Utility routines to handle expensive function optimization. - * Includes xfunc_trypullup(), which attempts early pullup of predicates - * to allow for maximal pruning. - * - * Copyright (c) 1994, Regents of the University of California - * - * - * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/Attic/xfunc.c,v 1.28 1999/02/13 23:16:24 momjian Exp $ - * - *------------------------------------------------------------------------- - */ -#include /* for MAXFLOAT on most systems */ - -#include /* for MAXFLOAT on SunOS */ -#include - -#include "postgres.h" - -#include "access/heapam.h" -#include "catalog/pg_language.h" -#include "catalog/pg_proc.h" -#include "catalog/pg_type.h" -#include "lib/lispsort.h" -#include "nodes/nodes.h" -#include "nodes/pg_list.h" -#include "nodes/primnodes.h" -#include "nodes/relation.h" -#include "optimizer/clauses.h" -#include "optimizer/cost.h" -#include "optimizer/internal.h" -#include "optimizer/keys.h" -#include "optimizer/pathnode.h" -#include "optimizer/tlist.h" /* for get_expr */ -#include "optimizer/xfunc.h" -#include "storage/buf_internals.h" /* for NBuffers */ -#include "tcop/dest.h" -#include "utils/syscache.h" - -#define ever ; 1 ; - -/* local funcs */ -static int xfunc_card_unreferenced(Query *queryInfo, - Expr *clause, Relid referenced); - -*/ - -/* -** xfunc_trypullup -** Preliminary pullup of predicates, to allow for maximal pruning. -** Given a relation, check each of its paths and see if you can -** pullup clauses from its inner and outer. -*/ - -void -xfunc_trypullup(RelOptInfo rel) -{ - LispValue y; /* list ptr */ - RestrictInfo maxcinfo; /* The RestrictInfo to pull up, as - * calculated by xfunc_shouldpull() */ - JoinPath curpath; /* current path in list */ - int progress; /* has progress been made this time - * through? */ - int clausetype; - - do - { - progress = false; /* no progress yet in this iteration */ - foreach(y, get_pathlist(rel)) - { - curpath = (JoinPath) lfirst(y); - - /* - * * for each operand, attempt to pullup predicates until - * first * failure. - */ - for (ever) - { - /* No, the following should NOT be '==' !! */ - if (clausetype = xfunc_shouldpull((Path) get_innerjoinpath(curpath), - curpath, INNER, &maxcinfo)) - { - - xfunc_pullup((Path) get_innerjoinpath(curpath), - curpath, maxcinfo, INNER, clausetype); - progress = true; - } - else - break; - } - for (ever) - { - - /* No, the following should NOT be '==' !! */ - if (clausetype = xfunc_shouldpull((Path) get_outerjoinpath(curpath), - curpath, OUTER, &maxcinfo)) - { - - xfunc_pullup((Path) get_outerjoinpath(curpath), - curpath, maxcinfo, OUTER, clausetype); - progress = true; - } - else - break; - } - - /* - * * make sure the unpruneable flag bubbles up, i.e. * if - * anywhere below us in the path pruneable is false, * then - * pruneable should be false here - */ - if (get_pruneable(get_parent(curpath)) && - (!get_pruneable(get_parent - ((Path) get_innerjoinpath(curpath))) || - !get_pruneable(get_parent((Path) - get_outerjoinpath(curpath))))) - { - - set_pruneable(get_parent(curpath), false); - progress = true; - } - } - } while (progress); -} - -/* - ** xfunc_shouldpull - ** find clause with highest rank, and decide whether to pull it up - ** from child to parent. Currently we only pullup secondary join clauses - ** that are in the pathrestrictinfo. Secondary hash and sort clauses are - ** left where they are. - ** If we find an expensive function but decide *not* to pull it up, - ** we'd better set the unpruneable flag. -- JMH, 11/11/92 - ** - ** Returns: 0 if nothing left to pullup - ** XFUNC_LOCPRD if a local predicate is to be pulled up - ** XFUNC_JOINPRD if a secondary join predicate is to be pulled up - */ -int -xfunc_shouldpull(Query *queryInfo, - Path childpath, - JoinPath parentpath, - int whichchild, - RestrictInfo *maxcinfopt) /* Out: pointer to clause - * to pullup */ -{ - LispValue clauselist, - tmplist; /* lists of clauses */ - RestrictInfo maxcinfo; /* clause to pullup */ - LispValue primjoinclause /* primary join clause */ - = xfunc_primary_join(parentpath); - Cost tmprank, - maxrank = (-1 * MAXFLOAT); /* ranks of clauses */ - Cost joinselec = 0; /* selectivity of the join predicate */ - Cost joincost = 0; /* join cost + primjoinclause cost */ - int retval = XFUNC_LOCPRD; - - clauselist = get_loc_restrictinfo(childpath); - - if (clauselist != LispNil) - { - /* find local predicate with maximum rank */ - for (tmplist = clauselist, - maxcinfo = (RestrictInfo) lfirst(tmplist), - maxrank = xfunc_rank(get_clause(maxcinfo)); - tmplist != LispNil; - tmplist = lnext(tmplist)) - { - - if ((tmprank = xfunc_rank(get_clause((RestrictInfo) lfirst(tmplist)))) - > maxrank) - { - maxcinfo = (RestrictInfo) lfirst(tmplist); - maxrank = tmprank; - } - } - } - - /* - * * If child is a join path, and there are multiple join clauses, * - * see if any join clause has even higher rank than the highest * - * local predicate - */ - if (is_join(childpath) && xfunc_num_join_clauses((JoinPath) childpath) > 1) - for (tmplist = get_pathrestrictinfo((JoinPath) childpath); - tmplist != LispNil; - tmplist = lnext(tmplist)) - { - - if (tmplist != LispNil && - (tmprank = xfunc_rank(get_clause((RestrictInfo) lfirst(tmplist)))) - > maxrank) - { - maxcinfo = (RestrictInfo) lfirst(tmplist); - maxrank = tmprank; - retval = XFUNC_JOINPRD; - } - } - if (maxrank == (-1 * MAXFLOAT)) /* no expensive clauses */ - return 0; - - /* - * * Pullup over join if clause is higher rank than join, or if * join - * is nested loop and current path is inner child (note that * - * restrictions on the inner of a nested loop don't buy you anything - * -- * you still have to scan the entire inner relation each time). * - * Note that the cost of a secondary join clause is only what's * - * calculated by xfunc_expense(), since the actual joining * (i.e. the - * usual path_cost) is paid for by the primary join clause. - */ - if (primjoinclause != LispNil) - { - joinselec = compute_clause_selec(queryInfo, primjoinclause, LispNil); - joincost = xfunc_join_expense(parentpath, whichchild); - - if (XfuncMode == XFUNC_PULLALL || - (XfuncMode != XFUNC_WAIT && - ((joincost != 0 && - (maxrank = xfunc_rank(get_clause(maxcinfo))) > - ((joinselec - 1.0) / joincost)) - || (joincost == 0 && joinselec < 1) - || (!is_join(childpath) - && (whichchild == INNER) - && IsA(parentpath, NestPath) - &&!IsA(parentpath, HashPath) - &&!IsA(parentpath, MergePath))))) - { - - *maxcinfopt = maxcinfo; - return retval; - - } - else if (maxrank != -(MAXFLOAT)) - { - - /* - * * we've left an expensive restriction below a join. Since * - * we may pullup this restriction in predmig.c, we'd best * - * set the RelOptInfo of this join to be unpruneable - */ - set_pruneable(get_parent(parentpath), false); - /* and fall through */ - } - } - return 0; -} - - -/* - ** xfunc_pullup - ** move clause from child pathnode to parent pathnode. This operation - ** makes the child pathnode produce a larger relation than it used to. - ** This means that we must construct a new RelOptInfo just for the childpath, - ** although this RelOptInfo will not be added to the list of Rels to be joined up - ** in the query; it's merely a parent for the new childpath. - ** We also have to fix up the path costs of the child and parent. - ** - ** Now returns a pointer to the new pulled-up RestrictInfo. -- JMH, 11/18/92 - */ -RestrictInfo -xfunc_pullup(Query *queryInfo, - Path childpath, - JoinPath parentpath, - RestrictInfo cinfo, /* clause to pull up */ - int whichchild, /* whether child is INNER or OUTER of join */ - int clausetype) /* whether clause to pull is join or local */ -{ - Path newkid; - RelOptInfo newrel; - Cost pulled_selec; - Cost cost; - RestrictInfo newinfo; - - /* remove clause from childpath */ - newkid = (Path) copyObject((Node) childpath); - if (clausetype == XFUNC_LOCPRD) - { - set_locrestrictinfo(newkid, - xfunc_LispRemove((LispValue) cinfo, - (List) get_loc_restrictinfo(newkid))); - } - else - { - set_pathrestrictinfo - ((JoinPath) newkid, - xfunc_LispRemove((LispValue) cinfo, - (List) get_pathrestrictinfo((JoinPath) newkid))); - } - - /* - * * give the new child path its own RelOptInfo node that reflects the * - * lack of the pulled-up predicate - */ - pulled_selec = compute_clause_selec(queryInfo, - get_clause(cinfo), LispNil); - xfunc_copyrel(get_parent(newkid), &newrel); - set_parent(newkid, newrel); - set_pathlist(newrel, lcons(newkid, NIL)); - set_unorderedpath(newrel, (PathPtr) newkid); - set_cheapestpath(newrel, (PathPtr) newkid); - set_size(newrel, - (Count) ((Cost) get_size(get_parent(childpath)) / pulled_selec)); - - /* - * * fix up path cost of newkid. To do this we subtract away all the * - * xfunc_costs of childpath, then recompute the xfunc_costs of newkid - */ - cost = get_path_cost(newkid) - xfunc_get_path_cost(childpath); - Assert(cost >= 0); - set_path_cost(newkid, cost); - cost = get_path_cost(newkid) + xfunc_get_path_cost(newkid); - set_path_cost(newkid, cost); - - /* - * * We copy the cinfo, since it may appear in other plans, and we're - * going * to munge it. -- JMH, 7/22/92 - */ - newinfo = (RestrictInfo) copyObject((Node) cinfo); - - /* - * * Fix all vars in the clause * to point to the right varno and - * varattno in parentpath - */ - xfunc_fixvars(get_clause(newinfo), newrel, whichchild); - - /* add clause to parentpath, and fix up its cost. */ - set_locrestrictinfo(parentpath, - lispCons((LispValue) newinfo, - (LispValue) get_loc_restrictinfo(parentpath))); - /* put new childpath into the path tree */ - if (whichchild == INNER) - set_innerjoinpath(parentpath, (pathPtr) newkid); - else - set_outerjoinpath(parentpath, (pathPtr) newkid); - - /* - * * recompute parentpath cost from scratch -- the cost * of the join - * method has changed - */ - cost = xfunc_total_path_cost(parentpath); - set_path_cost(parentpath, cost); - - return newinfo; -} - -/* - ** calculate (selectivity-1)/cost. - */ -Cost -xfunc_rank(Query *queryInfo, LispValue clause) -{ - Cost selec = compute_clause_selec(queryInfo, clause, LispNil); - Cost cost = xfunc_expense(queryInfo, clause); - - if (cost == 0) - if (selec > 1) - return MAXFLOAT; - else - return -(MAXFLOAT); - return (selec - 1) / cost; -} - -/* - ** Find the "global" expense of a clause; i.e. the local expense divided - ** by the cardinalities of all the base relations of the query that are *not* - ** referenced in the clause. - */ -Cost -xfunc_expense(Query *queryInfo, clause) -LispValue clause; -{ - Cost cost = xfunc_local_expense(clause); - - if (cost) - { - Count card = xfunc_card_unreferenced(queryInfo, clause, LispNil); - - if (card) - cost /= card; - } - - return cost; -} - -/* - ** xfunc_join_expense - ** Find global expense of a join clause - */ -Cost -xfunc_join_expense(Query *queryInfo, JoinPath path, int whichchild) -{ - LispValue primjoinclause = xfunc_primary_join(path); - - /* - * * the second argument to xfunc_card_unreferenced reflects all the * - * relations involved in the join clause, i.e. all the relids in the - * RelOptInfo * of the join clause - */ - Count card = 0; - Cost cost = xfunc_expense_per_tuple(path, whichchild); - - card = xfunc_card_unreferenced(queryInfo, - primjoinclause, - get_relids(get_parent(path))); - if (primjoinclause) - cost += xfunc_local_expense(primjoinclause); - - if (card) - cost /= card; - - return cost; -} - -/* - ** Recursively find the per-tuple expense of a clause. See - ** xfunc_func_expense for more discussion. - */ -Cost -xfunc_local_expense(LispValue clause) -{ - Cost cost = 0; /* running expense */ - LispValue tmpclause; - - /* First handle the base case */ - if (IsA(clause, Const) ||IsA(clause, Var) ||IsA(clause, Param)) - return 0; - /* now other stuff */ - else if (IsA(clause, Iter)) - /* Too low. Should multiply by the expected number of iterations. */ - return xfunc_local_expense(get_iterexpr((Iter) clause)); - else if (IsA(clause, ArrayRef)) - return xfunc_local_expense(get_refexpr((ArrayRef) clause)); - else if (fast_is_clause(clause)) - return (xfunc_func_expense((LispValue) get_op(clause), - (LispValue) get_opargs(clause))); - else if (fast_is_funcclause(clause)) - return (xfunc_func_expense((LispValue) get_function(clause), - (LispValue) get_funcargs(clause))); - else if (fast_not_clause(clause)) - return xfunc_local_expense(lsecond(clause)); - else if (fast_or_clause(clause) || fast_and_clause(clause)) - { - /* find cost of evaluating each disjunct */ - for (tmpclause = lnext(clause); tmpclause != LispNil; - tmpclause = lnext(tmpclause)) - cost += xfunc_local_expense(lfirst(tmpclause)); - return cost; - } - else - { - elog(ERROR, "Clause node of undetermined type"); - return -1; - } -} - -/* - ** xfunc_func_expense - ** given a Func or Oper and its args, find its expense. - ** Note: in Stonebraker's SIGMOD '91 paper, he uses a more complicated metric - ** than the one here. We can ignore the expected number of tuples for - ** our calculations; we just need the per-tuple expense. But he also - ** proposes components to take into account the costs of accessing disk and - ** archive. We didn't adopt that scheme here; eventually the vacuum - ** cleaner should be able to tell us what percentage of bytes to find on - ** which storage level, and that should be multiplied in appropriately - ** in the cost function below. Right now we don't model the cost of - ** accessing secondary or tertiary storage, since we don't have sufficient - ** stats to do it right. - */ -Cost -xfunc_func_expense(LispValue node, LispValue args) -{ - HeapTuple tupl; /* the pg_proc tuple for each function */ - Form_pg_proc proc; /* a data structure to hold the pg_proc - * tuple */ - int width = 0; /* byte width of the field referenced by - * each clause */ - RegProcedure funcid; /* ID of function associate with node */ - Cost cost = 0; /* running expense */ - LispValue tmpclause; - LispValue operand; /* one operand of an operator */ - - if (IsA(node, Oper)) - { - /* don't trust the opid in the Oper node. Use the opno. */ - if (!(funcid = get_opcode(get_opno((Oper) node)))) - elog(ERROR, "Oper's function is undefined"); - } - else - funcid = get_funcid((Func) node); - - /* look up tuple in cache */ - tupl = SearchSysCacheTuple(PROOID, - ObjectIdGetDatum(funcid), - 0, 0, 0); - if (!HeapTupleIsValid(tupl)) - elog(ERROR, "Cache lookup failed for procedure %d", funcid); - proc = (Form_pg_proc) GETSTRUCT(tupl); - - /* - * * if it's a Postquel function, its cost is stored in the * - * associated plan. - */ - if (proc->prolang == SQLlanguageId) - { - LispValue tmpplan; - List planlist; - - if (IsA(node, Oper) ||get_func_planlist((Func) node) == LispNil) - { - Oid *argOidVect; /* vector of argtypes */ - char *pq_src; /* text of PQ function */ - int nargs; /* num args to PQ function */ - QueryTreeList *queryTree_list; /* dummy variable */ - - /* - * * plan the function, storing it in the Func node for later * - * use by the executor. - */ - pq_src = (char *) textout(&(proc->prosrc)); - nargs = proc->pronargs; - if (nargs > 0) - argOidVect = proc->proargtypes; - planlist = (List) pg_parse_and_plan(pq_src, argOidVect, nargs, - &parseTree_list, None, FALSE); - if (IsA(node, Func)) - set_func_planlist((Func) node, planlist); - - } - else - { /* plan has been cached inside the Func - * node already */ - planlist = get_func_planlist((Func) node); - } - - /* - * * Return the sum of the costs of the plans (the PQ function * - * may have many queries in its body). - */ - foreach(tmpplan, planlist) - cost += get_cost((Plan) lfirst(tmpplan)); - return cost; - } - else - { /* it's a C function */ - - /* - * * find the cost of evaluating the function's arguments * and - * the width of the operands - */ - for (tmpclause = args; tmpclause != LispNil; - tmpclause = lnext(tmpclause)) - { - - if ((operand = lfirst(tmpclause)) != LispNil) - { - cost += xfunc_local_expense(operand); - width += xfunc_width(operand); - } - } - - /* - * * when stats become available, add in cost of accessing - * secondary * and tertiary storage here. - */ - return (cost + - (Cost) proc->propercall_cpu + - (Cost) proc->properbyte_cpu * (Cost) proc->probyte_pct / 100.00 * - (Cost) width - - /* - * Pct_of_obj_in_mem DISK_COST * proc->probyte_pct/100.00 * width - * Pct_of_obj_on_disk + ARCH_COST * proc->probyte_pct/100.00 * - * width Pct_of_obj_on_arch - */ - ); - } -} - -/* - ** xfunc_width - ** recursively find the width of a expression - */ - -int -xfunc_width(LispValue clause) -{ - Relation rd; /* Relation Descriptor */ - HeapTuple tupl; /* structure to hold a cached tuple */ - Form_pg_type type; /* structure to hold a type tuple */ - int retval = 0; - - if (IsA(clause, Const)) - { - /* base case: width is the width of this constant */ - retval = get_constlen((Const) clause); - goto exit; - } - else if (IsA(clause, ArrayRef)) - { - /* base case: width is width of the refelem within the array */ - retval = get_refelemlength((ArrayRef) clause); - goto exit; - } - else if (IsA(clause, Var)) - { - /* base case: width is width of this attribute */ - tupl = SearchSysCacheTuple(TYPOID, - ObjectIdGetDatum(get_vartype((Var) clause)), - 0, 0, 0); - if (!HeapTupleIsValid(tupl)) - elog(ERROR, "Cache lookup failed for type %d", - get_vartype((Var) clause)); - type = (Form_pg_type) GETSTRUCT(tupl); - if (get_varattno((Var) clause) == 0) - { - /* clause is a tuple. Get its width */ - rd = heap_open(type->typrelid); - retval = xfunc_tuple_width(rd); - heap_close(rd); - } - else - { - /* attribute is a base type */ - retval = type->typlen; - } - goto exit; - } - else if (IsA(clause, Param)) - { - if (typeidTypeRelid(get_paramtype((Param) clause))) - { - /* Param node returns a tuple. Find its width */ - rd = heap_open(typeidTypeRelid(get_paramtype((Param) clause))); - retval = xfunc_tuple_width(rd); - heap_close(rd); - } - else if (get_param_tlist((Param) clause) != LispNil) - { - /* Param node projects a complex type */ - Assert(length(get_param_tlist((Param) clause)) == 1); /* sanity */ - retval = xfunc_width((LispValue) - get_expr(lfirst(get_param_tlist((Param) clause)))); - } - else - { - /* Param node returns a base type */ - retval = typeLen(typeidType(get_paramtype((Param) clause))); - } - goto exit; - } - else if (IsA(clause, Iter)) - { - - /* - * * An Iter returns a setof things, so return the width of a - * single * thing. * Note: THIS MAY NOT WORK RIGHT WHEN AGGS GET - * FIXED, * SINCE AGG FUNCTIONS CHEW ON THE WHOLE SETOF THINGS!!!! * - * This whole Iter business is bogus, anyway. - */ - retval = xfunc_width(get_iterexpr((Iter) clause)); - goto exit; - } - else if (fast_is_clause(clause)) - { - - /* - * * get function associated with this Oper, and treat this as * a - * Func - */ - tupl = SearchSysCacheTuple(OPROID, - ObjectIdGetDatum(get_opno((Oper) get_op(clause))), - 0, 0, 0); - if (!HeapTupleIsValid(tupl)) - elog(ERROR, "Cache lookup failed for procedure %d", - get_opno((Oper) get_op(clause))); - return (xfunc_func_width - ((RegProcedure) (((Form_pg_operator) (GETSTRUCT(tupl)))->oprcode), - (LispValue) get_opargs(clause))); - } - else if (fast_is_funcclause(clause)) - { - Func func = (Func) get_function(clause); - - if (get_func_tlist(func) != LispNil) - { - - /* - * this function has a projection on it. Get the length of - * the projected attribute - */ - Assert(length(get_func_tlist(func)) == 1); /* sanity */ - retval = xfunc_width((LispValue) - get_expr(lfirst(get_func_tlist(func)))); - goto exit; - } - else - { - return (xfunc_func_width((RegProcedure) get_funcid(func), - (LispValue) get_funcargs(clause))); - } - } - else - { - elog(ERROR, "Clause node of undetermined type"); - return -1; - } - -exit: - if (retval == -1) - retval = VARLEN_DEFAULT; - return retval; -} - -/* - ** xfunc_card_unreferenced: - ** find all relations not referenced in clause, and multiply their - ** cardinalities. Ignore relation of cardinality 0. - ** User may pass in referenced list, if they know it (useful - ** for joins). - */ -static Count -xfunc_card_unreferenced(Query *queryInfo, - LispValue clause, Relid referenced) -{ - Relid unreferenced, - allrelids = LispNil; - LispValue temp; - - /* find all relids of base relations referenced in query */ - foreach(temp, queryInfo->base_rel_list) - { - Assert(lnext(get_relids((RelOptInfo) lfirst(temp))) == LispNil); - allrelids = lappend(allrelids, - lfirst(get_relids((RelOptInfo) lfirst(temp)))); - } - - /* find all relids referenced in query but not in clause */ - if (!referenced) - referenced = xfunc_find_references(clause); - unreferenced = set_difference(allrelids, referenced); - - return xfunc_card_product(unreferenced); -} - -/* - ** xfunc_card_product - ** multiple together cardinalities of a list relations. - */ -Count -xfunc_card_product(Query *queryInfo, Relid relids) -{ - LispValue cinfonode; - LispValue temp; - RelOptInfo currel; - Cost tuples; - Count retval = 0; - - foreach(temp, relids) - { - currel = get_rel(lfirst(temp)); - tuples = get_tuples(currel); - - if (tuples) - { /* not of cardinality 0 */ - /* factor in the selectivity of all zero-cost clauses */ - foreach(cinfonode, get_restrictinfo(currel)) - { - if (!xfunc_expense(queryInfo, get_clause((RestrictInfo) lfirst(cinfonode)))) - tuples *= compute_clause_selec(queryInfo, - get_clause((RestrictInfo) lfirst(cinfonode)), - LispNil); - } - - if (retval == 0) - retval = tuples; - else - retval *= tuples; - } - } - if (retval == 0) - retval = 1; /* saves caller from dividing by zero */ - return retval; -} - - -/* - ** xfunc_find_references: - ** Traverse a clause and find all relids referenced in the clause. - */ -List -xfunc_find_references(LispValue clause) -{ - List retval = (List) LispNil; - LispValue tmpclause; - - /* Base cases */ - if (IsA(clause, Var)) - return lispCons(lfirst(get_varid((Var) clause)), LispNil); - else if (IsA(clause, Const) ||IsA(clause, Param)) - return (List) LispNil; - - /* recursion */ - else if (IsA(clause, Iter)) - - /* - * Too low. Should multiply by the expected number of iterations. - * maybe - */ - return xfunc_find_references(get_iterexpr((Iter) clause)); - else if (IsA(clause, ArrayRef)) - return xfunc_find_references(get_refexpr((ArrayRef) clause)); - else if (fast_is_clause(clause)) - { - /* string together result of all operands of Oper */ - for (tmpclause = (LispValue) get_opargs(clause); tmpclause != LispNil; - tmpclause = lnext(tmpclause)) - retval = nconc(retval, xfunc_find_references(lfirst(tmpclause))); - return retval; - } - else if (fast_is_funcclause(clause)) - { - /* string together result of all args of Func */ - for (tmpclause = (LispValue) get_funcargs(clause); - tmpclause != LispNil; - tmpclause = lnext(tmpclause)) - retval = nconc(retval, xfunc_find_references(lfirst(tmpclause))); - return retval; - } - else if (fast_not_clause(clause)) - return xfunc_find_references(lsecond(clause)); - else if (fast_or_clause(clause) || fast_and_clause(clause)) - { - /* string together result of all operands of OR */ - for (tmpclause = lnext(clause); tmpclause != LispNil; - tmpclause = lnext(tmpclause)) - retval = nconc(retval, xfunc_find_references(lfirst(tmpclause))); - return retval; - } - else - { - elog(ERROR, "Clause node of undetermined type"); - return (List) LispNil; - } -} - -/* - ** xfunc_primary_join: - ** Find the primary join clause: for Hash and Merge Joins, this is the - ** min rank Hash or Merge clause, while for Nested Loop it's the - ** min rank pathclause - */ -LispValue -xfunc_primary_join(JoinPath pathnode) -{ - LispValue joinclauselist = get_pathrestrictinfo(pathnode); - RestrictInfo mincinfo; - LispValue tmplist; - LispValue minclause = LispNil; - Cost minrank, - tmprank; - - if (IsA(pathnode, MergePath)) - { - for (tmplist = get_path_mergeclauses((MergePath) pathnode), - minclause = lfirst(tmplist), - minrank = xfunc_rank(minclause); - tmplist != LispNil; - tmplist = lnext(tmplist)) - if ((tmprank = xfunc_rank(lfirst(tmplist))) - < minrank) - { - minrank = tmprank; - minclause = lfirst(tmplist); - } - return minclause; - } - else if (IsA(pathnode, HashPath)) - { - for (tmplist = get_path_hashclauses((HashPath) pathnode), - minclause = lfirst(tmplist), - minrank = xfunc_rank(minclause); - tmplist != LispNil; - tmplist = lnext(tmplist)) - if ((tmprank = xfunc_rank(lfirst(tmplist))) - < minrank) - { - minrank = tmprank; - minclause = lfirst(tmplist); - } - return minclause; - } - - /* if we drop through, it's nested loop join */ - if (joinclauselist == LispNil) - return LispNil; - - for (tmplist = joinclauselist, mincinfo = (RestrictInfo) lfirst(joinclauselist), - minrank = xfunc_rank(get_clause((RestrictInfo) lfirst(tmplist))); - tmplist != LispNil; - tmplist = lnext(tmplist)) - if ((tmprank = xfunc_rank(get_clause((RestrictInfo) lfirst(tmplist)))) - < minrank) - { - minrank = tmprank; - mincinfo = (RestrictInfo) lfirst(tmplist); - } - return (LispValue) get_clause(mincinfo); -} - -/* - ** xfunc_get_path_cost - ** get the expensive function costs of the path - */ -Cost -xfunc_get_path_cost(Query *queryInfo, Path pathnode) -{ - Cost cost = 0; - LispValue tmplist; - Cost selec = 1.0; - - /* - * * first add in the expensive local function costs. * We ensure that - * the clauses are sorted by rank, so that we * know (via - * selectivities) the number of tuples that will be checked * by each - * function. If we're not doing any optimization of expensive * - * functions, we don't sort. - */ - if (XfuncMode != XFUNC_OFF) - set_locrestrictinfo(pathnode, lisp_qsort(get_loc_restrictinfo(pathnode), - xfunc_cinfo_compare)); - for (tmplist = get_loc_restrictinfo(pathnode), selec = 1.0; - tmplist != LispNil; - tmplist = lnext(tmplist)) - { - cost += (Cost) (xfunc_local_expense(get_clause((RestrictInfo) lfirst(tmplist))) - * (Cost) get_tuples(get_parent(pathnode)) * selec); - selec *= compute_clause_selec(queryInfo, - get_clause((RestrictInfo) lfirst(tmplist)), - LispNil); - } - - /* - * * Now add in any node-specific expensive function costs. * Again, - * we must ensure that the clauses are sorted by rank. - */ - if (IsA(pathnode, JoinPath)) - { - if (XfuncMode != XFUNC_OFF) - set_pathrestrictinfo((JoinPath) pathnode, lisp_qsort - (get_pathrestrictinfo((JoinPath) pathnode), - xfunc_cinfo_compare)); - for (tmplist = get_pathrestrictinfo((JoinPath) pathnode), selec = 1.0; - tmplist != LispNil; - tmplist = lnext(tmplist)) - { - cost += (Cost) (xfunc_local_expense(get_clause((RestrictInfo) lfirst(tmplist))) - * (Cost) get_tuples(get_parent(pathnode)) * selec); - selec *= compute_clause_selec(queryInfo, - get_clause((RestrictInfo) lfirst(tmplist)), - LispNil); - } - } - if (IsA(pathnode, HashPath)) - { - if (XfuncMode != XFUNC_OFF) - set_path_hashclauses - ((HashPath) pathnode, - lisp_qsort(get_path_hashclauses((HashPath) pathnode), - xfunc_clause_compare)); - for (tmplist = get_path_hashclauses((HashPath) pathnode), selec = 1.0; - tmplist != LispNil; - tmplist = lnext(tmplist)) - { - cost += (Cost) (xfunc_local_expense(lfirst(tmplist)) - * (Cost) get_tuples(get_parent(pathnode)) * selec); - selec *= compute_clause_selec(queryInfo, - lfirst(tmplist), LispNil); - } - } - if (IsA(pathnode, MergePath)) - { - if (XfuncMode != XFUNC_OFF) - set_path_mergeclauses - ((MergePath) pathnode, - lisp_qsort(get_path_mergeclauses((MergePath) pathnode), - xfunc_clause_compare)); - for (tmplist = get_path_mergeclauses((MergePath) pathnode), selec = 1.0; - tmplist != LispNil; - tmplist = lnext(tmplist)) - { - cost += (Cost) (xfunc_local_expense(lfirst(tmplist)) - * (Cost) get_tuples(get_parent(pathnode)) * selec); - selec *= compute_clause_selec(queryInfo, - lfirst(tmplist), LispNil); - } - } - Assert(cost >= 0); - return cost; -} - -/* - ** Recalculate the cost of a path node. This includes the basic cost of the - ** node, as well as the cost of its expensive functions. - ** We need to do this to the parent after pulling a clause from a child into a - ** parent. Thus we should only be calling this function on JoinPaths. - */ -Cost -xfunc_total_path_cost(JoinPath pathnode) -{ - Cost cost = xfunc_get_path_cost((Path) pathnode); - - Assert(IsA(pathnode, JoinPath)); - if (IsA(pathnode, MergePath)) - { - MergePath mrgnode = (MergePath) pathnode; - - cost += cost_mergejoin(get_path_cost((Path) get_outerjoinpath(mrgnode)), - get_path_cost((Path) get_innerjoinpath(mrgnode)), - get_outersortkeys(mrgnode), - get_innersortkeys(mrgnode), - get_tuples(get_parent((Path) get_outerjoinpath - (mrgnode))), - get_tuples(get_parent((Path) get_innerjoinpath - (mrgnode))), - get_width(get_parent((Path) get_outerjoinpath - (mrgnode))), - get_width(get_parent((Path) get_innerjoinpath - (mrgnode)))); - Assert(cost >= 0); - return cost; - } - else if (IsA(pathnode, HashPath)) - { - HashPath hashnode = (HashPath) pathnode; - - cost += cost_hashjoin(get_path_cost((Path) get_outerjoinpath(hashnode)), - get_path_cost((Path) get_innerjoinpath(hashnode)), - get_outerhashkeys(hashnode), - get_innerhashkeys(hashnode), - get_tuples(get_parent((Path) get_outerjoinpath - (hashnode))), - get_tuples(get_parent((Path) get_innerjoinpath - (hashnode))), - get_width(get_parent((Path) get_outerjoinpath - (hashnode))), - get_width(get_parent((Path) get_innerjoinpath - (hashnode)))); - Assert(cost >= 0); - return cost; - } - else -/* Nested Loop Join */ - { - cost += cost_nestloop(get_path_cost((Path) get_outerjoinpath(pathnode)), - get_path_cost((Path) get_innerjoinpath(pathnode)), - get_tuples(get_parent((Path) get_outerjoinpath - (pathnode))), - get_tuples(get_parent((Path) get_innerjoinpath - (pathnode))), - get_pages(get_parent((Path) get_outerjoinpath - (pathnode))), - IsA(get_innerjoinpath(pathnode), IndexPath)); - Assert(cost >= 0); - return cost; - } -} - - -/* - ** xfunc_expense_per_tuple - ** return the expense of the join *per-tuple* of the input relation. - ** The cost model here is that a join costs - ** k*card(outer)*card(inner) + l*card(outer) + m*card(inner) + n - ** - ** We treat the l and m terms by considering them to be like restrictions - ** constrained to be right under the join. Thus the cost per inner and - ** cost per outer of the join is different, reflecting these virtual nodes. - ** - ** The cost per tuple of outer is k + l/referenced(inner). Cost per tuple - ** of inner is k + m/referenced(outer). - ** The constants k, l, m and n depend on the join method. Measures here are - ** based on the costs in costsize.c, with fudging for HashJoin and Sorts to - ** make it fit our model (the 'q' in HashJoin results in a - ** card(outer)/card(inner) term, and sorting results in a log term. - - */ -Cost -xfunc_expense_per_tuple(JoinPath joinnode, int whichchild) -{ - RelOptInfo outerrel = get_parent((Path) get_outerjoinpath(joinnode)); - RelOptInfo innerrel = get_parent((Path) get_innerjoinpath(joinnode)); - Count outerwidth = get_width(outerrel); - Count outers_per_page = ceil(BLCKSZ / (outerwidth + sizeof(HeapTupleData))); - - if (IsA(joinnode, HashPath)) - { - if (whichchild == INNER) - return (1 + _CPU_PAGE_WEIGHT_) * outers_per_page / NBuffers; - else - return (((1 + _CPU_PAGE_WEIGHT_) * outers_per_page / NBuffers) - + _CPU_PAGE_WEIGHT_ - / xfunc_card_product(get_relids(innerrel))); - } - else if (IsA(joinnode, MergePath)) - { - /* assumes sort exists, and costs one (I/O + CPU) per tuple */ - if (whichchild == INNER) - return ((2 * _CPU_PAGE_WEIGHT_ + 1) - / xfunc_card_product(get_relids(outerrel))); - else - return ((2 * _CPU_PAGE_WEIGHT_ + 1) - / xfunc_card_product(get_relids(innerrel))); - } - else -/* nestloop */ - { - Assert(IsA(joinnode, JoinPath)); - return _CPU_PAGE_WEIGHT_; - } -} - -/* - ** xfunc_fixvars - ** After pulling up a clause, we must walk its expression tree, fixing Var - ** nodes to point to the correct varno (either INNER or OUTER, depending - ** on which child the clause was pulled from), and the right varattno in the - ** target list of the child's former relation. If the target list of the - ** child RelOptInfo does not contain the attribute we need, we add it. - */ -void -xfunc_fixvars(LispValue clause, /* clause being pulled up */ - RelOptInfo rel, /* rel it's being pulled from */ - int varno) /* whether rel is INNER or OUTER of join */ -{ - LispValue tmpclause; /* temporary variable */ - TargetEntry *tle; /* tlist member corresponding to var */ - - - if (IsA(clause, Const) ||IsA(clause, Param)) - return; - else if (IsA(clause, Var)) - { - /* here's the meat */ - tle = tlistentry_member((Var) clause, get_targetlist(rel)); - if (tle == LispNil) - { - - /* - * * The attribute we need is not in the target list, * so we - * have to add it. * - * - */ - add_var_to_tlist(rel, (Var) clause); - tle = tlistentry_member((Var) clause, get_targetlist(rel)); - } - set_varno(((Var) clause), varno); - set_varattno(((Var) clause), get_resno(get_resdom(get_entry(tle)))); - } - else if (IsA(clause, Iter)) - xfunc_fixvars(get_iterexpr((Iter) clause), rel, varno); - else if (fast_is_clause(clause)) - { - xfunc_fixvars(lfirst(lnext(clause)), rel, varno); - xfunc_fixvars(lfirst(lnext(lnext(clause))), rel, varno); - } - else if (fast_is_funcclause(clause)) - for (tmpclause = lnext(clause); tmpclause != LispNil; - tmpclause = lnext(tmpclause)) - xfunc_fixvars(lfirst(tmpclause), rel, varno); - else if (fast_not_clause(clause)) - xfunc_fixvars(lsecond(clause), rel, varno); - else if (fast_or_clause(clause) || fast_and_clause(clause)) - for (tmpclause = lnext(clause); tmpclause != LispNil; - tmpclause = lnext(tmpclause)) - xfunc_fixvars(lfirst(tmpclause), rel, varno); - else - elog(ERROR, "Clause node of undetermined type"); -} - - -/* - ** Comparison function for lisp_qsort() on a list of RestrictInfo's. - ** arg1 and arg2 should really be of type (RestrictInfo *). - */ -int -xfunc_cinfo_compare(void *arg1, void *arg2) -{ - RestrictInfo info1 = *(RestrictInfo *) arg1; - RestrictInfo info2 = *(RestrictInfo *) arg2; - - LispValue clause1 = (LispValue) get_clause(info1), - clause2 = (LispValue) get_clause(info2); - - return xfunc_clause_compare((void *) &clause1, (void *) &clause2); -} - -/* - ** xfunc_clause_compare: comparison function for lisp_qsort() that compares two - ** clauses based on expense/(1 - selectivity) - ** arg1 and arg2 are really pointers to clauses. - */ -int -xfunc_clause_compare(void *arg1, void *arg2) -{ - LispValue clause1 = *(LispValue *) arg1; - LispValue clause2 = *(LispValue *) arg2; - Cost rank1, /* total xfunc rank of clause1 */ - rank2; /* total xfunc rank of clause2 */ - - rank1 = xfunc_rank(clause1); - rank2 = xfunc_rank(clause2); - - if (rank1 < rank2) - return -1; - else if (rank1 == rank2) - return 0; - else - return 1; -} - -/* - ** xfunc_disjunct_sort - ** given a list of clauses, for each clause sort the disjuncts by cost - ** (this assumes the predicates have been converted to Conjunctive NF) - ** Modifies the clause list! - */ -void -xfunc_disjunct_sort(LispValue clause_list) -{ - LispValue temp; - - foreach(temp, clause_list) - if (or_clause(lfirst(temp))) - lnext(lfirst(temp)) = lisp_qsort(lnext(lfirst(temp)), xfunc_disjunct_compare); -} - - -/* - ** xfunc_disjunct_compare: comparison function for qsort() that compares two - ** disjuncts based on cost/selec. - ** arg1 and arg2 are really pointers to disjuncts - */ -int -xfunc_disjunct_compare(Query *queryInfo, void *arg1, void *arg2) -{ - LispValue disjunct1 = *(LispValue *) arg1; - LispValue disjunct2 = *(LispValue *) arg2; - Cost cost1, /* total cost of disjunct1 */ - cost2, /* total cost of disjunct2 */ - selec1, - selec2; - Cost rank1, - rank2; - - cost1 = xfunc_expense(queryInfo, disjunct1); - cost2 = xfunc_expense(queryInfo, disjunct2); - selec1 = compute_clause_selec(queryInfo, - disjunct1, LispNil); - selec2 = compute_clause_selec(queryInfo, - disjunct2, LispNil); - - if (selec1 == 0) - rank1 = MAXFLOAT; - else if (cost1 == 0) - rank1 = 0; - else - rank1 = cost1 / selec1; - - if (selec2 == 0) - rank2 = MAXFLOAT; - else if (cost2 == 0) - rank2 = 0; - else - rank2 = cost2 / selec2; - - if (rank1 < rank2) - return -1; - else if (rank1 == rank2) - return 0; - else - return 1; -} - -/* ------------------------ UTILITY FUNCTIONS ------------------------------- */ -/* - ** xfunc_func_width - ** Given a function OID and operands, find the width of the return value. - */ -int -xfunc_func_width(RegProcedure funcid, LispValue args) -{ - Relation rd; /* Relation Descriptor */ - HeapTuple tupl; /* structure to hold a cached tuple */ - Form_pg_proc proc; /* structure to hold the pg_proc tuple */ - Form_pg_type type; /* structure to hold the pg_type tuple */ - LispValue tmpclause; - int retval; - - /* lookup function and find its return type */ - Assert(RegProcedureIsValid(funcid)); - tupl = SearchSysCacheTuple(PROOID, - ObjectIdGetDatum(funcid), - 0, 0, 0); - if (!HeapTupleIsValid(tupl)) - elog(ERROR, "Cache lookup failed for procedure %d", funcid); - proc = (Form_pg_proc) GETSTRUCT(tupl); - - /* if function returns a tuple, get the width of that */ - if (typeidTypeRelid(proc->prorettype)) - { - rd = heap_open(typeidTypeRelid(proc->prorettype)); - retval = xfunc_tuple_width(rd); - heap_close(rd); - goto exit; - } - else -/* function returns a base type */ - { - tupl = SearchSysCacheTuple(TYPOID, - ObjectIdGetDatum(proc->prorettype), - 0, 0, 0); - if (!HeapTupleIsValid(tupl)) - elog(ERROR, "Cache lookup failed for type %d", proc->prorettype); - type = (Form_pg_type) GETSTRUCT(tupl); - /* if the type length is known, return that */ - if (type->typlen != -1) - { - retval = type->typlen; - goto exit; - } - else -/* estimate the return size */ - { - /* find width of the function's arguments */ - for (tmpclause = args; tmpclause != LispNil; - tmpclause = lnext(tmpclause)) - retval += xfunc_width(lfirst(tmpclause)); - /* multiply by outin_ratio */ - retval = (int) (proc->prooutin_ratio / 100.0 * retval); - goto exit; - } - } -exit: - return retval; -} - -/* - ** xfunc_tuple_width - ** Return the sum of the lengths of all the attributes of a given relation - */ -int -xfunc_tuple_width(Relation rd) -{ - int i; - int retval = 0; - TupleDesc tdesc = RelationGetDescr(rd); - - for (i = 0; i < tdesc->natts; i++) - { - if (tdesc->attrs[i]->attlen != -1) - retval += tdesc->attrs[i]->attlen; - else - retval += VARLEN_DEFAULT; - } - - return retval; -} - -/* - ** xfunc_num_join_clauses - ** Find the number of join clauses associated with this join path - */ -int -xfunc_num_join_clauses(JoinPath path) -{ - int num = length(get_pathrestrictinfo(path)); - - if (IsA(path, MergePath)) - return num + length(get_path_mergeclauses((MergePath) path)); - else if (IsA(path, HashPath)) - return num + length(get_path_hashclauses((HashPath) path)); - else - return num; -} - -/* - ** xfunc_LispRemove - ** Just like LispRemove, but it whines if the item to be removed ain't there - */ -LispValue -xfunc_LispRemove(LispValue foo, List bar) -{ - LispValue temp = LispNil; - LispValue result = LispNil; - int sanity = false; - - for (temp = bar; !null(temp); temp = lnext(temp)) - if (!equal((Node) (foo), (Node) (lfirst(temp)))) - result = lappend(result, lfirst(temp)); - else - sanity = true; /* found a matching item to remove! */ - - if (!sanity) - elog(ERROR, "xfunc_LispRemove: didn't find a match!"); - - return result; -} - -#define Node_Copy(a, b, c, d) \ -do { \ - if (NodeCopy((Node)((a)->d), (Node*)&((b)->d), c) != true) \ - { \ - return false; \ - } \ -} while(0) - -/* - ** xfunc_copyrel - ** Just like _copyRel, but doesn't copy the paths - */ -bool -xfunc_copyrel(RelOptInfo from, RelOptInfo *to) -{ - RelOptInfo newnode; - - Pointer (*alloc) () = palloc; - - /* COPY_CHECKARGS() */ - if (to == NULL) - return false; - - /* COPY_CHECKNULL() */ - if (from == NULL) - { - (*to) = NULL; - return true; - } - - /* COPY_NEW(c) */ - newnode = (RelOptInfo) (*alloc) (classSize(RelOptInfo)); - if (newnode == NULL) - return false; - - /* ---------------- - * copy node superclass fields - * ---------------- - */ - CopyNodeFields((Node) from, (Node) newnode, alloc); - - /* ---------------- - * copy remainder of node - * ---------------- - */ - Node_Copy(from, newnode, alloc, relids); - - newnode->indexed = from->indexed; - newnode->pages = from->pages; - newnode->tuples = from->tuples; - newnode->size = from->size; - newnode->width = from->width; - - Node_Copy(from, newnode, alloc, targetlist); - - /* - * No!!!! Node_Copy(from, newnode, alloc, pathlist); - * Node_Copy(from, newnode, alloc, unorderedpath); Node_Copy(from, - * newnode, alloc, cheapestpath); - */ -#if 0 /* can't use Node_copy now. 2/95 -ay */ - Node_Copy(from, newnode, alloc, classlist); - Node_Copy(from, newnode, alloc, indexkeys); - Node_Copy(from, newnode, alloc, ordering); -#endif - Node_Copy(from, newnode, alloc, restrictinfo); - Node_Copy(from, newnode, alloc, joininfo); - Node_Copy(from, newnode, alloc, innerjoin); - Node_Copy(from, newnode, alloc, superrels); - - (*to) = newnode; - return true; -} diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c index e1bb596d452..b4ecb4dd01b 100644 --- a/src/backend/optimizer/plan/initsplan.c +++ b/src/backend/optimizer/plan/initsplan.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.27 1999/02/15 05:21:06 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.28 1999/02/18 00:49:26 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -41,8 +41,8 @@ extern int Quiet; static void add_restrict_and_join_to_rel(Query *root, List *clause); static void add_join_info_to_rels(Query *root, RestrictInfo *restrictinfo, - List *join_relids); -static void add_vars_to_targetlist(Query *root, List *vars, List *join_relids); + Relids join_relids); +static void add_vars_to_targetlist(Query *root, List *vars, Relids join_relids); static MergeOrder *mergejoinop(Expr *clause); static Oid hashjoinop(Expr *clause); @@ -107,7 +107,7 @@ add_missing_vars_to_tlist(Query *root, List *tlist) foreach(l, root->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); - List *relids; + Relids relids; RelOptInfo *result; Var *var; @@ -165,7 +165,7 @@ add_restrict_and_join_to_rels(Query *root, List *clauses) static void add_restrict_and_join_to_rel(Query *root, List *clause) { - List *relids; + Relids relids; List *vars; RestrictInfo *restrictinfo = makeNode(RestrictInfo); @@ -235,31 +235,31 @@ add_restrict_and_join_to_rel(Query *root, List *clause) * 'restrictinfo' describes the join clause * 'join_relids' is the list of relations participating in the join clause * - * Returns nothing. - * */ static void -add_join_info_to_rels(Query *root, RestrictInfo *restrictinfo, List *join_relids) +add_join_info_to_rels(Query *root, RestrictInfo *restrictinfo, + Relids join_relids) { List *join_relid; + /* For every relid, find the rel, and add the proper join entries */ foreach(join_relid, join_relids) { JoinInfo *joininfo; - List *unjoined_rels = NIL; - List *rel; + Relids unjoined_relids = NIL; + List *rel; + /* Get the relids not equal to the current relid */ foreach(rel, join_relids) { if (lfirsti(rel) != lfirsti(join_relid)) - unjoined_rels = lappendi(unjoined_rels, lfirsti(rel)); + unjoined_relids = lappendi(unjoined_relids, lfirsti(rel)); } joininfo = find_joininfo_node(get_base_rel(root, lfirsti(join_relid)), - unjoined_rels); + unjoined_relids); joininfo->jinfo_restrictinfo = lcons(copyObject((void *) restrictinfo), joininfo->jinfo_restrictinfo); - } } @@ -279,7 +279,7 @@ add_join_info_to_rels(Query *root, RestrictInfo *restrictinfo, List *join_relids * Returns nothing. */ static void -add_vars_to_targetlist(Query *root, List *vars, List *join_relids) +add_vars_to_targetlist(Query *root, List *vars, Relids join_relids) { Var *var; List *temp = NIL; diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c index b8b8a606656..1bbfa9ef06a 100644 --- a/src/backend/optimizer/prep/prepunion.c +++ b/src/backend/optimizer/prep/prepunion.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.29 1999/02/13 23:16:40 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.30 1999/02/18 00:49:32 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -34,7 +34,7 @@ #include "optimizer/planner.h" #include "optimizer/planmain.h" -static List *plan_inherit_query(List *relids, Index rt_index, +static List *plan_inherit_query(Relids relids, Index rt_index, RangeTblEntry *rt_entry, Query *parse, List **union_rtentriesPtr); static RangeTblEntry *new_rangetable_entry(Oid new_relid, @@ -248,7 +248,7 @@ plan_inherit_queries(Query *parse, Index rt_index) * in union_rtentries. */ static List * -plan_inherit_query(List *relids, +plan_inherit_query(Relids relids, Index rt_index, RangeTblEntry *rt_entry, Query *root, @@ -301,8 +301,8 @@ plan_inherit_query(List *relids, * lists. */ List * -find_all_inheritors(List *unexamined_relids, - List *examined_relids) +find_all_inheritors(Relids unexamined_relids, + Relids examined_relids) { List *new_inheritors = NIL; List *new_examined_relids = NIL; diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index d615d4e36d6..3fb0a901a04 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.30 1999/02/14 22:24:25 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.31 1999/02/18 00:49:37 momjian Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -369,7 +369,7 @@ pull_constant_clauses(List *quals, List **constantQual) * */ void -clause_get_relids_vars(Node *clause, List **relids, List **vars) +clause_get_relids_vars(Node *clause, Relids *relids, List **vars) { List *clvars = pull_var_clause(clause); List *var_list = NIL; diff --git a/src/backend/optimizer/util/joininfo.c b/src/backend/optimizer/util/joininfo.c index a7e66fa03ea..880cfa5f422 100644 --- a/src/backend/optimizer/util/joininfo.c +++ b/src/backend/optimizer/util/joininfo.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/util/joininfo.c,v 1.17 1999/02/15 05:21:11 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/util/joininfo.c,v 1.18 1999/02/18 00:49:37 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -38,16 +38,16 @@ JoinInfo * joininfo_member(List *join_relids, List *joininfo_list) { - List *i = NIL; - List *other_rels = NIL; + List *i; foreach(i, joininfo_list) { - other_rels = lfirst(i); - if (same(join_relids, ((JoinInfo *) other_rels)->unjoined_rels)) - return (JoinInfo *) other_rels; + JoinInfo *joininfo = (JoinInfo *)lfirst(i); + + if (same(join_relids, joininfo->unjoined_relids)) + return joininfo; } - return (JoinInfo *) NULL; + return NULL; } @@ -62,7 +62,7 @@ joininfo_member(List *join_relids, List *joininfo_list) * */ JoinInfo * -find_joininfo_node(RelOptInfo *this_rel, List *join_relids) +find_joininfo_node(RelOptInfo *this_rel, Relids join_relids) { JoinInfo *joininfo = joininfo_member(join_relids, this_rel->joininfo); @@ -70,11 +70,10 @@ find_joininfo_node(RelOptInfo *this_rel, List *join_relids) if (joininfo == NULL) { joininfo = makeNode(JoinInfo); - joininfo->unjoined_rels = join_relids; + joininfo->unjoined_relids = join_relids; joininfo->jinfo_restrictinfo = NIL; joininfo->mergejoinable = false; joininfo->hashjoinable = false; - joininfo->bushy_inactive = false; this_rel->joininfo = lcons(joininfo, this_rel->joininfo); } return joininfo; diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c index 8e72cabb740..9041a9fe9b7 100644 --- a/src/backend/optimizer/util/pathnode.c +++ b/src/backend/optimizer/util/pathnode.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.36 1999/02/15 03:22:16 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.37 1999/02/18 00:49:38 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -633,7 +633,7 @@ create_hashjoin_path(RelOptInfo *joinrel, pathnode->jpath.path.pathorder->ordtype = SORTOP_ORDER; pathnode->jpath.path.pathorder->ord.sortop = NULL; pathnode->jpath.path.outerjoincost = (Cost) 0.0; - pathnode->jpath.path.joinid = (Relid) NULL; + pathnode->jpath.path.joinid = (Relids) NULL; /* pathnode->hashjoinoperator = operator; */ pathnode->path_hashclauses = hashclauses; pathnode->outerhashkeys = outerkeys; diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index 63c0ca4f47b..3b7594d87f1 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.14 1999/02/15 03:22:17 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.15 1999/02/18 00:49:38 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -30,7 +30,7 @@ RelOptInfo * get_base_rel(Query *root, int relid) { - List *relids; + Relids relids; RelOptInfo *rel; relids = lconsi(relid, NIL); @@ -53,7 +53,6 @@ get_base_rel(Query *root, int relid) rel->restrictinfo = NIL; rel->joininfo = NIL; rel->innerjoin = NIL; - rel->superrels = NIL; root->base_rel_list = lcons(rel, root->base_rel_list); @@ -76,7 +75,6 @@ get_base_rel(Query *root, int relid) bool hasindex; int pages, tuples; - /* * Otherwise, retrieve relation characteristics from the * system catalogs. @@ -93,11 +91,10 @@ get_base_rel(Query *root, int relid) /* * get_join_rel * Returns relation entry corresponding to 'relid' (a list of relids), - * creating a new one if necessary. This is for join relations. - * + * or NULL. */ RelOptInfo * -get_join_rel(Query *root, List *relid) +get_join_rel(Query *root, Relids relid) { return rel_member(relid, root->join_rel_list); } @@ -111,17 +108,17 @@ get_join_rel(Query *root, List *relid) * */ RelOptInfo * -rel_member(List *relid, List *rels) +rel_member(Relids relids, List *rels) { List *temp = NIL; List *temprelid = NIL; - if (relid != NIL && rels != NIL) + if (relids != NIL && rels != NIL) { foreach(temp, rels) { temprelid = ((RelOptInfo *) lfirst(temp))->relids; - if (same(temprelid, relid)) + if (same(temprelid, relids)) return (RelOptInfo *) (lfirst(temp)); } } diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h index 7ade94575db..32c90392de3 100644 --- a/src/include/nodes/relation.h +++ b/src/include/nodes/relation.h @@ -6,7 +6,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: relation.h,v 1.25 1999/02/15 05:21:12 momjian Exp $ + * $Id: relation.h,v 1.26 1999/02/18 00:49:38 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -17,11 +17,11 @@ #include /* - * Relid + * Relids * List of relation identifiers (indexes into the rangetable). */ -typedef List *Relid; +typedef List *Relids; /* * RelOptInfo @@ -72,7 +72,7 @@ typedef struct RelOptInfo NodeTag type; /* all relations: */ - Relid relids; + Relids relids; /* integer list of base relids involved */ /* catalog statistics information */ bool indexed; @@ -84,7 +84,7 @@ typedef struct RelOptInfo /* materialization information */ List *targetlist; List *pathlist; /* Path structures */ - struct Path *cheapestpath; + struct Path *cheapestpath; bool pruneable; /* used solely by indices: */ @@ -100,7 +100,6 @@ typedef struct RelOptInfo List *restrictinfo; /* RestrictInfo structures */ List *joininfo; /* JoinInfo structures */ List *innerjoin; - List *superrels; } RelOptInfo; extern Var *get_expr(TargetEntry *foo); @@ -148,7 +147,7 @@ typedef struct Path * indexes. */ Cost outerjoincost; - Relid joinid; + Relids joinid; List *loc_restrictinfo; } Path; @@ -221,7 +220,7 @@ typedef struct RestrictInfo /* hashjoin only */ Oid hashjoinoperator; - Relid restrictinfojoinid; + Relids restrictinfojoinid; } RestrictInfo; typedef struct JoinMethod @@ -246,11 +245,10 @@ typedef struct MergeInfo typedef struct JoinInfo { NodeTag type; - List *unjoined_rels; + Relids unjoined_relids; List *jinfo_restrictinfo; bool mergejoinable; bool hashjoinable; - bool bushy_inactive; } JoinInfo; typedef struct Iter diff --git a/src/include/optimizer/clauses.h b/src/include/optimizer/clauses.h index d7068c6c426..6332729d45b 100644 --- a/src/include/optimizer/clauses.h +++ b/src/include/optimizer/clauses.h @@ -6,7 +6,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: clauses.h,v 1.15 1999/02/13 23:21:42 momjian Exp $ + * $Id: clauses.h,v 1.16 1999/02/18 00:49:46 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -14,6 +14,7 @@ #define CLAUSES_H #include +#include extern Expr *make_clause(int type, Node *oper, List *args); extern bool is_opclause(Node *clause); @@ -37,7 +38,7 @@ extern Expr *make_andclause(List *andclauses); extern bool case_clause(Node *clause); extern List *pull_constant_clauses(List *quals, List **constantQual); -extern void clause_get_relids_vars(Node *clause, List **relids, List **vars); +extern void clause_get_relids_vars(Node *clause, Relids *relids, List **vars); extern int NumRelids(Node *clause); extern bool contains_not(Node *clause); extern bool is_joinable(Node *clause); diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h index 74a651e4c29..1a52453d0df 100644 --- a/src/include/optimizer/pathnode.h +++ b/src/include/optimizer/pathnode.h @@ -6,7 +6,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: pathnode.h,v 1.14 1999/02/13 23:21:49 momjian Exp $ + * $Id: pathnode.h,v 1.15 1999/02/18 00:49:46 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -42,9 +42,9 @@ extern HashPath *create_hashjoin_path(RelOptInfo *joinrel, int outersize, /* * prototypes for rel.c */ -extern RelOptInfo *rel_member(List *relid, List *rels); +extern RelOptInfo *rel_member(Relids relid, List *rels); extern RelOptInfo *get_base_rel(Query *root, int relid); -extern RelOptInfo *get_join_rel(Query *root, List *relid); +extern RelOptInfo *get_join_rel(Query *root, Relids relid); /* * prototypes for indexnode.h diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h index 4e1bfe3323c..f49d018255e 100644 --- a/src/include/optimizer/paths.h +++ b/src/include/optimizer/paths.h @@ -7,7 +7,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: paths.h,v 1.20 1999/02/16 00:41:03 momjian Exp $ + * $Id: paths.h,v 1.21 1999/02/18 00:49:47 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -78,9 +78,8 @@ extern MergeInfo *match_order_mergeinfo(PathOrder *ordering, * routines to determine which relations to join */ extern List *make_rels_by_joins(Query *root, List *outer_rels); -extern void add_rel_to_rel_joininfos(Query *root, List *joinrels, List *outerrels); extern List *make_rels_by_clause_joins(Query *root, RelOptInfo *outer_rel, - List *joininfo_list, List *only_relids); + List *joininfo_list, Relids only_relids); extern List *make_rels_by_clauseless_joins(RelOptInfo *outer_rel, List *inner_rels); extern RelOptInfo *make_join_rel(RelOptInfo *outer_rel, RelOptInfo *inner_rel, JoinInfo *joininfo); diff --git a/src/include/optimizer/xfunc.h b/src/include/optimizer/xfunc.h index 5c95cc1dbbc..e96c5474813 100644 --- a/src/include/optimizer/xfunc.h +++ b/src/include/optimizer/xfunc.h @@ -6,7 +6,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: xfunc.h,v 1.15 1999/02/13 23:21:54 momjian Exp $ + * $Id: xfunc.h,v 1.16 1999/02/18 00:49:48 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -62,8 +62,8 @@ extern Cost xfunc_func_expense(Expr *node, List *args); extern int xfunc_width(Expr *clause); /* static, moved to xfunc.c */ -/* extern int xfunc_card_unreferenced(Expr *clause, Relid referenced); */ -extern int xfunc_card_product(Relid relids); +/* extern int xfunc_card_unreferenced(Expr *clause, Relids referenced); */ +extern int xfunc_card_product(Relids relids); extern List *xfunc_find_references(List *clause); extern List *xfunc_primary_join(JoinPath *pathnode); extern Cost xfunc_get_path_cost(Path *pathnode); -- cgit v1.2.3