
/*     
 *      FILE
 *     	keys
 *     
 *      DECLARATION
 *     	Key manipulation routines
 *     
 *      EXPORTS
 *     		match-indexkey-operand
 *     		equal_indexkey_var
 *     		extract-subkey
 *     		samekeys
 *     		collect-index-pathkeys
 *     		match-sortkeys-pathkeys
 *     		valid-sortkeys
 *     		valid-numkeys
 *	$Header: /private/postgres/src/planner/util/RCS/keys.c,v 1.13 1992/07/04 04:03:51 mao Exp $
 */

#include "tmp/c.h"
#include "nodes/pg_lisp.h"
#include "planner/internal.h"
#include "nodes/nodes.h"
#include "nodes/relation.h"
#include "nodes/relation.a.h"
#include "planner/keys.h"
#include "utils/log.h"
#include "planner/tlist.h"



/*    
 *    	1. index key
 *    		one of:
 *    			attnum
 *    			(attnum arrayindex)
 *    	2. path key
 *    		(subkey1 ... subkeyN)
 *    			where subkeyI is a var node
 *    		note that the 'Keys field is a list of these
 *    	3. join key
 *    		(outer-subkey inner-subkey)
 *    			where each subkey is a var node
 *    	4. sort key
 *    		one of:
 *    			SortKey node
 *    			number
 *    			nil
 *    		(may also refer to the 'SortKey field of a SortKey node,
 *    		 which looks exactly like an index key)
 *    
 */

/*    
 *    	match-indexkey-operand
 *    
 *    	Returns t iff an index key 'index-key' matches the given clause 
 *    	operand.
 *    
 */

/*  .. in-line-lambda%598037345, match-index-orclause
 */

bool
match_indexkey_operand (indexkey,operand,rel)
     LispValue indexkey;
     Var operand;
     Rel rel;

{
    if (IsA (operand,Var) &&
	(CInteger(CAR(get_relids (rel))) == get_varno (operand))&&
	equal_indexkey_var (indexkey,operand))
      return(true);
    else
      return(false);
}

/*    
 *    	equal_indexkey_var
 *    
 *    	Returns t iff an index key 'index-key' matches the corresponding 
 *    	fields of var node 'var'.
 *    
 */

/*  .. in-line-lambda%598037345, in-line-lambda%598037462
 *  .. match-indexkey-operand
 */
bool
equal_indexkey_var (index_key,var)
     LispValue index_key;
     Var var ;
{
    if (CInteger(index_key) == get_varattno (var))
      return(true);
    else
      return(false);
}

/*    
 *    	extract-subkey
 *    
 *    	Returns the subkey in a join key corresponding to the outer or inner
 *    	relation.
 *    
 */

/*  .. extract-path-keys, in-line-lambda%598037445, in-line-lambda%598037447
 */
LispValue
extract_subkey (jk,which_subkey)
     JoinKey jk;
     int which_subkey ;
{
     LispValue retval;

     switch (which_subkey) {
	case OUTER: 
	 retval = (LispValue)get_outer(jk);
	 break;
       case INNER: 
	 retval = (LispValue)get_inner(jk);
	 break;
       default: /* do nothing */
	 elog(DEBUG,"extract_subkey with neither INNER or OUTER");
	 retval = LispNil;
     }
     return(retval);
}

/*    
 *    	samekeys
 *    
 *    	Returns t iff two sets of path keys are equivalent.  They are 
 *    	equivalent if the first subkey (var node) within each sublist of 
 *    	list 'keys1' is contained within the corresponding sublist of 'keys2'.
 *    
 *    	XXX 	It isn't necessary to check that each sublist exactly contain 
 *    		the same elements because if the routine that built these
 *    		sublists together is correct, having one element in common 
 *    		implies having all elements in common.
 *    
 */

/*  .. in-line-lambda%598037501
 */
bool
samekeys (keys1,keys2)
     LispValue keys1,keys2 ;
{
     bool allmember = true;
     LispValue key1,key2;

     for(key1=keys1,key2=keys2 ; key1 != LispNil && key2 !=LispNil ; 
	 key1 = CDR(key1), key2=CDR(key2)) 
	  if ( ! member( CAR(key1),CAR(key2)))
	    allmember = false;

     if ( (length (keys2) >= length (keys1)) && allmember)
       return(true);
     else
       return(false);
}

/*    
 *    	collect-index-pathkeys
 *    
 *    	Creates a list of subkeys by retrieving var nodes corresponding to
 *    	each index key in 'index-keys' from the relation's target list
 *    	'tlist'.  If the key is not in the target list, the key is irrelevant
 *    	and is thrown away.  The returned subkey list is of the form:
 *    		((var1) (var2) ... (varn))
 *    
 *    	'index-keys' is a list of index keys
 *    	'tlist' is a relation target list
 *    
 *    	Returns the list of cons'd subkeys.
 *    
 */

/* used by collect_index_pathkeys .  This function is
 * identical to matching_tlvar and tlistentry_member.
 * They should be merged.
 */

Expr
matching2_tlvar(var,tlist,test)
     LispValue tlist;
     Var var;
     bool (*test)();
{
    LispValue tlentry = LispNil;

    if (var) {
	LispValue temp = LispNil;
	foreach (temp,tlist) {
	    if ( (*test)(var, get_expr(get_entry(CAR(temp))))) {
		tlentry = CAR(temp);
		break;
	    }
	}
    }

    if ( tlentry ) 
      return((Expr)get_expr (get_entry (tlentry)) );
    else
      return((Expr)NULL);
}

/*  .. create_index_path   */

    LispValue
collect_index_pathkeys (index_keys,tlist)
     LispValue index_keys,tlist ;
{
    LispValue index_key,retval = LispNil;
    
    foreach(index_key,index_keys) {
	if (CInteger(CAR(index_key)) != 0) {
	    Expr mvar;
	    mvar = matching2_tlvar (CAR(index_key),tlist,equal_indexkey_var);
	    if ( mvar ) 
	      retval = nconc(retval,lispCons( lispCons((LispValue)mvar,LispNil),
					      LispNil));
	}
    }
    return(retval);
}

/*    
 *    	match-sortkeys-pathkeys
 *    
 *    	Returns t iff the sort required by a result as specified by
 *    	'relid' and 'sortkeys' matches the 'pathkeys' of a path.
 *    
 */

/*  .. sort-relation-paths
 */
#define every2(a,x,b,y) for(a=x,b=y;a!=LispNil&&b!=LispNil;a=CDR(a),b=CDR(b))

bool
match_sortkeys_pathkeys (relid,sortkeys,pathkeys)
     LispValue relid,sortkeys,pathkeys ;
{
     /* declare (special (relid)); */
     LispValue sortkey,pathkey;
     bool every_val = true;

     every2 (sortkey,sortkeys, pathkey,pathkeys) {
	  if ( ! equal_sortkey_pathkey (relid,CAR(sortkey),CAR(pathkey)))
	    every_val = false;
     }

     if ( every_val  && (length (pathkeys) >= length (sortkeys)))
       return(true);
     else
       return(false);
}

/*    
 *    	equal-sortkey-pathkey
 *    
 *    	Returns t iff a sortkey as specified by 'relid' and 'sortkey' matches
 *    	one of the subkeys (var nodes) within 'pathkey'.
 *    
 */

/*  .. in-line-lambda%598037461
 */

bool
equal_sortkey_pathkey (relid,sortkey,pathkey)
     LispValue relid,sortkey,pathkey ;
{
     /* declare (special (relid,sortkey)); */
     LispValue subkey;
     bool retval = false;

     foreach(subkey,pathkey) {
	  if (equal ((Node)relid,(Node)get_varno ((Var)CAR(subkey))) &&
	      equal_indexkey_var (sortkey,(Var)CAR(subkey)))
	    retval = true;
     }
     return(retval);
}

/*    
 *    	valid-sortkeys
 *    
 *    	Returns t if 'sortkeys' appears to be a valid SortKey node.
 *    
 */

/*  .. find-index-paths, query_planner, subplanner
 */

bool
valid_sortkeys (node)
     LispValue node ;
{
    if (node)
      return ((bool)NodeIsType(node, T_SortKey));
    else
      return(false);   /* if sortkeys is nil  */
}

/*    
 *    	valid-numkeys
 *    
 *    	Returns t if 'sortkeys' appears to be a valid numkeys value
 *    
 */

/*  .. query_planner
 */
bool
valid_numkeys (sortkeys)
     LispValue sortkeys ;
{
     if ( integerp (sortkeys) && CInteger(sortkeys) <= _MAX_KEYS_)
       return(true);
     else
       return(false);
}
