
/*     
 *      FILE
 *     	joinutils
 *     
 *      DESCRIPTION
 *     	Utilities for matching and building join and path keys
 *     
 */

/*  RcsId("$Header: /private/postgres/src/planner/path/RCS/joinutils.c,v 1.20 1992/07/04 04:03:42 mao Exp $");   */

/*     
 *      EXPORTS
 *     		match-pathkeys-joinkeys
 *     		match-paths-joinkeys
 *     		extract-path-keys
 *     		new-join-pathkeys
 */

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

#include "planner/internal.h"
#include "planner/joinutils.h"
#include "planner/var.h"
#include "planner/keys.h"
#include "planner/tlist.h"
#include "planner/joininfo.h"


/*     	===============
 *     	KEY COMPARISONS
 *     	===============
 */

/*    
 *    	match-pathkeys-joinkeys
 *    
 *    	Attempts to match the keys of a path against the keys of join clauses.
 *    	This is done by looking for a matching join key in 'joinkeys' for
 *    	every path key in the list 'pathkeys'. If there is a matching join key
 *    	(not necessarily unique) for every path key, then the list of 
 *    	corresponding join keys and join clauses are returned in the order in 
 *    	which the keys matched the path keys.
 *    
 *    	'pathkeys' is a list of path keys:
 *    		( ( (var) (var) ... ) ( (var) ... ) )
 *    	'joinkeys' is a list of join keys:
 *    		( (outer inner) (outer inner) ... )
 *    	'joinclauses' is a list of clauses corresponding to the join keys in
 *    		'joinkeys'
 *    	'which-subkey' is a flag that selects the desired subkey of a join key
 *    		in 'joinkeys'
 *    
 *    	Returns the join keys and corresponding join clauses in a list if all
 *    	of the path keys were matched:
 *    		( 
 *    		 ( (outerkey0 innerkey0) ... (outerkeyN innerkeyN) )
 *    		 ( clause0 ... clauseN ) 
 *    		)
 *    	and nil otherwise.
 *    
 */

/*  .. match-unsorted-inner, match-unsorted-outer    */

LispValue
match_pathkeys_joinkeys(pathkeys,joinkeys,joinclauses,which_subkey)
     LispValue pathkeys,joinkeys,joinclauses;
     int which_subkey ;
{
     LispValue matched_joinkeys = LispNil;
     LispValue matched_joinclauses = LispNil;
     LispValue pathkey = LispNil;
     LispValue t_list = LispNil;
     LispValue i = LispNil;
     int matched_joinkey_index = -1;

     foreach(i, pathkeys) {
	 pathkey = CAR(i);
	 matched_joinkey_index = 
	   match_pathkey_joinkeys(pathkey,joinkeys,which_subkey);
	 
	 if(matched_joinkey_index != -1 ) {
	     LispValue xjoinkey = nth(matched_joinkey_index,joinkeys);
	     LispValue joinclause = nth(matched_joinkey_index,joinclauses);

	     /* XXX was "push" function */
	     
	     matched_joinkeys = nappend1(matched_joinkeys,xjoinkey);
	     matched_joinkeys = nreverse(matched_joinkeys);

	     matched_joinclauses = nappend1(matched_joinclauses,joinclause);
	     matched_joinclauses = nreverse(matched_joinclauses);
	     joinkeys = LispRemove(xjoinkey,joinkeys);
	     
	 } 
	 else {
	     return(LispNil);
	 } 
		
     }
     if(!lispNullp(matched_joinkeys) &&
	length(matched_joinkeys) == length(pathkeys)) {
	 t_list = lispCons(nreverse(matched_joinkeys),
			    lispCons(nreverse(matched_joinclauses),
				     LispNil));
     } 
     return(t_list);

}  /* function end  */

/*    
 *    	match-pathkey-joinkeys
 *    
 *    	Returns the 0-based index into 'joinkeys' of the first joinkey whose
 *    	outer or inner subkey matches any subkey of 'pathkey'.
 *    
 */

/*  .. match-pathkeys-joinkeys	 */

int
match_pathkey_joinkeys(pathkey,joinkeys,which_subkey)
     LispValue pathkey,joinkeys;
     int which_subkey ;
{
    Var path_subkey;
    int pos;
    LispValue i = LispNil;
    LispValue x = LispNil;
    JoinKey jk;

    foreach(i,pathkey) {
	path_subkey = (Var)CAR(i);
	pos = 0;
	foreach(x,joinkeys) {
	    jk = (JoinKey)CAR(x);
	    if(var_equal((LispValue)path_subkey, (LispValue)extract_subkey(jk, which_subkey)))
		return(pos);
	    pos++;
	}
    }
    return(-1);    /* no index found   */

}  /* function end   */

/*    
 *    	match-paths-joinkeys
 *    
 *    	Attempts to find a path in 'paths' whose keys match a set of join
 *    	keys 'joinkeys'.  To match,
 *    	1. the path node ordering must equal 'ordering'.
 *    	2. each subkey of a given path must match(i.e., be(var_equal) to) the
 *    	   appropriate subkey of the corresponding join key in 'joinkeys',
 *    	   i.e., the Nth path key must match its subkeys against the subkey of
 *    	   the Nth join key in 'joinkeys'.
 *    
 *    	'joinkeys' is the list of key pairs to which the path keys must be 
 *    		matched
 *    	'ordering' is the ordering of the(outer) path to which 'joinkeys'
 *    		must correspond
 *    	'paths' is a list of(inner) paths which are to be matched against
 *    		each join key in 'joinkeys'
 *    	'which-subkey' is a flag that selects the desired subkey of a join key
 *    		in 'joinkeys'
 *    
 *    	Returns the matching path node if one exists, nil otherwise.
 *    
 */

/*  function used by match_paths_joinkeys */
bool
every_func(joinkeys, pathkey, which_subkey)
     LispValue joinkeys, pathkey;
     int which_subkey;
{
     JoinKey xjoinkey;
     Var temp;
     LispValue tempkey = LispNil;
     bool found = false;
     LispValue i = LispNil;
     LispValue j = LispNil;

     foreach(i,joinkeys) {
	 xjoinkey = (JoinKey)CAR(i);
	 found = false;
	 foreach(j,pathkey) {
	     temp = (Var)CAR(CAR(j));
	     if(temp == NULL) continue;
	     tempkey = extract_subkey(xjoinkey,which_subkey);
	     if(var_equal((LispValue)tempkey,(LispValue)temp)) {
		 found = true;
		 break;
	     }
	 }
	 if(found == false)
	   return(false);
     }
     return(found);
}

/*  .. match-unsorted-outer	 */

Path
match_paths_joinkeys(joinkeys,ordering,paths,which_subkey)
     LispValue joinkeys,ordering,paths;
     int which_subkey ;
{
    Path path = (Path)NULL ;
    bool key_match = false;
    LispValue i = LispNil;
 
    foreach(i,paths) {
	path = (Path)CAR(i);
	key_match = every_func(joinkeys,
			       get_keys(path),
			       which_subkey);

	if(equal_path_path_ordering(ordering,
				      get_p_ordering(path)) &&
	    length(joinkeys) == length(get_keys(path)) &&
	    key_match) {
	    return(path);
	}
    }
    return((Path) LispNil);
}  /* function end  */



/*    
 *    	extract-path-keys
 *    
 *    	Builds a subkey list for a path by pulling one of the subkeys from
 *    	a list of join keys 'joinkeys' and then finding the var node in the
 *    	target list 'tlist' that corresponds to that subkey.
 *    
 *    	'joinkeys' is a list of join key pairs
 *    	'tlist' is a relation target list
 *    	'which-subkey' is a flag that selects the desired subkey of a join key
 *    		in 'joinkeys'
 *    
 *    	Returns a list of pathkeys: ((tlvar1)(tlvar2)...(tlvarN)).
 *    
 */

/*  .. hash-inner-and-outer, match-unsorted-inner, match-unsorted-outer
 *  .. sort-inner-and-outer
 */
LispValue
extract_path_keys(joinkeys,tlist,which_subkey)
     LispValue joinkeys,tlist;
     int which_subkey ;
{
    JoinKey xjoinkey = (JoinKey)NULL;
    LispValue t_list = LispNil;
    LispValue temp_node = LispNil;
    LispValue i = LispNil;

    foreach(i,joinkeys) {
	xjoinkey = (JoinKey)CAR(i);
	temp_node =
	  lispCons((LispValue)matching_tlvar((Var)extract_subkey(xjoinkey,
						    which_subkey),tlist),
		    LispNil);
	t_list = nappend1(t_list,temp_node);
    }
    return(t_list);
} /* function end  */

/*     	=====================
 *     	NEW PATHKEY FORMATION
 *     	=====================
 */

/*    
 *    	new-join-pathkeys
 *    
 *    	Find the path keys for a join relation by finding all vars in the list
 *    	of join clauses 'joinclauses' such that:
 *    		(1) the var corresponding to the outer join relation is a
 *    		    key on the outer path
 *    		(2) the var appears in the target list of the join relation
 *    	In other words, add to each outer path key the inner path keys that
 *    	are required for qualification.
 *    
 *    	'outer-pathkeys' is the list of the outer path's path keys
 *    	'join-rel-tlist' is the target list of the join relation
 *    	'joinclauses' is the list of restricting join clauses
 *    
 *    	Returns the list of new path keys. 
 *    
 */

/*  .. hash-inner-and-outer, match-unsorted-inner, match-unsorted-outer
 *  .. sort-inner-and-outer
 */
LispValue
new_join_pathkeys(outer_pathkeys,join_rel_tlist,joinclauses)
     LispValue outer_pathkeys,join_rel_tlist,joinclauses ;
{
    LispValue outer_pathkey = LispNil;
    LispValue t_list = LispNil;
    LispValue x;
    LispValue temp_node = LispNil;
    LispValue i = LispNil;

    foreach(i,outer_pathkeys) {
	outer_pathkey = CAR(i);
	x = new_join_pathkey(outer_pathkey, LispNil, 
			      join_rel_tlist,joinclauses);
	if(!lispNullp(x)) {
	    temp_node = lispCons(x, LispNil);
	    t_list = nconc(t_list,temp_node);
	  }
      }
    return(t_list);
}

/*    
 *    	new-join-pathkey
 *    
 *    	Finds new vars that become subkeys due to qualification clauses that
 *    	contain any previously considered subkeys.  These new subkeys plus the
 *    	subkeys from 'subkeys' form a new pathkey for the join relation.
 *    
 *    	Note that each returned subkey is the var node found in
 *    	'join-rel-tlist' rather than the joinclause var node.
 *    
 *    	'subkeys' is a list of subkeys for which matching subkeys are to be
 *    		found
 *    	'considered-subkeys' is the current list of all subkeys corresponding
 *    		to a given pathkey
 *    
 *    	Returns a new pathkey(list of subkeys).
 *    
 */

/*  .. new-join-pathkeys  	 */

LispValue
new_join_pathkey(subkeys,considered_subkeys,join_rel_tlist,joinclauses)
     LispValue subkeys,considered_subkeys,join_rel_tlist,joinclauses ;
{
    LispValue t_list = LispNil;
    Var subkey;
    LispValue i = LispNil;
    LispValue matched_subkeys = LispNil;
    Expr tlist_key = (Expr)NULL;
    LispValue newly_considered_subkeys = LispNil;

    foreach(i,subkeys) {
	subkey = (Var)CAR(i);
	if(subkey == NULL)
	  break;    /* XXX something is wrong */
	matched_subkeys = 
	  new_matching_subkeys(subkey,considered_subkeys,
				join_rel_tlist,joinclauses);
	tlist_key = matching_tlvar(subkey,join_rel_tlist);
	newly_considered_subkeys = LispNil;

	if( tlist_key ) {
	    if(!member((LispValue)tlist_key,matched_subkeys))
	      newly_considered_subkeys = lispCons((LispValue)tlist_key,
						  matched_subkeys);
	} 
	else {
	    newly_considered_subkeys = matched_subkeys;
	} 
	
	considered_subkeys = 
	  append(considered_subkeys,newly_considered_subkeys);
	t_list = nconc(t_list,newly_considered_subkeys);
    }
    return(t_list);
    
}  /* function end  */

/*    
 *    	new-matching-subkeys
 *    
 *    	Returns a list of new subkeys:
 *    	(1) which are not listed in 'considered-subkeys'
 *    	(2) for which the "other" variable in some clause in 'joinclauses' is
 *    	    'subkey'
 *    	(3) which are mentioned in 'join-rel-tlist'
 *    
 *    	Note that each returned subkey is the var node found in
 *    	'join-rel-tlist' rather than the joinclause var node.
 *    
 *    	'subkey' is the var node for which we are trying to find matching
 *    		clauses
 *    
 *    	Returns a list of new subkeys.
 *    
 */

/*  .. new-join-pathkey */

LispValue
new_matching_subkeys(subkey,considered_subkeys,join_rel_tlist,joinclauses)
     LispValue considered_subkeys,join_rel_tlist,joinclauses ;
     Var subkey;
{
     LispValue joinclause = LispNil;
     LispValue t_list = LispNil;
     LispValue temp = LispNil;
     LispValue i = LispNil;
     Expr tlist_other_var = (Expr)NULL;

     foreach(i,joinclauses) {
	 joinclause = CAR(i);
	 tlist_other_var = 
	   matching_tlvar(other_join_clause_var(subkey,joinclause),
			   join_rel_tlist);

	 if(tlist_other_var && 
	    !(member((LispValue)tlist_other_var,considered_subkeys))) {
	     /* XXX was "push" function  */
	     considered_subkeys = nappend1(considered_subkeys,
					   (LispValue)tlist_other_var);
	     /* considered_subkeys = nreverse(considered_subkeys); 
		XXX -- I am not sure of this. */

	     temp = lispCons((LispValue)tlist_other_var,LispNil);
	     t_list = nconc(t_list,temp);
	 } 
     }
     return(t_list);
 }  /* function end  */
