Skip to content
Snippets Groups Projects
Select Git revision
  • benchmark-tools
  • postgres-lambda
  • master default
  • REL9_4_25
  • REL9_5_20
  • REL9_6_16
  • REL_10_11
  • REL_11_6
  • REL_12_1
  • REL_12_0
  • REL_12_RC1
  • REL_12_BETA4
  • REL9_4_24
  • REL9_5_19
  • REL9_6_15
  • REL_10_10
  • REL_11_5
  • REL_12_BETA3
  • REL9_4_23
  • REL9_5_18
  • REL9_6_14
  • REL_10_9
  • REL_11_4
23 results

execQual.c

Blame
  • execQual.c 41.19 KiB
    /*-------------------------------------------------------------------------
     *
     * execQual.c
     *	  Routines to evaluate qualification and targetlist expressions
     *
     * Copyright (c) 1994, Regents of the University of California
     *
     *
     * IDENTIFICATION
     *	  $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.55 1999/07/15 22:39:06 momjian Exp $
     *
     *-------------------------------------------------------------------------
     */
    /*
     *	 INTERFACE ROUTINES
     *		ExecEvalExpr	- evaluate an expression and return a datum
     *		ExecQual		- return true/false if qualification is satisified
     *		ExecTargetList	- form a new tuple by projecting the given tuple
     *
     *	 NOTES
     *		ExecEvalExpr() and ExecEvalVar() are hotspots.	making these faster
     *		will speed up the entire system.  Unfortunately they are currently
     *		implemented recursively.  Eliminating the recursion is bound to
     *		improve the speed of the executor.
     *
     *		ExecTargetList() is used to make tuple projections.  Rather then
     *		trying to speed it up, the execution plan should be pre-processed
     *		to facilitate attribute sharing between nodes wherever possible,
     *		instead of doing needless copying.	-cim 5/31/91
     *
     */
    #include <string.h>
    
    #include "postgres.h"
    
    #include "access/heapam.h"
    #include "catalog/pg_language.h"
    #include "executor/execdebug.h"
    #include "executor/executor.h"
    #include "executor/execFlatten.h"
    #include "executor/functions.h"
    #include "executor/nodeSubplan.h"
    #include "utils/builtins.h"
    #include "utils/fcache2.h"
    
    
    /*
     *		externs and constants
     */
    
    /*
     * XXX Used so we can get rid of use of Const nodes in the executor.
     * Currently only used by ExecHashGetBucket and set only by ExecMakeVarConst
     * and by ExecEvalArrayRef.
     */
    bool		execConstByVal;
    int			execConstLen;
    
    /* static functions decls */
    static Datum ExecEvalAggref(Aggref *aggref, ExprContext *econtext, bool *isNull);
    static Datum ExecEvalArrayRef(ArrayRef *arrayRef, ExprContext *econtext,
    				 bool *isNull, bool *isDone);
    static Datum ExecEvalAnd(Expr *andExpr, ExprContext *econtext, bool *isNull);
    static Datum ExecEvalFunc(Expr *funcClause, ExprContext *econtext,
    			 bool *isNull, bool *isDone);
    static void ExecEvalFuncArgs(FunctionCachePtr fcache, ExprContext *econtext,
    				 List *argList, Datum argV[], bool *argIsDone);
    static Datum ExecEvalNot(Expr *notclause, ExprContext *econtext, bool *isNull);
    static Datum ExecEvalOper(Expr *opClause, ExprContext *econtext,
    			 bool *isNull);
    static Datum ExecEvalOr(Expr *orExpr, ExprContext *econtext, bool *isNull);
    static Datum ExecEvalVar(Var *variable, ExprContext *econtext, bool *isNull);
    static Datum ExecMakeFunctionResult(Node *node, List *arguments,
    					   ExprContext *econtext, bool *isNull, bool *isDone);
    static bool ExecQualClause(Node *clause, ExprContext *econtext);
    
    /*
     *	  ExecEvalArrayRef
     *
     *	   This function takes an ArrayRef and returns a Const Node if it
     *	   is an array reference or returns the changed Array Node if it is
     *		   an array assignment.
     */
    static Datum
    ExecEvalArrayRef(ArrayRef *arrayRef,
    				 ExprContext *econtext,
    				 bool *isNull,
    				 bool *isDone)
    {
    	bool		dummy;
    	int			i = 0,
    				j = 0;
    	ArrayType  *array_scanner;
    	List	   *upperIndexpr,
    			   *lowerIndexpr;
    	Node	   *assgnexpr;
    	List	   *elt;
    	IntArray	upper,
    				lower;
    	int		   *lIndex;
    	char	   *dataPtr;
    
    	*isNull = false;
    	array_scanner = (ArrayType *) ExecEvalExpr(arrayRef->refexpr,
    											   econtext,
    											   isNull,
    											   isDone);
    	if (*isNull)
    		return (Datum) NULL;
    
    	upperIndexpr = arrayRef->refupperindexpr;
    
    	foreach(elt, upperIndexpr)
    	{
    		upper.indx[i++] = (int32) ExecEvalExpr((Node *) lfirst(elt),
    											   econtext,
    											   isNull,
    											   &dummy);
    		if (*isNull)
    			return (Datum) NULL;
    	}
    
    	lowerIndexpr = arrayRef->reflowerindexpr;
    	lIndex = NULL;
    	if (lowerIndexpr != NIL)
    	{
    		foreach(elt, lowerIndexpr)
    		{
    			lower.indx[j++] = (int32) ExecEvalExpr((Node *) lfirst(elt),
    												   econtext,
    												   isNull,
    												   &dummy);
    			if (*isNull)
    				return (Datum) NULL;
    		}
    		if (i != j)
    			elog(ERROR,
    				 "ExecEvalArrayRef: upper and lower indices mismatch");
    		lIndex = lower.indx;
    	}
    
    	assgnexpr = arrayRef->refassgnexpr;
    	if (assgnexpr != NULL)
    	{
    		dataPtr = (char *) ExecEvalExpr((Node *)
    										assgnexpr, econtext,
    										isNull, &dummy);
    		if (*isNull)
    			return (Datum) NULL;
    		execConstByVal = arrayRef->refelembyval;
    		execConstLen = arrayRef->refelemlength;
    		if (lIndex == NULL)
    			return (Datum) array_set(array_scanner, i, upper.indx, dataPtr,
    									 arrayRef->refelembyval,
    									 arrayRef->refelemlength,
    									 arrayRef->refattrlength, isNull);
    		return (Datum) array_assgn(array_scanner, i, upper.indx,
    								   lower.indx,
    								   (ArrayType *) dataPtr,
    								   arrayRef->refelembyval,
    								   arrayRef->refelemlength, isNull);
    	}
    	execConstByVal = arrayRef->refelembyval;
    	execConstLen = arrayRef->refelemlength;
    	if (lIndex == NULL)
    		return (Datum) array_ref(array_scanner, i, upper.indx,
    								 arrayRef->refelembyval,
    								 arrayRef->refelemlength,
    								 arrayRef->refattrlength, isNull);
    	return (Datum) array_clip(array_scanner, i, upper.indx, lower.indx,
    							  arrayRef->refelembyval,
    							  arrayRef->refelemlength, isNull);
    }
    
    
    /* ----------------------------------------------------------------
     *		ExecEvalAggref
     *
     *		Returns a Datum whose value is the value of the precomputed
     *		aggregate found in the given expression context.
     * ----------------------------------------------------------------
     */
    static Datum
    ExecEvalAggref(Aggref *aggref, ExprContext *econtext, bool *isNull)
    {
    	*isNull = econtext->ecxt_nulls[aggref->aggno];
    	return econtext->ecxt_values[aggref->aggno];
    }
    
    /* ----------------------------------------------------------------
     *		ExecEvalVar
     *
     *		Returns a Datum whose value is the value of a range
     *		variable with respect to given expression context.
     *
     *
     *		As an entry condition, we expect that the datatype the
     *		plan expects to get (as told by our "variable" argument) is in
     *		fact the datatype of the attribute the plan says to fetch (as
     *		seen in the current context, identified by our "econtext"
     *		argument).
     *
     *		If we fetch a Type A attribute and Caller treats it as if it
     *		were Type B, there will be undefined results (e.g. crash).
     *		One way these might mismatch now is that we're accessing a
     *		catalog class and the type information in the pg_attribute
     *		class does not match the hardcoded pg_attribute information
     *		(in pg_attribute.h) for the class in question.
     *
     *		We have an Assert to make sure this entry condition is met.
     *
     * ---------------------------------------------------------------- */
    static Datum
    ExecEvalVar(Var *variable, ExprContext *econtext, bool *isNull)
    {
    	Datum		result;
    	TupleTableSlot *slot;
    	AttrNumber	attnum;
    	HeapTuple	heapTuple;
    	TupleDesc	tuple_type;
    	Buffer		buffer;
    	bool		byval;
    	int16		len;
    
    	/*
    	 * get the slot we want
    	 */
    	switch (variable->varno)
    	{
    		case INNER:				/* get the tuple from the inner node */
    			slot = econtext->ecxt_innertuple;
    			break;
    
    		case OUTER:				/* get the tuple from the outer node */
    			slot = econtext->ecxt_outertuple;
    			break;
    
    		default:				/* get the tuple from the relation being
    								 * scanned */
    			slot = econtext->ecxt_scantuple;
    			break;
    	}
    
    	/*
    	 * extract tuple information from the slot
    	 */
    	heapTuple = slot->val;
    	tuple_type = slot->ttc_tupleDescriptor;
    	buffer = slot->ttc_buffer;
    
    	attnum = variable->varattno;
    
    	/* (See prolog for explanation of this Assert) */
    	Assert(attnum <= 0 ||
    		   (attnum - 1 <= tuple_type->natts - 1 &&
    			tuple_type->attrs[attnum - 1] != NULL &&
    			variable->vartype == tuple_type->attrs[attnum - 1]->atttypid))
    
    	/*
    	 * If the attribute number is invalid, then we are supposed to return
    	 * the entire tuple, we give back a whole slot so that callers know
    	 * what the tuple looks like.
    	 */
    		if (attnum == InvalidAttrNumber)
    	{
    		TupleTableSlot *tempSlot;
    		TupleDesc	td;
    		HeapTuple	tup;
    
    		tempSlot = makeNode(TupleTableSlot);
    		tempSlot->ttc_shouldFree = false;
    		tempSlot->ttc_descIsNew = true;
    		tempSlot->ttc_tupleDescriptor = (TupleDesc) NULL;
    		tempSlot->ttc_buffer = InvalidBuffer;
    		tempSlot->ttc_whichplan = -1;
    
    		tup = heap_copytuple(heapTuple);
    		td = CreateTupleDescCopy(slot->ttc_tupleDescriptor);
    
    		ExecSetSlotDescriptor(tempSlot, td);
    
    		ExecStoreTuple(tup, tempSlot, InvalidBuffer, true);
    		return (Datum) tempSlot;
    	}
    
    	result = heap_getattr(heapTuple,	/* tuple containing attribute */
    						  attnum,		/* attribute number of desired
    										 * attribute */
    						  tuple_type,	/* tuple descriptor of tuple */
    						  isNull);		/* return: is attribute null? */
    
    	/*
    	 * return null if att is null
    	 */
    	if (*isNull)
    		return (Datum) 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
    		 */
    		len = heap_sysattrlen(attnum);	/* XXX see -mao above */
    		byval = heap_sysattrbyval(attnum);		/* XXX see -mao above */
    	}
    	else
    	{
    		len = tuple_type->attrs[attnum - 1]->attlen;
    		byval = tuple_type->attrs[attnum - 1]->attbyval ? true : false;
    	}
    
    	execConstByVal = byval;
    	execConstLen = len;
    
    	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 the datum
     *		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.
     * ----------------------------------------------------------------
     */
    Datum
    ExecEvalParam(Param *expression, ExprContext *econtext, bool *isNull)
    {
    
    	char	   *thisParameterName;
    	int			thisParameterKind = expression->paramkind;
    	AttrNumber	thisParameterId = expression->paramid;
    	int			matchFound;
    	ParamListInfo paramList;
    
    	if (thisParameterKind == PARAM_EXEC)
    	{
    		ParamExecData *prm = &(econtext->ecxt_param_exec_vals[thisParameterId]);
    
    		if (prm->execPlan != NULL)
    			ExecSetParamPlan(prm->execPlan);
    		Assert(prm->execPlan == NULL);
    		*isNull = prm->isnull;
    		return prm->value;
    	}
    
    	thisParameterName = expression->paramname;
    	paramList = econtext->ecxt_param_list_info;
    
    	*isNull = false;
    
    	/*
    	 * 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:
    					if (thisParameterKind == paramList->kind &&
    						strcmp(paramList->name, thisParameterName) == 0)
    						matchFound = 1;
    					break;
    				case PARAM_NUM:
    					if (thisParameterKind == paramList->kind &&
    						paramList->id == thisParameterId)
    						matchFound = 1;
    					break;
    				case PARAM_OLD:
    				case PARAM_NEW:
    					if (thisParameterKind == paramList->kind &&
    						paramList->id == thisParameterId)
    					{
    						matchFound = 1;
    
    						/*
    						 * sanity check
    						 */
    						if (strcmp(paramList->name, thisParameterName) != 0)
    						{
    							elog(ERROR,
    								 "ExecEvalParam: new/old params with same id & diff names");
    						}
    					}
    					break;
    				default:
    
    					/*
    					 * oops! this is not supposed to happen!
    					 */
    					elog(ERROR, "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(ERROR, "ExecEvalParam: Unknown value for parameter %s",
    			 thisParameterName);
    	}
    
    	/*
    	 * return the value.
    	 */
    	if (paramList->isnull)
    	{
    		*isNull = true;
    		return (Datum) NULL;
    	}
    
    	if (expression->param_tlist != NIL)
    	{
    		HeapTuple	tup;
    		Datum		value;
    		List	   *tlist = expression->param_tlist;
    		TargetEntry *tle = (TargetEntry *) lfirst(tlist);
    		TupleTableSlot *slot = (TupleTableSlot *) paramList->value;
    
    		tup = slot->val;
    		value = ProjectAttribute(slot->ttc_tupleDescriptor,
    								 tle, tup, isNull);
    		return value;
    	}
    	return paramList->value;
    }
    
    
    /* ----------------------------------------------------------------
     *		ExecEvalOper / ExecEvalFunc support routines
     * ----------------------------------------------------------------
     */
    
    /*
     *		GetAttributeByName
     *		GetAttributeByNum
     *
     *		These are functions which return the value of the
     *		named attribute out of the tuple from the arg slot.  User defined
     *		C functions which take a tuple as an argument are expected
     *		to use this.  Ex: overpaid(EMP) might call GetAttributeByNum().
     */
    /* static but gets called from external functions */
    char *
    GetAttributeByNum(TupleTableSlot *slot,
    				  AttrNumber attrno,
    				  bool *isNull)
    {
    	Datum		retval;
    
    	if (!AttributeNumberIsValid(attrno))
    		elog(ERROR, "GetAttributeByNum: Invalid attribute number");
    
    	if (!AttrNumberIsForUserDefinedAttr(attrno))
    		elog(ERROR, "GetAttributeByNum: cannot access system attributes here");
    
    	if (isNull == (bool *) NULL)
    		elog(ERROR, "GetAttributeByNum: a NULL isNull flag was passed");
    
    	if (TupIsNull(slot))
    	{
    		*isNull = true;
    		return (char *) NULL;
    	}
    
    	retval = heap_getattr(slot->val,
    						  attrno,
    						  slot->ttc_tupleDescriptor,
    						  isNull);
    	if (*isNull)
    		return (char *) NULL;
    	return (char *) retval;
    }
    
    /* XXX name for catalogs */
    #ifdef NOT_USED
    char *
    att_by_num(TupleTableSlot *slot,
    		   AttrNumber attrno,
    		   bool *isNull)
    {
    	return GetAttributeByNum(slot, attrno, isNull);
    }
    
    #endif
    
    char *
    GetAttributeByName(TupleTableSlot *slot, char *attname, bool *isNull)
    {
    	AttrNumber	attrno;
    	TupleDesc	tupdesc;
    	Datum		retval;
    	int			natts;
    	int			i;
    
    	if (attname == NULL)
    		elog(ERROR, "GetAttributeByName: Invalid attribute name");
    
    	if (isNull == (bool *) NULL)
    		elog(ERROR, "GetAttributeByName: a NULL isNull flag was passed");
    
    	if (TupIsNull(slot))
    	{
    		*isNull = true;
    		return (char *) NULL;
    	}
    
    	tupdesc = slot->ttc_tupleDescriptor;
    	natts = slot->val->t_data->t_natts;
    
    	attrno = InvalidAttrNumber;
    	for (i = 0; i < tupdesc->natts; i++)
    	{
    		if (namestrcmp(&(tupdesc->attrs[i]->attname), attname) == 0)
    		{
    			attrno = tupdesc->attrs[i]->attnum;
    			break;
    		}
    	}
    
    	if (attrno == InvalidAttrNumber)
    		elog(ERROR, "GetAttributeByName: attribute %s not found", attname);
    
    	retval = heap_getattr(slot->val,
    						  attrno,
    						  tupdesc,
    						  isNull);
    	if (*isNull)
    		return (char *) NULL;
    	return (char *) retval;
    }
    
    /* XXX name for catalogs */
    #ifdef NOT_USED
    char *
    att_by_name(TupleTableSlot *slot, char *attname, bool *isNull)
    {
    	return GetAttributeByName(slot, attname, isNull);
    }
    
    #endif
    
    static void
    ExecEvalFuncArgs(FunctionCachePtr fcache,
    				 ExprContext *econtext,
    				 List *argList,
    				 Datum argV[],
    				 bool *argIsDone)
    {
    	int			i;
    	bool		argIsNull,
    			   *nullVect;
    	List	   *arg;
    
    	nullVect = fcache->nullVect;
    
    	i = 0;
    	foreach(arg, argList)
    	{
    
    		/*
    		 * evaluate the expression, in general functions cannot take sets
    		 * as arguments but we make an exception in the case of nested dot
    		 * expressions.  We have to watch out for this case here.
    		 */
    		argV[i] = (Datum)
    			ExecEvalExpr((Node *) lfirst(arg),
    						 econtext,
    						 &argIsNull,
    						 argIsDone);
    
    
    		if (!(*argIsDone))
    		{
    			Assert(i == 0);
    			fcache->setArg = (char *) argV[0];
    			fcache->hasSetArg = true;
    		}
    		if (argIsNull)
    			nullVect[i] = true;
    		else
    			nullVect[i] = false;
    		i++;
    	}
    }
    
    /*
     *		ExecMakeFunctionResult
     */
    static Datum
    ExecMakeFunctionResult(Node *node,
    					   List *arguments,
    					   ExprContext *econtext,
    					   bool *isNull,
    					   bool *isDone)
    {
    	Datum		argV[MAXFMGRARGS];
    	FunctionCachePtr fcache;
    	Func	   *funcNode = NULL;
    	Oper	   *operNode = NULL;
    	bool		funcisset = false;
    
    	/*
    	 * This is kind of ugly, Func nodes now have targetlists so that we
    	 * know when and what to project out from postquel function results.
    	 * This means we have to pass the func node all the way down instead
    	 * of using only the fcache struct as before.  ExecMakeFunctionResult
    	 * becomes a little bit more of a dual personality as a result.
    	 */
    	if (IsA(node, Func))
    	{
    		funcNode = (Func *) node;
    		fcache = funcNode->func_fcache;
    	}
    	else
    	{
    		operNode = (Oper *) node;
    		fcache = operNode->op_fcache;
    	}
    
    	/*
    	 * 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 (argV) and pass this array to
    	 * arrayFmgr()
    	 */
    	if (fcache->nargs != 0)
    	{
    		bool		argDone;
    
    		if (fcache->nargs > MAXFMGRARGS)
    			elog(ERROR, "ExecMakeFunctionResult: too many arguments");
    
    		/*
    		 * If the setArg in the fcache is set we have an argument
    		 * returning a set of tuples (i.e. a nested dot expression).  We
    		 * don't want to evaluate the arguments again until the function
    		 * is done. hasSetArg will always be false until we eval the args
    		 * for the first time. We should set this in the parser.
    		 */
    		if ((fcache->hasSetArg) && fcache->setArg != NULL)
    		{
    			argV[0] = (Datum) fcache->setArg;
    			argDone = false;
    		}
    		else
    			ExecEvalFuncArgs(fcache, econtext, arguments, argV, &argDone);
    
    		if ((fcache->hasSetArg) && (argDone))
    		{
    			if (isDone)
    				*isDone = true;
    			return (Datum) NULL;
    		}
    	}
    
    	/*
    	 * If this function is really a set, we have to diddle with things. If
    	 * the function has already been called at least once, then the setArg
    	 * field of the fcache holds the OID of this set in pg_proc.  (This is
    	 * not quite legit, since the setArg field is really for functions
    	 * which take sets of tuples as input - set functions take no inputs
    	 * at all.	But it's a nice place to stash this value, for now.)
    	 *
    	 * If this is the first call of the set's function, then the call to
    	 * ExecEvalFuncArgs above just returned the OID of the pg_proc tuple
    	 * which defines this set.	So replace the existing funcid in the
    	 * funcnode with the set's OID.  Also, we want a new fcache which
    	 * points to the right function, so get that, now that we have the
    	 * right OID.  Also zero out the argV, since the real set doesn't take
    	 * any arguments.
    	 */
    	if (((Func *) node)->funcid == F_SETEVAL)
    	{
    		funcisset = true;
    		if (fcache->setArg)
    		{
    			argV[0] = 0;
    
    			((Func *) node)->funcid = (Oid) PointerGetDatum(fcache->setArg);
    
    		}
    		else
    		{
    			((Func *) node)->funcid = (Oid) argV[0];
    			setFcache(node, argV[0], NIL, econtext);
    			fcache = ((Func *) node)->func_fcache;
    			fcache->setArg = (char *) argV[0];
    			argV[0] = (Datum) 0;
    		}
    	}
    
    	/*
    	 * now return the value gotten by calling the function manager,
    	 * passing the function the evaluated parameter values.
    	 */
    	if (fcache->language == SQLlanguageId)
    	{
    		Datum		result;
    
    		Assert(funcNode);
    		result = postquel_function(funcNode, (char **) argV, isNull, isDone);
    
    		/*
    		 * finagle the situation where we are iterating through all
    		 * results in a nested dot function (whose argument function
    		 * returns a set of tuples) and the current function finally
    		 * finishes.  We need to get the next argument in the set and run
    		 * the function all over again.  This is getting unclean.
    		 */
    		if ((*isDone) && (fcache->hasSetArg))
    		{
    			bool		argDone;
    
    			ExecEvalFuncArgs(fcache, econtext, arguments, argV, &argDone);
    
    			if (argDone)
    			{
    				fcache->setArg = (char *) NULL;
    				*isDone = true;
    				result = (Datum) NULL;
    			}
    			else
    				result = postquel_function(funcNode,
    										   (char **) argV,
    										   isNull,
    										   isDone);
    		}
    		if (funcisset)
    		{
    
    			/*
    			 * reset the funcid so that next call to this routine will
    			 * still recognize this func as a set. Note that for now we
    			 * assume that the set function in pg_proc must be a Postquel
    			 * function - the funcid is not reset below for C functions.
    			 */
    			((Func *) node)->funcid = F_SETEVAL;
    
    			/*
    			 * If we're done with the results of this function, get rid of
    			 * its func cache.
    			 */
    			if (*isDone)
    				((Func *) node)->func_fcache = NULL;
    		}
    		return result;
    	}
    	else
    	{
    		int			i;
    
    		if (isDone)
    			*isDone = true;
    		for (i = 0; i < fcache->nargs; i++)
    			if (fcache->nullVect[i] == true)
    				*isNull = true;
    
    		return (Datum) fmgr_c(&fcache->func, (FmgrValues *) argV, isNull);
    	}
    }
    
    
    /* ----------------------------------------------------------------
     *		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.
     * ----------------------------------------------------------------
     */
    
    /* ----------------------------------------------------------------
     *		ExecEvalOper
     * ----------------------------------------------------------------
     */
    static Datum
    ExecEvalOper(Expr *opClause, ExprContext *econtext, bool *isNull)
    {
    	Oper	   *op;
    	List	   *argList;
    	FunctionCachePtr fcache;
    	bool		isDone;
    
    	/*
    	 * 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 *) opClause->oper;
    	argList = opClause->args;
    
    	/*
    	 * get the fcache from the Oper node. If it is NULL, then initialize
    	 * it
    	 */
    	fcache = op->op_fcache;
    	if (fcache == NULL)
    	{
    		setFcache((Node *) op, op->opid, argList, econtext);
    		fcache = op->op_fcache;
    	}
    
    	/*
    	 * call ExecMakeFunctionResult() with a dummy isDone that we ignore.
    	 * We don't have operator whose arguments are sets.
    	 */
    	return ExecMakeFunctionResult((Node *) op, argList, econtext, isNull, &isDone);
    }
    
    /* ----------------------------------------------------------------
     *		ExecEvalFunc
     * ----------------------------------------------------------------
     */
    
    static Datum
    ExecEvalFunc(Expr *funcClause,
    			 ExprContext *econtext,
    			 bool *isNull,
    			 bool *isDone)
    {
    	Func	   *func;
    	List	   *argList;
    	FunctionCachePtr fcache;
    
    	/*
    	 * 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 *) funcClause->oper;
    	argList = funcClause->args;
    
    	/*
    	 * get the fcache from the Func node. If it is NULL, then initialize
    	 * it
    	 */
    	fcache = func->func_fcache;
    	if (fcache == NULL)
    	{
    		setFcache((Node *) func, func->funcid, argList, econtext);
    		fcache = func->func_fcache;
    	}
    
    	return ExecMakeFunctionResult((Node *) func, argList, econtext, isNull, isDone);
    }
    
    /* ----------------------------------------------------------------
     *		ExecEvalNot
     *		ExecEvalOr
     *		ExecEvalAnd
     *
     *		Evaluate boolean expressions.  Evaluation of 'or' is
     *		short-circuited when the first true (or null) value is found.
     *
     *		The query planner reformulates clause expressions in the
     *		qualification to conjunctive normal form.  If we ever get
     *		an AND to evaluate, we can be sure that it's not a top-level
     *		clause in the qualification, but appears lower (as a function
     *		argument, for example), or in the target list.	Not that you
     *		need to know this, mind you...
     * ----------------------------------------------------------------
     */
    static Datum
    ExecEvalNot(Expr *notclause, ExprContext *econtext, bool *isNull)
    {
    	Datum		expr_value;
    	Node	   *clause;
    	bool		isDone;
    
    	clause = lfirst(notclause->args);
    
    	/*
    	 * We don't iterate over sets in the quals, so pass in an isDone flag,
    	 * but ignore it.
    	 */
    	expr_value = ExecEvalExpr(clause, econtext, isNull, &isDone);
    
    	/*
    	 * if the expression evaluates to null, then we just cascade the null
    	 * back to whoever called us.
    	 */
    	if (*isNull)
    		return expr_value;
    
    	/*
    	 * evaluation of 'not' is simple.. expr is false, then return 'true'
    	 * and vice versa.
    	 */
    	if (DatumGetInt32(expr_value) == 0)
    		return (Datum) true;
    
    	return (Datum) false;
    }
    
    /* ----------------------------------------------------------------
     *		ExecEvalOr
     * ----------------------------------------------------------------
     */
    static Datum
    ExecEvalOr(Expr *orExpr, ExprContext *econtext, bool *isNull)
    {
    	List	   *clauses;
    	List	   *clause;
    	bool		isDone;
    	bool		IsNull;
    	Datum		const_value = 0;
    
    	IsNull = false;
    	clauses = orExpr->args;
    
    	/*
    	 * we use three valued logic functions here... we evaluate each of the
    	 * clauses in turn, as soon as one is true we return that value.  If
    	 * none is true and  none of the clauses evaluate to NULL we return
    	 * the value of the last clause evaluated (which should be false) with
    	 * *isNull set to false else if none is true and at least one clause
    	 * evaluated to NULL we set *isNull flag to true -
    	 */
    	foreach(clause, clauses)
    	{
    
    		/*
    		 * We don't iterate over sets in the quals, so pass in an isDone
    		 * flag, but ignore it.
    		 */
    		const_value = ExecEvalExpr((Node *) lfirst(clause),
    								   econtext,
    								   isNull,
    								   &isDone);
    
    		/*
    		 * if the expression evaluates to null, then we remember it in the
    		 * local IsNull flag, if none of the clauses are true then we need
    		 * to set *isNull to true again.
    		 */
    		if (*isNull)
    		{
    			IsNull = *isNull;
    
    			/*
    			 * Many functions don't (or can't!) check if an argument is
    			 * NULL or NOT_NULL and may return TRUE (1) with *isNull TRUE
    			 * (an_int4_column <> 1: int4ne returns TRUE for NULLs). Not
    			 * having time to fix the function manager I want to fix OR:
    			 * if we had 'x <> 1 OR x isnull' then when x is NULL TRUE was
    			 * returned by the 'x <> 1' clause ... but ExecQualClause says
    			 * that the qualification should *fail* if isnull is TRUE for
    			 * any value returned by ExecEvalExpr. So, force this rule
    			 * here: if isnull is TRUE then the clause failed. Note:
    			 * nullvalue() & nonnullvalue() always sets isnull to FALSE
    			 * for NULLs. - vadim 09/22/97
    			 */
    			const_value = 0;
    		}
    
    		/*
    		 * if we have a true result, then we return it.
    		 */
    		if (DatumGetInt32(const_value) != 0)
    			return const_value;
    	}
    
    	/* IsNull is true if at least one clause evaluated to NULL */
    	*isNull = IsNull;
    	return const_value;
    }
    
    /* ----------------------------------------------------------------
     *		ExecEvalAnd
     * ----------------------------------------------------------------
     */
    static Datum
    ExecEvalAnd(Expr *andExpr, ExprContext *econtext, bool *isNull)
    {
    	List	   *clauses;
    	List	   *clause;
    	Datum		const_value = 0;
    	bool		isDone;
    	bool		IsNull;
    
    	IsNull = false;
    
    	clauses = andExpr->args;
    
    	/*
    	 * we evaluate each of the clauses in turn, as soon as one is false we
    	 * return that value.  If none are false or NULL then we return the
    	 * value of the last clause evaluated, which should be true.
    	 */
    	foreach(clause, clauses)
    	{
    
    		/*
    		 * We don't iterate over sets in the quals, so pass in an isDone
    		 * flag, but ignore it.
    		 */
    		const_value = ExecEvalExpr((Node *) lfirst(clause),
    								   econtext,
    								   isNull,
    								   &isDone);
    
    		/*
    		 * if the expression evaluates to null, then we remember it in
    		 * IsNull, if none of the clauses after this evaluates to false we
    		 * will have to set *isNull to true again.
    		 */
    		if (*isNull)
    			IsNull = *isNull;
    
    		/*
    		 * if we have a false result, then we return it, since the
    		 * conjunction must be false.
    		 */
    		if (DatumGetInt32(const_value) == 0)
    			return const_value;
    	}
    
    	*isNull = IsNull;
    	return const_value;
    }
    
    /* ----------------------------------------------------------------
     *		ExecEvalCase
     *
     *		Evaluate a CASE clause. Will have boolean expressions
     *		inside the WHEN clauses, and will have constants
     *		for results.
     *		- thomas 1998-11-09
     * ----------------------------------------------------------------
     */
    static Datum
    ExecEvalCase(CaseExpr *caseExpr, ExprContext *econtext, bool *isNull)
    {
    	List	   *clauses;
    	List	   *clause;
    	CaseWhen   *wclause;
    	Datum		const_value = 0;
    	bool		isDone;
    
    	clauses = caseExpr->args;
    
    	/*
    	 * we evaluate each of the WHEN clauses in turn, as soon as one is
    	 * true we return the corresponding result. If none are true then we
    	 * return the value of the default clause, or NULL.
    	 */
    	foreach(clause, clauses)
    	{
    
    		/*
    		 * We don't iterate over sets in the quals, so pass in an isDone
    		 * flag, but ignore it.
    		 */
    
    		wclause = lfirst(clause);
    		const_value = ExecEvalExpr((Node *) wclause->expr,
    								   econtext,
    								   isNull,
    								   &isDone);
    
    		/*
    		 * if we have a true test, then we return the result, since the
    		 * case statement is satisfied.
    		 */
    		if (DatumGetInt32(const_value) != 0)
    		{
    			const_value = ExecEvalExpr((Node *) wclause->result,
    									   econtext,
    									   isNull,
    									   &isDone);
    			return (Datum) const_value;
    
    		}
    	}
    
    	if (caseExpr->defresult)
    	{
    		const_value = ExecEvalExpr((Node *) caseExpr->defresult,
    								   econtext,
    								   isNull,
    								   &isDone);
    	}
    	else
    		*isNull = true;
    
    	return const_value;
    }
    
    /* ----------------------------------------------------------------
     *		ExecEvalExpr
     *
     *		Recursively evaluate a targetlist or qualification expression.
     *
     *		This routine is an inner loop routine and should be as fast
     *		as possible.
     *
     *		Node comparison functions were replaced by macros for speed and to plug
     *		memory leaks incurred by using the planner's Lispy stuff for
     *		comparisons.  Order of evaluation of node comparisons IS IMPORTANT;
     *		the macros do no checks.  Order of evaluation:
     *
     *		o an isnull check, largely to avoid coredumps since greg doubts this
     *		  routine is called with a null ptr anyway in proper operation, but is
     *		  not completely sure...
     *		o ExactNodeType checks.
     *		o clause checks or other checks where we look at the lfirst of something.
     * ----------------------------------------------------------------
     */
    Datum
    ExecEvalExpr(Node *expression,
    			 ExprContext *econtext,
    			 bool *isNull,
    			 bool *isDone)
    {
    	Datum		retDatum = 0;
    
    	*isNull = false;
    
    	/*
    	 * Some callers don't care about is done and only want 1 result.  They
    	 * indicate this by passing NULL
    	 */
    	if (isDone)
    		*isDone = true;
    
    	/*
    	 * here we dispatch the work to the appropriate type of function given
    	 * the type of our expression.
    	 */
    	if (expression == NULL)
    	{
    		*isNull = true;
    		return (Datum) true;
    	}
    
    	switch (nodeTag(expression))
    	{
    		case T_Var:
    			retDatum = (Datum) ExecEvalVar((Var *) expression, econtext, isNull);
    			break;
    		case T_Const:
    			{
    				Const	   *con = (Const *) expression;
    
    				if (con->constisnull)
    					*isNull = true;
    				retDatum = con->constvalue;
    				break;
    			}
    		case T_Param:
    			retDatum = (Datum) ExecEvalParam((Param *) expression, econtext, isNull);
    			break;
    		case T_Iter:
    			retDatum = (Datum) ExecEvalIter((Iter *) expression,
    											econtext,
    											isNull,
    											isDone);
    			break;
    		case T_Aggref:
    			retDatum = (Datum) ExecEvalAggref((Aggref *) expression,
    											  econtext,
    											  isNull);
    			break;
    		case T_ArrayRef:
    			retDatum = (Datum) ExecEvalArrayRef((ArrayRef *) expression,
    												econtext,
    												isNull,
    												isDone);
    			break;
    		case T_Expr:
    			{
    				Expr	   *expr = (Expr *) expression;
    
    				switch (expr->opType)
    				{
    					case OP_EXPR:
    						retDatum = (Datum) ExecEvalOper(expr, econtext, isNull);
    						break;
    					case FUNC_EXPR:
    						retDatum = (Datum) ExecEvalFunc(expr, econtext, isNull, isDone);
    						break;
    					case OR_EXPR:
    						retDatum = (Datum) ExecEvalOr(expr, econtext, isNull);
    						break;
    					case AND_EXPR:
    						retDatum = (Datum) ExecEvalAnd(expr, econtext, isNull);
    						break;
    					case NOT_EXPR:
    						retDatum = (Datum) ExecEvalNot(expr, econtext, isNull);
    						break;
    					case SUBPLAN_EXPR:
    						retDatum = (Datum) ExecSubPlan((SubPlan *) expr->oper, expr->args, econtext);
    						break;
    					default:
    						elog(ERROR, "ExecEvalExpr: unknown expression type %d", expr->opType);
    						break;
    				}
    				break;
    			}
    		case T_CaseExpr:
    			retDatum = (Datum) ExecEvalCase((CaseExpr *) expression, econtext, isNull);
    			break;
    
    		default:
    			elog(ERROR, "ExecEvalExpr: unknown expression type %d", nodeTag(expression));
    			break;
    	}
    
    	return retDatum;
    }	/* ExecEvalExpr() */
    
    
    /* ----------------------------------------------------------------
     *					 ExecQual / ExecTargetList
     * ----------------------------------------------------------------
     */
    
    /* ----------------------------------------------------------------
     *		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)
     * ----------------------------------------------------------------
     */
    static bool
    ExecQualClause(Node *clause, ExprContext *econtext)
    {
    	Datum		expr_value;
    	bool		isNull;
    	bool		isDone;
    
    	/* when there is a null clause, consider the qualification to be true */
    	if (clause == NULL)
    		return true;
    
    	/*
    	 * pass isDone, but ignore it.	We don't iterate over multiple returns
    	 * in the qualifications.
    	 */
    	expr_value = (Datum)
    		ExecEvalExpr(clause, econtext, &isNull, &isDone);
    
    	/*
    	 * 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 (isNull)
    		return true;
    
    	/*
    	 * remember, we return true when the qualification fails..
    	 */
    	if (DatumGetInt32(expr_value) == 0)
    		return true;
    
    	return false;
    }
    
    /* ----------------------------------------------------------------
     *		ExecQual
     *
     *		Evaluates a conjunctive boolean expression and returns t
     *		iff none of the subexpressions are false (or null).
     * ----------------------------------------------------------------
     */
    bool
    ExecQual(List *qual, ExprContext *econtext)
    {
    	List	   *clause;
    	bool		result;
    
    	/*
    	 * debugging stuff
    	 */
    	EV_printf("ExecQual: qual is ");
    	EV_nodeDisplay(qual);
    	EV_printf("\n");
    
    	IncrProcessed();
    
    	/*
    	 * return true immediately if no qual
    	 */
    	if (qual == NIL)
    		return true;
    
    	/*
    	 * 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((Node *) lfirst(clause), econtext);
    		if (result == true)
    			break;
    	}
    
    	/*
    	 * 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;
    }
    
    int
    ExecTargetListLength(List *targetlist)
    {
    	int			len;
    	List	   *tl;
    	TargetEntry *curTle;
    
    	len = 0;
    	foreach(tl, targetlist)
    	{
    		curTle = lfirst(tl);
    
    		if (curTle->resdom != NULL)
    			len++;
    		else
    			len += curTle->fjoin->fj_nNodes;
    	}
    	return len;
    }
    
    /* ----------------------------------------------------------------
     *		ExecTargetList
     *
     *		Evaluates a targetlist with respect to the current
     *		expression context and return a tuple.
     * ----------------------------------------------------------------
     */
    static HeapTuple
    ExecTargetList(List *targetlist,
    			   int nodomains,
    			   TupleDesc targettype,
    			   Datum *values,
    			   ExprContext *econtext,
    			   bool *isDone)
    {
    	char		nulls_array[64];
    	bool		fjNullArray[64];
    	bool	   *fjIsNull;
    	char	   *null_head;
    	List	   *tl;
    	TargetEntry *tle;
    	Node	   *expr;
    	Resdom	   *resdom;
    	AttrNumber	resind;
    	Datum		constvalue;
    	HeapTuple	newTuple;
    	bool		isNull;
    	static struct tupleDesc NullTupleDesc; /* we assume this inits to zeroes */
    
    	/*
    	 * debugging stuff
    	 */
    	EV_printf("ExecTargetList: tl is ");
    	EV_nodeDisplay(targetlist);
    	EV_printf("\n");
    
    	/*
    	 * There used to be some klugy and demonstrably broken code here that
    	 * special-cased the situation where targetlist == NIL.  Now we just
    	 * fall through and return an empty-but-valid tuple.  We do, however,
    	 * have to cope with the possibility that targettype is NULL ---
    	 * heap_formtuple won't like that, so pass a dummy descriptor with
    	 * natts = 0 to deal with it.
    	 */
    	if (targettype == NULL)
    		targettype = &NullTupleDesc;
    
    	/*
    	 * allocate an array of char's to hold the "null" information only if
    	 * we have a really large targetlist.  otherwise we use the stack.
    	 */
    	if (nodomains > 64)
    	{
    		null_head = (char *) palloc(nodomains + 1);
    		fjIsNull = (bool *) palloc(nodomains + 1);
    	}
    	else
    	{
    		null_head = &nulls_array[0];
    		fjIsNull = &fjNullArray[0];
    	}
    
    	/*
    	 * evaluate all the expressions in the target list
    	 */
    	EV_printf("ExecTargetList: setting target list values\n");
    
    	*isDone = true;
    	foreach(tl, targetlist)
    	{
    
    		/*
    		 * remember, a target list is a list of lists:
    		 *
    		 * ((<resdom | fjoin> expr) (<resdom | fjoin> expr) ...)
    		 *
    		 * tl is a pointer to successive cdr's of the targetlist tle is a
    		 * pointer to the target list entry in tl
    		 */
    		tle = lfirst(tl);
    
    		if (tle->resdom != NULL)
    		{
    			expr = tle->expr;
    			resdom = tle->resdom;
    			resind = resdom->resno - 1;
    			constvalue = (Datum) ExecEvalExpr(expr,
    											  econtext,
    											  &isNull,
    											  isDone);
    
    			if ((IsA(expr, Iter)) && (*isDone))
    				return (HeapTuple) NULL;
    
    			values[resind] = constvalue;
    
    			if (!isNull)
    				null_head[resind] = ' ';
    			else
    				null_head[resind] = 'n';
    		}
    		else
    		{
    			int			curNode;
    			Resdom	   *fjRes;
    			List	   *fjTlist = (List *) tle->expr;
    			Fjoin	   *fjNode = tle->fjoin;
    			int			nNodes = fjNode->fj_nNodes;
    			DatumPtr	results = fjNode->fj_results;
    
    			ExecEvalFjoin(tle, econtext, fjIsNull, isDone);
    			if (*isDone)
    				return (HeapTuple) NULL;
    
    			/*
    			 * get the result from the inner node
    			 */
    			fjRes = (Resdom *) fjNode->fj_innerNode;
    			resind = fjRes->resno - 1;
    			if (fjIsNull[0])
    				null_head[resind] = 'n';
    			else
    			{
    				null_head[resind] = ' ';
    				values[resind] = results[0];
    			}
    
    			/*
    			 * Get results from all of the outer nodes
    			 */
    			for (curNode = 1;
    				 curNode < nNodes;
    				 curNode++, fjTlist = lnext(fjTlist))
    			{
    #ifdef NOT_USED					/* what is this?? */
    				Node	   *outernode = lfirst(fjTlist);
    
    				fjRes = (Resdom *) outernode->iterexpr;
    #endif
    				resind = fjRes->resno - 1;
    				if (fjIsNull[curNode])
    					null_head[resind] = 'n';
    				else
    				{
    					null_head[resind] = ' ';
    					values[resind] = results[curNode];
    				}
    			}
    		}
    	}
    
    	/*
    	 * form the new result tuple (in the "normal" context)
    	 */
    	newTuple = (HeapTuple) heap_formtuple(targettype, values, null_head);
    
    	/*
    	 * free the nulls array if we allocated one..
    	 */
    	if (nodomains > 64)
    	{
    		pfree(null_head);
    		pfree(fjIsNull);
    	}
    
    	return newTuple;
    }
    
    /* ----------------------------------------------------------------
     *		ExecProject
     *
     *		projects a tuple based in projection info and stores
     *		it in the specified tuple table slot.
     *
     *		Note: someday soon the executor can be extended to eliminate
     *			  redundant projections by storing pointers to datums
     *			  in the tuple table and then passing these around when
     *			  possible.  this should make things much quicker.
     *			  -cim 6/3/91
     * ----------------------------------------------------------------
     */
    TupleTableSlot *
    ExecProject(ProjectionInfo *projInfo, bool *isDone)
    {
    	TupleTableSlot *slot;
    	List	   *targetlist;
    	int			len;
    	TupleDesc	tupType;
    	Datum	   *tupValue;
    	ExprContext *econtext;
    	HeapTuple	newTuple;
    
    	/*
    	 * sanity checks
    	 */
    	if (projInfo == NULL)
    		return (TupleTableSlot *) NULL;
    
    	/*
    	 * get the projection info we want
    	 */
    	slot = projInfo->pi_slot;
    	targetlist = projInfo->pi_targetlist;
    	len = projInfo->pi_len;
    	tupType = slot->ttc_tupleDescriptor;
    
    	tupValue = projInfo->pi_tupValue;
    	econtext = projInfo->pi_exprContext;
    
    	/*
    	 * form a new (result) tuple
    	 */
    	newTuple = ExecTargetList(targetlist,
    							  len,
    							  tupType,
    							  tupValue,
    							  econtext,
    							  isDone);
    
    	/*
    	 * store the tuple in the projection slot and return the slot.
    	 */
    	return (TupleTableSlot *)
    		ExecStoreTuple(newTuple,/* tuple to store */
    					   slot,	/* slot to store in */
    					   InvalidBuffer,	/* tuple has no buffer */
    					   true);
    }