
/*     
 *      FILE
 *     	joinrels
 *     
 *      DESCRIPTION
 *     	Routines to determine which relations should be joined
 *     
 */

/*  RcsId("$Header: /private/postgres/src/planner/path/RCS/joinrels.c,v 1.16 1992/07/23 15:33:07 joey Exp $");  */

/*     
 *      EXPORTS
 *     		find-join-rels
 */

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

#include "planner/internal.h"
#include "planner/joinrels.h"
#include "planner/joininfo.h"
#include "planner/relnode.h"

/*    
 *    	find-join-rels
 *    
 *    	Find all possible joins for each of the outer join relations in
 *    	'outer-rels'.  A rel node is created for each possible join relation,
 *    	and the resulting list of nodes is returned.  If at all possible, only
 *    	those relations for which join clauses exist are considered.  If none
 *    	of these exist for a given relation, all remaining possibilities are
 *    	considered.
 *    
 *    	'outer-rels' is the list of rel nodes
 *    
 *    	Returns a list of rel nodes corresponding to the new join relations.
 *    
 */

/*  .. find-join-paths    */

LispValue
find_join_rels(outer_rels)
     LispValue outer_rels ;
{
    Rel outer_rel;
    LispValue temp = LispNil;
    LispValue t_list = LispNil;
    LispValue i = LispNil;

    foreach(i,outer_rels) {
	outer_rel = (Rel)CAR(i);
	if(!(temp = find_clause_joins(outer_rel,get_joininfo(outer_rel))))
	  if (BushyPlanFlag)
	      temp = find_clauseless_joins(outer_rel,outer_rels);
	  else
	      temp = find_clauseless_joins(outer_rel,_base_relation_list_);
	t_list = nconc(t_list,temp);
    }
    return(t_list);

}  /* function end  */

/*    
 *    	find-clause-joins
 *    
 *    	Determines whether joins can be performed between an outer relation
 *    	'outer-rel' and those relations within 'outer-rel's joininfo nodes
 *    	(i.e., relations that participate in join clauses that 'outer-rel'
 *    	participates in).  This is possible if all but one of the relations
 *    	contained within the join clauses of the joininfo node are already
 *    	contained within 'outer-rel'.
 *    
 *    	'outer-rel' is the relation entry for the outer relation
 *    	'joininfo-list' is a list of join clauses which 'outer-rel' 
 *    		participates in
 *    
 *    	Returns a list of new join relations.
 *    
 */

/*  .. find-join-rels    */

LispValue
find_clause_joins(outer_rel,joininfo_list)
     Rel outer_rel;
     List joininfo_list ;
{
     LispValue temp_node = LispNil;
     LispValue t_list = LispNil;
     LispValue i = LispNil;
     
     foreach(i,joininfo_list) {
	 JInfo joininfo = (JInfo)CAR(i);
         if(!joininfo_inactive(joininfo)) {
               LispValue other_rels = get_otherrels(joininfo);
               if(!null(other_rels)) {
                  if(length(other_rels) == 1)
                     temp_node = lispCons((LispValue)init_join_rel(outer_rel,
                                                       get_rel(CAR(other_rels)),
                                                       joininfo),
                                          LispNil);
                  else
                     temp_node = lispCons((LispValue)init_join_rel(outer_rel,
                                                        get_rel(other_rels),
                                                         joininfo),
                                          LispNil);
                t_list = nconc(t_list,temp_node);

              }
           }
       }
     return(t_list);

}  /* function end  */

/*    
 *    	find-clauseless-joins
 *    
 *    	Given an outer relation 'outer-rel' and a list of inner relations
 *    	'inner-rels', create a join relation between 'outer-rel' and each
 *    	member of 'inner-rels' that isn't already included in 'outer-rel'.
 *    
 *    	Returns a list of new join relations.
 *    
 */

/*  .. find-join-rels    */

LispValue
find_clauseless_joins(outer_rel,inner_rels)
     Rel outer_rel;
     List inner_rels ;
{
     /*  XXX was mapcan   */
     Rel inner_rel;
     LispValue t_list = LispNil;
     LispValue temp_node = LispNil;
     LispValue i = LispNil;

     foreach(i,inner_rels) {
	 inner_rel = (Rel)CAR(i);
         if(nonoverlap_rels(inner_rel,outer_rel)) {
	     temp_node = lispCons((LispValue)init_join_rel(outer_rel, 
							   inner_rel,
							   (JInfo)NULL),
				   LispNil);
	     t_list = nconc(t_list,temp_node);
	 } 
     }
     return(t_list);

}  /*  function end   */

/*    
 *    	init-join-rel
 *    
 *    	Creates and initializes a new join relation.
 *    
 *    	'outer-rel' and 'inner-rel' are relation nodes for the relations to be
 *    		joined
 *    	'joininfo' is the joininfo node(join clause) containing both
 *    		'outer-rel' and 'inner-rel', if any exists
 *    
 *    	Returns the new join relation node.
 *    
 */

/*  .. find-clause-joins, find-clauseless-joins   */

Rel
init_join_rel(outer_rel,inner_rel,joininfo)
     Rel outer_rel,inner_rel;
     JInfo joininfo ;
{
    Rel joinrel = RMakeRel();
    LispValue joinrel_joininfo_list = LispNil;
    
    /*    Create a new tlist by removing irrelevant elements from both */
    /*    tlists of the outer and inner join relations and then merging */
    /*    the results together. */

    LispValue new_outer_tlist = 
      new_join_tlist(get_targetlist(outer_rel)   /*   XXX 1-based attnos */
		      ,get_relids(inner_rel),1);
    LispValue new_inner_tlist = 
      new_join_tlist(get_targetlist  (inner_rel)    /*   XXX 1-based attnos */
		      ,get_relids(outer_rel),
		      length(new_outer_tlist) + 1);
     
     
    set_relids(joinrel, LispNil);
    set_indexed(joinrel,false);
    set_pages(joinrel, 0);
    set_tuples(joinrel,0);
    set_width(joinrel,0);
    set_targetlist(joinrel,LispNil);
    set_pathlist(joinrel,LispNil);
    set_unorderedpath(joinrel,(PathPtr)NULL);
    set_cheapestpath(joinrel,(PathPtr)NULL);
    set_classlist(joinrel,(List)NULL);
    set_ordering(joinrel,LispNil);
    set_clauseinfo(joinrel,LispNil);
    set_joininfo(joinrel,LispNil);
    set_innerjoin(joinrel,LispNil);
    set_superrels(joinrel,LispNil);
    
    set_relids(joinrel,lispCons(get_relids(outer_rel),
				lispCons(get_relids(inner_rel),
					  LispNil)));
    new_outer_tlist = nconc(new_outer_tlist,new_inner_tlist);
    set_targetlist(joinrel,new_outer_tlist);
    
    if( joininfo) {
	set_clauseinfo(joinrel,get_jinfoclauseinfo(joininfo));
	if (BushyPlanFlag) deactivate_joininfo(joininfo);
    }
    
    
    joinrel_joininfo_list = 
      new_joininfo_list(append(get_joininfo(outer_rel),get_joininfo(inner_rel)),
                        append(get_relids(outer_rel), get_relids(inner_rel)));

    set_joininfo(joinrel,joinrel_joininfo_list);

    return(joinrel);

}  /* function end  */

/*    
 *    	new-join-tlist
 *    
 *    	Builds a join relations's target list by keeping those elements that 
 *    	will be in the final target list and any other elements that are still 
 *    	needed for future joins.  For a target list entry to still be needed 
 *    	for future joins, its 'joinlist' field must not be empty after removal 
 *    	of all relids in 'other-relids'.
 *    
 *    	'tlist' is the target list of one of the join relations
 *    	'other-relids' is a list of relids contained within the other
 *    		join relation
 *    	'first-resdomno' is the resdom number to use for the first created
 *    		target list entry
 *    
 *    	Returns the new target list.
 *    
 */

/*  .. init-join-rel    */

LispValue
new_join_tlist(tlist,other_relids,first_resdomno)
     LispValue tlist,other_relids;
     int first_resdomno ;
{
    int resdomno = first_resdomno - 1;
    LispValue xtl = LispNil;
    LispValue temp_node = LispNil;
    LispValue t_list = LispNil;
    LispValue i = LispNil;
    LispValue join_list = LispNil;
    bool in_final_tlist =false;
    LispValue future_join_list = LispNil;
    

    foreach(i,tlist) {
	xtl= CAR(i);
	join_list = get_joinlist(xtl);
	in_final_tlist = null(join_list);
	if( join_list ) 
	  future_join_list = set_difference(join_list,other_relids);
	else
	  future_join_list = LispNil;
	if( in_final_tlist || future_join_list)  {
	    resdomno += 1;
	    temp_node = 
	      lispCons((LispValue)create_tl_element(get_expr(get_entry(xtl)),
					   resdomno,
					   future_join_list),
			LispNil);
	    t_list = nconc(t_list,temp_node);
	} 
    }
    return(t_list);
}

/*    
 *    	new-joininfo-list
 *    
 *    	Builds a join relation's joininfo list by checking for join clauses
 *    	which still need to used in future joins involving this relation.  A
 *    	join clause is still needed if there are still relations in the clause
 *    	not contained in the list of relations comprising this join relation.
 *    	New joininfo nodes are only created and added to
 *    	'current-joininfo-list' if a node for a particular join hasn't already
 *    	been created.
 *    
 *    	'current-joininfo-list' contains a list of those joininfo nodes that 
 *    		have already been built 
 *    	'joininfo-list' is the list of join clauses involving this relation
 *    	'join-relids' is a list of relids corresponding to the relations 
 *    		currently being joined
 *    
 *    	Returns a list of joininfo nodes, new and old.
 *    
 */

/*  .. init-join-rel    */

LispValue
new_joininfo_list(joininfo_list,join_relids)
     LispValue joininfo_list,join_relids ;
{
   LispValue current_joininfo_list = LispNil;
   LispValue new_otherrels = LispNil;
   JInfo other_joininfo = (JInfo)NULL;
   LispValue xjoininfo = LispNil;

   foreach(xjoininfo, joininfo_list) {
      JInfo joininfo = (JInfo)CAR(xjoininfo);
      new_otherrels = get_otherrels(joininfo);
      if (nonoverlap_sets(new_otherrels,join_relids)) {
         other_joininfo = joininfo_member(new_otherrels,current_joininfo_list);
         if(other_joininfo)
	 {
            set_jinfoclauseinfo(other_joininfo, LispUnion((LispValue)get_jinfoclauseinfo(joininfo), (LispValue)get_jinfoclauseinfo(other_joininfo)));
	 }
         else {
            other_joininfo = MakeJInfo(get_otherrels(joininfo),
                                        get_jinfoclauseinfo(joininfo),
                                        get_mergesortable(joininfo),
                                        get_hashjoinable(joininfo),
					false);
            current_joininfo_list = push((LispValue)other_joininfo,
					 current_joininfo_list);
         }
       }
    }
    return(current_joininfo_list);
} /* function end  */

/*
 *      add-new-joininfos
 *
 *      For each new join relation, create new joininfos that
 *      use the join relation as inner relation, and add
 *      the new joininfos to those rel nodes that still
 *      have joins with the join relation.
 *
 *      'joinrels' is a list of join relations.
 *
 *      Modifies the joininfo field of appropriate rel nodes.
 *
 */

/*  .. find-join-paths
 */
LispValue
add_new_joininfos(joinrels,outerrels)
LispValue joinrels,outerrels ;
{
    LispValue xjoinrel = LispNil;
    LispValue xrelid = LispNil;
    LispValue xrel = LispNil;
    LispValue xjoininfo = LispNil;

    foreach(xjoinrel, joinrels) {
	Rel joinrel = (Rel)CAR(xjoinrel);
        foreach(xrelid, get_relids(joinrel)) {
	    Relid relid = (Relid)CAR(xrelid);
	    Rel rel = get_rel(relid);
	    add_superrels(rel,joinrel);
	}
    }
    foreach(xjoinrel, joinrels) {
	Rel joinrel = (Rel)CAR(xjoinrel);
	foreach(xjoininfo, get_joininfo(joinrel)) {
	    JInfo joininfo = (JInfo)CAR(xjoininfo);
	    LispValue other_rels = get_otherrels(joininfo);
	    LispValue clause_info = get_jinfoclauseinfo(joininfo);
	    bool mergesortable = get_mergesortable(joininfo);
	    bool hashjoinable = get_hashjoinable(joininfo);
	    foreach(xrelid, other_rels) {
		Relid relid = (Relid)CAR(xrelid);
		Rel rel = get_rel(relid);
		List super_rels = get_superrels(rel);
		LispValue xsuper_rel = LispNil;
		JInfo new_joininfo = MakeJInfo(get_relids(joinrel),
						    clause_info,
						    mergesortable,
						    hashjoinable,
						    false);
	        set_joininfo(rel,nappend1(get_joininfo(rel), (LispValue)new_joininfo));
		foreach(xsuper_rel, super_rels) {
		    Rel super_rel = (Rel)CAR(xsuper_rel);
		    if( nonoverlap_rels(super_rel,joinrel) ) {
			LispValue new_relids = get_relids(super_rel);
			JInfo other_joininfo = 
				  joininfo_member(new_relids,
						   get_joininfo(joinrel));
			if( other_joininfo ) {
			    set_jinfoclauseinfo(other_joininfo, LispUnion(clause_info, get_jinfoclauseinfo(other_joininfo)));
			} else {
			    JInfo new_joininfo = MakeJInfo(new_relids,
								clause_info,
								mergesortable,
								hashjoinable,
								false);
			    set_joininfo(joinrel, nappend1 (get_joininfo(joinrel), (LispValue)new_joininfo));
			}
		    }
		}
	     }
	 }
    }
    foreach(xrel, outerrels)  {
	Rel rel = (Rel)CAR(xrel);
	set_superrels(rel,LispNil);
    }
}

/*
 *      final-join-rels
 *
 *      Find the join relation that includes all the original
 *      relations, i.e. the final join result.
 *
 *      'join-rel-list' is a list of join relations.
 *
 *      Returns the list of final join relations.
 *
 */

/*  .. find-join-paths
 */
LispValue
final_join_rels(join_rel_list)
LispValue join_rel_list ;
{
    LispValue xrel = LispNil;
    LispValue temp = LispNil;
    LispValue t_list = LispNil;

/*    find the relations that has no further joins, */
/*    i.e., its joininfos all have otherrels nil. */
    foreach(xrel,join_rel_list)  {
	Rel rel = (Rel)CAR(xrel);
	LispValue xjoininfo = LispNil;
	bool final = true;
	foreach (xjoininfo,get_joininfo(rel)) {
	    JInfo joininfo = (JInfo)CAR(xjoininfo);
	    if(get_otherrels(joininfo) != LispNil)  {
	       final = false;
	       break;
	    }
	}
	if(final)  {
	   temp = lispCons((LispValue)rel,LispNil);
	   t_list = nconc(t_list, temp);
	}
    }
    return(t_list);
}  /* function end */

/*
 *      add_superrels
 *
 *      add rel to the temporary property list superrels.
 *
 *      'rel' a rel node
 *      'super-rel' rel node of a join relation that includes rel
 *
 *      Modifies the superrels field of rel
 *
 */

/*  .. init-join-rel
 */
void
add_superrels(rel,super_rel)
Rel rel,super_rel ;
{
    set_superrels(rel, nappend1(get_superrels(rel), (LispValue)super_rel));
}

/*
 *      nonoverlap-rels
 *
 *      test if two join relations overlap, i.e., includes the same
 *      relation.
 *
 *      'rel1' and 'rel2' are two join relations
 *
 *      Returns non-nil if rel1 and rel2 do not overlap.
 *
 */

/*  .. add-new-joininfos
 */
bool
nonoverlap_rels(rel1,rel2)
Rel rel1,rel2 ;
{
    return(nonoverlap_sets(get_relids(rel1),get_relids(rel2)));
}

bool
nonoverlap_sets(s1,s2)
LispValue s1,s2 ;
{
    LispValue x = LispNil;

    foreach(x,s1)  {
       LispValue e = CAR(x);
       if(member(e,s2))
	  return(false);
    }
    return(true);
}

/*
 *	cleanup-joininfos
 *
 *      after a round of find-join-paths, clear the JoinInfos that
 *      have already been considered.
 *
 *      'outer-rels' is a list of rel nodes
 *
 *      Modifies JoinInfo fields.
 *
 */

/* .. add-new-joininfos
*/
void
cleanup_joininfos(outer_rels)
LispValue outer_rels;
{
    LispValue xrel = LispNil;

    foreach(xrel,outer_rels)  {
	Rel rel = (Rel)CAR(xrel);
	set_joininfo(rel,LispNil);
    }
}
