/* 
 * FILE
 * 	clausesel 
 * 
 * DESCRIPTION 
 * 	Routines to compute and set clause selectivities 
 * 
 */ 
 
/* RcsId ("$Header: /private/postgres/src/planner/path/RCS/clausesel.c,v 1.25 1992/08/21 05:45:05 mer Exp $"); */

/*     
 *      EXPORTS
 *     		set_clause_selectivities
 *     		product_selec
 *     		set_rest_relselec
 *     		compute_clause_selec
 */

#include "nodes/pg_lisp.h"
#include "nodes/relation.h"
#include "nodes/relation.a.h"
#include "nodes/primnodes.h"
#include "nodes/primnodes.a.h"

#include "planner/internal.h"
#include "planner/clause.h"
#include "planner/clauses.h"
#include "planner/clausesel.h"
#include "planner/plancat.h"
#include "parser/parsetree.h"

#include "catalog/pg_proc.h"
#include "catalog/pg_operator.h"

/*     		----  ROUTINES TO SET CLAUSE SELECTIVITIES  ----   */


/*    
 *    	set_clause_selectivities
 *    
 *    	Sets the selectivity field for each of clause in 'clauseinfo-list'
 *    	to 'new-selectivity'.  If the selectivity has already been set, reset 
 *    	it only if the new one is better.
 *    
 *    	Returns nothing of interest.
 *
 */

/*  .. create_index_path
 */
void
set_clause_selectivities (clauseinfo_list,new_selectivity)
     LispValue clauseinfo_list; 
     Cost new_selectivity ;
{
    LispValue temp;
    CInfo clausenode;
    Cost cost_clause;

    foreach (temp,clauseinfo_list) {
	clausenode = (CInfo)CAR(temp);
	cost_clause = get_selectivity(clausenode);
	if ( FLOAT_IS_ZERO(cost_clause) || new_selectivity < cost_clause) {
	    set_selectivity (clausenode,new_selectivity);
	}
    }
} /* end set_clause_selectivities */

/*    
 *    	product_selec
 *    
 *    	Multiplies the selectivities of each clause in 'clauseinfo-list'.
 *    
 *    	Returns a flonum corresponding to the selectivity of 'clauseinfo-list'.
 *    
 */

/*  .. compute-joinrel-size, compute-rel-size     */

Cost
product_selec (clauseinfo_list)
     LispValue clauseinfo_list ;
{
     Cost result = 1.0;
     if ( consp (clauseinfo_list) ) {
	  LispValue xclausenode = LispNil;
	  Cost temp;

	  foreach(xclausenode,clauseinfo_list) {
	       temp = get_selectivity((CInfo)CAR(xclausenode));
	       result = result * temp;
	  }
     }
     return(result);
} /* end product_selec */

/*    
 *    	set_rest_relselec
 *    
 *    	Scans through clauses on each relation and assigns a selectivity to
 *    	those clauses that haven't been assigned a selectivity by an index.
 *    
 *    	Returns nothing of interest.
 *   	MODIFIES: selectivities of the various rel's clauseinfo
 *		  slots. 
 */

/*  .. find-paths    */

void
set_rest_relselec (rel_list)
     LispValue rel_list ;
{
     Rel rel;
     LispValue x;
     foreach (x,rel_list) {
	 rel = (Rel)CAR(x);
	 set_rest_selec(get_clauseinfo(rel));
     }
}

/*    
 *    	set_rest_selec
 *    
 *    	Sets the selectivity fields for those clauses within a single
 *    	relation's 'clauseinfo-list' that haven't already been set.
 *    
 *    	Returns nothing of interest.
 *    
 */

/*  .. set_rest_relselec    */

void
set_rest_selec (clauseinfo_list)
LispValue clauseinfo_list ;
{
    LispValue temp = LispNil;
    CInfo clausenode = (CInfo)NULL;
    Cost cost_clause;
    
    foreach (temp,clauseinfo_list) {
	clausenode = (CInfo)CAR(temp);
	cost_clause = get_selectivity(clausenode);

	/*    Check to see if the selectivity of this clause or any 'or' */
	/*    subclauses (if any) haven't been set yet. */

	if (valid_or_clause(clausenode) || FLOAT_IS_ZERO(cost_clause)) {
	    set_selectivity (clausenode,
			     compute_clause_selec((List)get_clause(clausenode),
					      lispCons(lispFloat(cost_clause), 
			/* XXX this bogus */		    LispNil)));
	}
    }
} /* end set_rest_selec */

/*    		----  ROUTINES TO COMPUTE SELECTIVITIES  ----
 */

/*    
 *    	compute_clause_selec
 *    
 *    	Given a clause, this routine will compute the selectivity of the
 *    	clause by calling 'compute_selec' with the appropriate parameters
 *    	and possibly use that return value to compute the real selectivity
 *    	of a clause.
 *    
 *    	'or-selectivities' are selectivities that have already been assigned
 *    		to subclauses of an 'or' clause.
 *    
 *    	Returns a flonum corresponding to the clause selectivity.
 *    
 */

/*  .. add-clause-to-rels, set_rest_selec    */

Cost
compute_clause_selec (clause,or_selectivities)
     LispValue clause,or_selectivities ;
{
/*    if (!is_clause (clause)) {  NO!!! THIS IS A BUG.  -- JMH 3/9/92 */
      if (is_clause (clause)) {
	/* Boolean variables get a selectivity of 1/2. */
	return(0.5);
    } 
    else if (not_clause (clause)) {
	/* 'not' gets "1.0 - selectivity-of-inner-clause". */
	return (1.000000 - compute_selec (lispCons(get_notclausearg (clause),
						   LispNil),
					  or_selectivities));
    }
    else if (or_clause (clause)) {
	/* Both 'or' and 'and' clauses are evaluated as described in 
	 *    (compute_selec). 
	 */
	return (compute_selec (get_orclauseargs (clause),or_selectivities));
    } 
    else {
	return(compute_selec (lispCons(clause,LispNil),or_selectivities));
    } 
}

/*    
 *    	compute_selec
 *    
 *    	Computes the selectivity of a clause.
 *    
 *    	If there is more than one clause in the argument 'clauses', then the
 *    	desired selectivity is that of an 'or' clause.  Selectivities for an
 *    	'or' clause such as (OR a b) are computed by finding the selectivity
 *    	of a (s1) and b (s2) and computing s1+s2 - s1*s2.
 *    
 *    	In addition, if the clause is an 'or' clause, individual selectivities
 *    	may have already been assigned by indices to subclauses.  These values
 *    	are contained in the list 'or-selectivities'.
 *    
 *    	Returns the clause selectivity as a flonum.
 *    
 */

/*  .. compute_clause_selec, compute_selec   */

Cost
compute_selec (clauses,or_selectivities)
     LispValue clauses,or_selectivities ;
{
    Cost s1 = 0;
    LispValue clause = CAR (clauses);

    if(null (clauses)) 
     {
	 s1 = 1.0;
     }
    else if (IsA(clause,Const))
     {
	 s1 = ((bool)get_constvalue((Const)clause)) ? 1.0 : 0.0;
     }
    else if (IsA(clause,Var))
     {
	 ObjectId relid;

	 relid = (ObjectId)
		CInteger(translate_relid(CAR(get_varid((Var)clause))));
	 /*
	  * we have a bool Var.  This is exactly equivalent to the clause:
	  *	reln.attribute = 't'
	  * so we compute the selectivity as if that is what we have. The
	  * magic #define constants are a hack.  I didn't want to have to
	  * do system cache look ups to find out all of that info.
	  */
	 s1 = restriction_selectivity(EqualSelectivityProcedure,
				      BooleanEqualOperator,
				      relid,
				      CInteger(CADR(get_varid((Var)clause))),
				      (Datum) 't',
				      _SELEC_CONSTANT_RIGHT_);

     }
    /* If s1 has already been assigned by an index, use that value. */ 
    else if (or_selectivities)
     {
	 s1 = (int) CAR (or_selectivities);
     } 
    else if (IsA(CAR(clause),Func))  /* this isn't an Oper, it's a Func!! */
     {
	 /*
	 ** This is not an operator, so we guess at the selectivity.  
	 ** THIS IS A HACK TO GET V4 OUT THE DOOR.  FUNCS SHOULD BE
	 ** ABLE TO HAVE SELECTIVITIES THEMSELVES.
	 **     -- JMH 7/9/92
	 */
	   s1 = 0.1;
     }
    else if (1 == NumRelids ((Expr)clause)) {

	/* ...otherwise, calculate s1 from 'clauses'. 
	 *    The clause is not a join clause, since there is 
	 *    only one relid in the clause.  The clause 
	 *    selectivity will be based on the operator 
	 *    selectivity and operand values. 
	 */

	LispValue relattvals = get_relattval (clause);
	ObjectId opno = get_opno ((Oper)get_op (clause));
	RegProcedure oprrest = get_oprrest (opno);
	LispValue relid = LispNil;
	
	if (translate_relid (CAR(relattvals)))
	  relid = translate_relid(CAR(relattvals));
	else
	  relid = lispInteger(_SELEC_VALUE_UNKNOWN_);
	
	s1 = (Cost)restriction_selectivity (oprrest,
					      opno,
					      CInteger(relid),
					      CInteger(CADR (relattvals)),
					      (char*)CInteger((CADDR(relattvals))),
					      CInteger(CADDR 
						       (CDR(relattvals))));
    }
     else {
	 
	 /*    The clause must be a join clause.  The clause 
	  *    selectivity will be based on the relations to be 
	  *    scanned and the attributes they are to be joined 
	  *    on. 
	  */
	 LispValue relsatts = get_relsatts (clause);
	 ObjectId opno = get_opno((Oper)get_op (clause));
	 RegProcedure oprjoin = get_oprjoin (opno);
	 LispValue relid1 = LispNil;
	 LispValue relid2 = LispNil;
	 
	 if(translate_relid (CAR(relsatts)))
	   relid1 = translate_relid(CAR(relsatts));
	 else
	   relid1 =  lispInteger(_SELEC_VALUE_UNKNOWN_);
	 if (translate_relid (CADDR (relsatts)))
	    relid2 = translate_relid(CADDR(relsatts));
	 else
	   relid2 =  lispInteger(_SELEC_VALUE_UNKNOWN_);
	 s1 = (Cost)join_selectivity (oprjoin,
					opno,
					CInteger(relid1),
					CInteger(CADR (relsatts)),
					CInteger(relid2),
					CInteger(CADDR (CDR (relsatts))));
     }
    
    /*    A null clause list eliminates no tuples, so return a selectivity 
     *    of 1.0.  If there is only one clause, the selectivity is not 
     *    that of an 'or' clause, but rather that of the single clause.
     */
    
    if (length (clauses) < 2) {
	return(s1);
    } 
    else {
	/* Compute selectivity of the 'or'ed subclauses. */
	/* Added check for taking CDR(LispNil).  -- JMH 3/9/92 */
	  Cost s2;
	  if (or_selectivities != LispNil)
	    s2 = compute_selec (CDR (clauses),CDR (or_selectivities));
	  else
	    s2 = compute_selec (CDR (clauses), LispNil);
	  return (s1 + s2 - s1*s2);
      }
} /* end compute_selec */

/*  .. compute_selec */

LispValue
translate_relid(relid)
    LispValue relid;
{
    if (integerp(relid))
	return
	    getrelid(CInteger(relid),_query_range_table_);
    
    return
	lispInteger(0);
}

