/* ----------------------------------------------------------------
 *   FILE
 *	material.c
 *	
 *   DESCRIPTION
 *	Routines to handle materialization nodes.
 *
 *   INTERFACE ROUTINES
 *     	ExecMaterial		- generate a temporary relation
 *     	ExecInitMaterial	- initialize node and subnodes..
 *     	ExecEndMaterial		- shutdown node and subnodes
 *
 *   NOTES
 *	
 *   IDENTIFICATION
 *	$Header: /private/postgres/src/executor/RCS/n_material.c,v 1.8 1992/08/04 17:37:55 mer Exp $
 * ----------------------------------------------------------------
 */

#include "executor/executor.h"

 RcsId("$Header: /private/postgres/src/executor/RCS/n_material.c,v 1.8 1992/08/04 17:37:55 mer Exp $");

/* ----------------------------------------------------------------
 *   	ExecMaterial
 *
 *	The first time this is called, ExecMaterial retrieves tuples
 *	this node's outer subplan and inserts them into a temporary
 *	relation.  After this is done, a flag is set indicating that
 *	the subplan has been materialized.  Once the relation is
 *	materialized, the first tuple is then returned.  Successive
 *	calls to ExecMaterial return successive tuples from the temp
 *	relation.
 *
 *	Initial State:
 *
 *	ExecMaterial assumes the temporary relation has been
 *	created and openend by ExecInitMaterial during the prior
 *	InitPlan() phase.
 *
 * ----------------------------------------------------------------
 */
/**** xxref:
 *           ExecProcNode
 ****/
TupleTableSlot			/* result tuple from subplan */
ExecMaterial(node)
    Material node;
{
    EState	  	estate;
    MaterialState	matstate;
    Plan 	  	outerNode;
    ScanDirection 	dir;
    Relation	  	tempRelation;
    Relation	  	currentRelation;
    HeapScanDesc  	currentScanDesc;
    HeapTuple     	heapTuple;
    TupleTableSlot  	slot;
    Buffer		buffer;
    
    /* ----------------
     *	get state info from node
     * ----------------
     */
    matstate =   	get_matstate(node);
    estate =      	(EState) get_state((Plan) node);
    dir =   	  	get_es_direction(estate);
    
    /* ----------------
     *	the first time we call this, we retrieve all tuples
     *  from the subplan into a temporary relation and then
     *  we sort the relation.  Subsequent calls return tuples
     *  from the temporary relation.
     * ----------------
     */
    
    if (get_mat_Flag(matstate) == false) {
	/* ----------------
	 *  set all relations to be scanned in the forward direction 
	 *  while creating the temporary relation.
	 * ----------------
	 */
	set_es_direction(estate, EXEC_FRWD);
	
	/* ----------------
	 *   if we couldn't create the temp or current relations then
	 *   we print a warning and return NULL.
	 * ----------------
	 */
	tempRelation =  get_mat_TempRelation(matstate);
	if (tempRelation == NULL) {
	    elog(DEBUG, "ExecMaterial: temp relation is NULL! aborting...");
	    return NULL;
	}
	
	currentRelation = get_css_currentRelation((CommonScanState)matstate);
	if (currentRelation == NULL) {
	    elog(DEBUG, "ExecMaterial: current relation is NULL! aborting...");
	    return NULL;
	}
	
	/* ----------------
	 *   retrieve tuples from the subplan and
	 *   insert them in the temporary relation
	 * ----------------
	 */
	outerNode =     get_outerPlan((Plan) node);
	for (;;) {
	    slot = ExecProcNode(outerNode);
	    
	    heapTuple = (HeapTuple) ExecFetchTuple((Pointer) slot);
	    if (heapTuple == NULL)
		break;
	    
	    aminsert(tempRelation, 	/* relation desc */
		     heapTuple,		/* heap tuple to insert */
		     0);		/* return: offset */
	    
	    ExecClearTuple((Pointer) slot);
	}
	currentRelation = tempRelation;
	
	/* ----------------
	 *   restore to user specified direction
	 * ----------------
	 */
	set_es_direction(estate, dir);
	
	/* ----------------
	 *   now initialize the scan descriptor to scan the
	 *   sorted relation and update the sortstate information
	 * ----------------
	 */
	currentScanDesc = ambeginscan(currentRelation,    /* relation */
				      (dir == EXEC_BKWD), /* bkwd flag */
				      NowTimeQual,        /* time qual */
				      0, 		  /* num scan keys */
				      NULL); 		  /* scan keys */
	set_css_currentRelation((CommonScanState)matstate, currentRelation);
	set_css_currentScanDesc((CommonScanState)matstate, currentScanDesc);
	
	ExecAssignScanType((CommonScanState)matstate,
			   TupDescToExecTupDesc(&currentRelation->rd_att,
					currentRelation->rd_rel->relnatts),
			   &currentRelation->rd_att);
	
	/* ----------------
	 *  finally set the sorted flag to true
	 * ----------------
	 */
	set_mat_Flag(matstate, true);
    }
    
    /* ----------------
     *	at this point we know we have a sorted relation so
     *  we preform a simple scan on it with amgetnext()..
     * ----------------
     */
    currentScanDesc = get_css_currentScanDesc((CommonScanState)matstate);
    
    heapTuple = amgetnext(currentScanDesc, 	/* scan desc */
			  (dir == EXEC_BKWD), 	/* bkwd flag */
			  &buffer); 		/* return: buffer */

    /* ----------------
     *	put the tuple into the scan tuple slot and return the slot.
     *  Note: since the tuple is really a pointer to a page, we don't want
     *  to call pfree() on it..
     * ----------------
     */
    slot = (TupleTableSlot)
	get_css_ScanTupleSlot((CommonScanState)matstate);

    return (TupleTableSlot)
	ExecStoreTuple((Pointer) heapTuple,  /* tuple to store */
		       (Pointer) slot,      /* slot to store in */
		       buffer,	   /* buffer for this tuple */
		       false);     /* don't pfree this pointer */
    
}
 
/* ----------------------------------------------------------------
 *   	ExecInitMaterial
 * ----------------------------------------------------------------
 */
/**** xxref:
 *           ExecInitNode
 ****/
List	/* initialization status */
ExecInitMaterial(node, estate, parent)
    Material 	node;
    EState 	estate;
    Plan	parent;
{
    MaterialState	matstate;
    Plan		outerPlan;
    TupleDescriptor	tupType;
    Relation		tempDesc;
    int			baseid;
    int			len;
    
    /* ----------------
     *  assign the node's execution state
     * ----------------
     */
    set_state((Plan) node, (EStatePtr)estate);
    
    /* ----------------
     * create state structure
     * ----------------
     */
    matstate = MakeMaterialState(false, NULL);
    set_matstate(node, matstate);
    
    /* ----------------
     *  Miscellanious initialization
     *
     *	     +	assign node's base_id
     *       +	assign debugging hooks and
     *       +  assign result tuple slot
     *
     *  Materialization nodes don't need ExprContexts because
     *  they never call ExecQual or ExecTargetList.
     * ----------------
     */
    ExecAssignNodeBaseInfo(estate, (BaseNode) matstate, parent);
    ExecAssignDebugHooks((Plan) node, (BaseNode) matstate);

#define MATERIAL_NSLOTS 1
    /* ----------------
     * tuple table initialization
     * ----------------
     */
    ExecInitScanTupleSlot(estate, (CommonScanState)matstate);
    
    /* ----------------
     * initializes child nodes
     * ----------------
     */
    outerPlan = get_outerPlan((Plan) node);
    ExecInitNode(outerPlan, estate, (Plan) node);
    
    /* ----------------
     *	initialize matstate information
     * ----------------
     */
    set_mat_Flag(matstate, false);

    /* ----------------
     * 	initialize tuple type.  no need to initialize projection
     *  info because this node doesn't do projections.
     * ----------------
     */
    ExecAssignScanTypeFromOuterPlan((Plan) node, (CommonState)matstate);
    set_cs_ProjInfo((CommonState)matstate, NULL);
    
    /* ----------------
     *	get type information needed for ExecCreatR
     * ----------------
     */
    tupType = ExecGetScanType((CommonScanState)matstate);

    /* ----------------
     *	ExecCreatR wants it's third argument to be an object id of
     *  a relation in the range table or a negative number like -1
     *  indicating that the relation is not in the range table.
     *
     *  In the second case ExecCreatR creates a temp relation.
     *  (currently this is the only case we support -cim 10/16/89)
     * ----------------
     */
    /* ----------------
     *	create the temporary relation
     * ----------------
     */
    len = ExecTargetListLength(get_qptargetlist((Plan) node));
    tempDesc = 	ExecCreatR(len, tupType, -1);
    
    /* ----------------
     *	save the relation descriptor in the sortstate
     * ----------------
     */
    set_mat_TempRelation(matstate, tempDesc);
    set_css_currentRelation((CommonScanState)matstate, tempDesc);
    
    /* ----------------
     *  return relation oid of temporary relation in a list
     *	(someday -- for now we return LispTrue... cim 10/12/89)
     * ----------------
     */
    return
	LispTrue;
}
 
int
ExecCountSlotsMaterial(node)
    Plan node;
{
    return ExecCountSlotsNode(get_outerPlan(node)) +
	   ExecCountSlotsNode(get_innerPlan(node)) +
	   MATERIAL_NSLOTS;
}
 
/* ----------------------------------------------------------------
 *   	ExecEndMaterial
 *
 * old comments
 *   	destroys the temporary relation.
 * ----------------------------------------------------------------
 */
 
/**** xxref:
 *           ExecEndNode
 ****/
void
ExecEndMaterial(node)
    Material node;
{
    MaterialState	matstate;
    Relation		tempRelation;
    Plan		outerPlan;
    
    /* ----------------
     *	get info from the material state 
     * ----------------
     */
    matstate = get_matstate(node);
    tempRelation = get_mat_TempRelation(matstate);
    
    /* ----------------
     *	the temporary relations are not cataloged so
     *  we can't call DestroyRelation() so we unlink the
     *  unix file explicitly.  Yet another hack -- the
     *  amclose routine should explicitly destroy temp relations
     *  but it doesn't yet -cim 1/25/90
     * ----------------
     */
    if (FileNameUnlink((char *) relpath((char *)
					&(tempRelation->rd_rel->relname))) < 0)
	elog(WARN, "ExecEndMaterial: unlink: %m");
    amclose(tempRelation);
    
    /* ----------------
     *	close the temp relation and shut down the scan.
     * ----------------
     */
    ExecCloseR((Plan) node);
    
    /* ----------------
     *	shut down the subplan
     * ----------------
     */
    outerPlan = get_outerPlan((Plan) node);
    ExecEndNode(outerPlan);
    
    /* ----------------
     *	clean out the tuple table
     * ----------------
     */
    ExecClearTuple((Pointer) get_css_ScanTupleSlot((CommonScanState)matstate));
} 
 
/* ----------------------------------------------------------------
 *	ExecMaterialMarkPos
 * ----------------------------------------------------------------
 */
/**** xxref:
 *           <apparently-unused>
 ****/
List /* nothing of interest */
ExecMaterialMarkPos(node)
    Material node;
{
    MaterialState 	matstate;
    HeapScanDesc 	sdesc;
    
    /* ----------------
     *	if we haven't materialized yet, just return LispNil.
     * ----------------
     */
    matstate =          get_matstate(node);
    if (get_mat_Flag(matstate) == false)
	return LispNil;
    
    /* ----------------
     *  XXX access methods don't return positions yet so
     *      for now we return LispNil.  It's possible that
     *      they will never return positions for all I know -cim 10/16/89
     * ----------------
     */
    sdesc = get_css_currentScanDesc((CommonScanState)matstate);
    ammarkpos(sdesc);
    
    return LispNil;
}
 
/* ----------------------------------------------------------------
 *	ExecMaterialRestrPos
 * ----------------------------------------------------------------
 */
/**** xxref:
 *           <apparently-unused>
 ****/
void
ExecMaterialRestrPos(node)
    Material node;
{
    MaterialState	matstate;
    HeapScanDesc 	sdesc;
    
    /* ----------------
     *	if we haven't materialized yet, just return.
     * ----------------
     */
    matstate =  get_matstate(node);
    if (get_mat_Flag(matstate) == false)
	return;
    
    /* ----------------
     *	restore the scan to the previously marked position
     * ----------------
     */
    sdesc = get_css_currentScanDesc((CommonScanState)matstate);
    amrestrpos(sdesc);
}
 
