xfunc.c
上传用户:blenddy
上传日期:2007-01-07
资源大小:6495k
文件大小:41k
- /*-------------------------------------------------------------------------
- *
- * 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: /usr/local/cvsroot/pgsql/src/backend/optimizer/path/_deadcode/xfunc.c,v 1.5 1999/07/03 00:32:42 momjian Exp $
- *
- *-------------------------------------------------------------------------
- */
- #include <math.h> /* for MAXFLOAT on most systems */
- #include <values.h> /* for MAXFLOAT on SunOS */
- #include <string.h>
- #include "postgres.h"
- #include "access/htup.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 %u", 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 %u",
- 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 %u",
- 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 + MinTupleSize));
- 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 %u", 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 %u", 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;
- }