/*-------------------------------------------------------------------------
 *
 * nodeBitmapAnd.c
 *	  routines to handle BitmapAnd nodes.
 *
 * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/executor/nodeBitmapAnd.c
 *
 *-------------------------------------------------------------------------
 */
/* INTERFACE ROUTINES
 *		ExecInitBitmapAnd	- initialize the BitmapAnd node
 *		MultiExecBitmapAnd	- retrieve the result bitmap from the node
 *		ExecEndBitmapAnd	- shut down the BitmapAnd node
 *		ExecReScanBitmapAnd - rescan the BitmapAnd node
 *
 *	 NOTES
 *		BitmapAnd nodes don't make use of their left and right
 *		subtrees, rather they maintain a list of subplans,
 *		much like Append nodes.  The logic is much simpler than
 *		Append, however, since we needn't cope with forward/backward
 *		execution.
 */

#include "postgres.h"

#include "cdb/cdbvars.h"
#include "executor/execdebug.h"
#include "executor/nodeBitmapAnd.h"
#include "nodes/tidbitmap.h"


/* ----------------------------------------------------------------
 *		ExecBitmapAnd
 *
 *		stub for pro forma compliance
 * ----------------------------------------------------------------
 */
static TupleTableSlot *
ExecBitmapAnd(PlanState *pstate)
{
	elog(ERROR, "BitmapAnd node does not support ExecProcNode call convention");
	return NULL;
}

/* ----------------------------------------------------------------
 *		ExecInitBitmapAnd
 *
 *		Begin all of the subscans of the BitmapAnd node.
 * ----------------------------------------------------------------
 */
BitmapAndState *
ExecInitBitmapAnd(BitmapAnd *node, EState *estate, int eflags)
{
	BitmapAndState *bitmapandstate;
	PlanState **bitmapplanstates;
	int			nplans;
	int			i;
	ListCell   *l;
	Plan	   *initNode;

	/* check for unsupported flags */
	Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));

	/*
	 * Set up empty vector of subplan states
	 */
	nplans = list_length(node->bitmapplans);

	bitmapandstate = makeNode(BitmapAndState);
	bitmapplanstates = (PlanState **) palloc0(nplans * sizeof(PlanState *));

	/*
	 * create new BitmapAndState for our BitmapAnd node
	 */
	bitmapandstate->ps.plan = (Plan *) node;
	bitmapandstate->ps.state = estate;
	bitmapandstate->ps.ExecProcNode = ExecBitmapAnd;
	bitmapandstate->bitmapplans = bitmapplanstates;
	bitmapandstate->nplans = nplans;

	/*
	 * call ExecInitNode on each of the plans to be executed and save the
	 * results into the array "bitmapplanstates".
	 */
	i = 0;
	foreach(l, node->bitmapplans)
	{
		initNode = (Plan *) lfirst(l);
		bitmapplanstates[i] = ExecInitNode(initNode, estate, eflags);
		i++;
	}

	/*
	 * Miscellaneous initialization
	 *
	 * BitmapAnd plans don't have expression contexts because they never call
	 * ExecQual or ExecProject.  They don't need any tuple slots either.
	 */

	return bitmapandstate;
}

/* ----------------------------------------------------------------
 *	   MultiExecBitmapAnd
 *
 *	   BitmapAnd node gets the bitmaps generated from BitmapIndexScan
 *	   nodes and outputs a bitmap that ANDs all input bitmaps.
 *
 *	   The first input bitmap is utilized to store the result of the
 *	   AND and returned to the caller. In addition, the output points
 *	   to a newly created OpStream node of type BMS_AND, where all
 *	   StreamNodes of input bitmaps are added as input streams.
 * ----------------------------------------------------------------
 */
Node *
MultiExecBitmapAnd(BitmapAndState *node)
{
	PlanState **bitmapplans;
	int			nplans;
	int			i;
	bool		empty = false;
	TIDBitmap  *hbm = NULL;

	/* must provide our own instrumentation support */
	if (node->ps.instrument)
		InstrStartNode(node->ps.instrument);

	/*
	 * get information from the node
	 */
	bitmapplans = node->bitmapplans;
	nplans = node->nplans;

	/*
	 * Scan all the subplans and AND their result bitmaps
	 */
	for (i = 0; i < nplans; i++)
	{
		PlanState  *subnode = bitmapplans[i];
		Node		*subresult = NULL;

		subresult = MultiExecProcNode(subnode);

		if (!subresult || !(IsA(subresult, TIDBitmap) || IsA(subresult, StreamBitmap)))
			elog(ERROR, "unrecognized result from subplan");

		/*
		 * If this is a hash bitmap, intersect it now with other hash bitmaps.
		 * If we encounter some streamed bitmaps we'll add this hash bitmap
		 * as a stream to it.
		 */
		if (IsA(subresult, TIDBitmap))
		{
			/* first subplan that generates a hash bitmap */
			if (hbm == NULL)
				hbm = (TIDBitmap *) subresult;
			else
			{
				tbm_intersect(hbm, (TIDBitmap *)subresult);
				tbm_generic_free(subresult);
			}

			/*
			 * If at any stage we have a completely empty bitmap, we can fall out
			 * without evaluating the remaining subplans, since ANDing them can no
			 * longer change the result.  (Note: the fact that indxpath.c orders
			 * the subplans by selectivity should make this case more likely to
			 * occur.)
			 */
			if (tbm_is_empty(hbm))
			{
				empty = true;
				break;
			}
		}
		else
		{
			/*
			 * result is a streamed bitmap, add it as a node to the existing
			 * stream -- or initialize one otherwise.
			 */
			if (node->bitmap)
			{
				if (node->bitmap != subresult)
   				{
	   				StreamBitmap *s = (StreamBitmap *)subresult;
	   				stream_move_node((StreamBitmap *)node->bitmap, s, BMS_AND);
					tbm_generic_free(subresult);
			   	}
			}
   			else
	   			node->bitmap = subresult;
		}
	}

	/* must provide our own instrumentation support */
	if (node->ps.instrument)
        InstrStopNode(node->ps.instrument, empty ? 0 : 1);

	/* check to see if we have any hash bitmaps */
	if (hbm != NULL)
	{
		if (node->bitmap && IsA(node->bitmap, StreamBitmap))
			stream_add_node((StreamBitmap *)node->bitmap,
						tbm_create_stream_node(hbm), BMS_AND);
		else
			node->bitmap = (Node *) hbm;
	}

	return (Node *) node->bitmap;
}

/* ----------------------------------------------------------------
 *		ExecEndBitmapAnd
 *
 *		Shuts down the subscans of the BitmapAnd node.
 *
 *		Returns nothing of interest.
 * ----------------------------------------------------------------
 */
void
ExecEndBitmapAnd(BitmapAndState *node)
{
	PlanState **bitmapplans;
	int			nplans;
	int			i;

	/*
	 * get information from the node
	 */
	bitmapplans = node->bitmapplans;
	nplans = node->nplans;

	/*
	 * shut down each of the subscans (that we've initialized)
	 */
	for (i = 0; i < nplans; i++)
	{
		if (bitmapplans[i])
			ExecEndNode(bitmapplans[i]);
	}
}

void
ExecReScanBitmapAnd(BitmapAndState *node)
{
	/*
	 * For optimizer a rescan call on BitmapIndexScan could free up the bitmap. So,
	 * we voluntarily set our bitmap to NULL to ensure that we don't have an out
	 * of scope pointer
	 */
	node->bitmap = NULL;

	int			i;

	for (i = 0; i < node->nplans; i++)
	{
		PlanState  *subnode = node->bitmapplans[i];

		/*
		 * ExecReScan doesn't know about my subplans, so I have to do
		 * changed-parameter signaling myself.
		 */
		if (node->ps.chgParam != NULL)
			UpdateChangedParamSet(subnode, node->ps.chgParam);

		/*
		 * If chgParam of subnode is not null then plan will be re-scanned by
		 * first ExecProcNode.
		 */
		if (subnode->chgParam == NULL)
			ExecReScan(subnode);
	}
}
