/*-------------------------------------------------------------------------
 *
 * 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.10 1999/09/21 20:58:13 momjian Exp $
 *
 *-------------------------------------------------------------------------
 */
#include <math.h>

#ifdef HAVE_VALUES_H
#include <values.h>
#endif

#include "postgres.h"

#include "access/heapam.h"
#include "access/htup.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"
#include "storage/buf_internals.h"
#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;
}
