/*-------------------------------------------------------------------------
 *
 * execAmi.c
 *	  miscellaneous executor access method routines
 *
 * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *	$Id: execAmi.c,v 1.1.1.1 2002/02/07 17:51:52 bpalmer Exp $
 *
 *-------------------------------------------------------------------------
 */
/*
 *	 INTERFACE ROUTINES
 *
 *		ExecOpenScanR	\							  / amopen
 *		ExecBeginScan	 \							 /	ambeginscan
 *		ExecCloseR		  \							/	amclose
 *		ExecInsert		   \  executor interface   /	aminsert
 *		ExecReScanR		   /  to access methods    \	amrescan
 *		ExecMarkPos		  /							\	ammarkpos
 *		ExecRestrPos	 /							 \	amrestpos
 */

#include "postgres.h"


#include "access/genam.h"
#include "access/heapam.h"
#include "catalog/heap.h"
#include "executor/execdebug.h"
#include "executor/instrument.h"
#include "executor/nodeAgg.h"
#include "executor/nodeAppend.h"
#include "executor/nodeGroup.h"
#include "executor/nodeGroup.h"
#include "executor/nodeHash.h"
#include "executor/nodeHashjoin.h"
#include "executor/nodeIndexscan.h"
#include "executor/nodeTidscan.h"
#include "executor/nodeLimit.h"
#include "executor/nodeMaterial.h"
#include "executor/nodeMergejoin.h"
#include "executor/nodeNestloop.h"
#include "executor/nodeResult.h"
#include "executor/nodeSeqscan.h"
#include "executor/nodeSetOp.h"
#include "executor/nodeSort.h"
#include "executor/nodeSubplan.h"
#include "executor/nodeSubqueryscan.h"
#include "executor/nodeUnique.h"

static Pointer ExecBeginScan(Relation relation, int nkeys, ScanKey skeys,
			  bool isindex, ScanDirection dir, Snapshot snapshot);

/* ----------------------------------------------------------------
 *		ExecOpenScanR
 *
 * old comments:
 *		Parameters:
 *		  relation -- relation to be opened and scanned.
 *		  nkeys    -- number of keys
 *		  skeys    -- keys to restrict scanning
 *			 isindex  -- if this is true, the relation is the relid of
 *						 an index relation, else it is a heap relation.
 *		Returns the relation as(relDesc scanDesc)
 * ----------------------------------------------------------------
 */
void
ExecOpenScanR(Oid relOid,
			  int nkeys,
			  ScanKey skeys,
			  bool isindex,
			  ScanDirection dir,
			  Snapshot snapshot,
			  Relation *returnRelation, /* return */
			  Pointer *returnScanDesc)	/* return */
{
	Relation	relation;
	Pointer		scanDesc;

	/*
	 * note: scanDesc returned by ExecBeginScan can be either a
	 * HeapScanDesc or an IndexScanDesc so for now we make it a Pointer.
	 * There should be a better scan abstraction someday -cim 9/9/89
	 */

	/*
	 * Open the relation with the correct call depending on whether this
	 * is a heap relation or an index relation.
	 *
	 * For a table, acquire AccessShareLock for the duration of the query
	 * execution.  For indexes, acquire no lock here; the index machinery
	 * does its own locks and unlocks.	(We rely on having some kind of
	 * lock on the parent table to ensure the index won't go away!)
	 */
	if (isindex)
		relation = index_open(relOid);
	else
		relation = heap_open(relOid, AccessShareLock);

	scanDesc = ExecBeginScan(relation,
							 nkeys,
							 skeys,
							 isindex,
							 dir,
							 snapshot);

	if (returnRelation != NULL)
		*returnRelation = relation;
	if (scanDesc != NULL)
		*returnScanDesc = scanDesc;
}

/* ----------------------------------------------------------------
 *		ExecBeginScan
 *
 *		beginscans a relation in current direction.
 *
 *		XXX fix parameters to AMbeginscan (and btbeginscan)
 *				currently we need to pass a flag stating whether
 *				or not the scan should begin at an endpoint of
 *				the relation.. Right now we always pass false
 *				-cim 9/14/89
 * ----------------------------------------------------------------
 */
static Pointer
ExecBeginScan(Relation relation,
			  int nkeys,
			  ScanKey skeys,
			  bool isindex,
			  ScanDirection dir,
			  Snapshot snapshot)
{
	Pointer		scanDesc;

	/*
	 * open the appropriate type of scan.
	 *
	 * Note: ambeginscan()'s second arg is a boolean indicating that the scan
	 * should be done in reverse..	That is, if you pass it true, then the
	 * scan is backward.
	 */
	if (isindex)
	{
		scanDesc = (Pointer) index_beginscan(relation,
											 false,		/* see above comment */
											 nkeys,
											 skeys);
	}
	else
	{
		scanDesc = (Pointer) heap_beginscan(relation,
											ScanDirectionIsBackward(dir),
											snapshot,
											nkeys,
											skeys);
	}

	if (scanDesc == NULL)
		elog(DEBUG, "ExecBeginScan: scanDesc = NULL, heap_beginscan failed.");

	return scanDesc;
}

/* ----------------------------------------------------------------
 *		ExecCloseR
 *
 *		closes the relation and scan descriptor for a scan node.
 *		Also closes index relations and scans for index scans.
 * ----------------------------------------------------------------
 */
void
ExecCloseR(Plan *node)
{
	CommonScanState *state;
	Relation	relation;
	HeapScanDesc scanDesc;

	/*
	 * get state for node and shut down the heap scan, if any
	 */
	switch (nodeTag(node))
	{
		case T_SeqScan:
			state = ((SeqScan *) node)->scanstate;
			break;

		case T_IndexScan:
			state = ((IndexScan *) node)->scan.scanstate;
			break;

		case T_TidScan:
			state = ((TidScan *) node)->scan.scanstate;
			break;

		default:
			elog(DEBUG, "ExecCloseR: not a scan node!");
			return;
	}

	relation = state->css_currentRelation;
	scanDesc = state->css_currentScanDesc;

	if (scanDesc != NULL)
		heap_endscan(scanDesc);

	/*
	 * if this is an index scan then we have to take care of the index
	 * relations as well.
	 */
	if (IsA(node, IndexScan))
	{
		IndexScan  *iscan = (IndexScan *) node;
		IndexScanState *indexstate = iscan->indxstate;
		int			numIndices;
		RelationPtr indexRelationDescs;
		IndexScanDescPtr indexScanDescs;
		int			i;

		numIndices = indexstate->iss_NumIndices;
		indexRelationDescs = indexstate->iss_RelationDescs;
		indexScanDescs = indexstate->iss_ScanDescs;

		for (i = 0; i < numIndices; i++)
		{
			/*
			 * shut down each of the index scans and close each of the
			 * index relations
			 */
			if (indexScanDescs[i] != NULL)
				index_endscan(indexScanDescs[i]);

			if (indexRelationDescs[i] != NULL)
				index_close(indexRelationDescs[i]);
		}
	}

	/*
	 * Finally, close the heap relation.
	 *
	 * Currently, we do not release the AccessShareLock acquired by
	 * ExecOpenScanR.  This lock should be held till end of transaction.
	 * (There is a faction that considers this too much locking, however.)
	 */
	if (relation != NULL)
		heap_close(relation, NoLock);
}

/* ----------------------------------------------------------------
 *		ExecReScan
 *
 *		XXX this should be extended to cope with all the node types..
 *
 *		takes the new expression context as an argument, so that
 *		index scans needn't have their scan keys updated separately
 *		- marcel 09/20/94
 * ----------------------------------------------------------------
 */
void
ExecReScan(Plan *node, ExprContext *exprCtxt, Plan *parent)
{
	if (node->instrument)
		InstrEndLoop(node->instrument);

	if (node->chgParam != NULL) /* Wow! */
	{
		List	   *lst;

		foreach(lst, node->initPlan)
		{
			Plan	   *splan = ((SubPlan *) lfirst(lst))->plan;

			if (splan->extParam != NULL)		/* don't care about child
												 * locParam */
				SetChangedParamList(splan, node->chgParam);
			if (splan->chgParam != NULL)
				ExecReScanSetParamPlan((SubPlan *) lfirst(lst), node);
		}
		foreach(lst, node->subPlan)
		{
			Plan	   *splan = ((SubPlan *) lfirst(lst))->plan;

			if (splan->extParam != NULL)
				SetChangedParamList(splan, node->chgParam);
		}
		/* Well. Now set chgParam for left/right trees. */
		if (node->lefttree != NULL)
			SetChangedParamList(node->lefttree, node->chgParam);
		if (node->righttree != NULL)
			SetChangedParamList(node->righttree, node->chgParam);
	}

	switch (nodeTag(node))
	{
		case T_SeqScan:
			ExecSeqReScan((SeqScan *) node, exprCtxt, parent);
			break;

		case T_IndexScan:
			ExecIndexReScan((IndexScan *) node, exprCtxt, parent);
			break;

		case T_TidScan:
			ExecTidReScan((TidScan *) node, exprCtxt, parent);
			break;

		case T_SubqueryScan:
			ExecSubqueryReScan((SubqueryScan *) node, exprCtxt, parent);
			break;

		case T_Material:
			ExecMaterialReScan((Material *) node, exprCtxt, parent);
			break;

		case T_NestLoop:
			ExecReScanNestLoop((NestLoop *) node, exprCtxt, parent);
			break;

		case T_HashJoin:
			ExecReScanHashJoin((HashJoin *) node, exprCtxt, parent);
			break;

		case T_Hash:
			ExecReScanHash((Hash *) node, exprCtxt, parent);
			break;

		case T_Agg:
			ExecReScanAgg((Agg *) node, exprCtxt, parent);
			break;

		case T_Group:
			ExecReScanGroup((Group *) node, exprCtxt, parent);
			break;

		case T_Result:
			ExecReScanResult((Result *) node, exprCtxt, parent);
			break;

		case T_Unique:
			ExecReScanUnique((Unique *) node, exprCtxt, parent);
			break;

		case T_SetOp:
			ExecReScanSetOp((SetOp *) node, exprCtxt, parent);
			break;

		case T_Limit:
			ExecReScanLimit((Limit *) node, exprCtxt, parent);
			break;

		case T_Sort:
			ExecReScanSort((Sort *) node, exprCtxt, parent);
			break;

		case T_MergeJoin:
			ExecReScanMergeJoin((MergeJoin *) node, exprCtxt, parent);
			break;

		case T_Append:
			ExecReScanAppend((Append *) node, exprCtxt, parent);
			break;

		default:
			elog(ERROR, "ExecReScan: node type %d not supported",
				 nodeTag(node));
			return;
	}

	if (node->chgParam != NULL)
	{
		freeList(node->chgParam);
		node->chgParam = NULL;
	}
}

/* ----------------------------------------------------------------
 *		ExecReScanR
 *
 *		XXX this does not do the right thing with indices yet.
 * ----------------------------------------------------------------
 */
HeapScanDesc
ExecReScanR(Relation relDesc,	/* LLL relDesc unused  */
			HeapScanDesc scanDesc,
			ScanDirection direction,
			int nkeys,			/* LLL nkeys unused  */
			ScanKey skeys)
{
	if (scanDesc != NULL)
		heap_rescan(scanDesc,	/* scan desc */
					ScanDirectionIsBackward(direction), /* backward flag */
					skeys);		/* scan keys */

	return scanDesc;
}

/* ----------------------------------------------------------------
 *		ExecMarkPos
 *
 *		Marks the current scan position.
 *
 *		XXX Needs to be extended to include all the node types,
 *		or at least all the ones that can be directly below a mergejoin.
 * ----------------------------------------------------------------
 */
void
ExecMarkPos(Plan *node)
{
	switch (nodeTag(node))
	{
		case T_SeqScan:
			ExecSeqMarkPos((SeqScan *) node);
			break;

		case T_IndexScan:
			ExecIndexMarkPos((IndexScan *) node);
			break;

		case T_Material:
			ExecMaterialMarkPos((Material *) node);
			break;

		case T_Sort:
			ExecSortMarkPos((Sort *) node);
			break;

		case T_TidScan:
			ExecTidMarkPos((TidScan *) node);
			break;

		default:
			/* don't make hard error unless caller asks to restore... */
			elog(DEBUG, "ExecMarkPos: node type %d not supported",
				 nodeTag(node));
			break;
	}
}

/* ----------------------------------------------------------------
 *		ExecRestrPos
 *
 *		restores the scan position previously saved with ExecMarkPos()
 *
 *		XXX Needs to be extended to include all the node types,
 *		or at least all the ones that can be directly below a mergejoin.
 * ----------------------------------------------------------------
 */
void
ExecRestrPos(Plan *node)
{
	switch (nodeTag(node))
	{
		case T_SeqScan:
			ExecSeqRestrPos((SeqScan *) node);
			break;

		case T_IndexScan:
			ExecIndexRestrPos((IndexScan *) node);
			break;

		case T_Material:
			ExecMaterialRestrPos((Material *) node);
			break;

		case T_Sort:
			ExecSortRestrPos((Sort *) node);
			break;

		default:
			elog(ERROR, "ExecRestrPos: node type %d not supported",
				 nodeTag(node));
			break;
	}
}
