aboutsummaryrefslogtreecommitdiff
path: root/src/backend/executor/nodeTee.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/executor/nodeTee.c')
-rw-r--r--src/backend/executor/nodeTee.c503
1 files changed, 503 insertions, 0 deletions
diff --git a/src/backend/executor/nodeTee.c b/src/backend/executor/nodeTee.c
new file mode 100644
index 00000000000..5be700b9ae7
--- /dev/null
+++ b/src/backend/executor/nodeTee.c
@@ -0,0 +1,503 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeTee.c--
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * DESCRIPTION
+ * This code provides support for a tee node, which allows multiple
+ * parent in a megaplan.
+ *
+ * INTERFACE ROUTINES
+ * ExecTee
+ * ExecInitTee
+ * ExecEndTee
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/executor/Attic/nodeTee.c,v 1.1.1.1 1996/07/09 06:21:27 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include <sys/file.h>
+#include "utils/palloc.h"
+#include "utils/relcache.h"
+#include "storage/bufmgr.h" /* for IncrBufferRefCount */
+#include "optimizer/internal.h"
+#include "executor/executor.h"
+#include "executor/nodeTee.h"
+#include "catalog/catalog.h"
+#include "tcop/pquery.h"
+
+/* ------------------------------------------------------------------
+ * ExecInitTee
+ *
+ * Create tee state
+ *
+ * ------------------------------------------------------------------
+ */
+bool
+ExecInitTee(Tee* node, EState *currentEstate, Plan * parent)
+{
+ TeeState *teeState;
+ Plan *outerPlan;
+ int len;
+ Relation bufferRel;
+ TupleDesc tupType;
+ EState *estate;
+
+ /* it is possible that the Tee has already been initialized
+ since it can be reached by multiple parents.
+ If it is already initialized, simply return and do
+ not initialize the children nodes again
+ */
+ if (node->plan.state)
+ return TRUE;
+
+ /* ----------------
+ * assign the node's execution state
+ * ----------------
+ */
+ /* make a new executor state, because we have a different
+ es_range_table */
+
+/* node->plan.state = estate;*/
+
+ estate = CreateExecutorState();
+ estate->es_direction = currentEstate->es_direction;
+ estate->es_BaseId = currentEstate->es_BaseId;
+ estate->es_BaseId = currentEstate->es_BaseId;
+ estate->es_tupleTable = currentEstate->es_tupleTable;
+ estate->es_refcount = currentEstate->es_refcount;
+ estate->es_junkFilter = currentEstate->es_junkFilter;
+
+ /* use the range table for Tee subplan since the range tables
+ for the two parents may be different */
+ if (node->rtentries)
+ estate->es_range_table = node->rtentries;
+ else
+ estate->es_range_table = currentEstate->es_range_table;
+
+ node->plan.state = estate;
+
+
+ /* ----------------
+ * create teeState structure
+ * ----------------
+ */
+ teeState = makeNode(TeeState);
+ teeState->tee_leftPlace = 0;
+ teeState->tee_rightPlace = 0;
+ teeState->tee_lastPlace = 0;
+ teeState->tee_bufferRel = NULL;
+ teeState->tee_leftScanDesc = NULL;
+ teeState->tee_rightScanDesc = NULL;
+
+
+ node->teestate = teeState;
+
+ /* ----------------
+ * Miscellanious initialization
+ *
+ * + assign node's base_id
+ * + assign debugging hooks and
+ * + create expression context for node
+ * ----------------
+ */
+ ExecAssignNodeBaseInfo(estate, &(teeState->cstate), parent);
+ ExecAssignExprContext(estate, &(teeState->cstate));
+
+#define TEE_NSLOTS 2
+ /* ----------------
+ * initialize tuple slots
+ * ----------------
+ */
+ ExecInitResultTupleSlot(estate, &(teeState->cstate));
+
+ /* initialize child nodes */
+ outerPlan = outerPlan((Plan*) node);
+ ExecInitNode(outerPlan, estate, (Plan*) node);
+
+ /* ----------------
+ * the tuple type info is from the outer plan of this node
+ * the result type is also the same as the outerplan
+ */
+ ExecAssignResultTypeFromOuterPlan((Plan*) node, &(teeState->cstate));
+ ExecAssignProjectionInfo((Plan*)node, &teeState->cstate);
+
+ /* ---------------------------------------
+ initialize temporary relation to buffer tuples
+ */
+ tupType = ExecGetResultType(&(teeState->cstate));
+ len = ExecTargetListLength(((Plan*)node)->targetlist);
+
+/* bufferRel = ExecCreatR(len, tupType, _TEMP_RELATION_ID_); */
+
+ /* create a catalogued relation even though this is a temporary relation */
+ /* cleanup of catalogued relations is easier to do */
+
+ if (node->teeTableName[0] != '\0') {
+ Relation r;
+
+ teeState->tee_bufferRelname = pstrdup(node->teeTableName);
+
+ /* we are given an tee table name,
+ if a relation by that name exists, then we open it,
+ else we create it and then open it */
+ r = RelationNameGetRelation(teeState->tee_bufferRelname);
+
+ if (RelationIsValid(r))
+ bufferRel = heap_openr(teeState->tee_bufferRelname);
+ else
+ bufferRel = heap_open(heap_create(teeState->tee_bufferRelname,
+/*FIX */ NULL,
+ 'n',
+ DEFAULT_SMGR,
+ tupType));
+ }
+ else {
+ sprintf(teeState->tee_bufferRelname,
+ "ttemp_%d", /* 'ttemp' for 'tee' temporary*/
+ newoid());
+/* bufferRel = ExecCreatR(len, tupType, _TEMP_RELATION_ID); */
+ bufferRel = heap_open(heap_create(teeState->tee_bufferRelname,
+ NULL, /*XXX */
+ 'n',
+ DEFAULT_SMGR,
+ tupType));
+ }
+
+ teeState->tee_bufferRel = bufferRel;
+
+ /*initialize a memory context for allocating thing like scan descriptors */
+ /* we do this so that on cleanup of the tee, we can free things.
+ if we didn't have our own memory context, we would be in the memory
+ context of the portal that we happen to be using at the moment */
+
+ teeState->tee_mcxt = (MemoryContext)CreateGlobalMemory(teeState->tee_bufferRelname);
+
+ /* don't initialize the scan descriptors here
+ because it's not good to initialize scan descriptors on empty
+ rels. Wait until the scan descriptors are needed
+ before initializing them. */
+
+ teeState->tee_leftScanDesc = NULL;
+ teeState->tee_rightScanDesc = NULL;
+
+ return TRUE;
+}
+
+int
+ExecCountSlotsTee(Tee *node)
+{
+ /* Tee nodes can't have innerPlans */
+ return ExecCountSlotsNode(outerPlan(node)) + TEE_NSLOTS;
+}
+
+/* ----------------------------------------------------------------
+ initTeeScanDescs
+ initializes the left and right scandescs on the temporary
+ relation of a Tee node
+
+ must open two separate scan descriptors,
+ because the left and right scans may be at different points
+* ----------------------------------------------------------------
+*/
+void
+initTeeScanDescs(Tee* node)
+{
+ TeeState *teeState;
+ Relation bufferRel;
+ ScanDirection dir;
+ MemoryContext orig;
+
+ teeState = node->teestate;
+ if (teeState->tee_leftScanDesc && teeState->tee_rightScanDesc)
+ return;
+
+ orig = CurrentMemoryContext;
+ MemoryContextSwitchTo(teeState->tee_mcxt);
+
+ bufferRel = teeState->tee_bufferRel;
+ dir = ((Plan*)node)->state->es_direction; /* backwards not handled yet XXX */
+
+ if (teeState->tee_leftScanDesc == NULL)
+ {
+ teeState->tee_leftScanDesc = heap_beginscan(bufferRel,
+ ScanDirectionIsBackward(dir),
+ NowTimeQual, /* time qual */
+ 0, /* num scan keys */
+ NULL /* scan keys */
+ );
+ }
+ if (teeState->tee_rightScanDesc == NULL)
+ {
+ teeState->tee_rightScanDesc = heap_beginscan(bufferRel,
+ ScanDirectionIsBackward(dir),
+ NowTimeQual, /* time qual */
+ 0, /* num scan keys */
+ NULL /* scan keys */
+ );
+ }
+
+ MemoryContextSwitchTo(orig);
+}
+
+/* ----------------------------------------------------------------
+ * ExecTee(node)
+ *
+ *
+ * A Tee serves to connect a subplan to multiple parents.
+ * the subplan is always the outplan of the Tee node.
+ *
+ * The Tee gets requests from either leftParent or rightParent,
+ * fetches the result tuple from the child, and then
+ * stored the result into a temporary relation (serving as a queue).
+ * leftPlace and rightPlace keep track of where the left and rightParents
+ * are.
+ * If a parent requests a tuple and that parent is not at the end
+ * of the temporary relation, then the request is satisfied from
+ * the queue instead of by executing the child plan
+ *
+ * ----------------------------------------------------------------
+ */
+
+TupleTableSlot*
+ExecTee(Tee *node, Plan *parent)
+{
+ EState *estate;
+ TeeState *teeState;
+ int leftPlace, rightPlace, lastPlace;
+ int branch;
+ TupleTableSlot* result;
+ TupleTableSlot* slot;
+ Plan *childNode;
+ ScanDirection dir;
+ HeapTuple heapTuple;
+ Relation bufferRel;
+ HeapScanDesc scanDesc;
+ Buffer buffer;
+
+ estate = ((Plan*)node)->state;
+ teeState = node->teestate;
+ leftPlace = teeState->tee_leftPlace;
+ rightPlace = teeState->tee_rightPlace;
+ lastPlace = teeState->tee_lastPlace;
+ bufferRel = teeState->tee_bufferRel;
+
+ childNode = outerPlan(node);
+
+ dir = estate->es_direction;
+
+ /* XXX doesn't handle backwards direction yet */
+
+ if (parent == node->leftParent) {
+ branch = leftPlace;
+ }
+ else
+ if ( (parent == node->rightParent) || (parent == (Plan*) node))
+ /* the tee node could be the root node of the plan,
+ in which case, we treat it like a right-parent pull*/
+ {
+ branch = rightPlace;
+ }
+ else
+ {
+ elog(WARN,"A Tee node can only be executed from its left or right parent\n");
+ return NULL;
+ }
+
+ if (branch == lastPlace)
+ { /* we're at the end of the queue already,
+ - get a new tuple from the child plan,
+ - store it in the queue,
+ - increment lastPlace,
+ - increment leftPlace or rightPlace as appropriate,
+ - and return result
+ */
+ slot = ExecProcNode(childNode, (Plan*)node);
+ if (!TupIsNull(slot))
+ {
+ heapTuple = slot->val;
+
+ /* insert into temporary relation */
+ heap_insert(bufferRel, heapTuple);
+
+ /* once there is data in the temporary relation,
+ ensure that the left and right scandescs are initialized */
+ initTeeScanDescs(node);
+
+ scanDesc = (parent == node->leftParent) ?
+ teeState->tee_leftScanDesc : teeState->tee_rightScanDesc;
+
+ {
+ /* move the scandesc forward so we don't re-read this tuple later */
+ HeapTuple throwAway;
+ /* Buffer buffer;*/
+ throwAway = heap_getnext(scanDesc,
+ ScanDirectionIsBackward(dir),
+ /* &buffer */
+ (Buffer*)NULL);
+ }
+
+ /* set the shouldFree field of the child's slot so that
+ when the child's slot is free'd, this tuple isn't free'd also */
+ /* does this mean this tuple has to be garbage collected later??*/
+ slot->ttc_shouldFree = false;
+
+ teeState->tee_lastPlace = lastPlace + 1;
+ }
+ result = slot;
+ }
+ else
+ {/* the desired data already exists in the temporary relation */
+ scanDesc = (parent == node->leftParent) ?
+ teeState->tee_leftScanDesc : teeState->tee_rightScanDesc;
+
+ heapTuple = heap_getnext(scanDesc,
+ ScanDirectionIsBackward(dir),
+ &buffer);
+
+ /* Increase the pin count on the buffer page, because the
+ tuple stored in the slot also points to it (as well as
+ the scan descriptor). If we don't, ExecStoreTuple will
+ decrease the pin count on the next iteration. */
+
+ if (buffer != InvalidBuffer)
+ IncrBufferRefCount(buffer);
+
+ slot = teeState->cstate.cs_ResultTupleSlot;
+ slot->ttc_tupleDescriptor = RelationGetTupleDescriptor(bufferRel);
+
+ result = ExecStoreTuple(heapTuple,/* tuple to store */
+ slot, /* slot to store in */
+ buffer,/* this tuple's buffer */
+ false); /* don't free stuff from heap_getnext */
+
+ }
+
+ if (parent == node->leftParent)
+ {
+ teeState->tee_leftPlace = leftPlace+1;
+ }
+ else
+ {
+ teeState->tee_rightPlace = rightPlace+1;
+ }
+
+ return result;
+}
+
+/* ----------------------------------------------------------------
+ * ExecTeeReScan(node)
+ *
+ * Rescans the relation.
+ * ----------------------------------------------------------------
+ */
+void
+ExecTeeReScan(Tee *node, ExprContext *exprCtxt, Plan *parent)
+{
+
+ EState *estate;
+ TeeState *teeState;
+ ScanDirection dir;
+
+ estate = ((Plan*)node)->state;
+ teeState = node->teestate;
+
+ dir = estate->es_direction;
+
+ /* XXX doesn't handle backwards direction yet */
+
+ if (parent == node->leftParent) {
+ if (teeState->tee_leftScanDesc)
+ {
+ heap_rescan(teeState->tee_leftScanDesc,
+ ScanDirectionIsBackward(dir),
+ NULL);
+ teeState->tee_leftPlace = 0;
+ }
+ }
+ else
+ {
+ if (teeState->tee_rightScanDesc)
+ {
+ heap_rescan(teeState->tee_leftScanDesc,
+ ScanDirectionIsBackward(dir),
+ NULL);
+ teeState->tee_rightPlace = 0;
+ }
+ }
+}
+
+
+/* ---------------------------------------------------------------------
+ * ExecEndTee
+ *
+ * End the Tee node, and free up any storage
+ * since a Tee node can be downstream of multiple parent nodes,
+ * only free when both parents are done
+ * --------------------------------------------------------------------
+ */
+
+void
+ExecEndTee(Tee* node, Plan* parent)
+{
+ EState *estate;
+ TeeState *teeState;
+ int leftPlace, rightPlace, lastPlace;
+ Relation bufferRel;
+ MemoryContext orig;
+
+ estate = ((Plan*)node)->state;
+ teeState = node->teestate;
+ leftPlace = teeState->tee_leftPlace;
+ rightPlace = teeState->tee_rightPlace;
+ lastPlace = teeState->tee_lastPlace;
+
+ if (!node->leftParent || parent == node->leftParent)
+ leftPlace = -1;
+
+ if (!node->rightParent || parent == node->rightParent)
+ rightPlace = -1;
+
+ if (parent == (Plan*)node)
+ rightPlace = leftPlace = -1;
+
+ teeState->tee_leftPlace = leftPlace;
+ teeState->tee_rightPlace = rightPlace;
+ if ( (leftPlace == -1) && (rightPlace == -1) )
+ {
+ /* remove the temporary relations */
+ /* and close the scan descriptors */
+
+ bufferRel = teeState->tee_bufferRel;
+ if (bufferRel) {
+ heap_destroyr(bufferRel);
+ teeState->tee_bufferRel = NULL;
+ if (teeState->tee_mcxt) {
+ orig = CurrentMemoryContext;
+ MemoryContextSwitchTo(teeState->tee_mcxt);
+ }
+
+ if (teeState->tee_leftScanDesc)
+ {
+ heap_endscan(teeState->tee_leftScanDesc);
+ teeState->tee_leftScanDesc = NULL;
+ }
+ if (teeState->tee_rightScanDesc)
+ {
+ heap_endscan(teeState->tee_rightScanDesc);
+ teeState->tee_rightScanDesc = NULL;
+ }
+
+ if (teeState->tee_mcxt) {
+ MemoryContextSwitchTo(orig);
+ teeState->tee_mcxt = NULL;
+ }
+ }
+ }
+
+}
+