/* ------------------------------------------------------------------------
 *
 * nodeCustom.c
 *		Routines to handle execution of custom scan node
 *
 * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * ------------------------------------------------------------------------
 */
#include "postgres.h"

#include "executor/executor.h"
#include "executor/nodeCustom.h"
#include "nodes/execnodes.h"
#include "nodes/plannodes.h"
#include "parser/parsetree.h"
#include "utils/hsearch.h"
#include "utils/memutils.h"
#include "utils/rel.h"

CustomScanState *
ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags)
{
	CustomScanState    *css;
	Index				scan_relid = cscan->scan.scanrelid;

	/* populate a CustomScanState according to the CustomScan */
	css = (CustomScanState *) cscan->methods->CreateCustomScanState(cscan);
	Assert(IsA(css, CustomScanState));

	/* fill up fields of ScanState */
	css->ss.ps.plan = &cscan->scan.plan;
	css->ss.ps.state = estate;

	/* create expression context for node */
	ExecAssignExprContext(estate, &css->ss.ps);

	/* initialize child expressions */
	css->ss.ps.targetlist = (List *)
		ExecInitExpr((Expr *) cscan->scan.plan.targetlist,
					 (PlanState *) css);
	css->ss.ps.qual = (List *)
		ExecInitExpr((Expr *) cscan->scan.plan.qual,
					 (PlanState *) css);

	/* tuple table initialization */
	ExecInitScanTupleSlot(estate, &css->ss);
	ExecInitResultTupleSlot(estate, &css->ss.ps);

	/*
	 * open the base relation and acquire an appropriate lock on it;
	 * also, get and assign the scan type
	 */
	if (scan_relid > 0)
	{
		Relation		scan_rel;

		scan_rel = ExecOpenScanRelation(estate, scan_relid, eflags);
		css->ss.ss_currentRelation = scan_rel;
		css->ss.ss_currentScanDesc = NULL;	/* set by provider */
		ExecAssignScanType(&css->ss, RelationGetDescr(scan_rel));
	}
	else
	{
		TupleDesc	ps_tupdesc;

		ps_tupdesc = ExecCleanTypeFromTL(cscan->custom_ps_tlist, false);
		ExecAssignScanType(&css->ss, ps_tupdesc);
	}
	css->ss.ps.ps_TupFromTlist = false;

	/*
	 * Initialize result tuple type and projection info.
	 */
	ExecAssignResultTypeFromTL(&css->ss.ps);
	ExecAssignScanProjectionInfo(&css->ss);

	/*
	 * The callback of custom-scan provider applies the final initialization
	 * of the custom-scan-state node according to its logic.
	 */
	css->methods->BeginCustomScan(css, estate, eflags);

	return css;
}

TupleTableSlot *
ExecCustomScan(CustomScanState *node)
{
	Assert(node->methods->ExecCustomScan != NULL);
	return node->methods->ExecCustomScan(node);
}

void
ExecEndCustomScan(CustomScanState *node)
{
	Assert(node->methods->EndCustomScan != NULL);
	node->methods->EndCustomScan(node);

	/* Free the exprcontext */
	ExecFreeExprContext(&node->ss.ps);

	/* Clean out the tuple table */
	ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
	ExecClearTuple(node->ss.ss_ScanTupleSlot);

	/* Close the heap relation */
	if (node->ss.ss_currentRelation)
		ExecCloseScanRelation(node->ss.ss_currentRelation);
}

void
ExecReScanCustomScan(CustomScanState *node)
{
	Assert(node->methods->ReScanCustomScan != NULL);
	node->methods->ReScanCustomScan(node);
}

void
ExecCustomMarkPos(CustomScanState *node)
{
	if (!node->methods->MarkPosCustomScan)
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg("custom-scan \"%s\" does not support MarkPos",
						node->methods->CustomName)));
	node->methods->MarkPosCustomScan(node);
}

void
ExecCustomRestrPos(CustomScanState *node)
{
	if (!node->methods->RestrPosCustomScan)
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg("custom-scan \"%s\" does not support MarkPos",
						node->methods->CustomName)));
	node->methods->RestrPosCustomScan(node);
}
