/* ----------------------------------------------------------------
 *   FILE
 *	qual.c
 *	
 *   DESCRIPTION
 *	Routines to evaluate qualification and targetlist expressions
 *
 *   INTERFACE ROUTINES
 *    	ExecQual	- return true/false if qualification is satisified
 *    	ExecTargetList	- form a new tuple by projecting the given tuple
 *    	ExecEvalExpr	- evaluate an expression and return a Const value
 *
 *   NOTES
 *	
 *   IDENTIFICATION
 *	$Header: RCS/qual.c,v 1.38 90/10/01 07:44:18 cimarron Exp $
 * ----------------------------------------------------------------
 */

#include "tmp/postgres.h"

 RcsId("$Header: RCS/qual.c,v 1.38 90/10/01 07:44:18 cimarron Exp $");

/* ----------------
 *	FILE INCLUDE ORDER GUIDELINES
 *
 *	1) execdebug.h
 *	2) various support files ("everything else")
 *	3) node files
 *	4) catalog/ files
 *	5) execdefs.h and execmisc.h
 *	6) x_ extern files come last.
 * ----------------
 */
#include "executor/execdebug.h"

#include "parser/parsetree.h"
#include "utils/log.h"
#include "utils/mcxt.h"

#include "nodes/pg_lisp.h"
#include "nodes/primnodes.h"
#include "nodes/primnodes.a.h"
#include "nodes/plannodes.h"
#include "nodes/plannodes.a.h"
#include "nodes/execnodes.h"
#include "nodes/execnodes.a.h"
#include "nodes/mnodes.h"

#include "catalog/syscache.h"
#include "catalog/pg_type.h"
#include "catalog/pg_proc.h"

#include "executor/execdefs.h"
#include "executor/execmisc.h"

#include "executor/x_qual.h"

/* ----------------
 *	sysattrlen
 *	sysattrbyval
 *
 *	these have been moved to access/tuple/tuple.c
 * ----------------
 */
extern int  sysattrlen();
extern bool sysattrbyval();

/* ----------------------------------------------------------------
 *    	ExecQualClause
 *
 *	this is a workhorse for ExecQual.  ExecQual has to deal
 *	with a list of qualifications, so it passes each qualification
 *	in the list to this function one at a time.  ExecQualClause
 *	returns true when the qualification *fails* and false if
 *	the qualification succeeded (meaning we have to test the
 *	rest of the qualification)
 * ----------------------------------------------------------------
 */
/**** xxref:
 *           ExecQual
 ****/
bool
ExecQualClause(clause, econtext)
    List clause;
    ExprContext econtext;
{
    Const  expr_value;
    Datum  const_value;
    
    expr_value = ExecEvalExpr(clause, econtext);
    
    /* ----------------
     *	this is interesting behaviour here.  When a clause evaluates
     *  to null, then we consider this as passing the qualification.
     *  it seems kind of like, if the qual is NULL, then there's no
     *  qual.. 
     * ----------------
     */
    if (!non_null(expr_value))
	return true;
    
    /* ----------------
     *  remember, we return true when the qualification fails..
     * ----------------
     */
    const_value = get_constvalue(expr_value);
    if (ExecCFalse(DatumGetInt32(const_value)))
	return true;
    
    return false;
}
 
/* ----------------------------------------------------------------
 *    	ExecQual
 *    
 *    	Evaluates a conjunctive boolean expression and returns t
 *	iff none of the subexpressions are false (or null).
 * ----------------------------------------------------------------
 */
 
/**** xxref:
 *           ExecMergeJoin
 *           ExecNestLoop
 *           ExecResult
 *           ExecScan
 ****/
bool
ExecQual(qual, econtext)
    List 	  qual;
    ExprContext	  econtext;
{
    List 		clause;
    bool 		result;
    MemoryContext	oldContext;
    extern GlobalMemory ExecTargetListContext;

    /* ----------------
     *	debugging stuff
     * ----------------
     */
    EV_printf("ExecQual: qual is ");
    EV_lispDisplay(qual);
    EV_printf("\n");

    /* ----------------
     *	change to the ExecTargetList context before doing any
     *  qualification processing..
     * ----------------
     */
    oldContext = MemoryContextSwitchTo(ExecTargetListContext);
        
    /* ----------------
     *  a "qual" is a list of clauses.  To evaluate the
     *  qual, we evaluate each of the clauses in the list.
     *
     *  ExecQualClause returns true when we know the qualification
     *  *failed* so we just pass each clause in qual to it until
     *  we know the qual failed or there are no more clauses.
     * ----------------
     */
    result = false;
    foreach (clause, qual) {
	result = ExecQualClause((Expr) CAR(clause), econtext);
	if (result == true) {
	    break;
	}
    }

    /* ----------------
     *	switch back to our previous memory context
     * ----------------
     */
    (void) MemoryContextSwitchTo(oldContext);

    /* ----------------
     *	free the allocations made during the qualification
     *  processing.
     * ----------------
     */
    AllocSetReset(&ExecTargetListContext->setData);

    /* ----------------
     *	if result is true, then it means a clause failed so we
     *  return false.  if result is false then it means no clause
     *  failed so we return true.
     * ----------------
     */
    if (result == true)
	return false;
    
    return true;
}
 
/* ----------------------------------------------------------------
 *    	ExecTargetList
 *    
 *    	Evaluates a targetlist with respect to the current
 *	expression context and return a tuple.
 * ----------------------------------------------------------------
 */
 
/**** xxref:
 *           ExecMergeJoin
 *           ExecNestLoop
 *           ExecResult
 *           ExecScan
 ****/
HeapTuple
ExecTargetList(targetlist, nodomains, targettype, values, econtext)
    List 	 targetlist;
    int 	 nodomains;
    AttributePtr targettype;
    Pointer 	 values;
    ExprContext	 econtext;
{
    char      		*nulls;
    char		*np;
    List 		tl;
    List 		tle;
    int			len;
    Expr 		expr;
    Resdom    	  	resdom;
    AttributeNumber	resno;
    Const 		expr_value;
    Datum 		constvalue;
    HeapTuple 		newTuple;

    MemoryContext	oldContext;
    extern GlobalMemory ExecTargetListContext;

    /* ----------------
     *	debugging stuff
     * ----------------
     */
    EV_printf("ExecTargetList: tl is ");
    EV_lispDisplay(targetlist);
    EV_printf("\n");

    /* ----------------
     * Return a dummy tuple if the targetlist is empty 
     * the dummy tuple is necessary to differentiate 
     * between passing and failing the qualification.
     * ----------------
     */
    if (lispNullp(targetlist)) {
	/* ----------------
	 *	I now think that the only time this makes
	 *	any sence is when we run a delete query.  Then
	 *	we need to return something other than nil
	 *	so we know to delete the tuple associated
	 *      with the saved tupleid.. see what ExecutePlan
	 *      does with the returned tuple.. -cim 9/21/89
	 * ----------------
	 */
	CXT1_printf("ExecTargetList: context is %d\n", CurrentMemoryContext);
	return (HeapTuple)
	    ExecAlloc("ExecTargetList", 1);
    }

    /* ----------------
     *	change to the ExecTargetList context before doing any
     *  target list processing..
     * ----------------
     */
    oldContext = MemoryContextSwitchTo(ExecTargetListContext);
    
    /* ----------------
     *  allocate an array of char's to hold the "null" information
     *  XXX we should use alloca() for this -cim 6/25/90
     * ----------------
     */
    len = length(targetlist) + 1;
    nulls = (char *) ExecAlloc("ExecTargetList", len);
    bzero(nulls, len);
    np = nulls;

    /* ----------------
     *	evaluate all the expressions in the target list
     * ----------------
     */
    EV_printf("ExecTargetList: setting target list values\n");
    
    for (tl = targetlist; !lispNullp(tl) ; tl = CDR(tl)) {
	/* ----------------
	 *    remember, a target list is a list of lists:
	 *
	 *	((resdom expr) (resdom expr) ...)
	 *
	 *    tl is a pointer to successive cdr's of the targetlist
	 *    tle is a pointer to the target list entry in tl
	 * ----------------
	 */
	tle = CAR(tl);
	if (lispNullp(tle))
	    break;
      
	expr = 		(Expr)   tl_expr(tle);
	resdom = 	(Resdom) tl_resdom(tle);
	resno = 	get_resno(resdom);
	expr_value = 	ExecEvalExpr(expr, econtext);
	constvalue = 	get_constvalue(expr_value);

	ExecSetTLValues(resno - 1, values, constvalue);
	
	if (non_null(expr_value))
	    *np++ = ' ';
	else
	    *np++ = 'n';
    }
    *np = '\0';

    /* ----------------
     *	switch back to our previous memory context
     * ----------------
     */
    (void) MemoryContextSwitchTo(oldContext);
    
    /* ----------------
     * 	form the new result tuple (in the "normal" context)
     * ----------------
     */
    newTuple = (HeapTuple)
	formtuple(nodomains, targettype, values, nulls);

    /* ----------------
     *	free the allocations made during the target list
     *  processing.
     * ----------------
     */
    AllocSetReset(&ExecTargetListContext->setData);
    
    return
	newTuple;
}
 
/* ----------------------------------------------------------------
 *		ExecEvalExpr support routines
 * ----------------------------------------------------------------
 */
/* ----------------------------------------------------------------
 *    	ExecMakeVarConst
 *    
 *    	if the result is non-null, returns a Const node containing
 *	the value of the desired attribute of the given tuple.
 *
 *	currently this routine doesn't use econtext, but the rule
 *	manager will have to use it..
 * ----------------------------------------------------------------
 */
/**** xxref:
 *           ExecEvalVar
 ****/
Const
ExecMakeVarConst(slot, attnum, tuple_type, buffer, econtext)
    TupleTableSlot	slot;
    AttributeNumber 	attnum;
    AttributePtr	tuple_type;
    Buffer 		buffer;
    ExprContext         econtext; /* LLL econtext unused */
{
    HeapTuple	heapTuple;
    ObjectId	typeoid;
    Const   	result;
    Boolean 	isNull;
    bool	byval;
    Pointer	value;
    int16   	len;
    
    /* ----------------
     *   extract the tuple from the slot containing it..
     * ----------------
     */
    heapTuple = (HeapTuple) ExecFetchTuple(slot);
    
    /* ----------------
     *	get the attribute our of the tuple.
     * ----------------
     */
    value =  (Pointer)
	amgetattr(heapTuple,	/* tuple containing attribute */
		  buffer,	/* buffer associated with tuple */
		  attnum,	/* attribute number of desired attribute */
		  tuple_type,	/* tuple descriptor of tuple */
		  &isNull);	/* return: is attribute null? */
    
    /* ----------------
     *   get length and type information..
     *   ??? what should we do about variable length attributes
     *	 - variable length attributes have their length stored
     *     in the first 4 bytes of the memory pointed to by the
     *     returned value.. If we can determine that the type
     *     is a variable length type, we can do the right thing.
     *	   -cim 9/15/89
     * ----------------
     */
    if (attnum < 0) {
	/* ----------------
	 *  If this is a pseudo-att, we get the type and fake the length.
	 *  There ought to be a routine to return the real lengths, so
	 *  we'll mark this one ... XXX -mao
	 * ----------------
	 */
        typeoid = att_typeid((Relation) NULL, attnum);
        len = 	sysattrlen(attnum);  	/* XXX see -mao above */
	byval = sysattrbyval(attnum);	/* XXX see -mao above */
    } else {
        typeoid = tuple_type[ attnum-1 ]->atttypid;
        len =     tuple_type[ attnum-1 ]->attlen;
	byval =   tuple_type[ attnum-1 ]->attbyval ? true : false ;
    }

    result = MakeConst(typeoid, len, value, (bool) isNull, byval);
    
    return result;
}
 
/* ----------------------------------------------------------------
 *    	ExecEvalVar
 *    
 *    	Returns a Const node whose value is the value of a range
 *	variable with respect to given expression context.
 * ----------------------------------------------------------------
 */
 
/**** xxref:
 *           ExecEvalExpr
 ****/
Const
ExecEvalVar(variable, econtext)
    Expr 	variable;
    ExprContext econtext;
{
    Const	    result;
    TupleTableSlot  slot;
    AttributePtr    tupType;
    Buffer	    tupBuffer;
    AttributeNumber no;
    
    /* ----------------
     *	here the var node represents referencing a simple attribute
     * ----------------
     */
    if (var_is_inner(variable)) {
	/* ----------------
	 *	get the tuple from the inner node
	 * ----------------
	 */
	slot =      get_ecxt_innertuple(econtext);
	tupType =   get_ecxt_innertype(econtext);
	tupBuffer = InvalidBuffer;
	no = 	    get_varattno(variable);
	
	EV5_printf("%s slot=%d tupType=%d tupBuffer=%d no=%d\n",
		   "ExecEvalVar: INNER", slot, tupType, tupBuffer, no);
	
    } else if (var_is_outer(variable)) {
	/* ----------------
	 *	get the tuple from the outer node
	 * ----------------
	 */
	slot =      get_ecxt_outertuple(econtext);
	tupType =   get_ecxt_outertype(econtext);
	tupBuffer = InvalidBuffer;
	no = 	    get_varattno(variable);
	
	EV5_printf("%s slot=%d tupType=%d tupBuffer=%d no=%d\n",
		   "ExecEvalVar: OUTER", slot, tupType, tupBuffer, no);
	
    } else if (var_is_rel(variable)) {
	/* ----------------
	 *	get the tuple from the relation being scanned
	 * ----------------
	 */
	slot =      get_ecxt_scantuple(econtext);
	tupType =   get_ecxt_scantype(econtext);
	tupBuffer = get_ecxt_scan_buffer(econtext);
	no = 	    get_varattno(variable);
	
	EV5_printf("%s slot=%d tupType=%d tupBuffer=%d no=%d\n",
		   "ExecEvalVar: SCAN", slot, tupType, tupBuffer, no);
    } else {
	elog(DEBUG, "ExecEvalVar: unknown variable type!");
	return NULL;
    }
    
    /* ----------------
     *	if the var node represents a reference into an
     *  array, then we have to evaluate the array reference.
     * ----------------
     */
    if (var_is_array(variable)) {
	result = (Const)
	    ExecEvalArrayRef(variable,
			     slot,				  
			     tupType,
			     tupBuffer,
			     econtext);
    } else {
	result = (Const)
	    ExecMakeVarConst(slot,
			     no,
			     tupType,
			     tupBuffer,
			     econtext);
    }
    
    return result;
}
 
/* ----------------------------------------------------------------
 *      ExecEvalParam
 *
 *      Returns the value of a parameter.  A param node contains
 *	something like ($.name) and the expression context contains
 *	the current parameter bindings (name = "sam") (age = 34)...
 *	so our job is to replace the param node with a const node
 *	containing the appropriate information ("sam").
 *
 *	Q: if we have a parameter ($.foo) without a binding, i.e.
 *	   there is no (foo = xxx) in the parameter list info,
 *	   is this a fatal error or should this be a "not available"
 *	   (in which case we shoud return a Const node with the
 *	    isnull flag) ?  -cim 10/13/89
 *
 *      Minor modification: Param nodes now have an extra field,
 *      `paramkind' which specifies the type of parameter 
 *      (see params.h). So while searching the paramList for
 *      a paramname/value pair, we have also to check for `kind'.
 *     
 *      NOTE: The last entry in `paramList' is always an
 *      entry with kind == PARAM_INVALID.
 * ----------------------------------------------------------------
 */
/**** xxref:
 *           ExecEvalExpr
 ****/
Const
ExecEvalParam(expression, econtext)
    Expr 	expression;
    ExprContext econtext;
{
    static ObjectId 	cached_type;
    static bool		cached_typbyval;

    Name 	  thisParameterName;
    int		  thisParameterKind;
    int32	  thisParameterId;
    int 	  i;
    int 	  matchFound;
    Const 	  constNode;
    ParamListInfo paramList;
    bool	  byval;
    
    thisParameterName = get_paramname(expression);
    thisParameterKind = get_paramkind(expression);
    thisParameterId =   get_paramid(expression);
    paramList = 	get_ecxt_param_list_info(econtext);
    
    /*
     * search the list with the parameter info to find a matching name.
     * An entry with an InvalidName denotes the last element in the array.
     */
    matchFound = 0;
    if (paramList != NULL) {
	/*
	 * search for an entry in 'paramList' that matches
	 * the `expression'.
	 */
	while(paramList->kind != PARAM_INVALID && !matchFound) {
	    switch (thisParameterKind) {
		case PARAM_NAMED:
		case PARAM_OLD:
		case PARAM_NEW:
		    if (thisParameterKind == paramList->kind &&
		        NameIsEqual(paramList->name, thisParameterName)){
			    matchFound = 1;
		    } 
		    break;
		case PARAM_NUM:
		    if (thisParameterKind == paramList->kind &&
			paramList->id == thisParameterId) {
			    matchFound = 1;
		    }
		    break;
		default:
		    /*
		     * oops! this is not supposed to happen!
		     */
		    elog(WARN, "ExecEvalParam: invalid paramkind %d",
		    thisParameterKind);
	    }
	    if (!matchFound) {
		paramList++;
	    }
	} /*while*/
    } /*if*/
    
    if (!matchFound) {
	/*
	 * ooops! we couldn't find this parameter
	 * in the parameter list. Signal an error
	 */
	elog(WARN, "ExecEvalParam: Unknown value for parameter %s",
	     thisParameterName);
    }
    
    /*
     * create a Const node.
     * and fill it with the right values
     */

    /* ----------------
     *	here we get the type-by-value information.  this should
     *  be stored in the paramList but spyros will do that..
     * ----------------
     */
    if (cached_type != paramList->type) {
	HeapTuple	typeTuple;
	TypeTupleForm	typeStruct;
	
	/* ----------------
	 *   get the type tuple corresponding to the paramList->type,
	 *   If this fails, returnValue has been pre-initialized
	 *   to "null" so we just return it.
	 * ----------------
	 */
	typeTuple = SearchSysCacheTuple(TYPOID,
					paramList->type,
					NULL,
					NULL,
					NULL);

	/* ----------------
	 *   get the type length and by-value from the type tuple and
	 *   save the information in our one element cache.
	 * ----------------
	 */
	Assert(PointerIsValid(typeTuple));
	
	typeStruct = (TypeTupleForm) GETSTRUCT(typeTuple);
	cached_typbyval = (typeStruct)->typbyval ? true : false ;
	cached_type = paramList->type;
    }
    byval = cached_typbyval;
    
    constNode = MakeConst(paramList->type, 	/* type */
			  paramList->length,    /* length */
			  paramList->value,     /* value */
			  paramList->isnull,	/* is-null flag */
			  byval); 		/* passed-by-value */
    return(constNode);
}
 
/* ----------------------------------------------------------------
 *    	ExecEvalOper
 *    	ExecEvalFunc
 *    
 *    	Evaluate the functional result of a list of arguments by calling the
 *    	function manager.  Note that in the case of operator expressions, the
 *    	optimizer had better have already replaced the operator OID with the
 *    	appropriate function OID or we're hosed.
 *
 * old comments
 *    	Presumably the function manager will not take null arguments, so we
 *    	check for null arguments before sending the arguments to (fmgr).
 *    
 *    	Returns the value of the functional expression.
 *    
 * ----------------------------------------------------------------
 */
 
/**** xxref:
 *           ExecEvalOper
 *           ExecEvalFunc
 ****/
Const
ExecMakeFunctionResult(functionOid, returnType, arguments, econtext)
    ObjectId 	functionOid;
    ObjectId 	returnType;
    List 	arguments;
    ExprContext econtext;
{
    static ObjectId 	cached_functionOid;
    static int		cached_typlen;
	static int		cached_nargs = 0;
    static bool		cached_typbyval;
    
    List	arg;
    List	nextValue;
    List	parameterValues;
    Datum	functionValue;
    Datum	*args;
    Const	returnValue;
    Const	expr_value;
    Size	size;
    bool	isNull;
    bool	byval;
    bool	needGetAttribute = false; 
    int		numberArguments;
    int		i;

    /* ----------------
     *  initialize returnValue to the proper type of "null" constant
     *  this kind of null is different than NULL - it indicates that
     *  the value of the function is "not available".  See Date's
     *  text for more information on this wierd kind of NULL value..
     * ----------------
     */
    nextValue =       LispNil;
    parameterValues = LispNil;
    numberArguments = 0;
    returnValue =     MakeConst(returnType, 0, NULL, true, true);
    
    /* ----------------
     *  We need to check CAR(arg) to see if it is a RELATION type, in which
     *  case we need to call the stuff to set up GetAttribute as well as
     *  shift the argument list to handle the special case for tuple arguments.
     * ----------------
     */
    if (ArgumentIsRelation(arguments)) {
	arguments = CDR(arguments);
	SetCurrentTuple(econtext);
    }

    /* ----------------
     *	 get information about the return type.  here we implement
     *   a one-bucket cache and pray that that's good enough to
     *   avoid hitting the system cache too often.  The correct thing
     *   to do would be to save the length and size information at
     *   the time we figure out the functionOid and then use it.
     *   -cim 5/31/90
     * ----------------
     */
    if (cached_functionOid != functionOid) {
	HeapTuple	procedureTuple;
	HeapTuple	typeTuple;
	struct proc	*procedureStruct;
	TypeTupleForm	typeStruct;
	
	/* ----------------
	 *   get the procedure tuple corresponding to the given
	 *   functionOid.  If this fails, returnValue has been
	 *   pre-initialized to "null" so we just return it.
	 * ----------------
	 */
	procedureTuple = SearchSysCacheTuple(PROOID,
					     (char *) functionOid,
					     NULL,
					     NULL,
					     NULL);
    
	if (!HeapTupleIsValid(procedureTuple)) {
	    elog(WARN, "ExecMakeFunctionResult: %s %d",
		 "Cache lookup failed for procedure",
		 functionOid);
	    return returnValue;
	}
	
	/* ----------------
	 *   get the return type from the procedure tuple
	 * ----------------
	 */
	procedureStruct = (struct proc *) GETSTRUCT(procedureTuple);
	
	/* ----------------
	 *   get the type tuple corresponding to the return type
	 *   If this fails, returnValue has been pre-initialized
	 *   to "null" so we just return it.
	 * ----------------
	 */
	typeTuple = SearchSysCacheTuple(TYPOID,
					(procedureStruct)->prorettype,
					NULL,
					NULL,
					NULL);

	cached_nargs = procedureStruct->pronargs;

	if (!HeapTupleIsValid(typeTuple)) {
	    elog(WARN, "ExecMakeFunctionResult: %s %d",
		 "Cache lookup failed for type",
		 (procedureStruct)->prorettype);
	    return returnValue;
	}
	
	/* ----------------
	 *   get the type length and by-value from the type tuple and
	 *   save the information in our one element cache.
	 * ----------------
	 */
	typeStruct = (TypeTupleForm) GETSTRUCT(typeTuple);
	cached_typlen =   (typeStruct)->typlen;
	cached_typbyval = (typeStruct)->typbyval ? true : false ;
	cached_functionOid = functionOid;
    }

    /* ----------------
     *	arguments is a list of expressions to evaluate
     *  before passing to the function manager.
     *  We collect the results of evaluating the expressions
     *  into a datum array (args) and pass this array to arrayFmgr()
     * ----------------
     */
    args = (Datum *) palloc(sizeof(Datum) * cached_nargs);
    i = 0;

    foreach (arg, arguments) {
	/* ----------------
	 *   evaluate the expression
	 * ----------------
	 */
	expr_value = ExecEvalExpr(CAR(arg), econtext);

	if (non_null(expr_value)) {
	    /* ----------------
	     *   if it's not null, add it to the list of arguments
	     *   passed to the function manager..
	     * ----------------
	     */
		args[i++] = get_constvalue(expr_value);
	} else {
	    /* ----------------
	     *  returnValue has been pre-initialized to "null" so
	     *  we just return that if the expression is null.
	     *
	     *  "null" values are never passed to functions -- the
	     *  user has to explicitly deal with these with a
	     *  "IS NULL" qualification of some sort.  Note: as
	     *  of 5/31/90, there is no way to do that -cim
	     * ----------------
	     */
	    return returnValue;
	}
    }

    numberArguments = i;

    /* ----------------
     *	at this point we know our cached values are up to date
     *  so we use them...
     * ----------------
     */
    size =  cached_typlen;
    byval = cached_typbyval;
    
    /* ----------------
     *   now call the function through the function manager, passing
     *   the function the evaluated parameter values. 
     * ----------------
     */
    functionValue = arrayFmgr(functionOid,
			     numberArguments,
			     args,
			     &isNull);
    
    /* ----------------
     *   if function value is non-null, store it into returnValue,
     *   otherwise just return returnValue (which is null)
     * ----------------
     */
    if (!isNull) {
	set_constvalue(returnValue,  functionValue);
	set_constlen(returnValue,    size);
	set_constisnull(returnValue, false);
	set_constbyval(returnValue,  byval);
    }

    pfree(args);
    
    return returnValue;
}
 
/* ----------------------------------------------------------------
 *	ExecEvalOper
 * ----------------------------------------------------------------
 */
/**** xxref:
 *           ExecEvalExpr
 ****/
Const
ExecEvalOper(opClause, econtext)
    List        opClause;
    ExprContext econtext;
{
    Oper	op; 
    ObjectId	functionOid;
    ObjectId	resultTypeOid;
    List	argList;
    Const	functionResult;
    
    /* ----------------
     *  an opclause is a list (op args).  (I think)
     *
     *  we extract the oid of the function associated with
     *  the op and then pass the work onto ExecMakeFunctionResult
     *  which evaluates the arguments and returns the result of
     *  calling the function on the evaluated arguments.
     * ----------------
     */
    op = (Oper) get_op(opClause);
    if (!IsA(op,Oper))
	elog(DEBUG, "ExecEvalOper: bad opClause - op is not an Oper!");
    
    functionOid =   get_opno(op);
    resultTypeOid = get_opresulttype(op);
    argList =       (List) get_opargs(opClause);
    
    functionResult =
	ExecMakeFunctionResult(functionOid,
			       resultTypeOid,
			       argList,
			       econtext);
    return functionResult;
}
 
/* ----------------------------------------------------------------
 *	ExecEvalFunc
 * ----------------------------------------------------------------
 */
 
/**** xxref:
 *           ExecEvalExpr
 ****/
Const
ExecEvalFunc(funcClause, econtext)
    Expr        funcClause;
    ExprContext econtext;
{
    Func	func;
    ObjectId	functionOid;
    ObjectId	resultTypeOid;
    List	argList;
    Const	functionResult;
    
    /* ----------------
     *  an funcclause is a list (func args).  (I think)
     *
     *  we extract the oid of the function associated with
     *  the func node and then pass the work onto ExecMakeFunctionResult
     *  which evaluates the arguments and returns the result of
     *  calling the function on the evaluated arguments.
     *
     *  this is nearly identical to the ExecEvalOper code.
     * ----------------
     */
    func =	    (Func) get_function(funcClause);
    if (!IsA(func,Func))
	elog(DEBUG, "ExecEvalFunc: bad funcClause - func is not a Func!");
    
    functionOid =   get_funcid(func);
    resultTypeOid = get_functype(func);
    argList =       (List) get_funcargs(funcClause);
    functionResult =
	ExecMakeFunctionResult(functionOid,
			       resultTypeOid,
			       argList,
			       econtext);
    return functionResult;
}
 
/* ----------------------------------------------------------------
 *    	ExecEvalNot
 *    	ExecEvalOr
 *    
 *    	Evaluate boolean expressions.  Evaluation of 'or' is
 *	short-circuited when the first true (or null) value is found.
 * ----------------------------------------------------------------
 */
/**** xxref:
 *           ExecEvalExpr
 ****/
Const
ExecEvalNot(notclause, econtext)
    List 	notclause;
    ExprContext econtext;
{
    Const expr_value;
    Datum const_value;
    List  clause;
    
    clause = 	 (List) get_notclausearg(notclause);
    expr_value = ExecEvalExpr(clause, econtext);
    
    /* ----------------
     *	if the expression evaluates to null, then we just
     *  cascade the null back to whoever called us.
     * ----------------
     */
    if (!non_null(expr_value))
	return expr_value;
    
    /* ----------------
     *  evaluation of 'not' is simple.. expr is false, then
     *  return 'true' and vice versa.
     * ----------------
     */
    const_value = get_constvalue(expr_value);
    if (ExecCFalse(DatumGetInt32(const_value)))
	return ConstTrue;
   
    return ConstFalse;
}
 
/* ----------------------------------------------------------------
 *	ExecEvalOr
 * ----------------------------------------------------------------
 */
 
/**** xxref:
 *           ExecEvalExpr
 ****/
Const
ExecEvalOr(orExpr, econtext)
    List 	orExpr;
    ExprContext econtext;
{
    List   clauses;
    List   clause;
    Const  expr_value;
    Datum  const_value;
    
    clauses = (List) get_orclauseargs(orExpr);
    
    /* ----------------
     *	we evaluate each of the clauses in turn,
     *  as soon as one is true we return that
     *  value.  If none are true then we return
     *  the value of the last clause evaluated, which
     *  should be false. -cim 8/31/89
     * ----------------
     */
    foreach (clause, clauses) {
	expr_value = ExecEvalExpr(CAR(clause), econtext);
	/* ----------------
	 *  if the expression evaluates to null, then we just
	 *  cascade the null back to whoever called us.
	 * ----------------
	 */
	if (!non_null(expr_value))
	    return expr_value;
	
	/* ----------------
	 *   if we have a non-false result, then we return it.
	 * ----------------
	 */
	const_value = get_constvalue(expr_value);
	if (! ExecCFalse(DatumGetInt32(const_value)))
	    return expr_value;
    }
    
    return expr_value;
}
 
/* ----------------------------------------------------------------  
 *    	ExecEvalExpr
 *    
 *    	Recursively evaluate a targetlist or qualification expression.
 * ----------------------------------------------------------------
 */
 
/**** xxref:
 *           ExecUpdateIndexScanKeys
 *           MergeCompare
 *           ExecQualClause
 *           ExecTargetList
 *           ExecMakeFunctionResult
 *           ExecEvalNot
 *           ExecEvalOr
 ****/
Const
ExecEvalExpr(expression, econtext)
    Node 	expression;
    ExprContext econtext;
{
    /* ----------------
     *	here we dispatch the work to the appropriate type
     *  of function given the type of our expression.
     * ----------------
    */
    if (expression == NULL)
	return ConstTrue;
   
    if (IsA(expression,Const))		/* was: if (const_p(expression)) .. */
	return (Const) expression;
    
    if (IsA(expression,Var))		/* was: if (var_p(expression)) */
	return ExecEvalVar(expression, econtext);
   
    if (is_clause(expression))	  	/* car is a Oper node */
	return ExecEvalOper(expression, econtext);
    
    if (is_funcclause(expression)) 	/* car is a Func node */
	return ExecEvalFunc(expression, econtext);
      
    if (or_clause(expression))
	return ExecEvalOr(expression, econtext);
   
    if (not_clause(expression))
	return  ExecEvalNot(expression, econtext);
    
    if (IsA(expression,Param))
	return  ExecEvalParam(expression, econtext);
   
    elog(DEBUG, "ExecEvalExpr: unknown expression type");
    
    return (Const) NULL;
}
