/*-------------------------------------------------------------------------
 *
 * orindxpath.c--
 *	  Routines to find index paths that match a set of 'or' clauses
 *
 * Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  $Header: /cvsroot/pgsql/src/backend/optimizer/path/orindxpath.c,v 1.6 1998/06/15 19:28:41 momjian Exp $
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "nodes/pg_list.h"
#include "nodes/relation.h"
#include "nodes/primnodes.h"

#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"

#include "optimizer/internal.h"
#include "optimizer/clauses.h"
#include "optimizer/clauseinfo.h"
#include "optimizer/paths.h"
#include "optimizer/cost.h"
#include "optimizer/plancat.h"
#include "optimizer/xfunc.h"

#include "parser/parsetree.h"


static void
best_or_subclause_indices(Query *root, Rel *rel, List *subclauses,
List *indices, List *examined_indexids, Cost subcost, List *selectivities,
						  List **indexids, Cost *cost, List **selecs);
static void
best_or_subclause_index(Query *root, Rel *rel, Expr *subclause,
				   List *indices, int *indexid, Cost *cost, Cost *selec);


/*
 * create-or-index-paths--
 *	  Creates index paths for indices that match 'or' clauses.
 *
 * 'rel' is the relation entry for which the paths are to be defined on
 * 'clauses' is the list of available restriction clause nodes
 *
 * Returns a list of these index path nodes.
 *
 */
List *
create_or_index_paths(Query *root,
					  Rel *rel, List *clauses)
{
	List	   *t_list = NIL;

	if (clauses != NIL)
	{
		CInfo	   *clausenode = (CInfo *) (lfirst(clauses));

		/*
		 * Check to see if this clause is an 'or' clause, and, if so,
		 * whether or not each of the subclauses within the 'or' clause
		 * has been matched by an index (the 'Index field was set in
		 * (match_or)  if no index matches a given subclause, one of the
		 * lists of index nodes returned by (get_index) will be 'nil').
		 */
		if (valid_or_clause(clausenode) &&
			clausenode->indexids)
		{
			List	   *temp = NIL;
			List	   *index_list = NIL;
			bool		index_flag = true;

			index_list = clausenode->indexids;
			foreach(temp, index_list)
			{
				if (!temp)
					index_flag = false;
			}
			if (index_flag)
			{					/* used to be a lisp every function */
				IndexPath  *pathnode = makeNode(IndexPath);
				List	   *indexids;
				Cost		cost;
				List	   *selecs;

				best_or_subclause_indices(root,
										  rel,
										  clausenode->clause->args,
										  clausenode->indexids,
										  NIL,
										  (Cost) 0,
										  NIL,
										  &indexids,
										  &cost,
										  &selecs);

				pathnode->path.pathtype = T_IndexScan;
				pathnode->path.parent = rel;
				pathnode->indexqual =
					lcons(clausenode, NIL);
				pathnode->indexid = indexids;
				pathnode->path.path_cost = cost;

				/*
				 * copy clauseinfo list into path for expensive function
				 * processing	 -- JMH, 7/7/92
				 */
				pathnode->path.locclauseinfo =
					set_difference(clauses,
								   copyObject((Node *)
											  rel->clauseinfo));

#if 0							/* fix xfunc */
				/* add in cost for expensive functions!  -- JMH, 7/7/92 */
				if (XfuncMode != XFUNC_OFF)
				{
					((Path *) pathnode)->path_cost +=
						xfunc_get_path_cost((Path) pathnode);
				}
#endif
				clausenode->selectivity = (Cost) floatVal(selecs);
				t_list =
					lcons(pathnode,
					   create_or_index_paths(root, rel, lnext(clauses)));
			}
			else
				t_list = create_or_index_paths(root, rel, lnext(clauses));
		}
	}

	return (t_list);
}

/*
 * best-or-subclause-indices--
 *	  Determines the best index to be used in conjunction with each subclause
 *	  of an 'or' clause and the cost of scanning a relation using these
 *	  indices.	The cost is the sum of the individual index costs.
 *
 * 'rel' is the node of the relation on which the index is defined
 * 'subclauses' are the subclauses of the 'or' clause
 * 'indices' are those index nodes that matched subclauses of the 'or'
 *		clause
 * 'examined-indexids' is a list of those index ids to be used with
 *		subclauses that have already been examined
 * 'subcost' is the cost of using the indices in 'examined-indexids'
 * 'selectivities' is a list of the selectivities of subclauses that
 *		have already been examined
 *
 * Returns a list of the indexids, cost, and selectivities of each
 * subclause, e.g., ((i1 i2 i3) cost (s1 s2 s3)), where 'i' is an OID,
 * 'cost' is a flonum, and 's' is a flonum.
 */
static void
best_or_subclause_indices(Query *root,
						  Rel *rel,
						  List *subclauses,
						  List *indices,
						  List *examined_indexids,
						  Cost subcost,
						  List *selectivities,
						  List **indexids,		/* return value */
						  Cost *cost,	/* return value */
						  List **selecs)		/* return value */
{
	if (subclauses == NIL)
	{
		*indexids = nreverse(examined_indexids);
		*cost = subcost;
		*selecs = nreverse(selectivities);
	}
	else
	{
		int			best_indexid;
		Cost		best_cost;
		Cost		best_selec;

		best_or_subclause_index(root, rel, lfirst(subclauses), lfirst(indices),
								&best_indexid, &best_cost, &best_selec);

		best_or_subclause_indices(root,
								  rel,
								  lnext(subclauses),
								  lnext(indices),
								  lconsi(best_indexid, examined_indexids),
								  subcost + best_cost,
							 lcons(makeFloat(best_selec), selectivities),
								  indexids,
								  cost,
								  selecs);
	}
	return;
}

/*
 * best-or-subclause-index--
 *	  Determines which is the best index to be used with a subclause of
 *	  an 'or' clause by estimating the cost of using each index and selecting
 *	  the least expensive.
 *
 * 'rel' is the node of the relation on which the index is defined
 * 'subclause' is the subclause
 * 'indices' is a list of index nodes that match the subclause
 *
 * Returns a list (index-id index-subcost index-selectivity)
 * (a fixnum, a fixnum, and a flonum respectively).
 *
 */
static void
best_or_subclause_index(Query *root,
						Rel *rel,
						Expr *subclause,
						List *indices,
						int *retIndexid,		/* return value */
						Cost *retCost,	/* return value */
						Cost *retSelec) /* return value */
{
	if (indices != NIL)
	{
		Datum		value;
		int			flag = 0;
		Cost		subcost;
		Rel		   *index = (Rel *) lfirst(indices);
		AttrNumber	attno = (get_leftop(subclause))->varattno;
		Oid			opno = ((Oper *) subclause->oper)->opno;
		bool		constant_on_right = non_null((Expr *) get_rightop(subclause));
		float		npages,
					selec;
		int			subclause_indexid;
		Cost		subclause_cost;
		Cost		subclause_selec;

		if (constant_on_right)
			value = ((Const *) get_rightop(subclause))->constvalue;
		else
			value = NameGetDatum("");
		if (constant_on_right)
			flag = (_SELEC_IS_CONSTANT_ || _SELEC_CONSTANT_RIGHT_);
		else
			flag = _SELEC_CONSTANT_RIGHT_;
		index_selectivity(lfirsti(index->relids),
						  index->classlist,
						  lconsi(opno, NIL),
						  getrelid(lfirsti(rel->relids),
								   root->rtable),
						  lconsi(attno, NIL),
						  lconsi(value, NIL),
						  lconsi(flag, NIL),
						  1,
						  &npages,
						  &selec);

		subcost = cost_index((Oid) lfirsti(index->relids),
							 (int) npages,
							 (Cost) selec,
							 rel->pages,
							 rel->tuples,
							 index->pages,
							 index->tuples,
							 false);
		best_or_subclause_index(root,
								rel,
								subclause,
								lnext(indices),
								&subclause_indexid,
								&subclause_cost,
								&subclause_selec);

		if (subclause_indexid == 0 || subcost < subclause_cost)
		{
			*retIndexid = lfirsti(index->relids);
			*retCost = subcost;
			*retSelec = selec;
		}
		else
		{
			*retIndexid = 0;
			*retCost = 0.0;
			*retSelec = 0.0;
		}
	}
	return;
}
