/*-------------------------------------------------------------------------
 *
 * indxpath.c--
 *    Routines to determine which indices are usable for scanning a
 *    given relation
 *
 * Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *    $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.8 1997/08/12 22:53:12 momjian Exp $
 *
 *-------------------------------------------------------------------------
 */
#include <math.h>
#include "postgres.h"
#include "access/attnum.h"
#include "access/heapam.h"
#include "access/nbtree.h"

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

#include "utils/lsyscache.h"
#include "utils/elog.h"

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


#include "catalog/catname.h"
#include "catalog/pg_amop.h"
#include "catalog/pg_proc.h"

#include "executor/executor.h"
#include "parser/parsetree.h"		/* for getrelid() */


static void match_index_orclauses(Rel *rel, Rel *index, int indexkey,
    int xclass, List *clauseinfo_list);
static bool match_index_to_operand(int indexkey, Expr *operand,
    Rel *rel, Rel *index);
static List *match_index_orclause(Rel *rel, Rel *index, int indexkey,
    int xclass, List *or_clauses, List *other_matching_indices);
static List *group_clauses_by_indexkey(Rel *rel, Rel *index,
    int *indexkeys, Oid *classes, List *clauseinfo_list);
static List *group_clauses_by_ikey_for_joins(Rel *rel, Rel *index,
    int *indexkeys, Oid *classes, List *join_cinfo_list, List *restr_cinfo_list);
static CInfo *match_clause_to_indexkey(Rel *rel, Rel *index, int indexkey,
    int xclass, CInfo *clauseInfo, bool join);
static bool pred_test(List *predicate_list, List *clauseinfo_list,
    List *joininfo_list);
static bool one_pred_test(Expr *predicate, List *clauseinfo_list);
static bool one_pred_clause_expr_test(Expr *predicate, Node *clause);
static bool one_pred_clause_test(Expr *predicate, Node *clause);
static bool clause_pred_clause_test(Expr *predicate, Node *clause);
static List *indexable_joinclauses (Rel *rel, Rel *index, 
		List *joininfo_list, List *clauseinfo_list);
static List *index_innerjoin(Query *root, Rel *rel, 
			     List *clausegroup_list, Rel *index);
static List *create_index_paths(Query *root, Rel *rel, Rel *index,
    List *clausegroup_list, bool join);
static List *add_index_paths(List *indexpaths, List *new_indexpaths);
static bool function_index_operand(Expr *funcOpnd, Rel *rel, Rel *index);
static bool SingleAttributeIndex(Rel *index);

/* If Spyros can use a constant PRS2_BOOL_TYPEID, I can use this */
#define BOOL_TYPEID ((Oid) 16)

/*    
 * find-index-paths--
 *    Finds all possible index paths by determining which indices in the
 *    list 'indices' are usable.
 *    
 *    To be usable, an index must match against either a set of 
 *    restriction clauses or join clauses.
 *    
 *    Note that the current implementation requires that there exist
 *    matching clauses for every key in the index (i.e., no partial
 *    matches are allowed).
 *    
 *    If an index can't be used with restriction clauses, but its keys
 *    match those of the result sort order (according to information stored
 *    within 'sortkeys'), then the index is also considered. 
 *
 * 'rel' is the relation entry to which these index paths correspond
 * 'indices' is a list of possible index paths
 * 'clauseinfo-list' is a list of restriction clauseinfo nodes for 'rel'
 * 'joininfo-list' is a list of joininfo nodes for 'rel'
 * 'sortkeys' is a node describing the result sort order (from
 * 	(find_sortkeys))
 *    
 * Returns a list of index nodes.
 *    
 */
List *
find_index_paths (Query *root,
		  Rel *rel,
		  List *indices,
		  List *clauseinfo_list,
		  List *joininfo_list)
{
    List *scanclausegroups = NIL;
    List *scanpaths = NIL;
    Rel *index = (Rel *)NULL;
    List *joinclausegroups = NIL;
    List *joinpaths = NIL;
    List *retval = NIL;
    
    if(indices == NIL)
	return(NULL);
	
    index = (Rel*)lfirst (indices);

    retval = find_index_paths(root,
			      rel,
			      lnext (indices),
			      clauseinfo_list,
			      joininfo_list);

    /* If this is a partial index, return if it fails the predicate test */
    if (index->indpred != NIL)
	if (!pred_test(index->indpred, clauseinfo_list, joininfo_list))
	    return retval;

    /*  1. If this index has only one key, try matching it against 
     * subclauses of an 'or' clause.  The fields of the clauseinfo
     * nodes are marked with lists of the matching indices no path
     * are actually created. 
     *
     * XXX NOTE:  Currently btrees dos not support indices with
     * > 1 key, so the following test will always be true for
     * now but we have decided not to support index-scans 
     * on disjunction . -- lp
     */
    if (SingleAttributeIndex(index)) 
	{
	    match_index_orclauses (rel,
				   index,
				   index->indexkeys[0],
				   index->classlist[0],
				   clauseinfo_list);
	}

    /*
     * 2. If the keys of this index match any of the available
     * restriction clauses, then create pathnodes corresponding
     * to each group of usable clauses.
     */
    scanclausegroups = group_clauses_by_indexkey(rel,
						 index,
						 index->indexkeys,
						 index->classlist,
						 clauseinfo_list);
    
    scanpaths = NIL;
    if (scanclausegroups != NIL)
	scanpaths = create_index_paths (root, 
					rel,
					index,
					scanclausegroups,
					false);
	     
    /*
     * 3. If this index can be used with any join clause, then 
     * create pathnodes for each group of usable clauses.  An 
     * index can be used with a join clause if its ordering is 
     * useful for a mergejoin, or if the index can possibly be 
     * used for scanning the inner relation of a nestloop join. 
     */
    joinclausegroups = indexable_joinclauses(rel,index,joininfo_list, clauseinfo_list);
    joinpaths = NIL;

    if (joinclausegroups != NIL)
	{
	    List *new_join_paths = create_index_paths(root, rel,
							  index,
							  joinclausegroups, 
							  true);
	    List *innerjoin_paths = index_innerjoin(root, rel,joinclausegroups,index);

	    rel->innerjoin = nconc (rel->innerjoin, innerjoin_paths);
	    joinpaths = new_join_paths;
	}
	     
    /*
     *  Some sanity checks to make sure that
     *  the indexpath is valid.
     */
    if (joinpaths!=NULL)
	retval = add_index_paths(joinpaths,retval);
    if (scanpaths!=NULL)
	retval = add_index_paths(scanpaths,retval);
	
    return retval;

}


/****************************************************************************
 *	----  ROUTINES TO MATCH 'OR' CLAUSES  ----   
 ****************************************************************************/


/*    
 * match-index-orclauses--
 *    Attempt to match an index against subclauses within 'or' clauses.
 *    If the index does match, then the clause is marked with information
 *    about the index.
 *    
 *    Essentially, this adds 'index' to the list of indices in the
 *    ClauseInfo field of each of the clauses which it matches.
 *    
 * 'rel' is the node of the relation on which the index is defined.
 * 'index' is the index node.
 * 'indexkey' is the (single) key of the index
 * 'class' is the class of the operator corresponding to 'indexkey'.
 * 'clauseinfo-list' is the list of available restriction clauses.
 *    
 * Returns nothing.
 *    
 */
static void
match_index_orclauses(Rel *rel,
		      Rel *index,
		      int indexkey,
		      int xclass,
		      List *clauseinfo_list)
{
    CInfo *clauseinfo = (CInfo*)NULL;
    List *i = NIL;

    foreach (i, clauseinfo_list) {
	clauseinfo = (CInfo*)lfirst(i);
	if (valid_or_clause(clauseinfo)) {
	    
	    /* Mark the 'or' clause with a list of indices which 
	     * match each of its subclauses.  The list is
	     * generated by adding 'index' to the existing
	     * list where appropriate.
	     */
	    clauseinfo->indexids =
		match_index_orclause (rel,index,indexkey,
				      xclass,
				      clauseinfo->clause->args,
				      clauseinfo->indexids);
	}
    }
}

/*
 * match_index_operand--
 *    Generalize test for a match between an existing index's key
 *    and the operand on the rhs of a restriction clause.  Now check
 *    for functional indices as well.
 */
static bool
match_index_to_operand(int indexkey,
		       Expr *operand,
		       Rel *rel,
		       Rel *index)
{
    /*
     * Normal index.
     */
    if (index->indproc == InvalidOid)
	return match_indexkey_operand(indexkey, (Var*)operand, rel);

    /*
     * functional index check
     */
    return (function_index_operand(operand, rel, index));
}

/*    
 * match-index-orclause--
 *    Attempts to match an index against the subclauses of an 'or' clause.
 *    
 *    A match means that:
 *    (1) the operator within the subclause can be used with one
 *        	of the index's operator classes, and
 *    (2) there is a usable key that matches the variable within a
 *         	sargable clause.
 *    
 * 'or-clauses' are the remaining subclauses within the 'or' clause
 * 'other-matching-indices' is the list of information on other indices
 * 	that have already been matched to subclauses within this
 * 	particular 'or' clause (i.e., a list previously generated by
 * 	this routine)
 *    
 * Returns a list of the form ((a b c) (d e f) nil (g h) ...) where
 * a,b,c are nodes of indices that match the first subclause in
 * 'or-clauses', d,e,f match the second subclause, no indices
 * match the third, g,h match the fourth, etc.
 */
static List *
match_index_orclause(Rel *rel,
		     Rel *index,
		     int indexkey,
		     int xclass,
		     List *or_clauses,
		     List *other_matching_indices)
{
    Node *clause = NULL;
    List *matched_indices  = other_matching_indices;
    List *index_list = NIL;
    List *clist;
    List *ind;

    if (!matched_indices)
	matched_indices = lcons(NIL, NIL);

    for (clist = or_clauses, ind = matched_indices; 
	 clist; 
	 clist = lnext(clist), ind = lnext(ind))
	{
	    clause = lfirst(clist);
	    if (is_opclause (clause) && 
		op_class(((Oper*)((Expr*)clause)->oper)->opno,
			 xclass, index->relam) && 
		match_index_to_operand(indexkey,
				       (Expr*)get_leftop((Expr*)clause),
				       rel,
				       index) &&
		IsA(get_rightop((Expr*)clause),Const)) {
	  
		matched_indices =  lcons(index, matched_indices);
		index_list = lappend(index_list,
				      matched_indices);
	    }
	}
    return(index_list);
     
}

/****************************************************************************
 *    		----  ROUTINES TO CHECK RESTRICTIONS  ---- 	 
 ****************************************************************************/


/*
 * DoneMatchingIndexKeys() - MACRO
 *
 * Determine whether we should continue matching index keys in a clause.
 * Depends on if there are more to match or if this is a functional index.
 * In the latter case we stop after the first match since the there can
 * be only key (i.e. the function's return value) and the attributes in
 * keys list represent the arguments to the function.  -mer 3 Oct. 1991
 */
#define DoneMatchingIndexKeys(indexkeys, index) \
	(indexkeys[0] == 0 || \
	 (index->indproc != InvalidOid))

/*    
 * group-clauses-by-indexkey--
 *    Determines whether there are clauses which will match each and every
 *    one of the remaining keys of an index.  
 *    
 * 'rel' is the node of the relation corresponding to the index.
 * 'indexkeys' are the remaining index keys to be matched.
 * 'classes' are the classes of the index operators on those keys.
 * 'clauses' is either:
 * 	(1) the list of available restriction clauses on a single
 * 		relation, or
 * 	(2) a list of join clauses between 'rel' and a fixed set of
 * 		relations,
 * 	depending on the value of 'join'.
 *
 *	NOTE: it works now for restriction clauses only. - vadim 03/18/97
 *    
 * Returns all possible groups of clauses that will match (given that
 * one or more clauses can match any of the remaining keys).
 * E.g., if you have clauses A, B, and C, ((A B) (A C)) might be 
 * returned for an index with 2 keys.
 *    
 */
static List *
group_clauses_by_indexkey(Rel *rel,
			  Rel *index,
			  int *indexkeys,
			  Oid *classes,
			  List *clauseinfo_list)
{
    List *curCinfo		= NIL;
    CInfo *matched_clause	= (CInfo*)NULL;
    List *clausegroup	= NIL;
    int curIndxKey;
    Oid curClass;

    if (clauseinfo_list == NIL)
	return NIL;

    while ( !DoneMatchingIndexKeys(indexkeys, index) )
    {
    	List *tempgroup = NIL;
    	
    	curIndxKey = indexkeys[0];
	curClass = classes[0];

    	foreach (curCinfo,clauseinfo_list) 
    	{
	    CInfo *temp	= (CInfo*)lfirst(curCinfo);

	    matched_clause = match_clause_to_indexkey (rel, 
						       index, 
						       curIndxKey,
						       curClass,
						       temp,
						       false);
	    if (!matched_clause)
		continue;

	    tempgroup = lappend(tempgroup, matched_clause);
	}
	if ( tempgroup == NIL )
	    break;

	clausegroup = nconc (clausegroup, tempgroup);
	
	indexkeys++;
	classes++;
	
    }

    /* clausegroup holds all matched clauses ordered by indexkeys */

    if (clausegroup != NIL)
	return(lcons(clausegroup, NIL));
    return NIL;
}

/*    
 * group-clauses-by-ikey-for-joins--
 *    special edition of group-clauses-by-indexkey - will
 *    match join & restriction clauses. See comment in indexable_joinclauses.
 *	- vadim 03/18/97
 *    
 */
static List *
group_clauses_by_ikey_for_joins(Rel *rel,
			  Rel *index,
			  int *indexkeys,
			  Oid *classes,
			  List *join_cinfo_list,
			  List *restr_cinfo_list)
{
    List *curCinfo		= NIL;
    CInfo *matched_clause	= (CInfo*)NULL;
    List *clausegroup	= NIL;
    int curIndxKey;
    Oid curClass;
    bool jfound = false;

    if (join_cinfo_list == NIL)
	return NIL;

    while ( !DoneMatchingIndexKeys(indexkeys, index) )
    {
    	List *tempgroup = NIL;
    	
    	curIndxKey = indexkeys[0];
	curClass = classes[0];

    	foreach (curCinfo,join_cinfo_list) 
    	{
	    CInfo *temp	= (CInfo*)lfirst(curCinfo);

	    matched_clause = match_clause_to_indexkey (rel, 
						       index, 
						       curIndxKey,
						       curClass,
						       temp,
						       true);
	    if (!matched_clause)
		continue;

	    tempgroup = lappend(tempgroup, matched_clause);
	    jfound = true;
	}
    	foreach (curCinfo,restr_cinfo_list) 
    	{
	    CInfo *temp	= (CInfo*)lfirst(curCinfo);

	    matched_clause = match_clause_to_indexkey (rel, 
						       index, 
						       curIndxKey,
						       curClass,
						       temp,
						       false);
	    if (!matched_clause)
		continue;

	    tempgroup = lappend(tempgroup, matched_clause);
	}
	if ( tempgroup == NIL )
	    break;

	clausegroup = nconc (clausegroup, tempgroup);
	
	indexkeys++;
	classes++;
	
    }

    /* clausegroup holds all matched clauses ordered by indexkeys */

    if (clausegroup != NIL)
    {
	/* 
	 * if no one join clause was matched then there ain't clauses
	 * for joins at all.
	 */
    	if ( !jfound )
    	{
    	    freeList (clausegroup);
    	    return NIL;
    	}
	return(lcons(clausegroup, NIL));
    }
    return NIL;
}

/*
 * IndexScanableClause ()  MACRO
 *
 * Generalize condition on which we match a clause with an index.
 * Now we can match with functional indices.
 */
#define IndexScanableOperand(opnd, indkeys, rel, index) \
    ((index->indproc == InvalidOid) ? \
	match_indexkey_operand(indkeys, opnd, rel) : \
	function_index_operand((Expr*)opnd,rel,index))

/*
 * There was
 *	equal_indexkey_var(indkeys,opnd) : \
 * above, and now
 *	match_indexkey_operand(indkeys, opnd, rel) : \
 * - vadim 01/22/97
 */

/*    
 * match_clause_to-indexkey--
 *    Finds the first of a relation's available restriction clauses that
 *    matches a key of an index.
 *    
 *    To match, the clause must:
 *    (1) be in the form (op var const) if the clause is a single-
 *    		relation clause, and
 *    (2) contain an operator which is in the same class as the index
 *    		operator for this key.
 *    
 *    If the clause being matched is a join clause, then 'join' is t.
 *    
 * Returns a single clauseinfo node corresponding to the matching 
 * clause.
 *
 * NOTE:  returns nil if clause is an or_clause.
 *    
 */
static CInfo *
match_clause_to_indexkey(Rel *rel,
			 Rel *index,
			 int indexkey,
			 int xclass,
			 CInfo *clauseInfo,
			 bool join)
{
    Expr *clause = clauseInfo->clause;
    Var *leftop, *rightop;
    Oid join_op = InvalidOid;
    Oid restrict_op = InvalidOid;
    bool isIndexable = false;

    if (or_clause((Node*)clause) ||
	not_clause((Node*)clause) || single_node((Node*)clause))
	return ((CInfo*)NULL);

    leftop = get_leftop(clause);
    rightop = get_rightop(clause);
    /*
     * If this is not a join clause, check for clauses of the form:
     * (operator var/func constant) and (operator constant var/func)
     */
    if (!join) 
    {
	/*
	 * Check for standard s-argable clause
	 */
#ifdef INDEXSCAN_PATCH
	    /* Handle also function parameters.  DZ - 27-8-1996 */ 
	if ((rightop && IsA(rightop,Const)) ||
		(rightop && IsA(rightop,Param)))
#else
	if (rightop && IsA(rightop,Const))
#endif
	{
	    restrict_op = ((Oper*)((Expr*)clause)->oper)->opno;
	    isIndexable =
		( op_class(restrict_op, xclass, index->relam) &&
			 IndexScanableOperand(leftop,
					      indexkey,
					      rel,
					      index) );
	}

	/*
	 * Must try to commute the clause to standard s-arg format.
	 */
#ifdef INDEXSCAN_PATCH
	    /* ...And here...  - vadim 01/22/97 */ 
	else if ((leftop && IsA(leftop,Const)) ||
			(leftop && IsA(leftop,Param)))
#else
	else if (leftop && IsA(leftop,Const))
#endif
	{
	    restrict_op =
		get_commutator(((Oper*)((Expr*)clause)->oper)->opno);

	    if ( (restrict_op != InvalidOid) &&
			op_class(restrict_op, xclass, index->relam) &&
			IndexScanableOperand(rightop,
					     indexkey,rel,index) )
	    {
		isIndexable = true;
		/*
		 * In place list modification.
		 * (op const var/func) -> (op var/func const)
		 */
		CommuteClause((Node*)clause);
	    }
	}
    } 
    /*
     * Check for an indexable scan on one of the join relations.
     * clause is of the form (operator var/func var/func)
     */
    else
    {
	if (rightop
		&& match_index_to_operand(indexkey,(Expr*)rightop,rel,index))
	{
					
		join_op = get_commutator(((Oper*)((Expr*)clause)->oper)->opno);

	} else if (leftop
		       && match_index_to_operand(indexkey,
						 (Expr*)leftop,rel,index))
	{
		join_op = ((Oper*)((Expr*)clause)->oper)->opno;
	}

	if ( join_op && op_class(join_op,xclass,index->relam) &&
		join_clause_p((Node*)clause))
	{
	    isIndexable = true;

	    /*
	     * If we're using the operand's commutator we must
	     * commute the clause.
	     */
	    if (join_op != ((Oper*)((Expr*)clause)->oper)->opno)
			CommuteClause((Node*)clause);
	}
    }

    if (isIndexable)
	return(clauseInfo);

    return(NULL);
}

/****************************************************************************
 *    		----  ROUTINES TO DO PARTIAL INDEX PREDICATE TESTS  ----  
 ****************************************************************************/

/*    
 * pred_test--
 *    Does the "predicate inclusion test" for partial indexes.
 *
 *    Recursively checks whether the clauses in clauseinfo_list imply
 *    that the given predicate is true.
 *
 *    This routine (together with the routines it calls) iterates over
 *    ANDs in the predicate first, then reduces the qualification
 *    clauses down to their constituent terms, and iterates over ORs
 *    in the predicate last.  This order is important to make the test
 *    succeed whenever possible (assuming the predicate has been
 *    successfully cnfify()-ed). --Nels, Jan '93
 */
static bool
pred_test(List *predicate_list, List *clauseinfo_list, List *joininfo_list)
{
    List *pred, *items, *item;

    /*
     * Note: if Postgres tried to optimize queries by forming equivalence
     * classes over equi-joined attributes (i.e., if it recognized that a
     * qualification such as "where a.b=c.d and a.b=5" could make use of
     * an index on c.d), then we could use that equivalence class info
     * here with joininfo_list to do more complete tests for the usability
     * of a partial index.  For now, the test only uses restriction
     * clauses (those in clauseinfo_list). --Nels, Dec '92
     */

    if (predicate_list == NULL)
	return true;	/* no predicate: the index is usable */
    if (clauseinfo_list == NULL)
	return false;	/* no restriction clauses: the test must fail */

    foreach (pred, predicate_list) {
	/* if any clause is not implied, the whole predicate is not implied */
	if (and_clause(lfirst(pred))) {
	    items = ((Expr*)lfirst(pred))->args;
	    foreach (item, items) {
		if (!one_pred_test(lfirst(item), clauseinfo_list))
		    return false;
	    }
	}
	else if (!one_pred_test(lfirst(pred), clauseinfo_list))
	    return false;
    }
    return true;
}


/*    
 * one_pred_test--
 *    Does the "predicate inclusion test" for one conjunct of a predicate
 *    expression.
 */
static bool
one_pred_test(Expr *predicate, List *clauseinfo_list)
{
    CInfo *clauseinfo;
    List *item;

    Assert(predicate != NULL);
    foreach (item, clauseinfo_list) {
	clauseinfo = (CInfo *)lfirst(item);
	/* if any clause implies the predicate, return true */
	if (one_pred_clause_expr_test(predicate, (Node*)clauseinfo->clause))
	    return true;
    }
    return false;
}


/*    
 * one_pred_clause_expr_test--
 *    Does the "predicate inclusion test" for a general restriction-clause
 *    expression.
 */
static bool
one_pred_clause_expr_test(Expr *predicate, Node *clause)
{
    List *items, *item;

    if (is_opclause(clause))
	return one_pred_clause_test(predicate, clause);
    else if (or_clause(clause)) {
	items = ((Expr*)clause)->args;
	foreach (item, items) {
	    /* if any OR item doesn't imply the predicate, clause doesn't */
	    if (!one_pred_clause_expr_test(predicate, lfirst(item)))
		return false;
	}
	return true;
    }else if (and_clause(clause)) {
	items = ((Expr*)clause)->args;
	foreach (item, items) {
	    /* if any AND item implies the predicate, the whole clause does */
	    if (one_pred_clause_expr_test(predicate, lfirst(item)))
		return true;
	}
	return false;
    }else {
	/* unknown clause type never implies the predicate */ 
	return false;
    }
}


/*    
 * one_pred_clause_test--
 *    Does the "predicate inclusion test" for one conjunct of a predicate
 *    expression for a simple restriction clause.
 */
static bool
one_pred_clause_test(Expr *predicate, Node *clause)
{
    List *items, *item;

    if (is_opclause((Node*)predicate))
	return clause_pred_clause_test(predicate, clause);
    else if (or_clause((Node*)predicate)) {
	items = predicate->args;
	foreach (item, items) {
	    /* if any item is implied, the whole predicate is implied */
	    if (one_pred_clause_test(lfirst(item), clause))
		return true;
	}
	return false;
    }else if (and_clause((Node*)predicate)) {
	items = predicate->args;
	foreach (item, items) {
	    /*
	     * if any item is not implied, the whole predicate is not
	     * implied
	     */
	    if (!one_pred_clause_test(lfirst(item), clause))
		return false;
	}
	return true;
    }
    else {
	elog(DEBUG, "Unsupported predicate type, index will not be used");
	return false;
    }
}


/*
 * Define an "operator implication table" for btree operators ("strategies").
 * The "strategy numbers" are:  (1) <   (2) <=   (3) =   (4) >=   (5) >
 *
 * The interpretation of:
 *
 *	test_op = BT_implic_table[given_op-1][target_op-1]
 *
 * where test_op, given_op and target_op are strategy numbers (from 1 to 5)
 * of btree operators, is as follows:
 *
 *   If you know, for some ATTR, that "ATTR given_op CONST1" is true, and you
 *   want to determine whether "ATTR target_op CONST2" must also be true, then
 *   you can use "CONST1 test_op CONST2" as a test.  If this test returns true,
 *   then the target expression must be true; if the test returns false, then
 *   the target expression may be false.
 *
 * An entry where test_op==0 means the implication cannot be determined, i.e.,
 * this test should always be considered false.
 */

StrategyNumber BT_implic_table[BTMaxStrategyNumber][BTMaxStrategyNumber] = {
    {2, 2, 0, 0, 0},
    {1, 2, 0, 0, 0},
    {1, 2, 3, 4, 5},
    {0, 0, 0, 4, 5},
    {0, 0, 0, 4, 4}
};


/*    
 * clause_pred_clause_test--
 *    Use operator class info to check whether clause implies predicate.
 *    
 *    Does the "predicate inclusion test" for a "simple clause" predicate
 *    for a single "simple clause" restriction.  Currently, this only handles
 *    (binary boolean) operators that are in some btree operator class.
 *    Eventually, rtree operators could also be handled by defining an
 *    appropriate "RT_implic_table" array.
 */
static bool
clause_pred_clause_test(Expr *predicate, Node *clause)
{
    Var		*pred_var, *clause_var;
    Const	*pred_const, *clause_const;
    Oid		pred_op, clause_op, test_op;
    Oid		opclass_id;
    StrategyNumber	pred_strategy, clause_strategy, test_strategy;
    Oper	*test_oper;
    Expr	*test_expr;
    bool	test_result, isNull;
    Relation	relation;
    HeapScanDesc  scan;
    HeapTuple	tuple;
    ScanKeyData	entry[3];
    Form_pg_amop form;

    pred_var = (Var*)get_leftop(predicate);
    pred_const = (Const*)get_rightop(predicate);
    clause_var = (Var*)get_leftop((Expr*)clause);
    clause_const = (Const*)get_rightop((Expr*)clause);

    /* Check the basic form; for now, only allow the simplest case */
    if (!is_opclause(clause) ||
	!IsA(clause_var,Var) ||
	!IsA(clause_const,Const) ||
	!IsA(predicate->oper,Oper) ||
	!IsA(pred_var,Var) ||
	!IsA(pred_const,Const)) {
	return false;
    }

    /*
     * The implication can't be determined unless the predicate and the clause
     * refer to the same attribute.
     */
    if (clause_var->varattno != pred_var->varattno)
	return false;

    /* Get the operators for the two clauses we're comparing */
    pred_op = ((Oper*)((Expr*)predicate)->oper)->opno;
    clause_op = ((Oper*)((Expr*)clause)->oper)->opno;


    /*
     * 1. Find a "btree" strategy number for the pred_op
     */
    /* XXX - hardcoded amopid value 403 to find "btree" operator classes */
    ScanKeyEntryInitialize(&entry[0], 0,
			   Anum_pg_amop_amopid,
			   ObjectIdEqualRegProcedure,
			   ObjectIdGetDatum(403));

    ScanKeyEntryInitialize(&entry[1], 0,
			   Anum_pg_amop_amopopr,
			   ObjectIdEqualRegProcedure,
			   ObjectIdGetDatum(pred_op));

    relation = heap_openr(AccessMethodOperatorRelationName);

    /*
     * The following assumes that any given operator will only be in a single
     * btree operator class.  This is true at least for all the pre-defined
     * operator classes.  If it isn't true, then whichever operator class
     * happens to be returned first for the given operator will be used to
     * find the associated strategy numbers for the test. --Nels, Jan '93
     */
    scan = heap_beginscan(relation, false, NowTimeQual, 2, entry);
    tuple = heap_getnext(scan, false, (Buffer *)NULL);
    if (! HeapTupleIsValid(tuple)) {
	elog(DEBUG, "clause_pred_clause_test: unknown pred_op");
	return false;
    }
    form = (Form_pg_amop) GETSTRUCT(tuple);

    /* Get the predicate operator's strategy number (1 to 5) */
    pred_strategy = (StrategyNumber)form->amopstrategy;

    /* Remember which operator class this strategy number came from */
    opclass_id = form->amopclaid;

    heap_endscan(scan);


    /*
     * 2. From the same opclass, find a strategy num for the clause_op
     */
    ScanKeyEntryInitialize(&entry[1], 0,
			   Anum_pg_amop_amopclaid,
			   ObjectIdEqualRegProcedure,
			   ObjectIdGetDatum(opclass_id));

    ScanKeyEntryInitialize(&entry[2], 0,
			   Anum_pg_amop_amopopr,
			   ObjectIdEqualRegProcedure,
			   ObjectIdGetDatum(clause_op));

    scan = heap_beginscan(relation, false, NowTimeQual, 3, entry);
    tuple = heap_getnext(scan, false, (Buffer *)NULL);
    if (! HeapTupleIsValid(tuple)) {
	elog(DEBUG, "clause_pred_clause_test: unknown clause_op");
	return false;
    }
    form = (Form_pg_amop) GETSTRUCT(tuple);

    /* Get the restriction clause operator's strategy number (1 to 5) */
    clause_strategy = (StrategyNumber)form->amopstrategy;
    heap_endscan(scan);


    /*
     * 3. Look up the "test" strategy number in the implication table
     */

    test_strategy = BT_implic_table[clause_strategy-1][pred_strategy-1];
    if (test_strategy == 0)
	return false;		/* the implication cannot be determined */


    /*
     * 4. From the same opclass, find the operator for the test strategy
     */

    ScanKeyEntryInitialize(&entry[2], 0,
			   Anum_pg_amop_amopstrategy,
			   Integer16EqualRegProcedure,
			   Int16GetDatum(test_strategy));

    scan = heap_beginscan(relation, false, NowTimeQual, 3, entry);
    tuple = heap_getnext(scan, false, (Buffer *)NULL);
    if (! HeapTupleIsValid(tuple)) {
	elog(DEBUG, "clause_pred_clause_test: unknown test_op");
	return false;
    }
    form = (Form_pg_amop) GETSTRUCT(tuple);

    /* Get the test operator */
    test_op = form->amopopr;
    heap_endscan(scan);


    /*
     * 5. Evaluate the test
     */
    test_oper = makeOper(test_op, /* opno */
			 InvalidOid, /* opid */
			 BOOL_TYPEID, /* opresulttype */
			 0,	/* opsize */
			 NULL);	/* op_fcache */
    replace_opid(test_oper);

    test_expr = make_opclause(test_oper,
			      copyObject(clause_const),
			      copyObject(pred_const));

#ifndef OMIT_PARTIAL_INDEX
    test_result = ExecEvalExpr((Node*)test_expr, NULL, &isNull, NULL);
#endif /* OMIT_PARTIAL_INDEX */    	
    if (isNull) {
	elog(DEBUG, "clause_pred_clause_test: null test result");
	return false;
    }
    return test_result;
}


/****************************************************************************
 *    		----  ROUTINES TO CHECK JOIN CLAUSES  ---- 	 
 ****************************************************************************/

/*    
 * indexable-joinclauses--
 *    Finds all groups of join clauses from among 'joininfo-list' that can 
 *    be used in conjunction with 'index'.
 *    
 *    The first clause in the group is marked as having the other relation
 *    in the join clause as its outer join relation.
 *    
 * Returns a list of these clause groups.
 *
 *    Added: clauseinfo_list - list of restriction CInfos. It's to
 *	support multi-column indices in joins and for cases
 *	when a key is in both join & restriction clauses. - vadim 03/18/97
 *    
 */
static List *
indexable_joinclauses(Rel *rel, Rel *index, 
			List *joininfo_list, List *clauseinfo_list)
{
    JInfo *joininfo = (JInfo*)NULL;
    List *cg_list = NIL;
    List *i = NIL;
    List *clausegroups = NIL;

    foreach(i,joininfo_list) { 
	joininfo = (JInfo*)lfirst(i);
	
	if ( joininfo->jinfoclauseinfo == NIL )
	    continue;
	clausegroups = 
	    group_clauses_by_ikey_for_joins (rel,
				       index,
				       index->indexkeys,
				       index->classlist,
				       joininfo->jinfoclauseinfo,
				       clauseinfo_list);

	if (clausegroups != NIL) {
	    List *clauses = lfirst(clausegroups);
	    
	    ((CInfo*)lfirst(clauses))->cinfojoinid =
		joininfo->otherrels;
	}
	cg_list = nconc(cg_list,clausegroups);
    }
    return(cg_list);
}

/****************************************************************************
 *    		----  PATH CREATION UTILITIES  ----
 ****************************************************************************/

/*
 * extract_restrict_clauses -
 *    the list of clause info contains join clauses and restriction clauses.
 *    This routine returns the restriction clauses only.
 */
#ifdef NOT_USED
static List *
extract_restrict_clauses(List *clausegroup)
{
    List *restrict_cls = NIL;
    List *l;
    
    foreach (l, clausegroup) {
	CInfo *cinfo = lfirst(l);

	if (!join_clause_p((Node*)cinfo->clause)) {
	    restrict_cls = lappend(restrict_cls, cinfo);
	}
    }
    return restrict_cls;
}
#endif

/*    
 * index-innerjoin--
 *    Creates index path nodes corresponding to paths to be used as inner
 *    relations in nestloop joins.
 *
 * 'clausegroup-list' is a list of list of clauseinfo nodes which can use
 * 'index' on their inner relation.
 *    
 * Returns a list of index pathnodes.
 *    
 */
static List *
index_innerjoin(Query *root, Rel *rel, List *clausegroup_list, Rel *index)
{
    List *clausegroup = NIL;
    List *cg_list = NIL;
    List *i = NIL;
    IndexPath *pathnode = (IndexPath*)NULL;
    Cost  	temp_selec;
    float	temp_pages;

    foreach(i,clausegroup_list) {
	List *attnos, *values, *flags;

	clausegroup = lfirst(i);
	pathnode = makeNode(IndexPath);

	get_joinvars(lfirsti(rel->relids),clausegroup,
		     &attnos, &values, &flags);
	index_selectivity(lfirsti(index->relids),
			  index->classlist,
			  get_opnos(clausegroup),
			  getrelid(lfirsti(rel->relids),
				   root->rtable),
			  attnos,
			  values,
			  flags,
			  length(clausegroup),
			  &temp_pages,
			  &temp_selec);
	pathnode->path.pathtype = T_IndexScan;
	pathnode->path.parent = rel;
	pathnode->indexid = index->relids;
	pathnode->indexkeys = index->indexkeys;
	pathnode->indexqual = clausegroup;

	pathnode->path.joinid = ((CInfo*)lfirst(clausegroup))->cinfojoinid;

	pathnode->path.path_cost =
	    cost_index((Oid)lfirsti(index->relids),
		       (int)temp_pages,
		       temp_selec,
		       rel->pages,
		       rel->tuples,
		       index->pages,
		       index->tuples,
		       true);

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

#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
	cg_list = lappend(cg_list,pathnode);
    }
    return(cg_list);
}

/*    
 * create-index-paths--
 *    Creates a list of index path nodes for each group of clauses
 *    (restriction or join) that can be used in conjunction with an index.
 *    
 * 'rel' is the relation for which 'index' is defined
 * 'clausegroup-list' is the list of clause groups (lists of clauseinfo 
 *    		nodes) grouped by mergesortorder
 * 'join' is a flag indicating whether or not the clauses are join
 *   		clauses
 *    
 * Returns a list of new index path nodes.
 *    
 */
static List *
create_index_paths(Query *root, 
		   Rel *rel,
		   Rel *index,
		   List *clausegroup_list,
		   bool join)
{
    List *clausegroup = NIL;
    List *ip_list = NIL;
    List *i = NIL;
    List *j = NIL;
    IndexPath  *temp_path;

    foreach(i, clausegroup_list) {
	CInfo *clauseinfo;
	List *temp_node = NIL;
	bool temp = true;

	clausegroup = lfirst(i);

	foreach (j,clausegroup) {
	    clauseinfo = (CInfo*)lfirst(j); 
	    if (!(join_clause_p((Node*)clauseinfo->clause) &&
		  equal_path_merge_ordering(index->ordering,
					    clauseinfo->mergesortorder))) {
		temp = false;
	    }
	}

	if (!join || temp) {	/* restriction, ordering scan */
	    temp_path = create_index_path (root, rel,index,clausegroup,join);
	    temp_node = 
		lcons(temp_path, NIL);
	    ip_list = nconc(ip_list,temp_node);
	} 
    }
    return(ip_list);
}

static List *
add_index_paths(List *indexpaths, List *new_indexpaths)
{
    return append(indexpaths, new_indexpaths); 
}

static bool
function_index_operand(Expr *funcOpnd, Rel *rel, Rel *index)
{
    Oid heapRelid	= (Oid)lfirsti(rel->relids);
    Func *function;
    List *funcargs;
    int *indexKeys	= index->indexkeys;
    List *arg;
    int i;

    /*
     * sanity check, make sure we know what we're dealing with here.
     */
    if (funcOpnd==NULL ||
	nodeTag(funcOpnd)!=T_Expr || funcOpnd->opType!=FUNC_EXPR ||
	funcOpnd->oper==NULL || indexKeys==NULL)
	return false;

    function = (Func*)funcOpnd->oper;
    funcargs = funcOpnd->args;

    if (function->funcid != index->indproc)
	return false;

    /*
     * Check that the arguments correspond to the same arguments used
     * to create the functional index.  To do this we must check that
     * 1. refer to the right relatiion. 
     * 2. the args have the right attr. numbers in the right order.
     *
     *
     * Check all args refer to the correct relation (i.e. the one with
     * the functional index defined on it (rel).  To do this we can
     * simply compare range table entry numbers, they must be the same.
     */
    foreach (arg, funcargs) {
	if (heapRelid != ((Var*)lfirst(arg))->varno)
	    return false;
    }

    /*
     * check attr numbers and order.
     */
    i = 0;
    foreach (arg, funcargs) {

	if (indexKeys[i]==0)
	    return (false);

	if (((Var*)lfirst(arg))->varattno != indexKeys[i])
	    return (false);

	i++;
    }

    return true;
}

static bool
SingleAttributeIndex(Rel *index)
{
    /*
     * return false for now as I don't know if we support index scans
     * on disjunction and the code doesn't work
     */
    return (false);

#if 0
    /*
     * Non-functional indices.
     */
    if (index->indproc == InvalidOid)
	return (index->indexkeys[0] != 0 &&
		index->indexkeys[1] == 0);
	
    /*
     * We have a functional index which is a single attr index
     */
    return true;
#endif
}
