/*     
 *      FILE
 *     	setrefs
 *     
 *      DESCRIPTION
 *     	Routines to change varno/attno entries to contain references
 *     
 */

/* RcsId ("$Header: /private/postgres/src/planner/plan/RCS/setrefs.c,v 1.27 1992/07/09 03:56:35 hong Exp $"); */

/*     
 *      EXPORTS
 *     		new-level-tlist
 *     		new-level-qual
 *     		new-result-tlist
 *     		new-result-qual
 *     		set-tlist-references
 *     		index-outerjoin-references
 *     		join-references
 *     		tlist-temp-references
 */
#include "tmp/c.h"

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

#include "parser/parse.h"

#include "utils/log.h"
#include "tmp/nodeFuncs.h"

#include "planner/internal.h"
#include "planner/clause.h"
#include "planner/clauseinfo.h"
#include "planner/clauses.h"
#include "planner/keys.h"
#include "planner/setrefs.h"
#include "planner/tlist.h"
#include "planner/var.h"

/*     
 *     	NESTING LEVEL REFERENCES
 *     
 */

/*     
 *     	RESULT REFERENCES
 *     
 */

/*    
 *    	new-result-tlist
 *    
 *    	Creates the target list for a result node by copying a tlist but
 *    	assigning reference values for the varno entry.  The reference pair 
 *    	corresponds to the nesting level where the attribute can be found and 
 *    	the result domain number within the tuple at that nesting level.
 *    
 *    	'tlist' is the the target list to be copied
 *    	'ltlist' is the target list from level 'levelnum'
 *    	'rtlist' is the target list from level 'levelnum+1'
 *    	'levelnum' is the current nesting level
 *    	'sorted' is t if the desired user result has been sorted in
 *    		some internal node
 *    
 *    	Returns the new target list.
 *    
 */

/*  .. query_planner	 */

LispValue
new_result_tlist (tlist,ltlist,rtlist,levelnum,sorted)
     LispValue tlist,ltlist,rtlist;
     int levelnum;
     bool sorted;
{
    LispValue t_list = LispNil;
    
    if ( consp (tlist) ) {
	  if ( consp (rtlist) ) {
	      LispValue xtl = LispNil;
	      LispValue temp = LispNil;
	      TLE entry;
	      
	      foreach(xtl,tlist) {
		  entry = CAR(xtl);
		  if ( sorted) {
		      set_reskey ((Resdom)tl_resdom(entry),0);
		      set_reskeyop ((Resdom)tl_resdom(entry),0);
		  }
		  temp = lispCons(tl_resdom (entry),
				  lispCons 
				  ((LispValue)replace_clause_resultvar_refs 
				   ((Expr)get_expr(entry),
				    ltlist,
				    rtlist,
				    levelnum),
				   LispNil));
		  t_list = nappend1(t_list,temp);
	       }
	      return(t_list);
	  }
	  else
	    return (tlist);
     }
     else
       return(LispNil);

}  /* function end  */

/*    
 *    	new-result-qual
 *    
 *    	Modifies a list of relation level clauses by assigning reference
 *    	values for varnos within vars appearing in the clauses.
 *    
 *    	'relquals' is the list of relation level clauses	
 *    	'ltlist' is the target list from level 'levelnum'
 *    	'rtlist' is the target list from level 'levelnum+1'
 *    	'levelnum' is the current nesting level
 *    
 *    	Returns a new qualification.
 *    
 */

/*  .. query_planner   	 */

LispValue
new_result_qual (clauses,ltlist,rtlist,levelnum)
     LispValue clauses,ltlist,rtlist;
     int levelnum ;
{
     return (replace_subclause_resultvar_refs (clauses,
					       ltlist,
					       rtlist,
					       levelnum));
} /* function end  */

/*    
 *    	replace-clause-resultvar-refs
 *    	replace-subclause-resultvar-refs
 *    	replace-resultvar-refs
 *    
 *    	Creates a new expression corresponding to a target list entry or
 *    	qualification in a result node by replacing the varno value in all
 *    	vars with a reference pair.  The reference pair is assigned by finding
 *    	a matching var in a previous target list.  If the var already is a
 *    	reference to a previous nesting, the var is just returned.
 *    
 *    	'expr' is the target list entry or qualification
 *    	'ltlist' is the target list from level 'levelnum'
 *    	'rtlist' is the target list from level 'levelnum+1'
 *    	'levelnum' is the current nesting level
 *    
 *    	Returns the new target list entry.
 *    
 */

/*  .. new-result-tlist, replace-clause-resultvar-refs
 *  .. replace-subclause-resultvar-refs
 */

Expr
replace_clause_resultvar_refs (clause,ltlist,rtlist,levelnum)
     Expr clause;
     List ltlist,rtlist;
     int levelnum ;
{
    if(IsA (clause,Var) && (get_varattno ((Var)clause) == 0))
      return(clause);
    else if (IsA (clause,Var)) 
      return ((Expr)replace_resultvar_refs((Var)clause,
					ltlist,
					rtlist,
					levelnum));
    else if (single_node ((Node)clause)) 
      return (clause);
    else if (or_clause ((LispValue)clause)) 
      return ((Expr)make_orclause (replace_subclause_resultvar_refs 
				   (get_orclauseargs ((LispValue)clause),
				    ltlist,
				    rtlist,
				    levelnum)));
    else if (IsA(clause,ArrayRef)) {
      Expr temp;
      temp = replace_clause_resultvar_refs(get_refindexpr((ArrayRef)clause),
					      ltlist,
					      rtlist,
					      levelnum);
      set_refindexpr((ArrayRef)clause,(LispValue)temp);
      temp = replace_clause_resultvar_refs(get_refexpr((ArrayRef)clause),
					      ltlist,
					      rtlist,
					      levelnum);
      set_refexpr((ArrayRef)clause,(LispValue)temp);
      return(clause);
    }
    else if (is_funcclause ((LispValue)clause)) 
      return ((Expr)make_funcclause (get_function ((LispValue)clause),
					replace_subclause_resultvar_refs 
					(get_funcargs ((LispValue)clause),
					 ltlist,
					 rtlist,
					 levelnum)));
    else if (not_clause ((LispValue)clause))
      return((Expr)make_notclause((List)replace_clause_resultvar_refs
				   ((Expr)get_notclausearg ((LispValue)clause),
					  ltlist,
					  rtlist,
					  levelnum)));
     else /*   operator clause */
      return((Expr)make_opclause(replace_opid((Oper)get_op((LispValue)clause)),
					(Var)replace_clause_resultvar_refs 
					((Expr)get_leftop((LispValue)clause),
					 ltlist,
					 rtlist,
					 levelnum),
					(Var)replace_clause_resultvar_refs 
					((Expr)get_rightop((LispValue)clause),
					 ltlist,
					 rtlist,
					 levelnum)));
}  /* function end  */

/*  .. new-result-qual, replace-clause-resultvar-refs	 */

LispValue
replace_subclause_resultvar_refs (clauses,ltlist,rtlist,levelnum)
     LispValue clauses,ltlist,rtlist;
     int levelnum ;
{
     LispValue t_list = LispNil;
     LispValue clause = LispNil;
     LispValue temp = LispNil;

     foreach(clause,clauses) {
	 temp = (LispValue)replace_clause_resultvar_refs ((Expr)CAR(clause),
							  ltlist,
							  rtlist,
							  levelnum);
	 t_list = nappend1(t_list,temp);
     }
     return(t_list);
}  /* function end  */

/*  .. replace-clause-resultvar-refs  	 */

Var
replace_resultvar_refs (var,ltlist,rtlist,levelnum)
     Var var;
     List ltlist,rtlist;
     int levelnum ;
{
     LispValue varno = LispNil;
     
     if (levelnum == numlevels (var)) 
	  varno = lispCons (lispInteger(levelnum - 1),
			    lispCons((LispValue)get_resno ((Resdom)tl_resdom 
						  (match_varid (var, ltlist))),
				     LispNil));
     else
       varno = lispCons (lispInteger(levelnum),
			 lispCons ((LispValue)get_resno ((Resdom)tl_resdom 
					  (match_varid (var, rtlist))),
				   LispNil));
     
     return (MakeVar ((Index)varno,
		      0,
		      get_vartype (var),
		      get_varid (var), (Pointer)NULL));
     
 } /* function end */

/*     
 *     	SUBPLAN REFERENCES
 *     
 */

/*    
 *    	set-tlist-references
 *    
 *    	Modifies the target list of nodes in a plan to reference target lists
 *    	at lower levels.
 *    
 *    	'plan' is the plan whose target list and children's target lists will
 *    		be modified
 *    
 *    	Returns nothing of interest, but modifies internal fields of nodes.
 *    
 */

/* query_planner, set-join-tlist-references, set-temp-tlist-references	 */

void
set_tlist_references (plan)
     Plan plan ;
{
    if(null (plan)) 
      LispNil;
    else 
      if (IsA (plan,Join)) 
	set_join_tlist_references ((Join)plan);
      else 
	if (IsA(plan,SeqScan) &&
	    get_lefttree (plan) && 
	    IsA (get_lefttree(plan),Temp))
	  set_tempscan_tlist_references((SeqScan)plan);
	else 
	  if (IsA (plan,Sort))
	    set_temp_tlist_references ((Temp)plan);
	  else if (IsA(plan,Result))
		set_result_tlist_references((Result)plan);
	    else if (IsA(plan,Hash))
		set_tlist_references((Plan)get_lefttree(plan));
	    else if (IsA(plan,Choose)) {
		LispValue x;
		foreach (x, get_chooseplanlist((Choose)plan)) {
		    set_tlist_references((Plan)CAR(x));
		  }
	      }

}   /* function end  */

/*    
 *    	set-join-tlist-references
 *    
 *    	Modifies the target list of a join node by setting the varnos and
 *    	varattnos to reference the target list of the outer and inner join
 *    	relations.
 *    
 *    	Creates a target list for a join node to contain references by setting 
 *    	varno values to OUTER or INNER and setting attno values to the 
 *    	result domain number of either the corresponding outer or inner join 
 *    	tuple.
 *    
 *    	'join' is a join plan node
 *    
 *   	Returns nothing of interest, but modifies internal fields of nodes.
 *    
 */

/*  .. set-tlist-references  	 */

void 
set_join_tlist_references (join)
     Join join ;
{
    Plan 	outer = (Plan)get_lefttree ((Plan)join);
    Plan	inner = (Plan)get_righttree ((Plan)join);
    List 	new_join_targetlist = LispNil;
    TLE		temp = (TLE)NULL;
    LispValue  	entry = LispNil;
    List	inner_tlist = NULL;
    List	outer_tlist = NULL;
    TLE	xtl = 	(TLE)NULL;
    List	qptlist = get_qptargetlist((Plan)join);

     foreach(entry,qptlist) {
	 xtl = (TLE)CAR(entry);
	 inner_tlist = ( null(inner) ? LispNil : get_qptargetlist(inner));
	 outer_tlist = ( null(outer) ? LispNil : get_qptargetlist(outer));

	 temp = MakeTLE(tl_resdom (xtl),
			replace_clause_joinvar_refs((LispValue)get_expr(xtl),
						     outer_tlist,
						     inner_tlist)
			);
	 new_join_targetlist = nappend1(new_join_targetlist,temp);
     }
	
    set_qptargetlist ((Plan)join,new_join_targetlist);
    if ( ! null (outer))
      set_tlist_references (outer);
    if ( ! null (inner))
      set_tlist_references (inner);
    
} /* function end  */

/*    
 *    	set-tempscan-tlist-references
 *    
 *    	Modifies the target list of a node that scans a temp relation (i.e., a
 *    	sort or hash node) so that the varnos refer to the child temporary.
 *    
 *    	'tempscan' is a seqscan node
 *    
 *    	Returns nothing of interest, but modifies internal fields of nodes.
 *    
 */

/*  .. set-tlist-references	 */

void
set_tempscan_tlist_references (tempscan)
     SeqScan tempscan ;
{

     Temp temp = (Temp)get_lefttree((Plan)tempscan);
     set_qptargetlist ((Plan)tempscan,
		       tlist_temp_references(get_tempid(temp),
					     get_qptargetlist((Plan)tempscan)));
     set_temp_tlist_references (temp);

}  /* function end  */

/*    
 *    	set-temp-tlist-references
 *    
 *    	The temp's vars are made consistent with (actually, identical to) the
 *    	modified version of the target list of the node from which temp node
 *    	receives its tuples.
 *    
 *    	'temp' is a temp (e.g., sort, hash) plan node
 *    
 *    	Returns nothing of interest, but modifies internal fields of nodes.
 *    
 */

/*  .. set-tempscan-tlist-references, set-tlist-references 	 */

void 
set_temp_tlist_references (temp)
     Temp temp ;
{
     Plan source = (Plan)get_lefttree ((Plan)temp);

     if (!null(source)) {
	 set_tlist_references (source);
	 set_qptargetlist ((Plan)temp,
			   copy_vars (get_qptargetlist ((Plan)temp),
				      get_qptargetlist (source)));
     }
     else
       printf (" WARN: calling set_temp_tlist_references with empty lefttree\n");
 }  /* function end  */

/*    
 *    	join-references
 *    
 *    	Creates a new set of join clauses by replacing the varno/varattno
 *    	values of variables in the clauses to reference target list values
 *    	from the outer and inner join relation target lists.
 *    
 *    	'clauses' is the list of join clauses
 *    	'outer-tlist' is the target list of the outer join relation
 *    	'inner-tlist' is the target list of the inner join relation
 *    
 *    	Returns the new join clauses.
 *    
 */

/* create_hashjoin_node, create_mergejoin_node, create_nestloop_node	 */

LispValue
join_references (clauses,outer_tlist,inner_tlist)
     LispValue clauses,outer_tlist,inner_tlist ;
{
     return (replace_subclause_joinvar_refs (clauses,
					     outer_tlist,
					     inner_tlist));
}  /* function end  */

/*    
 *    	index-outerjoin-references
 *    
 *    	Given a list of join clauses, replace the operand corresponding to the
 *    	outer relation in the join with references to the corresponding target
 *    	list element in 'outer-tlist' (the outer is rather obscurely
 *    	identified as the side that doesn't contain a var whose varno equals
 *    	'inner-relid').
 *    
 *    	As a side effect, the operator is replaced by the regproc id.
 *    
 *    	'inner-indxqual' is the list of join clauses (so-called because they
 *    	are used as qualifications for the inner (inbex) scan of a nestloop)
 *    
 *    	Returns the new list of clauses.
 *    
 */

/*  .. create_nestloop_node	 */

LispValue
index_outerjoin_references (inner_indxqual,outer_tlist,inner_relid)
     LispValue inner_indxqual,outer_tlist;
     Index inner_relid ;
{
    LispValue t_list = LispNil;
    LispValue temp = LispNil;
    LispValue t_clause = LispNil;
    LispValue clause = LispNil;

    foreach (t_clause,inner_indxqual) {
	clause = CAR(t_clause);
	/*
	 * if inner scan on the right.
	 */
	if(OperandIsInner((LispValue)get_rightop(clause), inner_relid)) {
	    temp = make_opclause(replace_opid((Oper)get_op (clause)),
				 (Var)replace_clause_joinvar_refs 
				  ((LispValue)get_leftop (clause),
				   outer_tlist,
				   LispNil),
				  get_rightop (clause));
	    t_list = nappend1(t_list,temp);
	} 
	else {   /*   inner scan on left */
	    temp = make_opclause(replace_opid((Oper)get_op(clause)),
				get_leftop (clause),
				(Var)replace_clause_joinvar_refs(
						(LispValue)get_rightop(clause),
						outer_tlist,
						LispNil));
	    t_list = nappend1(t_list,temp);
	} 
	
     }
    return(t_list);
     
}  /* function end  */

/*    
 *    	replace-clause-joinvar-refs
 *    	replace-subclause-joinvar-refs
 *    	replace-joinvar-refs
 *    
 *    	Replaces all variables within a join clause with a new var node
 *    	whose varno/varattno fields contain a reference to a target list
 *    	element from either the outer or inner join relation.
 *    
 *    	'clause' is the join clause
 *    	'outer-tlist' is the target list of the outer join relation
 *    	'inner-tlist' is the target list of the inner join relation
 *    
 *    	Returns the new join clause.
 *    
 */

/*  .. index-outerjoin-references, replace-clause-joinvar-refs
 *  .. replace-subclause-joinvar-refs, set-join-tlist-references
 */
LispValue
replace_clause_joinvar_refs (clause,outer_tlist,inner_tlist)
     LispValue clause,outer_tlist,inner_tlist ;
{
    LispValue temp = LispNil;
    if(IsA (clause,Var)) {
	if(temp = (LispValue)
	   replace_joinvar_refs((Var)clause,outer_tlist,inner_tlist))
	   return(temp);
	else
	  if (clause != LispNil)
	    return(clause);
	  else
	    return(LispNil);
    }
    else if (single_node ((Node)clause))
	return (clause);
    else if (or_clause (clause)) 
	return (make_orclause (replace_subclause_joinvar_refs 
				 (get_orclauseargs (clause),
				  outer_tlist,
				  inner_tlist)));
    else if (IsA(clause,ArrayRef)) {
	LispValue temp;
	temp = replace_clause_joinvar_refs(get_refindexpr((ArrayRef)clause),
					      outer_tlist,
					      inner_tlist);
	set_refindexpr((ArrayRef)clause,temp);
	temp = replace_clause_joinvar_refs(get_refexpr((ArrayRef)clause),
					      outer_tlist,
					      inner_tlist);
	set_refexpr((ArrayRef)clause,temp);
	return(clause);
      }
    else if (is_funcclause (clause)) {
	return (make_funcclause (get_function (clause),
				       replace_subclause_joinvar_refs 
						(get_funcargs (clause),
						outer_tlist,
						inner_tlist)));
       }
    else if (not_clause (clause)) 
	 return (make_notclause (replace_clause_joinvar_refs 
				      (get_notclausearg (clause),
					outer_tlist,
				       inner_tlist)));
    else if (is_clause (clause)) 
	 return (make_opclause(replace_opid((Oper)get_op(clause)),
				     (Var)replace_clause_joinvar_refs 
				     ((LispValue)get_leftop (clause),
				       outer_tlist,
				      inner_tlist),
				     (Var)replace_clause_joinvar_refs 
				     ((LispValue)get_rightop (clause),
				      outer_tlist,
				      inner_tlist)));
}  /* function end  */

/*  .. join-references, replace-clause-joinvar-refs	 */

LispValue
replace_subclause_joinvar_refs (clauses,outer_tlist,inner_tlist)
     LispValue clauses,outer_tlist,inner_tlist ;
{
     LispValue t_list = LispNil;
     LispValue temp = LispNil;
     LispValue clause = LispNil;

     foreach (clause,clauses) {
	  temp = replace_clause_joinvar_refs (CAR(clause),
					      outer_tlist,
					      inner_tlist);
	  t_list = nappend1(t_list,temp);
     }
     return(t_list);

} /* function end  */

/*  .. replace-clause-joinvar-refs 	 */

Var
replace_joinvar_refs (var,outer_tlist,inner_tlist)
     Var var;
     List outer_tlist,inner_tlist ;
{
    Resdom outer_resdom =(Resdom)NULL;

    outer_resdom= tlist_member(var,outer_tlist);

    if ( !null(outer_resdom) && IsA (outer_resdom,Resdom) ) {
	 return (MakeVar (OUTER,
			  get_resno (outer_resdom),
			  get_vartype (var),
			  get_varid (var),
			  0));
     } 
    else {
	Resdom inner_resdom;
	inner_resdom = tlist_member(var,inner_tlist);
	if ( !null(inner_resdom) && IsA (inner_resdom,Resdom) ) {
	    return (MakeVar (INNER,
			     get_resno (inner_resdom),
			     get_vartype (var),
			     get_varid (var),
				 0));
	 } 
    } 
    return (Var) NULL;

}  /* function end  */

/*    
 *    	tlist-temp-references
 *    
 *    	Creates a new target list for a node that scans a temp relation,
 *    	setting the varnos to the id of the temp relation and setting varids
 *    	if necessary (varids are only needed if this is a targetlist internal
 *    	to the tree, in which case the targetlist entry always contains a var
 *    	node, so we can just copy it from the temp).
 *    
 *    	'tempid' is the id of the temp relation
 *    	'tlist' is the target list to be modified
 *    
 *    	Returns new target list
 *    
 */

/*  .. set-tempscan-tlist-references, sort-level-result	 */

List
tlist_temp_references (tempid,tlist)
     ObjectId tempid;
     List tlist ;
{
     LispValue t_list = LispNil;
     TLE temp = (TLE)NULL;
     LispValue xtl = LispNil;
     LispValue var1 = LispNil;
     LispValue entry;
     
     foreach (entry,tlist) {
	 xtl = CAR(entry);
	 if ( IsA(get_expr (xtl),Var))
	   var1 = get_varid (get_expr (xtl));
	  else
	    var1 = LispNil;
	 
	 temp = (TLE)MakeTLE (tl_resdom (xtl),
			      MakeVar(tempid,
				      get_resno((Resdom)tl_resdom (xtl)),
				  get_restype ((Resdom)tl_resdom (xtl)),
				      var1,
					  0));
	 
	 t_list = nappend1(t_list,temp);
     }
     return(t_list);
 }  /* function end  */

/*---------------------------------------------------------
 *
 * set_result_tlist_references
 *
 * Change the target list of a Result node, so that it correctly
 * addresses the tuples returned by its left tree subplan.
 *
 * NOTE:
 *  1) we ignore the right tree! (in the current implementation
 *     it is always nil
 *  2) this routine will probably *NOT* work with nested dot
 *     fields....
 */
void
set_result_tlist_references(resultNode)
Result resultNode;
{
    Plan subplan;
    List resultTargetList;
    List subplanTargetList;
    LispValue t;
    TLE entry;
    Expr expr;

    resultTargetList= get_qptargetlist((Plan)resultNode);

    /*
     * NOTE: we only consider the left tree subplan.
     * This is usually a seq scan.
     */
    subplan = (Plan)get_lefttree((Plan)resultNode);
    if (subplan != NULL) {
	subplanTargetList = get_qptargetlist(subplan);
    } else {
	subplanTargetList = LispNil;
    }

    /*
     * now for traverse all the entris of the target list.
     * These should be of the form (Resdom_Node Expression).
     * For every expression clause, call "replace_result_clause()"
     * to appropriatelly change all the Var nodes.
     */
    foreach (t, resultTargetList) {
	entry = (TLE) CAR(t);
	expr = (Expr) get_expr(entry);
	replace_result_clause((LispValue)expr, subplanTargetList);
    }

}

/*---------------------------------------------------------
 *
 * replace_result_clause
 *
 * This routine is called from set_result_tlist_references().
 * and modifies the expressions of the target list of a Result
 * node so that all Var nodes reference the target list of its subplan.
 * 
 */

void
replace_result_clause(clause, subplanTargetList)
LispValue clause;
List subplanTargetList;		/* target list of the subplan */
{
    
    Expr subExpr;
    LispValue t;
    List subClause;
    LispValue subplanVar;

    if (IsA(clause,Var)) {
	/*
	 * Ha! A Var node!
	 */
	subplanVar = match_varid((Var)clause, subplanTargetList);
	/*
	 * Change the varno & varattno fields of the
	 * var node.
	 *
	 */
	set_varno((Var)clause,(Index) OUTER);
	set_varattno((Var)clause, get_resno((Resdom)tl_resdom(subplanVar)));
    } else if (is_funcclause(clause)) {
	/*
	 * This is a function. Recursively call this routine
	 * for its arguments...
	 */
	subClause = get_funcargs(clause);
	foreach (t, subClause) {
	    replace_result_clause(CAR(t),subplanTargetList);
	   }
    } else if (IsA(clause,ArrayRef)) {
	/*
	 * This is an arrayref. Recursively call this routine
	 * for its expression and its index expression...
	 */
	replace_result_clause(get_refindexpr((ArrayRef)clause),
			      subplanTargetList);
	replace_result_clause(get_refexpr((ArrayRef)clause),
			      subplanTargetList);
    } else if (is_clause(clause)) {
	/*
	 * This is an operator. Recursively call this routine
	 * for both its left and right operands
	 */
	subClause = (List)get_leftop(clause);
	replace_result_clause(subClause,subplanTargetList);
	subClause = (List)get_rightop(clause);
	replace_result_clause(subClause,subplanTargetList);
    } else if (IsA(clause,Param) || IsA(clause,Const)) {
	/* do nothing! */
    } else {
	/*
	 * Ooops! we can not handle that!
	 */
	elog(WARN,"replace_result_clause: Can not handle this tlist!\n");
    }
}

bool
OperandIsInner(opnd, inner_relid)
    LispValue opnd;
    int inner_relid;
{

    /*
     * Can be the inner scan if its a varnode or a function and the
     * inner_relid is equal to the varnode's var number or in the 
     * case of a function the first argument's var number (all args
     * in a functional index are from the same relation).
     */
    if ( IsA (opnd,Var) && 
	 (inner_relid == get_varno((Var)opnd)) )
    {
	return true;
    }
    if ( consp(opnd) && IsA(CAR(opnd),Func) )
    {
	LispValue firstArg = CAR(get_funcargs(opnd));

	if ( IsA (firstArg,Var) &&
	     (inner_relid == get_varno((Var)firstArg)) )
	{
		return true;
	}
    }
    return false;
}
