/* ----------------------------------------------------------------
 *   FILE
 *	ex_procnode.c
 *	
 *   DESCRIPTION
 *	contains dispatch functions which call the appropriate
 *	"initialize", "get a tuple", and "cleanup" routines for the
 *	given node type.  If the node has children, then it will
 *	presumably call ExecInitNode, ExecProcNode, or ExecEndNode
 *	on it's subnodes and do the appropriate processing..
 *
 *   INTERFACE ROUTINES
 *	ExecInitNode	-	initialize a plan node and it's subplans
 *	ExecProcNode	-	get a tuple by executing the plan node
 *	ExecEndNode	-	shut down a plan node and it's subplans
 *
 *   NOTES
 *	This used to be three files.  It is now all combined into
 *	one file so that it is easier to keep ExecInitNode, ExecProcNode,
 *	and ExecEndNode in sync when new nodes are added.
 *	
 *   EXAMPLE
 *	suppose we want the age of the manager of the shoe department and
 *	the number of employees in that department.  so we have the query:
 *
 *		retrieve (DEPT.no_emps, EMP.age)
 *		where EMP.name = DEPT.mgr and
 *		      DEPT.name = "shoe"
 *	
 *	Suppose the planner gives us the following plan:
 *	
 *			Nest Loop (DEPT.mgr = EMP.name)
 *			/	\ 
 *		       /         \
 *		   Seq Scan	Seq Scan
 *		    DEPT	  EMP
 *		(name = "shoe")
 *	
 *	ExecMain() is eventually called three times.  The first time
 *	it is called it calls InitPlan() which calls ExecInitNode() on
 *	the root of the plan -- the nest loop node.
 *
 *    *	ExecInitNode() notices that it is looking at a nest loop and
 *	as the code below demonstrates, it calls ExecInitNestLoop().
 *	Eventually this calls ExecInitNode() on the right and left subplans
 *	and so forth until the entire plan is initialized.
 *	
 *    *	The second time ExecMain() is called, it calls ExecutePlan() which
 *	calls ExecProcNode() repeatedly on the top node of the plan.
 *	Each time this happens, ExecProcNode() will end up calling
 *	ExecNestLoop(), which calls ExecProcNode() on its subplans.
 *	Each of these subplans is a sequential scan so ExecSeqScan() is
 *	called.  The slots returned by ExecSeqScan() may contain
 *	tuples which contain the attributes ExecNestLoop() uses to
 *	form the tuples it returns.
 *
 *    *	Eventually ExecSeqScan() stops returning tuples and the nest
 *	loop join ends.  Lastly, ExecMain() calls ExecEndNode() which
 *	calls ExecEndNestLoop() which in turn calls ExecEndNode() on
 *	its subplans which result in ExecEndSeqScan().
 *
 *	This should show how the executor works by having
 *	ExecInitNode(), ExecProcNode() and ExecEndNode() dispatch
 *	their work to the appopriate node support routines which may
 *	in turn call these routines themselves on their subplans.
 *
 *   IDENTIFICATION
 *	$Header: /private/postgres/src/executor/RCS/ex_procnode.c,v 1.4 1992/08/04 17:37:45 mer Exp $
 * ----------------------------------------------------------------
 */

#include "executor/executor.h"
 RcsId("$Header: /private/postgres/src/executor/RCS/ex_procnode.c,v 1.4 1992/08/04 17:37:45 mer Exp $");

/* ----------------------------------------------------------------
 *	Hook processing macros.  these are used to simplify
 *	the node processing routines below.  For example,
 *
 *	    case classTag(Result):
 *		END_WITH_HOOKS(Result,resstate);
 *		break;
 *
 *	becomes
 *	----------------
 *	    case classTag(Result):
 *	        {
 *		    Pointer p = (Pointer) get_resstate(node);
 *		    DO_END_BEFORE_HOOK(p);
 *		    ExecEndResult((Result) node);
 *		    DO_END_AFTER_HOOK(p);
 *		}
 *		break;
 *
 *	which in turn expands to:
 *	----------------
 *	    case classTag(Result):
 *	        {
 *		    Pointer p = (Pointer) get_resstate(node);
 *		    hook = get_base_hook(p);
 *		    if (hook != NULL) {
 *		    	proc = get_hook_pre_endnode(hook);
 *			(*proc)(node, state);
 *		    }
 *		    ExecEndResult((Result) node);
 *		    if (hook != NULL) { 
 *			proc = get_hook_post_endnode(hook);
 *			(*proc)(node, state);
 *		    }
 *		}
 *		break;
 *
 *	Hook functions are currently only used for debugging and
 *	generating plan traces.  If EXEC_ASSIGNDEBUGHOOKS is not defined
 *	in H/executor/execdebug.h then the macros only expand to something
 *	like:
 *
 *	    case classTag(Result):
 *		ExecEndResult((Result) node);
 *		break;
 *	
 * ----------------------------------------------------------------
 */

/* ----------------
 *	do_hooks macros
 * ----------------
 */
#define DO_INIT_AFTER_HOOK(state) \
    hook = get_base_hook(state); \
    if (hook != NULL) { \
	proc = get_hook_post_initnode(hook); \
	(*proc)(node, state); \
    }

#define DO_PROC_BEFORE_HOOK(state) \
    hook = get_base_hook(state); \
    if (hook != NULL) { \
	proc = get_hook_pre_procnode(hook); \
	(*proc)(node, state); \
    }

#define DO_PROC_AFTER_HOOK(state) \
    if (hook != NULL) { \
	proc = get_hook_post_procnode(hook); \
	(*proc)(node, state); \
    }

#define DO_END_BEFORE_HOOK(state) \
    hook = get_base_hook(state); \
    if (hook != NULL) { \
	proc = get_hook_pre_endnode(hook); \
	(*proc)(node, state); \
    }

#define DO_END_AFTER_HOOK(state) \
    if (hook != NULL) { \
	proc = get_hook_post_endnode(hook); \
	(*proc)(node, state); \
    }

/* ----------------
 *	with_hooks macros
 * ----------------
 */
#ifdef EXEC_ASSIGNDEBUGHOOKS

#define INIT_WITH_HOOKS(n,s) \
{ \
    Pointer p; \
    result = (List) CppConcat(ExecInit,n)((n) node, estate, parent); \
    p = (Pointer) CppConcat(get_,s)(node); \
    DO_INIT_AFTER_HOOK(p); \
}

#define PROC_WITH_HOOKS(f,n,s) \
{ \
    Pointer p = (Pointer) CppConcat(get_,s)(node); \
    DO_PROC_BEFORE_HOOK(p); \
    result = (TupleTableSlot) f((n) node); \
    DO_PROC_AFTER_HOOK(p); \
}

#define END_WITH_HOOKS(n,s) \
{ \
    Pointer p = (Pointer) CppConcat(get_,s)(node); \
    DO_END_BEFORE_HOOK(p); \
    CppConcat(ExecEnd,n)((n) node); \
    DO_END_AFTER_HOOK(p); \
}

#else /*EXEC_ASSIGNDEBUGHOOKS*/

#define INIT_WITH_HOOKS(n,s) \
    result = (List) CppConcat(ExecInit,n)((n) node, estate, parent);

#define PROC_WITH_HOOKS(f,n,s) \
    result = (TupleTableSlot) f((n) node);

#define END_WITH_HOOKS(n,s) \
    CppConcat(ExecEnd,n)((n) node);
	
#endif /*EXEC_ASSIGNDEBUGHOOKS*/

/* ----------------------------------------------------------------
 *   	ExecInitNode
 *   
 *   	Recursively initializes all the nodes in the plan rooted
 *   	at 'node'. 
 *   
 *   	Initial States:
 *   	  'node' is the plan produced by the query planner
 * ----------------------------------------------------------------
 */
List
ExecInitNode(node, estate, parent)
    Plan 	node;
    EState 	estate;
    Plan 	parent;
{
    List	   	result;
    HookNode 		hook;
    HookFunction 	proc;

    /* ----------------
     *	do nothing when we get to the end
     *  of a leaf on tree.
     * ----------------
     */   
    if (node == NULL)
	return LispNil;
    
    switch(NodeType(node)) {
	/* ----------------
	 *	control nodes
	 * ----------------
	 */
    case classTag(Result):
/*	INIT_WITH_HOOKS( Result,resstate); type casting problem*/ 
	result = (List) ExecInitResult((Plan) node, estate, parent);	
	break;
	
    case classTag(Append):
	INIT_WITH_HOOKS(Append,unionstate);
	break;
              
	/* ----------------
	 *	scan nodes
	 * ----------------
	 */
    case classTag(SeqScan):
/*	INIT_WITH_HOOKS((Plan) SeqScan,scanstate); type casting problem */
	result = (List) ExecInitSeqScan((Plan) node, estate, parent);	
	break;

    case classTag(IndexScan):
	INIT_WITH_HOOKS(IndexScan,indxstate);
	break;
	
	/* ----------------
	 *	join nodes
	 * ----------------
	 */
    case classTag(NestLoop):
	INIT_WITH_HOOKS(NestLoop,nlstate);
	break;

    case classTag(MergeJoin):
	INIT_WITH_HOOKS(MergeJoin,mergestate);
	break;
	
	/* ----------------
	 *	materialization nodes
	 * ----------------
	 */
    case classTag(Material):
	INIT_WITH_HOOKS(Material,matstate);
	break;

    case classTag(Sort):
	INIT_WITH_HOOKS(Sort,sortstate);
	break;

    case classTag(Unique):
	INIT_WITH_HOOKS(Unique,uniquestate);
	break;

    case classTag(Agg):
	INIT_WITH_HOOKS(Agg, aggstate);
	break;
		
	/* ----------------
	 *	XXX add hooks to these
	 * ----------------
	 */
    case classTag(Hash):
	result = (List) ExecInitHash((Hash) node,
				     estate,
				     parent);
	break;
	
    case classTag(HashJoin):
	result = (List) ExecInitHashJoin((HashJoin) node,
					 estate,
					 parent);
	break;
	
    case classTag(ScanTemps):
	result = (List) ExecInitScanTemps((ScanTemps) node,
					  estate,
					  parent);
	break;
	
    default:
	elog(DEBUG, "ExecInitNode: node not yet supported: %d",
	     NodeGetTag(node));
	result = LispNil;
    }
    
    return result;
}
 

/* ----------------------------------------------------------------
 *   	ExecProcNode
 *   
 *   	Initial States:
 *   	  the query tree must be initialized once by calling ExecInit.
 * ----------------------------------------------------------------
 */
TupleTableSlot
ExecProcNode(node)
    Plan node;
{
    TupleTableSlot	result;
    HookNode 		hook;
    HookFunction 	proc;

    extern int testFlag;
    extern bool _exec_collect_stats_;

    /* ----------------
     *	deal with NULL nodes..
     * ----------------
     */
    if (node == NULL)
	return NULL;

    switch(NodeType(node)) {
	/* ----------------
	 *	control nodes
	 * ----------------
	 */
    case classTag(Result):
	PROC_WITH_HOOKS(ExecResult,Result,resstate);
	break;
	
    case classTag(Append):
	PROC_WITH_HOOKS(ExecProcAppend,Append,unionstate);
	break;
           
	/* ----------------
	 *	scan nodes
	 * ----------------
	 */
    case classTag(SeqScan):
	PROC_WITH_HOOKS(ExecSeqScan,SeqScan,scanstate);
	break;

    case classTag(IndexScan):
	PROC_WITH_HOOKS(ExecIndexScan,IndexScan,indxstate);
	break;

	/* ----------------
	 *	join nodes
	 * ----------------
	 */
    case classTag(NestLoop):
	PROC_WITH_HOOKS(ExecNestLoop,NestLoop,nlstate);
	break;

    case classTag(MergeJoin):
	PROC_WITH_HOOKS(ExecMergeJoin,MergeJoin,mergestate);
	break;

	/* ----------------
	 *	materialization nodes
	 * ----------------
	 */
    case classTag(Material):
	PROC_WITH_HOOKS(ExecMaterial,Material,matstate);
	break;

    case classTag(Sort):
	PROC_WITH_HOOKS(ExecSort,Sort,sortstate);
	break;

    case classTag(Unique):
	PROC_WITH_HOOKS(ExecUnique,Unique,uniquestate);
	break;

    case classTag(Agg):
	PROC_WITH_HOOKS(ExecAgg, Agg, aggstate);
	break;


    /* ----------------
     *  XXX add hooks to these
     * ----------------
     */
    case classTag(Hash):
	result = (TupleTableSlot) ExecHash((Hash) node);
	break;
   
    case classTag(HashJoin):
	result = (TupleTableSlot) ExecHashJoin((HashJoin) node);
	break;
	
    case classTag(ScanTemps):
	result = (TupleTableSlot) ExecScanTemps((ScanTemps) node);
	break;

    default:
	elog(DEBUG, "ExecProcNode: node not yet supported: %d",
	     NodeGetTag(node));
	result = (TupleTableSlot) NULL;
    }
    
    if (_exec_collect_stats_ && testFlag && !TupIsNull((Pointer) result))
	set_plan_size(node, get_plan_size(node) + 1);
    
    return result;
}

int
ExecCountSlotsNode(node)
    Plan node;
{
    if (node == (Plan)NULL)
	return 0;

    switch(NodeType(node)) {
	/* ----------------
	 *	control nodes
	 * ----------------
	 */
    case classTag(Result):
	return ExecCountSlotsResult(node);
	
    case classTag(Append):
	return ExecCountSlotsAppend(node);
              
	/* ----------------
	 *	scan nodes
	 * ----------------
	 */
    case classTag(SeqScan):
	return ExecCountSlotsSeqScan(node);

    case classTag(IndexScan):
	return ExecCountSlotsIndexScan(node);
	
	/* ----------------
	 *	join nodes
	 * ----------------
	 */
    case classTag(NestLoop):
	return ExecCountSlotsNestLoop(node);

    case classTag(MergeJoin):
	return ExecCountSlotsMergeJoin(node);
	
	/* ----------------
	 *	materialization nodes
	 * ----------------
	 */
    case classTag(Material):
	return ExecCountSlotsMaterial(node);

    case classTag(Sort):
	return ExecCountSlotsSort(node);

    case classTag(Unique):
	return ExecCountSlotsUnique(node);

    case classTag(Agg):
	return ExecCountSlotsAgg(node);
		
    case classTag(Hash):
	return ExecCountSlotsHash(node);
	
    case classTag(HashJoin):
	return ExecCountSlotsHashJoin(node);
	
    case classTag(ScanTemps):
	return ExecCountSlotsScanTemps(node);
	
    default:
	elog(WARN, "ExecCountSlotsNode: node not yet supported: %d",
	     NodeGetTag(node));
	break;
    }
    return 0;
}

/* ----------------------------------------------------------------  
 *   	ExecEndNode
 *   
 *   	Recursively cleans up all the nodes in the plan rooted
 *   	at 'node'.
 *
 *   	After this operation, the query plan will not be able to
 *	processed any further.  This should be called only after
 *	the query plan has been fully executed.
 * ----------------------------------------------------------------  
 */
void
ExecEndNode(node)
    Plan node;
{
    HookNode 		hook;
    HookFunction 	proc;

    /* ----------------
     *	do nothing when we get to the end
     *  of a leaf on tree.
     * ----------------
     */
    if (node == NULL) 
	return;

    switch(NodeType(node)) {
	/* ----------------
	 *  control nodes
	 * ----------------
	 */
    case classTag(Result):
	END_WITH_HOOKS(Result,resstate);
	break;
	
    case classTag(Append):
	END_WITH_HOOKS(Append,unionstate);
	break;
	
	/* ----------------
	 *	scan nodes
	 * ----------------
	 */
    case classTag(SeqScan):
	END_WITH_HOOKS(SeqScan,scanstate);
	break;
    
    case classTag(IndexScan):
	END_WITH_HOOKS(IndexScan,indxstate);
	break;
    
	/* ----------------
	 *	join nodes
	 * ----------------
	 */
    case classTag(NestLoop):
	END_WITH_HOOKS(NestLoop,nlstate);
	break;
	
    case classTag(MergeJoin):
	END_WITH_HOOKS(MergeJoin,mergestate);
	break;
	
	/* ----------------
	 *	materialization nodes
	 * ----------------
	 */
    case classTag(Material):
	END_WITH_HOOKS(Material,matstate);
	break;
    
    case classTag(Sort):
	END_WITH_HOOKS(Sort,sortstate);
	break;
    
    case classTag(Unique):
	END_WITH_HOOKS(Unique,uniquestate);
	break;

    case classTag(Agg):
	END_WITH_HOOKS(Agg, aggstate);
	break;


	/* ----------------
	 *	XXX add hooks to these
	 * ----------------
	 */
    case classTag(Hash):
	ExecEndHash((Hash) node);
	break;
	
    case classTag(HashJoin):
	ExecEndHashJoin((HashJoin) node);
	break;
    
    case classTag(ScanTemps):
	ExecEndScanTemps((ScanTemps) node);
	break;

    default:
	elog(DEBUG, "ExecEndNode: node not yet supported",
	     NodeGetTag(node));
	break;
    }
}
