/* ----------------------------------------------------------------
 *   FILE
 *      append.c
 *      
 *   DESCRIPTION
 *      routines to handle append nodes.
 *
 *   INTERFACE ROUTINES
 *      ExecInitAppend  - initialize the append node
 *      ExecProcAppend  - retrieve the next tuple from the node
 *      ExecEndAppend   - shut down the append node
 *
 *   NOTES
 *      Each append node contains a list of one or more subplans which
 *      must be iteratively processed (forwards or backwards).
 *      Tuples are retrieved by executing the 'whichplan'th subplan
 *      until the subplan stops returning tuples, at which point that
 *      plan is shut down and the next started up.
 *
 *      Append nodes don't make use of their left and right
 *      subtrees, rather they maintain a list of subplans so
 *      a typical append node looks like this in the plan tree:
 *
 *                 ...
 *                 /
 *              Append -------+------+------+--- nil
 *              /   \         |      |      |
 *            nil   nil      ...    ...    ...
 *                               subplans
 *
 *      Append nodes are currently used to support inheritance
 *      queries, where several relations need to be scanned.
 *      For example, in our standard person/student/employee/student-emp
 *      example, where student and employee inherit from person
 *      and student-emp inherits from student and employee, the
 *      query:
 *
 *              retrieve (e.name) from e in person*
 *
 *      generates the plan:
 *
 *                |
 *              Append -------+-------+--------+--------+
 *              /   \         |       |        |        |
 *            nil   nil      Scan    Scan     Scan     Scan
 *                            |       |        |        |
 *                          person employee student student-emp
 *
 *
 *   IDENTIFICATION
 *      $Header: /private/postgres/src/executor/RCS/n_append.c,v 1.11 1992/08/25 17:48:18 mer Exp $
 * ----------------------------------------------------------------
 */

#include "executor/executor.h"

 RcsId("$Header: /private/postgres/src/executor/RCS/n_append.c,v 1.11 1992/08/25 17:48:18 mer Exp $");
/* ----------------------------------------------------------------
 *      exec-append-initialize-next
 *    
 *      Sets up the append node state (i.e. the append state node)
 *      for the "next" scan.
 *    
 *      Returns t iff there is a "next" scan to process.
 * ----------------------------------------------------------------
 */
/**** xxref:
 *           ExecInitAppend
 *           ExecProcAppend
 ****/
List
exec_append_initialize_next(node)
    Append      node;
{
    EState         estate;
    AppendState    unionstate;
    TupleTableSlot result_slot;
    List           rangeTable;
    
    int            whichplan;
    int            nplans;
    List           rtentries;
    List           rtentry;
    
    Index          unionrelid;
    
    /* ----------------
     *  get information from the append node
     * ----------------
     */
    estate =    (EState) get_state((Plan) node);
    unionstate =         get_unionstate(node);
    result_slot =        get_cs_ResultTupleSlot((CommonState) unionstate);
    rangeTable =         get_es_range_table(estate);
    
    whichplan = get_as_whichplan(unionstate);
    nplans =    get_as_nplans(unionstate);
    rtentries = get_unionrtentries(node);
    
    if (whichplan < 0) {
        /* ----------------
         *      if scanning in reverse, we start at
         *      the last scan in the list and then
         *      proceed back to the first.. in any case
         *      we inform ExecProcAppend that we are
         *      at the end of the line by returning LispNil
         * ----------------
         */
        set_as_whichplan(unionstate, 0);
        return LispNil;
	
    } else if (whichplan >= nplans) {
        /* ----------------
         *      as above, end the scan if we go beyond
         *      the last scan in our list..
         * ----------------
         */
        set_as_whichplan(unionstate, nplans - 1);
        return LispNil;
        
    } else {
        /* ----------------
         *      initialize the scan
         *      (and update the range table appropriately)
         * ----------------
         */
	if (get_unionrelid(node) > 0) {
	    rtentry = nth(whichplan, rtentries);
	    if (lispNullp(rtentry))
		elog(DEBUG, "exec_append_initialize_next: rtentry is nil");
	  
	    unionrelid = get_unionrelid(node);
	    rt_store(unionrelid, rangeTable, rtentry);
	    if (get_es_junkFilter_list(estate)) {
		set_es_junkFilter(estate,
		    (JunkFilter)nth(whichplan, get_es_junkFilter_list(estate)));
	    }
	    if (get_es_result_relation_info_list(estate)) {
		set_es_result_relation_info(estate,
		    (RelationInfo)nth(whichplan,
				     get_es_result_relation_info_list(estate)));
	    }
	    set_ttc_whichplan( result_slot, whichplan );
	}
      
	return LispTrue;
    }
}
 
/* ----------------------------------------------------------------
 *      ExecInitAppend
 *    
 *      Begins all of the subscans of the append node, storing the
 *      scan structures in the 'initialized' vector of the append-state
 *      structure.
 *
 *     (This is potentially wasteful, since the entire result of the
 *      append node may not be scanned, but this way all of the
 *      structures get allocated in the executor's top level memory
 *      block instead of that of the call to ExecProcAppend.)
 *    
 *      Returns the scan result of the first scan.
 * ----------------------------------------------------------------
 */
/**** xxref:
 *           ExecInitNode
 ****/
List
ExecInitAppend(node, estate, parent)
    Append      node;
    EState      estate;
    Plan 	parent;
{
    AppendState unionstate;
    List        rangeTable;     /* LLL unused */
    
    int         nplans;
    List        rtentries;
    List        unionplans;
    ListPtr     initialized;
    int         i;
    Plan        initNode;
    List        result;
    List        junkList;
    int         baseid;
    RelationInfo es_rri = get_es_result_relation_info(estate);
    
    /* ----------------
     *  assign execution state to node and get information
     *  for append state
     * ----------------
     */
    set_state((Plan) node,  (EStatePtr)estate);
    
    unionplans =        get_unionplans(node);
    nplans =            length(unionplans);
    rtentries =         get_unionrtentries(node);
    
    CXT1_printf("ExecInitAppend: context is %d\n", CurrentMemoryContext);
    initialized = (ListPtr)
        palloc(nplans * sizeof(List));
    
    /* ----------------
     *  create new AppendState for our append node
     * ----------------
     */   
    unionstate =  MakeAppendState(0, nplans, initialized, rtentries);
    set_unionstate(node, unionstate);
    
    /* ----------------
     *  Miscellanious initialization
     *
     *	     +	assign node's base_id
     *       +	assign debugging hooks
     *
     *  Append plans don't have expression contexts because they
     *  never call ExecQual or ExecTargetList.
     * ----------------
     */
    ExecAssignNodeBaseInfo(estate, (BaseNode) unionstate, parent);
    ExecAssignDebugHooks((Plan) node, (BaseNode) unionstate);

#define APPEND_NSLOTS 1
    /* ----------------
     *	append nodes still have Result slots, which hold pointers
     *  to tuples, so we have to initialize them..
     * ----------------
     */
    ExecInitResultTupleSlot(estate, (CommonState) unionstate);
    
    /*
     * If the inherits rtentry is the result relation, we have to make
     * a result relation info list for all inheritors so we can update
     * their indices and put the result tuples in the right place etc.
     *
     * e.g. replace p (age = p.age + 1) from p in person*
     */
    if ((es_rri != (RelationInfo)NULL) &&
	(get_unionrelid((Append)node) == get_ri_RangeTableIndex(es_rri)))
    {
	RelationInfo rri;
	List         resultList = LispNil;
	List         rtentryP;

	foreach(rtentryP,rtentries)
	{
	    ObjectId reloid;
	    List     rtentry = CAR(rtentryP);

	    reloid = CInteger(rt_relid(rtentry));
	    rri = MakeRelationInfo(get_ri_RangeTableIndex(es_rri),
				   heap_open(reloid),
				   0,		/* num indices */
				   NULL,	/* index descs */
				   NULL);	/* index key info */
	    resultList = nappend1(resultList, (LispValue)rri);
	    ExecOpenIndices(reloid, rri);
	}
	set_es_result_relation_info_list(estate, resultList);
    }
    /* ----------------
     *  call ExecInitNode on each of the plans in our list
     *  and save the results into the array "initialized"
     * ----------------
     */       
    junkList = LispNil;
    for(i = 0; i < nplans ; i++ ) {
	JunkFilter j;
	List       targetList;
        /* ----------------
         *  NOTE: we first modify range table in 
         *        exec_append_initialize_next() and
         *        then initialize the subnode,
         *        since it may use the range table.
         * ----------------
         */
        set_as_whichplan(unionstate, i);
        exec_append_initialize_next(node);
	
        initNode = (Plan) nth(i, unionplans);
        result =          ExecInitNode(initNode, estate, (Plan) node);
	
        initialized[i] = result;
	
	/* ---------------
	 *  Each targetlist in the subplan may need its own junk filter
	 *
	 *  This is true only when the reln being replaced/deleted is
	 *  the one that we're looking at the subclasses of
	 * ---------------
	 */
	if ((es_rri != (RelationInfo)NULL) &&
	    (get_unionrelid((Append)node) == get_ri_RangeTableIndex(es_rri))) {

	    targetList = get_qptargetlist(initNode);
	    j = (JunkFilter) ExecInitJunkFilter(targetList);
	    junkList = nappend1(junkList, (LispValue)j);
	}

    }
    set_es_junkFilter_list(estate, junkList);
    if (junkList != LispNil)
	set_es_junkFilter(estate, (JunkFilter)CAR(junkList));
    
    /* ----------------
     *	initialize the return type from the appropriate subplan.
     * ----------------
     */
    initNode = (Plan) nth(0, unionplans);
    ExecAssignResultType((CommonState) unionstate,
			 ExecGetExecTupDesc(initNode),
			 ExecGetTupType(initNode));
    set_cs_ProjInfo((CommonState) unionstate, NULL);
    
    /* ----------------
     *  return the result from the first subplan's initialization
     * ----------------
     */       
    set_as_whichplan(unionstate, 0);
    exec_append_initialize_next(node);
    result = (List) initialized[0];
    
    return
        result;
}
 
int
ExecCountSlotsAppend(node)
    Plan node;
{
    List plan;
    List unionplans = get_unionplans((Append)node);
    int  nSlots     = 0;

    foreach (plan,unionplans) {
	nSlots += ExecCountSlotsNode(CAR(plan));
    }
    return nSlots + APPEND_NSLOTS;
}
 
/* ----------------------------------------------------------------
 *     ExecProcAppend
 *    
 *      Handles the iteration over the multiple scans.
 *    
 *     NOTE: Can't call this ExecAppend, that name is used in execMain.l
 * ----------------------------------------------------------------
 */
/**** xxref:
 *           ExecProcAppend
 *           ExecProcNode
 ****/
TupleTableSlot
ExecProcAppend(node)
    Append              node;
{
    EState              estate;
    AppendState         unionstate;
    
    int                 whichplan;
    List                unionplans;
    Plan                subnode;
    TupleTableSlot      result;
    TupleTableSlot      result_slot;
    ScanDirection       direction;
    
    /* ----------------
     *  get information from the node
     * ----------------
     */
    unionstate =      get_unionstate(node);
    estate = (EState) get_state((Plan) node);
    direction =       get_es_direction(estate);
    
    unionplans =      get_unionplans(node);
    whichplan =       get_as_whichplan(unionstate);
    result_slot =     get_cs_ResultTupleSlot((CommonState) unionstate);
	
    /* ----------------
     *  figure out which subplan we are currently processing
     * ----------------
     */
    subnode = (Plan) nth(whichplan, unionplans);
    
    if (lispNullp(subnode))
        elog(DEBUG, "ExecProcAppend: subnode is NULL");
    
    /* ----------------
     *  get a tuple from the subplan
     * ----------------
     */
    result = ExecProcNode(subnode);
    
    if (! TupIsNull((Pointer) result)) {
        /* ----------------
         *  if the subplan gave us something then place a copy of
	 *  whatever we get into our result slot and return it, else..
         * ----------------
         */
	return
	    (TupleTableSlot)ExecStoreTuple(ExecFetchTuple((Pointer) result),
					   (Pointer) result_slot,
			   		    ExecSlotBuffer((Pointer) result),
			   		    false);
    
    } else {
        /* ----------------
         *  .. go on to the "next" subplan in the appropriate
         *  direction and try processing again (recursively)
         * ----------------
         */
        whichplan = get_as_whichplan(unionstate);
        
        if (direction == EXEC_FRWD)
	{
            set_as_whichplan(unionstate, whichplan + 1);
	}
        else
	{
            set_as_whichplan(unionstate, whichplan - 1);
	}

	/* ----------------
	 *  return something from next node or an empty slot
	 *  all of our subplans have been exhausted.
	 * ----------------
	 */
        if (exec_append_initialize_next(node)) {
	    ExecSetSlotDescriptorIsNew((Pointer) result_slot, true);
            return
		ExecProcAppend(node);
        } else
	    return
		(TupleTableSlot)ExecClearTuple((Pointer) result_slot);
    }
}
 
/* ----------------------------------------------------------------
 *      ExecEndAppend
 *    
 *      Shuts down the subscans of the append node.
 *    
 *      Returns nothing of interest.
 * ----------------------------------------------------------------
 */
/**** xxref:
 *           ExecEndNode
 ****/
void
ExecEndAppend(node)
    Append      node;
{
    AppendState unionstate;
    int         nplans;
    List        unionplans;
    ListPtr     initialized;
    int         i;
    
    /* ----------------
     *  get information from the node
     * ----------------
     */
    unionstate =  get_unionstate(node);
    unionplans =  get_unionplans(node);
    nplans =      get_as_nplans(unionstate);
    initialized = get_as_initialized(unionstate);
    
    /* ----------------
     *  shut down each of the subscans
     * ----------------
     */
    for(i = 0; i < nplans; i++) {
        if (! lispNullp(initialized[i])) {
            ExecEndNode( (Plan) nth(i, unionplans) );
        }
    }
}
 
