Skip to content
Snippets Groups Projects
Select Git revision
  • benchmark-tools
  • postgres-lambda
  • master default
  • REL9_4_25
  • REL9_5_20
  • REL9_6_16
  • REL_10_11
  • REL_11_6
  • REL_12_1
  • REL_12_0
  • REL_12_RC1
  • REL_12_BETA4
  • REL9_4_24
  • REL9_5_19
  • REL9_6_15
  • REL_10_10
  • REL_11_5
  • REL_12_BETA3
  • REL9_4_23
  • REL9_5_18
  • REL9_6_14
  • REL_10_9
  • REL_11_4
23 results

nodeSetOp.c

Blame
  • nodeSetOp.c 8.86 KiB
    /*-------------------------------------------------------------------------
     *
     * nodeSetOp.c
     *	  Routines to handle INTERSECT and EXCEPT selection
     *
     * The input of a SetOp node consists of tuples from two relations,
     * which have been combined into one dataset and sorted on all the nonjunk
     * attributes.	In addition there is a junk attribute that shows which
     * relation each tuple came from.  The SetOp node scans each group of
     * identical tuples to determine how many came from each input relation.
     * Then it is a simple matter to emit the output demanded by the SQL spec
     * for INTERSECT, INTERSECT ALL, EXCEPT, or EXCEPT ALL.
     *
     * This node type is not used for UNION or UNION ALL, since those can be
     * implemented more cheaply (there's no need for the junk attribute to
     * identify the source relation).
     *
     *
     * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
     * Portions Copyright (c) 1994, Regents of the University of California
     *
     *
     * IDENTIFICATION
     *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeSetOp.c,v 1.5 2001/10/25 05:49:29 momjian Exp $
     *
     *-------------------------------------------------------------------------
     */
    /*
     * INTERFACE ROUTINES
     *		ExecSetOp		- filter input to generate INTERSECT/EXCEPT results
     *		ExecInitSetOp	- initialize node and subnodes..
     *		ExecEndSetOp	- shutdown node and subnodes
     */
    
    #include "postgres.h"
    
    #include "access/heapam.h"
    #include "executor/executor.h"
    #include "executor/nodeGroup.h"
    #include "executor/nodeSetOp.h"
    
    /* ----------------------------------------------------------------
     *		ExecSetOp
     * ----------------------------------------------------------------
     */
    TupleTableSlot *				/* return: a tuple or NULL */
    ExecSetOp(SetOp *node)
    {
    	SetOpState *setopstate;
    	TupleTableSlot *resultTupleSlot;
    	Plan	   *outerPlan;
    	TupleDesc	tupDesc;
    
    	/*
    	 * get information from the node
    	 */
    	setopstate = node->setopstate;
    	outerPlan = outerPlan((Plan *) node);
    	resultTupleSlot = setopstate->cstate.cs_ResultTupleSlot;
    	tupDesc = ExecGetResultType(&setopstate->cstate);
    
    	/*
    	 * If the previously-returned tuple needs to be returned more than
    	 * once, keep returning it.
    	 */
    	if (setopstate->numOutput > 0)
    	{
    		setopstate->numOutput--;
    		return resultTupleSlot;
    	}
    
    	/* Flag that we have no current tuple */
    	ExecClearTuple(resultTupleSlot);
    
    	/*
    	 * Absorb groups of duplicate tuples, counting them, and saving the
    	 * first of each group as a possible return value. At the end of each
    	 * group, decide whether to return anything.
    	 *
    	 * We assume that the tuples arrive in sorted order so we can detect
    	 * duplicates easily.
    	 */
    	for (;;)
    	{
    		TupleTableSlot *inputTupleSlot;
    		bool		endOfGroup;
    
    		/*
    		 * fetch a tuple from the outer subplan, unless we already did.
    		 */
    		if (setopstate->cstate.cs_OuterTupleSlot == NULL &&
    			!setopstate->subplan_done)
    		{
    			setopstate->cstate.cs_OuterTupleSlot =
    				ExecProcNode(outerPlan, (Plan *) node);
    			if (TupIsNull(setopstate->cstate.cs_OuterTupleSlot))
    				setopstate->subplan_done = true;
    		}
    		inputTupleSlot = setopstate->cstate.cs_OuterTupleSlot;
    
    		if (TupIsNull(resultTupleSlot))
    		{
    			/*
    			 * First of group: save a copy in result slot, and reset
    			 * duplicate-counters for new group.
    			 */
    			if (setopstate->subplan_done)
    				return NULL;	/* no more tuples */
    			ExecStoreTuple(heap_copytuple(inputTupleSlot->val),
    						   resultTupleSlot,
    						   InvalidBuffer,
    						   true);		/* free copied tuple at
    										 * ExecClearTuple */
    			setopstate->numLeft = 0;
    			setopstate->numRight = 0;
    			endOfGroup = false;
    		}
    		else if (setopstate->subplan_done)
    		{
    			/*
    			 * Reached end of input, so finish processing final group
    			 */
    			endOfGroup = true;
    		}
    		else
    		{
    			/*
    			 * Else test if the new tuple and the previously saved tuple
    			 * match.
    			 */
    			if (execTuplesMatch(inputTupleSlot->val,
    								resultTupleSlot->val,
    								tupDesc,
    								node->numCols, node->dupColIdx,
    								setopstate->eqfunctions,
    								setopstate->tempContext))
    				endOfGroup = false;
    			else
    				endOfGroup = true;
    		}
    
    		if (endOfGroup)
    		{
    			/*
    			 * We've reached the end of the group containing resultTuple.
    			 * Decide how many copies (if any) to emit.  This logic is
    			 * straight from the SQL92 specification.
    			 */
    			switch (node->cmd)
    			{
    				case SETOPCMD_INTERSECT:
    					if (setopstate->numLeft > 0 && setopstate->numRight > 0)
    						setopstate->numOutput = 1;
    					else
    						setopstate->numOutput = 0;
    					break;
    				case SETOPCMD_INTERSECT_ALL:
    					setopstate->numOutput =
    						(setopstate->numLeft < setopstate->numRight) ?
    						setopstate->numLeft : setopstate->numRight;
    					break;
    				case SETOPCMD_EXCEPT:
    					if (setopstate->numLeft > 0 && setopstate->numRight == 0)
    						setopstate->numOutput = 1;
    					else
    						setopstate->numOutput = 0;
    					break;
    				case SETOPCMD_EXCEPT_ALL:
    					setopstate->numOutput =
    						(setopstate->numLeft < setopstate->numRight) ?
    						0 : (setopstate->numLeft - setopstate->numRight);
    					break;
    				default:
    					elog(ERROR, "ExecSetOp: bogus command code %d",
    						 (int) node->cmd);
    					break;
    			}
    			/* Fall out of for-loop if we have tuples to emit */
    			if (setopstate->numOutput > 0)
    				break;
    			/* Else flag that we have no current tuple, and loop around */
    			ExecClearTuple(resultTupleSlot);
    		}
    		else
    		{
    			/*
    			 * Current tuple is member of same group as resultTuple. Count
    			 * it in the appropriate counter.
    			 */
    			int			flag;
    			bool		isNull;
    
    			flag = DatumGetInt32(heap_getattr(inputTupleSlot->val,
    											  node->flagColIdx,
    											  tupDesc,
    											  &isNull));
    			Assert(!isNull);
    			if (flag)
    				setopstate->numRight++;
    			else
    				setopstate->numLeft++;
    			/* Set flag to fetch a new input tuple, and loop around */
    			setopstate->cstate.cs_OuterTupleSlot = NULL;
    		}
    	}
    
    	/*
    	 * If we fall out of loop, then we need to emit at least one copy of
    	 * resultTuple.
    	 */
    	Assert(setopstate->numOutput > 0);
    	setopstate->numOutput--;
    	return resultTupleSlot;
    }
    
    /* ----------------------------------------------------------------
     *		ExecInitSetOp
     *
     *		This initializes the setop node state structures and
     *		the node's subplan.
     * ----------------------------------------------------------------
     */
    bool							/* return: initialization status */
    ExecInitSetOp(SetOp *node, EState *estate, Plan *parent)
    {
    	SetOpState *setopstate;
    	Plan	   *outerPlan;
    
    	/*
    	 * assign execution state to node
    	 */
    	node->plan.state = estate;
    
    	/*
    	 * create new SetOpState for node
    	 */
    	setopstate = makeNode(SetOpState);
    	node->setopstate = setopstate;
    	setopstate->cstate.cs_OuterTupleSlot = NULL;
    	setopstate->subplan_done = false;
    	setopstate->numOutput = 0;
    
    	/*
    	 * Miscellaneous initialization
    	 *
    	 * SetOp nodes have no ExprContext initialization because they never call
    	 * ExecQual or ExecProject.  But they do need a per-tuple memory
    	 * context anyway for calling execTuplesMatch.
    	 */
    	setopstate->tempContext =
    		AllocSetContextCreate(CurrentMemoryContext,
    							  "SetOp",
    							  ALLOCSET_DEFAULT_MINSIZE,
    							  ALLOCSET_DEFAULT_INITSIZE,
    							  ALLOCSET_DEFAULT_MAXSIZE);
    
    #define SETOP_NSLOTS 1
    
    	/*
    	 * Tuple table initialization
    	 */
    	ExecInitResultTupleSlot(estate, &setopstate->cstate);
    
    	/*
    	 * then initialize outer plan
    	 */
    	outerPlan = outerPlan((Plan *) node);
    	ExecInitNode(outerPlan, estate, (Plan *) node);
    
    	/*
    	 * setop nodes do no projections, so initialize projection info for
    	 * this node appropriately
    	 */
    	ExecAssignResultTypeFromOuterPlan((Plan *) node, &setopstate->cstate);
    	setopstate->cstate.cs_ProjInfo = NULL;
    
    	/*
    	 * Precompute fmgr lookup data for inner loop
    	 */
    	setopstate->eqfunctions =
    		execTuplesMatchPrepare(ExecGetResultType(&setopstate->cstate),
    							   node->numCols,
    							   node->dupColIdx);
    
    	return TRUE;
    }
    
    int
    ExecCountSlotsSetOp(SetOp *node)
    {
    	return ExecCountSlotsNode(outerPlan(node)) +
    		ExecCountSlotsNode(innerPlan(node)) +
    		SETOP_NSLOTS;
    }
    
    /* ----------------------------------------------------------------
     *		ExecEndSetOp
     *
     *		This shuts down the subplan and frees resources allocated
     *		to this node.
     * ----------------------------------------------------------------
     */
    void
    ExecEndSetOp(SetOp *node)
    {
    	SetOpState *setopstate = node->setopstate;
    
    	ExecEndNode(outerPlan((Plan *) node), (Plan *) node);
    
    	MemoryContextDelete(setopstate->tempContext);
    
    	/* clean up tuple table */
    	ExecClearTuple(setopstate->cstate.cs_ResultTupleSlot);
    	setopstate->cstate.cs_OuterTupleSlot = NULL;
    }
    
    
    void
    ExecReScanSetOp(SetOp *node, ExprContext *exprCtxt, Plan *parent)
    {
    	SetOpState *setopstate = node->setopstate;
    
    	ExecClearTuple(setopstate->cstate.cs_ResultTupleSlot);
    	setopstate->cstate.cs_OuterTupleSlot = NULL;
    	setopstate->subplan_done = false;
    	setopstate->numOutput = 0;
    
    	/*
    	 * if chgParam of subnode is not null then plan will be re-scanned by
    	 * first ExecProcNode.
    	 */
    	if (((Plan *) node)->lefttree->chgParam == NULL)
    		ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node);
    }