aboutsummaryrefslogtreecommitdiff
path: root/src/backend/executor
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/executor')
-rw-r--r--src/backend/executor/Makefile.inc29
-rw-r--r--src/backend/executor/execAmi.c439
-rw-r--r--src/backend/executor/execFlatten.c236
-rw-r--r--src/backend/executor/execFlatten.h26
-rw-r--r--src/backend/executor/execJunk.c389
-rw-r--r--src/backend/executor/execMain.c1023
-rw-r--r--src/backend/executor/execProcnode.c477
-rw-r--r--src/backend/executor/execQual.c1504
-rw-r--r--src/backend/executor/execScan.c136
-rw-r--r--src/backend/executor/execTuples.c1013
-rw-r--r--src/backend/executor/execUtils.c1092
-rw-r--r--src/backend/executor/execdebug.h377
-rw-r--r--src/backend/executor/execdefs.h54
-rw-r--r--src/backend/executor/execdesc.h38
-rw-r--r--src/backend/executor/executor.h229
-rw-r--r--src/backend/executor/functions.c388
-rw-r--r--src/backend/executor/functions.h22
-rw-r--r--src/backend/executor/hashjoin.h82
-rw-r--r--src/backend/executor/nodeAgg.c558
-rw-r--r--src/backend/executor/nodeAgg.h21
-rw-r--r--src/backend/executor/nodeAppend.c483
-rw-r--r--src/backend/executor/nodeAppend.h22
-rw-r--r--src/backend/executor/nodeGroup.c407
-rw-r--r--src/backend/executor/nodeGroup.h21
-rw-r--r--src/backend/executor/nodeHash.c828
-rw-r--r--src/backend/executor/nodeHash.h35
-rw-r--r--src/backend/executor/nodeHashjoin.c792
-rw-r--r--src/backend/executor/nodeHashjoin.h33
-rw-r--r--src/backend/executor/nodeIndexscan.c902
-rw-r--r--src/backend/executor/nodeIndexscan.h32
-rw-r--r--src/backend/executor/nodeMaterial.c392
-rw-r--r--src/backend/executor/nodeMaterial.h23
-rw-r--r--src/backend/executor/nodeMergejoin.c1194
-rw-r--r--src/backend/executor/nodeMergejoin.h40
-rw-r--r--src/backend/executor/nodeNestloop.c370
-rw-r--r--src/backend/executor/nodeNestloop.h21
-rw-r--r--src/backend/executor/nodeResult.c288
-rw-r--r--src/backend/executor/nodeResult.h21
-rw-r--r--src/backend/executor/nodeSeqscan.c449
-rw-r--r--src/backend/executor/nodeSeqscan.h27
-rw-r--r--src/backend/executor/nodeSort.c523
-rw-r--r--src/backend/executor/nodeSort.h23
-rw-r--r--src/backend/executor/nodeTee.c503
-rw-r--r--src/backend/executor/nodeTee.h22
-rw-r--r--src/backend/executor/nodeUnique.c316
-rw-r--r--src/backend/executor/nodeUnique.h21
-rw-r--r--src/backend/executor/tuptable.h72
47 files changed, 15993 insertions, 0 deletions
diff --git a/src/backend/executor/Makefile.inc b/src/backend/executor/Makefile.inc
new file mode 100644
index 00000000000..211e725cec4
--- /dev/null
+++ b/src/backend/executor/Makefile.inc
@@ -0,0 +1,29 @@
+#-------------------------------------------------------------------------
+#
+# Makefile.inc--
+# Makefile for the executor module
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+#
+# IDENTIFICATION
+# $Header: /cvsroot/pgsql/src/backend/executor/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:21:24 scrappy Exp $
+#
+#-------------------------------------------------------------------------
+
+VPATH:= $(VPATH):$(CURDIR)/executor
+
+SRCS_EXECUTOR= execAmi.c execFlatten.c execJunk.c execMain.c \
+ execProcnode.c execQual.c execScan.c execTuples.c \
+ execUtils.c functions.c nodeAppend.c nodeAgg.c nodeHash.c \
+ nodeHashjoin.c nodeIndexscan.c nodeMaterial.c nodeMergejoin.c \
+ nodeNestloop.c nodeResult.c nodeSeqscan.c nodeSort.c \
+ nodeUnique.c nodeTee.c nodeGroup.c
+
+HEADERS+= execFlatten.h execdebug.h execdefs.h execdesc.h \
+ executor.h functions.h hashjoin.h nodeAgg.h nodeAppend.h \
+ nodeHash.h nodeHashjoin.h nodeIndexscan.h nodeMaterial.h \
+ nodeMergejoin.h nodeNestloop.h nodeResult.h \
+ nodeSeqscan.h nodeSort.h nodeUnique.h tuptable.h nodeTee.h \
+ nodeGroup.h
+
diff --git a/src/backend/executor/execAmi.c b/src/backend/executor/execAmi.c
new file mode 100644
index 00000000000..08d3e70d8b9
--- /dev/null
+++ b/src/backend/executor/execAmi.c
@@ -0,0 +1,439 @@
+/*-------------------------------------------------------------------------
+ *
+ * execAmi.c--
+ * miscellanious executor access method routines
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/executor/execAmi.c,v 1.1.1.1 1996/07/09 06:21:24 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+ * INTERFACE ROUTINES
+ *
+ * ExecOpenScanR \ / amopen
+ * ExecBeginScan \ / ambeginscan
+ * ExecCloseR \ / amclose
+ * ExecInsert \ executor interface / aminsert
+ * ExecReScanNode / to access methods \ amrescan
+ * ExecReScanR / \ amrescan
+ * ExecMarkPos / \ ammarkpos
+ * ExecRestrPos / \ amrestpos
+ *
+ * ExecCreatR function to create temporary relations
+ *
+ */
+#include <stdio.h> /* for sprintf() */
+#include "executor/executor.h"
+#include "storage/smgr.h"
+#include "executor/nodeSeqscan.h"
+#include "executor/nodeIndexscan.h"
+#include "executor/nodeSort.h"
+#include "executor/nodeTee.h"
+#include "optimizer/internal.h" /* for _TEMP_RELATION_ID_ */
+
+/* ----------------------------------------------------------------
+ * 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 an index into the
+ * range table.
+ * Returns the relation as(relDesc scanDesc)
+ * If this structure is changed, need to modify the access macros
+ * defined in execInt.h.
+ * ----------------------------------------------------------------
+ */
+void
+ExecOpenScanR(Oid relOid,
+ int nkeys,
+ ScanKey skeys,
+ bool isindex,
+ ScanDirection dir,
+ TimeQual timeRange,
+ 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
+ * ----------------
+ */
+ relation = ExecOpenR(relOid, isindex);
+ scanDesc = ExecBeginScan(relation,
+ nkeys,
+ skeys,
+ isindex,
+ dir,
+ timeRange);
+
+ if (returnRelation != NULL)
+ *returnRelation = relation;
+ if (scanDesc != NULL)
+ *returnScanDesc = scanDesc;
+}
+
+/* ----------------------------------------------------------------
+ * ExecOpenR
+ *
+ * returns a relation descriptor given an object id.
+ * ----------------------------------------------------------------
+ */
+Relation
+ExecOpenR(Oid relationOid, bool isindex)
+{
+ Relation relation;
+ relation = (Relation) NULL;
+
+ /* ----------------
+ * open the relation with the correct call depending
+ * on whether this is a heap relation or an index relation.
+ * ----------------
+ */
+ if (isindex) {
+ relation = index_open(relationOid);
+ } else
+ relation = heap_open(relationOid);
+
+ if (relation == NULL)
+ elog(DEBUG, "ExecOpenR: relation == NULL, heap_open failed.");
+
+ return relation;
+}
+
+/* ----------------------------------------------------------------
+ * 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
+ * ----------------------------------------------------------------
+ */
+Pointer
+ExecBeginScan(Relation relation,
+ int nkeys,
+ ScanKey skeys,
+ bool isindex,
+ ScanDirection dir,
+ TimeQual time_range)
+{
+ Pointer scanDesc;
+
+ scanDesc = NULL;
+
+ /* ----------------
+ * 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),
+ time_range,
+ 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 or sort
+ * node. Also closes index relations and scans for index scans.
+ *
+ * old comments
+ * closes the relation indicated in 'relID'
+ * ----------------------------------------------------------------
+ */
+void
+ExecCloseR(Plan *node)
+{
+ CommonScanState *state;
+ Relation relation;
+ HeapScanDesc scanDesc;
+
+ /* ----------------
+ * shut down the heap scan and close the heap relation
+ * ----------------
+ */
+ switch (nodeTag(node)) {
+
+ case T_SeqScan:
+ state = ((SeqScan *)node)->scanstate;
+ break;
+
+ case T_IndexScan:
+ state = ((IndexScan *)node)->scan.scanstate;
+ break;
+
+ case T_Material:
+ state = &(((Material *)node)->matstate->csstate);
+ break;
+
+ case T_Sort:
+ state = &(((Sort *)node)->sortstate->csstate);
+ break;
+
+ case T_Agg:
+ state = &(((Agg *)node)->aggstate->csstate);
+ break;
+
+ default:
+ elog(DEBUG, "ExecCloseR: not a scan, material, or sort node!");
+ return;
+ }
+
+ relation = state->css_currentRelation;
+ scanDesc = state->css_currentScanDesc;
+
+ if (scanDesc != NULL)
+ heap_endscan(scanDesc);
+
+ if (relation != NULL)
+ heap_close(relation);
+
+ /* ----------------
+ * if this is an index scan then we have to take care
+ * of the index relations as well..
+ * ----------------
+ */
+ if (nodeTag(node) == T_IndexScan) {
+ IndexScan *iscan= (IndexScan *)node;
+ IndexScanState *indexstate;
+ int numIndices;
+ RelationPtr indexRelationDescs;
+ IndexScanDescPtr indexScanDescs;
+ int i;
+
+ indexstate = iscan->indxstate;
+ numIndices = indexstate->iss_NumIndices;
+ indexRelationDescs = indexstate->iss_RelationDescs;
+ indexScanDescs = indexstate->iss_ScanDescs;
+
+ for (i = 0; i<numIndices; i++) {
+ /* ----------------
+ * shut down each of the scans and
+ * close each of the index relations
+ * ----------------
+ */
+ if (indexScanDescs[i] != NULL)
+ index_endscan(indexScanDescs[i]);
+
+ if (indexRelationDescs[i] != NULL)
+ index_close(indexRelationDescs[i]);
+ }
+ }
+}
+
+/* ----------------------------------------------------------------
+ * 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)
+{
+ switch(nodeTag(node)) {
+ case T_SeqScan:
+ ExecSeqReScan((SeqScan *) node, exprCtxt, parent);
+ return;
+
+ case T_IndexScan:
+ ExecIndexReScan((IndexScan *) node, exprCtxt, parent);
+ return;
+
+ case T_Material:
+ /* the first call to ExecReScan should have no effect because
+ * everything is initialized properly already. the following
+ * calls will be handled by ExecSeqReScan() because the nodes
+ * below the Material node have already been materialized into
+ * a temp relation.
+ */
+ return;
+
+ case T_Tee:
+ ExecTeeReScan((Tee*) node, exprCtxt, parent);
+ break;
+
+ default:
+ elog(WARN, "ExecReScan: not a seqscan or indexscan node.");
+ return;
+ }
+}
+
+/* ----------------------------------------------------------------
+ * 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.
+ * ----------------------------------------------------------------
+ */
+void
+ExecMarkPos(Plan *node)
+{
+ switch(nodeTag(node)) {
+ case T_SeqScan:
+ ExecSeqMarkPos((SeqScan *) node);
+ break;
+
+ case T_IndexScan:
+ ExecIndexMarkPos((IndexScan *) node);
+ break;
+
+ case T_Sort:
+ ExecSortMarkPos((Sort *) node);
+ break;
+
+ default:
+ /* elog(DEBUG, "ExecMarkPos: unsupported node type"); */
+ break;
+ }
+ return;
+}
+
+/* ----------------------------------------------------------------
+ * ExecRestrPos
+ *
+ * restores the scan position previously saved with ExecMarkPos()
+ * ----------------------------------------------------------------
+ */
+void
+ExecRestrPos(Plan *node)
+{
+ switch(nodeTag(node)) {
+ case T_SeqScan:
+ ExecSeqRestrPos((SeqScan *) node);
+ return;
+
+ case T_IndexScan:
+ ExecIndexRestrPos((IndexScan *) node);
+ return;
+
+ case T_Sort:
+ ExecSortRestrPos((Sort *) node);
+ return;
+
+ default:
+ /* elog(DEBUG, "ExecRestrPos: node type not supported"); */
+ return;
+ }
+}
+
+/* ----------------------------------------------------------------
+ * ExecCreatR
+ *
+ * old comments
+ * Creates a relation.
+ *
+ * Parameters:
+ * attrType -- type information on the attributes.
+ * accessMtd -- access methods used to access the created relation.
+ * relation -- optional. Either an index to the range table or
+ * negative number indicating a temporary relation.
+ * A temporary relation is assume is this field is absent.
+ * ----------------------------------------------------------------
+ */
+
+Relation
+ExecCreatR(TupleDesc tupType,
+ Oid relationOid)
+{
+ Relation relDesc;
+
+ EU4_printf("ExecCreatR: %s numatts=%d type=%d oid=%d\n",
+ "entering: ", numberAttributes, tupType, relationOid);
+ CXT1_printf("ExecCreatR: context is %d\n", CurrentMemoryContext);
+
+ relDesc = NULL;
+
+ if (relationOid == _TEMP_RELATION_ID_ ) {
+ /* ----------------
+ * create a temporary relation
+ * (currently the planner always puts a _TEMP_RELATION_ID
+ * in the relation argument so we expect this to be the case although
+ * it's possible that someday we'll get the name from
+ * from the range table.. -cim 10/12/89)
+ * ----------------
+ */
+/*
+ sprintf(tempname, "temp_%d.%d", getpid(), tmpcnt++);
+ EU1_printf("ExecCreatR: attempting to create %s\n", tempname);
+*/
+ /* heap_creatr creates a name if the argument to heap_creatr is '\0 ' */
+ relDesc = heap_creatr("",
+ DEFAULT_SMGR,
+ tupType);
+ } else {
+ /* ----------------
+ * use a relation from the range table
+ * ----------------
+ */
+ elog(DEBUG, "ExecCreatR: %s",
+ "stuff using range table id's is not functional");
+ }
+
+ if (relDesc == NULL)
+ elog(DEBUG, "ExecCreatR: failed to create relation.");
+
+ EU1_printf("ExecCreatR: returning relDesc=%d\n", relDesc);
+
+ return relDesc;
+}
+
diff --git a/src/backend/executor/execFlatten.c b/src/backend/executor/execFlatten.c
new file mode 100644
index 00000000000..646c571506e
--- /dev/null
+++ b/src/backend/executor/execFlatten.c
@@ -0,0 +1,236 @@
+/*-------------------------------------------------------------------------
+ *
+ * execFlatten.c--
+ * This file handles the nodes associated with flattening sets in the
+ * target list of queries containing functions returning sets.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/executor/Attic/execFlatten.c,v 1.1.1.1 1996/07/09 06:21:24 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+
+/*
+ * ExecEvalIter() -
+ * Iterate through the all return tuples/base types from a function one
+ * at time (i.e. one per ExecEvalIter call). Not really needed for
+ * postquel functions, but for reasons of orthogonality, these nodes
+ * exist above pq functions as well as c functions.
+ *
+ * ExecEvalFjoin() -
+ * Given N Iter nodes return a vector of all combinations of results
+ * one at a time (i.e. one result vector per ExecEvalFjoin call). This
+ * node does the actual flattening work.
+ */
+#include "postgres.h"
+#include "nodes/primnodes.h"
+#include "nodes/relation.h"
+#include "nodes/execnodes.h"
+#include "executor/executor.h"
+#include "executor/execFlatten.h"
+
+Datum
+ExecEvalIter(Iter *iterNode,
+ ExprContext *econtext,
+ bool *resultIsNull,
+ bool *iterIsDone)
+{
+ Node *expression;
+
+ expression = iterNode->iterexpr;
+
+ /*
+ * Really Iter nodes are only needed for C functions, postquel function
+ * by their nature return 1 result at a time. For now we are only worrying
+ * about postquel functions, c functions will come later.
+ */
+ return ExecEvalExpr(expression, econtext, resultIsNull, iterIsDone);
+}
+
+void
+ExecEvalFjoin(TargetEntry *tlist,
+ ExprContext *econtext,
+ bool *isNullVect,
+ bool *fj_isDone)
+{
+
+#ifdef SETS_FIXED
+ bool isDone;
+ int curNode;
+ List *tlistP;
+
+ Fjoin *fjNode = tlist->fjoin;
+ DatumPtr resVect = fjNode->fj_results;
+ BoolPtr alwaysDone = fjNode->fj_alwaysDone;
+
+ if (fj_isDone) *fj_isDone = false;
+ /*
+ * For the next tuple produced by the plan, we need to re-initialize
+ * the Fjoin node.
+ */
+ if (!fjNode->fj_initialized)
+ {
+ /*
+ * Initialize all of the Outer nodes
+ */
+ curNode = 1;
+ foreach(tlistP, lnext(tlist))
+ {
+ TargetEntry *tle = lfirst(tlistP);
+
+ resVect[curNode] = ExecEvalIter((Iter*)tle->expr,
+ econtext,
+ &isNullVect[curNode],
+ &isDone);
+ if (isDone)
+ isNullVect[curNode] = alwaysDone[curNode] = true;
+ else
+ alwaysDone[curNode] = false;
+
+ curNode++;
+ }
+
+ /*
+ * Initialize the inner node
+ */
+ resVect[0] = ExecEvalIter((Iter*)fjNode->fj_innerNode->expr,
+ econtext,
+ &isNullVect[0],
+ &isDone);
+ if (isDone)
+ isNullVect[0] = alwaysDone[0] = true;
+ else
+ alwaysDone[0] = false;
+
+ /*
+ * Mark the Fjoin as initialized now.
+ */
+ fjNode->fj_initialized = TRUE;
+
+ /*
+ * If the inner node is always done, then we are done for now
+ */
+ if (isDone)
+ return;
+ }
+ else
+ {
+ /*
+ * If we're already initialized, all we need to do is get the
+ * next inner result and pair it up with the existing outer node
+ * result vector. Watch out for the degenerate case, where the
+ * inner node never returns results.
+ */
+
+ /*
+ * Fill in nulls for every function that is always done.
+ */
+ for (curNode=fjNode->fj_nNodes-1; curNode >= 0; curNode--)
+ isNullVect[curNode] = alwaysDone[curNode];
+
+ if (alwaysDone[0] == true)
+ {
+ *fj_isDone = FjoinBumpOuterNodes(tlist,
+ econtext,
+ resVect,
+ isNullVect);
+ return;
+ }
+ else
+ resVect[0] = ExecEvalIter((Iter*)fjNode->fj_innerNode->expr,
+ econtext,
+ &isNullVect[0],
+ &isDone);
+ }
+
+ /*
+ * if the inner node is done
+ */
+ if (isDone)
+ {
+ *fj_isDone = FjoinBumpOuterNodes(tlist,
+ econtext,
+ resVect,
+ isNullVect);
+ if (*fj_isDone)
+ return;
+
+ resVect[0] = ExecEvalIter((Iter*)fjNode->fj_innerNode->expr,
+ econtext,
+ &isNullVect[0],
+ &isDone);
+
+ }
+#endif
+ return;
+}
+
+bool
+FjoinBumpOuterNodes(TargetEntry *tlist,
+ ExprContext *econtext,
+ DatumPtr results,
+ char *nulls)
+{
+#ifdef SETS_FIXED
+ bool funcIsDone = true;
+ Fjoin *fjNode = tlist->fjoin;
+ char *alwaysDone = fjNode->fj_alwaysDone;
+ List *outerList = lnext(tlist);
+ List *trailers = lnext(tlist);
+ int trailNode = 1;
+ int curNode = 1;
+
+ /*
+ * Run through list of functions until we get to one that isn't yet
+ * done returning values. Watch out for funcs that are always done.
+ */
+ while ((funcIsDone == true) && (outerList != NIL))
+ {
+ TargetEntry *tle = lfirst(outerList);
+
+ if (alwaysDone[curNode] == true)
+ nulls[curNode] = 'n';
+ else
+ results[curNode] = ExecEvalIter((Iter)tle->expr,
+ econtext,
+ &nulls[curNode],
+ &funcIsDone);
+ curNode++;
+ outerList = lnext(outerList);
+ }
+
+ /*
+ * If every function is done, then we are done flattening.
+ * Mark the Fjoin node unitialized, it is time to get the
+ * next tuple from the plan and redo all of the flattening.
+ */
+ if (funcIsDone)
+ {
+ set_fj_initialized(fjNode, false);
+ return (true);
+ }
+
+ /*
+ * We found a function that wasn't done. Now re-run every function
+ * before it. As usual watch out for functions that are always done.
+ */
+ trailNode = 1;
+ while (trailNode != curNode-1)
+ {
+ TargetEntry *tle = lfirst(trailers);
+
+ if (alwaysDone[trailNode] != true)
+ results[trailNode] = ExecEvalIter((Iter)tle->expr,
+ econtext,
+ &nulls[trailNode],
+ &funcIsDone);
+ trailNode++;
+ trailers = lnext(trailers);
+ }
+ return false;
+#endif
+ return false;
+}
diff --git a/src/backend/executor/execFlatten.h b/src/backend/executor/execFlatten.h
new file mode 100644
index 00000000000..fe06823619f
--- /dev/null
+++ b/src/backend/executor/execFlatten.h
@@ -0,0 +1,26 @@
+/*-------------------------------------------------------------------------
+ *
+ * execFlatten.h--
+ * prototypes for execFlatten.c.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id: execFlatten.h,v 1.1.1.1 1996/07/09 06:21:24 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef EXECFLATTEN_H
+#define EXECFLATTEN_H
+
+extern Datum ExecEvalIter(Iter *iterNode, ExprContext *econtext, bool *resultIsNull, bool *iterIsDone);
+
+extern void ExecEvalFjoin(TargetEntry *tlist, ExprContext *econtext, bool *isNullVect, bool *fj_isDone);
+
+extern bool FjoinBumpOuterNodes(TargetEntry *tlist, ExprContext *econtext, DatumPtr results, char *nulls);
+
+
+#endif /* EXECFLATTEN_H */
+
+
+
diff --git a/src/backend/executor/execJunk.c b/src/backend/executor/execJunk.c
new file mode 100644
index 00000000000..7ee1543299a
--- /dev/null
+++ b/src/backend/executor/execJunk.c
@@ -0,0 +1,389 @@
+/*-------------------------------------------------------------------------
+ *
+ * junk.c--
+ * Junk attribute support stuff....
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/executor/execJunk.c,v 1.1.1.1 1996/07/09 06:21:24 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "utils/palloc.h"
+#include "executor/executor.h"
+#include "nodes/relation.h"
+#include "optimizer/tlist.h" /* for MakeTLE */
+
+/*-------------------------------------------------------------------------
+ * XXX this stuff should be rewritten to take advantage
+ * of ExecProject() and the ProjectionInfo node.
+ * -cim 6/3/91
+ *
+ * An attribute of a tuple living inside the executor, can be
+ * either a normal attribute or a "junk" attribute. "junk" attributes
+ * never make it out of the executor, i.e. they are never printed,
+ * returned or stored in disk. Their only purpose in life is to
+ * store some information useful only to the executor, mainly the values
+ * of some system attributes like "ctid" or rule locks.
+ *
+ * The general idea is the following: A target list consists of a list of
+ * Resdom nodes & expression pairs. Each Resdom node has an attribute
+ * called 'resjunk'. If the value of this attribute is 1 then the
+ * corresponding attribute is a "junk" attribute.
+ *
+ * When we initialize a plan we call 'ExecInitJunkFilter' to create
+ * and store the appropriate information in the 'es_junkFilter' attribute of
+ * EState.
+ *
+ * We then execute the plan ignoring the "resjunk" attributes.
+ *
+ * Finally, when at the top level we get back a tuple, we can call
+ * 'ExecGetJunkAttribute' to retrieve the value of the junk attributes we
+ * are interested in, and 'ExecRemoveJunk' to remove all the junk attributes
+ * from a tuple. This new "clean" tuple is then printed, replaced, deleted
+ * or inserted.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+/*-------------------------------------------------------------------------
+ * ExecInitJunkFilter
+ *
+ * Initialize the Junk filter.
+ *-------------------------------------------------------------------------
+ */
+JunkFilter *
+ExecInitJunkFilter(List *targetList)
+{
+ JunkFilter *junkfilter;
+ List *cleanTargetList;
+ int len, cleanLength;
+ TupleDesc tupType, cleanTupType;
+ List *t;
+ TargetEntry *tle;
+ Resdom *resdom, *cleanResdom;
+ int resjunk;
+ AttrNumber cleanResno;
+ AttrNumber *cleanMap;
+ Size size;
+ Node *expr;
+
+ /* ---------------------
+ * First find the "clean" target list, i.e. all the entries
+ * in the original target list which have a zero 'resjunk'
+ * NOTE: make copy of the Resdom nodes, because we have
+ * to change the 'resno's...
+ * ---------------------
+ */
+ cleanTargetList = NIL;
+ cleanResno = 1;
+
+ foreach (t, targetList) {
+ TargetEntry *rtarget = lfirst(t);
+ if (rtarget->resdom != NULL) {
+ resdom = rtarget->resdom;
+ expr = rtarget->expr;
+ resjunk = resdom->resjunk;
+ if (resjunk == 0) {
+ /*
+ * make a copy of the resdom node, changing its resno.
+ */
+ cleanResdom = (Resdom *) copyObject(resdom);
+ cleanResdom->resno = cleanResno;
+ cleanResno ++;
+ /*
+ * create a new target list entry
+ */
+ tle = makeNode(TargetEntry);
+ tle->resdom = cleanResdom;
+ tle->expr = expr;
+ cleanTargetList = lappend(cleanTargetList, tle);
+ }
+ }
+ else {
+#ifdef SETS_FIXED
+ List *fjListP;
+ Fjoin *cleanFjoin;
+ List *cleanFjList;
+ List *fjList = lfirst(t);
+ Fjoin *fjNode = (Fjoin *)tl_node(fjList);
+
+ cleanFjoin = (Fjoin)copyObject((Node) fjNode);
+ cleanFjList = lcons(cleanFjoin, NIL);
+
+ resdom = (Resdom) lfirst(get_fj_innerNode(fjNode));
+ expr = lsecond(get_fj_innerNode(fjNode));
+ cleanResdom = (Resdom) copyObject((Node) resdom);
+ set_resno(cleanResdom, cleanResno);
+ cleanResno++;
+ tle = (List) MakeTLE(cleanResdom, (Expr) expr);
+ set_fj_innerNode(cleanFjoin, tle);
+
+ foreach(fjListP, lnext(fjList)) {
+ TargetEntry *tle = lfirst(fjListP);
+
+ resdom = tle->resdom;
+ expr = tle->expr;
+ cleanResdom = (Resdom*) copyObject((Node) resdom);
+ cleanResno++;
+ cleanResdom->Resno = cleanResno;
+ /*
+ * create a new target list entry
+ */
+ tle = (List) MakeTLE(cleanResdom, (Expr) expr);
+ cleanFjList = lappend(cleanFjList, tle);
+ }
+ lappend(cleanTargetList, cleanFjList);
+#endif
+ }
+ }
+
+ /* ---------------------
+ * Now calculate the tuple types for the original and the clean tuple
+ *
+ * XXX ExecTypeFromTL should be used sparingly. Don't we already
+ * have the tupType corresponding to the targetlist we are passed?
+ * -cim 5/31/91
+ * ---------------------
+ */
+ tupType = (TupleDesc) ExecTypeFromTL(targetList);
+ cleanTupType = (TupleDesc) ExecTypeFromTL(cleanTargetList);
+
+ len = ExecTargetListLength(targetList);
+ cleanLength = ExecTargetListLength(cleanTargetList);
+
+ /* ---------------------
+ * Now calculate the "map" between the original tuples attributes
+ * and the "clean" tuple's attributes.
+ *
+ * The "map" is an array of "cleanLength" attribute numbers, i.e.
+ * one entry for every attribute of the "clean" tuple.
+ * The value of this entry is the attribute number of the corresponding
+ * attribute of the "original" tuple.
+ * ---------------------
+ */
+ if (cleanLength > 0) {
+ size = cleanLength * sizeof(AttrNumber);
+ cleanMap = (AttrNumber*) palloc(size);
+ cleanResno = 1;
+ foreach (t, targetList) {
+ TargetEntry *tle = lfirst(t);
+ if (tle->resdom != NULL) {
+ resdom = tle->resdom;
+ expr = tle->expr;
+ resjunk = resdom->resjunk;
+ if (resjunk == 0) {
+ cleanMap[cleanResno-1] = resdom->resno;
+ cleanResno ++;
+ }
+ } else {
+#ifdef SETS_FIXED
+ List fjListP;
+ List fjList = lfirst(t);
+ Fjoin fjNode = (Fjoin)lfirst(fjList);
+
+ /* what the hell is this????? */
+ resdom = (Resdom) lfirst(get_fj_innerNode(fjNode));
+#endif
+
+ cleanMap[cleanResno-1] = tle->resdom->resno;
+ cleanResno++;
+
+#ifdef SETS_FIXED
+ foreach(fjListP, lnext(fjList)) {
+ TargetEntry *tle = lfirst(fjListP);
+
+ resdom = tle->resdom;
+ cleanMap[cleanResno-1] = resdom->resno;
+ cleanResno++;
+ }
+#endif
+ }
+ }
+ } else {
+ cleanMap = NULL;
+ }
+
+ /* ---------------------
+ * Finally create and initialize the JunkFilter.
+ * ---------------------
+ */
+ junkfilter = makeNode(JunkFilter);
+
+ junkfilter->jf_targetList = targetList;
+ junkfilter->jf_length = len;
+ junkfilter->jf_tupType = tupType;
+ junkfilter->jf_cleanTargetList = cleanTargetList;
+ junkfilter->jf_cleanLength = cleanLength;
+ junkfilter->jf_cleanTupType = cleanTupType;
+ junkfilter->jf_cleanMap = cleanMap;
+
+ return(junkfilter);
+
+}
+
+/*-------------------------------------------------------------------------
+ * ExecGetJunkAttribute
+ *
+ * Given a tuple (slot), the junk filter and a junk attribute's name,
+ * extract & return the value of this attribute.
+ *
+ * It returns false iff no junk attribute with such name was found.
+ *
+ * NOTE: isNull might be NULL !
+ *-------------------------------------------------------------------------
+ */
+bool
+ExecGetJunkAttribute(JunkFilter *junkfilter,
+ TupleTableSlot *slot,
+ char *attrName,
+ Datum *value,
+ bool *isNull)
+{
+ List *targetList;
+ List *t;
+ Resdom *resdom;
+ AttrNumber resno;
+ char *resname;
+ int resjunk;
+ TupleDesc tupType;
+ HeapTuple tuple;
+
+ /* ---------------------
+ * first look in the junkfilter's target list for
+ * an attribute with the given name
+ * ---------------------
+ */
+ resno = InvalidAttrNumber;
+ targetList = junkfilter->jf_targetList;
+
+ foreach (t, targetList) {
+ TargetEntry *tle = lfirst(t);
+ resdom = tle->resdom;
+ resname = resdom->resname;
+ resjunk = resdom->resjunk;
+ if (resjunk != 0 && (strcmp(resname, attrName) == 0)) {
+ /* We found it ! */
+ resno = resdom->resno;
+ break;
+ }
+ }
+
+ if (resno == InvalidAttrNumber) {
+ /* Ooops! We couldn't find this attribute... */
+ return(false);
+ }
+
+ /* ---------------------
+ * Now extract the attribute value from the tuple.
+ * ---------------------
+ */
+ tuple = slot->val;
+ tupType = (TupleDesc) junkfilter->jf_tupType;
+
+ *value = (Datum)
+ heap_getattr(tuple, InvalidBuffer, resno, tupType, isNull);
+
+ return true;
+}
+
+/*-------------------------------------------------------------------------
+ * ExecRemoveJunk
+ *
+ * Construct and return a tuple with all the junk attributes removed.
+ *-------------------------------------------------------------------------
+ */
+HeapTuple
+ExecRemoveJunk(JunkFilter *junkfilter, TupleTableSlot *slot)
+{
+ HeapTuple tuple;
+ HeapTuple cleanTuple;
+ AttrNumber *cleanMap;
+ TupleDesc cleanTupType;
+ TupleDesc tupType;
+ int cleanLength;
+ bool isNull;
+ int i;
+ Size size;
+ Datum *values;
+ char *nulls;
+ Datum values_array[64];
+ char nulls_array[64];
+
+ /* ----------------
+ * get info from the slot and the junk filter
+ * ----------------
+ */
+ tuple = slot->val;
+
+ tupType = (TupleDesc) junkfilter->jf_tupType;
+ cleanTupType = (TupleDesc) junkfilter->jf_cleanTupType;
+ cleanLength = junkfilter->jf_cleanLength;
+ cleanMap = junkfilter->jf_cleanMap;
+
+ /* ---------------------
+ * Handle the trivial case first.
+ * ---------------------
+ */
+ if (cleanLength == 0)
+ return (HeapTuple) NULL;
+
+ /* ---------------------
+ * Create the arrays that will hold the attribute values
+ * and the null information for the new "clean" tuple.
+ *
+ * Note: we use memory on the stack to optimize things when
+ * we are dealing with a small number of tuples.
+ * for large tuples we just use palloc.
+ * ---------------------
+ */
+ if (cleanLength > 64) {
+ size = cleanLength * sizeof(Datum);
+ values = (Datum *) palloc(size);
+
+ size = cleanLength * sizeof(char);
+ nulls = (char *) palloc(size);
+ } else {
+ values = values_array;
+ nulls = nulls_array;
+ }
+
+ /* ---------------------
+ * Exctract one by one all the values of the "clean" tuple.
+ * ---------------------
+ */
+ for (i=0; i<cleanLength; i++) {
+ Datum d = (Datum)
+ heap_getattr(tuple, InvalidBuffer, cleanMap[i], tupType, &isNull);
+
+ values[i] = d;
+
+ if (isNull)
+ nulls[i] = 'n';
+ else
+ nulls[i] = ' ';
+ }
+
+ /* ---------------------
+ * Now form the new tuple.
+ * ---------------------
+ */
+ cleanTuple = heap_formtuple(cleanTupType,
+ values,
+ nulls);
+
+ /* ---------------------
+ * We are done. Free any space allocated for 'values' and 'nulls'
+ * and return the new tuple.
+ * ---------------------
+ */
+ if (cleanLength > 64) {
+ pfree(values);
+ pfree(nulls);
+ }
+
+ return(cleanTuple);
+}
+
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
new file mode 100644
index 00000000000..07aac34accb
--- /dev/null
+++ b/src/backend/executor/execMain.c
@@ -0,0 +1,1023 @@
+/*-------------------------------------------------------------------------
+ *
+ * execMain.c--
+ * top level executor interface routines
+ *
+ * INTERFACE ROUTINES
+ * ExecutorStart()
+ * ExecutorRun()
+ * ExecutorEnd()
+ *
+ * The old ExecutorMain() has been replaced by ExecutorStart(),
+ * ExecutorRun() and ExecutorEnd()
+ *
+ * These three procedures are the external interfaces to the executor.
+ * In each case, the query descriptor and the execution state is required
+ * as arguments
+ *
+ * ExecutorStart() must be called at the beginning of any execution of any
+ * query plan and ExecutorEnd() should always be called at the end of
+ * execution of a plan.
+ *
+ * ExecutorRun accepts 'feature' and 'count' arguments that specify whether
+ * the plan is to be executed forwards, backwards, and for how many tuples.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.1.1.1 1996/07/09 06:21:25 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "executor/executor.h"
+#include "utils/builtins.h"
+#include "utils/palloc.h"
+#include "utils/acl.h"
+#include "parser/parsetree.h" /* rt_fetch() */
+#include "storage/bufmgr.h"
+#include "commands/async.h"
+/* #include "access/localam.h" */
+#include "optimizer/var.h"
+
+
+/* decls for local routines only used within this module */
+static void ExecCheckPerms(CmdType operation, int resultRelation, List *rangeTable,
+ Query *parseTree);
+static TupleDesc InitPlan(CmdType operation, Query *parseTree,
+ Plan *plan, EState *estate);
+static void EndPlan(Plan *plan, EState *estate);
+static TupleTableSlot *ExecutePlan(EState *estate, Plan *plan,
+ Query *parseTree, CmdType operation,
+ int numberTuples, int direction,
+ void (*printfunc)());
+static void ExecRetrieve(TupleTableSlot *slot, void (*printfunc)(),
+ Relation intoRelationDesc);
+static void ExecAppend(TupleTableSlot *slot,ItemPointer tupleid,
+ EState *estate);
+static void ExecDelete(TupleTableSlot *slot, ItemPointer tupleid,
+ EState *estate);
+static void ExecReplace(TupleTableSlot *slot, ItemPointer tupleid,
+ EState *estate, Query *parseTree);
+
+/* end of local decls */
+
+/* ----------------------------------------------------------------
+ * ExecutorStart
+ *
+ * This routine must be called at the beginning of any execution of any
+ * query plan
+ *
+ * returns (AttrInfo*) which describes the attributes of the tuples to
+ * be returned by the query.
+ *
+ * ----------------------------------------------------------------
+ */
+TupleDesc
+ExecutorStart(QueryDesc *queryDesc, EState *estate)
+{
+ TupleDesc result;
+
+ /* sanity checks */
+ Assert(queryDesc!=NULL);
+
+ result = InitPlan(queryDesc->operation,
+ queryDesc->parsetree,
+ queryDesc->plantree,
+ estate);
+
+ /* reset buffer refcount. the current refcounts
+ * are saved and will be restored when ExecutorEnd is called
+ *
+ * this makes sure that when ExecutorRun's are
+ * called recursively as for postquel functions,
+ * the buffers pinned by one ExecutorRun will not be
+ * unpinned by another ExecutorRun.
+ */
+ BufferRefCountReset(estate->es_refcount);
+
+ return result;
+}
+
+/* ----------------------------------------------------------------
+ * ExecutorRun
+ *
+ * This is the main routine of the executor module. It accepts
+ * the query descriptor from the traffic cop and executes the
+ * query plan.
+ *
+ * ExecutorStart must have been called already.
+ *
+ * the different features supported are:
+ * EXEC_RUN: retrieve all tuples in the forward direction
+ * EXEC_FOR: retrieve 'count' number of tuples in the forward dir
+ * EXEC_BACK: retrieve 'count' number of tuples in the backward dir
+ * EXEC_RETONE: return one tuple but don't 'retrieve' it
+ * used in postquel function processing
+ *
+ *
+ * ----------------------------------------------------------------
+ */
+TupleTableSlot*
+ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature, int count)
+{
+ CmdType operation;
+ Query *parseTree;
+ Plan *plan;
+ TupleTableSlot *result;
+ CommandDest dest;
+ void (*destination)();
+
+ /* ----------------
+ * sanity checks
+ * ----------------
+ */
+ Assert(queryDesc!=NULL);
+
+ /* ----------------
+ * extract information from the query descriptor
+ * and the query feature.
+ * ----------------
+ */
+ operation = queryDesc->operation;
+ parseTree = queryDesc->parsetree;
+ plan = queryDesc->plantree;
+ dest = queryDesc->dest;
+ destination = (void (*)()) DestToFunction(dest);
+
+ switch(feature) {
+
+ case EXEC_RUN:
+ result = ExecutePlan(estate,
+ plan,
+ parseTree,
+ operation,
+ ALL_TUPLES,
+ EXEC_FRWD,
+ destination);
+ break;
+ case EXEC_FOR:
+ result = ExecutePlan(estate,
+ plan,
+ parseTree,
+ operation,
+ count,
+ EXEC_FRWD,
+ destination);
+ break;
+
+ /* ----------------
+ * retrieve next n "backward" tuples
+ * ----------------
+ */
+ case EXEC_BACK:
+ result = ExecutePlan(estate,
+ plan,
+ parseTree,
+ operation,
+ count,
+ EXEC_BKWD,
+ destination);
+ break;
+
+ /* ----------------
+ * return one tuple but don't "retrieve" it.
+ * (this is used by the rule manager..) -cim 9/14/89
+ * ----------------
+ */
+ case EXEC_RETONE:
+ result = ExecutePlan(estate,
+ plan,
+ parseTree,
+ operation,
+ ONE_TUPLE,
+ EXEC_FRWD,
+ destination);
+ break;
+ default:
+ elog(DEBUG, "ExecutorRun: Unknown feature %d", feature);
+ break;
+ }
+
+ return result;
+}
+
+/* ----------------------------------------------------------------
+ * ExecutorEnd
+ *
+ * This routine must be called at the end of any execution of any
+ * query plan
+ *
+ * returns (AttrInfo*) which describes the attributes of the tuples to
+ * be returned by the query.
+ *
+ * ----------------------------------------------------------------
+ */
+void
+ExecutorEnd(QueryDesc *queryDesc, EState *estate)
+{
+ /* sanity checks */
+ Assert(queryDesc!=NULL);
+
+ EndPlan(queryDesc->plantree, estate);
+
+ /* restore saved refcounts. */
+ BufferRefCountRestore(estate->es_refcount);
+}
+
+/* ===============================================================
+ * ===============================================================
+ static routines follow
+ * ===============================================================
+ * ===============================================================
+ */
+
+static void
+ExecCheckPerms(CmdType operation,
+ int resultRelation,
+ List *rangeTable,
+ Query *parseTree)
+{
+ int i = 1;
+ Oid relid;
+ HeapTuple htp;
+ List *lp;
+ List *qvars, *tvars;
+ int32 ok = 1;
+ char *opstr;
+ NameData rname;
+ char *userName;
+
+#define CHECK(MODE) pg_aclcheck(rname.data, userName, MODE)
+
+ userName = GetPgUserName();
+
+ foreach (lp, rangeTable) {
+ RangeTblEntry *rte = lfirst(lp);
+
+ relid = rte->relid;
+ htp = SearchSysCacheTuple(RELOID,
+ ObjectIdGetDatum(relid),
+ 0,0,0);
+ if (!HeapTupleIsValid(htp))
+ elog(WARN, "ExecCheckPerms: bogus RT relid: %d",
+ relid);
+ strncpy(rname.data,
+ ((Form_pg_class) GETSTRUCT(htp))->relname.data,
+ NAMEDATALEN);
+ if (i == resultRelation) { /* this is the result relation */
+ qvars = pull_varnos(parseTree->qual);
+ tvars = pull_varnos((Node*)parseTree->targetList);
+ if (intMember(resultRelation, qvars) ||
+ intMember(resultRelation, tvars)) {
+ /* result relation is scanned */
+ ok = CHECK(ACL_RD);
+ opstr = "read";
+ if (!ok)
+ break;
+ }
+ switch (operation) {
+ case CMD_INSERT:
+ ok = CHECK(ACL_AP) ||
+ CHECK(ACL_WR);
+ opstr = "append";
+ break;
+ case CMD_NOTIFY: /* what does this mean?? -- jw, 1/6/94 */
+ case CMD_DELETE:
+ case CMD_UPDATE:
+ ok = CHECK(ACL_WR);
+ opstr = "write";
+ break;
+ default:
+ elog(WARN, "ExecCheckPerms: bogus operation %d",
+ operation);
+ }
+ } else {
+ /* XXX NOTIFY?? */
+ ok = CHECK(ACL_RD);
+ opstr = "read";
+ }
+ if (!ok)
+ break;
+ ++i;
+ }
+ if (!ok) {
+/*
+ elog(WARN, "%s on \"%-.*s\": permission denied", opstr,
+ NAMEDATALEN, rname.data);
+*/
+ elog(WARN, "%s %s", rname.data, ACL_NO_PRIV_WARNING);
+ }
+}
+
+
+/* ----------------------------------------------------------------
+ * InitPlan
+ *
+ * Initializes the query plan: open files, allocate storage
+ * and start up the rule manager
+ * ----------------------------------------------------------------
+ */
+static TupleDesc
+InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate)
+{
+ List *rangeTable;
+ int resultRelation;
+ Relation intoRelationDesc;
+
+ TupleDesc tupType;
+ List *targetList;
+ int len;
+
+ /* ----------------
+ * get information from query descriptor
+ * ----------------
+ */
+ rangeTable = parseTree->rtable;
+ resultRelation = parseTree->resultRelation;
+
+ /* ----------------
+ * initialize the node's execution state
+ * ----------------
+ */
+ estate->es_range_table = rangeTable;
+
+ /* ----------------
+ * initialize the BaseId counter so node base_id's
+ * are assigned correctly. Someday baseid's will have to
+ * be stored someplace other than estate because they
+ * should be unique per query planned.
+ * ----------------
+ */
+ estate->es_BaseId = 1;
+
+ /* ----------------
+ * initialize result relation stuff
+ * ----------------
+ */
+
+ if (resultRelation != 0 && operation != CMD_SELECT) {
+ /* ----------------
+ * if we have a result relation, open it and
+ * initialize the result relation info stuff.
+ * ----------------
+ */
+ RelationInfo *resultRelationInfo;
+ Index resultRelationIndex;
+ RangeTblEntry *rtentry;
+ Oid resultRelationOid;
+ Relation resultRelationDesc;
+
+ resultRelationIndex = resultRelation;
+ rtentry = rt_fetch(resultRelationIndex, rangeTable);
+ resultRelationOid = rtentry->relid;
+ resultRelationDesc = heap_open(resultRelationOid);
+
+ /* Write-lock the result relation right away: if the relation
+ is used in a subsequent scan, we won't have to elevate the
+ read-lock set by heap_beginscan to a write-lock (needed by
+ heap_insert, heap_delete and heap_replace).
+ This will hopefully prevent some deadlocks. - 01/24/94 */
+ RelationSetLockForWrite(resultRelationDesc);
+
+ resultRelationInfo = makeNode(RelationInfo);
+ resultRelationInfo->ri_RangeTableIndex = resultRelationIndex;
+ resultRelationInfo->ri_RelationDesc = resultRelationDesc;
+ resultRelationInfo->ri_NumIndices = 0;
+ resultRelationInfo->ri_IndexRelationDescs = NULL;
+ resultRelationInfo->ri_IndexRelationInfo = NULL;
+
+ /* ----------------
+ * open indices on result relation and save descriptors
+ * in the result relation information..
+ * ----------------
+ */
+ ExecOpenIndices(resultRelationOid, resultRelationInfo);
+
+ estate->es_result_relation_info = resultRelationInfo;
+ } else {
+ /* ----------------
+ * if no result relation, then set state appropriately
+ * ----------------
+ */
+ estate->es_result_relation_info = NULL;
+ }
+
+#ifndef NO_SECURITY
+ ExecCheckPerms(operation, resultRelation, rangeTable, parseTree);
+#endif
+
+ /* ----------------
+ * initialize the executor "tuple" table.
+ * ----------------
+ */
+ {
+ int nSlots = ExecCountSlotsNode(plan);
+ TupleTable tupleTable = ExecCreateTupleTable(nSlots+10); /* why add ten? - jolly */
+
+ estate->es_tupleTable = tupleTable;
+ }
+
+ /* ----------------
+ * initialize the private state information for
+ * all the nodes in the query tree. This opens
+ * files, allocates storage and leaves us ready
+ * to start processing tuples..
+ * ----------------
+ */
+ ExecInitNode(plan, estate, NULL);
+
+ /* ----------------
+ * get the tuple descriptor describing the type
+ * of tuples to return.. (this is especially important
+ * if we are creating a relation with "retrieve into")
+ * ----------------
+ */
+ tupType = ExecGetTupType(plan); /* tuple descriptor */
+ targetList = plan->targetlist;
+ len = ExecTargetListLength(targetList); /* number of attributes */
+
+ /* ----------------
+ * now that we have the target list, initialize the junk filter
+ * if this is a REPLACE or a DELETE query.
+ * We also init the junk filter if this is an append query
+ * (there might be some rule lock info there...)
+ * NOTE: in the future we might want to initialize the junk
+ * filter for all queries.
+ * ----------------
+ */
+ if (operation == CMD_UPDATE || operation == CMD_DELETE ||
+ operation == CMD_INSERT) {
+
+ JunkFilter *j = (JunkFilter*) ExecInitJunkFilter(targetList);
+ estate->es_junkFilter = j;
+ } else
+ estate->es_junkFilter = NULL;
+
+ /* ----------------
+ * initialize the "into" relation
+ * ----------------
+ */
+ intoRelationDesc = (Relation) NULL;
+
+ if (operation == CMD_SELECT) {
+ char *intoName;
+ char archiveMode;
+ Oid intoRelationId;
+
+ if (!parseTree->isPortal) {
+ /*
+ * a select into table
+ */
+ if (parseTree->into != NULL) {
+ /* ----------------
+ * create the "into" relation
+ *
+ * note: there is currently no way for the user to
+ * specify the desired archive mode of the
+ * "into" relation...
+ * ----------------
+ */
+ intoName = parseTree->into;
+ archiveMode = 'n';
+
+ intoRelationId = heap_create(intoName,
+ intoName, /* not used */
+ archiveMode,
+ DEFAULT_SMGR,
+ tupType);
+
+ /* ----------------
+ * XXX rather than having to call setheapoverride(true)
+ * and then back to false, we should change the
+ * arguments to heap_open() instead..
+ * ----------------
+ */
+ setheapoverride(true);
+
+ intoRelationDesc = heap_open(intoRelationId);
+
+ setheapoverride(false);
+ }
+ }
+ }
+
+ estate->es_into_relation_descriptor = intoRelationDesc;
+
+ /* ----------------
+ * return the type information..
+ * ----------------
+ */
+/*
+ attinfo = (AttrInfo *)palloc(sizeof(AttrInfo));
+ attinfo->numAttr = len;
+ attinfo->attrs = tupType->attrs;
+*/
+
+ return tupType;
+}
+
+/* ----------------------------------------------------------------
+ * EndPlan
+ *
+ * Cleans up the query plan -- closes files and free up storages
+ * ----------------------------------------------------------------
+ */
+static void
+EndPlan(Plan *plan, EState *estate)
+{
+ RelationInfo *resultRelationInfo;
+ Relation intoRelationDesc;
+
+ /* ----------------
+ * get information from state
+ * ----------------
+ */
+ resultRelationInfo = estate->es_result_relation_info;
+ intoRelationDesc = estate->es_into_relation_descriptor;
+
+ /* ----------------
+ * shut down the query
+ * ----------------
+ */
+ ExecEndNode(plan, plan);
+
+ /* ----------------
+ * destroy the executor "tuple" table.
+ * ----------------
+ */
+ {
+ TupleTable tupleTable = (TupleTable) estate->es_tupleTable;
+ ExecDestroyTupleTable(tupleTable,true); /* was missing last arg */
+ estate->es_tupleTable = NULL;
+ }
+
+ /* ----------------
+ * close the result relations if necessary
+ * ----------------
+ */
+ if (resultRelationInfo != NULL) {
+ Relation resultRelationDesc;
+
+ resultRelationDesc = resultRelationInfo->ri_RelationDesc;
+ heap_close(resultRelationDesc);
+
+ /* ----------------
+ * close indices on the result relation
+ * ----------------
+ */
+ ExecCloseIndices(resultRelationInfo);
+ }
+
+ /* ----------------
+ * close the "into" relation if necessary
+ * ----------------
+ */
+ if (intoRelationDesc != NULL) {
+ heap_close(intoRelationDesc);
+ }
+}
+
+/* ----------------------------------------------------------------
+ * ExecutePlan
+ *
+ * processes the query plan to retrieve 'tupleCount' tuples in the
+ * direction specified.
+ * Retrieves all tuples if tupleCount is 0
+ *
+ * result is either a slot containing a tuple in the case
+ * of a RETRIEVE or NULL otherwise.
+ *
+ * ----------------------------------------------------------------
+ */
+
+/* the ctid attribute is a 'junk' attribute that is removed before the
+ user can see it*/
+
+static TupleTableSlot *
+ExecutePlan(EState *estate,
+ Plan *plan,
+ Query *parseTree,
+ CmdType operation,
+ int numberTuples,
+ int direction,
+ void (*printfunc)())
+{
+ Relation intoRelationDesc;
+ JunkFilter *junkfilter;
+
+ TupleTableSlot *slot;
+ ItemPointer tupleid = NULL;
+ ItemPointerData tuple_ctid;
+ int current_tuple_count;
+ TupleTableSlot *result;
+
+ /* ----------------
+ * get information
+ * ----------------
+ */
+ intoRelationDesc = estate->es_into_relation_descriptor;
+
+ /* ----------------
+ * initialize local variables
+ * ----------------
+ */
+ slot = NULL;
+ current_tuple_count = 0;
+ result = NULL;
+
+ /* ----------------
+ * Set the direction.
+ * ----------------
+ */
+ estate->es_direction = direction;
+
+ /* ----------------
+ * Loop until we've processed the proper number
+ * of tuples from the plan..
+ * ----------------
+ */
+
+ for(;;) {
+ if (operation != CMD_NOTIFY) {
+ /* ----------------
+ * Execute the plan and obtain a tuple
+ * ----------------
+ */
+ /* at the top level, the parent of a plan (2nd arg) is itself */
+ slot = ExecProcNode(plan,plan);
+
+ /* ----------------
+ * if the tuple is null, then we assume
+ * there is nothing more to process so
+ * we just return null...
+ * ----------------
+ */
+ if (TupIsNull(slot)) {
+ result = NULL;
+ break;
+ }
+ }
+
+ /* ----------------
+ * if we have a junk filter, then project a new
+ * tuple with the junk removed.
+ *
+ * Store this new "clean" tuple in the place of the
+ * original tuple.
+ *
+ * Also, extract all the junk ifnormation we need.
+ * ----------------
+ */
+ if ((junkfilter = estate->es_junkFilter) != (JunkFilter*)NULL) {
+ Datum datum;
+/* NameData attrName; */
+ HeapTuple newTuple;
+ bool isNull;
+
+ /* ---------------
+ * extract the 'ctid' junk attribute.
+ * ---------------
+ */
+ if (operation == CMD_UPDATE || operation == CMD_DELETE) {
+ if (! ExecGetJunkAttribute(junkfilter,
+ slot,
+ "ctid",
+ &datum,
+ &isNull))
+ elog(WARN,"ExecutePlan: NO (junk) `ctid' was found!");
+
+ if (isNull)
+ elog(WARN,"ExecutePlan: (junk) `ctid' is NULL!");
+
+ tupleid = (ItemPointer) DatumGetPointer(datum);
+ tuple_ctid = *tupleid; /* make sure we don't free the ctid!! */
+ tupleid = &tuple_ctid;
+ }
+
+ /* ---------------
+ * Finally create a new "clean" tuple with all junk attributes
+ * removed
+ * ---------------
+ */
+ newTuple = ExecRemoveJunk(junkfilter, slot);
+
+ slot = ExecStoreTuple(newTuple, /* tuple to store */
+ slot, /* destination slot */
+ InvalidBuffer,/* this tuple has no buffer */
+ true); /* tuple should be pfreed */
+ } /* if (junkfilter... */
+
+ /* ----------------
+ * now that we have a tuple, do the appropriate thing
+ * with it.. either return it to the user, add
+ * it to a relation someplace, delete it from a
+ * relation, or modify some of it's attributes.
+ * ----------------
+ */
+
+ switch(operation) {
+ case CMD_SELECT:
+ ExecRetrieve(slot, /* slot containing tuple */
+ printfunc, /* print function */
+ intoRelationDesc); /* "into" relation */
+ result = slot;
+ break;
+
+ case CMD_INSERT:
+ ExecAppend(slot, tupleid, estate);
+ result = NULL;
+ break;
+
+ case CMD_DELETE:
+ ExecDelete(slot, tupleid, estate);
+ result = NULL;
+ break;
+
+ case CMD_UPDATE:
+ ExecReplace(slot, tupleid, estate, parseTree);
+ result = NULL;
+ break;
+
+ /* Total hack. I'm ignoring any accessor functions for
+ Relation, RelationTupleForm, NameData.
+ Assuming that NameData.data has offset 0.
+ */
+ case CMD_NOTIFY: {
+ RelationInfo *rInfo = estate->es_result_relation_info;
+ Relation rDesc = rInfo->ri_RelationDesc;
+ Async_Notify(rDesc->rd_rel->relname.data);
+ result = NULL;
+ current_tuple_count = 0;
+ numberTuples = 1;
+ elog(DEBUG, "ExecNotify %s",&rDesc->rd_rel->relname);
+ }
+ break;
+
+ default:
+ elog(DEBUG, "ExecutePlan: unknown operation in queryDesc");
+ result = NULL;
+ break;
+ }
+ /* ----------------
+ * check our tuple count.. if we've returned the
+ * proper number then return, else loop again and
+ * process more tuples..
+ * ----------------
+ */
+ current_tuple_count += 1;
+ if (numberTuples == current_tuple_count)
+ break;
+ }
+
+ /* ----------------
+ * here, result is either a slot containing a tuple in the case
+ * of a RETRIEVE or NULL otherwise.
+ * ----------------
+ */
+ return result;
+}
+
+/* ----------------------------------------------------------------
+ * ExecRetrieve
+ *
+ * RETRIEVEs are easy.. we just pass the tuple to the appropriate
+ * print function. The only complexity is when we do a
+ * "retrieve into", in which case we insert the tuple into
+ * the appropriate relation (note: this is a newly created relation
+ * so we don't need to worry about indices or locks.)
+ * ----------------------------------------------------------------
+ */
+static void
+ExecRetrieve(TupleTableSlot *slot,
+ void (*printfunc)(),
+ Relation intoRelationDesc)
+{
+ HeapTuple tuple;
+ TupleDesc attrtype;
+
+ /* ----------------
+ * get the heap tuple out of the tuple table slot
+ * ----------------
+ */
+ tuple = slot->val;
+ attrtype = slot->ttc_tupleDescriptor;
+
+ /* ----------------
+ * insert the tuple into the "into relation"
+ * ----------------
+ */
+ if (intoRelationDesc != NULL) {
+ heap_insert (intoRelationDesc, tuple);
+ IncrAppended();
+ }
+
+ /* ----------------
+ * send the tuple to the front end (or the screen)
+ * ----------------
+ */
+ (*printfunc)(tuple, attrtype);
+ IncrRetrieved();
+}
+
+/* ----------------------------------------------------------------
+ * ExecAppend
+ *
+ * APPENDs are trickier.. we have to insert the tuple into
+ * the base relation and insert appropriate tuples into the
+ * index relations.
+ * ----------------------------------------------------------------
+ */
+
+static void
+ExecAppend(TupleTableSlot *slot,
+ ItemPointer tupleid,
+ EState *estate)
+{
+ HeapTuple tuple;
+ RelationInfo *resultRelationInfo;
+ Relation resultRelationDesc;
+ int numIndices;
+ Oid newId;
+
+ /* ----------------
+ * get the heap tuple out of the tuple table slot
+ * ----------------
+ */
+ tuple = slot->val;
+
+ /* ----------------
+ * get information on the result relation
+ * ----------------
+ */
+ resultRelationInfo = estate->es_result_relation_info;
+ resultRelationDesc = resultRelationInfo->ri_RelationDesc;
+
+ /* ----------------
+ * have to add code to preform unique checking here.
+ * cim -12/1/89
+ * ----------------
+ */
+
+ /* ----------------
+ * insert the tuple
+ * ----------------
+ */
+ newId = heap_insert(resultRelationDesc, /* relation desc */
+ tuple); /* heap tuple */
+ IncrAppended();
+ UpdateAppendOid(newId);
+
+ /* ----------------
+ * process indices
+ *
+ * Note: heap_insert adds a new tuple to a relation. As a side
+ * effect, the tupleid of the new tuple is placed in the new
+ * tuple's t_ctid field.
+ * ----------------
+ */
+ numIndices = resultRelationInfo->ri_NumIndices;
+ if (numIndices > 0) {
+ ExecInsertIndexTuples(slot, &(tuple->t_ctid), estate);
+ }
+}
+
+/* ----------------------------------------------------------------
+ * ExecDelete
+ *
+ * DELETE is like append, we delete the tuple and its
+ * index tuples.
+ * ----------------------------------------------------------------
+ */
+static void
+ExecDelete(TupleTableSlot *slot,
+ ItemPointer tupleid,
+ EState *estate)
+{
+ RelationInfo *resultRelationInfo;
+ Relation resultRelationDesc;
+
+ /* ----------------
+ * get the result relation information
+ * ----------------
+ */
+ resultRelationInfo = estate->es_result_relation_info;
+ resultRelationDesc = resultRelationInfo->ri_RelationDesc;
+
+ /* ----------------
+ * delete the tuple
+ * ----------------
+ */
+ (void) heap_delete(resultRelationDesc, /* relation desc */
+ tupleid); /* item pointer to tuple */
+
+ IncrDeleted();
+
+ /* ----------------
+ * Note: Normally one would think that we have to
+ * delete index tuples associated with the
+ * heap tuple now..
+ *
+ * ... but in POSTGRES, we have no need to do this
+ * because the vacuum daemon automatically
+ * opens an index scan and deletes index tuples
+ * when it finds deleted heap tuples. -cim 9/27/89
+ * ----------------
+ */
+
+}
+
+/* ----------------------------------------------------------------
+ * ExecReplace
+ *
+ * note: we can't run replace queries with transactions
+ * off because replaces are actually appends and our
+ * scan will mistakenly loop forever, replacing the tuple
+ * it just appended.. This should be fixed but until it
+ * is, we don't want to get stuck in an infinite loop
+ * which corrupts your database..
+ * ----------------------------------------------------------------
+ */
+static void
+ExecReplace(TupleTableSlot *slot,
+ ItemPointer tupleid,
+ EState *estate,
+ Query *parseTree)
+{
+ HeapTuple tuple;
+ RelationInfo *resultRelationInfo;
+ Relation resultRelationDesc;
+ int numIndices;
+
+ /* ----------------
+ * abort the operation if not running transactions
+ * ----------------
+ */
+ if (IsBootstrapProcessingMode()) {
+ elog(DEBUG, "ExecReplace: replace can't run without transactions");
+ return;
+ }
+
+ /* ----------------
+ * get the heap tuple out of the tuple table slot
+ * ----------------
+ */
+ tuple = slot->val;
+
+ /* ----------------
+ * get the result relation information
+ * ----------------
+ */
+ resultRelationInfo = estate->es_result_relation_info;
+ resultRelationDesc = resultRelationInfo->ri_RelationDesc;
+
+ /* ----------------
+ * have to add code to preform unique checking here.
+ * in the event of unique tuples, this becomes a deletion
+ * of the original tuple affected by the replace.
+ * cim -12/1/89
+ * ----------------
+ */
+
+ /* ----------------
+ * replace the heap tuple
+ *
+ * Don't want to continue if our heap_replace didn't actually
+ * do a replace. This would be the case if heap_replace
+ * detected a non-functional update. -kw 12/30/93
+ * ----------------
+ */
+ if (heap_replace(resultRelationDesc, /* relation desc */
+ tupleid, /* item ptr of tuple to replace */
+ tuple)) { /* replacement heap tuple */
+ return;
+ }
+
+ IncrReplaced();
+
+ /* ----------------
+ * Note: instead of having to update the old index tuples
+ * associated with the heap tuple, all we do is form
+ * and insert new index tuples.. This is because
+ * replaces are actually deletes and inserts and
+ * index tuple deletion is done automagically by
+ * the vaccuum deamon.. All we do is insert new
+ * index tuples. -cim 9/27/89
+ * ----------------
+ */
+
+ /* ----------------
+ * process indices
+ *
+ * heap_replace updates a tuple in the base relation by invalidating
+ * it and then appending a new tuple to the relation. As a side
+ * effect, the tupleid of the new tuple is placed in the new
+ * tuple's t_ctid field. So we now insert index tuples using
+ * the new tupleid stored there.
+ * ----------------
+ */
+ numIndices = resultRelationInfo->ri_NumIndices;
+ if (numIndices > 0) {
+ ExecInsertIndexTuples(slot, &(tuple->t_ctid), estate);
+ }
+}
diff --git a/src/backend/executor/execProcnode.c b/src/backend/executor/execProcnode.c
new file mode 100644
index 00000000000..11a6f63a778
--- /dev/null
+++ b/src/backend/executor/execProcnode.c
@@ -0,0 +1,477 @@
+/*-------------------------------------------------------------------------
+ *
+ * execProcnode.c--
+ * contains dispatch functions which call the appropriate "initialize",
+ * "get a tuple", and "cleanup" routines for the given node type.
+ * If the node has children, then it will presumably call ExecInitNode,
+ * ExecProcNode, or ExecEndNode on it's subnodes and do the appropriate
+ * processing..
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/executor/execProcnode.c,v 1.1.1.1 1996/07/09 06:21:25 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+ * INTERFACE ROUTINES
+ * ExecInitNode - initialize a plan node and it's subplans
+ * ExecProcNode - get a tuple by executing the plan node
+ * ExecEndNode - shut down a plan node and it's subplans
+ *
+ * NOTES
+ * This used to be three files. It is now all combined into
+ * one file so that it is easier to keep ExecInitNode, ExecProcNode,
+ * and ExecEndNode in sync when new nodes are added.
+ *
+ * EXAMPLE
+ * suppose we want the age of the manager of the shoe department and
+ * the number of employees in that department. so we have the query:
+ *
+ * retrieve (DEPT.no_emps, EMP.age)
+ * where EMP.name = DEPT.mgr and
+ * DEPT.name = "shoe"
+ *
+ * Suppose the planner gives us the following plan:
+ *
+ * Nest Loop (DEPT.mgr = EMP.name)
+ * / \
+ * / \
+ * Seq Scan Seq Scan
+ * DEPT EMP
+ * (name = "shoe")
+ *
+ * ExecStart() is called first.
+ * It calls InitPlan() which calls ExecInitNode() on
+ * the root of the plan -- the nest loop node.
+ *
+ * * ExecInitNode() notices that it is looking at a nest loop and
+ * as the code below demonstrates, it calls ExecInitNestLoop().
+ * Eventually this calls ExecInitNode() on the right and left subplans
+ * and so forth until the entire plan is initialized.
+ *
+ * * Then when ExecRun() is called, it calls ExecutePlan() which
+ * calls ExecProcNode() repeatedly on the top node of the plan.
+ * Each time this happens, ExecProcNode() will end up calling
+ * ExecNestLoop(), which calls ExecProcNode() on its subplans.
+ * Each of these subplans is a sequential scan so ExecSeqScan() is
+ * called. The slots returned by ExecSeqScan() may contain
+ * tuples which contain the attributes ExecNestLoop() uses to
+ * form the tuples it returns.
+ *
+ * * Eventually ExecSeqScan() stops returning tuples and the nest
+ * loop join ends. Lastly, ExecEnd() calls ExecEndNode() which
+ * calls ExecEndNestLoop() which in turn calls ExecEndNode() on
+ * its subplans which result in ExecEndSeqScan().
+ *
+ * This should show how the executor works by having
+ * ExecInitNode(), ExecProcNode() and ExecEndNode() dispatch
+ * their work to the appopriate node support routines which may
+ * in turn call these routines themselves on their subplans.
+ *
+ */
+#include "executor/executor.h"
+#include "executor/nodeResult.h"
+#include "executor/nodeAppend.h"
+#include "executor/nodeSeqscan.h"
+#include "executor/nodeIndexscan.h"
+#include "executor/nodeNestloop.h"
+#include "executor/nodeMergejoin.h"
+#include "executor/nodeMaterial.h"
+#include "executor/nodeSort.h"
+#include "executor/nodeUnique.h"
+#include "executor/nodeGroup.h"
+#include "executor/nodeAgg.h"
+#include "executor/nodeHash.h"
+#include "executor/nodeHashjoin.h"
+#include "executor/nodeTee.h"
+
+/* ------------------------------------------------------------------------
+ * ExecInitNode
+ *
+ * Recursively initializes all the nodes in the plan rooted
+ * at 'node'.
+ *
+ * Initial States:
+ * 'node' is the plan produced by the query planner
+ *
+ * returns TRUE/FALSE on whether the plan was successfully initialized
+ * ------------------------------------------------------------------------
+ */
+bool
+ExecInitNode(Plan *node, EState *estate, Plan *parent)
+{
+ bool result;
+
+ /* ----------------
+ * do nothing when we get to the end
+ * of a leaf on tree.
+ * ----------------
+ */
+ if (node == NULL)
+ return FALSE;
+
+ switch(nodeTag(node)) {
+ /* ----------------
+ * control nodes
+ * ----------------
+ */
+ case T_Result:
+ result = ExecInitResult((Result *)node, estate, parent);
+ break;
+
+ case T_Append:
+ result = ExecInitAppend((Append *)node, estate, parent);
+ break;
+
+ /* ----------------
+ * scan nodes
+ * ----------------
+ */
+ case T_SeqScan:
+ result = ExecInitSeqScan((SeqScan *)node, estate, parent);
+ break;
+
+ case T_IndexScan:
+ result = ExecInitIndexScan((IndexScan *)node, estate, parent);
+ break;
+
+ /* ----------------
+ * join nodes
+ * ----------------
+ */
+ case T_NestLoop:
+ result = ExecInitNestLoop((NestLoop *)node, estate, parent);
+ break;
+
+ case T_MergeJoin:
+ result = ExecInitMergeJoin((MergeJoin *)node, estate, parent);
+ break;
+
+ /* ----------------
+ * materialization nodes
+ * ----------------
+ */
+ case T_Material:
+ result = ExecInitMaterial((Material *)node, estate, parent);
+ break;
+
+ case T_Sort:
+ result = ExecInitSort((Sort *)node, estate, parent);
+ break;
+
+ case T_Unique:
+ result = ExecInitUnique((Unique *)node, estate, parent);
+ break;
+
+ case T_Group:
+ result = ExecInitGroup((Group *)node, estate, parent);
+ break;
+
+ case T_Agg:
+ result = ExecInitAgg((Agg *)node, estate, parent);
+ break;
+
+ case T_Hash:
+ result = ExecInitHash((Hash *)node, estate, parent);
+ break;
+
+ case T_HashJoin:
+ result = ExecInitHashJoin((HashJoin *)node, estate, parent);
+ break;
+
+ case T_Tee:
+ result = ExecInitTee((Tee*)node, estate, parent);
+ break;
+
+ default:
+ elog(DEBUG, "ExecInitNode: node not yet supported: %d",
+ nodeTag(node));
+ result = FALSE;
+ }
+
+ return result;
+}
+
+
+/* ----------------------------------------------------------------
+ * ExecProcNode
+ *
+ * Initial States:
+ * the query tree must be initialized once by calling ExecInit.
+ * ----------------------------------------------------------------
+ */
+TupleTableSlot *
+ExecProcNode(Plan *node, Plan *parent)
+{
+ TupleTableSlot *result;
+
+ /* ----------------
+ * deal with NULL nodes..
+ * ----------------
+ */
+ if (node == NULL)
+ return NULL;
+
+ switch(nodeTag(node)) {
+ /* ----------------
+ * control nodes
+ * ----------------
+ */
+ case T_Result:
+ result = ExecResult((Result *)node);
+ break;
+
+ case T_Append:
+ result = ExecProcAppend((Append *)node);
+ break;
+
+ /* ----------------
+ * scan nodes
+ * ----------------
+ */
+ case T_SeqScan:
+ result = ExecSeqScan((SeqScan *)node);
+ break;
+
+ case T_IndexScan:
+ result = ExecIndexScan((IndexScan *)node);
+ break;
+
+ /* ----------------
+ * join nodes
+ * ----------------
+ */
+ case T_NestLoop:
+ result = ExecNestLoop((NestLoop *)node, parent);
+ break;
+
+ case T_MergeJoin:
+ result = ExecMergeJoin((MergeJoin *)node);
+ break;
+
+ /* ----------------
+ * materialization nodes
+ * ----------------
+ */
+ case T_Material:
+ result = ExecMaterial((Material *)node);
+ break;
+
+ case T_Sort:
+ result = ExecSort((Sort *)node);
+ break;
+
+ case T_Unique:
+ result = ExecUnique((Unique *)node);
+ break;
+
+ case T_Group:
+ result = ExecGroup((Group *)node);
+ break;
+
+ case T_Agg:
+ result = ExecAgg((Agg *)node);
+ break;
+
+ case T_Hash:
+ result = ExecHash((Hash *)node);
+ break;
+
+ case T_HashJoin:
+ result = ExecHashJoin((HashJoin *)node);
+ break;
+
+ case T_Tee:
+ result = ExecTee((Tee*)node, parent);
+ break;
+
+ default:
+ elog(DEBUG, "ExecProcNode: node not yet supported: %d",
+ nodeTag(node));
+ result = FALSE;
+ }
+
+ return result;
+}
+
+int
+ExecCountSlotsNode(Plan *node)
+{
+ if (node == (Plan *)NULL)
+ return 0;
+
+ switch(nodeTag(node)) {
+ /* ----------------
+ * control nodes
+ * ----------------
+ */
+ case T_Result:
+ return ExecCountSlotsResult((Result *)node);
+
+ case T_Append:
+ return ExecCountSlotsAppend((Append *)node);
+
+ /* ----------------
+ * scan nodes
+ * ----------------
+ */
+ case T_SeqScan:
+ return ExecCountSlotsSeqScan((SeqScan *)node);
+
+ case T_IndexScan:
+ return ExecCountSlotsIndexScan((IndexScan *)node);
+
+ /* ----------------
+ * join nodes
+ * ----------------
+ */
+ case T_NestLoop:
+ return ExecCountSlotsNestLoop((NestLoop *)node);
+
+ case T_MergeJoin:
+ return ExecCountSlotsMergeJoin((MergeJoin *)node);
+
+ /* ----------------
+ * materialization nodes
+ * ----------------
+ */
+ case T_Material:
+ return ExecCountSlotsMaterial((Material *)node);
+
+ case T_Sort:
+ return ExecCountSlotsSort((Sort *)node);
+
+ case T_Unique:
+ return ExecCountSlotsUnique((Unique *)node);
+
+ case T_Group:
+ return ExecCountSlotsGroup((Group *)node);
+
+ case T_Agg:
+ return ExecCountSlotsAgg((Agg *)node);
+
+ case T_Hash:
+ return ExecCountSlotsHash((Hash *)node);
+
+ case T_HashJoin:
+ return ExecCountSlotsHashJoin((HashJoin *)node);
+
+ case T_Tee:
+ return ExecCountSlotsTee((Tee*)node);
+
+ default:
+ elog(WARN, "ExecCountSlotsNode: node not yet supported: %d",
+ nodeTag(node));
+ break;
+ }
+ return 0;
+}
+
+/* ----------------------------------------------------------------
+ * ExecEndNode
+ *
+ * Recursively cleans up all the nodes in the plan rooted
+ * at 'node'.
+ *
+ * After this operation, the query plan will not be able to
+ * processed any further. This should be called only after
+ * the query plan has been fully executed.
+ * ----------------------------------------------------------------
+ */
+void
+ExecEndNode(Plan *node, Plan *parent)
+{
+ /* ----------------
+ * do nothing when we get to the end
+ * of a leaf on tree.
+ * ----------------
+ */
+ if (node == NULL)
+ return;
+
+ switch(nodeTag(node)) {
+ /* ----------------
+ * control nodes
+ * ----------------
+ */
+ case T_Result:
+ ExecEndResult((Result *)node);
+ break;
+
+ case T_Append:
+ ExecEndAppend((Append *)node);
+ break;
+
+ /* ----------------
+ * scan nodes
+ * ----------------
+ */
+ case T_SeqScan:
+ ExecEndSeqScan((SeqScan *)node);
+ break;
+
+ case T_IndexScan:
+ ExecEndIndexScan((IndexScan *)node);
+ break;
+
+ /* ----------------
+ * join nodes
+ * ----------------
+ */
+ case T_NestLoop:
+ ExecEndNestLoop((NestLoop *)node);
+ break;
+
+ case T_MergeJoin:
+ ExecEndMergeJoin((MergeJoin *)node);
+ break;
+
+ /* ----------------
+ * materialization nodes
+ * ----------------
+ */
+ case T_Material:
+ ExecEndMaterial((Material *)node);
+ break;
+
+ case T_Sort:
+ ExecEndSort((Sort *)node);
+ break;
+
+ case T_Unique:
+ ExecEndUnique((Unique *)node);
+ break;
+
+ case T_Group:
+ ExecEndGroup((Group *)node);
+ break;
+
+ case T_Agg:
+ ExecEndAgg((Agg *)node);
+ break;
+
+ /* ----------------
+ * XXX add hooks to these
+ * ----------------
+ */
+ case T_Hash:
+ ExecEndHash((Hash *) node);
+ break;
+
+ case T_HashJoin:
+ ExecEndHashJoin((HashJoin *) node);
+ break;
+
+ case T_Tee:
+ ExecEndTee((Tee*) node, parent);
+ break;
+
+ default:
+ elog(DEBUG, "ExecEndNode: node not yet supported",
+ nodeTag(node));
+ break;
+ }
+}
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
new file mode 100644
index 00000000000..104c1f2f506
--- /dev/null
+++ b/src/backend/executor/execQual.c
@@ -0,0 +1,1504 @@
+/*-------------------------------------------------------------------------
+ *
+ * execQual.c--
+ * Routines to evaluate qualification and targetlist expressions
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.1.1.1 1996/07/09 06:21:25 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+ * INTERFACE ROUTINES
+ * ExecEvalExpr - evaluate an expression and return a datum
+ * ExecQual - return true/false if qualification is satisified
+ * ExecTargetList - form a new tuple by projecting the given tuple
+ *
+ * NOTES
+ * ExecEvalExpr() and ExecEvalVar() are hotspots. making these faster
+ * will speed up the entire system. Unfortunately they are currently
+ * implemented recursively.. Eliminating the recursion is bound to
+ * improve the speed of the executor.
+ *
+ * ExecTargetList() is used to make tuple projections. Rather then
+ * trying to speed it up, the execution plan should be pre-processed
+ * to facilitate attribute sharing between nodes wherever possible,
+ * instead of doing needless copying. -cim 5/31/91
+ *
+ */
+#include "nodes/primnodes.h"
+#include "nodes/relation.h"
+
+#include "optimizer/clauses.h"
+
+#include "nodes/memnodes.h"
+#include "catalog/pg_language.h"
+#include "executor/executor.h"
+#include "executor/execFlatten.h"
+#include "executor/functions.h"
+#include "access/heapam.h"
+#include "utils/memutils.h"
+#include "utils/builtins.h"
+#include "utils/palloc.h"
+#include "utils/fcache.h"
+#include "utils/fcache2.h"
+#include "utils/array.h"
+
+/* ----------------
+ * externs and constants
+ * ----------------
+ */
+
+/*
+ * XXX Used so we can get rid of use of Const nodes in the executor.
+ * Currently only used by ExecHashGetBucket and set only by ExecMakeVarConst
+ * and by ExecEvalArrayRef.
+ */
+bool execConstByVal;
+int execConstLen;
+
+/* static functions decls */
+static Datum ExecEvalAggreg(Aggreg *agg, ExprContext *econtext, bool *isNull);
+static Datum ExecEvalArrayRef(ArrayRef *arrayRef, ExprContext *econtext,
+ bool *isNull, bool *isDone);
+
+/* --------------------------------
+ * ExecEvalArrayRef
+ *
+ * This function takes an ArrayRef and returns a Const Node if it
+ * is an array reference or returns the changed Array Node if it is
+ * an array assignment.
+ *
+ * --------------------------------
+ */
+static Datum
+ExecEvalArrayRef(ArrayRef *arrayRef,
+ ExprContext *econtext,
+ bool *isNull,
+ bool *isDone)
+{
+ bool dummy;
+ int i = 0, j = 0;
+ ArrayType *array_scanner;
+ List *upperIndexpr, *lowerIndexpr;
+ Node *assgnexpr;
+ List *elt;
+ IntArray upper, lower;
+ int *lIndex;
+ char *dataPtr;
+
+ execConstByVal = arrayRef->refelembyval;
+ *isNull = false;
+ array_scanner = (ArrayType*)ExecEvalExpr(arrayRef->refexpr,
+ econtext,
+ isNull,
+ isDone);
+ if (*isNull) return (Datum)NULL;
+
+ upperIndexpr = arrayRef->refupperindexpr;
+
+ foreach (elt, upperIndexpr) {
+ upper.indx[i++] = (int32)ExecEvalExpr((Node*)lfirst(elt),
+ econtext,
+ isNull,
+ &dummy);
+ if (*isNull) return (Datum)NULL;
+ }
+
+ lowerIndexpr = arrayRef->reflowerindexpr;
+ lIndex = NULL;
+ if (lowerIndexpr != NIL) {
+ foreach (elt, lowerIndexpr) {
+ lower.indx[j++] = (int32)ExecEvalExpr((Node*)lfirst(elt),
+ econtext,
+ isNull,
+ &dummy);
+ if (*isNull) return (Datum)NULL;
+ }
+ if (i != j)
+ elog(WARN,
+ "ExecEvalArrayRef: upper and lower indices mismatch");
+ lIndex = lower.indx;
+ }
+
+ assgnexpr = arrayRef->refassgnexpr;
+ if (assgnexpr != NULL) {
+ dataPtr = (char*)ExecEvalExpr((Node *)
+ assgnexpr, econtext,
+ isNull, &dummy);
+ if (*isNull) return (Datum)NULL;
+ if (lIndex == NULL)
+ return (Datum) array_set(array_scanner, i, upper.indx, dataPtr,
+ arrayRef->refelembyval,
+ arrayRef->refelemlength,
+ arrayRef->refattrlength, isNull);
+ return (Datum) array_assgn(array_scanner, i, upper.indx,
+ lower.indx,
+ (ArrayType*)dataPtr,
+ arrayRef->refelembyval,
+ arrayRef->refelemlength, isNull);
+ }
+ if (lIndex == NULL)
+ return (Datum) array_ref(array_scanner, i, upper.indx,
+ arrayRef->refelembyval,
+ arrayRef->refelemlength,
+ arrayRef->refattrlength, isNull);
+ return (Datum) array_clip(array_scanner, i, upper.indx, lower.indx,
+ arrayRef->refelembyval,
+ arrayRef->refelemlength, isNull);
+}
+
+
+/* ----------------------------------------------------------------
+ * ExecEvalAggreg
+ *
+ * Returns a Datum whose value is the value of the precomputed
+ * aggregate found in the given expression context.
+ * ----------------------------------------------------------------
+ */
+static Datum
+ExecEvalAggreg(Aggreg *agg, ExprContext *econtext, bool *isNull)
+{
+
+ *isNull = econtext->ecxt_nulls[agg->aggno];
+ return econtext->ecxt_values[agg->aggno];
+}
+
+/* ----------------------------------------------------------------
+ * ExecEvalVar
+ *
+ * Returns a Datum whose value is the value of a range
+ * variable with respect to given expression context.
+ * ----------------------------------------------------------------
+ */
+Datum
+ExecEvalVar(Var *variable, ExprContext *econtext, bool *isNull)
+{
+ Datum result;
+ TupleTableSlot *slot;
+ AttrNumber attnum;
+ HeapTuple heapTuple;
+ TupleDesc tuple_type;
+ Buffer buffer;
+ bool byval;
+ int16 len;
+
+ /* ----------------
+ * get the slot we want
+ * ----------------
+ */
+ switch(variable->varno) {
+ case INNER: /* get the tuple from the inner node */
+ slot = econtext->ecxt_innertuple;
+ break;
+
+ case OUTER: /* get the tuple from the outer node */
+ slot = econtext->ecxt_outertuple;
+ break;
+
+ default: /* get the tuple from the relation being scanned */
+ slot = econtext->ecxt_scantuple;
+ break;
+ }
+
+ /* ----------------
+ * extract tuple information from the slot
+ * ----------------
+ */
+ heapTuple = slot->val;
+ tuple_type = slot->ttc_tupleDescriptor;
+ buffer = slot->ttc_buffer;
+
+ attnum = variable->varattno;
+
+ /*
+ * If the attribute number is invalid, then we are supposed to
+ * return the entire tuple, we give back a whole slot so that
+ * callers know what the tuple looks like.
+ */
+ if (attnum == InvalidAttrNumber)
+ {
+ TupleTableSlot *tempSlot;
+ TupleDesc td;
+ HeapTuple tup;
+
+ tempSlot = makeNode(TupleTableSlot);
+ tempSlot->ttc_shouldFree = false;
+ tempSlot->ttc_descIsNew = true;
+ tempSlot->ttc_tupleDescriptor = (TupleDesc)NULL,
+ tempSlot->ttc_buffer = InvalidBuffer;
+ tempSlot->ttc_whichplan = -1;
+
+ tup = heap_copytuple(slot->val);
+ td = CreateTupleDescCopy(slot->ttc_tupleDescriptor);
+
+ ExecSetSlotDescriptor(tempSlot, td);
+
+ ExecStoreTuple(tup, tempSlot, InvalidBuffer, true);
+ return (Datum) tempSlot;
+ }
+
+ result = (Datum)
+ heap_getattr(heapTuple, /* tuple containing attribute */
+ buffer, /* buffer associated with tuple */
+ attnum, /* attribute number of desired attribute */
+ tuple_type, /* tuple descriptor of tuple */
+ isNull); /* return: is attribute null? */
+
+ /* ----------------
+ * return null if att is null
+ * ----------------
+ */
+ if (*isNull)
+ return (Datum) NULL;
+
+ /* ----------------
+ * get length and type information..
+ * ??? what should we do about variable length attributes
+ * - variable length attributes have their length stored
+ * in the first 4 bytes of the memory pointed to by the
+ * returned value.. If we can determine that the type
+ * is a variable length type, we can do the right thing.
+ * -cim 9/15/89
+ * ----------------
+ */
+ if (attnum < 0) {
+ /* ----------------
+ * If this is a pseudo-att, we get the type and fake the length.
+ * There ought to be a routine to return the real lengths, so
+ * we'll mark this one ... XXX -mao
+ * ----------------
+ */
+ len = heap_sysattrlen(attnum); /* XXX see -mao above */
+ byval = heap_sysattrbyval(attnum); /* XXX see -mao above */
+ } else {
+ len = tuple_type->attrs[ attnum-1 ]->attlen;
+ byval = tuple_type->attrs[ attnum-1 ]->attbyval ? true : false ;
+ }
+
+ execConstByVal = byval;
+ execConstLen = len;
+
+ return result;
+}
+
+/* ----------------------------------------------------------------
+ * ExecEvalParam
+ *
+ * Returns the value of a parameter. A param node contains
+ * something like ($.name) and the expression context contains
+ * the current parameter bindings (name = "sam") (age = 34)...
+ * so our job is to replace the param node with the datum
+ * containing the appropriate information ("sam").
+ *
+ * Q: if we have a parameter ($.foo) without a binding, i.e.
+ * there is no (foo = xxx) in the parameter list info,
+ * is this a fatal error or should this be a "not available"
+ * (in which case we shoud return a Const node with the
+ * isnull flag) ? -cim 10/13/89
+ *
+ * Minor modification: Param nodes now have an extra field,
+ * `paramkind' which specifies the type of parameter
+ * (see params.h). So while searching the paramList for
+ * a paramname/value pair, we have also to check for `kind'.
+ *
+ * NOTE: The last entry in `paramList' is always an
+ * entry with kind == PARAM_INVALID.
+ * ----------------------------------------------------------------
+ */
+Datum
+ExecEvalParam(Param *expression, ExprContext *econtext, bool *isNull)
+{
+
+ char *thisParameterName;
+ int thisParameterKind;
+ AttrNumber thisParameterId;
+ int matchFound;
+ ParamListInfo paramList;
+
+ thisParameterName = expression->paramname;
+ thisParameterKind = expression->paramkind;
+ thisParameterId = expression->paramid;
+ paramList = econtext->ecxt_param_list_info;
+
+ *isNull = false;
+ /*
+ * search the list with the parameter info to find a matching name.
+ * An entry with an InvalidName denotes the last element in the array.
+ */
+ matchFound = 0;
+ if (paramList != NULL) {
+ /*
+ * search for an entry in 'paramList' that matches
+ * the `expression'.
+ */
+ while(paramList->kind != PARAM_INVALID && !matchFound) {
+ switch (thisParameterKind) {
+ case PARAM_NAMED:
+ if (thisParameterKind == paramList->kind &&
+ strcmp(paramList->name, thisParameterName) == 0){
+ matchFound = 1;
+ }
+ break;
+ case PARAM_NUM:
+ if (thisParameterKind == paramList->kind &&
+ paramList->id == thisParameterId) {
+ matchFound = 1;
+ }
+ break;
+ case PARAM_OLD:
+ case PARAM_NEW:
+ if (thisParameterKind == paramList->kind &&
+ paramList->id == thisParameterId)
+ {
+ matchFound = 1;
+ /*
+ * sanity check
+ */
+ if (strcmp(paramList->name, thisParameterName) != 0){
+ elog(WARN,
+ "ExecEvalParam: new/old params with same id & diff names");
+ }
+ }
+ break;
+ default:
+ /*
+ * oops! this is not supposed to happen!
+ */
+ elog(WARN, "ExecEvalParam: invalid paramkind %d",
+ thisParameterKind);
+ }
+ if (! matchFound) {
+ paramList++;
+ }
+ } /*while*/
+ } /*if*/
+
+ if (!matchFound) {
+ /*
+ * ooops! we couldn't find this parameter
+ * in the parameter list. Signal an error
+ */
+ elog(WARN, "ExecEvalParam: Unknown value for parameter %s",
+ thisParameterName);
+ }
+
+ /*
+ * return the value.
+ */
+ if (paramList->isnull)
+ {
+ *isNull = true;
+ return (Datum)NULL;
+ }
+
+ if (expression->param_tlist != NIL)
+ {
+ HeapTuple tup;
+ Datum value;
+ List *tlist = expression->param_tlist;
+ TargetEntry *tle = (TargetEntry *)lfirst(tlist);
+ TupleTableSlot *slot = (TupleTableSlot *)paramList->value;
+
+ tup = slot->val;
+ value = ProjectAttribute(slot->ttc_tupleDescriptor,
+ tle, tup, isNull);
+ return value;
+ }
+ return(paramList->value);
+}
+
+
+/* ----------------------------------------------------------------
+ * ExecEvalOper / ExecEvalFunc support routines
+ * ----------------------------------------------------------------
+ */
+
+/* ----------------
+ * GetAttributeByName
+ * GetAttributeByNum
+ *
+ * These are functions which return the value of the
+ * named attribute out of the tuple from the arg slot. User defined
+ * C functions which take a tuple as an argument are expected
+ * to use this. Ex: overpaid(EMP) might call GetAttributeByNum().
+ * ----------------
+ */
+char *
+GetAttributeByNum(TupleTableSlot *slot,
+ AttrNumber attrno,
+ bool *isNull)
+{
+ Datum retval;
+
+ if (!AttributeNumberIsValid(attrno))
+ elog(WARN, "GetAttributeByNum: Invalid attribute number");
+
+ if (!AttrNumberIsForUserDefinedAttr(attrno))
+ elog(WARN, "GetAttributeByNum: cannot access system attributes here");
+
+ if (isNull == (bool *)NULL)
+ elog(WARN, "GetAttributeByNum: a NULL isNull flag was passed");
+
+ if (TupIsNull(slot))
+ {
+ *isNull = true;
+ return (char *) NULL;
+ }
+
+ retval = (Datum)
+ heap_getattr(slot->val,
+ slot->ttc_buffer,
+ attrno,
+ slot->ttc_tupleDescriptor,
+ isNull);
+ if (*isNull)
+ return (char *) NULL;
+ return (char *) retval;
+}
+
+/* XXX char16 name for catalogs */
+char *
+att_by_num(TupleTableSlot *slot,
+ AttrNumber attrno,
+ bool *isNull)
+{
+ return(GetAttributeByNum(slot, attrno, isNull));
+}
+
+char *
+GetAttributeByName(TupleTableSlot *slot, char *attname, bool *isNull)
+{
+ AttrNumber attrno;
+ TupleDesc tupdesc;
+ HeapTuple tuple;
+ Datum retval;
+ int natts;
+ int i;
+
+ if (attname == NULL)
+ elog(WARN, "GetAttributeByName: Invalid attribute name");
+
+ if (isNull == (bool *)NULL)
+ elog(WARN, "GetAttributeByName: a NULL isNull flag was passed");
+
+ if (TupIsNull(slot))
+ {
+ *isNull = true;
+ return (char *) NULL;
+ }
+
+ tupdesc = slot->ttc_tupleDescriptor;
+ tuple = slot->val;
+
+ natts = tuple->t_natts;
+
+ attrno = InvalidAttrNumber;
+ for (i=0;i<tupdesc->natts;i++) {
+ if (namestrcmp(&(tupdesc->attrs[i]->attname), attname) == 0) {
+ attrno = tupdesc->attrs[i]->attnum;
+ break;
+ }
+ }
+
+ if (attrno == InvalidAttrNumber)
+ elog(WARN, "GetAttributeByName: attribute %s not found", attname);
+
+ retval = (Datum)
+ heap_getattr(slot->val,
+ slot->ttc_buffer,
+ attrno,
+ tupdesc,
+ isNull);
+ if (*isNull)
+ return (char *) NULL;
+ return (char *) retval;
+}
+
+/* XXX char16 name for catalogs */
+char *
+att_by_name(TupleTableSlot *slot, char *attname, bool *isNull)
+{
+ return(GetAttributeByName(slot, attname, isNull));
+}
+
+void
+ExecEvalFuncArgs(FunctionCachePtr fcache,
+ ExprContext *econtext,
+ List *argList,
+ Datum argV[],
+ bool *argIsDone)
+{
+ int i;
+ bool argIsNull, *nullVect;
+ List *arg;
+
+ nullVect = fcache->nullVect;
+
+ i = 0;
+ foreach (arg, argList) {
+ /* ----------------
+ * evaluate the expression, in general functions cannot take
+ * sets as arguments but we make an exception in the case of
+ * nested dot expressions. We have to watch out for this case
+ * here.
+ * ----------------
+ */
+ argV[i] = (Datum)
+ ExecEvalExpr((Node *) lfirst(arg),
+ econtext,
+ &argIsNull,
+ argIsDone);
+ if (! (*argIsDone))
+ {
+ Assert(i == 0);
+ fcache->setArg = (char *)argV[0];
+ fcache->hasSetArg = true;
+ }
+ if (argIsNull)
+ nullVect[i] = true;
+ else
+ nullVect[i] = false;
+ i++;
+ }
+}
+
+/* ----------------
+ * ExecMakeFunctionResult
+ * ----------------
+ */
+Datum
+ExecMakeFunctionResult(Node *node,
+ List *arguments,
+ ExprContext *econtext,
+ bool *isNull,
+ bool *isDone)
+{
+ Datum argv[MAXFMGRARGS];
+ FunctionCachePtr fcache;
+ Func *funcNode = NULL;
+ Oper *operNode = NULL;
+ bool funcisset = false;
+
+ /*
+ * This is kind of ugly, Func nodes now have targetlists so that
+ * we know when and what to project out from postquel function results.
+ * This means we have to pass the func node all the way down instead
+ * of using only the fcache struct as before. ExecMakeFunctionResult
+ * becomes a little bit more of a dual personality as a result.
+ */
+ if (IsA(node,Func))
+ {
+ funcNode = (Func *)node;
+ fcache = funcNode->func_fcache;
+ }
+ else
+ {
+ operNode = (Oper *)node;
+ fcache = operNode->op_fcache;
+ }
+
+ /* ----------------
+ * arguments is a list of expressions to evaluate
+ * before passing to the function manager.
+ * We collect the results of evaluating the expressions
+ * into a datum array (argv) and pass this array to arrayFmgr()
+ * ----------------
+ */
+ if (fcache->nargs != 0) {
+ bool argDone;
+
+ if (fcache->nargs > MAXFMGRARGS)
+ elog(WARN, "ExecMakeFunctionResult: too many arguments");
+
+ /*
+ * If the setArg in the fcache is set we have an argument
+ * returning a set of tuples (i.e. a nested dot expression). We
+ * don't want to evaluate the arguments again until the function
+ * is done. hasSetArg will always be false until we eval the args
+ * for the first time. We should set this in the parser.
+ */
+ if ((fcache->hasSetArg) && fcache->setArg != NULL)
+ {
+ argv[0] = (Datum)fcache->setArg;
+ argDone = false;
+ }
+ else
+ ExecEvalFuncArgs(fcache, econtext, arguments, argv, &argDone);
+
+ if ((fcache->hasSetArg) && (argDone)) {
+ if (isDone) *isDone = true;
+ return (Datum)NULL;
+ }
+ }
+
+ /* If this function is really a set, we have to diddle with things.
+ * If the function has already been called at least once, then the
+ * setArg field of the fcache holds
+ * the OID of this set in pg_proc. (This is not quite legit, since
+ * the setArg field is really for functions which take sets of tuples
+ * as input - set functions take no inputs at all. But it's a nice
+ * place to stash this value, for now.)
+ *
+ * If this is the first call of the set's function, then
+ * the call to ExecEvalFuncArgs above just returned the OID of
+ * the pg_proc tuple which defines this set. So replace the existing
+ * funcid in the funcnode with the set's OID. Also, we want a new
+ * fcache which points to the right function, so get that, now that
+ * we have the right OID. Also zero out the argv, since the real
+ * set doesn't take any arguments.
+ */
+ if (((Func *)node)->funcid == SetEvalRegProcedure) {
+ funcisset = true;
+ if (fcache->setArg) {
+ argv[0] = 0;
+
+ ((Func *)node)->funcid = (Oid) PointerGetDatum(fcache->setArg);
+
+ } else {
+ ((Func *)node)->funcid = (Oid) argv[0];
+ setFcache(node, argv[0], NIL,econtext);
+ fcache = ((Func *)node)->func_fcache;
+ fcache->setArg = (char*)argv[0];
+ argv[0] = (Datum)0;
+ }
+ }
+
+ /* ----------------
+ * now return the value gotten by calling the function manager,
+ * passing the function the evaluated parameter values.
+ * ----------------
+ */
+ if (fcache->language == SQLlanguageId) {
+ Datum result;
+
+ Assert(funcNode);
+ result = postquel_function (funcNode, (char **) argv, isNull, isDone);
+ /*
+ * finagle the situation where we are iterating through all results
+ * in a nested dot function (whose argument function returns a set
+ * of tuples) and the current function finally finishes. We need to
+ * get the next argument in the set and run the function all over
+ * again. This is getting unclean.
+ */
+ if ((*isDone) && (fcache->hasSetArg)) {
+ bool argDone;
+
+ ExecEvalFuncArgs(fcache, econtext, arguments, argv, &argDone);
+
+ if (argDone) {
+ fcache->setArg = (char *)NULL;
+ *isDone = true;
+ result = (Datum)NULL;
+ }
+ else
+ result = postquel_function(funcNode,
+ (char **) argv,
+ isNull,
+ isDone);
+ }
+ if (funcisset) {
+ /* reset the funcid so that next call to this routine will
+ * still recognize this func as a set.
+ * Note that for now we assume that the set function in
+ * pg_proc must be a Postquel function - the funcid is
+ * not reset below for C functions.
+ */
+ ((Func *)node)->funcid = SetEvalRegProcedure;
+ /* If we're done with the results of this function, get rid
+ * of its func cache.
+ */
+ if (*isDone) {
+ ((Func *)node)->func_fcache = NULL;
+ }
+ }
+ return result;
+ }
+ else
+ {
+ int i;
+
+ if (isDone) *isDone = true;
+ for (i = 0; i < fcache->nargs; i++)
+ if (fcache->nullVect[i] == true) *isNull = true;
+
+ return((Datum) fmgr_c(fcache->func, fcache->foid, fcache->nargs,
+ (FmgrValues *) argv, isNull));
+ }
+}
+
+
+/* ----------------------------------------------------------------
+ * ExecEvalOper
+ * ExecEvalFunc
+ *
+ * Evaluate the functional result of a list of arguments by calling the
+ * function manager. Note that in the case of operator expressions, the
+ * optimizer had better have already replaced the operator OID with the
+ * appropriate function OID or we're hosed.
+ *
+ * old comments
+ * Presumably the function manager will not take null arguments, so we
+ * check for null arguments before sending the arguments to (fmgr).
+ *
+ * Returns the value of the functional expression.
+ * ----------------------------------------------------------------
+ */
+
+/* ----------------------------------------------------------------
+ * ExecEvalOper
+ * ----------------------------------------------------------------
+ */
+Datum
+ExecEvalOper(Expr *opClause, ExprContext *econtext, bool *isNull)
+{
+ Oper *op;
+ List *argList;
+ FunctionCachePtr fcache;
+ bool isDone;
+
+ /* ----------------
+ * an opclause is a list (op args). (I think)
+ *
+ * we extract the oid of the function associated with
+ * the op and then pass the work onto ExecMakeFunctionResult
+ * which evaluates the arguments and returns the result of
+ * calling the function on the evaluated arguments.
+ * ----------------
+ */
+ op = (Oper *) opClause->oper;
+ argList = opClause->args;
+
+ /*
+ * get the fcache from the Oper node.
+ * If it is NULL, then initialize it
+ */
+ fcache = op->op_fcache;
+ if (fcache == NULL) {
+ setFcache((Node*)op, op->opid, argList, econtext);
+ fcache = op->op_fcache;
+ }
+
+ /* -----------
+ * call ExecMakeFunctionResult() with a dummy isDone that we ignore.
+ * We don't have operator whose arguments are sets.
+ * -----------
+ */
+ return
+ ExecMakeFunctionResult((Node *)op, argList, econtext, isNull, &isDone);
+}
+
+/* ----------------------------------------------------------------
+ * ExecEvalFunc
+ * ----------------------------------------------------------------
+ */
+
+Datum
+ExecEvalFunc(Expr *funcClause,
+ ExprContext *econtext,
+ bool *isNull,
+ bool *isDone)
+{
+ Func *func;
+ List *argList;
+ FunctionCachePtr fcache;
+
+ /* ----------------
+ * an funcclause is a list (func args). (I think)
+ *
+ * we extract the oid of the function associated with
+ * the func node and then pass the work onto ExecMakeFunctionResult
+ * which evaluates the arguments and returns the result of
+ * calling the function on the evaluated arguments.
+ *
+ * this is nearly identical to the ExecEvalOper code.
+ * ----------------
+ */
+ func = (Func *)funcClause->oper;
+ argList = funcClause->args;
+
+ /*
+ * get the fcache from the Func node.
+ * If it is NULL, then initialize it
+ */
+ fcache = func->func_fcache;
+ if (fcache == NULL) {
+ setFcache((Node*)func, func->funcid, argList, econtext);
+ fcache = func->func_fcache;
+ }
+
+ return
+ ExecMakeFunctionResult((Node*)func, argList, econtext, isNull, isDone);
+}
+
+/* ----------------------------------------------------------------
+ * ExecEvalNot
+ * ExecEvalOr
+ * ExecEvalAnd
+ *
+ * Evaluate boolean expressions. Evaluation of 'or' is
+ * short-circuited when the first true (or null) value is found.
+ *
+ * The query planner reformulates clause expressions in the
+ * qualification to conjunctive normal form. If we ever get
+ * an AND to evaluate, we can be sure that it's not a top-level
+ * clause in the qualification, but appears lower (as a function
+ * argument, for example), or in the target list. Not that you
+ * need to know this, mind you...
+ * ----------------------------------------------------------------
+ */
+Datum
+ExecEvalNot(Expr *notclause, ExprContext *econtext, bool *isNull)
+{
+ Datum expr_value;
+ Node *clause;
+ bool isDone;
+
+ clause = lfirst(notclause->args);
+
+ /* ----------------
+ * We don't iterate over sets in the quals, so pass in an isDone
+ * flag, but ignore it.
+ * ----------------
+ */
+ expr_value = ExecEvalExpr(clause, econtext, isNull, &isDone);
+
+ /* ----------------
+ * if the expression evaluates to null, then we just
+ * cascade the null back to whoever called us.
+ * ----------------
+ */
+ if (*isNull)
+ return expr_value;
+
+ /* ----------------
+ * evaluation of 'not' is simple.. expr is false, then
+ * return 'true' and vice versa.
+ * ----------------
+ */
+ if (DatumGetInt32(expr_value) == 0)
+ return (Datum) true;
+
+ return (Datum) false;
+}
+
+/* ----------------------------------------------------------------
+ * ExecEvalOr
+ * ----------------------------------------------------------------
+ */
+Datum
+ExecEvalOr(Expr *orExpr, ExprContext *econtext, bool *isNull)
+{
+ List *clauses;
+ List *clause;
+ bool isDone;
+ bool IsNull;
+ Datum const_value;
+
+ IsNull = false;
+ clauses = orExpr->args;
+
+ /* ----------------
+ * we use three valued logic functions here...
+ * we evaluate each of the clauses in turn,
+ * as soon as one is true we return that
+ * value. If none is true and none of the
+ * clauses evaluate to NULL we return
+ * the value of the last clause evaluated (which
+ * should be false) with *isNull set to false else
+ * if none is true and at least one clause evaluated
+ * to NULL we set *isNull flag to true -
+ * ----------------
+ */
+ foreach (clause, clauses) {
+
+ /* ----------------
+ * We don't iterate over sets in the quals, so pass in an isDone
+ * flag, but ignore it.
+ * ----------------
+ */
+ const_value = ExecEvalExpr((Node *) lfirst(clause),
+ econtext,
+ isNull,
+ &isDone);
+
+ /* ----------------
+ * if the expression evaluates to null, then we
+ * remember it in the local IsNull flag, if none of the
+ * clauses are true then we need to set *isNull
+ * to true again.
+ * ----------------
+ */
+ if (*isNull)
+ IsNull = *isNull;
+
+ /* ----------------
+ * if we have a true result, then we return it.
+ * ----------------
+ */
+ if (DatumGetInt32(const_value) != 0)
+ return const_value;
+ }
+
+ /* IsNull is true if at least one clause evaluated to NULL */
+ *isNull = IsNull;
+ return const_value;
+}
+
+/* ----------------------------------------------------------------
+ * ExecEvalAnd
+ * ----------------------------------------------------------------
+ */
+Datum
+ExecEvalAnd(Expr *andExpr, ExprContext *econtext, bool *isNull)
+{
+ List *clauses;
+ List *clause;
+ Datum const_value;
+ bool isDone;
+ bool IsNull;
+
+ IsNull = false;
+
+ clauses = andExpr->args;
+
+ /* ----------------
+ * we evaluate each of the clauses in turn,
+ * as soon as one is false we return that
+ * value. If none are false or NULL then we return
+ * the value of the last clause evaluated, which
+ * should be true.
+ * ----------------
+ */
+ foreach (clause, clauses) {
+
+ /* ----------------
+ * We don't iterate over sets in the quals, so pass in an isDone
+ * flag, but ignore it.
+ * ----------------
+ */
+ const_value = ExecEvalExpr((Node *) lfirst(clause),
+ econtext,
+ isNull,
+ &isDone);
+
+ /* ----------------
+ * if the expression evaluates to null, then we
+ * remember it in IsNull, if none of the clauses after
+ * this evaluates to false we will have to set *isNull
+ * to true again.
+ * ----------------
+ */
+ if (*isNull)
+ IsNull = *isNull;
+
+ /* ----------------
+ * if we have a false result, then we return it, since the
+ * conjunction must be false.
+ * ----------------
+ */
+ if (DatumGetInt32(const_value) == 0)
+ return const_value;
+ }
+
+ *isNull = IsNull;
+ return const_value;
+}
+
+/* ----------------------------------------------------------------
+ * ExecEvalExpr
+ *
+ * Recursively evaluate a targetlist or qualification expression.
+ *
+ * This routine is an inner loop routine and should be as fast
+ * as possible.
+ *
+ * Node comparison functions were replaced by macros for speed and to plug
+ * memory leaks incurred by using the planner's Lispy stuff for
+ * comparisons. Order of evaluation of node comparisons IS IMPORTANT;
+ * the macros do no checks. Order of evaluation:
+ *
+ * o an isnull check, largely to avoid coredumps since greg doubts this
+ * routine is called with a null ptr anyway in proper operation, but is
+ * not completely sure...
+ * o ExactNodeType checks.
+ * o clause checks or other checks where we look at the lfirst of something.
+ * ----------------------------------------------------------------
+ */
+Datum
+ExecEvalExpr(Node *expression,
+ ExprContext *econtext,
+ bool *isNull,
+ bool *isDone)
+{
+ Datum retDatum;
+
+ *isNull = false;
+
+ /*
+ * Some callers don't care about is done and only want 1 result. They
+ * indicate this by passing NULL
+ */
+ if (isDone)
+ *isDone = true;
+
+ /* ----------------
+ * here we dispatch the work to the appropriate type
+ * of function given the type of our expression.
+ * ----------------
+ */
+ if (expression == NULL) {
+ *isNull = true;
+ return (Datum) true;
+ }
+
+ switch(nodeTag(expression)) {
+ case T_Var:
+ retDatum = (Datum) ExecEvalVar((Var *) expression, econtext, isNull);
+ break;
+ case T_Const: {
+ Const *con = (Const *)expression;
+
+ if (con->constisnull)
+ *isNull = true;
+ retDatum = con->constvalue;
+ break;
+ }
+ case T_Param:
+ retDatum = (Datum)ExecEvalParam((Param *)expression, econtext, isNull);
+ break;
+ case T_Iter:
+ retDatum = (Datum) ExecEvalIter((Iter *) expression,
+ econtext,
+ isNull,
+ isDone);
+ break;
+ case T_Aggreg:
+ retDatum = (Datum) ExecEvalAggreg((Aggreg *)expression,
+ econtext,
+ isNull);
+ break;
+ case T_ArrayRef:
+ retDatum = (Datum) ExecEvalArrayRef((ArrayRef *) expression,
+ econtext,
+ isNull,
+ isDone);
+ break;
+ case T_Expr: {
+ Expr *expr = (Expr *)expression;
+ switch (expr->opType) {
+ case OP_EXPR:
+ retDatum = (Datum) ExecEvalOper(expr, econtext, isNull);
+ break;
+ case FUNC_EXPR:
+ retDatum = (Datum) ExecEvalFunc(expr, econtext, isNull, isDone);
+ break;
+ case OR_EXPR:
+ retDatum = (Datum) ExecEvalOr(expr, econtext, isNull);
+ break;
+ case AND_EXPR:
+ retDatum = (Datum) ExecEvalAnd(expr, econtext, isNull);
+ break;
+ case NOT_EXPR:
+ retDatum = (Datum) ExecEvalNot(expr, econtext, isNull);
+ break;
+ default:
+ elog(WARN, "ExecEvalExpr: unknown expression type");
+ break;
+ }
+ break;
+ }
+ default:
+ elog(WARN, "ExecEvalExpr: unknown expression type");
+ break;
+ }
+
+ return retDatum;
+}
+
+/* ----------------------------------------------------------------
+ * ExecQual / ExecTargetList
+ * ----------------------------------------------------------------
+ */
+
+/* ----------------------------------------------------------------
+ * ExecQualClause
+ *
+ * this is a workhorse for ExecQual. ExecQual has to deal
+ * with a list of qualifications, so it passes each qualification
+ * in the list to this function one at a time. ExecQualClause
+ * returns true when the qualification *fails* and false if
+ * the qualification succeeded (meaning we have to test the
+ * rest of the qualification)
+ * ----------------------------------------------------------------
+ */
+bool
+ExecQualClause(Node *clause, ExprContext *econtext)
+{
+ Datum expr_value;
+ bool isNull;
+ bool isDone;
+
+ /* when there is a null clause, consider the qualification to be true */
+ if (clause == NULL)
+ return true;
+
+ /*
+ * pass isDone, but ignore it. We don't iterate over multiple
+ * returns in the qualifications.
+ */
+ expr_value = (Datum)
+ ExecEvalExpr(clause, econtext, &isNull, &isDone);
+
+ /* ----------------
+ * this is interesting behaviour here. When a clause evaluates
+ * to null, then we consider this as passing the qualification.
+ * it seems kind of like, if the qual is NULL, then there's no
+ * qual..
+ * ----------------
+ */
+ if (isNull)
+ return true;
+
+ /* ----------------
+ * remember, we return true when the qualification fails..
+ * ----------------
+ */
+ if (DatumGetInt32(expr_value) == 0)
+ return true;
+
+ return false;
+}
+
+/* ----------------------------------------------------------------
+ * ExecQual
+ *
+ * Evaluates a conjunctive boolean expression and returns t
+ * iff none of the subexpressions are false (or null).
+ * ----------------------------------------------------------------
+ */
+bool
+ExecQual(List *qual, ExprContext *econtext)
+{
+ List *clause;
+ bool result;
+
+ /* ----------------
+ * debugging stuff
+ * ----------------
+ */
+ EV_printf("ExecQual: qual is ");
+ EV_nodeDisplay(qual);
+ EV_printf("\n");
+
+ IncrProcessed();
+
+ /* ----------------
+ * return true immediately if no qual
+ * ----------------
+ */
+ if (qual == NIL)
+ return true;
+
+ /* ----------------
+ * a "qual" is a list of clauses. To evaluate the
+ * qual, we evaluate each of the clauses in the list.
+ *
+ * ExecQualClause returns true when we know the qualification
+ * *failed* so we just pass each clause in qual to it until
+ * we know the qual failed or there are no more clauses.
+ * ----------------
+ */
+ result = false;
+ foreach (clause, qual) {
+ result = ExecQualClause((Node *)lfirst(clause), econtext);
+ if (result == true)
+ break;
+ }
+
+ /* ----------------
+ * if result is true, then it means a clause failed so we
+ * return false. if result is false then it means no clause
+ * failed so we return true.
+ * ----------------
+ */
+ if (result == true)
+ return false;
+
+ return true;
+}
+
+int
+ExecTargetListLength(List *targetlist)
+{
+ int len;
+ List *tl;
+ TargetEntry *curTle;
+
+ len = 0;
+ foreach (tl, targetlist) {
+ curTle = lfirst(tl);
+
+ if (curTle->resdom != NULL)
+ len++;
+ else
+ len += curTle->fjoin->fj_nNodes;
+ }
+ return len;
+}
+
+/* ----------------------------------------------------------------
+ * ExecTargetList
+ *
+ * Evaluates a targetlist with respect to the current
+ * expression context and return a tuple.
+ * ----------------------------------------------------------------
+ */
+static HeapTuple
+ExecTargetList(List *targetlist,
+ int nodomains,
+ TupleDesc targettype,
+ Datum *values,
+ ExprContext *econtext,
+ bool *isDone)
+{
+ char nulls_array[64];
+ bool fjNullArray[64];
+ bool *fjIsNull;
+ char *null_head;
+ List *tl;
+ TargetEntry *tle;
+ Node *expr;
+ Resdom *resdom;
+ AttrNumber resind;
+ Datum constvalue;
+ HeapTuple newTuple;
+ bool isNull;
+
+ /* ----------------
+ * debugging stuff
+ * ----------------
+ */
+ EV_printf("ExecTargetList: tl is ");
+ EV_nodeDisplay(targetlist);
+ EV_printf("\n");
+
+ /* ----------------
+ * Return a dummy tuple if the targetlist is empty .
+ * the dummy tuple is necessary to differentiate
+ * between passing and failing the qualification.
+ * ----------------
+ */
+ if (targetlist == NIL) {
+ /* ----------------
+ * I now think that the only time this makes
+ * any sence is when we run a delete query. Then
+ * we need to return something other than nil
+ * so we know to delete the tuple associated
+ * with the saved tupleid.. see what ExecutePlan
+ * does with the returned tuple.. -cim 9/21/89
+ *
+ * It could also happen in queries like:
+ * retrieve (foo.all) where bar.a = 3
+ *
+ * is this a new phenomenon? it might cause bogus behavior
+ * if we try to free this tuple later!! I put a hook in
+ * ExecProject to watch out for this case -mer 24 Aug 1992
+ * ----------------
+ */
+ CXT1_printf("ExecTargetList: context is %d\n", CurrentMemoryContext);
+ *isDone = true;
+ return (HeapTuple) true;
+ }
+
+ /* ----------------
+ * allocate an array of char's to hold the "null" information
+ * only if we have a really large targetlist. otherwise we use
+ * the stack.
+ * ----------------
+ */
+ if (nodomains > 64) {
+ null_head = (char *) palloc(nodomains+1);
+ fjIsNull = (bool *) palloc(nodomains+1);
+ } else {
+ null_head = &nulls_array[0];
+ fjIsNull = &fjNullArray[0];
+ }
+
+ /* ----------------
+ * evaluate all the expressions in the target list
+ * ----------------
+ */
+ EV_printf("ExecTargetList: setting target list values\n");
+
+ *isDone = true;
+ foreach (tl, targetlist) {
+ /* ----------------
+ * remember, a target list is a list of lists:
+ *
+ * ((<resdom | fjoin> expr) (<resdom | fjoin> expr) ...)
+ *
+ * tl is a pointer to successive cdr's of the targetlist
+ * tle is a pointer to the target list entry in tl
+ * ----------------
+ */
+ tle = lfirst(tl);
+
+ if (tle->resdom != NULL) {
+ expr = tle->expr;
+ resdom = tle->resdom;
+ resind = resdom->resno - 1;
+ constvalue = (Datum) ExecEvalExpr(expr,
+ econtext,
+ &isNull,
+ isDone);
+
+ if ((IsA(expr,Iter)) && (*isDone))
+ return (HeapTuple)NULL;
+
+ values[resind] = constvalue;
+
+ if (!isNull)
+ null_head[resind] = ' ';
+ else
+ null_head[resind] = 'n';
+ }else {
+ int curNode;
+ Resdom *fjRes;
+ List *fjTlist = (List *)tle->expr;
+ Fjoin *fjNode = tle->fjoin;
+ int nNodes = fjNode->fj_nNodes;
+ DatumPtr results = fjNode->fj_results;
+
+ ExecEvalFjoin(tle, econtext, fjIsNull, isDone);
+ if (*isDone)
+ return (HeapTuple)NULL;
+
+ /*
+ * get the result from the inner node
+ */
+ fjRes = (Resdom *)fjNode->fj_innerNode;
+ resind = fjRes->resno - 1;
+ if (fjIsNull[0])
+ null_head[resind] = 'n';
+ else {
+ null_head[resind] = ' ';
+ values[resind] = results[0];
+ }
+
+ /*
+ * Get results from all of the outer nodes
+ */
+ for (curNode = 1;
+ curNode < nNodes;
+ curNode++, fjTlist = lnext(fjTlist))
+ {
+#if 0 /* what is this?? */
+ Node *outernode = lfirst(fjTlist);
+ fjRes = (Resdom *)outernode->iterexpr;
+#endif
+ resind = fjRes->resno - 1;
+ if (fjIsNull[curNode]) {
+ null_head[resind] = 'n';
+ }else {
+ null_head[resind] = ' ';
+ values[resind] = results[curNode];
+ }
+ }
+ }
+ }
+
+ /* ----------------
+ * form the new result tuple (in the "normal" context)
+ * ----------------
+ */
+ newTuple = (HeapTuple)
+ heap_formtuple(targettype, values, null_head);
+
+ /* ----------------
+ * free the nulls array if we allocated one..
+ * ----------------
+ */
+ if (nodomains > 64) pfree(null_head);
+
+ return
+ newTuple;
+}
+
+/* ----------------------------------------------------------------
+ * ExecProject
+ *
+ * projects a tuple based in projection info and stores
+ * it in the specified tuple table slot.
+ *
+ * Note: someday soon the executor can be extended to eliminate
+ * redundant projections by storing pointers to datums
+ * in the tuple table and then passing these around when
+ * possible. this should make things much quicker.
+ * -cim 6/3/91
+ * ----------------------------------------------------------------
+ */
+TupleTableSlot *
+ExecProject(ProjectionInfo *projInfo, bool *isDone)
+{
+ TupleTableSlot *slot;
+ List *targetlist;
+ int len;
+ TupleDesc tupType;
+ Datum *tupValue;
+ ExprContext *econtext;
+ HeapTuple newTuple;
+
+ /* ----------------
+ * sanity checks
+ * ----------------
+ */
+ if (projInfo == NULL)
+ return (TupleTableSlot *) NULL;
+
+ /* ----------------
+ * get the projection info we want
+ * ----------------
+ */
+ slot = projInfo->pi_slot;
+ targetlist = projInfo->pi_targetlist;
+ len = projInfo->pi_len;
+ tupType = slot->ttc_tupleDescriptor;
+
+ tupValue = projInfo->pi_tupValue;
+ econtext = projInfo->pi_exprContext;
+
+ if (targetlist == NIL) {
+ *isDone = true;
+ return (TupleTableSlot *) NULL;
+ }
+
+ /* ----------------
+ * form a new (result) tuple
+ * ----------------
+ */
+ newTuple = ExecTargetList(targetlist,
+ len,
+ tupType,
+ tupValue,
+ econtext,
+ isDone);
+
+ /* ----------------
+ * store the tuple in the projection slot and return the slot.
+ *
+ * If there's no projection target list we don't want to pfree
+ * the bogus tuple that ExecTargetList passes back to us.
+ * -mer 24 Aug 1992
+ * ----------------
+ */
+ return (TupleTableSlot *)
+ ExecStoreTuple(newTuple, /* tuple to store */
+ slot, /* slot to store in */
+ InvalidBuffer, /* tuple has no buffer */
+ true);
+}
+
diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c
new file mode 100644
index 00000000000..96dd5551289
--- /dev/null
+++ b/src/backend/executor/execScan.c
@@ -0,0 +1,136 @@
+/*-------------------------------------------------------------------------
+ *
+ * execScan.c--
+ * This code provides support for generalized relation scans. ExecScan
+ * is passed a node and a pointer to a function to "do the right thing"
+ * and return a tuple from the relation. ExecScan then does the tedious
+ * stuff - checking the qualification and projecting the tuple
+ * appropriately.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/executor/execScan.c,v 1.1.1.1 1996/07/09 06:21:25 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include <sys/file.h>
+#include "executor/executor.h"
+
+/* ----------------------------------------------------------------
+ * ExecScan
+ *
+ * Scans the relation using the 'access method' indicated and
+ * returns the next qualifying tuple in the direction specified
+ * in the global variable ExecDirection.
+ * The access method returns the next tuple and execScan() is
+ * responisble for checking the tuple returned against the qual-clause.
+ *
+ * Conditions:
+ * -- the "cursor" maintained by the AMI is positioned at the tuple
+ * returned previously.
+ *
+ * Initial States:
+ * -- the relation indicated is opened for scanning so that the
+ * "cursor" is positioned before the first qualifying tuple.
+ *
+ * May need to put startmmgr and endmmgr in here.
+ * ----------------------------------------------------------------
+ */
+TupleTableSlot *
+ExecScan(Scan *node,
+ TupleTableSlot* (*accessMtd)()) /* function returning a tuple */
+{
+ CommonScanState *scanstate;
+ EState *estate;
+ List *qual;
+ bool isDone;
+
+ TupleTableSlot *slot;
+ TupleTableSlot *resultSlot;
+ HeapTuple newTuple;
+
+ ExprContext *econtext;
+ ProjectionInfo *projInfo;
+
+
+ /* ----------------
+ * initialize misc variables
+ * ----------------
+ */
+ newTuple = NULL;
+ slot = NULL;
+
+ estate = node->plan.state;
+ scanstate = node->scanstate;
+
+ /* ----------------
+ * get the expression context
+ * ----------------
+ */
+ econtext = scanstate->cstate.cs_ExprContext;
+
+ /* ----------------
+ * initialize fields in ExprContext which don't change
+ * in the course of the scan..
+ * ----------------
+ */
+ qual = node->plan.qual;
+ econtext->ecxt_relation = scanstate->css_currentRelation;
+ econtext->ecxt_relid = node->scanrelid;
+
+ if (scanstate->cstate.cs_TupFromTlist) {
+ projInfo = scanstate->cstate.cs_ProjInfo;
+ resultSlot = ExecProject(projInfo, &isDone);
+ if (!isDone)
+ return resultSlot;
+ }
+ /*
+ * get a tuple from the access method
+ * loop until we obtain a tuple which passes the qualification.
+ */
+ for(;;) {
+ slot = (TupleTableSlot *) (*accessMtd)(node);
+
+ /* ----------------
+ * if the slot returned by the accessMtd contains
+ * NULL, then it means there is nothing more to scan
+ * so we just return the empty slot.
+ * ----------------
+ */
+ if (TupIsNull(slot)) return slot;
+
+ /* ----------------
+ * place the current tuple into the expr context
+ * ----------------
+ */
+ econtext->ecxt_scantuple = slot;
+
+ /* ----------------
+ * check that the current tuple satisfies the qual-clause
+ * if our qualification succeeds then we
+ * leave the loop.
+ * ----------------
+ */
+
+ /* add a check for non-nil qual here to avoid a
+ function call to ExecQual() when the qual is nil */
+ if (!qual || ExecQual(qual, econtext) == true)
+ break;
+ }
+
+ /* ----------------
+ * form a projection tuple, store it in the result tuple
+ * slot and return it.
+ * ----------------
+ */
+ projInfo = scanstate->cstate.cs_ProjInfo;
+
+ resultSlot = ExecProject(projInfo, &isDone);
+ scanstate->cstate.cs_TupFromTlist = !isDone;
+
+ return resultSlot;
+}
+
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
new file mode 100644
index 00000000000..8e7b5283dc6
--- /dev/null
+++ b/src/backend/executor/execTuples.c
@@ -0,0 +1,1013 @@
+/*-------------------------------------------------------------------------
+ *
+ * execTuples.c--
+ * Routines dealing with the executor tuple tables. These are used to
+ * ensure that the executor frees copies of tuples (made by
+ * ExecTargetList) properly.
+ *
+ * Routines dealing with the type information for tuples. Currently,
+ * the type information for a tuple is an array of FormData_pg_attribute.
+ * This information is needed by routines manipulating tuples
+ * (getattribute, formtuple, etc.).
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.1.1.1 1996/07/09 06:21:25 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+ * INTERFACE ROUTINES
+ *
+ * TABLE CREATE/DELETE
+ * ExecCreateTupleTable - create a new tuple table
+ * ExecDestroyTupleTable - destroy a table
+ *
+ * SLOT RESERVERATION
+ * ExecAllocTableSlot - find an available slot in the table
+ *
+ * SLOT ACCESSORS
+ * ExecStoreTuple - store a tuple in the table
+ * ExecFetchTuple - fetch a tuple from the table
+ * ExecClearTuple - clear contents of a table slot
+ * ExecSlotPolicy - return slot's tuple pfree policy
+ * ExecSetSlotPolicy - diddle the slot policy
+ * ExecSlotDescriptor - type of tuple in a slot
+ * ExecSetSlotDescriptor - set a slot's tuple descriptor
+ * ExecSetSlotDescriptorIsNew - diddle the slot-desc-is-new flag
+ * ExecSetNewSlotDescriptor - set a desc and the is-new-flag all at once
+ * ExecSlotBuffer - return buffer of tuple in slot
+ * ExecSetSlotBuffer - set the buffer for tuple in slot
+ * ExecIncrSlotBufferRefcnt - bump the refcnt of the slot buffer
+ *
+ * SLOT STATUS PREDICATES
+ * TupIsNull - true when slot contains no tuple
+ * ExecSlotDescriptorIsNew - true if we're now storing a different
+ * type of tuple in a slot
+ *
+ * CONVENIENCE INITIALIZATION ROUTINES
+ * ExecInitResultTupleSlot \ convience routines to initialize
+ * ExecInitScanTupleSlot \ the various tuple slots for nodes
+ * ExecInitMarkedTupleSlot / which store copies of tuples.
+ * ExecInitOuterTupleSlot /
+ * ExecInitHashTupleSlot /
+ *
+ * old routines:
+ * ExecGetTupType - get type of tuple returned by this node
+ * ExecTypeFromTL - form a TupleDesc from a target list
+ *
+ * EXAMPLE OF HOW TABLE ROUTINES WORK
+ * Suppose we have a query such as retrieve (EMP.name) and we have
+ * a single SeqScan node in the query plan.
+ *
+ * At ExecStart()
+ * ----------------
+ * - InitPlan() calls ExecCreateTupleTable() to create the tuple
+ * table which will hold tuples processed by the executor.
+ *
+ * - ExecInitSeqScan() calls ExecInitScanTupleSlot() and
+ * ExecInitResultTupleSlot() to reserve places in the tuple
+ * table for the tuples returned by the access methods and the
+ * tuples resulting from preforming target list projections.
+ *
+ * During ExecRun()
+ * ----------------
+ * - SeqNext() calls ExecStoreTuple() to place the tuple returned
+ * by the access methods into the scan tuple slot.
+ *
+ * - ExecSeqScan() calls ExecStoreTuple() to take the result
+ * tuple from ExecTargetList() and place it into the result tuple
+ * slot.
+ *
+ * - ExecutePlan() calls ExecRetrieve() which gets the tuple out of
+ * the slot passed to it by calling ExecFetchTuple(). this tuple
+ * is then returned.
+ *
+ * At ExecEnd()
+ * ----------------
+ * - EndPlan() calls ExecDestroyTupleTable() to clean up any remaining
+ * tuples left over from executing the query.
+ *
+ * The important thing to watch in the executor code is how pointers
+ * to the slots containing tuples are passed instead of the tuples
+ * themselves. This facilitates the communication of related information
+ * (such as whether or not a tuple should be pfreed, what buffer contains
+ * this tuple, the tuple's tuple descriptor, etc). Note that much of
+ * this information is also kept in the ExprContext of each node.
+ * Soon the executor will be redesigned and ExprContext's will contain
+ * only slot pointers. -cim 3/14/91
+ *
+ * NOTES
+ * The tuple table stuff is relatively new, put here to alleviate
+ * the process growth problems in the executor. The other routines
+ * are old (from the original lisp system) and may someday become
+ * obsolete. -cim 6/23/90
+ *
+ * In the implementation of nested-dot queries such as
+ * "retrieve (EMP.hobbies.all)", a single scan may return tuples
+ * of many types, so now we return pointers to tuple descriptors
+ * along with tuples returned via the tuple table. This means
+ * we now have a bunch of routines to diddle the slot descriptors
+ * too. -cim 1/18/90
+ *
+ * The tuple table stuff depends on the executor/tuptable.h macros,
+ * and the TupleTableSlot node in execnodes.h.
+ *
+ */
+
+#include "executor/executor.h"
+#undef ExecStoreTuple
+
+#include "access/tupdesc.h"
+#include "utils/palloc.h"
+#include "utils/lsyscache.h"
+#include "storage/bufmgr.h"
+#include "parser/catalog_utils.h"
+
+/* ----------------------------------------------------------------
+ * tuple table create/delete functions
+ * ----------------------------------------------------------------
+ */
+/* --------------------------------
+ * ExecCreateTupleTable
+ *
+ * This creates a new tuple table of the specified initial
+ * size. If the size is insufficient, ExecAllocTableSlot()
+ * will grow the table as necessary.
+ *
+ * This should be used by InitPlan() to allocate the table.
+ * The table's address will be stored in the EState structure.
+ * --------------------------------
+ */
+TupleTable /* return: address of table */
+ExecCreateTupleTable(int initialSize) /* initial number of slots in table */
+{
+ TupleTable newtable; /* newly allocated table */
+ TupleTableSlot* array; /* newly allocated slot array */
+
+ /* ----------------
+ * sanity checks
+ * ----------------
+ */
+ Assert(initialSize >= 1);
+
+ /* ----------------
+ * Now allocate our new table along with space for the pointers
+ * to the tuples.
+ */
+
+ newtable = (TupleTable) palloc(sizeof(TupleTableData));
+ array = (TupleTableSlot*) palloc(initialSize * sizeof(TupleTableSlot));
+
+ /* ----------------
+ * clean out the slots we just allocated
+ * ----------------
+ */
+ memset(array, 0, initialSize * sizeof(TupleTableSlot));
+
+ /* ----------------
+ * initialize the new table and return it to the caller.
+ * ----------------
+ */
+ newtable->size = initialSize;
+ newtable->next = 0;
+ newtable->array = array;
+
+ return newtable;
+}
+
+/* --------------------------------
+ * ExecDestroyTupleTable
+ *
+ * This pfrees the storage assigned to the tuple table and
+ * optionally pfrees the contents of the table also.
+ * It is expected that this routine be called by EndPlan().
+ * --------------------------------
+ */
+void
+ExecDestroyTupleTable(TupleTable table, /* tuple table */
+ bool shouldFree) /* true if we should free slot contents */
+{
+ int next; /* next avaliable slot */
+ TupleTableSlot *array; /* start of table array */
+ int i; /* counter */
+
+ /* ----------------
+ * sanity checks
+ * ----------------
+ */
+ Assert(table != NULL);
+
+ /* ----------------
+ * get information from the table
+ * ----------------
+ */
+ array = table->array;
+ next = table->next;
+
+ /* ----------------
+ * first free all the valid pointers in the tuple array
+ * if that's what the caller wants..
+ *
+ * Note: we do nothing about the Buffer and Tuple Descriptor's
+ * we store in the slots. This may have to change (ex: we should
+ * probably worry about pfreeing tuple descs too) -cim 3/14/91
+ * ----------------
+ */
+ if (shouldFree)
+ for (i = 0; i < next; i++) {
+ TupleTableSlot slot;
+ HeapTuple tuple;
+
+ slot = array[i];
+ tuple = slot.val;
+
+ if (tuple != NULL) {
+ slot.val = (HeapTuple)NULL;
+ if (slot.ttc_shouldFree) {
+ /* ----------------
+ * since a tuple may contain a pointer to
+ * lock information allocated along with the
+ * tuple, we have to be careful to free any
+ * rule locks also -cim 1/17/90
+ * ----------------
+ */
+ pfree(tuple);
+ }
+ }
+ }
+
+ /* ----------------
+ * finally free the tuple array and the table itself.
+ * ----------------
+ */
+ pfree(array);
+ pfree(table);
+
+}
+
+
+/* ----------------------------------------------------------------
+ * tuple table slot reservation functions
+ * ----------------------------------------------------------------
+ */
+/* --------------------------------
+ * ExecAllocTableSlot
+ *
+ * This routine is used to reserve slots in the table for
+ * use by the various plan nodes. It is expected to be
+ * called by the node init routines (ex: ExecInitNestLoop).
+ * once per slot needed by the node. Not all nodes need
+ * slots (some just pass tuples around).
+ * --------------------------------
+ */
+TupleTableSlot* /* return: the slot allocated in the tuple table */
+ExecAllocTableSlot(TupleTable table)
+{
+ int slotnum; /* new slot number */
+
+ /* ----------------
+ * sanity checks
+ * ----------------
+ */
+ Assert(table != NULL);
+
+ /* ----------------
+ * if our table is full we have to allocate a larger
+ * size table. Since ExecAllocTableSlot() is only called
+ * before the table is ever used to store tuples, we don't
+ * have to worry about the contents of the old table.
+ * If this changes, then we will have to preserve the contents.
+ * -cim 6/23/90
+ *
+ * Unfortunately, we *cannot* do this. All of the nodes in
+ * the plan that have already initialized their slots will have
+ * pointers into _freed_ memory. This leads to bad ends. We
+ * now count the number of slots we will need and create all the
+ * slots we will need ahead of time. The if below should never
+ * happen now. Give a WARN if it does. -mer 4 Aug 1992
+ * ----------------
+ */
+ if (table->next >= table->size) {
+ /*
+ * int newsize = NewTableSize(table->size);
+ *
+ * pfree(table->array);
+ * table->array = (Pointer) palloc(newsize * TableSlotSize);
+ * bzero(table->array, newsize * TableSlotSize);
+ * table->size = newsize;
+ */
+ elog(NOTICE, "Plan requires more slots than are available");
+ elog(WARN, "send mail to your local executor guru to fix this");
+ }
+
+ /* ----------------
+ * at this point, space in the table is guaranteed so we
+ * reserve the next slot, initialize and return it.
+ * ----------------
+ */
+ slotnum = table->next;
+ table->next++;
+
+ table->array[slotnum].type = T_TupleTableSlot;
+
+ return &(table->array[slotnum]);
+}
+
+/* ----------------------------------------------------------------
+ * tuple table slot accessor functions
+ * ----------------------------------------------------------------
+ */
+
+/* --------------------------------
+ * ExecStoreTuple
+ *
+ * This function is used to store a tuple into a specified
+ * slot in the tuple table. Note: the only slots which should
+ * be called with shouldFree == false are those slots used to
+ * store tuples not allocated with pfree(). Currently the
+ * seqscan and indexscan nodes use this for the tuples returned
+ * by amgetattr, which are actually pointers onto disk pages.
+ * --------------------------------
+ */
+TupleTableSlot* /* return: slot passed */
+ExecStoreTuple(HeapTuple tuple, /* tuple to store */
+ TupleTableSlot* slot, /* slot in which to store tuple */
+ Buffer buffer, /* buffer associated with tuple */
+ bool shouldFree) /* true if we call pfree() when we gc. */
+{
+ /* ----------------
+ * sanity checks
+ * ----------------
+ */
+ Assert(slot != NULL);
+
+ /* clear out the slot first */
+ ExecClearTuple(slot);
+
+ /* ----------------
+ * store the new tuple into the specified slot and
+ * return the slot into which we stored the tuple.
+ * ----------------
+ */
+ slot->val = tuple;
+ slot->ttc_buffer = buffer;
+ slot->ttc_shouldFree = shouldFree;
+
+ return slot;
+}
+
+/* --------------------------------
+ * ExecClearTuple
+ *
+ * This function is used to clear out a slot in the tuple table.
+ * --------------------------------
+ */
+TupleTableSlot* /* return: slot passed */
+ExecClearTuple(TupleTableSlot* slot) /* slot in which to store tuple */
+{
+ HeapTuple oldtuple; /* prior contents of slot */
+
+ /* ----------------
+ * sanity checks
+ * ----------------
+ */
+ Assert(slot != NULL);
+
+ /* ----------------
+ * get information from the tuple table
+ * ----------------
+ */
+ oldtuple = slot->val;
+
+ /* ----------------
+ * free the old contents of the specified slot if necessary.
+ * ----------------
+ */
+ if (slot->ttc_shouldFree && oldtuple != NULL) {
+ /* ----------------
+ * since a tuple may contain a pointer to
+ * lock information allocated along with the
+ * tuple, we have to be careful to free any
+ * rule locks also -cim 1/17/90
+ * ----------------
+ */
+ pfree(oldtuple);
+ }
+
+ /* ----------------
+ * store NULL into the specified slot and return the slot.
+ * - also set buffer to InvalidBuffer -cim 3/14/91
+ * ----------------
+ */
+ slot->val = (HeapTuple)NULL;
+
+ if (BufferIsValid(slot->ttc_buffer))
+ ReleaseBuffer(slot->ttc_buffer);
+
+ slot->ttc_buffer = InvalidBuffer;
+ slot->ttc_shouldFree = true;
+
+ return slot;
+}
+
+
+/* --------------------------------
+ * ExecSlotPolicy
+ *
+ * This function is used to get the call/don't call pfree
+ * setting of a slot. Most executor routines don't need this.
+ * It's only when you do tricky things like marking tuples for
+ * merge joins that you need to diddle the slot policy.
+ * --------------------------------
+ */
+bool /* return: slot policy */
+ExecSlotPolicy(TupleTableSlot* slot) /* slot to inspect */
+{
+ return slot->ttc_shouldFree;
+}
+
+/* --------------------------------
+ * ExecSetSlotPolicy
+ *
+ * This function is used to change the call/don't call pfree
+ * setting of a slot. Most executor routines don't need this.
+ * It's only when you do tricky things like marking tuples for
+ * merge joins that you need to diddle the slot policy.
+ * --------------------------------
+ */
+bool /* return: old slot policy */
+ExecSetSlotPolicy(TupleTableSlot* slot, /* slot to change */
+ bool shouldFree) /* true if we call pfree() when we gc. */
+{
+ bool old_shouldFree = slot->ttc_shouldFree;
+ slot->ttc_shouldFree = shouldFree;
+
+ return old_shouldFree;
+}
+
+/* --------------------------------
+ * ExecSlotDescriptor
+ *
+ * This function is used to get the tuple descriptor associated
+ * with the slot's tuple.
+ *
+ * Now a macro in tuptable.h -mer 5 March 1992
+ * --------------------------------
+ */
+
+/* --------------------------------
+ * ExecSetSlotDescriptor
+ *
+ * This function is used to set the tuple descriptor associated
+ * with the slot's tuple.
+ * --------------------------------
+ */
+TupleDesc /* return: old slot tuple descriptor */
+ExecSetSlotDescriptor(TupleTableSlot *slot, /* slot to change */
+ TupleDesc tupdesc) /* tuple descriptor */
+{
+ TupleDesc old_tupdesc = slot->ttc_tupleDescriptor;
+
+ slot->ttc_tupleDescriptor = tupdesc;
+ return old_tupdesc;
+}
+
+/* --------------------------------
+ * ExecSetSlotDescriptorIsNew
+ *
+ * This function is used to change the setting of the "isNew" flag
+ * --------------------------------
+ */
+void
+ExecSetSlotDescriptorIsNew(TupleTableSlot *slot,/* slot to change */
+ bool isNew) /* "isNew" setting */
+{
+ slot->ttc_descIsNew = isNew;
+}
+
+/* --------------------------------
+ * ExecSetNewSlotDescriptor
+ *
+ * This function is used to set the tuple descriptor associated
+ * with the slot's tuple, and set the "isNew" flag at the same time.
+ * --------------------------------
+ */
+TupleDesc /* return: old slot tuple descriptor */
+ExecSetNewSlotDescriptor(TupleTableSlot *slot, /* slot to change */
+ TupleDesc tupdesc) /* tuple descriptor */
+{
+ TupleDesc old_tupdesc = slot->ttc_tupleDescriptor;
+ slot->ttc_tupleDescriptor = tupdesc;
+ slot->ttc_descIsNew = true;
+
+ return old_tupdesc;
+}
+
+/* --------------------------------
+ * ExecSlotBuffer
+ *
+ * This function is used to get the tuple descriptor associated
+ * with the slot's tuple. Be very careful with this as it does not
+ * balance the reference counts. If the buffer returned is stored
+ * someplace else, then also use ExecIncrSlotBufferRefcnt().
+ *
+ * Now a macro in tuptable.h
+ * --------------------------------
+ */
+
+/* --------------------------------
+ * ExecSetSlotBuffer
+ *
+ * This function is used to set the tuple descriptor associated
+ * with the slot's tuple. Be very careful with this as it does not
+ * balance the reference counts. If we're using this then we should
+ * also use ExecIncrSlotBufferRefcnt().
+ * --------------------------------
+ */
+Buffer /* return: old slot buffer */
+ExecSetSlotBuffer(TupleTableSlot *slot, /* slot to change */
+ Buffer b) /* tuple descriptor */
+{
+ Buffer oldb = slot->ttc_buffer;
+ slot->ttc_buffer = b;
+
+ return oldb;
+}
+
+/* --------------------------------
+ * ExecIncrSlotBufferRefcnt
+ *
+ * When we pass around buffers in the tuple table, we have to
+ * be careful to increment reference counts appropriately.
+ * This is used mainly in the mergejoin code.
+ * --------------------------------
+ */
+void
+ExecIncrSlotBufferRefcnt(TupleTableSlot *slot) /* slot to bump refcnt */
+{
+/* Buffer b = SlotBuffer((TupleTableSlot*) slot); */
+ Buffer b = slot->ttc_buffer;
+ if (BufferIsValid(b))
+ IncrBufferRefCount(b);
+}
+
+/* ----------------------------------------------------------------
+ * tuple table slot status predicates
+ * ----------------------------------------------------------------
+ */
+
+/* ----------------
+ * TupIsNull
+ *
+ * This is used mainly to detect when there are no more
+ * tuples to process.
+ * ----------------
+ */
+bool /* return: true if tuple in slot is NULL */
+TupIsNull(TupleTableSlot* slot) /* slot to check */
+{
+ HeapTuple tuple; /* contents of slot (returned) */
+
+ /* ----------------
+ * if the slot itself is null then we return true
+ * ----------------
+ */
+ if (slot == NULL)
+ return true;
+
+ /* ----------------
+ * get information from the slot and return true or
+ * false depending on the contents of the slot.
+ * ----------------
+ */
+ tuple = slot->val;
+
+ return
+ (tuple == NULL ? true : false);
+}
+
+/* --------------------------------
+ * ExecSlotDescriptorIsNew
+ *
+ * This function is used to check if the tuple descriptor
+ * associated with this slot has just changed. ie: we are
+ * now storing a new type of tuple in this slot
+ * --------------------------------
+ */
+bool /* return: descriptor "is new" */
+ExecSlotDescriptorIsNew(TupleTableSlot *slot) /* slot to inspect */
+{
+/* bool isNew = SlotTupleDescriptorIsNew((TupleTableSlot*) slot);
+ return isNew; */
+ return slot->ttc_descIsNew;
+}
+
+/* ----------------------------------------------------------------
+ * convenience initialization routines
+ * ----------------------------------------------------------------
+ */
+/* --------------------------------
+ * ExecInit{Result,Scan,Raw,Marked,Outer,Hash}TupleSlot
+ *
+ * These are convenience routines to initialize the specfied slot
+ * in nodes inheriting the appropriate state.
+ * --------------------------------
+ */
+#define INIT_SLOT_DEFS \
+ TupleTable tupleTable; \
+ TupleTableSlot* slot
+
+#define INIT_SLOT_ALLOC \
+ tupleTable = (TupleTable) estate->es_tupleTable; \
+ slot = ExecAllocTableSlot(tupleTable); \
+ slot->val = (HeapTuple)NULL; \
+ slot->ttc_shouldFree = true; \
+ slot->ttc_tupleDescriptor = (TupleDesc)NULL; \
+ slot->ttc_whichplan = -1;\
+ slot->ttc_descIsNew = true;
+
+/* ----------------
+ * ExecInitResultTupleSlot
+ * ----------------
+ */
+void
+ExecInitResultTupleSlot(EState *estate, CommonState *commonstate)
+{
+ INIT_SLOT_DEFS;
+ INIT_SLOT_ALLOC;
+ commonstate->cs_ResultTupleSlot = (TupleTableSlot *) slot;
+}
+
+/* ----------------
+ * ExecInitScanTupleSlot
+ * ----------------
+ */
+void
+ExecInitScanTupleSlot(EState *estate, CommonScanState *commonscanstate)
+{
+ INIT_SLOT_DEFS;
+ INIT_SLOT_ALLOC;
+ commonscanstate->css_ScanTupleSlot = (TupleTableSlot *)slot;
+}
+
+/* ----------------
+ * ExecInitMarkedTupleSlot
+ * ----------------
+ */
+void
+ExecInitMarkedTupleSlot(EState *estate, MergeJoinState *mergestate)
+{
+ INIT_SLOT_DEFS;
+ INIT_SLOT_ALLOC;
+ mergestate->mj_MarkedTupleSlot = (TupleTableSlot *) slot;
+}
+
+/* ----------------
+ * ExecInitOuterTupleSlot
+ * ----------------
+ */
+void
+ExecInitOuterTupleSlot(EState *estate, HashJoinState *hashstate)
+{
+ INIT_SLOT_DEFS;
+ INIT_SLOT_ALLOC;
+ hashstate->hj_OuterTupleSlot = slot;
+}
+
+/* ----------------
+ * ExecInitHashTupleSlot
+ * ----------------
+ */
+void
+ExecInitHashTupleSlot(EState *estate, HashJoinState *hashstate)
+{
+ INIT_SLOT_DEFS;
+ INIT_SLOT_ALLOC;
+ hashstate->hj_HashTupleSlot = slot;
+}
+
+TupleTableSlot *
+NodeGetResultTupleSlot(Plan *node)
+{
+ TupleTableSlot *slot;
+
+ switch(nodeTag(node)) {
+
+ case T_Result:
+ {
+ ResultState *resstate = ((Result *)node)->resstate;
+ slot = resstate->cstate.cs_ResultTupleSlot;
+ }
+ break;
+
+ case T_SeqScan:
+ {
+ CommonScanState *scanstate = ((SeqScan *)node)->scanstate;
+ slot = scanstate->cstate.cs_ResultTupleSlot;
+ }
+ break;
+
+ case T_NestLoop:
+ {
+ NestLoopState *nlstate = ((NestLoop *)node)->nlstate;
+ slot = nlstate->jstate.cs_ResultTupleSlot;
+ }
+ break;
+
+ case T_Append:
+ {
+ Append *n = (Append *)node;
+ AppendState *unionstate;
+ List *unionplans;
+ int whichplan;
+ Plan *subplan;
+
+ unionstate = n->unionstate;
+ unionplans = n->unionplans;
+ whichplan = unionstate->as_whichplan;
+
+ subplan = (Plan*) nth(whichplan, unionplans);
+ slot = NodeGetResultTupleSlot(subplan);
+ break;
+ }
+
+ case T_IndexScan:
+ {
+ CommonScanState *scanstate = ((IndexScan *)node)->scan.scanstate;
+ slot = scanstate->cstate.cs_ResultTupleSlot;
+ }
+ break;
+
+ case T_Material:
+ {
+ MaterialState *matstate = ((Material *)node)->matstate;
+ slot = matstate->csstate.css_ScanTupleSlot;
+ }
+ break;
+
+ case T_Sort:
+ {
+ SortState *sortstate = ((Sort *)node)->sortstate;
+ slot = sortstate->csstate.css_ScanTupleSlot;
+ }
+ break;
+
+ case T_Agg:
+ {
+ AggState *aggstate = ((Agg *)node)->aggstate;
+ slot = aggstate->csstate.cstate.cs_ResultTupleSlot;
+ }
+ break;
+
+ case T_Group:
+ {
+ GroupState *grpstate = ((Group *)node)->grpstate;
+ slot = grpstate->csstate.cstate.cs_ResultTupleSlot;
+ }
+ break;
+
+ case T_Hash:
+ {
+ HashState *hashstate = ((Hash *)node)->hashstate;
+ slot = hashstate->cstate.cs_ResultTupleSlot;
+ }
+ break;
+
+ case T_Unique:
+ {
+ UniqueState *uniquestate = ((Unique *)node)->uniquestate;
+ slot = uniquestate->cs_ResultTupleSlot;
+ }
+ break;
+
+ case T_MergeJoin:
+ {
+ MergeJoinState *mergestate = ((MergeJoin *)node)->mergestate;
+ slot = mergestate->jstate.cs_ResultTupleSlot;
+ }
+ break;
+
+ case T_HashJoin:
+ {
+ HashJoinState *hashjoinstate = ((HashJoin *)node)->hashjoinstate;
+ slot = hashjoinstate->jstate.cs_ResultTupleSlot;
+ }
+ break;
+
+ case T_Tee:
+ {
+ TeeState *teestate = ((Tee*)node)->teestate;
+ slot = teestate->cstate.cs_ResultTupleSlot;
+ }
+ break;
+
+ default:
+ /* ----------------
+ * should never get here
+ * ----------------
+ */
+ elog(WARN, "NodeGetResultTupleSlot: node not yet supported: %d ",
+ nodeTag(node));
+
+ return NULL;
+ }
+ return slot;
+}
+
+/* ----------------------------------------------------------------
+ * ExecGetTupType
+ *
+ * this gives you the tuple descriptor for tuples returned
+ * by this node. I really wish I could ditch this routine,
+ * but since not all nodes store their type info in the same
+ * place, we have to do something special for each node type.
+ *
+ * Soon, the system will have to adapt to deal with changing
+ * tuple descriptors as we deal with dynamic tuple types
+ * being returned from procedure nodes. Perhaps then this
+ * routine can be retired. -cim 6/3/91
+ *
+ * old comments
+ * This routine just gets the type information out of the
+ * node's state. If you already have a node's state, you
+ * can get this information directly, but this is a useful
+ * routine if you want to get the type information from
+ * the node's inner or outer subplan easily without having
+ * to inspect the subplan.. -cim 10/16/89
+ *
+ * Assume that for existential nodes, we get the targetlist out
+ * of the right node's targetlist
+ * ----------------------------------------------------------------
+ */
+
+TupleDesc
+ExecGetTupType(Plan *node)
+{
+ TupleTableSlot *slot;
+ TupleDesc tupType;
+
+ if (node == NULL)
+ return NULL;
+
+ slot = NodeGetResultTupleSlot(node);
+ tupType = slot->ttc_tupleDescriptor;
+ return tupType;
+}
+
+/*
+TupleDesc
+ExecCopyTupType(TupleDesc td, int natts)
+{
+ TupleDesc newTd;
+ int i;
+
+ newTd = CreateTemplateTupleDesc(natts);
+ i = 0;
+ while (i < natts)
+ {
+ newTd[i] =
+ (AttributeTupleForm)palloc(sizeof(FormData_pg_attribute));
+ memmove(newTd[i], td[i], sizeof(FormData_pg_attribute));
+ i++;
+ }
+ return newTd;
+}
+*/
+
+/* ----------------------------------------------------------------
+ * ExecTypeFromTL
+ *
+ * Currently there are about 4 different places where we create
+ * TupleDescriptors. They should all be merged, or perhaps
+ * be rewritten to call BuildDesc().
+ *
+ * old comments
+ * Forms attribute type info from the target list in the node.
+ * It assumes all domains are individually specified in the target list.
+ * It fails if the target list contains something like Emp.all
+ * which represents all the attributes from EMP relation.
+ *
+ * Conditions:
+ * The inner and outer subtrees should be initialized because it
+ * might be necessary to know the type infos of the subtrees.
+ * ----------------------------------------------------------------
+ */
+TupleDesc
+ExecTypeFromTL(List *targetList)
+{
+ List *tlcdr;
+ TupleDesc typeInfo;
+ Resdom *resdom;
+ Oid restype;
+ int len;
+
+ /* ----------------
+ * examine targetlist - if empty then return NULL
+ * ----------------
+ */
+ len = ExecTargetListLength(targetList);
+
+ if (len == 0)
+ return NULL;
+
+ /* ----------------
+ * allocate a new typeInfo
+ * ----------------
+ */
+ typeInfo = CreateTemplateTupleDesc(len);
+
+ /* ----------------
+ * notes: get resdom from (resdom expr)
+ * get_typbyval comes from src/lib/l-lisp/lsyscache.c
+ * ----------------
+ */
+ tlcdr = targetList;
+ while (tlcdr != NIL) {
+ TargetEntry *tle = lfirst(tlcdr);
+ if (tle->resdom != NULL) {
+ resdom = tle->resdom;
+ restype = resdom->restype;
+
+ TupleDescInitEntry(typeInfo,
+ resdom->resno,
+ resdom->resname,
+ get_id_typname(restype),
+ 0,
+ false);
+
+/*
+ ExecSetTypeInfo(resdom->resno - 1,
+ typeInfo,
+ (Oid) restype,
+ resdom->resno,
+ resdom->reslen,
+ resdom->resname->data,
+ get_typbyval(restype),
+ get_typalign(restype));
+*/
+ }
+ else {
+ Resdom *fjRes;
+ List *fjTlistP;
+ List *fjList = lfirst(tlcdr);
+#ifdef SETS_FIXED
+ TargetEntry *tle;
+ Fjoin *fjNode = ((TargetEntry *)lfirst(fjList))->fjoin;
+
+ tle = fjNode->fj_innerNode; /* ??? */
+#endif
+ fjRes = tle->resdom;
+ restype = fjRes->restype;
+
+ TupleDescInitEntry(typeInfo,
+ fjRes->resno,
+ fjRes->resname,
+ get_id_typname(restype),
+ 0,
+ false);
+/*
+ ExecSetTypeInfo(fjRes->resno - 1,
+ typeInfo,
+ (Oid) restype,
+ fjRes->resno,
+ fjRes->reslen,
+ (char *) fjRes->resname,
+ get_typbyval(restype),
+ get_typalign(restype));
+*/
+
+ foreach(fjTlistP, lnext(fjList)) {
+ TargetEntry *fjTle = lfirst(fjTlistP);
+
+ fjRes = fjTle->resdom;
+
+ TupleDescInitEntry(typeInfo,
+ fjRes->resno,
+ fjRes->resname,
+ get_id_typname(restype),
+ 0,
+ false);
+
+/*
+ ExecSetTypeInfo(fjRes->resno - 1,
+ typeInfo,
+ (Oid) fjRes->restype,
+ fjRes->resno,
+ fjRes->reslen,
+ (char *) fjRes->resname,
+ get_typbyval(fjRes->restype),
+ get_typalign(fjRes->restype));
+*/
+ }
+ }
+
+ tlcdr = lnext(tlcdr);
+ }
+
+ return typeInfo;
+}
+
+
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
new file mode 100644
index 00000000000..8d1108aca25
--- /dev/null
+++ b/src/backend/executor/execUtils.c
@@ -0,0 +1,1092 @@
+/*-------------------------------------------------------------------------
+ *
+ * execUtils.c--
+ * miscellanious executor utility routines
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.1.1.1 1996/07/09 06:21:25 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+ * INTERFACE ROUTINES
+ * ExecAssignNodeBaseInfo \
+ * ExecAssignDebugHooks > preforms misc work done in all the
+ * ExecAssignExprContext / init node routines.
+ *
+ * ExecGetTypeInfo | old execCStructs interface
+ * ExecMakeTypeInfo | code from the version 1
+ * ExecOrderTypeInfo | lisp system. These should
+ * ExecSetTypeInfo | go away or be updated soon.
+ * ExecFreeTypeInfo | -cim 11/1/89
+ * ExecTupleAttributes /
+ *
+
+ * QueryDescGetTypeInfo - moved here from main.c
+ * am not sure what uses it -cim 10/12/89
+ *
+ * ExecGetIndexKeyInfo \
+ * ExecOpenIndices | referenced by InitPlan, EndPlan,
+ * ExecCloseIndices | ExecAppend, ExecReplace
+ * ExecFormIndexTuple |
+ * ExecInsertIndexTuple /
+ *
+ * NOTES
+ * This file has traditionally been the place to stick misc.
+ * executor support stuff that doesn't really go anyplace else.
+ *
+ */
+
+#include "executor/executor.h"
+#include "access/itup.h"
+#include "optimizer/clauses.h"
+#include "utils/palloc.h"
+#include "commands/command.h"
+#include "catalog/index.h"
+
+/* ----------------------------------------------------------------
+ * global counters for number of tuples processed, retrieved,
+ * appended, replaced, deleted.
+ * ----------------------------------------------------------------
+ */
+int NTupleProcessed;
+int NTupleRetrieved;
+int NTupleReplaced;
+int NTupleAppended;
+int NTupleDeleted;
+int NIndexTupleInserted;
+extern int NIndexTupleProcessed; /* have to be defined in the access
+ method level so that the cinterface.a
+ will link ok. */
+
+/* ----------------------------------------------------------------
+ * statistic functions
+ * ----------------------------------------------------------------
+ */
+
+/* ----------------------------------------------------------------
+ * ResetTupleCount
+ * ----------------------------------------------------------------
+ */
+void
+ResetTupleCount()
+{
+ NTupleProcessed = 0;
+ NTupleRetrieved = 0;
+ NTupleAppended = 0;
+ NTupleDeleted = 0;
+ NTupleReplaced = 0;
+ NIndexTupleProcessed = 0;
+}
+
+/* ----------------------------------------------------------------
+ * PrintTupleCount
+ * ----------------------------------------------------------------
+ */
+void
+DisplayTupleCount(FILE *statfp)
+{
+ if (NTupleProcessed > 0)
+ fprintf(statfp, "!\t%d tuple%s processed, ", NTupleProcessed,
+ (NTupleProcessed == 1) ? "" : "s");
+ else {
+ fprintf(statfp, "!\tno tuples processed.\n");
+ return;
+ }
+ if (NIndexTupleProcessed > 0)
+ fprintf(statfp, "%d indextuple%s processed, ", NIndexTupleProcessed,
+ (NIndexTupleProcessed == 1) ? "" : "s");
+ if (NIndexTupleInserted > 0)
+ fprintf(statfp, "%d indextuple%s inserted, ", NIndexTupleInserted,
+ (NIndexTupleInserted == 1) ? "" : "s");
+ if (NTupleRetrieved > 0)
+ fprintf(statfp, "%d tuple%s retrieved. ", NTupleRetrieved,
+ (NTupleRetrieved == 1) ? "" : "s");
+ if (NTupleAppended > 0)
+ fprintf(statfp, "%d tuple%s appended. ", NTupleAppended,
+ (NTupleAppended == 1) ? "" : "s");
+ if (NTupleDeleted > 0)
+ fprintf(statfp, "%d tuple%s deleted. ", NTupleDeleted,
+ (NTupleDeleted == 1) ? "" : "s");
+ if (NTupleReplaced > 0)
+ fprintf(statfp, "%d tuple%s replaced. ", NTupleReplaced,
+ (NTupleReplaced == 1) ? "" : "s");
+ fprintf(statfp, "\n");
+}
+
+/* ----------------------------------------------------------------
+ * miscellanious init node support functions
+ *
+ * ExecAssignNodeBaseInfo - assigns the baseid field of the node
+ * ExecAssignDebugHooks - assigns the node's debugging hooks
+ * ExecAssignExprContext - assigns the node's expression context
+ * ----------------------------------------------------------------
+ */
+
+/* ----------------
+ * ExecAssignNodeBaseInfo
+ *
+ * as it says, this assigns the baseid field of the node and
+ * increments the counter in the estate. In addition, it initializes
+ * the base_parent field of the basenode.
+ * ----------------
+ */
+void
+ExecAssignNodeBaseInfo(EState *estate, CommonState *cstate, Plan *parent)
+{
+ int baseId;
+
+ baseId = estate->es_BaseId;
+ cstate->cs_base_id = baseId;
+ estate->es_BaseId = baseId + 1;
+}
+
+/* ----------------
+ * ExecAssignExprContext
+ *
+ * This initializes the ExprContext field. It is only necessary
+ * to do this for nodes which use ExecQual or ExecTargetList
+ * because those routines depend on econtext. Other nodes which
+ * dont have to evaluate expressions don't need to do this.
+ * ----------------
+ */
+void
+ExecAssignExprContext(EState *estate, CommonState *commonstate)
+{
+ ExprContext *econtext;
+ ParamListInfo paraminfo;
+ List *rangeTable;
+
+ paraminfo = estate->es_param_list_info;
+ rangeTable = estate->es_range_table;
+
+ econtext = makeNode(ExprContext);
+ econtext->ecxt_scantuple = NULL; /* scan tuple slot */
+ econtext->ecxt_innertuple = NULL; /* inner tuple slot */
+ econtext->ecxt_outertuple = NULL; /* outer tuple slot */
+ econtext->ecxt_relation = NULL; /* relation */
+ econtext->ecxt_relid = 0; /* relid */
+ econtext->ecxt_param_list_info = paraminfo; /* param list info */
+ econtext->ecxt_range_table = rangeTable; /* range table */
+
+ commonstate->cs_ExprContext = econtext;
+}
+
+/* ----------------------------------------------------------------
+ * Result slot tuple type and ProjectionInfo support
+ * ----------------------------------------------------------------
+ */
+
+/* ----------------
+ * ExecAssignResultType
+ * ----------------
+ */
+void
+ExecAssignResultType(CommonState *commonstate,
+ TupleDesc tupDesc)
+{
+ TupleTableSlot *slot;
+
+ slot = commonstate->cs_ResultTupleSlot;
+ slot->ttc_tupleDescriptor = tupDesc;
+}
+
+/* ----------------
+ * ExecAssignResultTypeFromOuterPlan
+ * ----------------
+ */
+void
+ExecAssignResultTypeFromOuterPlan(Plan *node, CommonState *commonstate)
+{
+ Plan *outerPlan;
+ TupleDesc tupDesc;
+
+ outerPlan = outerPlan(node);
+ tupDesc = ExecGetTupType(outerPlan);
+
+ ExecAssignResultType(commonstate, tupDesc);
+}
+
+/* ----------------
+ * ExecAssignResultTypeFromTL
+ * ----------------
+ */
+void
+ExecAssignResultTypeFromTL(Plan *node, CommonState *commonstate)
+{
+ List *targetList;
+ int i;
+ int len;
+ List *tl;
+ TargetEntry *tle;
+ List *fjtl;
+ TupleDesc origTupDesc;
+
+ targetList = node->targetlist;
+ origTupDesc = ExecTypeFromTL(targetList);
+ len = ExecTargetListLength(targetList);
+
+ fjtl = NIL;
+ tl = targetList;
+ i = 0;
+ while (tl != NIL || fjtl != NIL) {
+ if (fjtl != NIL) {
+ tle = lfirst(fjtl);
+ fjtl = lnext(fjtl);
+ }
+ else {
+ tle = lfirst(tl);
+ tl = lnext(tl);
+ }
+#ifdef SETS_FIXED
+ if (!tl_is_resdom(tle)) {
+ Fjoin *fj = (Fjoin *)lfirst(tle);
+ /* it is a FJoin */
+ fjtl = lnext(tle);
+ tle = fj->fj_innerNode;
+ }
+#endif
+ i++;
+ }
+ if (len > 0) {
+ ExecAssignResultType(commonstate,
+ origTupDesc);
+ }
+ else
+ ExecAssignResultType(commonstate,
+ (TupleDesc)NULL);
+}
+
+/* ----------------
+ * ExecGetResultType
+ * ----------------
+ */
+TupleDesc
+ExecGetResultType(CommonState *commonstate)
+{
+ TupleTableSlot *slot = commonstate->cs_ResultTupleSlot;
+
+ return slot->ttc_tupleDescriptor;
+}
+
+/* ----------------
+ * ExecFreeResultType
+ * ----------------
+ */
+void
+ExecFreeResultType(CommonState *commonstate)
+{
+ TupleTableSlot *slot;
+ TupleDesc tupType;
+
+ slot = commonstate->cs_ResultTupleSlot;
+ tupType = slot->ttc_tupleDescriptor;
+
+/* ExecFreeTypeInfo(tupType); */
+ pfree(tupType);
+}
+
+
+/* ----------------
+ * ExecAssignProjectionInfo
+ forms the projection information from the node's targetlist
+ * ----------------
+ */
+void
+ExecAssignProjectionInfo(Plan *node, CommonState *commonstate)
+{
+ ProjectionInfo *projInfo;
+ List *targetList;
+ int len;
+
+ targetList = node->targetlist;
+ len = ExecTargetListLength(targetList);
+
+ projInfo = makeNode(ProjectionInfo);
+ projInfo->pi_targetlist = targetList;
+ projInfo->pi_len = len;
+ projInfo->pi_tupValue =
+ (len <= 0) ? NULL : (Datum *) palloc(sizeof(Datum) * len);
+ projInfo->pi_exprContext = commonstate->cs_ExprContext;
+ projInfo->pi_slot = commonstate->cs_ResultTupleSlot;
+
+ commonstate->cs_ProjInfo = projInfo;
+}
+
+
+/* ----------------
+ * ExecFreeProjectionInfo
+ * ----------------
+ */
+void
+ExecFreeProjectionInfo(CommonState *commonstate)
+{
+ ProjectionInfo *projInfo;
+
+ /* ----------------
+ * get projection info. if NULL then this node has
+ * none so we just return.
+ * ----------------
+ */
+ projInfo = commonstate->cs_ProjInfo;
+ if (projInfo == NULL)
+ return;
+
+ /* ----------------
+ * clean up memory used.
+ * ----------------
+ */
+ if (projInfo->pi_tupValue != NULL)
+ pfree(projInfo->pi_tupValue);
+
+ pfree(projInfo);
+ commonstate->cs_ProjInfo = NULL;
+}
+
+/* ----------------------------------------------------------------
+ * the following scan type support functions are for
+ * those nodes which are stubborn and return tuples in
+ * their Scan tuple slot instead of their Result tuple
+ * slot.. luck fur us, these nodes do not do projections
+ * so we don't have to worry about getting the ProjectionInfo
+ * right for them... -cim 6/3/91
+ * ----------------------------------------------------------------
+ */
+
+/* ----------------
+ * ExecGetScanType
+ * ----------------
+ */
+TupleDesc
+ExecGetScanType(CommonScanState *csstate)
+{
+ TupleTableSlot *slot = csstate->css_ScanTupleSlot;
+ return slot->ttc_tupleDescriptor;
+}
+
+/* ----------------
+ * ExecFreeScanType
+ * ----------------
+ */
+void
+ExecFreeScanType(CommonScanState *csstate)
+{
+ TupleTableSlot *slot;
+ TupleDesc tupType;
+
+ slot = csstate->css_ScanTupleSlot;
+ tupType = slot->ttc_tupleDescriptor;
+
+/* ExecFreeTypeInfo(tupType); */
+ pfree(tupType);
+}
+
+/* ----------------
+ * ExecAssignScanType
+ * ----------------
+ */
+void
+ExecAssignScanType(CommonScanState *csstate,
+ TupleDesc tupDesc)
+{
+ TupleTableSlot *slot;
+
+ slot = (TupleTableSlot *) csstate->css_ScanTupleSlot;
+ slot->ttc_tupleDescriptor = tupDesc;
+}
+
+/* ----------------
+ * ExecAssignScanTypeFromOuterPlan
+ * ----------------
+ */
+void
+ExecAssignScanTypeFromOuterPlan(Plan *node, CommonScanState *csstate)
+{
+ Plan *outerPlan;
+ TupleDesc tupDesc;
+
+ outerPlan = outerPlan(node);
+ tupDesc = ExecGetTupType(outerPlan);
+
+ ExecAssignScanType(csstate, tupDesc);
+}
+
+
+/* ----------------------------------------------------------------
+ * ExecTypeFromTL support routines.
+ *
+ * these routines are used mainly from ExecTypeFromTL.
+ * -cim 6/12/90
+ *
+ * old comments
+ * Routines dealing with the structure 'attribute' which conatains
+ * the type information about attributes in a tuple:
+ *
+ * ExecMakeTypeInfo(noType) --
+ * returns pointer to array of 'noType' structure 'attribute'.
+ * ExecSetTypeInfo(index, typeInfo, attNum, attLen) --
+ * sets the element indexed by 'index' in typeInfo with
+ * the values: attNum, attLen.
+ * ExecFreeTypeInfo(typeInfo) --
+ * frees the structure 'typeInfo'.
+ * ----------------------------------------------------------------
+ */
+
+/* ----------------
+ * ExecSetTypeInfo
+ *
+ * This initializes fields of a single attribute in a
+ * tuple descriptor from the specified parameters.
+ *
+ * XXX this duplicates much of the functionality of TupleDescInitEntry.
+ * the routines should be moved to the same place and be rewritten
+ * to share common code.
+ * ----------------
+ */
+#if 0
+void
+ExecSetTypeInfo(int index,
+ TupleDesc typeInfo,
+ Oid typeID,
+ int attNum,
+ int attLen,
+ char *attName,
+ bool attbyVal,
+ char attalign)
+{
+ AttributeTupleForm att;
+
+ /* ----------------
+ * get attribute pointer and preform a sanity check..
+ * ----------------
+ */
+ att = typeInfo[index];
+ if (att == NULL)
+ elog(WARN, "ExecSetTypeInfo: trying to assign through NULL");
+
+ /* ----------------
+ * assign values to the tuple descriptor, being careful not
+ * to copy a null attName..
+ *
+ * XXX it is unknown exactly what information is needed to
+ * initialize the attribute struct correctly so for now
+ * we use 0. this should be fixed -- otherwise we run the
+ * risk of using garbage data. -cim 5/5/91
+ * ----------------
+ */
+ att->attrelid = 0; /* dummy value */
+
+ if (attName != (char *) NULL)
+ strncpy(att->attname.data, attName, NAMEDATALEN);
+ else
+ memset(att->attname.data,0,NAMEDATALEN);
+
+ att->atttypid = typeID;
+ att->attdefrel = 0; /* dummy value */
+ att->attnvals = 0; /* dummy value */
+ att->atttyparg = 0; /* dummy value */
+ att->attlen = attLen;
+ att->attnum = attNum;
+ att->attbound = 0; /* dummy value */
+ att->attbyval = attbyVal;
+ att->attcanindex = 0; /* dummy value */
+ att->attproc = 0; /* dummy value */
+ att->attnelems = 0; /* dummy value */
+ att->attcacheoff = -1;
+ att->attisset = false;
+ att->attalign = attalign;
+}
+
+/* ----------------
+ * ExecFreeTypeInfo frees the array of attrbutes
+ * created by ExecMakeTypeInfo and returned by ExecTypeFromTL...
+ * ----------------
+ */
+void
+ExecFreeTypeInfo(TupleDesc typeInfo)
+{
+ /* ----------------
+ * do nothing if asked to free a null pointer
+ * ----------------
+ */
+ if (typeInfo == NULL)
+ return;
+
+ /* ----------------
+ * the entire array of typeinfo pointers created by
+ * ExecMakeTypeInfo was allocated with a single palloc()
+ * so we can deallocate the whole array with a single pfree().
+ * (we should not try and free all the elements in the array)
+ * -cim 6/12/90
+ * ----------------
+ */
+ pfree(typeInfo);
+}
+
+
+/* ----------------------------------------------------------------
+ * QueryDescGetTypeInfo
+ *
+ *| I don't know how this is used, all I know is that it
+ *| appeared one day in main.c so I moved it here. -cim 11/1/89
+ * ----------------------------------------------------------------
+ */
+TupleDesc
+QueryDescGetTypeInfo(QueryDesc *queryDesc)
+{
+ Plan *plan;
+ TupleDesc tupleType;
+ List *targetList;
+ AttrInfo *attinfo = (AttrInfo *)palloc(sizeof(AttrInfo));
+
+ plan = queryDesc->plantree;
+ tupleType = (TupleDesc) ExecGetTupType(plan);
+/*
+ targetList = plan->targetlist;
+
+ attinfo->numAttr = ExecTargetListLength(targetList);
+ attinfo->attrs = tupleType;
+*/
+ attinfo->numAttr = tupleType->natts;
+ attinfo->attrs = tupleType->attrs;
+ return attinfo;
+}
+#endif
+
+/* ----------------------------------------------------------------
+ * ExecInsertIndexTuples support
+ * ----------------------------------------------------------------
+ */
+/* ----------------------------------------------------------------
+ * ExecGetIndexKeyInfo
+ *
+ * Extracts the index key attribute numbers from
+ * an index tuple form (i.e. a tuple from the pg_index relation)
+ * into an array of attribute numbers. The array and the
+ * size of the array are returned to the caller via return
+ * parameters.
+ * ----------------------------------------------------------------
+ */
+void
+ExecGetIndexKeyInfo(IndexTupleForm indexTuple,
+ int *numAttsOutP,
+ AttrNumber **attsOutP,
+ FuncIndexInfoPtr fInfoP)
+{
+ int i;
+ int numKeys;
+ AttrNumber *attKeys;
+
+ /* ----------------
+ * check parameters
+ * ----------------
+ */
+ if (numAttsOutP == NULL && attsOutP == NULL) {
+ elog(DEBUG, "ExecGetIndexKeyInfo: %s",
+ "invalid parameters: numAttsOutP and attsOutP must be non-NULL");
+ }
+
+ /* ----------------
+ * set the procid for a possible functional index.
+ * ----------------
+ */
+ FIsetProcOid(fInfoP, indexTuple->indproc);
+
+ /* ----------------
+ * count the number of keys..
+ * ----------------
+ */
+ numKeys = 0;
+ for (i=0; i<8 && indexTuple->indkey[i] != 0; i++)
+ numKeys++;
+
+ /* ----------------
+ * place number keys in callers return area
+ * or the number of arguments for a functional index.
+ *
+ * If we have a functional index then the number of
+ * attributes defined in the index must 1 (the function's
+ * single return value).
+ * ----------------
+ */
+ if (FIgetProcOid(fInfoP) != InvalidOid) {
+ FIsetnArgs(fInfoP, numKeys);
+ (*numAttsOutP) = 1;
+ }
+ else
+ (*numAttsOutP) = numKeys;
+
+ if (numKeys < 1) {
+ elog(DEBUG, "ExecGetIndexKeyInfo: %s",
+ "all index key attribute numbers are zero!");
+ (*attsOutP) = NULL;
+ return;
+ }
+
+ /* ----------------
+ * allocate and fill in array of key attribute numbers
+ * ----------------
+ */
+ CXT1_printf("ExecGetIndexKeyInfo: context is %d\n", CurrentMemoryContext);
+
+ attKeys = (AttrNumber*)
+ palloc(numKeys * sizeof(AttrNumber));
+
+ for (i=0; i<numKeys; i++)
+ attKeys[i] = indexTuple->indkey[i];
+
+ /* ----------------
+ * return array to caller.
+ * ----------------
+ */
+ (*attsOutP) = attKeys;
+}
+
+/* ----------------------------------------------------------------
+ * ExecOpenIndices
+ *
+ * Here we scan the pg_index relation to find indices
+ * associated with a given heap relation oid. Since we
+ * don't know in advance how many indices we have, we
+ * form lists containing the information we need from
+ * pg_index and then process these lists.
+ *
+ * Note: much of this code duplicates effort done by
+ * the IndexCatalogInformation function in plancat.c
+ * because IndexCatalogInformation is poorly written.
+ *
+ * It would be much better the functionality provided
+ * by this function and IndexCatalogInformation was
+ * in the form of a small set of orthogonal routines..
+ * If you are trying to understand this, I suggest you
+ * look at the code to IndexCatalogInformation and
+ * FormIndexTuple.. -cim 9/27/89
+ * ----------------------------------------------------------------
+ */
+void
+ExecOpenIndices(Oid resultRelationOid,
+ RelationInfo *resultRelationInfo)
+{
+ Relation indexRd;
+ HeapScanDesc indexSd;
+ ScanKeyData key;
+ HeapTuple tuple;
+ IndexTupleForm indexStruct;
+ Oid indexOid;
+ List *oidList;
+ List *nkeyList;
+ List *keyList;
+ List *fiList;
+ char *predString;
+ List *predList;
+ List *indexoid;
+ List *numkeys;
+ List *indexkeys;
+ List *indexfuncs;
+ List *indexpreds;
+ int len;
+
+ RelationPtr relationDescs;
+ IndexInfo **indexInfoArray;
+ FuncIndexInfoPtr fInfoP;
+ int numKeyAtts;
+ AttrNumber *indexKeyAtts;
+ PredInfo *predicate;
+ int i;
+
+ /* ----------------
+ * open pg_index
+ * ----------------
+ */
+ indexRd = heap_openr(IndexRelationName);
+
+ /* ----------------
+ * form a scan key
+ * ----------------
+ */
+ ScanKeyEntryInitialize(&key, 0, Anum_pg_index_indrelid,
+ ObjectIdEqualRegProcedure,
+ ObjectIdGetDatum(resultRelationOid));
+
+ /* ----------------
+ * scan the index relation, looking for indices for our
+ * result relation..
+ * ----------------
+ */
+ indexSd = heap_beginscan(indexRd, /* scan desc */
+ false, /* scan backward flag */
+ NowTimeQual, /* time qual */
+ 1, /* number scan keys */
+ &key); /* scan keys */
+
+ oidList = NIL;
+ nkeyList = NIL;
+ keyList = NIL;
+ fiList = NIL;
+ predList = NIL;
+
+ while(tuple = heap_getnext(indexSd, /* scan desc */
+ false, /* scan backward flag */
+ NULL), /* return: buffer */
+ HeapTupleIsValid(tuple)) {
+
+ /* ----------------
+ * For each index relation we find, extract the information
+ * we need and store it in a list..
+ *
+ * first get the oid of the index relation from the tuple
+ * ----------------
+ */
+ indexStruct = (IndexTupleForm) GETSTRUCT(tuple);
+ indexOid = indexStruct->indexrelid;
+
+ /* ----------------
+ * allocate space for functional index information.
+ * ----------------
+ */
+ fInfoP = (FuncIndexInfoPtr)palloc( sizeof(*fInfoP) );
+
+ /* ----------------
+ * next get the index key information from the tuple
+ * ----------------
+ */
+ ExecGetIndexKeyInfo(indexStruct,
+ &numKeyAtts,
+ &indexKeyAtts,
+ fInfoP);
+
+ /* ----------------
+ * next get the index predicate from the tuple
+ * ----------------
+ */
+ if (VARSIZE(&indexStruct->indpred) != 0) {
+ predString = fmgr(F_TEXTOUT, &indexStruct->indpred);
+ predicate = (PredInfo*)stringToNode(predString);
+ pfree(predString);
+ } else {
+ predicate = NULL;
+ }
+
+ /* ----------------
+ * save the index information into lists
+ * ----------------
+ */
+ oidList = lconsi(indexOid, oidList);
+ nkeyList = lconsi(numKeyAtts, nkeyList);
+ keyList = lcons(indexKeyAtts, keyList);
+ fiList = lcons(fInfoP, fiList);
+ predList = lcons(predicate, predList);
+ }
+
+ /* ----------------
+ * we have the info we need so close the pg_index relation..
+ * ----------------
+ */
+ heap_endscan(indexSd);
+ heap_close(indexRd);
+
+ /* ----------------
+ * Now that we've collected the index information into three
+ * lists, we open the index relations and store the descriptors
+ * and the key information into arrays.
+ * ----------------
+ */
+ len = length(oidList);
+ if (len > 0) {
+ /* ----------------
+ * allocate space for relation descs
+ * ----------------
+ */
+ CXT1_printf("ExecOpenIndices: context is %d\n", CurrentMemoryContext);
+ relationDescs = (RelationPtr)
+ palloc(len * sizeof(Relation));
+
+ /* ----------------
+ * initialize index info array
+ * ----------------
+ */
+ CXT1_printf("ExecOpenIndices: context is %d\n", CurrentMemoryContext);
+ indexInfoArray = (IndexInfo**)
+ palloc(len * sizeof(IndexInfo*));
+
+ for (i=0; i<len; i++) {
+ IndexInfo *ii = makeNode(IndexInfo);
+ ii->ii_NumKeyAttributes = 0;
+ ii->ii_KeyAttributeNumbers = (AttrNumber*) NULL;
+ ii->ii_FuncIndexInfo = (FuncIndexInfoPtr) NULL;
+ ii->ii_Predicate = NULL;
+ indexInfoArray[i] = ii;
+ }
+
+ /* ----------------
+ * attempt to open each of the indices. If we succeed,
+ * then store the index relation descriptor into the
+ * relation descriptor array.
+ * ----------------
+ */
+ i = 0;
+ foreach (indexoid, oidList) {
+ Relation indexDesc;
+
+ indexOid = lfirsti(indexoid);
+ indexDesc = index_open(indexOid);
+ if (indexDesc != NULL)
+ relationDescs[i++] = indexDesc;
+ }
+
+ /* ----------------
+ * store the relation descriptor array and number of
+ * descs into the result relation info.
+ * ----------------
+ */
+ resultRelationInfo->ri_NumIndices = i;
+ resultRelationInfo->ri_IndexRelationDescs = relationDescs;
+
+ /* ----------------
+ * store the index key information collected in our
+ * lists into the index info array
+ * ----------------
+ */
+ i = 0;
+ foreach (numkeys, nkeyList) {
+ numKeyAtts = lfirsti(numkeys);
+ indexInfoArray[i++]->ii_NumKeyAttributes = numKeyAtts;
+ }
+
+ i = 0;
+ foreach (indexkeys, keyList) {
+ indexKeyAtts = (AttrNumber*) lfirst(indexkeys);
+ indexInfoArray[i++]->ii_KeyAttributeNumbers = indexKeyAtts;
+ }
+
+ i = 0;
+ foreach (indexfuncs, fiList) {
+ FuncIndexInfoPtr fiP = (FuncIndexInfoPtr)lfirst(indexfuncs);
+ indexInfoArray[i++]->ii_FuncIndexInfo = fiP;
+ }
+
+ i = 0;
+ foreach (indexpreds, predList) {
+ indexInfoArray[i++]->ii_Predicate = lfirst(indexpreds);
+ }
+ /* ----------------
+ * store the index info array into relation info
+ * ----------------
+ */
+ resultRelationInfo->ri_IndexRelationInfo = indexInfoArray;
+ }
+
+ /* ----------------
+ * All done, resultRelationInfo now contains complete information
+ * on the indices associated with the result relation.
+ * ----------------
+ */
+
+ /* should free oidList, nkeyList and keyList here */
+ /* OK - let's do it -jolly */
+ freeList(oidList);
+ freeList(nkeyList);
+ freeList(keyList);
+ freeList(fiList);
+ freeList(predList);
+}
+
+/* ----------------------------------------------------------------
+ * ExecCloseIndices
+ *
+ * Close the index relations stored in resultRelationInfo
+ * ----------------------------------------------------------------
+ */
+void
+ExecCloseIndices(RelationInfo *resultRelationInfo)
+{
+ int i;
+ int numIndices;
+ RelationPtr relationDescs;
+
+ numIndices = resultRelationInfo->ri_NumIndices;
+ relationDescs = resultRelationInfo->ri_IndexRelationDescs;
+
+ for (i=0; i<numIndices; i++)
+ if (relationDescs[i] != NULL)
+ index_close(relationDescs[i]);
+ /*
+ * XXX should free indexInfo array here too.
+ */
+}
+
+/* ----------------------------------------------------------------
+ * ExecFormIndexTuple
+ *
+ * Most of this code is cannabilized from DefaultBuild().
+ * As said in the comments for ExecOpenIndices, most of
+ * this functionality should be rearranged into a proper
+ * set of routines..
+ * ----------------------------------------------------------------
+ */
+IndexTuple
+ExecFormIndexTuple(HeapTuple heapTuple,
+ Relation heapRelation,
+ Relation indexRelation,
+ IndexInfo *indexInfo)
+{
+ IndexTuple indexTuple;
+ TupleDesc heapDescriptor;
+ TupleDesc indexDescriptor;
+ Datum *datum;
+ char *nulls;
+
+ int numberOfAttributes;
+ AttrNumber *keyAttributeNumbers;
+ FuncIndexInfoPtr fInfoP;
+
+ /* ----------------
+ * get information from index info structure
+ * ----------------
+ */
+ numberOfAttributes = indexInfo->ii_NumKeyAttributes;
+ keyAttributeNumbers = indexInfo->ii_KeyAttributeNumbers;
+ fInfoP = indexInfo->ii_FuncIndexInfo;
+
+ /* ----------------
+ * datum and null are arrays in which we collect the index attributes
+ * when forming a new index tuple.
+ * ----------------
+ */
+ CXT1_printf("ExecFormIndexTuple: context is %d\n", CurrentMemoryContext);
+ datum = (Datum *) palloc(numberOfAttributes * sizeof *datum);
+ nulls = (char *) palloc(numberOfAttributes * sizeof *nulls);
+
+ /* ----------------
+ * get the tuple descriptors from the relations so we know
+ * how to form the index tuples..
+ * ----------------
+ */
+ heapDescriptor = RelationGetTupleDescriptor(heapRelation);
+ indexDescriptor = RelationGetTupleDescriptor(indexRelation);
+
+ /* ----------------
+ * FormIndexDatum fills in its datum and null parameters
+ * with attribute information taken from the given heap tuple.
+ * ----------------
+ */
+ FormIndexDatum(numberOfAttributes, /* num attributes */
+ keyAttributeNumbers, /* array of att nums to extract */
+ heapTuple, /* tuple from base relation */
+ heapDescriptor, /* heap tuple's descriptor */
+ InvalidBuffer, /* buffer associated with heap tuple */
+ datum, /* return: array of attributes */
+ nulls, /* return: array of char's */
+ fInfoP); /* functional index information */
+
+ indexTuple = index_formtuple(indexDescriptor,
+ datum,
+ nulls);
+
+ /* ----------------
+ * free temporary arrays
+ *
+ * XXX should store these in the IndexInfo instead of allocating
+ * and freeing on every insertion, but efficency here is not
+ * that important and FormIndexTuple is wasteful anyways..
+ * -cim 9/27/89
+ * ----------------
+ */
+ pfree(nulls);
+ pfree(datum);
+
+ return indexTuple;
+}
+
+/* ----------------------------------------------------------------
+ * ExecInsertIndexTuples
+ *
+ * This routine takes care of inserting index tuples
+ * into all the relations indexing the result relation
+ * when a heap tuple is inserted into the result relation.
+ * Much of this code should be moved into the genam
+ * stuff as it only exists here because the genam stuff
+ * doesn't provide the functionality needed by the
+ * executor.. -cim 9/27/89
+ * ----------------------------------------------------------------
+ */
+void
+ExecInsertIndexTuples(TupleTableSlot *slot,
+ ItemPointer tupleid,
+ EState *estate)
+{
+ HeapTuple heapTuple;
+ RelationInfo *resultRelationInfo;
+ int i;
+ int numIndices;
+ RelationPtr relationDescs;
+ Relation heapRelation;
+ IndexInfo **indexInfoArray;
+ Node *predicate;
+ bool satisfied;
+ ExprContext *econtext;
+ IndexTuple indexTuple;
+ InsertIndexResult result;
+
+ heapTuple = slot->val;
+
+ /* ----------------
+ * get information from the result relation info structure.
+ * ----------------
+ */
+ resultRelationInfo = estate->es_result_relation_info;
+ numIndices = resultRelationInfo->ri_NumIndices;
+ relationDescs = resultRelationInfo->ri_IndexRelationDescs;
+ indexInfoArray = resultRelationInfo->ri_IndexRelationInfo;
+ heapRelation = resultRelationInfo->ri_RelationDesc;
+
+ /* ----------------
+ * for each index, form and insert the index tuple
+ * ----------------
+ */
+ econtext = NULL;
+ for (i=0; i<numIndices; i++) {
+ if (relationDescs[i] == NULL) continue;
+
+ predicate = indexInfoArray[i]->ii_Predicate;
+ if (predicate != NULL) {
+ if (econtext == NULL) {
+ econtext = makeNode(ExprContext);
+ }
+ econtext->ecxt_scantuple = slot;
+
+ /* Skip this index-update if the predicate isn't satisfied */
+ satisfied = ExecQual((List*)predicate, econtext);
+ if (satisfied == false)
+ continue;
+ }
+
+ indexTuple = ExecFormIndexTuple(heapTuple,
+ heapRelation,
+ relationDescs[i],
+ indexInfoArray[i]);
+
+ indexTuple->t_tid = (*tupleid); /* structure assignment */
+
+ result = index_insert(relationDescs[i], /* index relation */
+ indexTuple); /* index tuple */
+
+ /* ----------------
+ * keep track of index inserts for debugging
+ * ----------------
+ */
+ IncrIndexInserted();
+
+ /* ----------------
+ * free index tuple after insertion
+ * ----------------
+ */
+ if (result) pfree(result);
+ pfree(indexTuple);
+ }
+ if (econtext != NULL) pfree(econtext);
+}
+
diff --git a/src/backend/executor/execdebug.h b/src/backend/executor/execdebug.h
new file mode 100644
index 00000000000..b5200ca2577
--- /dev/null
+++ b/src/backend/executor/execdebug.h
@@ -0,0 +1,377 @@
+/*-------------------------------------------------------------------------
+ *
+ * execdebug.h--
+ * #defines governing debugging behaviour in the executor
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id: execdebug.h,v 1.1.1.1 1996/07/09 06:21:25 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef EXECDEBUG_H
+#define EXECDEBUG_H
+
+/* ----------------------------------------------------------------
+ * debugging defines.
+ *
+ * If you want certain debugging behaviour, then #define
+ * the variable to 1, else #undef it. -cim 10/26/89
+ * ----------------------------------------------------------------
+ */
+
+/* ----------------
+ * EXEC_DEBUGSTORETUP is for tuple table debugging - this
+ * will print a message every time we call ExecStoreTuple.
+ * -cim 3/20/91
+ * ----------------
+ */
+#undef EXEC_DEBUGSTORETUP
+
+/* ----------------
+ * EXEC_TUPLECOUNT is a #define which causes the
+ * executor keep track of tuple counts. This might be
+ * causing some problems with the decstation stuff so
+ * you might want to undefine this if you are doing work
+ * on the decs - cim 10/20/89
+ * ----------------
+ */
+#undef EXEC_TUPLECOUNT
+
+/* ----------------
+ * EXEC_SHOWBUFSTATS controls whether or not buffer statistics
+ * are shown for each query. -cim 2/9/89
+ * ----------------
+ */
+#undef EXEC_SHOWBUFSTATS
+
+/* ----------------
+ * EXEC_CONTEXTDEBUG turns on the printing of debugging information
+ * by CXT_printf() calls regarding which memory context is the
+ * CurrentMemoryContext for palloc() calls.
+ * ----------------
+ */
+#undef EXEC_CONTEXTDEBUG
+
+/* ----------------
+ * EXEC_RETURNSIZE is a compile flag governing the
+ * behaviour of lispFmgr.. See ExecMakeFunctionResult().
+ * Undefining this avoids a problem in the system cache.
+ *
+ * Note: undefining this means that there is incorrect
+ * information in the const nodes corresponding
+ * to function (or operator) results. The thing is,
+ * 99% of the time this is fine because when you do
+ * something like x = emp.sal + 1, you already know
+ * the type and size of x so the fact that + didn't
+ * return the correct size doesn't matter.
+ * With variable length stuff the size is stored in
+ * the first few bytes of the data so again, it's
+ * not likely to matter.
+ * ----------------
+ */
+#undef EXEC_RETURNSIZE
+
+/* ----------------
+ * EXEC_UTILSDEBUG is a flag which turns on debugging of the
+ * executor utilities by EU_printf() in eutils.c
+ * ----------------
+ */
+#undef EXEC_UTILSDEBUG
+
+/* ----------------
+ * EXEC_NESTLOOPDEBUG is a flag which turns on debugging of the
+ * nest loop node by NL_printf() and ENL_printf() in nestloop.c
+ * ----------------
+ */
+#undef EXEC_NESTLOOPDEBUG
+
+/* ----------------
+ * EXEC_PROCDEBUG is a flag which turns on debugging of
+ * ExecProcNode() by PN_printf() in procnode.c
+ * ----------------
+ */
+#undef EXEC_PROCDEBUG
+
+/* ----------------
+ * EXEC_EVALDEBUG is a flag which turns on debugging of
+ * ExecEval and ExecTargetList() stuff by EV_printf() in qual.c
+ * ----------------
+ */
+#undef EXEC_EVALDEBUG
+
+/* ----------------
+ * EXEC_SCANDEBUG is a flag which turns on debugging of
+ * the ExecSeqScan() stuff by S_printf() in seqscan.c
+ * ----------------
+ */
+#undef EXEC_SCANDEBUG
+
+/* ----------------
+ * EXEC_SORTDEBUG is a flag which turns on debugging of
+ * the ExecSort() stuff by SO_printf() in sort.c
+ * ----------------
+ */
+#undef EXEC_SORTDEBUG
+
+/* ----------------
+ * EXEC_MERGEJOINDEBUG is a flag which turns on debugging of
+ * the ExecMergeJoin() stuff by MJ_printf() in mergejoin.c
+ * ----------------
+ */
+#undef EXEC_MERGEJOINDEBUG
+
+/* ----------------
+ * EXEC_MERGEJOINPFREE is a flag which causes merge joins
+ * to pfree intermittant tuples (which is the proper thing)
+ * Not defining this means we avoid menory management problems
+ * at the cost of doing deallocation of stuff only at the
+ * end of the transaction
+ * ----------------
+ */
+#undef EXEC_MERGEJOINPFREE
+
+/* ----------------
+ * EXEC_DEBUGINTERACTIVE is a flag which enables the
+ * user to issue "DEBUG" commands from an interactive
+ * backend.
+ * ----------------
+ */
+#undef EXEC_DEBUGINTERACTIVE
+
+/* ----------------
+ * EXEC_DEBUGVARIABLEFILE is string, which if defined will
+ * be loaded when the executor is initialized. If this
+ * string is not defined then nothing will be loaded..
+ *
+ * Example:
+ *
+ * #define EXEC_DEBUGVARIABLEFILE "/a/postgres/cimarron/.pg_debugvars"
+ #
+ * Note: since these variables are read at execution time,
+ * they can't affect the first query.. this hack should be
+ * replaced by something better sometime. -cim 11/2/89
+ * ----------------
+ */
+#undef EXEC_DEBUGVARIABLEFILE
+
+/* ----------------------------------------------------------------
+ * #defines controlled by above definitions
+ *
+ * Note: most of these are "incomplete" because I didn't
+ * need the ones not defined. More should be added
+ * only as necessary -cim 10/26/89
+ * ----------------------------------------------------------------
+ */
+#define T_OR_F(b) (b ? "true" : "false")
+#define NULL_OR_TUPLE(slot) (TupIsNull(slot) ? "null" : "a tuple")
+
+
+/* #define EXEC_TUPLECOUNT - XXX take out for now for executor stubbing -- jolly*/
+/* ----------------
+ * tuple count debugging defines
+ * ----------------
+ */
+#ifdef EXEC_TUPLECOUNT
+extern int NTupleProcessed;
+extern int NTupleRetrieved;
+extern int NTupleReplaced;
+extern int NTupleAppended;
+extern int NTupleDeleted;
+extern int NIndexTupleProcessed;
+extern int NIndexTupleInserted;
+
+#define IncrRetrieved() NTupleRetrieved++
+#define IncrAppended() NTupleAppended++
+#define IncrDeleted() NTupleDeleted++
+#define IncrReplaced() NTupleReplaced++
+#define IncrInserted() NTupleInserted++
+#define IncrProcessed() NTupleProcessed++
+#define IncrIndexProcessed() NIndexTupleProcessed++
+#define IncrIndexInserted() NIndexTupleInserted++
+#else
+#define IncrRetrieved()
+#define IncrAppended()
+#define IncrDeleted()
+#define IncrReplaced()
+#define IncrInserted()
+#define IncrProcessed()
+#define IncrIndexProcessed()
+#define IncrIndexInserted()
+#endif /* EXEC_TUPLECOUNT */
+
+/* ----------------
+ * memory context debugging defines
+ * ----------------
+ */
+#ifdef EXEC_CONTEXTDEBUG
+#define CXT_printf(s) printf(s)
+#define CXT1_printf(s, a) printf(s, a)
+#else
+#define CXT_printf(s)
+#define CXT1_printf(s, a)
+#endif /* EXEC_CONTEXTDEBUG */
+
+/* ----------------
+ * eutils debugging defines
+ * ----------------
+ */
+#ifdef EXEC_UTILSDEBUG
+#define EU_nodeDisplay(l) nodeDisplay(l, 0)
+#define EU_printf(s) printf(s)
+#define EU1_printf(s, a) printf(s, a)
+#define EU4_printf(s, a, b, c, d) printf(s, a, b, c, d)
+#else
+#define EU_nodeDisplay(l)
+#define EU_printf(s)
+#define EU1_printf(s, a)
+#define EU4_printf(s, a, b, c, d)
+#endif /* EXEC_UTILSDEBUG */
+
+
+/* ----------------
+ * nest loop debugging defines
+ * ----------------
+ */
+#ifdef EXEC_NESTLOOPDEBUG
+#define NL_nodeDisplay(l) nodeDisplay(l, 0)
+#define NL_printf(s) printf(s)
+#define NL1_printf(s, a) printf(s, a)
+#define NL4_printf(s, a, b, c, d) printf(s, a, b, c, d)
+#define ENL1_printf(message) printf("ExecNestLoop: %s\n", message)
+#else
+#define NL_nodeDisplay(l)
+#define NL_printf(s)
+#define NL1_printf(s, a)
+#define NL4_printf(s, a, b, c, d)
+#define ENL1_printf(message)
+#endif /* EXEC_NESTLOOPDEBUG */
+
+/* ----------------
+ * proc node debugging defines
+ * ----------------
+ */
+#ifdef EXEC_PROCDEBUG
+#define PN_printf(s) printf(s)
+#define PN1_printf(s, p) printf(s, p)
+#else
+#define PN_printf(s)
+#define PN1_printf(s, p)
+#endif /* EXEC_PROCDEBUG */
+
+/* ----------------
+ * exec eval / target list debugging defines
+ * ----------------
+ */
+#ifdef EXEC_EVALDEBUG
+#define EV_nodeDisplay(l) nodeDisplay(l, 0)
+#define EV_printf(s) printf(s)
+#define EV1_printf(s, a) printf(s, a)
+#define EV5_printf(s, a, b, c, d, e) printf(s, a, b, c, d, e)
+#else
+#define EV_nodeDisplay(l)
+#define EV_printf(s)
+#define EV1_printf(s, a)
+#define EV5_printf(s, a, b, c, d, e)
+#endif /* EXEC_EVALDEBUG */
+
+/* ----------------
+ * scan debugging defines
+ * ----------------
+ */
+#ifdef EXEC_SCANDEBUG
+#define S_nodeDisplay(l) nodeDisplay(l, 0)
+#define S_printf(s) printf(s)
+#define S1_printf(s, p) printf(s, p)
+#else
+#define S_nodeDisplay(l)
+#define S_printf(s)
+#define S1_printf(s, p)
+#endif /* EXEC_SCANDEBUG */
+
+/* ----------------
+ * sort node debugging defines
+ * ----------------
+ */
+#ifdef EXEC_SORTDEBUG
+#define SO_nodeDisplay(l) nodeDisplay(l, 0)
+#define SO_printf(s) printf(s)
+#define SO1_printf(s, p) printf(s, p)
+#else
+#define SO_nodeDisplay(l)
+#define SO_printf(s)
+#define SO1_printf(s, p)
+#endif /* EXEC_SORTDEBUG */
+
+/* ----------------
+ * merge join debugging defines
+ * ----------------
+ */
+#ifdef EXEC_MERGEJOINDEBUG
+#define MJ_nodeDisplay(l) nodeDisplay(l, 0)
+#define MJ_printf(s) printf(s)
+#define MJ1_printf(s, p) printf(s, p)
+#define MJ2_printf(s, p1, p2) printf(s, p1, p2)
+#define MJ_debugtup(tuple, type) debugtup(tuple, type)
+#define MJ_dump(context, state) ExecMergeTupleDump(econtext, state)
+#define MJ_DEBUG_QUAL(clause, res) \
+ MJ2_printf(" ExecQual(%s, econtext) returns %s\n", \
+ CppAsString(clause), T_OR_F(res));
+
+#define MJ_DEBUG_MERGE_COMPARE(qual, res) \
+ MJ2_printf(" MergeCompare(mergeclauses, %s, ..) returns %s\n", \
+ CppAsString(qual), T_OR_F(res));
+
+#define MJ_DEBUG_PROC_NODE(slot) \
+ MJ2_printf(" %s = ExecProcNode(innerPlan) returns %s\n", \
+ CppAsString(slot), NULL_OR_TUPLE(slot));
+#else
+#define MJ_nodeDisplay(l)
+#define MJ_printf(s)
+#define MJ1_printf(s, p)
+#define MJ2_printf(s, p1, p2)
+#define MJ_debugtup(tuple, type)
+#define MJ_dump(context, state)
+#define MJ_DEBUG_QUAL(clause, res)
+#define MJ_DEBUG_MERGE_COMPARE(qual, res)
+#define MJ_DEBUG_PROC_NODE(slot)
+#endif /* EXEC_MERGEJOINDEBUG */
+
+/* ----------------------------------------------------------------
+ * DO NOT DEFINE THESE EVER OR YOU WILL BURN!
+ * ----------------------------------------------------------------
+ */
+/* ----------------
+ * DOESNOTWORK is currently placed around memory manager
+ * code that is known to cause problems. Code in between
+ * is likely not converted and probably won't work anyways.
+ * ----------------
+ */
+#undef DOESNOTWORK
+
+/* ----------------
+ * PERHAPSNEVER is placed around the "scan attribute"
+ * support code for the rule manager because for now we
+ * do things inefficiently. The correct solution to our
+ * problem is to add code to the parser/planner to save
+ * attribute information for the rule manager rather than
+ * have the executor have to grope through the entire plan
+ * for it so if we ever decide to make things better,
+ * we should probably delete the stuff in between PERHAPSNEVER..
+ * ----------------
+ */
+#undef PERHAPSNEVER
+
+/* ----------------
+ * NOTYET is placed around any code not yet implemented
+ * in the executor. Only remove these when actually implementing
+ * said code.
+ * ----------------
+ */
+#undef NOTYET
+
+extern long NDirectFileRead;
+extern long NDirectFileWrite;
+
+#endif /* ExecDebugIncluded */
diff --git a/src/backend/executor/execdefs.h b/src/backend/executor/execdefs.h
new file mode 100644
index 00000000000..5aec485c95c
--- /dev/null
+++ b/src/backend/executor/execdefs.h
@@ -0,0 +1,54 @@
+/*-------------------------------------------------------------------------
+ *
+ * execdefs.h--
+ *
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id: execdefs.h,v 1.1.1.1 1996/07/09 06:21:25 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef EXECDEFS_H
+#define EXECDEFS_H
+
+/* ----------------
+ * executor scan direction definitions
+ * ----------------
+ */
+#define EXEC_FRWD 1 /* Scan forward */
+#define EXEC_BKWD -1 /* Scan backward */
+
+/* ----------------
+ * ExecutePlan() tuplecount definitions
+ * ----------------
+ */
+#define ALL_TUPLES 0 /* return all tuples */
+#define ONE_TUPLE 1 /* return only one tuple */
+
+/* ----------------
+ * constants used by ExecMain
+ * ----------------
+ */
+#define EXEC_RUN 3
+#define EXEC_FOR 4
+#define EXEC_BACK 5
+#define EXEC_RETONE 6
+#define EXEC_RESULT 7
+
+/* ----------------
+ * Merge Join states
+ * ----------------
+ */
+#define EXEC_MJ_INITIALIZE 1
+#define EXEC_MJ_JOINMARK 2
+#define EXEC_MJ_JOINTEST 3
+#define EXEC_MJ_JOINTUPLES 4
+#define EXEC_MJ_NEXTOUTER 5
+#define EXEC_MJ_TESTOUTER 6
+#define EXEC_MJ_NEXTINNER 7
+#define EXEC_MJ_SKIPINNER 8
+#define EXEC_MJ_SKIPOUTER 9
+
+#endif /* EXECDEFS_H */
diff --git a/src/backend/executor/execdesc.h b/src/backend/executor/execdesc.h
new file mode 100644
index 00000000000..54752625f55
--- /dev/null
+++ b/src/backend/executor/execdesc.h
@@ -0,0 +1,38 @@
+/*-------------------------------------------------------------------------
+ *
+ * execdesc.h--
+ * plan and query descriptor accessor macros used by the executor
+ * and related modules.
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id: execdesc.h,v 1.1.1.1 1996/07/09 06:21:25 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef EXECDESC_H
+#define EXECDESC_H
+
+#include "nodes/parsenodes.h"
+#include "nodes/plannodes.h"
+#include "tcop/dest.h"
+
+/* ----------------
+ * query descriptor:
+ * a QueryDesc encapsulates everything that the executor
+ * needs to execute the query
+ * ---------------------
+ */
+typedef struct QueryDesc {
+ CmdType operation; /* CMD_SELECT, CMD_UPDATE, etc. */
+ Query *parsetree;
+ Plan *plantree;
+ CommandDest dest; /* the destination output of the execution */
+} QueryDesc;
+
+/* in pquery.c */
+extern QueryDesc *CreateQueryDesc(Query *parsetree, Plan *plantree,
+ CommandDest dest);
+
+#endif /* EXECDESC_H */
diff --git a/src/backend/executor/executor.h b/src/backend/executor/executor.h
new file mode 100644
index 00000000000..65caf098f13
--- /dev/null
+++ b/src/backend/executor/executor.h
@@ -0,0 +1,229 @@
+/*-------------------------------------------------------------------------
+ *
+ * executor.h--
+ * support for the POSTGRES executor module
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id: executor.h,v 1.1.1.1 1996/07/09 06:21:25 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef EXECUTOR_H
+#define EXECUTOR_H
+
+/* ----------------------------------------------------------------
+ * #includes
+ * ----------------------------------------------------------------
+ */
+#include <stdio.h>
+#include <string.h>
+
+#include "postgres.h"
+#include "nodes/pg_list.h"
+
+/* ----------------
+ * executor debugging definitions are kept in a separate file
+ * so people can customize what debugging they want to see and not
+ * have this information clobbered every time a new version of
+ * executor.h is checked in -cim 10/26/89
+ * ----------------
+ */
+#include "executor/execdebug.h"
+
+#include "access/heapam.h"
+#include "access/htup.h"
+#include "access/istrat.h"
+#include "access/itup.h"
+#include "access/skey.h"
+#include "utils/tqual.h"
+#include "catalog/catname.h"
+#include "utils/syscache.h"
+#include "executor/execdefs.h"
+#include "executor/tuptable.h"
+
+#include "nodes/parsenodes.h"
+
+#include "storage/buf.h"
+#include "miscadmin.h"
+#include "fmgr.h"
+#include "utils/elog.h"
+#include "utils/mcxt.h"
+#include "utils/memutils.h"
+#include "utils/rel.h"
+
+#include "catalog/pg_index.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_type.h"
+#include "catalog/pg_aggregate.h"
+
+#include "access/printtup.h"
+#include "nodes/primnodes.h"
+#include "nodes/plannodes.h"
+#include "nodes/execnodes.h"
+
+#include "tcop/dest.h"
+#include "storage/smgr.h"
+
+#include "access/genam.h"
+#include "executor/execdesc.h"
+
+/*
+ * prototypes from functions in execAmi.c
+ */
+extern void ExecOpenScanR(Oid relOid, int nkeys, ScanKey skeys, bool isindex,
+ ScanDirection dir, TimeQual timeRange,
+ Relation *returnRelation, Pointer *returnScanDesc);
+extern Relation ExecOpenR(Oid relationOid, bool isindex);
+extern Pointer ExecBeginScan(Relation relation, int nkeys, ScanKey skeys,
+ bool isindex, ScanDirection dir, TimeQual time_range);
+extern void ExecCloseR(Plan *node);
+extern void ExecReScan(Plan *node, ExprContext *exprCtxt, Plan *parent);
+extern HeapScanDesc ExecReScanR(Relation relDesc, HeapScanDesc scanDesc,
+ ScanDirection direction, int nkeys, ScanKey skeys);
+extern void ExecMarkPos(Plan *node);
+extern void ExecRestrPos(Plan *node);
+extern Relation ExecCreatR(TupleDesc tupType, Oid relationOid);
+
+/*
+ * prototypes from functions in execJunk.c
+ */
+extern JunkFilter *ExecInitJunkFilter(List *targetList);
+extern bool ExecGetJunkAttribute(JunkFilter *junkfilter, TupleTableSlot *slot,
+ char *attrName, Datum *value, bool *isNull);
+extern HeapTuple ExecRemoveJunk(JunkFilter *junkfilter, TupleTableSlot *slot);
+
+
+/*
+ * prototypes from functions in execMain.c
+ */
+extern TupleDesc ExecutorStart(QueryDesc *queryDesc, EState *estate);
+extern TupleTableSlot* ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature, int count);
+extern void ExecutorEnd(QueryDesc *queryDesc, EState *estate);
+
+/*
+ * prototypes from functions in execProcnode.c
+ */
+extern bool ExecInitNode(Plan *node, EState *estate, Plan *parent);
+extern TupleTableSlot *ExecProcNode(Plan *node, Plan *parent);
+extern int ExecCountSlotsNode(Plan *node);
+extern void ExecEndNode(Plan *node, Plan *parent);
+
+/*
+ * prototypes from functions in execQual.c
+ */
+extern bool execConstByVal;
+extern int execConstLen;
+
+extern Datum ExecExtractResult(TupleTableSlot *slot, AttrNumber attnum,
+ bool *isNull);
+extern Datum ExecEvalVar(Var *variable, ExprContext *econtext, bool *isNull);
+extern Datum ExecEvalParam(Param *expression, ExprContext *econtext,
+ bool *isNull);
+extern char *GetAttributeByNum(TupleTableSlot *slot, AttrNumber attrno,
+ bool *isNull);
+extern char *att_by_num(TupleTableSlot *slot, AttrNumber attrno,
+ bool *isNull);
+/* stop here */
+extern char *GetAttributeByName(TupleTableSlot *slot, char *attname,
+ bool *isNull);
+extern char *att_by_name(TupleTableSlot *slot, char *attname, bool *isNull);
+extern void ExecEvalFuncArgs(FunctionCachePtr fcache, ExprContext *econtext,
+ List *argList, Datum argV[], bool *argIsDone);
+extern Datum ExecMakeFunctionResult(Node *node, List *arguments,
+ ExprContext *econtext, bool *isNull, bool *isDone);
+extern Datum ExecEvalOper(Expr *opClause, ExprContext *econtext,
+ bool *isNull);
+extern Datum ExecEvalFunc(Expr *funcClause, ExprContext *econtext,
+ bool *isNull, bool *isDone);
+extern Datum ExecEvalNot(Expr *notclause, ExprContext *econtext, bool *isNull);
+extern Datum ExecEvalOr(Expr *orExpr, ExprContext *econtext, bool *isNull);
+extern Datum ExecEvalAnd(Expr *andExpr, ExprContext *econtext, bool *isNull);
+extern Datum ExecEvalExpr(Node *expression, ExprContext *econtext, bool *isNull,
+ bool *isDone);
+extern bool ExecQualClause(Node *clause, ExprContext *econtext);
+extern bool ExecQual(List *qual, ExprContext *econtext);
+extern int ExecTargetListLength(List *targetlist);
+extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo, bool *isDone);
+
+/*
+ * prototypes from functions in execScan.c
+ */
+extern TupleTableSlot *ExecScan(Scan *node, TupleTableSlot* (*accessMtd)());
+
+/*
+ * prototypes from functions in execTuples.c
+ */
+extern TupleTable ExecCreateTupleTable(int initialSize);
+extern void ExecDestroyTupleTable(TupleTable table, bool shouldFree);
+extern TupleTableSlot* ExecAllocTableSlot(TupleTable table);
+extern TupleTableSlot* ExecStoreTuple(HeapTuple tuple,
+ TupleTableSlot *slot,
+ Buffer buffer,
+ bool shouldFree);
+extern TupleTableSlot* ExecClearTuple(TupleTableSlot* slot);
+extern bool ExecSlotPolicy(TupleTableSlot *slot);
+extern bool ExecSetSlotPolicy(TupleTableSlot *slot, bool shouldFree);
+extern TupleDesc ExecSetSlotDescriptor(TupleTableSlot *slot,
+ TupleDesc tupdesc);
+extern void ExecSetSlotDescriptorIsNew(TupleTableSlot *slot, bool isNew);
+extern TupleDesc ExecSetNewSlotDescriptor(TupleTableSlot *slot,
+ TupleDesc tupdesc);
+extern Buffer ExecSetSlotBuffer(TupleTableSlot *slot, Buffer b);
+extern void ExecIncrSlotBufferRefcnt(TupleTableSlot *slot);
+extern bool TupIsNull(TupleTableSlot* slot);
+extern bool ExecSlotDescriptorIsNew(TupleTableSlot *slot);
+extern void ExecInitResultTupleSlot(EState *estate, CommonState *commonstate);
+extern void ExecInitScanTupleSlot(EState *estate,
+ CommonScanState *commonscanstate);
+extern void ExecInitMarkedTupleSlot(EState *estate, MergeJoinState *mergestate);
+extern void ExecInitOuterTupleSlot(EState *estate, HashJoinState *hashstate);
+extern void ExecInitHashTupleSlot(EState *estate, HashJoinState *hashstate);
+extern TupleTableSlot *NodeGetResultTupleSlot(Plan *node);
+
+extern TupleDesc ExecGetTupType(Plan *node);
+extern TupleDesc ExecTypeFromTL(List *targetList);
+
+/*
+ * prototypes from functions in execTuples.c
+ */
+extern void ResetTupleCount();
+extern void DisplayTupleCount(FILE *statfp);
+extern void ExecAssignNodeBaseInfo(EState *estate, CommonState *basenode,
+ Plan *parent);
+extern void ExecAssignExprContext(EState *estate, CommonState *commonstate);
+extern void ExecAssignResultType(CommonState *commonstate,
+ TupleDesc tupDesc);
+extern void ExecAssignResultTypeFromOuterPlan(Plan *node,
+ CommonState *commonstate);
+extern void ExecAssignResultTypeFromTL(Plan *node, CommonState *commonstate);
+extern TupleDesc ExecGetResultType(CommonState *commonstate);
+extern void ExecFreeResultType(CommonState *commonstate);
+extern void ExecAssignProjectionInfo(Plan *node, CommonState *commonstate);
+extern void ExecFreeProjectionInfo(CommonState *commonstate);
+extern TupleDesc ExecGetScanType(CommonScanState *csstate);
+extern void ExecFreeScanType(CommonScanState *csstate);
+extern void ExecAssignScanType(CommonScanState *csstate,
+ TupleDesc tupDesc);
+extern void ExecAssignScanTypeFromOuterPlan(Plan *node,
+ CommonScanState *csstate);
+extern AttributeTupleForm ExecGetTypeInfo(Relation relDesc);
+
+extern void ExecGetIndexKeyInfo(IndexTupleForm indexTuple, int *numAttsOutP,
+ AttrNumber **attsOutP, FuncIndexInfoPtr fInfoP);
+extern void ExecOpenIndices(Oid resultRelationOid,
+ RelationInfo *resultRelationInfo);
+extern void ExecCloseIndices(RelationInfo *resultRelationInfo);
+extern IndexTuple ExecFormIndexTuple(HeapTuple heapTuple,
+ Relation heapRelation, Relation indexRelation, IndexInfo *indexInfo);
+extern void ExecInsertIndexTuples(TupleTableSlot *slot, ItemPointer tupleid,
+ EState *estate);
+
+
+/* ----------------------------------------------------------------
+ * the end
+ * ----------------------------------------------------------------
+ */
+
+#endif /* EXECUTOR_H */
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
new file mode 100644
index 00000000000..2f6e29d8277
--- /dev/null
+++ b/src/backend/executor/functions.c
@@ -0,0 +1,388 @@
+/*-------------------------------------------------------------------------
+ *
+ * functions.c--
+ * Routines to handle functions called from the executor
+ * Putting this stuff in fmgr makes the postmaster a mess....
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.1.1.1 1996/07/09 06:21:25 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "nodes/primnodes.h"
+#include "nodes/relation.h"
+#include "nodes/execnodes.h"
+#include "nodes/plannodes.h"
+
+#include "catalog/pg_proc.h"
+#include "parser/parse_query.h"
+#include "tcop/pquery.h"
+#include "tcop/tcopprot.h"
+#include "nodes/params.h"
+#include "fmgr.h"
+#include "utils/fcache.h"
+#include "utils/datum.h"
+#include "utils/elog.h"
+#include "utils/palloc.h"
+#include "utils/syscache.h"
+#include "catalog/pg_language.h"
+#include "access/heapam.h"
+#include "access/xact.h"
+#include "executor/executor.h"
+#include "executor/functions.h"
+
+#undef new
+
+typedef enum {F_EXEC_START, F_EXEC_RUN, F_EXEC_DONE} ExecStatus;
+
+typedef struct local_es {
+ QueryDesc *qd;
+ EState *estate;
+ struct local_es *next;
+ ExecStatus status;
+} execution_state;
+
+#define LAST_POSTQUEL_COMMAND(es) ((es)->next == (execution_state *)NULL)
+
+/* non-export function prototypes */
+static TupleDesc postquel_start(execution_state *es);
+static execution_state *init_execution_state(FunctionCachePtr fcache,
+ char *args[]);
+static TupleTableSlot *postquel_getnext(execution_state *es);
+static void postquel_end(execution_state *es);
+static void postquel_sub_params(execution_state *es, int nargs,
+ char *args[], bool *nullV);
+static Datum postquel_execute(execution_state *es, FunctionCachePtr fcache,
+ List *fTlist, char **args, bool *isNull);
+
+
+Datum
+ProjectAttribute(TupleDesc TD,
+ TargetEntry *tlist,
+ HeapTuple tup,
+ bool *isnullP)
+{
+ Datum val,valueP;
+ Var *attrVar = (Var *)tlist->expr;
+ AttrNumber attrno = attrVar->varattno;
+
+
+ val = PointerGetDatum(heap_getattr(tup,
+ InvalidBuffer,
+ attrno,
+ TD,
+ isnullP));
+ if (*isnullP)
+ return (Datum) NULL;
+
+ valueP = datumCopy(val,
+ TD->attrs[attrno-1]->atttypid,
+ TD->attrs[attrno-1]->attbyval,
+ (Size) TD->attrs[attrno-1]->attlen);
+ return valueP;
+}
+
+static execution_state *
+init_execution_state(FunctionCachePtr fcache,
+ char *args[])
+{
+ execution_state *newes;
+ execution_state *nextes;
+ execution_state *preves;
+ QueryTreeList *queryTree_list;
+ int i;
+ List *planTree_list;
+ int nargs;
+
+ nargs = fcache->nargs;
+
+ newes = (execution_state *) palloc(sizeof(execution_state));
+ nextes = newes;
+ preves = (execution_state *)NULL;
+
+
+ planTree_list = (List *)
+ pg_plan(fcache->src, fcache->argOidVect, nargs, &queryTree_list, None);
+
+ for (i=0; i < queryTree_list->len; i++) {
+ EState *estate;
+ Query *queryTree = (Query*) (queryTree_list->qtrees[i]);
+ Plan *planTree = lfirst(planTree_list);
+
+ if (!nextes)
+ nextes = (execution_state *) palloc(sizeof(execution_state));
+ if (preves)
+ preves->next = nextes;
+
+ nextes->next = NULL;
+ nextes->status = F_EXEC_START;
+ nextes->qd = CreateQueryDesc(queryTree,
+ planTree,
+ None);
+ estate = CreateExecutorState();
+
+ if (nargs > 0) {
+ int i;
+ ParamListInfo paramLI;
+
+ paramLI =
+ (ParamListInfo)palloc((nargs+1)*sizeof(ParamListInfoData));
+
+ memset(paramLI, 0, nargs*sizeof(ParamListInfoData));
+
+ estate->es_param_list_info = paramLI;
+
+ for (i=0; i<nargs; paramLI++, i++) {
+ paramLI->kind = PARAM_NUM;
+ paramLI->id = i+1;
+ paramLI->isnull = false;
+ paramLI->value = (Datum) NULL;
+ }
+ paramLI->kind = PARAM_INVALID;
+ }
+ else
+ estate->es_param_list_info = (ParamListInfo)NULL;
+ nextes->estate = estate;
+ preves = nextes;
+ nextes = (execution_state *)NULL;
+
+ planTree_list = lnext(planTree_list);
+ }
+
+ return newes;
+}
+
+static TupleDesc
+postquel_start(execution_state *es)
+{
+ return ExecutorStart(es->qd, es->estate);
+}
+
+static TupleTableSlot *
+postquel_getnext(execution_state *es)
+{
+ int feature;
+
+ feature = (LAST_POSTQUEL_COMMAND(es)) ? EXEC_RETONE : EXEC_RUN;
+
+ return ExecutorRun(es->qd, es->estate, feature, 0);
+}
+
+static void
+postquel_end(execution_state *es)
+{
+ ExecutorEnd(es->qd, es->estate);
+}
+
+static void
+postquel_sub_params(execution_state *es,
+ int nargs,
+ char *args[],
+ bool *nullV)
+{
+ ParamListInfo paramLI;
+ EState *estate;
+
+ estate = es->estate;
+ paramLI = estate->es_param_list_info;
+
+ while (paramLI->kind != PARAM_INVALID) {
+ if (paramLI->kind == PARAM_NUM) {
+ Assert(paramLI->id <= nargs);
+ paramLI->value = (Datum)args[(paramLI->id - 1)];
+ paramLI->isnull = nullV[(paramLI->id - 1)];
+ }
+ paramLI++;
+ }
+}
+
+static TupleTableSlot *
+copy_function_result(FunctionCachePtr fcache,
+ TupleTableSlot *resultSlot)
+{
+ TupleTableSlot *funcSlot;
+ TupleDesc resultTd;
+ HeapTuple newTuple;
+ HeapTuple oldTuple;
+
+ Assert(! TupIsNull(resultSlot));
+ oldTuple = resultSlot->val;
+
+ funcSlot = (TupleTableSlot*)fcache->funcSlot;
+
+ if (funcSlot == (TupleTableSlot*)NULL)
+ return resultSlot;
+
+ resultTd = resultSlot->ttc_tupleDescriptor;
+ /*
+ * When the funcSlot is NULL we have to initialize the funcSlot's
+ * tuple descriptor.
+ */
+ if (TupIsNull(funcSlot)) {
+ int i= 0;
+ TupleDesc funcTd = funcSlot->ttc_tupleDescriptor;
+
+ while (i < oldTuple->t_natts) {
+ funcTd->attrs[i] =
+ (AttributeTupleForm)palloc(ATTRIBUTE_TUPLE_SIZE);
+ memmove(funcTd->attrs[i],
+ resultTd->attrs[i],
+ ATTRIBUTE_TUPLE_SIZE);
+ i++;
+ }
+ }
+
+ newTuple = heap_copytuple(oldTuple);
+
+ return ExecStoreTuple(newTuple,funcSlot,InvalidBuffer,true);
+}
+
+static Datum
+postquel_execute(execution_state *es,
+ FunctionCachePtr fcache,
+ List *fTlist,
+ char **args,
+ bool *isNull)
+{
+ TupleTableSlot *slot;
+ Datum value;
+
+ if (es->status == F_EXEC_START)
+ {
+ (void) postquel_start(es);
+ es->status = F_EXEC_RUN;
+ }
+
+ if (fcache->nargs > 0)
+ postquel_sub_params(es, fcache->nargs, args, fcache->nullVect);
+
+ slot = postquel_getnext(es);
+
+ if (TupIsNull(slot)) {
+ postquel_end(es);
+ es->status = F_EXEC_DONE;
+ *isNull = true;
+ /*
+ * If this isn't the last command for the function
+ * we have to increment the command
+ * counter so that subsequent commands can see changes made
+ * by previous ones.
+ */
+ if (!LAST_POSTQUEL_COMMAND(es)) CommandCounterIncrement();
+ return (Datum)NULL;
+ }
+
+ if (LAST_POSTQUEL_COMMAND(es)) {
+ TupleTableSlot *resSlot;
+
+ /*
+ * Copy the result. copy_function_result is smart enough
+ * to do nothing when no action is called for. This helps
+ * reduce the logic and code redundancy here.
+ */
+ resSlot = copy_function_result(fcache, slot);
+ if (fTlist != NIL) {
+ HeapTuple tup;
+ TargetEntry *tle = lfirst(fTlist);
+
+ tup = resSlot->val;
+ value = ProjectAttribute(resSlot->ttc_tupleDescriptor,
+ tle,
+ tup,
+ isNull);
+ }else {
+ value = (Datum)resSlot;
+ *isNull = false;
+ }
+
+ /*
+ * If this is a single valued function we have to end the
+ * function execution now.
+ */
+ if (fcache->oneResult) {
+ postquel_end(es);
+ es->status = F_EXEC_DONE;
+ }
+
+ return value;
+ }
+ /*
+ * If this isn't the last command for the function, we don't
+ * return any results, but we have to increment the command
+ * counter so that subsequent commands can see changes made
+ * by previous ones.
+ */
+ CommandCounterIncrement();
+ return (Datum)NULL;
+}
+
+Datum
+postquel_function(Func *funcNode, char **args, bool *isNull, bool *isDone)
+{
+ execution_state *es;
+ Datum result;
+ FunctionCachePtr fcache = funcNode->func_fcache;
+
+ es = (execution_state *) fcache->func_state;
+ if (es == NULL)
+ {
+ es = init_execution_state(fcache, args);
+ fcache->func_state = (char *) es;
+ }
+
+ while (es && es->status == F_EXEC_DONE)
+ es = es->next;
+
+ Assert(es);
+ /*
+ * Execute each command in the function one after another until we're
+ * executing the final command and get a result or we run out of
+ * commands.
+ */
+ while (es != (execution_state *)NULL)
+ {
+ result = postquel_execute(es,
+ fcache,
+ funcNode->func_tlist,
+ args,
+ isNull);
+ if (es->status != F_EXEC_DONE)
+ break;
+ es = es->next;
+ }
+
+ /*
+ * If we've gone through every command in this function, we are done.
+ */
+ if (es == (execution_state *)NULL)
+ {
+ /*
+ * Reset the execution states to start over again
+ */
+ es = (execution_state *)fcache->func_state;
+ while (es)
+ {
+ es->status = F_EXEC_START;
+ es = es->next;
+ }
+ /*
+ * Let caller know we're finished.
+ */
+ *isDone = true;
+ return (fcache->oneResult) ? result : (Datum)NULL;
+ }
+ /*
+ * If we got a result from a command within the function it has
+ * to be the final command. All others shouldn't be returing
+ * anything.
+ */
+ Assert ( LAST_POSTQUEL_COMMAND(es) );
+ *isDone = false;
+
+ return result;
+}
diff --git a/src/backend/executor/functions.h b/src/backend/executor/functions.h
new file mode 100644
index 00000000000..1a1a88b36a1
--- /dev/null
+++ b/src/backend/executor/functions.h
@@ -0,0 +1,22 @@
+/*-------------------------------------------------------------------------
+ *
+ * functions.h--
+ *
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id: functions.h,v 1.1.1.1 1996/07/09 06:21:25 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef FUNCTIONS_H
+#define FUNCTIONS_H
+
+extern Datum ProjectAttribute(TupleDesc TD, TargetEntry *tlist,
+ HeapTuple tup, bool *isnullP);
+
+extern Datum postquel_function(Func *funcNode, char **args,
+ bool *isNull, bool *isDone);
+
+#endif /* FUNCTIONS_H */
diff --git a/src/backend/executor/hashjoin.h b/src/backend/executor/hashjoin.h
new file mode 100644
index 00000000000..e7ae086fe16
--- /dev/null
+++ b/src/backend/executor/hashjoin.h
@@ -0,0 +1,82 @@
+/*-------------------------------------------------------------------------
+ *
+ * hashjoin.h--
+ * internal structures for hash table and buckets
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id: hashjoin.h,v 1.1.1.1 1996/07/09 06:21:25 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef HASHJOIN_H
+#define HASHJOIN_H
+
+#include "access/htup.h"
+#include "storage/ipc.h"
+
+/* -----------------
+ * have to use relative address as pointers in the hashtable
+ * because the hashtable may reallocate in difference processes
+ * -----------------
+ */
+typedef int RelativeAddr;
+
+/* ------------------
+ * the relative addresses are always relative to the head of the
+ * hashtable, the following macro converts them to absolute address.
+ * ------------------
+ */
+#define ABSADDR(X) ((X) < 0 ? NULL: (char*)hashtable + X)
+#define RELADDR(X) (RelativeAddr)((char*)(X) - (char*)hashtable)
+
+typedef char **charPP;
+typedef int *intP;
+
+/* ----------------------------------------------------------------
+ * hash-join hash table structures
+ * ----------------------------------------------------------------
+ */
+typedef struct HashTableData {
+ int nbuckets;
+ int totalbuckets;
+ int bucketsize;
+ IpcMemoryId shmid;
+ RelativeAddr top; /* char* */
+ RelativeAddr bottom; /* char* */
+ RelativeAddr overflownext; /* char* */
+ RelativeAddr batch; /* char* */
+ RelativeAddr readbuf; /* char* */
+ int nbatch;
+ RelativeAddr outerbatchNames; /* RelativeAddr* */
+ RelativeAddr outerbatchPos; /* RelativeAddr* */
+ RelativeAddr innerbatchNames; /* RelativeAddr* */
+ RelativeAddr innerbatchPos; /* RelativeAddr* */
+ RelativeAddr innerbatchSizes; /* int* */
+ int curbatch;
+ int nprocess;
+ int pcount;
+} HashTableData; /* real hash table follows here */
+
+typedef HashTableData *HashJoinTable;
+
+typedef struct OverflowTupleData {
+ RelativeAddr tuple; /* HeapTuple */
+ RelativeAddr next; /* struct OverflowTupleData * */
+} OverflowTupleData; /* real tuple follows here */
+
+typedef OverflowTupleData *OverflowTuple;
+
+typedef struct HashBucketData {
+ RelativeAddr top; /* HeapTuple */
+ RelativeAddr bottom; /* HeapTuple */
+ RelativeAddr firstotuple; /* OverflowTuple */
+ RelativeAddr lastotuple; /* OverflowTuple */
+} HashBucketData; /* real bucket follows here */
+
+typedef HashBucketData *HashBucket;
+
+#define HASH_PERMISSION 0700
+
+#endif /* HASHJOIN_H */
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
new file mode 100644
index 00000000000..ee187367c74
--- /dev/null
+++ b/src/backend/executor/nodeAgg.c
@@ -0,0 +1,558 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeAgg.c--
+ * Routines to handle aggregate nodes.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * NOTE
+ * The implementation of Agg node has been reworked to handle legal
+ * SQL aggregates. (Do not expect POSTQUEL semantics.) -- ay 2/95
+ *
+ * IDENTIFICATION
+ * /usr/local/devel/pglite/cvs/src/backend/executor/nodeAgg.c,v 1.13 1995/08/01 20:19:07 jolly Exp
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "access/heapam.h"
+#include "catalog/pg_aggregate.h"
+#include "catalog/catalog.h"
+#include "executor/executor.h"
+#include "executor/nodeAgg.h"
+#include "storage/bufmgr.h"
+#include "utils/palloc.h"
+#include "parser/catalog_utils.h"
+
+/*
+ * AggFuncInfo -
+ * keeps the transition functions information around
+ */
+typedef struct AggFuncInfo {
+ Oid xfn1_oid;
+ Oid xfn2_oid;
+ Oid finalfn_oid;
+ func_ptr xfn1;
+ func_ptr xfn2;
+ func_ptr finalfn;
+ int xfn1_nargs;
+ int xfn2_nargs;
+ int finalfn_nargs;
+} AggFuncInfo;
+
+static Datum aggGetAttr(TupleTableSlot *tuple, Aggreg *agg, bool *isNull);
+
+
+/* ---------------------------------------
+ *
+ * ExecAgg -
+ *
+ * ExecAgg receives tuples from its outer subplan and aggregates over
+ * the appropriate attribute for each (unique) aggregate in the target
+ * list. (The number of tuples to aggregate over depends on whether a
+ * GROUP BY clause is present. It might be the number of tuples in a
+ * group or all the tuples that satisfy the qualifications.) The value of
+ * each aggregate is stored in the expression context for ExecProject to
+ * evaluate the result tuple.
+ *
+ * ExecAgg evaluates each aggregate in the following steps: (initcond1,
+ * initcond2 are the initial values and sfunc1, sfunc2, and finalfunc are
+ * the transition functions.)
+ *
+ * value1[i] = initcond1
+ * value2[i] = initcond2
+ * forall tuples do
+ * value1[i] = sfunc1(aggregate_attribute, value1[i])
+ * value2[i] = sfunc2(value2[i])
+ * value1[i] = finalfunc(value1[i], value2[i])
+ *
+ * If the outer subplan is a Group node, ExecAgg returns as many tuples
+ * as there are groups.
+ *
+ * XXX handling of NULL doesn't work
+ *
+ * OLD COMMENTS
+ *
+ * XXX Aggregates should probably have another option: what to do
+ * with transfn2 if we hit a null value. "count" (transfn1 = null,
+ * transfn2 = increment) will want to have transfn2 called; "avg"
+ * (transfn1 = add, transfn2 = increment) will not. -pma 1/3/93
+ *
+ * ------------------------------------------
+ */
+TupleTableSlot *
+ExecAgg(Agg *node)
+{
+ AggState *aggstate;
+ EState *estate;
+ Aggreg **aggregates;
+ Plan *outerPlan;
+ int i, nagg;
+ Datum *value1, *value2;
+ int *noInitValue;
+ AggFuncInfo *aggFuncInfo;
+ long nTuplesAgged = 0;
+ ExprContext *econtext;
+ ProjectionInfo *projInfo;
+ TupleTableSlot *resultSlot;
+ HeapTuple oneTuple;
+ char* nulls;
+ bool isDone;
+ bool isNull = FALSE, isNull1 = FALSE, isNull2 = FALSE;
+
+ /* ---------------------
+ * get state info from node
+ * ---------------------
+ */
+ aggstate = node->aggstate;
+ if (aggstate->agg_done)
+ return NULL;
+
+ estate = node->plan.state;
+ econtext = aggstate->csstate.cstate.cs_ExprContext;
+ aggregates = node->aggs;
+ nagg = node->numAgg;
+
+ value1 = node->aggstate->csstate.cstate.cs_ExprContext->ecxt_values;
+ nulls = node->aggstate->csstate.cstate.cs_ExprContext->ecxt_nulls;
+
+ value2 = (Datum *)palloc(sizeof(Datum) * nagg);
+ memset(value2, 0, sizeof(Datum) * nagg);
+
+ aggFuncInfo = (AggFuncInfo *)palloc(sizeof(AggFuncInfo) * nagg);
+ memset(aggFuncInfo, 0, sizeof(AggFuncInfo) * nagg);
+
+ noInitValue = (int *)palloc(sizeof(int) * nagg);
+ memset(noInitValue, 0, sizeof(noInitValue) * nagg);
+
+ outerPlan = outerPlan(node);
+ oneTuple = NULL;
+
+ projInfo = aggstate->csstate.cstate.cs_ProjInfo;
+
+ for(i = 0; i < nagg; i++) {
+ Aggreg *agg;
+ char *aggname;
+ HeapTuple aggTuple;
+ Form_pg_aggregate aggp;
+ Oid xfn1_oid, xfn2_oid, finalfn_oid;
+ func_ptr xfn1_ptr, xfn2_ptr, finalfn_ptr;
+ int xfn1_nargs, xfn2_nargs, finalfn_nargs;
+
+ agg = aggregates[i];
+
+ /* ---------------------
+ * find transfer functions of all the aggregates and initialize
+ * their initial values
+ * ---------------------
+ */
+ aggname = agg->aggname;
+ aggTuple = SearchSysCacheTuple(AGGNAME,
+ PointerGetDatum(aggname),
+ ObjectIdGetDatum(agg->basetype),
+ 0,0);
+ if (!HeapTupleIsValid(aggTuple))
+ elog(WARN, "ExecAgg: cache lookup failed for aggregate \"%s\"(%s)",
+ aggname,
+ tname(get_id_type(agg->basetype)));
+ aggp = (Form_pg_aggregate) GETSTRUCT(aggTuple);
+
+ xfn1_oid = aggp->aggtransfn1;
+ xfn2_oid = aggp->aggtransfn2;
+ finalfn_oid = aggp->aggfinalfn;
+
+ if (OidIsValid(finalfn_oid)) {
+ fmgr_info(finalfn_oid, &finalfn_ptr, &finalfn_nargs);
+ aggFuncInfo[i].finalfn_oid = finalfn_oid;
+ aggFuncInfo[i].finalfn = finalfn_ptr;
+ aggFuncInfo[i].finalfn_nargs = finalfn_nargs;
+ }
+
+ if (OidIsValid(xfn2_oid)) {
+ fmgr_info(xfn2_oid, &xfn2_ptr, &xfn2_nargs);
+ aggFuncInfo[i].xfn2_oid = xfn2_oid;
+ aggFuncInfo[i].xfn2 = xfn2_ptr;
+ aggFuncInfo[i].xfn2_nargs = xfn2_nargs;
+ value2[i] = (Datum)AggNameGetInitVal((char*)aggname,
+ aggp->aggbasetype,
+ 2,
+ &isNull2);
+ /* ------------------------------------------
+ * If there is a second transition function, its initial
+ * value must exist -- as it does not depend on data values,
+ * we have no other way of determining an initial value.
+ * ------------------------------------------
+ */
+ if (isNull2)
+ elog(WARN, "ExecAgg: agginitval2 is null");
+ }
+
+ if (OidIsValid(xfn1_oid)) {
+ fmgr_info(xfn1_oid, &xfn1_ptr, &xfn1_nargs);
+ aggFuncInfo[i].xfn1_oid = xfn1_oid;
+ aggFuncInfo[i].xfn1 = xfn1_ptr;
+ aggFuncInfo[i].xfn1_nargs = xfn1_nargs;
+ value1[i] = (Datum)AggNameGetInitVal((char*)aggname,
+ aggp->aggbasetype,
+ 1,
+ &isNull1);
+
+ /* ------------------------------------------
+ * If the initial value for the first transition function
+ * doesn't exist in the pg_aggregate table then we let
+ * the first value returned from the outer procNode become
+ * the initial value. (This is useful for aggregates like
+ * max{} and min{}.)
+ * ------------------------------------------
+ */
+ if (isNull1) {
+ noInitValue[i] = 1;
+ nulls[i] = 1;
+ }
+ }
+ }
+
+ /* ----------------
+ * for each tuple from the the outer plan, apply all the aggregates
+ * ----------------
+ */
+ for (;;) {
+ HeapTuple outerTuple = NULL;
+ TupleTableSlot *outerslot;
+
+ isNull = isNull1 = isNull2 = 0;
+ outerslot = ExecProcNode(outerPlan, (Plan*)node);
+ if (outerslot) outerTuple = outerslot->val;
+ if (!HeapTupleIsValid(outerTuple)) {
+ /* when the outerplan doesn't return a single tuple,
+ create a dummy heaptuple anyway
+ because we still need to return a valid aggregate value.
+ The value returned will be the initial values of the
+ transition functions */
+ if (nTuplesAgged == 0) {
+ TupleDesc tupType;
+ Datum *tupValue;
+ char* null_array;
+
+ tupType = aggstate->csstate.css_ScanTupleSlot->ttc_tupleDescriptor;
+ tupValue = projInfo->pi_tupValue;
+
+ /* initially, set all the values to NULL */
+ null_array = malloc(nagg);
+ for (i=0;i<nagg;i++)
+ null_array[i] = 'n';
+ oneTuple = heap_formtuple(tupType, tupValue, null_array);
+ free(null_array);
+ }
+ break;
+ }
+
+ for(i = 0; i < nagg; i++) {
+ AttrNumber attnum;
+ int2 attlen;
+ Datum newVal;
+ AggFuncInfo *aggfns = &aggFuncInfo[i];
+ Datum args[2];
+
+ newVal = aggGetAttr(outerslot,
+ aggregates[i],
+ &isNull);
+
+ if (isNull)
+ continue; /* ignore this tuple for this agg */
+
+ if (aggfns->xfn1) {
+ if (noInitValue[i]) {
+ /*
+ * value1 and value2 has not been initialized. This
+ * is the first non-NULL value. We use it as the
+ * initial value.
+ */
+ /* but we can't just use it straight, we have
+ to make a copy of it since the tuple from which
+ it came will be freed on the next iteration
+ of the scan */
+ attnum = ((Var*)aggregates[i]->target)->varattno;
+ attlen = outerslot->ttc_tupleDescriptor->attrs[attnum-1]->attlen;
+ if (attlen == -1) {
+ /* variable length */
+ attlen = VARSIZE((struct varlena*) newVal);
+ }
+ value1[i] = (Datum)palloc(attlen);
+ if (outerslot->ttc_tupleDescriptor->attrs[attnum-1]->attbyval)
+ value1[i] = newVal;
+ else
+ memmove((char*) (value1[i]), (char*) (newVal), attlen);
+ /* value1[i] = newVal; */
+ noInitValue[i] = 0;
+ nulls[i] = 0;
+ } else {
+ /*
+ * apply the transition functions.
+ */
+ args[0] = value1[i];
+ args[1] = newVal;
+ value1[i] =
+ (Datum)fmgr_c(aggfns->xfn1, aggfns->xfn1_oid,
+ aggfns->xfn1_nargs, (FmgrValues *)args,
+ &isNull1);
+ Assert(!isNull1);
+ }
+ }
+
+ if (aggfns->xfn2) {
+ Datum xfn2_val = value2[i];
+
+ value2[i] =
+ (Datum)fmgr_c(aggfns->xfn2, aggfns->xfn2_oid,
+ aggfns->xfn2_nargs,
+ (FmgrValues *)&xfn2_val, &isNull2);
+ Assert(!isNull2);
+ }
+ }
+
+ /*
+ * keep this for the projection (we only need one of these -
+ * all the tuples we aggregate over share the same group column)
+ */
+ if (!oneTuple) {
+ oneTuple = heap_copytuple(outerslot->val);
+ }
+
+ nTuplesAgged++;
+ }
+
+ /* --------------
+ * finalize the aggregate (if necessary), and get the resultant value
+ * --------------
+ */
+ for(i = 0; i < nagg; i++) {
+ char *args[2];
+ AggFuncInfo *aggfns = &aggFuncInfo[i];
+
+ if (aggfns->finalfn && nTuplesAgged > 0) {
+ if (aggfns->finalfn_nargs > 1) {
+ args[0] = (char*)value1[i];
+ args[1] = (char*)value2[i];
+ } else if (aggfns->xfn1) {
+ args[0] = (char*)value1[i];
+ } else if (aggfns->xfn2) {
+ args[0] = (char*)value2[i];
+ } else
+ elog(WARN, "ExecAgg: no valid transition functions??");
+ value1[i] =
+ (Datum)fmgr_c(aggfns->finalfn, aggfns->finalfn_oid,
+ aggfns->finalfn_nargs, (FmgrValues *) args,
+ &(nulls[i]));
+ } else if (aggfns->xfn1) {
+ /*
+ * value in the right place, ignore. (If you remove this
+ * case, fix the else part. -ay 2/95)
+ */
+ } else if (aggfns->xfn2) {
+ value1[i] = value2[i];
+ } else
+ elog(WARN, "ExecAgg: no valid transition functions??");
+ }
+
+ /*
+ * whether the aggregation is done depends on whether we are doing
+ * aggregation over groups or the entire table
+ */
+ if (nodeTag(outerPlan)==T_Group) {
+ /* aggregation over groups */
+ aggstate->agg_done = ((Group*)outerPlan)->grpstate->grp_done;
+ } else {
+ aggstate->agg_done = TRUE;
+ }
+
+ /* ----------------
+ * form a projection tuple, store it in the result tuple
+ * slot and return it.
+ * ----------------
+ */
+ ExecStoreTuple(oneTuple,
+ aggstate->csstate.css_ScanTupleSlot,
+ InvalidBuffer,
+ false);
+ econtext->ecxt_scantuple = aggstate->csstate.css_ScanTupleSlot;
+ resultSlot = ExecProject(projInfo, &isDone);
+
+ if (oneTuple)
+ pfree(oneTuple);
+
+ return resultSlot;
+}
+
+/* -----------------
+ * ExecInitAgg
+ *
+ * Creates the run-time information for the agg node produced by the
+ * planner and initializes its outer subtree
+ * -----------------
+ */
+bool
+ExecInitAgg(Agg *node, EState *estate, Plan *parent)
+{
+ AggState *aggstate;
+ Plan *outerPlan;
+ ExprContext *econtext;
+
+ /*
+ * assign the node's execution state
+ */
+ node->plan.state = estate;
+
+ /*
+ * create state structure
+ */
+ aggstate = makeNode(AggState);
+ node->aggstate = aggstate;
+ aggstate->agg_done = FALSE;
+
+ /*
+ * assign node's base id and create expression context
+ */
+ ExecAssignNodeBaseInfo(estate, &aggstate->csstate.cstate,
+ (Plan*) parent);
+ ExecAssignExprContext(estate, &aggstate->csstate.cstate);
+
+#define AGG_NSLOTS 2
+ /*
+ * tuple table initialization
+ */
+ ExecInitScanTupleSlot(estate, &aggstate->csstate);
+ ExecInitResultTupleSlot(estate, &aggstate->csstate.cstate);
+
+ econtext = aggstate->csstate.cstate.cs_ExprContext;
+ econtext->ecxt_values =
+ (Datum *)palloc(sizeof(Datum) * node->numAgg);
+ memset(econtext->ecxt_values, 0, sizeof(Datum) * node->numAgg);
+ econtext->ecxt_nulls = (char *)palloc(node->numAgg);
+ memset(econtext->ecxt_nulls, 0, node->numAgg);
+
+ /*
+ * initializes child nodes
+ */
+ outerPlan = outerPlan(node);
+ ExecInitNode(outerPlan, estate, (Plan *)node);
+
+ /* ----------------
+ * initialize tuple type.
+ * ----------------
+ */
+ ExecAssignScanTypeFromOuterPlan((Plan *) node, &aggstate->csstate);
+
+ /*
+ * Initialize tuple type for both result and scan.
+ * This node does no projection
+ */
+ ExecAssignResultTypeFromTL((Plan*) node, &aggstate->csstate.cstate);
+ ExecAssignProjectionInfo((Plan*)node, &aggstate->csstate.cstate);
+
+ return TRUE;
+}
+
+int
+ExecCountSlotsAgg(Agg *node)
+{
+ return ExecCountSlotsNode(outerPlan(node)) +
+ ExecCountSlotsNode(innerPlan(node)) +
+ AGG_NSLOTS;
+}
+
+/* ------------------------
+ * ExecEndAgg(node)
+ *
+ * -----------------------
+ */
+void
+ExecEndAgg(Agg *node)
+{
+ AggState *aggstate;
+ Plan *outerPlan;
+
+ aggstate = node->aggstate;
+
+ ExecFreeProjectionInfo(&aggstate->csstate.cstate);
+
+ outerPlan = outerPlan(node);
+ ExecEndNode(outerPlan, (Plan*)node);
+
+ /* clean up tuple table */
+ ExecClearTuple(aggstate->csstate.css_ScanTupleSlot);
+}
+
+
+/*****************************************************************************
+ * Support Routines
+ *****************************************************************************/
+
+/*
+ * aggGetAttr -
+ * get the attribute (specified in the Var node in agg) to aggregate
+ * over from the tuple
+ */
+static Datum
+aggGetAttr(TupleTableSlot *slot,
+ Aggreg *agg,
+ bool *isNull)
+{
+ Datum result;
+ AttrNumber attnum;
+ HeapTuple heapTuple;
+ TupleDesc tuple_type;
+ Buffer buffer;
+
+ /* ----------------
+ * extract tuple information from the slot
+ * ----------------
+ */
+ heapTuple = slot->val;
+ tuple_type = slot->ttc_tupleDescriptor;
+ buffer = slot->ttc_buffer;
+
+ attnum = ((Var*)agg->target)->varattno;
+
+ /*
+ * If the attribute number is invalid, then we are supposed to
+ * return the entire tuple, we give back a whole slot so that
+ * callers know what the tuple looks like.
+ */
+ if (attnum == InvalidAttrNumber) {
+ TupleTableSlot *tempSlot;
+ TupleDesc td;
+ HeapTuple tup;
+
+ tempSlot = makeNode(TupleTableSlot);
+ tempSlot->ttc_shouldFree = false;
+ tempSlot->ttc_descIsNew = true;
+ tempSlot->ttc_tupleDescriptor = (TupleDesc)NULL,
+ tempSlot->ttc_buffer = InvalidBuffer;
+ tempSlot->ttc_whichplan = -1;
+
+ tup = heap_copytuple(slot->val);
+ td = CreateTupleDescCopy(slot->ttc_tupleDescriptor);
+
+ ExecSetSlotDescriptor(tempSlot, td);
+
+ ExecStoreTuple(tup, tempSlot, InvalidBuffer, true);
+ return (Datum) tempSlot;
+ }
+
+ result = (Datum)
+ heap_getattr(heapTuple, /* tuple containing attribute */
+ buffer, /* buffer associated with tuple */
+ attnum, /* attribute number of desired attribute */
+ tuple_type, /* tuple descriptor of tuple */
+ isNull); /* return: is attribute null? */
+
+ /* ----------------
+ * return null if att is null
+ * ----------------
+ */
+ if (*isNull)
+ return (Datum) NULL;
+
+ return result;
+}
diff --git a/src/backend/executor/nodeAgg.h b/src/backend/executor/nodeAgg.h
new file mode 100644
index 00000000000..51c2b2b2270
--- /dev/null
+++ b/src/backend/executor/nodeAgg.h
@@ -0,0 +1,21 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeAgg.h--
+ *
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id: nodeAgg.h,v 1.1.1.1 1996/07/09 06:21:26 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef NODEAGG_H
+#define NODEAGG_H
+
+extern TupleTableSlot *ExecAgg(Agg *node);
+extern bool ExecInitAgg(Agg *node, EState *estate, Plan *parent);
+extern int ExecCountSlotsAgg(Agg *node);
+extern void ExecEndAgg(Agg *node);
+
+#endif /* NODEAGG_H */
diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c
new file mode 100644
index 00000000000..0a6cd5d01bb
--- /dev/null
+++ b/src/backend/executor/nodeAppend.c
@@ -0,0 +1,483 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeAppend.c--
+ * routines to handle append nodes.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeAppend.c,v 1.1.1.1 1996/07/09 06:21:26 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+/* INTERFACE ROUTINES
+ * ExecInitAppend - initialize the append node
+ * ExecProcAppend - retrieve the next tuple from the node
+ * ExecEndAppend - shut down the append node
+ *
+ * NOTES
+ * Each append node contains a list of one or more subplans which
+ * must be iteratively processed (forwards or backwards).
+ * Tuples are retrieved by executing the 'whichplan'th subplan
+ * until the subplan stops returning tuples, at which point that
+ * plan is shut down and the next started up.
+ *
+ * Append nodes don't make use of their left and right
+ * subtrees, rather they maintain a list of subplans so
+ * a typical append node looks like this in the plan tree:
+ *
+ * ...
+ * /
+ * Append -------+------+------+--- nil
+ * / \ | | |
+ * nil nil ... ... ...
+ * subplans
+ *
+ * Append nodes are currently used to support inheritance
+ * queries, where several relations need to be scanned.
+ * For example, in our standard person/student/employee/student-emp
+ * example, where student and employee inherit from person
+ * and student-emp inherits from student and employee, the
+ * query:
+ *
+ * retrieve (e.name) from e in person*
+ *
+ * generates the plan:
+ *
+ * |
+ * Append -------+-------+--------+--------+
+ * / \ | | | |
+ * nil nil Scan Scan Scan Scan
+ * | | | |
+ * person employee student student-emp
+ */
+
+#include "executor/executor.h"
+#include "executor/nodeAppend.h"
+#include "executor/nodeIndexscan.h"
+#include "utils/palloc.h"
+#include "parser/parsetree.h" /* for rt_store() macro */
+
+/* ----------------------------------------------------------------
+ * exec-append-initialize-next
+ *
+ * Sets up the append node state (i.e. the append state node)
+ * for the "next" scan.
+ *
+ * Returns t iff there is a "next" scan to process.
+ * ----------------------------------------------------------------
+ */
+bool
+exec_append_initialize_next(Append *node)
+{
+ EState *estate;
+ AppendState *unionstate;
+ TupleTableSlot *result_slot;
+ List *rangeTable;
+
+ int whichplan;
+ int nplans;
+ List *rtentries;
+ ResTarget *rtentry;
+
+ Index unionrelid;
+
+ /* ----------------
+ * get information from the append node
+ * ----------------
+ */
+ estate = node->plan.state;
+ unionstate = node->unionstate;
+ result_slot = unionstate->cstate.cs_ResultTupleSlot;
+ rangeTable = estate->es_range_table;
+
+ whichplan = unionstate->as_whichplan;
+ nplans = unionstate->as_nplans;
+ rtentries = node->unionrtentries;
+
+ if (whichplan < 0) {
+ /* ----------------
+ * if scanning in reverse, we start at
+ * the last scan in the list and then
+ * proceed back to the first.. in any case
+ * we inform ExecProcAppend that we are
+ * at the end of the line by returning FALSE
+ * ----------------
+ */
+ unionstate->as_whichplan = 0;
+ return FALSE;
+
+ } else if (whichplan >= nplans) {
+ /* ----------------
+ * as above, end the scan if we go beyond
+ * the last scan in our list..
+ * ----------------
+ */
+ unionstate->as_whichplan = nplans - 1;
+ return FALSE;
+
+ } else {
+ /* ----------------
+ * initialize the scan
+ * (and update the range table appropriately)
+ * (doesn't this leave the range table hosed for anybody upstream
+ * of the Append node??? - jolly )
+ * ----------------
+ */
+ if (node->unionrelid > 0) {
+ rtentry = nth(whichplan, rtentries);
+ if (rtentry == NULL)
+ elog(DEBUG, "exec_append_initialize_next: rtentry is nil");
+
+ unionrelid = node->unionrelid;
+
+ rt_store(unionrelid, rangeTable, rtentry);
+
+ if (unionstate->as_junkFilter_list) {
+ estate->es_junkFilter =
+ (JunkFilter*)nth(whichplan,
+ unionstate->as_junkFilter_list);
+ }
+ if (unionstate->as_result_relation_info_list) {
+ estate->es_result_relation_info =
+ (RelationInfo*) nth(whichplan,
+ unionstate->as_result_relation_info_list);
+ }
+ result_slot->ttc_whichplan = whichplan;
+ }
+
+ return TRUE;
+ }
+}
+
+/* ----------------------------------------------------------------
+ * ExecInitAppend
+ *
+ * Begins all of the subscans of the append node, storing the
+ * scan structures in the 'initialized' vector of the append-state
+ * structure.
+ *
+ * (This is potentially wasteful, since the entire result of the
+ * append node may not be scanned, but this way all of the
+ * structures get allocated in the executor's top level memory
+ * block instead of that of the call to ExecProcAppend.)
+ *
+ * Returns the scan result of the first scan.
+ * ----------------------------------------------------------------
+ */
+bool
+ExecInitAppend(Append *node, EState *estate, Plan *parent)
+{
+ AppendState *unionstate;
+ int nplans;
+ List *resultList;
+ List *rtentries;
+ List *unionplans;
+ bool *initialized;
+ int i;
+ Plan *initNode;
+ List *junkList;
+ RelationInfo *es_rri = estate->es_result_relation_info;
+
+ /* ----------------
+ * assign execution state to node and get information
+ * for append state
+ * ----------------
+ */
+ node->plan.state = estate;
+
+ unionplans = node->unionplans;
+ nplans = length(unionplans);
+ rtentries = node->unionrtentries;
+
+ CXT1_printf("ExecInitAppend: context is %d\n", CurrentMemoryContext);
+ initialized = (bool *)palloc(nplans * sizeof(bool));
+
+ /* ----------------
+ * create new AppendState for our append node
+ * ----------------
+ */
+ unionstate = makeNode(AppendState);
+ unionstate->as_whichplan = 0;
+ unionstate->as_nplans = nplans;
+ unionstate->as_initialized = initialized;
+ unionstate->as_rtentries = rtentries;
+
+ node->unionstate = unionstate;
+
+ /* ----------------
+ * Miscellanious initialization
+ *
+ * + assign node's base_id
+ * + assign debugging hooks
+ *
+ * Append plans don't have expression contexts because they
+ * never call ExecQual or ExecTargetList.
+ * ----------------
+ */
+ ExecAssignNodeBaseInfo(estate, &unionstate->cstate, parent);
+
+#define APPEND_NSLOTS 1
+ /* ----------------
+ * append nodes still have Result slots, which hold pointers
+ * to tuples, so we have to initialize them..
+ * ----------------
+ */
+ ExecInitResultTupleSlot(estate, &unionstate->cstate);
+
+ /*
+ * If the inherits rtentry is the result relation, we have to make
+ * a result relation info list for all inheritors so we can update
+ * their indices and put the result tuples in the right place etc.
+ *
+ * e.g. replace p (age = p.age + 1) from p in person*
+ */
+ if ((es_rri != (RelationInfo*)NULL) &&
+ (node->unionrelid == es_rri->ri_RangeTableIndex))
+ {
+ RelationInfo *rri;
+ List *rtentryP;
+
+ foreach(rtentryP,rtentries)
+ {
+ Oid reloid;
+ RangeTblEntry *rtentry = lfirst(rtentryP);
+
+ reloid = rtentry->relid;
+ rri = makeNode(RelationInfo);
+ rri->ri_RangeTableIndex = es_rri->ri_RangeTableIndex;
+ rri->ri_RelationDesc = heap_open(reloid);
+ rri->ri_NumIndices = 0;
+ rri->ri_IndexRelationDescs = NULL; /* index descs */
+ rri->ri_IndexRelationInfo = NULL; /* index key info */
+
+ resultList = lcons(rri,resultList);
+ ExecOpenIndices(reloid, rri);
+ }
+ unionstate->as_result_relation_info_list = resultList;
+ }
+ /* ----------------
+ * call ExecInitNode on each of the plans in our list
+ * and save the results into the array "initialized"
+ * ----------------
+ */
+ junkList = NIL;
+
+ for(i = 0; i < nplans ; i++ ) {
+ JunkFilter *j;
+ List *targetList;
+ /* ----------------
+ * NOTE: we first modify range table in
+ * exec_append_initialize_next() and
+ * then initialize the subnode,
+ * since it may use the range table.
+ * ----------------
+ */
+ unionstate->as_whichplan = i;
+ exec_append_initialize_next(node);
+
+ initNode = (Plan *) nth(i, unionplans);
+ initialized[i] = ExecInitNode(initNode, estate, (Plan*) node);
+
+ /* ---------------
+ * Each targetlist in the subplan may need its own junk filter
+ *
+ * This is true only when the reln being replaced/deleted is
+ * the one that we're looking at the subclasses of
+ * ---------------
+ */
+ if ((es_rri != (RelationInfo*)NULL) &&
+ (node->unionrelid == es_rri->ri_RangeTableIndex)) {
+
+ targetList = initNode->targetlist;
+ j = (JunkFilter *) ExecInitJunkFilter(targetList);
+ junkList = lappend(junkList, j);
+ }
+
+ }
+ unionstate->as_junkFilter_list = junkList;
+ if (junkList != NIL)
+ estate->es_junkFilter = (JunkFilter *)lfirst(junkList);
+
+ /* ----------------
+ * initialize the return type from the appropriate subplan.
+ * ----------------
+ */
+ initNode = (Plan *) nth(0, unionplans);
+ ExecAssignResultType(&unionstate->cstate,
+/* ExecGetExecTupDesc(initNode), */
+ ExecGetTupType(initNode));
+ unionstate->cstate.cs_ProjInfo = NULL;
+
+ /* ----------------
+ * return the result from the first subplan's initialization
+ * ----------------
+ */
+ unionstate->as_whichplan = 0;
+ exec_append_initialize_next(node);
+#if 0
+ result = (List *) initialized[0];
+#endif
+ return TRUE;
+}
+
+int
+ExecCountSlotsAppend(Append *node)
+{
+ List *plan;
+ List *unionplans = node->unionplans;
+ int nSlots = 0;
+
+ foreach (plan,unionplans) {
+ nSlots += ExecCountSlotsNode((Plan *)lfirst(plan));
+ }
+ return nSlots + APPEND_NSLOTS;
+}
+
+/* ----------------------------------------------------------------
+ * ExecProcAppend
+ *
+ * Handles the iteration over the multiple scans.
+ *
+ * NOTE: Can't call this ExecAppend, that name is used in execMain.l
+ * ----------------------------------------------------------------
+ */
+TupleTableSlot *
+ExecProcAppend(Append *node)
+{
+ EState *estate;
+ AppendState *unionstate;
+
+ int whichplan;
+ List *unionplans;
+ Plan *subnode;
+ TupleTableSlot *result;
+ TupleTableSlot *result_slot;
+ ScanDirection direction;
+
+ /* ----------------
+ * get information from the node
+ * ----------------
+ */
+ unionstate = node->unionstate;
+ estate = node->plan.state;
+ direction = estate->es_direction;
+
+ unionplans = node->unionplans;
+ whichplan = unionstate->as_whichplan;
+ result_slot = unionstate->cstate.cs_ResultTupleSlot;
+
+ /* ----------------
+ * figure out which subplan we are currently processing
+ * ----------------
+ */
+ subnode = (Plan *) nth(whichplan, unionplans);
+
+ if (subnode == NULL)
+ elog(DEBUG, "ExecProcAppend: subnode is NULL");
+
+ /* ----------------
+ * get a tuple from the subplan
+ * ----------------
+ */
+ result = ExecProcNode(subnode, (Plan*)node);
+
+ if (! TupIsNull(result)) {
+ /* ----------------
+ * if the subplan gave us something then place a copy of
+ * whatever we get into our result slot and return it, else..
+ * ----------------
+ */
+ return ExecStoreTuple(result->val,
+ result_slot, result->ttc_buffer, false);
+
+ } else {
+ /* ----------------
+ * .. go on to the "next" subplan in the appropriate
+ * direction and try processing again (recursively)
+ * ----------------
+ */
+ whichplan = unionstate->as_whichplan;
+
+ if (ScanDirectionIsForward(direction))
+ {
+ unionstate->as_whichplan = whichplan + 1;
+ }
+ else
+ {
+ unionstate->as_whichplan = whichplan - 1;
+ }
+
+ /* ----------------
+ * return something from next node or an empty slot
+ * all of our subplans have been exhausted.
+ * ----------------
+ */
+ if (exec_append_initialize_next(node)) {
+ ExecSetSlotDescriptorIsNew(result_slot, true);
+ return
+ ExecProcAppend(node);
+ } else
+ return ExecClearTuple(result_slot);
+ }
+}
+
+/* ----------------------------------------------------------------
+ * ExecEndAppend
+ *
+ * Shuts down the subscans of the append node.
+ *
+ * Returns nothing of interest.
+ * ----------------------------------------------------------------
+ */
+void
+ExecEndAppend(Append *node)
+{
+ AppendState *unionstate;
+ int nplans;
+ List *unionplans;
+ bool *initialized;
+ int i;
+ List *resultRelationInfoList;
+ RelationInfo *resultRelationInfo;
+
+ /* ----------------
+ * get information from the node
+ * ----------------
+ */
+ unionstate = node->unionstate;
+ unionplans = node->unionplans;
+ nplans = unionstate->as_nplans;
+ initialized = unionstate->as_initialized;
+
+ /* ----------------
+ * shut down each of the subscans
+ * ----------------
+ */
+ for(i = 0; i < nplans; i++) {
+ if (initialized[i]==TRUE) {
+ ExecEndNode( (Plan *) nth(i, unionplans), (Plan*)node );
+ }
+ }
+
+ /* ----------------
+ * close out the different result relations
+ * ----------------
+ */
+ resultRelationInfoList = unionstate->as_result_relation_info_list;
+ while (resultRelationInfoList != NIL) {
+ Relation resultRelationDesc;
+
+ resultRelationInfo = (RelationInfo*) lfirst(resultRelationInfoList);
+ resultRelationDesc = resultRelationInfo->ri_RelationDesc;
+ heap_close(resultRelationDesc);
+ pfree(resultRelationInfo);
+ resultRelationInfoList = lnext(resultRelationInfoList);
+ }
+ if (unionstate->as_result_relation_info_list)
+ pfree(unionstate->as_result_relation_info_list);
+
+ /* XXX should free unionstate->as_rtentries and unionstate->as_junkfilter_list here */
+}
+
diff --git a/src/backend/executor/nodeAppend.h b/src/backend/executor/nodeAppend.h
new file mode 100644
index 00000000000..fd2cdbbe81e
--- /dev/null
+++ b/src/backend/executor/nodeAppend.h
@@ -0,0 +1,22 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeAppend.h--
+ *
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id: nodeAppend.h,v 1.1.1.1 1996/07/09 06:21:26 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef NODEAPPEND_H
+#define NODEAPPEND_H
+
+extern bool exec_append_initialize_next(Append *node);
+extern bool ExecInitAppend(Append *node, EState *estate, Plan *parent);
+extern int ExecCountSlotsAppend(Append *node);
+extern TupleTableSlot *ExecProcAppend(Append *node);
+extern void ExecEndAppend(Append *node);
+
+#endif /* NODEAPPEND_H */
diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c
new file mode 100644
index 00000000000..e9b0847b5f9
--- /dev/null
+++ b/src/backend/executor/nodeGroup.c
@@ -0,0 +1,407 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeGroup.c--
+ * Routines to handle group nodes (used for queries with GROUP BY clause).
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * DESCRIPTION
+ * The Group node is designed for handling queries with a GROUP BY clause.
+ * It's outer plan must be a sort node. It assumes that the tuples it gets
+ * back from the outer plan is sorted in the order specified by the group
+ * columns. (ie. tuples from the same group are consecutive)
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeGroup.c,v 1.1.1.1 1996/07/09 06:21:26 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "access/heapam.h"
+#include "catalog/catalog.h"
+#include "executor/executor.h"
+#include "executor/nodeGroup.h"
+
+static TupleTableSlot *ExecGroupEveryTuple(Group *node);
+static TupleTableSlot *ExecGroupOneTuple(Group *node);
+static bool sameGroup(TupleTableSlot *oldslot, TupleTableSlot *newslot,
+ int numCols, AttrNumber *grpColIdx, TupleDesc tupdesc);
+
+/* ---------------------------------------
+ * ExecGroup -
+ *
+ * There are two modes in which tuples are returned by ExecGroup. If
+ * tuplePerGroup is TRUE, every tuple from the same group will be
+ * returned, followed by a NULL at the end of each group. This is
+ * useful for Agg node which needs to aggregate over tuples of the same
+ * group. (eg. SELECT salary, count{*} FROM emp GROUP BY salary)
+ *
+ * If tuplePerGroup is FALSE, only one tuple per group is returned. The
+ * tuple returned contains only the group columns. NULL is returned only
+ * at the end when no more groups is present. This is useful when
+ * the query does not involve aggregates. (eg. SELECT salary FROM emp
+ * GROUP BY salary)
+ * ------------------------------------------
+ */
+TupleTableSlot *
+ExecGroup(Group *node)
+{
+ if (node->tuplePerGroup)
+ return ExecGroupEveryTuple(node);
+ else
+ return ExecGroupOneTuple(node);
+}
+
+/*
+ * ExecGroupEveryTuple -
+ * return every tuple with a NULL between each group
+ */
+static TupleTableSlot *
+ExecGroupEveryTuple(Group *node)
+{
+ GroupState *grpstate;
+ EState *estate;
+ ExprContext *econtext;
+
+ HeapTuple outerTuple = NULL;
+ TupleTableSlot *outerslot, *lastslot;
+ ProjectionInfo *projInfo;
+ TupleTableSlot *resultSlot;
+
+ bool isDone;
+
+ /* ---------------------
+ * get state info from node
+ * ---------------------
+ */
+ grpstate = node->grpstate;
+ if (grpstate->grp_done)
+ return NULL;
+
+ estate = node->plan.state;
+
+ econtext = grpstate->csstate.cstate.cs_ExprContext;
+
+ if (grpstate->grp_useLastTuple) {
+ /*
+ * we haven't returned last tuple yet because it is not of the
+ * same group
+ */
+ grpstate->grp_useLastTuple = FALSE;
+
+ ExecStoreTuple(grpstate->grp_lastSlot->val,
+ grpstate->csstate.css_ScanTupleSlot,
+ grpstate->grp_lastSlot->ttc_buffer,
+ false);
+ } else {
+ outerslot = ExecProcNode(outerPlan(node), (Plan*)node);
+ if (outerslot)
+ outerTuple = outerslot->val;
+ if (!HeapTupleIsValid(outerTuple)) {
+ grpstate->grp_done = TRUE;
+ return NULL;
+ }
+
+ /* ----------------
+ * Compare with last tuple and see if this tuple is of
+ * the same group.
+ * ----------------
+ */
+ lastslot = grpstate->csstate.css_ScanTupleSlot;
+
+ if (lastslot->val != NULL &&
+ (!sameGroup(lastslot, outerslot,
+ node->numCols, node->grpColIdx,
+ ExecGetScanType(&grpstate->csstate)))) {
+/* ExecGetResultType(&grpstate->csstate.cstate)))) {*/
+
+ grpstate->grp_useLastTuple = TRUE;
+
+ /* save it for next time */
+ grpstate->grp_lastSlot = outerslot;
+
+ /*
+ * signifies the end of the group
+ */
+ return NULL;
+ }
+
+ ExecStoreTuple(outerTuple,
+ grpstate->csstate.css_ScanTupleSlot,
+ outerslot->ttc_buffer,
+ false);
+ }
+
+ /* ----------------
+ * form a projection tuple, store it in the result tuple
+ * slot and return it.
+ * ----------------
+ */
+ projInfo = grpstate->csstate.cstate.cs_ProjInfo;
+
+ econtext->ecxt_scantuple = grpstate->csstate.css_ScanTupleSlot;
+ resultSlot = ExecProject(projInfo, &isDone);
+
+ return resultSlot;
+}
+
+/*
+ * ExecGroupOneTuple -
+ * returns one tuple per group, a NULL at the end when there are no more
+ * tuples.
+ */
+static TupleTableSlot *
+ExecGroupOneTuple(Group *node)
+{
+ GroupState *grpstate;
+ EState *estate;
+ ExprContext *econtext;
+
+ HeapTuple outerTuple = NULL;
+ TupleTableSlot *outerslot, *lastslot;
+ ProjectionInfo *projInfo;
+ TupleTableSlot *resultSlot;
+
+ bool isDone;
+
+ /* ---------------------
+ * get state info from node
+ * ---------------------
+ */
+ grpstate = node->grpstate;
+ if (grpstate->grp_done)
+ return NULL;
+
+ estate = node->plan.state;
+
+ econtext = node->grpstate->csstate.cstate.cs_ExprContext;
+
+ if (grpstate->grp_useLastTuple) {
+ grpstate->grp_useLastTuple = FALSE;
+ ExecStoreTuple(grpstate->grp_lastSlot->val,
+ grpstate->csstate.css_ScanTupleSlot,
+ grpstate->grp_lastSlot->ttc_buffer,
+ false);
+ } else {
+ outerslot = ExecProcNode(outerPlan(node), (Plan*)node);
+ if (outerslot) outerTuple = outerslot->val;
+ if (!HeapTupleIsValid(outerTuple)) {
+ grpstate->grp_done = TRUE;
+ return NULL;
+ }
+ ExecStoreTuple(outerTuple,
+ grpstate->csstate.css_ScanTupleSlot,
+ outerslot->ttc_buffer,
+ false);
+ }
+ lastslot = grpstate->csstate.css_ScanTupleSlot;
+
+ /*
+ * find all tuples that belong to a group
+ */
+ for(;;) {
+ outerslot = ExecProcNode(outerPlan(node), (Plan*)node);
+ outerTuple = (outerslot) ? outerslot->val : NULL;
+ if (!HeapTupleIsValid(outerTuple)) {
+ /*
+ * we have at least one tuple (lastslot) if we reach here
+ */
+ grpstate->grp_done = TRUE;
+
+ /* return lastslot */
+ break;
+ }
+
+ /* ----------------
+ * Compare with last tuple and see if this tuple is of
+ * the same group.
+ * ----------------
+ */
+ if ((!sameGroup(lastslot, outerslot,
+ node->numCols, node->grpColIdx,
+ ExecGetScanType(&grpstate->csstate)))) {
+/* ExecGetResultType(&grpstate->csstate.cstate)))) {*/
+
+ grpstate->grp_useLastTuple = TRUE;
+
+ /* save it for next time */
+ grpstate->grp_lastSlot = outerslot;
+
+ /* return lastslot */
+ break;
+ }
+
+ ExecStoreTuple(outerTuple,
+ grpstate->csstate.css_ScanTupleSlot,
+ outerslot->ttc_buffer,
+ false);
+
+ lastslot = grpstate->csstate.css_ScanTupleSlot;
+ }
+
+ ExecStoreTuple(lastslot->val,
+ grpstate->csstate.css_ScanTupleSlot,
+ lastslot->ttc_buffer,
+ false);
+
+ /* ----------------
+ * form a projection tuple, store it in the result tuple
+ * slot and return it.
+ * ----------------
+ */
+ projInfo = grpstate->csstate.cstate.cs_ProjInfo;
+
+ econtext->ecxt_scantuple = lastslot;
+ resultSlot = ExecProject(projInfo, &isDone);
+
+ return resultSlot;
+}
+
+/* -----------------
+ * ExecInitGroup
+ *
+ * Creates the run-time information for the group node produced by the
+ * planner and initializes its outer subtree
+ * -----------------
+ */
+bool
+ExecInitGroup(Group *node, EState *estate, Plan *parent)
+{
+ GroupState *grpstate;
+ Plan *outerPlan;
+
+ /*
+ * assign the node's execution state
+ */
+ node->plan.state = estate;
+
+ /*
+ * create state structure
+ */
+ grpstate = makeNode(GroupState);
+ node->grpstate = grpstate;
+ grpstate->grp_useLastTuple = FALSE;
+ grpstate->grp_done = FALSE;
+
+ /*
+ * assign node's base id and create expression context
+ */
+ ExecAssignNodeBaseInfo(estate, &grpstate->csstate.cstate,
+ (Plan*) parent);
+ ExecAssignExprContext(estate, &grpstate->csstate.cstate);
+
+#define GROUP_NSLOTS 2
+ /*
+ * tuple table initialization
+ */
+ ExecInitScanTupleSlot(estate, &grpstate->csstate);
+ ExecInitResultTupleSlot(estate, &grpstate->csstate.cstate);
+
+ /*
+ * initializes child nodes
+ */
+ outerPlan = outerPlan(node);
+ ExecInitNode(outerPlan, estate, (Plan *)node);
+
+ /* ----------------
+ * initialize tuple type.
+ * ----------------
+ */
+ ExecAssignScanTypeFromOuterPlan((Plan *) node, &grpstate->csstate);
+
+ /*
+ * Initialize tuple type for both result and scan.
+ * This node does no projection
+ */
+ ExecAssignResultTypeFromTL((Plan*) node, &grpstate->csstate.cstate);
+ ExecAssignProjectionInfo((Plan*)node, &grpstate->csstate.cstate);
+
+ return TRUE;
+}
+
+int
+ExecCountSlotsGroup(Group *node)
+{
+ return ExecCountSlotsNode(outerPlan(node)) + GROUP_NSLOTS;
+}
+
+/* ------------------------
+ * ExecEndGroup(node)
+ *
+ * -----------------------
+ */
+void
+ExecEndGroup(Group *node)
+{
+ GroupState *grpstate;
+ Plan *outerPlan;
+
+ grpstate = node->grpstate;
+
+ ExecFreeProjectionInfo(&grpstate->csstate.cstate);
+
+ outerPlan = outerPlan(node);
+ ExecEndNode(outerPlan, (Plan*)node);
+
+ /* clean up tuple table */
+ ExecClearTuple(grpstate->csstate.css_ScanTupleSlot);
+}
+
+/*****************************************************************************
+ *
+ *****************************************************************************/
+
+/*
+ * code swiped from nodeUnique.c
+ */
+static bool
+sameGroup(TupleTableSlot *oldslot,
+ TupleTableSlot *newslot,
+ int numCols,
+ AttrNumber *grpColIdx,
+ TupleDesc tupdesc)
+{
+ bool isNull1,isNull2;
+ char *attr1, *attr2;
+ char *val1, *val2;
+ int i;
+ AttrNumber att;
+ Oid typoutput;
+
+ for(i = 0; i < numCols; i++) {
+ att = grpColIdx[i];
+ typoutput = typtoout((Oid)tupdesc->attrs[att-1]->atttypid);
+
+ attr1 = heap_getattr(oldslot->val,
+ InvalidBuffer,
+ att,
+ tupdesc,
+ &isNull1);
+
+ attr2 = heap_getattr(newslot->val,
+ InvalidBuffer,
+ att,
+ tupdesc,
+ &isNull2);
+
+ if (isNull1 == isNull2) {
+ if (isNull1) /* both are null, they are equal */
+ continue;
+
+ val1 = fmgr(typoutput, attr1,
+ gettypelem(tupdesc->attrs[att-1]->atttypid));
+ val2 = fmgr(typoutput, attr2,
+ gettypelem(tupdesc->attrs[att-1]->atttypid));
+
+ /* now, val1 and val2 are ascii representations so we can
+ use strcmp for comparison */
+ if (strcmp(val1,val2) != 0)
+ return FALSE;
+ } else {
+ /* one is null and the other isn't, they aren't equal */
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
diff --git a/src/backend/executor/nodeGroup.h b/src/backend/executor/nodeGroup.h
new file mode 100644
index 00000000000..067028ea8e1
--- /dev/null
+++ b/src/backend/executor/nodeGroup.h
@@ -0,0 +1,21 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeGroup.h--
+ * prototypes for nodeGroup.c
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id: nodeGroup.h,v 1.1.1.1 1996/07/09 06:21:26 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef NODEGROUP_H
+#define NODEGROUP_H
+
+extern TupleTableSlot *ExecGroup(Group *node);
+extern bool ExecInitGroup(Group *node, EState *estate, Plan *parent);
+extern int ExecCountSlotsGroup(Group *node);
+extern void ExecEndGroup(Group *node);
+
+#endif /* NODEGROUP_H */
diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c
new file mode 100644
index 00000000000..55a5e1f0276
--- /dev/null
+++ b/src/backend/executor/nodeHash.c
@@ -0,0 +1,828 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeHash.c--
+ * Routines to hash relations for hashjoin
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeHash.c,v 1.1.1.1 1996/07/09 06:21:26 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+ * INTERFACE ROUTINES
+ * ExecHash - generate an in-memory hash table of the relation
+ * ExecInitHash - initialize node and subnodes..
+ * ExecEndHash - shutdown node and subnodes
+ *
+ */
+
+#include <stdio.h> /* for sprintf() */
+#include <math.h>
+#include <sys/file.h>
+#include "storage/fd.h" /* for SEEK_ */
+#include "storage/ipc.h"
+#include "storage/bufmgr.h" /* for BLCKSZ */
+#include "executor/executor.h"
+#include "executor/nodeHash.h"
+#include "executor/nodeHashjoin.h"
+#include "utils/palloc.h"
+
+extern int NBuffers;
+static int HashTBSize;
+
+static void mk_hj_temp(char *tempname);
+static int hashFunc(char *key, int len);
+
+/* ----------------------------------------------------------------
+ * ExecHash
+ *
+ * build hash table for hashjoin, all do partitioning if more
+ * than one batches are required.
+ * ----------------------------------------------------------------
+ */
+TupleTableSlot *
+ExecHash(Hash *node)
+{
+ EState *estate;
+ HashState *hashstate;
+ Plan *outerNode;
+ Var *hashkey;
+ HashJoinTable hashtable;
+ TupleTableSlot *slot;
+ ExprContext *econtext;
+
+ int nbatch;
+ File *batches;
+ RelativeAddr *batchPos;
+ int *batchSizes;
+ int i;
+ RelativeAddr *innerbatchNames;
+
+ /* ----------------
+ * get state info from node
+ * ----------------
+ */
+
+ hashstate = node->hashstate;
+ estate = node->plan.state;
+ outerNode = outerPlan(node);
+
+ hashtable = node->hashtable;
+ if (hashtable == NULL)
+ elog(WARN, "ExecHash: hash table is NULL.");
+
+ nbatch = hashtable->nbatch;
+
+ if (nbatch > 0) { /* if needs hash partition */
+ innerbatchNames = (RelativeAddr *) ABSADDR(hashtable->innerbatchNames);
+
+ /* --------------
+ * allocate space for the file descriptors of batch files
+ * then open the batch files in the current processes.
+ * --------------
+ */
+ batches = (File*)palloc(nbatch * sizeof(File));
+ for (i=0; i<nbatch; i++) {
+ batches[i] = FileNameOpenFile(ABSADDR(innerbatchNames[i]),
+ O_CREAT | O_RDWR, 0600);
+ }
+ hashstate->hashBatches = batches;
+ batchPos = (RelativeAddr*) ABSADDR(hashtable->innerbatchPos);
+ batchSizes = (int*) ABSADDR(hashtable->innerbatchSizes);
+ }
+
+ /* ----------------
+ * set expression context
+ * ----------------
+ */
+ hashkey = node->hashkey;
+ econtext = hashstate->cstate.cs_ExprContext;
+
+ /* ----------------
+ * get tuple and insert into the hash table
+ * ----------------
+ */
+ for (;;) {
+ slot = ExecProcNode(outerNode, (Plan*)node);
+ if (TupIsNull(slot))
+ break;
+
+ econtext->ecxt_innertuple = slot;
+ ExecHashTableInsert(hashtable, econtext, hashkey,
+ hashstate->hashBatches);
+
+ ExecClearTuple(slot);
+ }
+
+ /*
+ * end of build phase, flush all the last pages of the batches.
+ */
+ for (i=0; i<nbatch; i++) {
+ if (FileSeek(batches[i], 0L, SEEK_END) < 0)
+ perror("FileSeek");
+ if (FileWrite(batches[i],ABSADDR(hashtable->batch)+i*BLCKSZ,BLCKSZ) < 0)
+ perror("FileWrite");
+ NDirectFileWrite++;
+ }
+
+ /* ---------------------
+ * Return the slot so that we have the tuple descriptor
+ * when we need to save/restore them. -Jeff 11 July 1991
+ * ---------------------
+ */
+ return slot;
+}
+
+/* ----------------------------------------------------------------
+ * ExecInitHash
+ *
+ * Init routine for Hash node
+ * ----------------------------------------------------------------
+ */
+bool
+ExecInitHash(Hash *node, EState *estate, Plan *parent)
+{
+ HashState *hashstate;
+ Plan *outerPlan;
+
+ SO1_printf("ExecInitHash: %s\n",
+ "initializing hash node");
+
+ /* ----------------
+ * assign the node's execution state
+ * ----------------
+ */
+ node->plan.state = estate;
+
+ /* ----------------
+ * create state structure
+ * ----------------
+ */
+ hashstate = makeNode(HashState);
+ node->hashstate = hashstate;
+ hashstate->hashBatches = NULL;
+
+ /* ----------------
+ * Miscellanious initialization
+ *
+ * + assign node's base_id
+ * + assign debugging hooks and
+ * + create expression context for node
+ * ----------------
+ */
+ ExecAssignNodeBaseInfo(estate, &hashstate->cstate, parent);
+ ExecAssignExprContext(estate, &hashstate->cstate);
+
+#define HASH_NSLOTS 1
+ /* ----------------
+ * initialize our result slot
+ * ----------------
+ */
+ ExecInitResultTupleSlot(estate, &hashstate->cstate);
+
+ /* ----------------
+ * initializes child nodes
+ * ----------------
+ */
+ outerPlan = outerPlan(node);
+ ExecInitNode(outerPlan, estate, (Plan *)node);
+
+ /* ----------------
+ * initialize tuple type. no need to initialize projection
+ * info because this node doesn't do projections
+ * ----------------
+ */
+ ExecAssignResultTypeFromOuterPlan((Plan *) node, &hashstate->cstate);
+ hashstate->cstate.cs_ProjInfo = NULL;
+
+ return TRUE;
+}
+
+int
+ExecCountSlotsHash(Hash *node)
+{
+ return ExecCountSlotsNode(outerPlan(node)) +
+ ExecCountSlotsNode(innerPlan(node)) +
+ HASH_NSLOTS;
+}
+
+/* ---------------------------------------------------------------
+ * ExecEndHash
+ *
+ * clean up routine for Hash node
+ * ----------------------------------------------------------------
+ */
+void
+ExecEndHash(Hash *node)
+{
+ HashState *hashstate;
+ Plan *outerPlan;
+ File *batches;
+
+ /* ----------------
+ * get info from the hash state
+ * ----------------
+ */
+ hashstate = node->hashstate;
+ batches = hashstate->hashBatches;
+ if (batches != NULL)
+ pfree(batches);
+
+ /* ----------------
+ * free projection info. no need to free result type info
+ * because that came from the outer plan...
+ * ----------------
+ */
+ ExecFreeProjectionInfo(&hashstate->cstate);
+
+ /* ----------------
+ * shut down the subplan
+ * ----------------
+ */
+ outerPlan = outerPlan(node);
+ ExecEndNode(outerPlan, (Plan*)node);
+}
+
+RelativeAddr
+hashTableAlloc(int size, HashJoinTable hashtable)
+{
+ RelativeAddr p;
+ p = hashtable->top;
+ hashtable->top += size;
+ return p;
+}
+
+/* ----------------------------------------------------------------
+ * ExecHashTableCreate
+ *
+ * create a hashtable in shared memory for hashjoin.
+ * ----------------------------------------------------------------
+ */
+#define NTUP_PER_BUCKET 10
+#define FUDGE_FAC 1.5
+
+HashJoinTable
+ExecHashTableCreate(Hash *node)
+{
+ Plan *outerNode;
+ int nbatch;
+ int ntuples;
+ int tupsize;
+ IpcMemoryId shmid;
+ HashJoinTable hashtable;
+ HashBucket bucket;
+ int nbuckets;
+ int totalbuckets;
+ int bucketsize;
+ int i;
+ RelativeAddr *outerbatchNames;
+ RelativeAddr *outerbatchPos;
+ RelativeAddr *innerbatchNames;
+ RelativeAddr *innerbatchPos;
+ int *innerbatchSizes;
+ RelativeAddr tempname;
+
+ nbatch = -1;
+ HashTBSize = NBuffers/2;
+ while (nbatch < 0) {
+ /*
+ * determine number of batches for the hashjoin
+ */
+ HashTBSize *= 2;
+ nbatch = ExecHashPartition(node);
+ }
+ /* ----------------
+ * get information about the size of the relation
+ * ----------------
+ */
+ outerNode = outerPlan(node);
+ ntuples = outerNode->plan_size;
+ if (ntuples <= 0)
+ ntuples = 1000; /* XXX just a hack */
+ tupsize = outerNode->plan_width + sizeof(HeapTupleData);
+
+ /*
+ * totalbuckets is the total number of hash buckets needed for
+ * the entire relation
+ */
+ totalbuckets = ceil((double)ntuples/NTUP_PER_BUCKET);
+ bucketsize = LONGALIGN (NTUP_PER_BUCKET * tupsize + sizeof(*bucket));
+
+ /*
+ * nbuckets is the number of hash buckets for the first pass
+ * of hybrid hashjoin
+ */
+ nbuckets = (HashTBSize - nbatch) * BLCKSZ / (bucketsize * FUDGE_FAC);
+ if (totalbuckets < nbuckets)
+ totalbuckets = nbuckets;
+ if (nbatch == 0)
+ nbuckets = totalbuckets;
+#ifdef HJDEBUG
+ printf("nbatch = %d, totalbuckets = %d, nbuckets = %d\n", nbatch, totalbuckets, nbuckets);
+#endif
+
+ /* ----------------
+ * in non-parallel machines, we don't need to put the hash table
+ * in the shared memory. We just palloc it.
+ * ----------------
+ */
+ hashtable = (HashJoinTable)palloc((HashTBSize+1)*BLCKSZ);
+ shmid = 0;
+
+ if (hashtable == NULL) {
+ elog(WARN, "not enough memory for hashjoin.");
+ }
+ /* ----------------
+ * initialize the hash table header
+ * ----------------
+ */
+ hashtable->nbuckets = nbuckets;
+ hashtable->totalbuckets = totalbuckets;
+ hashtable->bucketsize = bucketsize;
+ hashtable->shmid = shmid;
+ hashtable->top = sizeof(HashTableData);
+ hashtable->bottom = HashTBSize * BLCKSZ;
+ /*
+ * hashtable->readbuf has to be long aligned!!!
+ */
+ hashtable->readbuf = hashtable->bottom;
+ hashtable->nbatch = nbatch;
+ hashtable->curbatch = 0;
+ hashtable->pcount = hashtable->nprocess = 0;
+ if (nbatch > 0) {
+ /* ---------------
+ * allocate and initialize the outer batches
+ * ---------------
+ */
+ outerbatchNames = (RelativeAddr*)ABSADDR(
+ hashTableAlloc(nbatch * sizeof(RelativeAddr), hashtable));
+ outerbatchPos = (RelativeAddr*)ABSADDR(
+ hashTableAlloc(nbatch * sizeof(RelativeAddr), hashtable));
+ for (i=0; i<nbatch; i++) {
+ tempname = hashTableAlloc(12, hashtable);
+ mk_hj_temp(ABSADDR(tempname));
+ outerbatchNames[i] = tempname;
+ outerbatchPos[i] = -1;
+ }
+ hashtable->outerbatchNames = RELADDR(outerbatchNames);
+ hashtable->outerbatchPos = RELADDR(outerbatchPos);
+ /* ---------------
+ * allocate and initialize the inner batches
+ * ---------------
+ */
+ innerbatchNames = (RelativeAddr*)ABSADDR(
+ hashTableAlloc(nbatch * sizeof(RelativeAddr), hashtable));
+ innerbatchPos = (RelativeAddr*)ABSADDR(
+ hashTableAlloc(nbatch * sizeof(RelativeAddr), hashtable));
+ innerbatchSizes = (int*)ABSADDR(
+ hashTableAlloc(nbatch * sizeof(int), hashtable));
+ for (i=0; i<nbatch; i++) {
+ tempname = hashTableAlloc(12, hashtable);
+ mk_hj_temp(ABSADDR(tempname));
+ innerbatchNames[i] = tempname;
+ innerbatchPos[i] = -1;
+ innerbatchSizes[i] = 0;
+ }
+ hashtable->innerbatchNames = RELADDR(innerbatchNames);
+ hashtable->innerbatchPos = RELADDR(innerbatchPos);
+ hashtable->innerbatchSizes = RELADDR(innerbatchSizes);
+ }
+ else {
+ hashtable->outerbatchNames = (RelativeAddr)NULL;
+ hashtable->outerbatchPos = (RelativeAddr)NULL;
+ hashtable->innerbatchNames = (RelativeAddr)NULL;
+ hashtable->innerbatchPos = (RelativeAddr)NULL;
+ hashtable->innerbatchSizes = (RelativeAddr)NULL;
+ }
+
+ hashtable->batch = (RelativeAddr)LONGALIGN(hashtable->top +
+ bucketsize * nbuckets);
+ hashtable->overflownext=hashtable->batch + nbatch * BLCKSZ;
+ /* ----------------
+ * initialize each hash bucket
+ * ----------------
+ */
+ bucket = (HashBucket)ABSADDR(hashtable->top);
+ for (i=0; i<nbuckets; i++) {
+ bucket->top = RELADDR((char*)bucket + sizeof(*bucket));
+ bucket->bottom = bucket->top;
+ bucket->firstotuple = bucket->lastotuple = -1;
+ bucket = (HashBucket)LONGALIGN(((char*)bucket + bucketsize));
+ }
+ return(hashtable);
+}
+
+/* ----------------------------------------------------------------
+ * ExecHashTableInsert
+ *
+ * insert a tuple into the hash table depending on the hash value
+ * it may just go to a tmp file for other batches
+ * ----------------------------------------------------------------
+ */
+void
+ExecHashTableInsert(HashJoinTable hashtable,
+ ExprContext *econtext,
+ Var *hashkey,
+ File *batches)
+{
+ TupleTableSlot *slot;
+ HeapTuple heapTuple;
+ HashBucket bucket;
+ int bucketno;
+ int nbatch;
+ int batchno;
+ char *buffer;
+ RelativeAddr *batchPos;
+ int *batchSizes;
+ char *pos;
+
+ nbatch = hashtable->nbatch;
+ batchPos = (RelativeAddr*)ABSADDR(hashtable->innerbatchPos);
+ batchSizes = (int*)ABSADDR(hashtable->innerbatchSizes);
+
+ slot = econtext->ecxt_innertuple;
+ heapTuple = slot->val;
+
+#ifdef HJDEBUG
+ printf("Inserting ");
+#endif
+
+ bucketno = ExecHashGetBucket(hashtable, econtext, hashkey);
+
+ /* ----------------
+ * decide whether to put the tuple in the hash table or a tmp file
+ * ----------------
+ */
+ if (bucketno < hashtable->nbuckets) {
+ /* ---------------
+ * put the tuple in hash table
+ * ---------------
+ */
+ bucket = (HashBucket)
+ (ABSADDR(hashtable->top) + bucketno * hashtable->bucketsize);
+ if ((char*)LONGALIGN(ABSADDR(bucket->bottom))
+ -(char*)bucket+heapTuple->t_len > hashtable->bucketsize)
+ ExecHashOverflowInsert(hashtable, bucket, heapTuple);
+ else {
+ memmove((char*)LONGALIGN(ABSADDR(bucket->bottom)),
+ heapTuple,
+ heapTuple->t_len);
+ bucket->bottom =
+ ((RelativeAddr)LONGALIGN(bucket->bottom) + heapTuple->t_len);
+ }
+ }
+ else {
+ /* -----------------
+ * put the tuple into a tmp file for other batches
+ * -----------------
+ */
+ batchno = (float)(bucketno - hashtable->nbuckets)/
+ (float)(hashtable->totalbuckets - hashtable->nbuckets)
+ * nbatch;
+ buffer = ABSADDR(hashtable->batch) + batchno * BLCKSZ;
+ batchSizes[batchno]++;
+ pos= (char *)
+ ExecHashJoinSaveTuple(heapTuple,
+ buffer,
+ batches[batchno],
+ (char*)ABSADDR(batchPos[batchno]));
+ batchPos[batchno] = RELADDR(pos);
+ }
+}
+
+/* ----------------------------------------------------------------
+ * ExecHashTableDestroy
+ *
+ * destroy a hash table
+ * ----------------------------------------------------------------
+ */
+void
+ExecHashTableDestroy(HashJoinTable hashtable)
+{
+ pfree(hashtable);
+}
+
+/* ----------------------------------------------------------------
+ * ExecHashGetBucket
+ *
+ * Get the hash value for a tuple
+ * ----------------------------------------------------------------
+ */
+int
+ExecHashGetBucket(HashJoinTable hashtable,
+ ExprContext *econtext,
+ Var *hashkey)
+{
+ int bucketno;
+ Datum keyval;
+ bool isNull;
+
+
+ /* ----------------
+ * Get the join attribute value of the tuple
+ * ----------------
+ */
+ keyval = ExecEvalVar(hashkey, econtext, &isNull);
+
+ /* ------------------
+ * compute the hash function
+ * ------------------
+ */
+ if (execConstByVal)
+ bucketno =
+ hashFunc((char *) &keyval, execConstLen) % hashtable->totalbuckets;
+ else
+ bucketno =
+ hashFunc((char *) keyval, execConstLen) % hashtable->totalbuckets;
+#ifdef HJDEBUG
+ if (bucketno >= hashtable->nbuckets)
+ printf("hash(%d) = %d SAVED\n", keyval, bucketno);
+ else
+ printf("hash(%d) = %d\n", keyval, bucketno);
+#endif
+
+ return(bucketno);
+}
+
+/* ----------------------------------------------------------------
+ * ExecHashOverflowInsert
+ *
+ * insert into the overflow area of a hash bucket
+ * ----------------------------------------------------------------
+ */
+void
+ExecHashOverflowInsert(HashJoinTable hashtable,
+ HashBucket bucket,
+ HeapTuple heapTuple)
+{
+ OverflowTuple otuple;
+ RelativeAddr newend;
+ OverflowTuple firstotuple;
+ OverflowTuple lastotuple;
+
+ firstotuple = (OverflowTuple)ABSADDR(bucket->firstotuple);
+ lastotuple = (OverflowTuple)ABSADDR(bucket->lastotuple);
+ /* ----------------
+ * see if we run out of overflow space
+ * ----------------
+ */
+ newend = (RelativeAddr)LONGALIGN(hashtable->overflownext + sizeof(*otuple)
+ + heapTuple->t_len);
+ if (newend > hashtable->bottom) {
+ elog(DEBUG, "hash table out of memory. expanding.");
+ /* ------------------
+ * XXX this is a temporary hack
+ * eventually, recursive hash partitioning will be
+ * implemented
+ * ------------------
+ */
+ hashtable->readbuf = hashtable->bottom = 2 * hashtable->bottom;
+ hashtable =
+ (HashJoinTable)repalloc(hashtable, hashtable->bottom+BLCKSZ);
+ if (hashtable == NULL) {
+ perror("repalloc");
+ elog(WARN, "can't expand hashtable.");
+ }
+ }
+
+ /* ----------------
+ * establish the overflow chain
+ * ----------------
+ */
+ otuple = (OverflowTuple)ABSADDR(hashtable->overflownext);
+ hashtable->overflownext = newend;
+ if (firstotuple == NULL)
+ bucket->firstotuple = bucket->lastotuple = RELADDR(otuple);
+ else {
+ lastotuple->next = RELADDR(otuple);
+ bucket->lastotuple = RELADDR(otuple);
+ }
+
+ /* ----------------
+ * copy the tuple into the overflow area
+ * ----------------
+ */
+ otuple->next = -1;
+ otuple->tuple = RELADDR(LONGALIGN(((char*)otuple + sizeof(*otuple))));
+ memmove(ABSADDR(otuple->tuple),
+ heapTuple,
+ heapTuple->t_len);
+}
+
+/* ----------------------------------------------------------------
+ * ExecScanHashBucket
+ *
+ * scan a hash bucket of matches
+ * ----------------------------------------------------------------
+ */
+HeapTuple
+ExecScanHashBucket(HashJoinState *hjstate,
+ HashBucket bucket,
+ HeapTuple curtuple,
+ List *hjclauses,
+ ExprContext *econtext)
+{
+ HeapTuple heapTuple;
+ bool qualResult;
+ OverflowTuple otuple = NULL;
+ OverflowTuple curotuple;
+ TupleTableSlot *inntuple;
+ OverflowTuple firstotuple;
+ OverflowTuple lastotuple;
+ HashJoinTable hashtable;
+
+ hashtable = hjstate->hj_HashTable;
+ firstotuple = (OverflowTuple)ABSADDR(bucket->firstotuple);
+ lastotuple = (OverflowTuple)ABSADDR(bucket->lastotuple);
+
+ /* ----------------
+ * search the hash bucket
+ * ----------------
+ */
+ if (curtuple == NULL || curtuple < (HeapTuple)ABSADDR(bucket->bottom)) {
+ if (curtuple == NULL)
+ heapTuple = (HeapTuple)
+ LONGALIGN(ABSADDR(bucket->top));
+ else
+ heapTuple = (HeapTuple)
+ LONGALIGN(((char*)curtuple+curtuple->t_len));
+
+ while (heapTuple < (HeapTuple)ABSADDR(bucket->bottom)) {
+
+ inntuple = ExecStoreTuple(heapTuple, /* tuple to store */
+ hjstate->hj_HashTupleSlot, /* slot */
+ InvalidBuffer,/* tuple has no buffer */
+ false); /* do not pfree this tuple */
+
+ econtext->ecxt_innertuple = inntuple;
+ qualResult = ExecQual((List*)hjclauses, econtext);
+
+ if (qualResult)
+ return heapTuple;
+
+ heapTuple = (HeapTuple)
+ LONGALIGN(((char*)heapTuple+heapTuple->t_len));
+ }
+
+ if (firstotuple == NULL)
+ return NULL;
+ otuple = firstotuple;
+ }
+
+ /* ----------------
+ * search the overflow area of the hash bucket
+ * ----------------
+ */
+ if (otuple == NULL) {
+ curotuple = hjstate->hj_CurOTuple;
+ otuple = (OverflowTuple)ABSADDR(curotuple->next);
+ }
+
+ while (otuple != NULL) {
+ heapTuple = (HeapTuple)ABSADDR(otuple->tuple);
+
+ inntuple = ExecStoreTuple(heapTuple, /* tuple to store */
+ hjstate->hj_HashTupleSlot, /* slot */
+ InvalidBuffer, /* SP?? this tuple has no buffer */
+ false); /* do not pfree this tuple */
+
+ econtext->ecxt_innertuple = inntuple;
+ qualResult = ExecQual((List*)hjclauses, econtext);
+
+ if (qualResult) {
+ hjstate->hj_CurOTuple = otuple;
+ return heapTuple;
+ }
+
+ otuple = (OverflowTuple)ABSADDR(otuple->next);
+ }
+
+ /* ----------------
+ * no match
+ * ----------------
+ */
+ return NULL;
+}
+
+/* ----------------------------------------------------------------
+ * hashFunc
+ *
+ * the hash function, copied from Margo
+ * ----------------------------------------------------------------
+ */
+static int
+hashFunc(char *key, int len)
+{
+ register unsigned int h;
+ register int l;
+ register unsigned char *k;
+
+ /*
+ * If this is a variable length type, then 'k' points
+ * to a "struct varlena" and len == -1.
+ * NOTE:
+ * VARSIZE returns the "real" data length plus the sizeof the
+ * "vl_len" attribute of varlena (the length information).
+ * 'k' points to the beginning of the varlena struct, so
+ * we have to use "VARDATA" to find the beginning of the "real"
+ * data.
+ */
+ if (len == -1) {
+ l = VARSIZE(key) - VARHDRSZ;
+ k = (unsigned char*) VARDATA(key);
+ } else {
+ l = len;
+ k = (unsigned char *) key;
+ }
+
+ h = 0;
+
+ /*
+ * Convert string to integer
+ */
+ while (l--) h = h * PRIME1 ^ (*k++);
+ h %= PRIME2;
+
+ return (h);
+}
+
+/* ----------------------------------------------------------------
+ * ExecHashPartition
+ *
+ * determine the number of batches needed for a hashjoin
+ * ----------------------------------------------------------------
+ */
+int
+ExecHashPartition(Hash *node)
+{
+ Plan *outerNode;
+ int b;
+ int pages;
+ int ntuples;
+ int tupsize;
+
+ /*
+ * get size information for plan node
+ */
+ outerNode = outerPlan(node);
+ ntuples = outerNode->plan_size;
+ if (ntuples == 0) ntuples = 1000;
+ tupsize = outerNode->plan_width + sizeof(HeapTupleData);
+ pages = ceil((double)ntuples * tupsize * FUDGE_FAC / BLCKSZ);
+
+ /*
+ * if amount of buffer space below hashjoin threshold,
+ * return negative
+ */
+ if (ceil(sqrt((double)pages)) > HashTBSize)
+ return -1;
+ if (pages <= HashTBSize)
+ b = 0; /* fit in memory, no partitioning */
+ else
+ b = ceil((double)(pages - HashTBSize)/(double)(HashTBSize - 1));
+
+ return b;
+}
+
+/* ----------------------------------------------------------------
+ * ExecHashTableReset
+ *
+ * reset hash table header for new batch
+ * ----------------------------------------------------------------
+ */
+void
+ExecHashTableReset(HashJoinTable hashtable, int ntuples)
+{
+ int i;
+ HashBucket bucket;
+
+ hashtable->nbuckets = hashtable->totalbuckets
+ = ceil((double)ntuples/NTUP_PER_BUCKET);
+
+ hashtable->overflownext = hashtable->top + hashtable->bucketsize *
+ hashtable->nbuckets;
+
+ bucket = (HashBucket)ABSADDR(hashtable->top);
+ for (i=0; i<hashtable->nbuckets; i++) {
+ bucket->top = RELADDR((char*)bucket + sizeof(*bucket));
+ bucket->bottom = bucket->top;
+ bucket->firstotuple = bucket->lastotuple = -1;
+ bucket = (HashBucket)((char*)bucket + hashtable->bucketsize);
+ }
+ hashtable->pcount = hashtable->nprocess;
+}
+
+static int hjtmpcnt = 0;
+
+static void
+mk_hj_temp(char *tempname)
+{
+ sprintf(tempname, "HJ%d.%d", getpid(), hjtmpcnt);
+ hjtmpcnt = (hjtmpcnt + 1) % 1000;
+}
+
+
+
diff --git a/src/backend/executor/nodeHash.h b/src/backend/executor/nodeHash.h
new file mode 100644
index 00000000000..cec479dbb01
--- /dev/null
+++ b/src/backend/executor/nodeHash.h
@@ -0,0 +1,35 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeHash.h--
+ *
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id: nodeHash.h,v 1.1.1.1 1996/07/09 06:21:26 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef NODEHASH_H
+#define NODEHASH_H
+
+extern TupleTableSlot *ExecHash(Hash *node);
+extern bool ExecInitHash(Hash *node, EState *estate, Plan *parent);
+extern int ExecCountSlotsHash(Hash *node);
+extern void ExecEndHash(Hash *node);
+extern RelativeAddr hashTableAlloc(int size, HashJoinTable hashtable);
+extern HashJoinTable ExecHashTableCreate(Hash *node);
+extern void ExecHashTableInsert(HashJoinTable hashtable, ExprContext *econtext,
+ Var *hashkey, File *batches);
+extern void ExecHashTableDestroy(HashJoinTable hashtable);
+extern int ExecHashGetBucket(HashJoinTable hashtable, ExprContext *econtext,
+ Var *hashkey);
+extern void ExecHashOverflowInsert(HashJoinTable hashtable, HashBucket bucket,
+ HeapTuple heapTuple);
+extern HeapTuple ExecScanHashBucket(HashJoinState *hjstate, HashBucket bucket,
+ HeapTuple curtuple, List *hjclauses,
+ ExprContext *econtext);
+extern int ExecHashPartition(Hash *node);
+extern void ExecHashTableReset(HashJoinTable hashtable, int ntuples);
+
+#endif /* NODEHASH_H */
diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c
new file mode 100644
index 00000000000..7ed4c141b95
--- /dev/null
+++ b/src/backend/executor/nodeHashjoin.c
@@ -0,0 +1,792 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeHashjoin.c--
+ * Routines to handle hash join nodes
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeHashjoin.c,v 1.1.1.1 1996/07/09 06:21:26 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <sys/file.h>
+
+#include "storage/bufmgr.h" /* for BLCKSZ */
+#include "storage/fd.h" /* for SEEK_ */
+#include "executor/executor.h"
+#include "executor/nodeHash.h"
+#include "executor/nodeHashjoin.h"
+
+#include "optimizer/clauses.h" /* for get_leftop */
+
+
+#include "utils/palloc.h"
+
+static TupleTableSlot *
+ExecHashJoinOuterGetTuple(Plan *node, Plan* parent, HashJoinState *hjstate);
+
+static TupleTableSlot *
+ExecHashJoinGetSavedTuple(HashJoinState *hjstate, char *buffer,
+ File file, TupleTableSlot *tupleSlot, int *block, char **position);
+
+/* ----------------------------------------------------------------
+ * ExecHashJoin
+ *
+ * This function implements the Hybrid Hashjoin algorithm.
+ * recursive partitioning remains to be added.
+ * Note: the relation we build hash table on is the inner
+ * the other one is outer.
+ * ----------------------------------------------------------------
+ */
+TupleTableSlot * /* return: a tuple or NULL */
+ExecHashJoin(HashJoin *node)
+{
+ HashJoinState *hjstate;
+ EState *estate;
+ Plan *outerNode;
+ Hash *hashNode;
+ List *hjclauses;
+ Expr *clause;
+ List *qual;
+ ScanDirection dir;
+ TupleTableSlot *inntuple;
+ Var *outerVar;
+ ExprContext *econtext;
+
+ HashJoinTable hashtable;
+ int bucketno;
+ HashBucket bucket;
+ HeapTuple curtuple;
+
+ bool qualResult;
+
+ TupleTableSlot *outerTupleSlot;
+ TupleTableSlot *innerTupleSlot;
+ int nbatch;
+ int curbatch;
+ File *outerbatches;
+ RelativeAddr *outerbatchNames;
+ RelativeAddr *outerbatchPos;
+ Var *innerhashkey;
+ int batch;
+ int batchno;
+ char *buffer;
+ int i;
+ bool hashPhaseDone;
+ char *pos;
+
+ /* ----------------
+ * get information from HashJoin node
+ * ----------------
+ */
+ hjstate = node->hashjoinstate;
+ hjclauses = node->hashclauses;
+ clause = lfirst(hjclauses);
+ estate = node->join.state;
+ qual = node->join.qual;
+ hashNode = (Hash *)innerPlan(node);
+ outerNode = outerPlan(node);
+ hashPhaseDone = node->hashdone;
+
+ dir = estate->es_direction;
+
+ /* -----------------
+ * get information from HashJoin state
+ * -----------------
+ */
+ hashtable = hjstate->hj_HashTable;
+ bucket = hjstate->hj_CurBucket;
+ curtuple = hjstate->hj_CurTuple;
+
+ /* --------------------
+ * initialize expression context
+ * --------------------
+ */
+ econtext = hjstate->jstate.cs_ExprContext;
+
+ if (hjstate->jstate.cs_TupFromTlist) {
+ TupleTableSlot *result;
+ bool isDone;
+
+ result = ExecProject(hjstate->jstate.cs_ProjInfo, &isDone);
+ if (!isDone)
+ return result;
+ }
+ /* ----------------
+ * if this is the first call, build the hash table for inner relation
+ * ----------------
+ */
+ if (!hashPhaseDone) { /* if the hash phase not completed */
+ hashtable = node->hashjointable;
+ if (hashtable == NULL) { /* if the hash table has not been created */
+ /* ----------------
+ * create the hash table
+ * ----------------
+ */
+ hashtable = ExecHashTableCreate(hashNode);
+ hjstate->hj_HashTable = hashtable;
+ innerhashkey = hashNode->hashkey;
+ hjstate->hj_InnerHashKey = innerhashkey;
+
+ /* ----------------
+ * execute the Hash node, to build the hash table
+ * ----------------
+ */
+ hashNode->hashtable = hashtable;
+ innerTupleSlot = ExecProcNode((Plan *)hashNode, (Plan*) node);
+ }
+ bucket = NULL;
+ curtuple = NULL;
+ curbatch = 0;
+ node->hashdone = true;
+ }
+ nbatch = hashtable->nbatch;
+ outerbatches = hjstate->hj_OuterBatches;
+ if (nbatch > 0 && outerbatches == NULL) { /* if needs hash partition */
+ /* -----------------
+ * allocate space for file descriptors of outer batch files
+ * then open the batch files in the current process
+ * -----------------
+ */
+ innerhashkey = hashNode->hashkey;
+ hjstate->hj_InnerHashKey = innerhashkey;
+ outerbatchNames = (RelativeAddr*)
+ ABSADDR(hashtable->outerbatchNames);
+ outerbatches = (File*)
+ palloc(nbatch * sizeof(File));
+ for (i=0; i<nbatch; i++) {
+ outerbatches[i] = FileNameOpenFile(
+ ABSADDR(outerbatchNames[i]),
+ O_CREAT | O_RDWR, 0600);
+ }
+ hjstate->hj_OuterBatches = outerbatches;
+
+ /* ------------------
+ * get the inner batch file descriptors from the
+ * hash node
+ * ------------------
+ */
+ hjstate->hj_InnerBatches =
+ hashNode->hashstate->hashBatches;
+ }
+ outerbatchPos = (RelativeAddr*)ABSADDR(hashtable->outerbatchPos);
+ curbatch = hashtable->curbatch;
+ outerbatchNames = (RelativeAddr*)ABSADDR(hashtable->outerbatchNames);
+
+ /* ----------------
+ * Now get an outer tuple and probe into the hash table for matches
+ * ----------------
+ */
+ outerTupleSlot = hjstate->jstate.cs_OuterTupleSlot;
+ outerVar = get_leftop(clause);
+
+ bucketno = -1; /* if bucketno remains -1, means use old outer tuple */
+ if (TupIsNull(outerTupleSlot)) {
+ /*
+ * if the current outer tuple is nil, get a new one
+ */
+ outerTupleSlot = (TupleTableSlot*)
+ ExecHashJoinOuterGetTuple(outerNode, (Plan*)node, hjstate);
+
+ while (curbatch <= nbatch && TupIsNull(outerTupleSlot)) {
+ /*
+ * if the current batch runs out, switch to new batch
+ */
+ curbatch = ExecHashJoinNewBatch(hjstate);
+ if (curbatch > nbatch) {
+ /*
+ * when the last batch runs out, clean up
+ */
+ ExecHashTableDestroy(hashtable);
+ hjstate->hj_HashTable = NULL;
+ return NULL;
+ }
+ else
+ outerTupleSlot = (TupleTableSlot*)
+ ExecHashJoinOuterGetTuple(outerNode, (Plan*)node, hjstate);
+ }
+ /*
+ * now we get an outer tuple, find the corresponding bucket for
+ * this tuple from the hash table
+ */
+ econtext->ecxt_outertuple = outerTupleSlot;
+
+#ifdef HJDEBUG
+ printf("Probing ");
+#endif
+ bucketno = ExecHashGetBucket(hashtable, econtext, outerVar);
+ bucket=(HashBucket)(ABSADDR(hashtable->top)
+ + bucketno * hashtable->bucketsize);
+ }
+
+ for (;;) {
+ /* ----------------
+ * Now we've got an outer tuple and the corresponding hash bucket,
+ * but this tuple may not belong to the current batch.
+ * ----------------
+ */
+ if (curbatch == 0 && bucketno != -1) /* if this is the first pass */
+ batch = ExecHashJoinGetBatch(bucketno, hashtable, nbatch);
+ else
+ batch = 0;
+ if (batch > 0) {
+ /*
+ * if the current outer tuple does not belong to
+ * the current batch, save to the tmp file for
+ * the corresponding batch.
+ */
+ buffer = ABSADDR(hashtable->batch) + (batch - 1) * BLCKSZ;
+ batchno = batch - 1;
+ pos = ExecHashJoinSaveTuple(outerTupleSlot->val,
+ buffer,
+ outerbatches[batchno],
+ ABSADDR(outerbatchPos[batchno]));
+
+ outerbatchPos[batchno] = RELADDR(pos);
+ }
+ else if (bucket != NULL) {
+ do {
+ /*
+ * scan the hash bucket for matches
+ */
+ curtuple = ExecScanHashBucket(hjstate,
+ bucket,
+ curtuple,
+ hjclauses,
+ econtext);
+
+ if (curtuple != NULL) {
+ /*
+ * we've got a match, but still need to test qpqual
+ */
+ inntuple = ExecStoreTuple(curtuple,
+ hjstate->hj_HashTupleSlot,
+ InvalidBuffer,
+ false); /* don't pfree this tuple */
+
+ econtext->ecxt_innertuple = inntuple;
+
+ /* ----------------
+ * test to see if we pass the qualification
+ * ----------------
+ */
+ qualResult = ExecQual((List*)qual, econtext);
+
+ /* ----------------
+ * if we pass the qual, then save state for next call and
+ * have ExecProject form the projection, store it
+ * in the tuple table, and return the slot.
+ * ----------------
+ */
+ if (qualResult) {
+ ProjectionInfo *projInfo;
+ TupleTableSlot *result;
+ bool isDone;
+
+ hjstate->hj_CurBucket = bucket;
+ hjstate->hj_CurTuple = curtuple;
+ hashtable->curbatch = curbatch;
+ hjstate->jstate.cs_OuterTupleSlot = outerTupleSlot;
+
+ projInfo = hjstate->jstate.cs_ProjInfo;
+ result = ExecProject(projInfo, &isDone);
+ hjstate->jstate.cs_TupFromTlist = !isDone;
+ return result;
+ }
+ }
+ }
+ while (curtuple != NULL);
+ }
+
+ /* ----------------
+ * Now the current outer tuple has run out of matches,
+ * so we free it and get a new outer tuple.
+ * ----------------
+ */
+ outerTupleSlot = (TupleTableSlot*)
+ ExecHashJoinOuterGetTuple(outerNode, (Plan*) node, hjstate);
+
+ while (curbatch <= nbatch && TupIsNull(outerTupleSlot)) {
+ /*
+ * if the current batch runs out, switch to new batch
+ */
+ curbatch = ExecHashJoinNewBatch(hjstate);
+ if (curbatch > nbatch) {
+ /*
+ * when the last batch runs out, clean up
+ */
+ ExecHashTableDestroy(hashtable);
+ hjstate->hj_HashTable = NULL;
+ return NULL;
+ }
+ else
+ outerTupleSlot = (TupleTableSlot*)
+ ExecHashJoinOuterGetTuple(outerNode, (Plan*)node, hjstate);
+ }
+
+ /* ----------------
+ * Now get the corresponding hash bucket for the new
+ * outer tuple.
+ * ----------------
+ */
+ econtext->ecxt_outertuple = outerTupleSlot;
+#ifdef HJDEBUG
+ printf("Probing ");
+#endif
+ bucketno = ExecHashGetBucket(hashtable, econtext, outerVar);
+ bucket=(HashBucket)(ABSADDR(hashtable->top)
+ + bucketno * hashtable->bucketsize);
+ curtuple = NULL;
+ }
+}
+
+/* ----------------------------------------------------------------
+ * ExecInitHashJoin
+ *
+ * Init routine for HashJoin node.
+ * ----------------------------------------------------------------
+ */
+bool /* return: initialization status */
+ExecInitHashJoin(HashJoin *node, EState *estate, Plan *parent)
+{
+ HashJoinState *hjstate;
+ Plan *outerNode;
+ Hash *hashNode;
+
+ /* ----------------
+ * assign the node's execution state
+ * ----------------
+ */
+ node->join.state = estate;
+
+ /* ----------------
+ * create state structure
+ * ----------------
+ */
+ hjstate = makeNode(HashJoinState);
+
+ node->hashjoinstate = hjstate;
+
+ /* ----------------
+ * Miscellanious initialization
+ *
+ * + assign node's base_id
+ * + assign debugging hooks and
+ * + create expression context for node
+ * ----------------
+ */
+ ExecAssignNodeBaseInfo(estate, &hjstate->jstate, parent);
+ ExecAssignExprContext(estate, &hjstate->jstate);
+
+#define HASHJOIN_NSLOTS 2
+ /* ----------------
+ * tuple table initialization
+ * ----------------
+ */
+ ExecInitResultTupleSlot(estate, &hjstate->jstate);
+ ExecInitOuterTupleSlot(estate, hjstate);
+
+ /* ----------------
+ * initializes child nodes
+ * ----------------
+ */
+ outerNode = outerPlan((Plan *)node);
+ hashNode = (Hash*)innerPlan((Plan *)node);
+
+ ExecInitNode(outerNode, estate, (Plan *) node);
+ ExecInitNode((Plan*)hashNode, estate, (Plan *) node);
+
+ /* ----------------
+ * now for some voodoo. our temporary tuple slot
+ * is actually the result tuple slot of the Hash node
+ * (which is our inner plan). we do this because Hash
+ * nodes don't return tuples via ExecProcNode() -- instead
+ * the hash join node uses ExecScanHashBucket() to get
+ * at the contents of the hash table. -cim 6/9/91
+ * ----------------
+ */
+ {
+ HashState *hashstate = hashNode->hashstate;
+ TupleTableSlot *slot =
+ hashstate->cstate.cs_ResultTupleSlot;
+ hjstate->hj_HashTupleSlot = slot;
+ }
+ hjstate->hj_OuterTupleSlot->ttc_tupleDescriptor =
+ ExecGetTupType(outerNode);
+
+/*
+ hjstate->hj_OuterTupleSlot->ttc_execTupDescriptor =
+ ExecGetExecTupDesc(outerNode);
+*/
+
+ /* ----------------
+ * initialize tuple type and projection info
+ * ----------------
+ */
+ ExecAssignResultTypeFromTL((Plan*) node, &hjstate->jstate);
+ ExecAssignProjectionInfo((Plan*) node, &hjstate->jstate);
+
+ /* ----------------
+ * XXX comment me
+ * ----------------
+ */
+
+ node->hashdone = false;
+
+ hjstate->hj_HashTable = (HashJoinTable)NULL;
+ hjstate->hj_HashTableShmId = (IpcMemoryId)0;
+ hjstate->hj_CurBucket = (HashBucket )NULL;
+ hjstate->hj_CurTuple = (HeapTuple )NULL;
+ hjstate->hj_CurOTuple = (OverflowTuple )NULL;
+ hjstate->hj_InnerHashKey = (Var*)NULL;
+ hjstate->hj_OuterBatches = (File*)NULL;
+ hjstate->hj_InnerBatches = (File*)NULL;
+ hjstate->hj_OuterReadPos = (char*)NULL;
+ hjstate->hj_OuterReadBlk = (int)0;
+
+ hjstate->jstate.cs_OuterTupleSlot = (TupleTableSlot*) NULL;
+ hjstate->jstate.cs_TupFromTlist = (bool) false;
+
+ return TRUE;
+}
+
+int
+ExecCountSlotsHashJoin(HashJoin *node)
+{
+ return ExecCountSlotsNode(outerPlan(node)) +
+ ExecCountSlotsNode(innerPlan(node)) +
+ HASHJOIN_NSLOTS;
+}
+
+/* ----------------------------------------------------------------
+ * ExecEndHashJoin
+ *
+ * clean up routine for HashJoin node
+ * ----------------------------------------------------------------
+ */
+void
+ExecEndHashJoin(HashJoin *node)
+{
+ HashJoinState *hjstate;
+
+ /* ----------------
+ * get info from the HashJoin state
+ * ----------------
+ */
+ hjstate = node->hashjoinstate;
+
+ /* ----------------
+ * free hash table in case we end plan before all tuples are retrieved
+ * ---------------
+ */
+ if (hjstate->hj_HashTable) {
+ ExecHashTableDestroy(hjstate->hj_HashTable);
+ hjstate->hj_HashTable = NULL;
+ }
+
+ /* ----------------
+ * Free the projection info and the scan attribute info
+ *
+ * Note: we don't ExecFreeResultType(hjstate)
+ * because the rule manager depends on the tupType
+ * returned by ExecMain(). So for now, this
+ * is freed at end-transaction time. -cim 6/2/91
+ * ----------------
+ */
+ ExecFreeProjectionInfo(&hjstate->jstate);
+
+ /* ----------------
+ * clean up subtrees
+ * ----------------
+ */
+ ExecEndNode(outerPlan((Plan *) node), (Plan*)node);
+ ExecEndNode(innerPlan((Plan *) node), (Plan*)node);
+
+ /* ----------------
+ * clean out the tuple table
+ * ----------------
+ */
+ ExecClearTuple(hjstate->jstate.cs_ResultTupleSlot);
+ ExecClearTuple(hjstate->hj_OuterTupleSlot);
+ ExecClearTuple(hjstate->hj_HashTupleSlot);
+
+}
+
+/* ----------------------------------------------------------------
+ * ExecHashJoinOuterGetTuple
+ *
+ * get the next outer tuple for hashjoin: either by
+ * executing a plan node as in the first pass, or from
+ * the tmp files for the hashjoin batches.
+ * ----------------------------------------------------------------
+ */
+
+static TupleTableSlot *
+ExecHashJoinOuterGetTuple(Plan *node, Plan* parent, HashJoinState *hjstate)
+{
+ TupleTableSlot *slot;
+ HashJoinTable hashtable;
+ int curbatch;
+ File *outerbatches;
+ char *outerreadPos;
+ int batchno;
+ char *outerreadBuf;
+ int outerreadBlk;
+
+ hashtable = hjstate->hj_HashTable;
+ curbatch = hashtable->curbatch;
+
+ if (curbatch == 0) { /* if it is the first pass */
+ slot = ExecProcNode(node, parent);
+ return slot;
+ }
+
+ /*
+ * otherwise, read from the tmp files
+ */
+ outerbatches = hjstate->hj_OuterBatches;
+ outerreadPos = hjstate->hj_OuterReadPos;
+ outerreadBlk = hjstate->hj_OuterReadBlk;
+ outerreadBuf = ABSADDR(hashtable->readbuf);
+ batchno = curbatch - 1;
+
+ slot = ExecHashJoinGetSavedTuple(hjstate,
+ outerreadBuf,
+ outerbatches[batchno],
+ hjstate->hj_OuterTupleSlot,
+ &outerreadBlk,
+ &outerreadPos);
+
+ hjstate->hj_OuterReadPos = outerreadPos;
+ hjstate->hj_OuterReadBlk = outerreadBlk;
+
+ return slot;
+}
+
+/* ----------------------------------------------------------------
+ * ExecHashJoinGetSavedTuple
+ *
+ * read the next tuple from a tmp file using a certain buffer
+ * ----------------------------------------------------------------
+ */
+
+static TupleTableSlot *
+ExecHashJoinGetSavedTuple(HashJoinState *hjstate,
+ char *buffer,
+ File file,
+ TupleTableSlot *tupleSlot,
+ int *block, /* return parameter */
+ char **position) /* return parameter */
+{
+ char *bufstart;
+ char *bufend;
+ int cc;
+ HeapTuple heapTuple;
+ HashJoinTable hashtable;
+
+ hashtable = hjstate->hj_HashTable;
+ bufend = buffer + *(long*)buffer;
+ bufstart = (char*)(buffer + sizeof(long));
+ if ((*position == NULL) || (*position >= bufend)) {
+ if (*position == NULL)
+ (*block) = 0;
+ else
+ (*block)++;
+ FileSeek(file, *block * BLCKSZ, SEEK_SET);
+ cc = FileRead(file, buffer, BLCKSZ);
+ NDirectFileRead++;
+ if (cc < 0)
+ perror("FileRead");
+ if (cc == 0) /* end of file */
+ return NULL;
+ else
+ (*position) = bufstart;
+ }
+ heapTuple = (HeapTuple) (*position);
+ (*position) = (char*)LONGALIGN(*position + heapTuple->t_len);
+
+ return ExecStoreTuple(heapTuple,tupleSlot,InvalidBuffer,false);
+}
+
+/* ----------------------------------------------------------------
+ * ExecHashJoinNewBatch
+ *
+ * switch to a new hashjoin batch
+ * ----------------------------------------------------------------
+ */
+int
+ExecHashJoinNewBatch(HashJoinState *hjstate)
+{
+ File *innerBatches;
+ File *outerBatches;
+ int *innerBatchSizes;
+ Var *innerhashkey;
+ HashJoinTable hashtable;
+ int nbatch;
+ char *readPos;
+ int readBlk;
+ char *readBuf;
+ TupleTableSlot *slot;
+ ExprContext *econtext;
+ int i;
+ int cc;
+ int newbatch;
+
+ hashtable = hjstate->hj_HashTable;
+ outerBatches = hjstate->hj_OuterBatches;
+ innerBatches = hjstate->hj_InnerBatches;
+ nbatch = hashtable->nbatch;
+ newbatch = hashtable->curbatch + 1;
+
+ /* ------------------
+ * this is the last process, so it will do the cleanup and
+ * batch-switching.
+ * ------------------
+ */
+ if (newbatch == 1) {
+ /*
+ * if it is end of the first pass, flush all the last pages for
+ * the batches.
+ */
+ outerBatches = hjstate->hj_OuterBatches;
+ for (i=0; i<nbatch; i++) {
+ cc = FileSeek(outerBatches[i], 0L, SEEK_END);
+ if (cc < 0)
+ perror("FileSeek");
+ cc = FileWrite(outerBatches[i],
+ ABSADDR(hashtable->batch) + i * BLCKSZ, BLCKSZ);
+ NDirectFileWrite++;
+ if (cc < 0)
+ perror("FileWrite");
+ }
+ }
+ if (newbatch > 1) {
+ /*
+ * remove the previous outer batch
+ */
+ FileUnlink(outerBatches[newbatch - 2]);
+ }
+ /*
+ * rebuild the hash table for the new inner batch
+ */
+ innerBatchSizes = (int*)ABSADDR(hashtable->innerbatchSizes);
+ /* --------------
+ * skip over empty inner batches
+ * --------------
+ */
+ while (newbatch <= nbatch && innerBatchSizes[newbatch - 1] == 0) {
+ FileUnlink(outerBatches[newbatch-1]);
+ FileUnlink(innerBatches[newbatch-1]);
+ newbatch++;
+ }
+ if (newbatch > nbatch) {
+ hashtable->pcount = hashtable->nprocess;
+
+ return newbatch;
+ }
+ ExecHashTableReset(hashtable, innerBatchSizes[newbatch - 1]);
+
+
+ econtext = hjstate->jstate.cs_ExprContext;
+ innerhashkey = hjstate->hj_InnerHashKey;
+ readPos = NULL;
+ readBlk = 0;
+ readBuf = ABSADDR(hashtable->readbuf);
+
+ while ((slot = ExecHashJoinGetSavedTuple(hjstate,
+ readBuf,
+ innerBatches[newbatch-1],
+ hjstate->hj_HashTupleSlot,
+ &readBlk,
+ &readPos))
+ && ! TupIsNull(slot)) {
+ econtext->ecxt_innertuple = slot;
+ ExecHashTableInsert(hashtable, econtext, innerhashkey,NULL);
+ /* possible bug - glass */
+ }
+
+
+ /* -----------------
+ * only the last process comes to this branch
+ * now all the processes have finished the build phase
+ * ----------------
+ */
+
+ /*
+ * after we build the hash table, the inner batch is no longer needed
+ */
+ FileUnlink(innerBatches[newbatch - 1]);
+ hjstate->hj_OuterReadPos = NULL;
+ hashtable->pcount = hashtable->nprocess;
+
+ hashtable->curbatch = newbatch;
+ return newbatch;
+}
+
+/* ----------------------------------------------------------------
+ * ExecHashJoinGetBatch
+ *
+ * determine the batch number for a bucketno
+ * +----------------+-------+-------+ ... +-------+
+ * 0 nbuckets totalbuckets
+ * batch 0 1 2 ...
+ * ----------------------------------------------------------------
+ */
+int
+ExecHashJoinGetBatch(int bucketno, HashJoinTable hashtable, int nbatch)
+{
+ int b;
+ if (bucketno < hashtable->nbuckets || nbatch == 0)
+ return 0;
+
+ b = (float)(bucketno - hashtable->nbuckets) /
+ (float)(hashtable->totalbuckets - hashtable->nbuckets) *
+ nbatch;
+ return b+1;
+}
+
+/* ----------------------------------------------------------------
+ * ExecHashJoinSaveTuple
+ *
+ * save a tuple to a tmp file using a buffer.
+ * the first few bytes in a page is an offset to the end
+ * of the page.
+ * ----------------------------------------------------------------
+ */
+
+char *
+ExecHashJoinSaveTuple(HeapTuple heapTuple,
+ char *buffer,
+ File file,
+ char *position)
+{
+ long *pageend;
+ char *pagestart;
+ char *pagebound;
+ int cc;
+
+ pageend = (long*)buffer;
+ pagestart = (char*)(buffer + sizeof(long));
+ pagebound = buffer + BLCKSZ;
+ if (position == NULL)
+ position = pagestart;
+
+ if (position + heapTuple->t_len >= pagebound) {
+ cc = FileSeek(file, 0L, SEEK_END);
+ if (cc < 0)
+ perror("FileSeek");
+ cc = FileWrite(file, buffer, BLCKSZ);
+ NDirectFileWrite++;
+ if (cc < 0)
+ perror("FileWrite");
+ position = pagestart;
+ *pageend = 0;
+ }
+ memmove(position, heapTuple, heapTuple->t_len);
+ position = (char*)LONGALIGN(position + heapTuple->t_len);
+ *pageend = position - buffer;
+
+ return position;
+}
diff --git a/src/backend/executor/nodeHashjoin.h b/src/backend/executor/nodeHashjoin.h
new file mode 100644
index 00000000000..b8c12942b3b
--- /dev/null
+++ b/src/backend/executor/nodeHashjoin.h
@@ -0,0 +1,33 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeHashjoin.h--
+ *
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id: nodeHashjoin.h,v 1.1.1.1 1996/07/09 06:21:26 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef NODEHASHJOIN_H
+#define NODEHASHJOIN_H
+
+extern TupleTableSlot *ExecHashJoin(HashJoin *node);
+
+extern bool ExecInitHashJoin(HashJoin *node, EState *estate, Plan *parent);
+
+extern int ExecCountSlotsHashJoin(HashJoin *node);
+
+extern void ExecEndHashJoin(HashJoin *node);
+
+extern int ExecHashJoinNewBatch(HashJoinState *hjstate);
+
+extern char *ExecHashJoinSaveTuple(HeapTuple heapTuple, char *buffer,
+ File file, char *position);
+
+extern int ExecHashJoinGetBatch(int bucketno, HashJoinTable hashtable,
+ int nbatch);
+
+
+#endif /* NODEHASHJOIN_H */
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
new file mode 100644
index 00000000000..758fabdefe5
--- /dev/null
+++ b/src/backend/executor/nodeIndexscan.c
@@ -0,0 +1,902 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeIndexscan.c--
+ * Routines to support indexes and indexed scans of relations
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeIndexscan.c,v 1.1.1.1 1996/07/09 06:21:26 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+ * INTERFACE ROUTINES
+ * ExecInsertIndexTuples inserts tuples into indices on result relation
+ *
+ * ExecIndexScan scans a relation using indices
+ * ExecIndexNext using index to retrieve next tuple
+ * ExecInitIndexScan creates and initializes state info.
+ * ExecIndexReScan rescans the indexed relation.
+ * ExecEndIndexScan releases all storage.
+ * ExecIndexMarkPos marks scan position.
+ * ExecIndexRestrPos restores scan position.
+ *
+ * NOTES
+ * the code supporting ExecInsertIndexTuples should be
+ * collected and merged with the genam stuff.
+ *
+ */
+#include "executor/executor.h"
+#include "executor/nodeIndexscan.h"
+
+#include "optimizer/clauses.h" /* for get_op, get_leftop, get_rightop */
+#include "parser/parsetree.h" /* for rt_fetch() */
+
+#include "access/skey.h"
+#include "utils/palloc.h"
+#include "catalog/index.h"
+#include "storage/bufmgr.h"
+#include "storage/lmgr.h"
+#include "nodes/nodeFuncs.h"
+
+/* ----------------
+ * Misc stuff to move to executor.h soon -cim 6/5/90
+ * ----------------
+ */
+#define NO_OP 0
+#define LEFT_OP 1
+#define RIGHT_OP 2
+
+static TupleTableSlot *IndexNext(IndexScan *node);
+
+/* ----------------------------------------------------------------
+ * IndexNext
+ *
+ * Retrieve a tuple from the IndexScan node's currentRelation
+ * using the indices in the IndexScanState information.
+ *
+ * note: the old code mentions 'Primary indices'. to my knowledge
+ * we only support a single secondary index. -cim 9/11/89
+ *
+ * old comments:
+ * retrieve a tuple from relation using the indices given.
+ * The indices are used in the order they appear in 'indices'.
+ * The indices may be primary or secondary indices:
+ * * primary index -- scan the relation 'relID' using keys supplied.
+ * * secondary index -- scan the index relation to get the 'tid' for
+ * a tuple in the relation 'relID'.
+ * If the current index(pointed by 'indexPtr') fails to return a
+ * tuple, the next index in the indices is used.
+ *
+ * bug fix so that it should retrieve on a null scan key.
+ * ----------------------------------------------------------------
+ */
+static TupleTableSlot *
+IndexNext(IndexScan *node)
+{
+ EState *estate;
+ CommonScanState *scanstate;
+ IndexScanState *indexstate;
+ ScanDirection direction;
+ int indexPtr;
+ IndexScanDescPtr scanDescs;
+ IndexScanDesc scandesc;
+ Relation heapRelation;
+ RetrieveIndexResult result;
+ ItemPointer iptr;
+ HeapTuple tuple;
+ TupleTableSlot *slot;
+ Buffer buffer = InvalidBuffer;
+
+ /* ----------------
+ * extract necessary information from index scan node
+ * ----------------
+ */
+ estate = node->scan.plan.state;
+ direction = estate->es_direction;
+ scanstate = node->scan.scanstate;
+ indexstate = node->indxstate;
+ indexPtr = indexstate->iss_IndexPtr;
+ scanDescs = indexstate->iss_ScanDescs;
+ scandesc = scanDescs[ indexPtr ];
+ heapRelation = scanstate->css_currentRelation;
+
+ slot = scanstate->css_ScanTupleSlot;
+
+ /* ----------------
+ * ok, now that we have what we need, fetch an index tuple.
+ * ----------------
+ */
+
+ for(;;) {
+ result = index_getnext(scandesc, direction);
+ /* ----------------
+ * if scanning this index succeeded then return the
+ * appropriate heap tuple.. else return NULL.
+ * ----------------
+ */
+ if (result) {
+ iptr = &result->heap_iptr;
+ tuple = heap_fetch(heapRelation,
+ NowTimeQual,
+ iptr,
+ &buffer);
+ /* be tidy */
+ pfree(result);
+
+ if (tuple == NULL) {
+ /* ----------------
+ * we found a deleted tuple, so keep on scanning..
+ * ----------------
+ */
+ if (BufferIsValid(buffer))
+ ReleaseBuffer(buffer);
+ continue;
+ }
+
+ /* ----------------
+ * store the scanned tuple in the scan tuple slot of
+ * the scan state. Eventually we will only do this and not
+ * return a tuple. Note: we pass 'false' because tuples
+ * returned by amgetnext are pointers onto disk pages and
+ * were not created with palloc() and so should not be pfree()'d.
+ * ----------------
+ */
+ ExecStoreTuple(tuple, /* tuple to store */
+ slot, /* slot to store in */
+ buffer, /* buffer associated with tuple */
+ false); /* don't pfree */
+
+ return slot;
+ }
+
+ /* ----------------
+ * if we get here it means the index scan failed so we
+ * are at the end of the scan..
+ * ----------------
+ */
+ return ExecClearTuple(slot);
+ }
+}
+
+/* ----------------------------------------------------------------
+ * ExecIndexScan(node)
+ *
+ * old comments:
+ * Scans the relation using primary or secondary indices and returns
+ * the next qualifying tuple in the direction specified.
+ * It calls ExecScan() and passes it the access methods which returns
+ * the next tuple using the indices.
+ *
+ * Conditions:
+ * -- the "cursor" maintained by the AMI is positioned at the tuple
+ * returned previously.
+ *
+ * Initial States:
+ * -- the relation indicated is opened for scanning so that the
+ * "cursor" is positioned before the first qualifying tuple.
+ * -- all index realtions are opened for scanning.
+ * -- indexPtr points to the first index.
+ * -- state variable ruleFlag = nil.
+ * ----------------------------------------------------------------
+ */
+TupleTableSlot *
+ExecIndexScan(IndexScan *node)
+{
+ TupleTableSlot *returnTuple;
+
+ /* ----------------
+ * use IndexNext as access method
+ * ----------------
+ */
+ returnTuple = ExecScan(&node->scan, IndexNext);
+ return returnTuple;
+}
+
+/* ----------------------------------------------------------------
+ * ExecIndexReScan(node)
+ *
+ * Recalculates the value of the scan keys whose value depends on
+ * information known at runtime and rescans the indexed relation.
+ * Updating the scan key was formerly done separately in
+ * ExecUpdateIndexScanKeys. Integrating it into ReScan
+ * makes rescans of indices and
+ * relations/general streams more uniform.
+ *
+ * ----------------------------------------------------------------
+ */
+void
+ExecIndexReScan(IndexScan *node, ExprContext *exprCtxt, Plan* parent)
+{
+ EState *estate;
+ IndexScanState *indexstate;
+ ScanDirection direction;
+ IndexScanDescPtr scanDescs;
+ ScanKey *scanKeys;
+ IndexScanDesc sdesc;
+ ScanKey skey;
+ int numIndices;
+ int i;
+
+ Pointer *runtimeKeyInfo;
+ int indexPtr;
+ int *numScanKeys;
+ List *indxqual;
+ List *qual;
+ int n_keys;
+ ScanKey scan_keys;
+ int *run_keys;
+ int j;
+ Expr *clause;
+ Node *scanexpr;
+ Datum scanvalue;
+ bool isNull;
+ bool isDone;
+
+ indexstate = node->indxstate;
+ estate = node->scan.plan.state;
+ direction = estate->es_direction;
+ indexstate = node->indxstate;
+ numIndices = indexstate->iss_NumIndices;
+ scanDescs = indexstate->iss_ScanDescs;
+ scanKeys = indexstate->iss_ScanKeys;
+
+ runtimeKeyInfo = (Pointer *) indexstate->iss_RuntimeKeyInfo;
+
+ if (runtimeKeyInfo != NULL) {
+ /*
+ * get the index qualifications and
+ * recalculate the appropriate values
+ */
+ indexPtr = indexstate->iss_IndexPtr;
+ indxqual = node->indxqual;
+ qual = nth(indexPtr, indxqual);
+ numScanKeys = indexstate->iss_NumScanKeys;
+ n_keys = numScanKeys[indexPtr];
+ run_keys = (int *) runtimeKeyInfo[indexPtr];
+ scan_keys = (ScanKey) scanKeys[indexPtr];
+
+ for (j=0; j < n_keys; j++) {
+ /*
+ * If we have a run-time key, then extract the run-time
+ * expression and evaluate it with respect to the current
+ * outer tuple. We then stick the result into the scan
+ * key.
+ */
+ if (run_keys[j] != NO_OP) {
+ clause = nth(j, qual);
+ scanexpr = (run_keys[j] == RIGHT_OP) ?
+ (Node*) get_rightop(clause) : (Node*) get_leftop(clause) ;
+ /* pass in isDone but ignore it. We don't iterate in quals */
+ scanvalue = (Datum)
+ ExecEvalExpr(scanexpr, exprCtxt, &isNull, &isDone);
+ scan_keys[j].sk_argument = scanvalue;
+ }
+ }
+ }
+
+ /*
+ * rescans all indices
+ *
+ * note: AMrescan assumes only one scan key. This may have
+ * to change if we ever decide to support multiple keys.
+ */
+ for (i = 0; i < numIndices; i++) {
+ sdesc = scanDescs[ i ];
+ skey = scanKeys[ i ];
+ index_rescan(sdesc, direction, skey);
+ }
+
+ /* ----------------
+ * perhaps return something meaningful
+ * ----------------
+ */
+ return;
+}
+
+/* ----------------------------------------------------------------
+ * ExecEndIndexScan
+ *
+ * old comments
+ * Releases any storage allocated through C routines.
+ * Returns nothing.
+ * ----------------------------------------------------------------
+ */
+void
+ExecEndIndexScan(IndexScan *node)
+{
+ CommonScanState *scanstate;
+ IndexScanState *indexstate;
+ ScanKey *scanKeys;
+ int numIndices;
+ int i;
+
+ scanstate = node->scan.scanstate;
+ indexstate = node->indxstate;
+
+ /* ----------------
+ * extract information from the node
+ * ----------------
+ */
+ numIndices = indexstate->iss_NumIndices;
+ scanKeys = indexstate->iss_ScanKeys;
+
+ /* ----------------
+ * Free the projection info and the scan attribute info
+ *
+ * Note: we don't ExecFreeResultType(scanstate)
+ * because the rule manager depends on the tupType
+ * returned by ExecMain(). So for now, this
+ * is freed at end-transaction time. -cim 6/2/91
+ * ----------------
+ */
+ ExecFreeProjectionInfo(&scanstate->cstate);
+
+ /* ----------------
+ * close the heap and index relations
+ * ----------------
+ */
+ ExecCloseR((Plan *) node);
+
+ /* ----------------
+ * free the scan keys used in scanning the indices
+ * ----------------
+ */
+ for (i=0; i<numIndices; i++) {
+ if (scanKeys[i]!=NULL)
+ pfree(scanKeys[i]);
+
+ }
+
+ /* ----------------
+ * clear out tuple table slots
+ * ----------------
+ */
+ ExecClearTuple(scanstate->cstate.cs_ResultTupleSlot);
+ ExecClearTuple(scanstate->css_ScanTupleSlot);
+/* ExecClearTuple(scanstate->css_RawTupleSlot); */
+}
+
+/* ----------------------------------------------------------------
+ * ExecIndexMarkPos
+ *
+ * old comments
+ * Marks scan position by marking the current index.
+ * Returns nothing.
+ * ----------------------------------------------------------------
+ */
+void
+ExecIndexMarkPos(IndexScan *node)
+{
+ IndexScanState *indexstate;
+ IndexScanDescPtr indexScanDescs;
+ IndexScanDesc scanDesc;
+ int indexPtr;
+
+ indexstate = node->indxstate;
+ indexPtr = indexstate->iss_IndexPtr;
+ indexScanDescs = indexstate->iss_ScanDescs;
+ scanDesc = indexScanDescs[ indexPtr ];
+
+ /* ----------------
+ * XXX access methods don't return marked positions so
+ * ----------------
+ */
+ IndexScanMarkPosition( scanDesc );
+ return;
+}
+
+/* ----------------------------------------------------------------
+ * ExecIndexRestrPos
+ *
+ * old comments
+ * Restores scan position by restoring the current index.
+ * Returns nothing.
+ *
+ * XXX Assumes previously marked scan position belongs to current index
+ * ----------------------------------------------------------------
+ */
+void
+ExecIndexRestrPos(IndexScan *node)
+{
+ IndexScanState *indexstate;
+ IndexScanDescPtr indexScanDescs;
+ IndexScanDesc scanDesc;
+ int indexPtr;
+
+ indexstate = node->indxstate;
+ indexPtr = indexstate->iss_IndexPtr;
+ indexScanDescs = indexstate->iss_ScanDescs;
+ scanDesc = indexScanDescs[ indexPtr ];
+
+ IndexScanRestorePosition( scanDesc );
+}
+
+/* ----------------------------------------------------------------
+ * ExecInitIndexScan
+ *
+ * Initializes the index scan's state information, creates
+ * scan keys, and opens the base and index relations.
+ *
+ * Note: index scans have 2 sets of state information because
+ * we have to keep track of the base relation and the
+ * index relations.
+ *
+ * old comments
+ * Creates the run-time state information for the node and
+ * sets the relation id to contain relevant decriptors.
+ *
+ * Parameters:
+ * node: IndexNode node produced by the planner.
+ * estate: the execution state initialized in InitPlan.
+ * ----------------------------------------------------------------
+ */
+bool
+ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
+{
+ IndexScanState *indexstate;
+ CommonScanState *scanstate;
+ List *indxqual;
+ List *indxid;
+ int i;
+ int numIndices;
+ int indexPtr;
+ ScanKey *scanKeys;
+ int *numScanKeys;
+ RelationPtr relationDescs;
+ IndexScanDescPtr scanDescs;
+ Pointer *runtimeKeyInfo;
+ bool have_runtime_keys;
+ List *rangeTable;
+ RangeTblEntry *rtentry;
+ Index relid;
+ Oid reloid;
+ TimeQual timeQual;
+
+ Relation currentRelation;
+ HeapScanDesc currentScanDesc;
+ ScanDirection direction;
+ int baseid;
+
+ /* ----------------
+ * assign execution state to node
+ * ----------------
+ */
+ node->scan.plan.state = estate;
+
+ /* --------------------------------
+ * Part 1) initialize scan state
+ *
+ * create new CommonScanState for node
+ * --------------------------------
+ */
+ scanstate = makeNode(CommonScanState);
+/*
+ scanstate->ss_ProcOuterFlag = false;
+ scanstate->ss_OldRelId = 0;
+*/
+
+ node->scan.scanstate = scanstate;
+
+ /* ----------------
+ * assign node's base_id .. we don't use AssignNodeBaseid() because
+ * the increment is done later on after we assign the index scan's
+ * scanstate. see below.
+ * ----------------
+ */
+ baseid = estate->es_BaseId;
+/* scanstate->csstate.cstate.bnode.base_id = baseid; */
+ scanstate->cstate.cs_base_id = baseid;
+
+ /* ----------------
+ * create expression context for node
+ * ----------------
+ */
+ ExecAssignExprContext(estate, &scanstate->cstate);
+
+#define INDEXSCAN_NSLOTS 3
+ /* ----------------
+ * tuple table initialization
+ * ----------------
+ */
+ ExecInitResultTupleSlot(estate, &scanstate->cstate);
+ ExecInitScanTupleSlot(estate, scanstate);
+/* ExecInitRawTupleSlot(estate, scanstate); */
+
+ /* ----------------
+ * initialize projection info. result type comes from scan desc
+ * below..
+ * ----------------
+ */
+ ExecAssignProjectionInfo((Plan *) node, &scanstate->cstate);
+
+ /* --------------------------------
+ * Part 2) initialize index scan state
+ *
+ * create new IndexScanState for node
+ * --------------------------------
+ */
+ indexstate = makeNode(IndexScanState);
+ indexstate->iss_NumIndices = 0;
+ indexstate->iss_IndexPtr = 0;
+ indexstate->iss_ScanKeys = NULL;
+ indexstate->iss_NumScanKeys = NULL;
+ indexstate->iss_RuntimeKeyInfo = NULL;
+ indexstate->iss_RelationDescs = NULL;
+ indexstate->iss_ScanDescs = NULL;
+
+ node->indxstate = indexstate;
+
+ /* ----------------
+ * assign base id to index scan state also
+ * ----------------
+ */
+ indexstate->cstate.cs_base_id = baseid;
+ baseid++;
+ estate->es_BaseId = baseid;
+
+ /* ----------------
+ * get the index node information
+ * ----------------
+ */
+ indxid = node->indxid;
+ indxqual = node->indxqual;
+ numIndices = length(indxid);
+ indexPtr = 0;
+
+ CXT1_printf("ExecInitIndexScan: context is %d\n", CurrentMemoryContext);
+
+ /* ----------------
+ * scanKeys is used to keep track of the ScanKey's. This is needed
+ * because a single scan may use several indices and each index has
+ * its own ScanKey.
+ * ----------------
+ */
+ numScanKeys = (int *) palloc(numIndices * sizeof(int));
+ scanKeys = (ScanKey *) palloc(numIndices * sizeof(ScanKey));
+ relationDescs = (RelationPtr) palloc(numIndices * sizeof(Relation));
+ scanDescs = (IndexScanDescPtr) palloc(numIndices * sizeof(IndexScanDesc));
+
+ /* ----------------
+ * initialize runtime key info.
+ * ----------------
+ */
+ have_runtime_keys = false;
+ runtimeKeyInfo = (Pointer *)
+ palloc(numIndices * sizeof(Pointer));
+
+ /* ----------------
+ * build the index scan keys from the index qualification
+ * ----------------
+ */
+ for (i=0; i < numIndices; i++) {
+ int j;
+ List *qual;
+ int n_keys;
+ ScanKey scan_keys;
+ int *run_keys;
+
+ qual = nth(i, indxqual);
+ n_keys = length(qual);
+ scan_keys = (n_keys <= 0) ? NULL :
+ (ScanKey)palloc(n_keys * sizeof(ScanKeyData));
+
+ CXT1_printf("ExecInitIndexScan: context is %d\n",
+ CurrentMemoryContext);
+
+ if (n_keys > 0) {
+ run_keys = (int *) palloc(n_keys * sizeof(int));
+ }
+
+ /* ----------------
+ * for each opclause in the given qual,
+ * convert each qual's opclause into a single scan key
+ * ----------------
+ */
+ for (j=0; j < n_keys; j++) {
+ Expr *clause; /* one part of index qual */
+ Oper *op; /* operator used in scan.. */
+ Node *leftop; /* expr on lhs of operator */
+ Node *rightop; /* expr on rhs ... */
+
+ int scanvar; /* which var identifies varattno */
+ AttrNumber varattno; /* att number used in scan */
+ Oid opid; /* operator id used in scan */
+ Datum scanvalue; /* value used in scan (if const) */
+
+ /* ----------------
+ * extract clause information from the qualification
+ * ----------------
+ */
+ clause = nth(j, qual);
+
+ op = (Oper*)clause->oper;
+ if (!IsA(op,Oper))
+ elog(WARN, "ExecInitIndexScan: op not an Oper!");
+
+ opid = op->opid;
+
+ /* ----------------
+ * Here we figure out the contents of the index qual.
+ * The usual case is (op var const) or (op const var)
+ * which means we form a scan key for the attribute
+ * listed in the var node and use the value of the const.
+ *
+ * If we don't have a const node, then it means that
+ * one of the var nodes refers to the "scan" tuple and
+ * is used to determine which attribute to scan, and the
+ * other expression is used to calculate the value used in
+ * scanning the index.
+ *
+ * This means our index scan's scan key is a function of
+ * information obtained during the execution of the plan
+ * in which case we need to recalculate the index scan key
+ * at run time.
+ *
+ * Hence, we set have_runtime_keys to true and then set
+ * the appropriate flag in run_keys to LEFT_OP or RIGHT_OP.
+ * The corresponding scan keys are recomputed at run time.
+ * ----------------
+ */
+
+ scanvar = NO_OP;
+
+ /* ----------------
+ * determine information in leftop
+ * ----------------
+ */
+ leftop = (Node*) get_leftop(clause);
+
+ if (IsA(leftop,Var) && var_is_rel((Var*)leftop)) {
+ /* ----------------
+ * if the leftop is a "rel-var", then it means
+ * that it is a var node which tells us which
+ * attribute to use for our scan key.
+ * ----------------
+ */
+ varattno = ((Var*) leftop)->varattno;
+ scanvar = LEFT_OP;
+ } else if (IsA(leftop,Const)) {
+ /* ----------------
+ * if the leftop is a const node then it means
+ * it identifies the value to place in our scan key.
+ * ----------------
+ */
+ run_keys[ j ] = NO_OP;
+ scanvalue = ((Const*) leftop)->constvalue;
+ } else if (leftop != NULL &&
+ is_funcclause(leftop) &&
+ var_is_rel(lfirst(((Expr*)leftop)->args))) {
+ /* ----------------
+ * if the leftop is a func node then it means
+ * it identifies the value to place in our scan key.
+ * Since functional indices have only one attribute
+ * the attno must always be set to 1.
+ * ----------------
+ */
+ varattno = 1;
+ scanvar = LEFT_OP;
+
+ } else {
+ /* ----------------
+ * otherwise, the leftop contains information usable
+ * at runtime to figure out the value to place in our
+ * scan key.
+ * ----------------
+ */
+ have_runtime_keys = true;
+ run_keys[ j ] = LEFT_OP;
+ scanvalue = Int32GetDatum((int32) true);
+ }
+
+ /* ----------------
+ * now determine information in rightop
+ * ----------------
+ */
+ rightop = (Node*) get_rightop(clause);
+
+ if (IsA(rightop,Var) && var_is_rel((Var*)rightop)) {
+ /* ----------------
+ * here we make sure only one op identifies the
+ * scan-attribute...
+ * ----------------
+ */
+ if (scanvar == LEFT_OP)
+ elog(WARN, "ExecInitIndexScan: %s",
+ "both left and right op's are rel-vars");
+
+ /* ----------------
+ * if the rightop is a "rel-var", then it means
+ * that it is a var node which tells us which
+ * attribute to use for our scan key.
+ * ----------------
+ */
+ varattno = ((Var*) rightop)->varattno;
+ scanvar = RIGHT_OP;
+
+ } else if (IsA(rightop,Const)) {
+ /* ----------------
+ * if the leftop is a const node then it means
+ * it identifies the value to place in our scan key.
+ * ----------------
+ */
+ run_keys[ j ] = NO_OP;
+ scanvalue = ((Const*) rightop)->constvalue;
+
+ } else if (rightop!=NULL &&
+ is_funcclause(rightop) &&
+ var_is_rel(lfirst(((Expr*)rightop)->args))) {
+ /* ----------------
+ * if the rightop is a func node then it means
+ * it identifies the value to place in our scan key.
+ * Since functional indices have only one attribute
+ * the attno must always be set to 1.
+ * ----------------
+ */
+ if (scanvar == LEFT_OP)
+ elog(WARN, "ExecInitIndexScan: %s",
+ "both left and right ops are rel-vars");
+
+ varattno = 1;
+ scanvar = RIGHT_OP;
+
+ } else {
+ /* ----------------
+ * otherwise, the leftop contains information usable
+ * at runtime to figure out the value to place in our
+ * scan key.
+ * ----------------
+ */
+ have_runtime_keys = true;
+ run_keys[ j ] = RIGHT_OP;
+ scanvalue = Int32GetDatum((int32) true);
+ }
+
+ /* ----------------
+ * now check that at least one op tells us the scan
+ * attribute...
+ * ----------------
+ */
+ if (scanvar == NO_OP)
+ elog(WARN, "ExecInitIndexScan: %s",
+ "neither leftop nor rightop refer to scan relation");
+
+ /* ----------------
+ * initialize the scan key's fields appropriately
+ * ----------------
+ */
+ ScanKeyEntryInitialize(&scan_keys[j],
+ 0,
+ varattno, /* attribute number to scan */
+ (RegProcedure) opid, /* reg proc to use */
+ (Datum) scanvalue); /* constant */
+ }
+
+ /* ----------------
+ * store the key information into our array.
+ * ----------------
+ */
+ numScanKeys[ i ] = n_keys;
+ scanKeys[ i ] = scan_keys;
+ runtimeKeyInfo[ i ] = (Pointer) run_keys;
+ }
+
+ indexstate->iss_NumIndices = numIndices;
+ indexstate->iss_IndexPtr = indexPtr;
+ indexstate->iss_ScanKeys = scanKeys;
+ indexstate->iss_NumScanKeys = numScanKeys;
+
+ /* ----------------
+ * If all of our keys have the form (op var const) , then we have no
+ * runtime keys so we store NULL in the runtime key info.
+ * Otherwise runtime key info contains an array of pointers
+ * (one for each index) to arrays of flags (one for each key)
+ * which indicate that the qual needs to be evaluated at runtime.
+ * -cim 10/24/89
+ * ----------------
+ */
+ if (have_runtime_keys)
+ {
+ indexstate->iss_RuntimeKeyInfo = (Pointer) runtimeKeyInfo;
+ }
+ else {
+ indexstate->iss_RuntimeKeyInfo = NULL;
+ for (i=0; i < numIndices; i++) {
+ List *qual;
+ int n_keys;
+ qual = nth(i, indxqual);
+ n_keys = length(qual);
+ if (n_keys > 0)
+ pfree(runtimeKeyInfo[i]);
+ }
+ pfree(runtimeKeyInfo);
+ }
+
+ /* ----------------
+ * get the range table and direction information
+ * from the execution state (these are needed to
+ * open the relations).
+ * ----------------
+ */
+ rangeTable = estate->es_range_table;
+ direction = estate->es_direction;
+
+ /* ----------------
+ * open the base relation
+ * ----------------
+ */
+ relid = node->scan.scanrelid;
+ rtentry = rt_fetch(relid, rangeTable);
+ reloid = rtentry->relid;
+ timeQual = rtentry->timeQual;
+
+ ExecOpenScanR(reloid, /* relation */
+ 0, /* nkeys */
+ (ScanKey) NULL, /* scan key */
+ 0, /* is index */
+ direction, /* scan direction */
+ timeQual, /* time qual */
+ &currentRelation, /* return: rel desc */
+ (Pointer *) &currentScanDesc); /* return: scan desc */
+
+ scanstate->css_currentRelation = currentRelation;
+ scanstate->css_currentScanDesc = currentScanDesc;
+
+
+ /* ----------------
+ * get the scan type from the relation descriptor.
+ * ----------------
+ */
+ ExecAssignScanType(scanstate, RelationGetTupleDescriptor(currentRelation));
+ ExecAssignResultTypeFromTL((Plan *) node, &scanstate->cstate);
+
+ /* ----------------
+ * index scans don't have subtrees..
+ * ----------------
+ */
+/* scanstate->ss_ProcOuterFlag = false; */
+
+ /* ----------------
+ * open the index relations and initialize
+ * relation and scan descriptors.
+ * ----------------
+ */
+ for (i=0; i < numIndices; i++) {
+ Oid indexOid;
+
+ indexOid = (Oid)nth(i, indxid);
+
+ if (indexOid != 0) {
+ ExecOpenScanR(indexOid, /* relation */
+ numScanKeys[ i ], /* nkeys */
+ scanKeys[ i ], /* scan key */
+ true, /* is index */
+ direction, /* scan direction */
+ timeQual, /* time qual */
+ &(relationDescs[ i ]), /* return: rel desc */
+ (Pointer *) &(scanDescs[ i ]));
+ /* return: scan desc */
+ }
+ }
+
+ indexstate->iss_RelationDescs = relationDescs;
+ indexstate->iss_ScanDescs = scanDescs;
+
+ indexstate->cstate.cs_TupFromTlist = false;
+
+ /* ----------------
+ * all done.
+ * ----------------
+ */
+ return TRUE;
+}
+
+int
+ExecCountSlotsIndexScan(IndexScan *node)
+{
+ return ExecCountSlotsNode(outerPlan((Plan *)node)) +
+ ExecCountSlotsNode(innerPlan((Plan *)node)) +
+ INDEXSCAN_NSLOTS;
+}
diff --git a/src/backend/executor/nodeIndexscan.h b/src/backend/executor/nodeIndexscan.h
new file mode 100644
index 00000000000..27bbff0a293
--- /dev/null
+++ b/src/backend/executor/nodeIndexscan.h
@@ -0,0 +1,32 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeIndexscan.h--
+ *
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id: nodeIndexscan.h,v 1.1.1.1 1996/07/09 06:21:26 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef NODEINDEXSCAN_H
+#define NODEINDEXSCAN_H
+
+extern TupleTableSlot *ExecIndexScan(IndexScan *node);
+
+extern void ExecIndexReScan(IndexScan *node, ExprContext *exprCtxt, Plan *parent);
+
+extern void ExecEndIndexScan(IndexScan *node);
+
+extern void ExecIndexMarkPos(IndexScan *node);
+
+extern void ExecIndexRestrPos(IndexScan *node);
+
+extern void ExecUpdateIndexScanKeys(IndexScan *node, ExprContext *econtext);
+
+extern bool ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent);
+
+extern int ExecCountSlotsIndexScan(IndexScan *node);
+
+#endif /* NODEINDEXSCAN_H */
diff --git a/src/backend/executor/nodeMaterial.c b/src/backend/executor/nodeMaterial.c
new file mode 100644
index 00000000000..88fc93d2d4e
--- /dev/null
+++ b/src/backend/executor/nodeMaterial.c
@@ -0,0 +1,392 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeMaterial.c--
+ * Routines to handle materialization nodes.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeMaterial.c,v 1.1.1.1 1996/07/09 06:21:26 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+ * INTERFACE ROUTINES
+ * ExecMaterial - generate a temporary relation
+ * ExecInitMaterial - initialize node and subnodes..
+ * ExecEndMaterial - shutdown node and subnodes
+ *
+ */
+
+#include "executor/executor.h"
+#include "executor/nodeMaterial.h"
+#include "catalog/catalog.h"
+#include "optimizer/internal.h" /* for _TEMP_RELATION_ID_ */
+
+/* ----------------------------------------------------------------
+ * ExecMaterial
+ *
+ * The first time this is called, ExecMaterial retrieves tuples
+ * this node's outer subplan and inserts them into a temporary
+ * relation. After this is done, a flag is set indicating that
+ * the subplan has been materialized. Once the relation is
+ * materialized, the first tuple is then returned. Successive
+ * calls to ExecMaterial return successive tuples from the temp
+ * relation.
+ *
+ * Initial State:
+ *
+ * ExecMaterial assumes the temporary relation has been
+ * created and openend by ExecInitMaterial during the prior
+ * InitPlan() phase.
+ *
+ * ----------------------------------------------------------------
+ */
+TupleTableSlot * /* result tuple from subplan */
+ExecMaterial(Material *node)
+{
+ EState *estate;
+ MaterialState *matstate;
+ Plan *outerNode;
+ ScanDirection dir;
+ Relation tempRelation;
+ Relation currentRelation;
+ HeapScanDesc currentScanDesc;
+ HeapTuple heapTuple;
+ TupleTableSlot *slot;
+ Buffer buffer;
+
+ /* ----------------
+ * get state info from node
+ * ----------------
+ */
+ matstate = node->matstate;
+ estate = node->plan.state;
+ dir = estate->es_direction;
+
+ /* ----------------
+ * the first time we call this, we retrieve all tuples
+ * from the subplan into a temporary relation and then
+ * we sort the relation. Subsequent calls return tuples
+ * from the temporary relation.
+ * ----------------
+ */
+
+ if (matstate->mat_Flag == false) {
+ /* ----------------
+ * set all relations to be scanned in the forward direction
+ * while creating the temporary relation.
+ * ----------------
+ */
+ estate->es_direction = EXEC_FRWD;
+
+ /* ----------------
+ * if we couldn't create the temp or current relations then
+ * we print a warning and return NULL.
+ * ----------------
+ */
+ tempRelation = matstate->mat_TempRelation;
+ if (tempRelation == NULL) {
+ elog(DEBUG, "ExecMaterial: temp relation is NULL! aborting...");
+ return NULL;
+ }
+
+ currentRelation = matstate->csstate.css_currentRelation;
+ if (currentRelation == NULL) {
+ elog(DEBUG, "ExecMaterial: current relation is NULL! aborting...");
+ return NULL;
+ }
+
+ /* ----------------
+ * retrieve tuples from the subplan and
+ * insert them in the temporary relation
+ * ----------------
+ */
+ outerNode = outerPlan((Plan *) node);
+ for (;;) {
+ slot = ExecProcNode(outerNode, (Plan*) node);
+
+ heapTuple = slot->val;
+ if (heapTuple == NULL)
+ break;
+
+ heap_insert(tempRelation, /* relation desc */
+ heapTuple); /* heap tuple to insert */
+
+ ExecClearTuple( slot);
+ }
+ currentRelation = tempRelation;
+
+ /* ----------------
+ * restore to user specified direction
+ * ----------------
+ */
+ estate->es_direction = dir;
+
+ /* ----------------
+ * now initialize the scan descriptor to scan the
+ * sorted relation and update the sortstate information
+ * ----------------
+ */
+ currentScanDesc = heap_beginscan(currentRelation, /* relation */
+ ScanDirectionIsBackward(dir),
+ /* bkwd flag */
+ NowTimeQual, /* time qual */
+ 0, /* num scan keys */
+ NULL); /* scan keys */
+ matstate->csstate.css_currentRelation = currentRelation;
+ matstate->csstate.css_currentScanDesc = currentScanDesc;
+
+ ExecAssignScanType(&matstate->csstate,
+ RelationGetTupleDescriptor(currentRelation));
+
+ /* ----------------
+ * finally set the sorted flag to true
+ * ----------------
+ */
+ matstate->mat_Flag = true;
+ }
+
+ /* ----------------
+ * at this point we know we have a sorted relation so
+ * we preform a simple scan on it with amgetnext()..
+ * ----------------
+ */
+ currentScanDesc = matstate->csstate.css_currentScanDesc;
+
+ heapTuple = heap_getnext(currentScanDesc, /* scan desc */
+ ScanDirectionIsBackward(dir),
+ /* bkwd flag */
+ &buffer); /* return: buffer */
+
+ /* ----------------
+ * put the tuple into the scan tuple slot and return the slot.
+ * Note: since the tuple is really a pointer to a page, we don't want
+ * to call pfree() on it..
+ * ----------------
+ */
+ slot = (TupleTableSlot *)matstate->csstate.css_ScanTupleSlot;
+
+ return ExecStoreTuple(heapTuple, /* tuple to store */
+ slot, /* slot to store in */
+ buffer, /* buffer for this tuple */
+ false); /* don't pfree this pointer */
+
+}
+
+/* ----------------------------------------------------------------
+ * ExecInitMaterial
+ * ----------------------------------------------------------------
+ */
+bool /* initialization status */
+ExecInitMaterial(Material *node, EState *estate, Plan *parent)
+{
+ MaterialState *matstate;
+ Plan *outerPlan;
+ TupleDesc tupType;
+ Relation tempDesc;
+ int len;
+
+ /* ----------------
+ * assign the node's execution state
+ * ----------------
+ */
+ node->plan.state = estate;
+
+ /* ----------------
+ * create state structure
+ * ----------------
+ */
+ matstate = makeNode(MaterialState);
+ matstate->mat_Flag = false;
+ matstate->mat_TempRelation = NULL;
+ node->matstate = matstate;
+
+ /* ----------------
+ * Miscellanious initialization
+ *
+ * + assign node's base_id
+ * + assign debugging hooks and
+ * + assign result tuple slot
+ *
+ * Materialization nodes don't need ExprContexts because
+ * they never call ExecQual or ExecTargetList.
+ * ----------------
+ */
+ ExecAssignNodeBaseInfo(estate, &matstate->csstate.cstate, parent);
+
+#define MATERIAL_NSLOTS 1
+ /* ----------------
+ * tuple table initialization
+ * ----------------
+ */
+ ExecInitScanTupleSlot(estate, &matstate->csstate);
+
+ /* ----------------
+ * initializes child nodes
+ * ----------------
+ */
+ outerPlan = outerPlan((Plan *) node);
+ ExecInitNode(outerPlan, estate, (Plan *) node);
+
+ /* ----------------
+ * initialize matstate information
+ * ----------------
+ */
+ matstate->mat_Flag = false;
+
+ /* ----------------
+ * initialize tuple type. no need to initialize projection
+ * info because this node doesn't do projections.
+ * ----------------
+ */
+ ExecAssignScanTypeFromOuterPlan((Plan *) node, &matstate->csstate);
+ matstate->csstate.cstate.cs_ProjInfo = NULL;
+
+ /* ----------------
+ * get type information needed for ExecCreatR
+ * ----------------
+ */
+ tupType = ExecGetScanType(&matstate->csstate);
+
+ /* ----------------
+ * ExecCreatR wants it's second argument to be an object id of
+ * a relation in the range table or a _TEMP_RELATION_ID
+ * indicating that the relation is not in the range table.
+ *
+ * In the second case ExecCreatR creates a temp relation.
+ * (currently this is the only case we support -cim 10/16/89)
+ * ----------------
+ */
+ /* ----------------
+ * create the temporary relation
+ * ----------------
+ */
+/* len = ExecTargetListLength(node->plan.targetlist); */
+ tempDesc = ExecCreatR(tupType, _TEMP_RELATION_ID_);
+
+ /* ----------------
+ * save the relation descriptor in the sortstate
+ * ----------------
+ */
+ matstate->mat_TempRelation = tempDesc;
+ matstate->csstate.css_currentRelation = tempDesc;
+
+ /* ----------------
+ * return relation oid of temporary relation in a list
+ * (someday -- for now we return LispTrue... cim 10/12/89)
+ * ----------------
+ */
+ return TRUE;
+}
+
+int
+ExecCountSlotsMaterial(Material *node)
+{
+ return ExecCountSlotsNode(outerPlan((Plan *)node)) +
+ ExecCountSlotsNode(innerPlan((Plan *)node)) +
+ MATERIAL_NSLOTS;
+}
+
+/* ----------------------------------------------------------------
+ * ExecEndMaterial
+ *
+ * old comments
+ * destroys the temporary relation.
+ * ----------------------------------------------------------------
+ */
+void
+ExecEndMaterial(Material *node)
+{
+ MaterialState *matstate;
+ Relation tempRelation;
+ Plan *outerPlan;
+
+ /* ----------------
+ * get info from the material state
+ * ----------------
+ */
+ matstate = node->matstate;
+ tempRelation = matstate->mat_TempRelation;
+
+ heap_destroyr(tempRelation);
+
+ /* ----------------
+ * close the temp relation and shut down the scan.
+ * ----------------
+ */
+ ExecCloseR((Plan *) node);
+
+ /* ----------------
+ * shut down the subplan
+ * ----------------
+ */
+ outerPlan = outerPlan((Plan *) node);
+ ExecEndNode(outerPlan, (Plan*) node);
+
+ /* ----------------
+ * clean out the tuple table
+ * ----------------
+ */
+ ExecClearTuple(matstate->csstate.css_ScanTupleSlot);
+}
+
+#if 0 /* not used */
+/* ----------------------------------------------------------------
+ * ExecMaterialMarkPos
+ * ----------------------------------------------------------------
+ */
+List /* nothing of interest */
+ExecMaterialMarkPos(Material node)
+{
+ MaterialState matstate;
+ HeapScanDesc sdesc;
+
+ /* ----------------
+ * if we haven't materialized yet, just return NIL.
+ * ----------------
+ */
+ matstate = get_matstate(node);
+ if (get_mat_Flag(matstate) == false)
+ return NIL;
+
+ /* ----------------
+ * XXX access methods don't return positions yet so
+ * for now we return NIL. It's possible that
+ * they will never return positions for all I know -cim 10/16/89
+ * ----------------
+ */
+ sdesc = get_css_currentScanDesc((CommonScanState)matstate);
+ heap_markpos(sdesc);
+
+ return NIL;
+}
+
+/* ----------------------------------------------------------------
+ * ExecMaterialRestrPos
+ * ----------------------------------------------------------------
+ */
+void
+ExecMaterialRestrPos(Material node)
+{
+ MaterialState matstate;
+ HeapScanDesc sdesc;
+
+ /* ----------------
+ * if we haven't materialized yet, just return.
+ * ----------------
+ */
+ matstate = get_matstate(node);
+ if (get_mat_Flag(matstate) == false)
+ return;
+
+ /* ----------------
+ * restore the scan to the previously marked position
+ * ----------------
+ */
+ sdesc = get_css_currentScanDesc((CommonScanState)matstate);
+ heap_restrpos(sdesc);
+}
+#endif
+
diff --git a/src/backend/executor/nodeMaterial.h b/src/backend/executor/nodeMaterial.h
new file mode 100644
index 00000000000..d85b025b7bf
--- /dev/null
+++ b/src/backend/executor/nodeMaterial.h
@@ -0,0 +1,23 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeMaterial.h--
+ *
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id: nodeMaterial.h,v 1.1.1.1 1996/07/09 06:21:27 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef NODEMATERIAL_H
+#define NODEMATERIAL_H
+
+extern TupleTableSlot *ExecMaterial(Material *node);
+extern bool ExecInitMaterial(Material *node, EState *estate, Plan *parent);
+extern int ExecCountSlotsMaterial(Material *node);
+extern void ExecEndMaterial(Material *node);
+extern List ExecMaterialMarkPos(Material *node);
+extern void ExecMaterialRestrPos(Material *node);
+
+#endif /* NODEMATERIAL_H */
diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c
new file mode 100644
index 00000000000..54a3aa28c4e
--- /dev/null
+++ b/src/backend/executor/nodeMergejoin.c
@@ -0,0 +1,1194 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeMergejoin.c--
+ * routines supporting merge joins
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeMergejoin.c,v 1.1.1.1 1996/07/09 06:21:27 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+ * INTERFACE ROUTINES
+ * ExecMergeJoin mergejoin outer and inner relations.
+ * ExecInitMergeJoin creates and initializes run time states
+ * ExecEndMergeJoin cleand up the node.
+ *
+ * NOTES
+ * Essential operation of the merge join algorithm is as follows:
+ * (** indicates the tuples satisify the merge clause).
+ *
+ * Join { -
+ * get initial outer and inner tuples INITIALIZE
+ * Skip Inner SKIPINNER
+ * mark inner position JOINMARK
+ * do forever { -
+ * while (outer ** inner) { JOINTEST
+ * join tuples JOINTUPLES
+ * advance inner position NEXTINNER
+ * } -
+ * advance outer position NEXTOUTER
+ * if (outer ** mark) { TESTOUTER
+ * restore inner position to mark TESTOUTER
+ * continue -
+ * } else { -
+ * Skip Outer SKIPOUTER
+ * mark inner position JOINMARK
+ * } -
+ * } -
+ * } -
+ *
+ * Skip Outer { SKIPOUTER
+ * if (inner ** outer) Join Tuples JOINTUPLES
+ * while (outer < inner) SKIPOUTER
+ * advance outer SKIPOUTER
+ * if (outer > inner) SKIPOUTER
+ * Skip Inner SKIPINNER
+ * } -
+ *
+ * Skip Inner { SKIPINNER
+ * if (inner ** outer) Join Tuples JOINTUPLES
+ * while (outer > inner) SKIPINNER
+ * advance inner SKIPINNER
+ * if (outer < inner) SKIPINNER
+ * Skip Outer SKIPOUTER
+ * } -
+ *
+ * Currently, the merge join operation is coded in the fashion
+ * of a state machine. At each state, we do something and then
+ * proceed to another state. This state is stored in the node's
+ * execution state information and is preserved across calls to
+ * ExecMergeJoin. -cim 10/31/89
+ *
+ * Warning: This code is known to fail for inequality operations
+ * and is being redesigned. Specifically, = and > work
+ * but the logic is not correct for <. Since mergejoins
+ * are no better then nestloops for inequalitys, the planner
+ * should not plan them anyways. Alternatively, the
+ * planner could just exchange the inner/outer relations
+ * if it ever sees a <... -cim 7/1/90
+ *
+ * Update: The executor tuple table has long since alleviated the
+ * problem described above -cim 4/23/91
+ *
+ */
+#include "executor/executor.h"
+#include "executor/nodeMergejoin.h"
+#include "utils/lsyscache.h"
+
+/* ----------------------------------------------------------------
+ * MarkInnerTuple and RestoreInnerTuple macros
+ *
+ * when we "mark" a tuple, we place a pointer to it
+ * in the marked tuple slot. now there are two pointers
+ * to this tuple and we don't want it to be freed until
+ * next time we mark a tuple, so we move the policy to
+ * the marked tuple slot and set the inner tuple slot policy
+ * to false.
+ *
+ * But, when we restore the inner tuple, the marked tuple
+ * retains the policy. Basically once a tuple is marked, it
+ * should only be freed when we mark another tuple. -cim 9/27/90
+ *
+ * Note: now that we store buffers in the tuple table,
+ * we have to also increment buffer reference counts
+ * correctly whenever we propagate an additional pointer
+ * to a buffer item. Later, when ExecStoreTuple() is
+ * called again on this slot, the refcnt is decremented
+ * when the old tuple is replaced.
+ * ----------------------------------------------------------------
+ */
+#define MarkInnerTuple(innerTupleSlot, mergestate) \
+{ \
+ bool shouldFree; \
+ shouldFree = ExecSetSlotPolicy(innerTupleSlot, false); \
+ ExecStoreTuple(innerTupleSlot->val, \
+ mergestate->mj_MarkedTupleSlot, \
+ innerTupleSlot->ttc_buffer, \
+ shouldFree); \
+ ExecIncrSlotBufferRefcnt(innerTupleSlot); \
+}
+
+#define RestoreInnerTuple(innerTupleSlot, markedTupleSlot) \
+ ExecStoreTuple(markedTupleSlot->val, \
+ innerTupleSlot, \
+ markedTupleSlot->ttc_buffer, \
+ false); \
+ ExecIncrSlotBufferRefcnt(innerTupleSlot)
+
+/* ----------------------------------------------------------------
+ * MJFormOSortopI
+ *
+ * This takes the mergeclause which is a qualification of the
+ * form ((= expr expr) (= expr expr) ...) and forms a new
+ * qualification like ((> expr expr) (> expr expr) ...) which
+ * is used by ExecMergeJoin() in order to determine if we should
+ * skip tuples.
+ *
+ * old comments
+ * The 'qual' must be of the form:
+ * {(= outerkey1 innerkey1)(= outerkey2 innerkey2) ...}
+ * The "sortOp outerkey innerkey" is formed by substituting the "="
+ * by "sortOp".
+ * ----------------------------------------------------------------
+ */
+static List *
+MJFormOSortopI(List *qualList, Oid sortOp)
+{
+ List *qualCopy;
+ List *qualcdr;
+ Expr *qual;
+ Oper *op;
+
+ /* ----------------
+ * qualList is a list: ((op .. ..) ...)
+ * first we make a copy of it. copyObject() makes a deep copy
+ * so let's use it instead of the old fashoned lispCopy()...
+ * ----------------
+ */
+ qualCopy = (List*) copyObject((Node*) qualList);
+
+ foreach (qualcdr, qualCopy) {
+ /* ----------------
+ * first get the current (op .. ..) list
+ * ----------------
+ */
+ qual = lfirst(qualcdr);
+
+ /* ----------------
+ * now get at the op
+ * ----------------
+ */
+ op = (Oper*)qual->oper;
+ if (!IsA(op,Oper)) {
+ elog(DEBUG, "MJFormOSortopI: op not an Oper!");
+ return NIL;
+ }
+
+ /* ----------------
+ * change it's opid and since Op nodes now carry around a
+ * cached pointer to the associated op function, we have
+ * to make sure we invalidate this. Otherwise you get bizarre
+ * behavior when someone runs a mergejoin with _exec_repeat_ > 1
+ * -cim 4/23/91
+ * ----------------
+ */
+ op->opid = sortOp;
+ op->op_fcache = NULL;
+ }
+
+ return qualCopy;
+}
+
+/* ----------------------------------------------------------------
+ * MJFormISortopO
+ *
+ * This does the same thing as MJFormOSortopI() except that
+ * it also reverses the expressions in the qualifications.
+ * For example: ((= expr1 expr2)) produces ((> expr2 expr1))
+ *
+ * old comments
+ * The 'qual' must be of the form:
+ * {(= outerkey1 innerkey1) (= outerkey2 innerkey2) ...}
+ * The 'sortOp innerkey1 outerkey" is formed by substituting the "="
+ * by "sortOp" and reversing the positions of the keys.
+ * ----------------------------------------------------------------
+ */
+List *
+MJFormISortopO(List *qualList, Oid sortOp)
+{
+ List *ISortopO;
+ List *qualcdr;
+
+ /* ----------------
+ * first generate OSortopI, a list of the form
+ * ((op outer inner) (op outer inner) ... )
+ * ----------------
+ */
+ ISortopO = MJFormOSortopI(qualList, sortOp);
+
+ /* ----------------
+ * now swap the cadr and caddr of each qual to form ISortopO,
+ * ((op inner outer) (op inner outer) ... )
+ * ----------------
+ */
+ foreach (qualcdr, ISortopO) {
+ Expr *qual;
+ List *inner;
+ List *outer;
+ qual = lfirst(qualcdr);
+
+ inner = lfirst(qual->args);
+ outer = lfirst(lnext(qual->args));
+ lfirst(qual->args) = outer;
+ lfirst(lnext(qual->args)) = inner;
+ }
+
+ return ISortopO;
+}
+
+/* ----------------------------------------------------------------
+ * MergeCompare
+ *
+ * Compare the keys according to 'compareQual' which is of the
+ * form: {(key1a > key2a)(key1b > key2b) ...}.
+ *
+ * (actually, it could also be the form (key1a < key2a)..)
+ *
+ * This is different from calling ExecQual because ExecQual returns
+ * true only if ALL the comparisions clauses are satisfied.
+ * However, there is an order of significance among the keys with
+ * the first keys being most significant. Therefore, the clauses
+ * are evaluated in order and the 'compareQual' is satisfied
+ * if (key1i > key2i) is true and (key1j = key2j) for 0 < j < i.
+ * ----------------------------------------------------------------
+ */
+bool
+MergeCompare(List *eqQual, List *compareQual, ExprContext *econtext)
+{
+ List *clause;
+ List *eqclause;
+ Datum const_value;
+ bool isNull;
+ bool isDone;
+
+ /* ----------------
+ * if we have no compare qualification, return nil
+ * ----------------
+ */
+ if (compareQual == NIL)
+ return false;
+
+ /* ----------------
+ * for each pair of clauses, test them until
+ * our compare conditions are satisified
+ * ----------------
+ */
+ eqclause = eqQual;
+ foreach (clause, compareQual) {
+ /* ----------------
+ * first test if our compare clause is satisified.
+ * if so then return true. ignore isDone, don't iterate in
+ * quals.
+ * ----------------
+ */
+ const_value = (Datum)
+ ExecEvalExpr((Node*) lfirst(clause), econtext, &isNull, &isDone);
+
+ if (DatumGetInt32(const_value) != 0)
+ return true;
+
+ /* ----------------
+ * ok, the compare clause failed so we test if the keys
+ * are equal... if key1 != key2, we return false.
+ * otherwise key1 = key2 so we move on to the next pair of keys.
+ *
+ * ignore isDone, don't iterate in quals.
+ * ----------------
+ */
+ const_value = ExecEvalExpr((Node*) lfirst(eqclause),
+ econtext,
+ &isNull,
+ &isDone);
+
+ if (DatumGetInt32(const_value) == 0)
+ return false;
+ eqclause = lnext(eqclause);
+ }
+
+ /* ----------------
+ * if we get here then it means none of our key greater-than
+ * conditions were satisified so we return false.
+ * ----------------
+ */
+ return false;
+}
+
+/* ----------------------------------------------------------------
+ * ExecMergeTupleDump
+ *
+ * This function is called through the MJ_dump() macro
+ * when EXEC_MERGEJOINDEBUG is defined
+ * ----------------------------------------------------------------
+ */
+void
+ExecMergeTupleDumpInner(ExprContext *econtext)
+{
+ TupleTableSlot *innerSlot;
+
+ printf("==== inner tuple ====\n");
+ innerSlot = econtext->ecxt_innertuple;
+ if (TupIsNull(innerSlot))
+ printf("(nil)\n");
+ else
+ debugtup(innerSlot->val,
+ innerSlot->ttc_tupleDescriptor);
+}
+
+void
+ExecMergeTupleDumpOuter(ExprContext *econtext)
+{
+ TupleTableSlot *outerSlot;
+
+ printf("==== outer tuple ====\n");
+ outerSlot = econtext->ecxt_outertuple;
+ if (TupIsNull(outerSlot))
+ printf("(nil)\n");
+ else
+ debugtup(outerSlot->val,
+ outerSlot->ttc_tupleDescriptor);
+}
+
+void
+ExecMergeTupleDumpMarked(ExprContext *econtext,
+ MergeJoinState *mergestate)
+{
+ TupleTableSlot *markedSlot;
+
+ printf("==== marked tuple ====\n");
+ markedSlot = mergestate->mj_MarkedTupleSlot;
+
+ if (TupIsNull(markedSlot))
+ printf("(nil)\n");
+ else
+ debugtup(markedSlot->val,
+ markedSlot->ttc_tupleDescriptor);
+}
+
+void
+ExecMergeTupleDump(ExprContext *econtext, MergeJoinState *mergestate)
+{
+ printf("******** ExecMergeTupleDump ********\n");
+
+ ExecMergeTupleDumpInner(econtext);
+ ExecMergeTupleDumpOuter(econtext);
+ ExecMergeTupleDumpMarked(econtext, mergestate);
+
+ printf("******** \n");
+}
+
+/* ----------------------------------------------------------------
+ * ExecMergeJoin
+ *
+ * old comments
+ * Details of the merge-join routines:
+ *
+ * (1) ">" and "<" operators
+ *
+ * Merge-join is done by joining the inner and outer tuples satisfying
+ * the join clauses of the form ((= outerKey innerKey) ...).
+ * The join clauses is provided by the query planner and may contain
+ * more than one (= outerKey innerKey) clauses (for composite key).
+ *
+ * However, the query executor needs to know whether an outer
+ * tuple is "greater/smaller" than an inner tuple so that it can
+ * "synchronize" the two relations. For e.g., consider the following
+ * relations:
+ *
+ * outer: (0 ^1 1 2 5 5 5 6 6 7) current tuple: 1
+ * inner: (1 ^3 5 5 5 5 6) current tuple: 3
+ *
+ * To continue the merge-join, the executor needs to scan both inner
+ * and outer relations till the matching tuples 5. It needs to know
+ * that currently inner tuple 3 is "greater" than outer tuple 1 and
+ * therefore it should scan the outer relation first to find a
+ * matching tuple and so on.
+ *
+ * Therefore, when initializing the merge-join node, the executor
+ * creates the "greater/smaller" clause by substituting the "="
+ * operator in the join clauses with the sort operator used to
+ * sort the outer and inner relation forming (outerKey sortOp innerKey).
+ * The sort operator is "<" if the relations are in ascending order
+ * otherwise, it is ">" if the relations are in descending order.
+ * The opposite "smaller/greater" clause is formed by reversing the
+ * outer and inner keys forming (innerKey sortOp outerKey).
+ *
+ * (2) repositioning inner "cursor"
+ *
+ * Consider the above relations and suppose that the executor has
+ * just joined the first outer "5" with the last inner "5". The
+ * next step is of course to join the second outer "5" with all
+ * the inner "5's". This requires repositioning the inner "cursor"
+ * to point at the first inner "5". This is done by "marking" the
+ * first inner 5 and restore the "cursor" to it before joining
+ * with the second outer 5. The access method interface provides
+ * routines to mark and restore to a tuple.
+ * ----------------------------------------------------------------
+ */
+TupleTableSlot *
+ExecMergeJoin(MergeJoin *node)
+{
+ EState *estate;
+ MergeJoinState *mergestate;
+ ScanDirection direction;
+ List *innerSkipQual;
+ List *outerSkipQual;
+ List *mergeclauses;
+ List *qual;
+ bool qualResult;
+ bool compareResult;
+
+ Plan *innerPlan;
+ TupleTableSlot *innerTupleSlot;
+
+ Plan *outerPlan;
+ TupleTableSlot *outerTupleSlot;
+
+ TupleTableSlot *markedTupleSlot;
+
+ ExprContext *econtext;
+
+ /* ----------------
+ * get information from node
+ * ----------------
+ */
+ mergestate = node->mergestate;
+ estate = node->join.state;
+ direction = estate->es_direction;
+ innerPlan = innerPlan((Plan *)node);
+ outerPlan = outerPlan((Plan *)node);
+ econtext = mergestate->jstate.cs_ExprContext;
+ mergeclauses = node->mergeclauses;
+ qual = node->join.qual;
+
+ if (ScanDirectionIsForward(direction)) {
+ outerSkipQual = mergestate->mj_OSortopI;
+ innerSkipQual = mergestate->mj_ISortopO;
+ } else {
+ outerSkipQual = mergestate->mj_ISortopO;
+ innerSkipQual = mergestate->mj_OSortopI;
+ }
+
+ /* ----------------
+ * ok, everything is setup.. let's go to work
+ * ----------------
+ */
+ if (mergestate->jstate.cs_TupFromTlist) {
+ TupleTableSlot *result;
+ ProjectionInfo *projInfo;
+ bool isDone;
+
+ projInfo = mergestate->jstate.cs_ProjInfo;
+ result = ExecProject(projInfo, &isDone);
+ if (!isDone)
+ return result;
+ }
+ for (;;) {
+ /* ----------------
+ * get the current state of the join and do things accordingly.
+ * Note: The join states are highlighted with 32-* comments for
+ * improved readability.
+ * ----------------
+ */
+ MJ_dump(econtext, mergestate);
+
+ switch (mergestate->mj_JoinState) {
+ /* ********************************
+ * EXEC_MJ_INITIALIZE means that this is the first time
+ * ExecMergeJoin() has been called and so we have to
+ * initialize the inner, outer and marked tuples as well
+ * as various stuff in the expression context.
+ * ********************************
+ */
+ case EXEC_MJ_INITIALIZE:
+ MJ_printf("ExecMergeJoin: EXEC_MJ_INITIALIZE\n");
+ /* ----------------
+ * Note: at this point, if either of our inner or outer
+ * tuples are nil, then the join ends immediately because
+ * we know one of the subplans is empty.
+ * ----------------
+ */
+ innerTupleSlot = ExecProcNode(innerPlan, (Plan*)node);
+ if (TupIsNull(innerTupleSlot)) {
+ MJ_printf("ExecMergeJoin: **** inner tuple is nil ****\n");
+ return NULL;
+ }
+
+ outerTupleSlot = ExecProcNode(outerPlan, (Plan*)node);
+ if (TupIsNull(outerTupleSlot)) {
+ MJ_printf("ExecMergeJoin: **** outer tuple is nil ****\n");
+ return NULL;
+ }
+
+ /* ----------------
+ * store the inner and outer tuple in the merge state
+ * ----------------
+ */
+ econtext->ecxt_innertuple = innerTupleSlot;
+ econtext->ecxt_outertuple = outerTupleSlot;
+
+ /* ----------------
+ * set the marked tuple to nil
+ * and initialize its tuple descriptor atttributes.
+ * -jeff 10 july 1991
+ * ----------------
+ */
+ ExecClearTuple(mergestate->mj_MarkedTupleSlot);
+ mergestate->mj_MarkedTupleSlot->ttc_tupleDescriptor =
+ innerTupleSlot->ttc_tupleDescriptor;
+/*
+ mergestate->mj_MarkedTupleSlot->ttc_execTupDescriptor =
+ innerTupleSlot->ttc_execTupDescriptor;
+*/
+ /* ----------------
+ * initialize merge join state to skip inner tuples.
+ * ----------------
+ */
+ mergestate->mj_JoinState = EXEC_MJ_SKIPINNER;
+ break;
+
+ /* ********************************
+ * EXEC_MJ_JOINMARK means we have just found a new
+ * outer tuple and a possible matching inner tuple.
+ * This is the case after the INITIALIZE, SKIPOUTER
+ * or SKIPINNER states.
+ * ********************************
+ */
+ case EXEC_MJ_JOINMARK:
+ MJ_printf("ExecMergeJoin: EXEC_MJ_JOINMARK\n");
+ ExecMarkPos(innerPlan);
+
+ innerTupleSlot = econtext->ecxt_innertuple;
+ MarkInnerTuple(innerTupleSlot, mergestate);
+
+ mergestate->mj_JoinState = EXEC_MJ_JOINTEST;
+ break;
+
+ /* ********************************
+ * EXEC_MJ_JOINTEST means we have two tuples which
+ * might satisify the merge clause, so we test them.
+ *
+ * If they do satisify, then we join them and move
+ * on to the next inner tuple (EXEC_MJ_JOINTUPLES).
+ *
+ * If they do not satisify then advance to next outer tuple.
+ * ********************************
+ */
+ case EXEC_MJ_JOINTEST:
+ MJ_printf("ExecMergeJoin: EXEC_MJ_JOINTEST\n");
+
+ qualResult = ExecQual((List*)mergeclauses, econtext);
+ MJ_DEBUG_QUAL(mergeclauses, qualResult);
+
+ if (qualResult)
+ {
+ mergestate->mj_JoinState = EXEC_MJ_JOINTUPLES;
+ }
+ else
+ {
+ mergestate->mj_JoinState = EXEC_MJ_NEXTOUTER;
+ }
+ break;
+
+ /* ********************************
+ * EXEC_MJ_JOINTUPLES means we have two tuples which
+ * satisified the merge clause so we join them and then
+ * proceed to get the next inner tuple (EXEC_NEXT_INNER).
+ * ********************************
+ */
+ case EXEC_MJ_JOINTUPLES:
+ MJ_printf("ExecMergeJoin: EXEC_MJ_JOINTUPLES\n");
+ mergestate->mj_JoinState = EXEC_MJ_NEXTINNER;
+
+ qualResult = ExecQual((List*)qual, econtext);
+ MJ_DEBUG_QUAL(qual, qualResult);
+
+ if (qualResult) {
+ /* ----------------
+ * qualification succeeded. now form the desired
+ * projection tuple and return the slot containing it.
+ * ----------------
+ */
+ ProjectionInfo *projInfo;
+ TupleTableSlot *result;
+ bool isDone;
+
+ MJ_printf("ExecMergeJoin: **** returning tuple ****\n");
+
+ projInfo = mergestate->jstate.cs_ProjInfo;
+
+ result = ExecProject(projInfo, &isDone);
+ mergestate->jstate.cs_TupFromTlist = !isDone;
+ return result;
+ }
+ break;
+
+ /* ********************************
+ * EXEC_MJ_NEXTINNER means advance the inner scan
+ * to the next tuple. If the tuple is not nil, we then
+ * proceed to test it against the join qualification.
+ * ********************************
+ */
+ case EXEC_MJ_NEXTINNER:
+ MJ_printf("ExecMergeJoin: EXEC_MJ_NEXTINNER\n");
+
+ /* ----------------
+ * now we get the next inner tuple, if any
+ * ----------------
+ */
+ innerTupleSlot = ExecProcNode(innerPlan, (Plan*)node);
+ MJ_DEBUG_PROC_NODE(innerTupleSlot);
+ econtext->ecxt_innertuple = innerTupleSlot;
+
+ if (TupIsNull(innerTupleSlot))
+ {
+ mergestate->mj_JoinState = EXEC_MJ_NEXTOUTER;
+ }
+ else
+ {
+ mergestate->mj_JoinState = EXEC_MJ_JOINTEST;
+ }
+ break;
+
+ /* ********************************
+ * EXEC_MJ_NEXTOUTER means
+ *
+ * outer inner
+ * outer tuple - 5 5 - marked tuple
+ * 5 5
+ * 6 6 - inner tuple
+ * 7 7
+ *
+ * we know we just bumped into
+ * the first inner tuple > current outer tuple
+ * so get a new outer tuple and then proceed to test
+ * it against the marked tuple (EXEC_MJ_TESTOUTER)
+ * ********************************
+ */
+ case EXEC_MJ_NEXTOUTER:
+ MJ_printf("ExecMergeJoin: EXEC_MJ_NEXTOUTER\n");
+
+ outerTupleSlot = ExecProcNode(outerPlan, (Plan*)node);
+ MJ_DEBUG_PROC_NODE(outerTupleSlot);
+ econtext->ecxt_outertuple = outerTupleSlot;
+
+ /* ----------------
+ * if the outer tuple is null then we know
+ * we are done with the join
+ * ----------------
+ */
+ if (TupIsNull(outerTupleSlot)) {
+ MJ_printf("ExecMergeJoin: **** outer tuple is nil ****\n");
+ return NULL;
+ }
+
+ mergestate->mj_JoinState = EXEC_MJ_TESTOUTER;
+ break;
+
+ /* ********************************
+ * EXEC_MJ_TESTOUTER
+ * If the new outer tuple and the marked tuple satisify
+ * the merge clause then we know we have duplicates in
+ * the outer scan so we have to restore the inner scan
+ * to the marked tuple and proceed to join the new outer
+ * tuples with the inner tuples (EXEC_MJ_JOINTEST)
+ *
+ * This is the case when
+ *
+ * outer inner
+ * 4 5 - marked tuple
+ * outer tuple - 5 5
+ * new outer tuple - 5 5
+ * 6 8 - inner tuple
+ * 7 12
+ *
+ * new outer tuple = marked tuple
+ *
+ * If the outer tuple fails the test, then we know we have
+ * to proceed to skip outer tuples until outer >= inner
+ * (EXEC_MJ_SKIPOUTER).
+ *
+ * This is the case when
+ *
+ * outer inner
+ * 5 5 - marked tuple
+ * outer tuple - 5 5
+ * new outer tuple - 6 8 - inner tuple
+ * 7 12
+ *
+ * new outer tuple > marked tuple
+ *
+ * ********************************
+ */
+ case EXEC_MJ_TESTOUTER:
+ MJ_printf("ExecMergeJoin: EXEC_MJ_TESTOUTER\n");
+
+ /* ----------------
+ * here we compare the outer tuple with the marked inner tuple
+ * by using the marked tuple in place of the inner tuple.
+ * ----------------
+ */
+ innerTupleSlot = econtext->ecxt_innertuple;
+ markedTupleSlot = mergestate->mj_MarkedTupleSlot;
+ econtext->ecxt_innertuple = markedTupleSlot;
+
+ qualResult = ExecQual((List*)mergeclauses, econtext);
+ MJ_DEBUG_QUAL(mergeclauses, qualResult);
+
+ if (qualResult) {
+ /* ----------------
+ * the merge clause matched so now we juggle the slots
+ * back the way they were and proceed to JOINTEST.
+ * ----------------
+ */
+ econtext->ecxt_innertuple = innerTupleSlot;
+
+ RestoreInnerTuple(innerTupleSlot, markedTupleSlot);
+
+ ExecRestrPos(innerPlan);
+ mergestate->mj_JoinState = EXEC_MJ_JOINTEST;
+
+ } else {
+ /* ----------------
+ * if the inner tuple was nil and the new outer
+ * tuple didn't match the marked outer tuple then
+ * we may have the case:
+ *
+ * outer inner
+ * 4 4 - marked tuple
+ * new outer - 5 4
+ * 6 nil - inner tuple
+ * 7
+ *
+ * which means that all subsequent outer tuples will be
+ * larger than our inner tuples.
+ * ----------------
+ */
+ if (TupIsNull(innerTupleSlot)) {
+ MJ_printf("ExecMergeJoin: **** wierd case 1 ****\n");
+ return NULL;
+ }
+
+ /* ----------------
+ * restore the inner tuple and continue on to
+ * skip outer tuples.
+ * ----------------
+ */
+ econtext->ecxt_innertuple = innerTupleSlot;
+ mergestate->mj_JoinState = EXEC_MJ_SKIPOUTER;
+ }
+ break;
+
+ /* ********************************
+ * EXEC_MJ_SKIPOUTER means skip over tuples in the outer plan
+ * until we find an outer tuple > current inner tuple.
+ *
+ * For example:
+ *
+ * outer inner
+ * 5 5
+ * 5 5
+ * outer tuple - 6 8 - inner tuple
+ * 7 12
+ * 8 14
+ *
+ * we have to advance the outer scan
+ * until we find the outer 8.
+ *
+ * ********************************
+ */
+ case EXEC_MJ_SKIPOUTER:
+ MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPOUTER\n");
+ /* ----------------
+ * before we advance, make sure the current tuples
+ * do not satisify the mergeclauses. If they do, then
+ * we update the marked tuple and go join them.
+ * ----------------
+ */
+ qualResult = ExecQual((List*)mergeclauses, econtext);
+ MJ_DEBUG_QUAL(mergeclauses, qualResult);
+
+ if (qualResult) {
+ ExecMarkPos(innerPlan);
+ innerTupleSlot = econtext->ecxt_innertuple;
+
+ MarkInnerTuple(innerTupleSlot, mergestate);
+
+ mergestate->mj_JoinState = EXEC_MJ_JOINTUPLES;
+ break;
+ }
+
+ /* ----------------
+ * ok, now test the skip qualification
+ * ----------------
+ */
+ compareResult = MergeCompare(mergeclauses,
+ outerSkipQual,
+ econtext);
+
+ MJ_DEBUG_MERGE_COMPARE(outerSkipQual, compareResult);
+
+ /* ----------------
+ * compareResult is true as long as we should
+ * continue skipping tuples.
+ * ----------------
+ */
+ if (compareResult) {
+
+ outerTupleSlot = ExecProcNode(outerPlan, (Plan*)node);
+ MJ_DEBUG_PROC_NODE(outerTupleSlot);
+ econtext->ecxt_outertuple = outerTupleSlot;
+
+ /* ----------------
+ * if the outer tuple is null then we know
+ * we are done with the join
+ * ----------------
+ */
+ if (TupIsNull(outerTupleSlot)) {
+ MJ_printf("ExecMergeJoin: **** outerTuple is nil ****\n");
+ return NULL;
+ }
+ /* ----------------
+ * otherwise test the new tuple against the skip qual.
+ * (we remain in the EXEC_MJ_SKIPOUTER state)
+ * ----------------
+ */
+ break;
+ }
+
+ /* ----------------
+ * now check the inner skip qual to see if we
+ * should now skip inner tuples... if we fail the
+ * inner skip qual, then we know we have a new pair
+ * of matching tuples.
+ * ----------------
+ */
+ compareResult = MergeCompare(mergeclauses,
+ innerSkipQual,
+ econtext);
+
+ MJ_DEBUG_MERGE_COMPARE(innerSkipQual, compareResult);
+
+ if (compareResult)
+ {
+ mergestate->mj_JoinState = EXEC_MJ_SKIPINNER;
+ }
+ else
+ {
+ mergestate->mj_JoinState = EXEC_MJ_JOINMARK;
+ }
+ break;
+
+ /* ********************************
+ * EXEC_MJ_SKIPINNER means skip over tuples in the inner plan
+ * until we find an inner tuple > current outer tuple.
+ *
+ * For example:
+ *
+ * outer inner
+ * 5 5
+ * 5 5
+ * outer tuple - 12 8 - inner tuple
+ * 14 10
+ * 17 12
+ *
+ * we have to advance the inner scan
+ * until we find the inner 12.
+ *
+ * ********************************
+ */
+ case EXEC_MJ_SKIPINNER:
+ MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPINNER\n");
+ /* ----------------
+ * before we advance, make sure the current tuples
+ * do not satisify the mergeclauses. If they do, then
+ * we update the marked tuple and go join them.
+ * ----------------
+ */
+ qualResult = ExecQual((List*)mergeclauses, econtext);
+ MJ_DEBUG_QUAL(mergeclauses, qualResult);
+
+ if (qualResult) {
+ ExecMarkPos(innerPlan);
+ innerTupleSlot = econtext->ecxt_innertuple;
+
+ MarkInnerTuple(innerTupleSlot, mergestate);
+
+ mergestate->mj_JoinState = EXEC_MJ_JOINTUPLES;
+ break;
+ }
+
+ /* ----------------
+ * ok, now test the skip qualification
+ * ----------------
+ */
+ compareResult = MergeCompare(mergeclauses,
+ innerSkipQual,
+ econtext);
+
+ MJ_DEBUG_MERGE_COMPARE(innerSkipQual, compareResult);
+
+ /* ----------------
+ * compareResult is true as long as we should
+ * continue skipping tuples.
+ * ----------------
+ */
+ if (compareResult) {
+ /* ----------------
+ * now try and get a new inner tuple
+ * ----------------
+ */
+ innerTupleSlot = ExecProcNode(innerPlan, (Plan*)node);
+ MJ_DEBUG_PROC_NODE(innerTupleSlot);
+ econtext->ecxt_innertuple = innerTupleSlot;
+
+ /* ----------------
+ * if the inner tuple is null then we know
+ * we have to restore the inner scan
+ * and advance to the next outer tuple
+ * ----------------
+ */
+ if (TupIsNull(innerTupleSlot)) {
+ /* ----------------
+ * this is an interesting case.. all our
+ * inner tuples are smaller then our outer
+ * tuples so we never found an inner tuple
+ * to mark.
+ *
+ * outer inner
+ * outer tuple - 5 4
+ * 5 4
+ * 6 nil - inner tuple
+ * 7
+ *
+ * This means the join should end.
+ * ----------------
+ */
+ MJ_printf("ExecMergeJoin: **** wierd case 2 ****\n");
+ return NULL;
+ }
+
+ /* ----------------
+ * otherwise test the new tuple against the skip qual.
+ * (we remain in the EXEC_MJ_SKIPINNER state)
+ * ----------------
+ */
+ break;
+ }
+
+ /* ----------------
+ * compare finally failed and we have stopped skipping
+ * inner tuples so now check the outer skip qual
+ * to see if we should now skip outer tuples...
+ * ----------------
+ */
+ compareResult = MergeCompare(mergeclauses,
+ outerSkipQual,
+ econtext);
+
+ MJ_DEBUG_MERGE_COMPARE(outerSkipQual, compareResult);
+
+ if (compareResult)
+ {
+ mergestate->mj_JoinState = EXEC_MJ_SKIPOUTER;
+ }
+ else
+ {
+ mergestate->mj_JoinState = EXEC_MJ_JOINMARK;
+ }
+
+ break;
+
+ /* ********************************
+ * if we get here it means our code is fucked up and
+ * so we just end the join prematurely.
+ * ********************************
+ */
+ default:
+ elog(NOTICE, "ExecMergeJoin: invalid join state. aborting");
+ return NULL;
+ }
+ }
+}
+
+/* ----------------------------------------------------------------
+ * ExecInitMergeJoin
+ *
+ * old comments
+ * Creates the run-time state information for the node and
+ * sets the relation id to contain relevant decriptors.
+ * ----------------------------------------------------------------
+ */
+bool
+ExecInitMergeJoin(MergeJoin *node, EState *estate, Plan *parent)
+{
+ MergeJoinState *mergestate;
+ List *joinclauses;
+ RegProcedure rightsortop;
+ RegProcedure leftsortop;
+ RegProcedure sortop;
+
+ List *OSortopI;
+ List *ISortopO;
+
+ MJ1_printf("ExecInitMergeJoin: %s\n",
+ "initializing node");
+
+ /* ----------------
+ * assign the node's execution state and
+ * get the range table and direction from it
+ * ----------------
+ */
+ node->join.state = estate;
+
+ /* ----------------
+ * create new merge state for node
+ * ----------------
+ */
+ mergestate = makeNode(MergeJoinState);
+ mergestate->mj_OSortopI = NIL;
+ mergestate->mj_ISortopO = NIL;
+ mergestate->mj_JoinState = 0;
+ mergestate->mj_MarkedTupleSlot = NULL;
+ node->mergestate = mergestate;
+
+ /* ----------------
+ * Miscellanious initialization
+ *
+ * + assign node's base_id
+ * + assign debugging hooks and
+ * + create expression context for node
+ * ----------------
+ */
+ ExecAssignNodeBaseInfo(estate, &mergestate->jstate, parent);
+ ExecAssignExprContext(estate, &mergestate->jstate);
+
+#define MERGEJOIN_NSLOTS 2
+ /* ----------------
+ * tuple table initialization
+ * ----------------
+ */
+ ExecInitResultTupleSlot(estate, &mergestate->jstate);
+ ExecInitMarkedTupleSlot(estate, mergestate);
+
+ /* ----------------
+ * get merge sort operators.
+ *
+ * XXX for now we assume all quals in the joinclauses were
+ * sorted with the same operator in both the inner and
+ * outer relations. -cim 11/2/89
+ * ----------------
+ */
+ joinclauses = node->mergeclauses;
+
+ rightsortop = get_opcode(node->mergerightorder[0]);
+ leftsortop = get_opcode(node->mergeleftorder[0]);
+
+ if (leftsortop != rightsortop)
+ elog(NOTICE, "ExecInitMergeJoin: %s",
+ "left and right sortop's are unequal!");
+
+ sortop = rightsortop;
+
+ /* ----------------
+ * form merge skip qualifications
+ *
+ * XXX MJform routines need to be extended
+ * to take a list of sortops.. -cim 11/2/89
+ * ----------------
+ */
+ OSortopI = MJFormOSortopI(joinclauses, sortop);
+ ISortopO = MJFormISortopO(joinclauses, sortop);
+ mergestate->mj_OSortopI = OSortopI;
+ mergestate->mj_ISortopO = ISortopO;
+
+ MJ_printf("\nExecInitMergeJoin: OSortopI is ");
+ MJ_nodeDisplay(OSortopI);
+ MJ_printf("\nExecInitMergeJoin: ISortopO is ");
+ MJ_nodeDisplay(ISortopO);
+ MJ_printf("\n");
+
+ /* ----------------
+ * initialize join state
+ * ----------------
+ */
+ mergestate->mj_JoinState = EXEC_MJ_INITIALIZE;
+
+ /* ----------------
+ * initialize subplans
+ * ----------------
+ */
+ ExecInitNode(outerPlan((Plan *) node), estate, (Plan *) node);
+ ExecInitNode(innerPlan((Plan *) node), estate, (Plan *) node);
+
+ /* ----------------
+ * initialize tuple type and projection info
+ * ----------------
+ */
+ ExecAssignResultTypeFromTL((Plan *) node, &mergestate->jstate);
+ ExecAssignProjectionInfo((Plan *) node, &mergestate->jstate);
+
+ mergestate->jstate.cs_TupFromTlist = false;
+ /* ----------------
+ * initialization successful
+ * ----------------
+ */
+ MJ1_printf("ExecInitMergeJoin: %s\n",
+ "node initialized");
+
+ return TRUE;
+}
+
+int
+ExecCountSlotsMergeJoin(MergeJoin *node)
+{
+ return ExecCountSlotsNode(outerPlan((Plan *)node)) +
+ ExecCountSlotsNode(innerPlan((Plan *)node)) +
+ MERGEJOIN_NSLOTS;
+}
+
+/* ----------------------------------------------------------------
+ * ExecEndMergeJoin
+ *
+ * old comments
+ * frees storage allocated through C routines.
+ * ----------------------------------------------------------------
+ */
+void
+ExecEndMergeJoin(MergeJoin *node)
+{
+ MergeJoinState *mergestate;
+
+ MJ1_printf("ExecEndMergeJoin: %s\n",
+ "ending node processing");
+
+ /* ----------------
+ * get state information from the node
+ * ----------------
+ */
+ mergestate = node->mergestate;
+
+ /* ----------------
+ * Free the projection info and the scan attribute info
+ *
+ * Note: we don't ExecFreeResultType(mergestate)
+ * because the rule manager depends on the tupType
+ * returned by ExecMain(). So for now, this
+ * is freed at end-transaction time. -cim 6/2/91
+ * ----------------
+ */
+ ExecFreeProjectionInfo(&mergestate->jstate);
+
+ /* ----------------
+ * shut down the subplans
+ * ----------------
+ */
+ ExecEndNode((Plan*) innerPlan((Plan *) node), (Plan*)node);
+ ExecEndNode((Plan*) outerPlan((Plan *) node), (Plan*)node);
+
+ /* ----------------
+ * clean out the tuple table so that we don't try and
+ * pfree the marked tuples.. see HACK ALERT at the top of
+ * this file.
+ * ----------------
+ */
+ ExecClearTuple(mergestate->jstate.cs_ResultTupleSlot);
+ ExecClearTuple(mergestate->mj_MarkedTupleSlot);
+
+ MJ1_printf("ExecEndMergeJoin: %s\n",
+ "node processing ended");
+}
+
diff --git a/src/backend/executor/nodeMergejoin.h b/src/backend/executor/nodeMergejoin.h
new file mode 100644
index 00000000000..ebdca08e32f
--- /dev/null
+++ b/src/backend/executor/nodeMergejoin.h
@@ -0,0 +1,40 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeMergejoin.h--
+ *
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id: nodeMergejoin.h,v 1.1.1.1 1996/07/09 06:21:27 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef NODEMERGEJOIN_H
+#define NODEMERGEJOIN_H
+
+#if 0 /* aren't these static? */
+extern List MJFormOSortopI(List qualList, Oid sortOp);
+extern List MJFormISortopO(List qualList, Oid sortOp);
+#endif
+extern bool MergeCompare(List *eqQual, List *compareQual, ExprContext *econtext);
+
+extern void ExecMergeTupleDumpInner(ExprContext *econtext);
+
+extern void ExecMergeTupleDumpOuter(ExprContext *econtext);
+
+extern void ExecMergeTupleDumpMarked(ExprContext *econtext,
+ MergeJoinState *mergestate);
+
+extern void ExecMergeTupleDump(ExprContext *econtext,
+ MergeJoinState *mergestate);
+
+extern TupleTableSlot *ExecMergeJoin(MergeJoin *node);
+
+extern bool ExecInitMergeJoin(MergeJoin *node, EState *estate, Plan *parent);
+
+extern int ExecCountSlotsMergeJoin(MergeJoin *node);
+
+extern void ExecEndMergeJoin(MergeJoin *node);
+
+#endif /* NODEMERGEJOIN_H; */
diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c
new file mode 100644
index 00000000000..1ef30ce431f
--- /dev/null
+++ b/src/backend/executor/nodeNestloop.c
@@ -0,0 +1,370 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeNestloop.c--
+ * routines to support nest-loop joins
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeNestloop.c,v 1.1.1.1 1996/07/09 06:21:27 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+ * INTERFACE ROUTINES
+ * ExecNestLoop - process a nestloop join of two plans
+ * ExecInitNestLoop - initialize the join
+ * ExecEndNestLoop - shut down the join
+ */
+#include "executor/executor.h"
+#include "executor/nodeNestloop.h"
+#include "executor/nodeIndexscan.h"
+
+/* ----------------------------------------------------------------
+ * ExecNestLoop(node)
+ *
+ * old comments
+ * Returns the tuple joined from inner and outer tuples which
+ * satisfies the qualification clause.
+ *
+ * It scans the inner relation to join with current outer tuple.
+ *
+ * If none is found, next tuple form the outer relation is retrieved
+ * and the inner relation is scanned from the beginning again to join
+ * with the outer tuple.
+ *
+ * Nil is returned if all the remaining outer tuples are tried and
+ * all fail to join with the inner tuples.
+ *
+ * Nil is also returned if there is no tuple from inner realtion.
+ *
+ * Conditions:
+ * -- outerTuple contains current tuple from outer relation and
+ * the right son(inner realtion) maintains "cursor" at the tuple
+ * returned previously.
+ * This is achieved by maintaining a scan position on the outer
+ * relation.
+ *
+ * Initial States:
+ * -- the outer child and the inner child
+ * are prepared to return the first tuple.
+ * ----------------------------------------------------------------
+ */
+TupleTableSlot *
+ExecNestLoop(NestLoop *node, Plan* parent)
+{
+ NestLoopState *nlstate;
+ Plan *innerPlan;
+ Plan *outerPlan;
+ bool needNewOuterTuple;
+
+ TupleTableSlot *outerTupleSlot;
+ TupleTableSlot *innerTupleSlot;
+
+ List *qual;
+ bool qualResult;
+ ExprContext *econtext;
+
+ /* ----------------
+ * get information from the node
+ * ----------------
+ */
+ ENL1_printf("getting info from node");
+
+ nlstate = node->nlstate;
+ qual = node->join.qual;
+ outerPlan = outerPlan(&node->join);
+ innerPlan = innerPlan(&node->join);
+
+ /* ----------------
+ * initialize expression context
+ * ----------------
+ */
+ econtext = nlstate->jstate.cs_ExprContext;
+
+ /* ---------------- * get the current outer tuple
+ * ----------------
+ */
+ outerTupleSlot = nlstate->jstate.cs_OuterTupleSlot;
+ econtext->ecxt_outertuple = outerTupleSlot;
+
+ /* ----------------
+ * Ok, everything is setup for the join so now loop until
+ * we return a qualifying join tuple..
+ * ----------------
+ */
+
+ if (nlstate->jstate.cs_TupFromTlist) {
+ TupleTableSlot *result;
+ bool isDone;
+
+ result = ExecProject(nlstate->jstate.cs_ProjInfo, &isDone);
+ if (!isDone)
+ return result;
+ }
+
+ ENL1_printf("entering main loop");
+ for(;;) {
+ /* ----------------
+ * The essential idea now is to get the next inner tuple
+ * and join it with the current outer tuple.
+ * ----------------
+ */
+ needNewOuterTuple = false;
+
+ /* ----------------
+ * If outer tuple is not null then that means
+ * we are in the middle of a scan and we should
+ * restore our previously saved scan position.
+ * ----------------
+ */
+ if (! TupIsNull(outerTupleSlot)) {
+ ENL1_printf("have outer tuple, restoring outer plan");
+ ExecRestrPos(outerPlan);
+ } else {
+ ENL1_printf("outer tuple is nil, need new outer tuple");
+ needNewOuterTuple = true;
+ }
+
+ /* ----------------
+ * if we have an outerTuple, try to get the next inner tuple.
+ * ----------------
+ */
+ if (!needNewOuterTuple) {
+ ENL1_printf("getting new inner tuple");
+
+ innerTupleSlot = ExecProcNode(innerPlan, (Plan*)node);
+ econtext->ecxt_innertuple = innerTupleSlot;
+
+ if (TupIsNull(innerTupleSlot)) {
+ ENL1_printf("no inner tuple, need new outer tuple");
+ needNewOuterTuple = true;
+ }
+ }
+
+ /* ----------------
+ * loop until we have a new outer tuple and a new
+ * inner tuple.
+ * ----------------
+ */
+ while (needNewOuterTuple) {
+ /* ----------------
+ * now try to get the next outer tuple
+ * ----------------
+ */
+ ENL1_printf("getting new outer tuple");
+ outerTupleSlot = ExecProcNode(outerPlan, (Plan*)node);
+ econtext->ecxt_outertuple = outerTupleSlot;
+
+ /* ----------------
+ * if there are no more outer tuples, then the join
+ * is complete..
+ * ----------------
+ */
+ if (TupIsNull(outerTupleSlot)) {
+ ENL1_printf("no outer tuple, ending join");
+ return NULL;
+ }
+
+ /* ----------------
+ * we have a new outer tuple so we mark our position
+ * in the outer scan and save the outer tuple in the
+ * NestLoop state
+ * ----------------
+ */
+ ENL1_printf("saving new outer tuple information");
+ ExecMarkPos(outerPlan);
+ nlstate->jstate.cs_OuterTupleSlot = outerTupleSlot;
+
+ /* ----------------
+ * now rescan the inner plan and get a new inner tuple
+ * ----------------
+ */
+
+ ENL1_printf("rescanning inner plan");
+ /*
+ * The scan key of the inner plan might depend on the current
+ * outer tuple (e.g. in index scans), that's why we pass our
+ * expr context.
+ */
+ ExecReScan(innerPlan, econtext, parent);
+
+ ENL1_printf("getting new inner tuple");
+
+ innerTupleSlot = ExecProcNode(innerPlan, (Plan*)node);
+ econtext->ecxt_innertuple = innerTupleSlot;
+
+ if (TupIsNull(innerTupleSlot)) {
+ ENL1_printf("couldn't get inner tuple - need new outer tuple");
+ } else {
+ ENL1_printf("got inner and outer tuples");
+ needNewOuterTuple = false;
+ }
+ } /* while (needNewOuterTuple) */
+
+ /* ----------------
+ * at this point we have a new pair of inner and outer
+ * tuples so we test the inner and outer tuples to see
+ * if they satisify the node's qualification.
+ * ----------------
+ */
+ ENL1_printf("testing qualification");
+ qualResult = ExecQual((List*)qual, econtext);
+
+ if (qualResult) {
+ /* ----------------
+ * qualification was satisified so we project and
+ * return the slot containing the result tuple
+ * using ExecProject().
+ * ----------------
+ */
+ ProjectionInfo *projInfo;
+ TupleTableSlot *result;
+ bool isDone;
+
+ ENL1_printf("qualification succeeded, projecting tuple");
+
+ projInfo = nlstate->jstate.cs_ProjInfo;
+ result = ExecProject(projInfo, &isDone);
+ nlstate->jstate.cs_TupFromTlist = !isDone;
+ return result;
+ }
+
+ /* ----------------
+ * qualification failed so we have to try again..
+ * ----------------
+ */
+ ENL1_printf("qualification failed, looping");
+ }
+}
+
+/* ----------------------------------------------------------------
+ * ExecInitNestLoop
+ *
+ * Creates the run-time state information for the nestloop node
+ * produced by the planner and initailizes inner and outer relations
+ * (child nodes).
+ * ----------------------------------------------------------------
+ */
+bool
+ExecInitNestLoop(NestLoop *node, EState *estate, Plan *parent)
+{
+ NestLoopState *nlstate;
+
+ NL1_printf("ExecInitNestLoop: %s\n",
+ "initializing node");
+
+ /* ----------------
+ * assign execution state to node
+ * ----------------
+ */
+ node->join.state = estate;
+
+ /* ----------------
+ * create new nest loop state
+ * ----------------
+ */
+ nlstate = makeNode(NestLoopState);
+ nlstate->nl_PortalFlag = false;
+ node->nlstate = nlstate;
+
+ /* ----------------
+ * Miscellanious initialization
+ *
+ * + assign node's base_id
+ * + assign debugging hooks and
+ * + create expression context for node
+ * ----------------
+ */
+ ExecAssignNodeBaseInfo(estate, &nlstate->jstate, parent);
+ ExecAssignExprContext(estate, &nlstate->jstate);
+
+#define NESTLOOP_NSLOTS 1
+ /* ----------------
+ * tuple table initialization
+ * ----------------
+ */
+ ExecInitResultTupleSlot(estate, &nlstate->jstate);
+
+ /* ----------------
+ * now initialize children
+ * ----------------
+ */
+ ExecInitNode(outerPlan((Plan*)node), estate, (Plan*)node);
+ ExecInitNode(innerPlan((Plan*)node), estate, (Plan*)node);
+
+ /* ----------------
+ * initialize tuple type and projection info
+ * ----------------
+ */
+ ExecAssignResultTypeFromTL((Plan *) node, &nlstate->jstate);
+ ExecAssignProjectionInfo((Plan *) node, &nlstate->jstate);
+
+ /* ----------------
+ * finally, wipe the current outer tuple clean.
+ * ----------------
+ */
+ nlstate->jstate.cs_OuterTupleSlot = NULL;
+ nlstate->jstate.cs_TupFromTlist = false;
+
+ NL1_printf("ExecInitNestLoop: %s\n",
+ "node initialized");
+ return TRUE;
+}
+
+int
+ExecCountSlotsNestLoop(NestLoop *node)
+{
+ return ExecCountSlotsNode(outerPlan(node)) +
+ ExecCountSlotsNode(innerPlan(node)) +
+ NESTLOOP_NSLOTS;
+}
+
+/* ----------------------------------------------------------------
+ * ExecEndNestLoop
+ *
+ * closes down scans and frees allocated storage
+ * ----------------------------------------------------------------
+ */
+void
+ExecEndNestLoop(NestLoop *node)
+{
+ NestLoopState *nlstate;
+
+ NL1_printf("ExecEndNestLoop: %s\n",
+ "ending node processing");
+
+ /* ----------------
+ * get info from the node
+ * ----------------
+ */
+ nlstate = node->nlstate;
+
+ /* ----------------
+ * Free the projection info
+ *
+ * Note: we don't ExecFreeResultType(nlstate)
+ * because the rule manager depends on the tupType
+ * returned by ExecMain(). So for now, this
+ * is freed at end-transaction time. -cim 6/2/91
+ * ----------------
+ */
+ ExecFreeProjectionInfo(&nlstate->jstate);
+
+ /* ----------------
+ * close down subplans
+ * ----------------
+ */
+ ExecEndNode(outerPlan((Plan *) node), (Plan*)node);
+ ExecEndNode(innerPlan((Plan *) node), (Plan*)node);
+
+ /* ----------------
+ * clean out the tuple table
+ * ----------------
+ */
+ ExecClearTuple(nlstate->jstate.cs_ResultTupleSlot);
+
+ NL1_printf("ExecEndNestLoop: %s\n",
+ "node processing ended");
+}
diff --git a/src/backend/executor/nodeNestloop.h b/src/backend/executor/nodeNestloop.h
new file mode 100644
index 00000000000..c227c90a735
--- /dev/null
+++ b/src/backend/executor/nodeNestloop.h
@@ -0,0 +1,21 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeNestloop.h--
+ *
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id: nodeNestloop.h,v 1.1.1.1 1996/07/09 06:21:27 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef NODENESTLOOP_H
+#define NODENESTLOOP_H
+
+extern TupleTableSlot *ExecNestLoop(NestLoop *node, Plan *parent);
+extern bool ExecInitNestLoop(NestLoop *node, EState *estate, Plan *parent);
+extern int ExecCountSlotsNestLoop(NestLoop *node);
+extern void ExecEndNestLoop(NestLoop *node);
+
+#endif /* NODENESTLOOP_H */
diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c
new file mode 100644
index 00000000000..793119244db
--- /dev/null
+++ b/src/backend/executor/nodeResult.c
@@ -0,0 +1,288 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeResult.c--
+ * support for constant nodes needing special code.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * DESCRIPTION
+ *
+ * Example: in constant queries where no relations are scanned,
+ * the planner generates result nodes. Examples of such queries are:
+ *
+ * retrieve (x = 1)
+ * and
+ * append emp (name = "mike", salary = 15000)
+ *
+ * Result nodes are also used to optimise queries
+ * with tautological qualifications like:
+ *
+ * retrieve (emp.all) where 2 > 1
+ *
+ * In this case, the plan generated is
+ *
+ * Result (with 2 > 1 qual)
+ * /
+ * SeqScan (emp.all)
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeResult.c,v 1.1.1.1 1996/07/09 06:21:27 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "executor/executor.h"
+#include "executor/nodeResult.h"
+
+/* ----------------------------------------------------------------
+ * ExecResult(node)
+ *
+ * returns the tuples from the outer plan which satisify the
+ * qualification clause. Since result nodes with right
+ * subtrees are never planned, we ignore the right subtree
+ * entirely (for now).. -cim 10/7/89
+ *
+ * The qualification containing only constant clauses are
+ * checked first before any processing is done. It always returns
+ * 'nil' if the constant qualification is not satisfied.
+ * ----------------------------------------------------------------
+ */
+TupleTableSlot *
+ExecResult(Result *node)
+{
+ ResultState *resstate;
+ TupleTableSlot *outerTupleSlot;
+ TupleTableSlot *resultSlot;
+ Plan *outerPlan;
+ ExprContext *econtext;
+ Node *qual;
+ bool qualResult;
+ bool isDone;
+ ProjectionInfo *projInfo;
+
+ /* ----------------
+ * initialize the result node's state
+ * ----------------
+ */
+ resstate = node->resstate;
+
+ /* ----------------
+ * get the expression context
+ * ----------------
+ */
+ econtext = resstate->cstate.cs_ExprContext;
+
+ /* ----------------
+ * check tautological qualifications like (2 > 1)
+ * ----------------
+ */
+ qual = node->resconstantqual;
+ if (qual != NULL) {
+ qualResult = ExecQual((List*)qual, econtext);
+ /* ----------------
+ * if we failed the constant qual, then there
+ * is no need to continue processing because regardless of
+ * what happens, the constant qual will be false..
+ * ----------------
+ */
+ if (qualResult == false)
+ return NULL;
+
+ /* ----------------
+ * our constant qualification succeeded so now we
+ * throw away the qual because we know it will always
+ * succeed.
+ * ----------------
+ */
+ node->resconstantqual = NULL;
+ }
+
+ if (resstate->cstate.cs_TupFromTlist) {
+ ProjectionInfo *projInfo;
+
+ projInfo = resstate->cstate.cs_ProjInfo;
+ resultSlot = ExecProject(projInfo, &isDone);
+ if (!isDone)
+ return resultSlot;
+ }
+
+ /* ----------------
+ * retrieve a tuple that satisfy the qual from the outer plan until
+ * there are no more.
+ *
+ * if rs_done is 1 then it means that we were asked to return
+ * a constant tuple and we alread did the last time ExecResult()
+ * was called, so now we are through.
+ * ----------------
+ */
+ outerPlan = outerPlan(node);
+
+ while (!resstate->rs_done) {
+
+ /* ----------------
+ * get next outer tuple if necessary.
+ * ----------------
+ */
+ if (outerPlan != NULL) {
+ outerTupleSlot = ExecProcNode(outerPlan, (Plan*)node);
+
+ if (TupIsNull(outerTupleSlot))
+ return NULL;
+
+ resstate->cstate.cs_OuterTupleSlot = outerTupleSlot;
+ } else {
+
+ /* ----------------
+ * if we don't have an outer plan, then it's probably
+ * the case that we are doing a retrieve or an append
+ * with a constant target list, so we should only return
+ * the constant tuple once or never if we fail the qual.
+ * ----------------
+ */
+ resstate->rs_done = 1;
+ }
+
+ /* ----------------
+ * get the information to place into the expr context
+ * ----------------
+ */
+ resstate = node->resstate;
+
+ outerTupleSlot = resstate->cstate.cs_OuterTupleSlot;
+
+ /* ----------------
+ * fill in the information in the expression context
+ * XXX gross hack. use outer tuple as scan tuple
+ * ----------------
+ */
+ econtext->ecxt_outertuple = outerTupleSlot;
+ econtext->ecxt_scantuple = outerTupleSlot;
+
+ /* ----------------
+ * form the result tuple and pass it back using ExecProject()
+ * ----------------
+ */
+ projInfo = resstate->cstate.cs_ProjInfo;
+ resultSlot = ExecProject(projInfo, &isDone);
+ resstate->cstate.cs_TupFromTlist = !isDone;
+ return resultSlot;
+ }
+
+ return NULL;
+}
+
+/* ----------------------------------------------------------------
+ * ExecInitResult
+ *
+ * Creates the run-time state information for the result node
+ * produced by the planner and initailizes outer relations
+ * (child nodes).
+ * ----------------------------------------------------------------
+ */
+bool
+ExecInitResult(Result *node, EState *estate, Plan *parent)
+{
+ ResultState *resstate;
+
+ /* ----------------
+ * assign execution state to node
+ * ----------------
+ */
+ node->plan.state = estate;
+
+ /* ----------------
+ * create new ResultState for node
+ * ----------------
+ */
+ resstate = makeNode(ResultState);
+ resstate->rs_done = 0;
+ node->resstate = resstate;
+
+ /* ----------------
+ * Miscellanious initialization
+ *
+ * + assign node's base_id
+ * + assign debugging hooks and
+ * + create expression context for node
+ * ----------------
+ */
+ ExecAssignNodeBaseInfo(estate, &resstate->cstate, parent);
+ ExecAssignExprContext(estate, &resstate->cstate);
+
+#define RESULT_NSLOTS 1
+ /* ----------------
+ * tuple table initialization
+ * ----------------
+ */
+ ExecInitResultTupleSlot(estate, &resstate->cstate);
+
+ /* ----------------
+ * then initialize children
+ * ----------------
+ */
+ ExecInitNode(outerPlan(node), estate, (Plan*)node);
+
+ /*
+ * we don't use inner plan
+ */
+ Assert(innerPlan(node)==NULL);
+
+ /* ----------------
+ * initialize tuple type and projection info
+ * ----------------
+ */
+ ExecAssignResultTypeFromTL((Plan*)node, &resstate->cstate);
+ ExecAssignProjectionInfo((Plan*)node, &resstate->cstate);
+
+ /* ----------------
+ * set "are we done yet" to false
+ * ----------------
+ */
+ resstate->rs_done = 0;
+
+ return TRUE;
+}
+
+int
+ExecCountSlotsResult(Result *node)
+{
+ return ExecCountSlotsNode(outerPlan(node)) + RESULT_NSLOTS;
+}
+
+/* ----------------------------------------------------------------
+ * ExecEndResult
+ *
+ * fees up storage allocated through C routines
+ * ----------------------------------------------------------------
+ */
+void
+ExecEndResult(Result *node)
+{
+ ResultState *resstate;
+
+ resstate = node->resstate;
+
+ /* ----------------
+ * Free the projection info
+ *
+ * Note: we don't ExecFreeResultType(resstate)
+ * because the rule manager depends on the tupType
+ * returned by ExecMain(). So for now, this
+ * is freed at end-transaction time. -cim 6/2/91
+ * ----------------
+ */
+ ExecFreeProjectionInfo(&resstate->cstate);
+
+ /* ----------------
+ * shut down subplans
+ * ----------------
+ */
+ ExecEndNode(outerPlan(node), (Plan*)node);
+
+ /* ----------------
+ * clean out the tuple table
+ * ----------------
+ */
+ ExecClearTuple(resstate->cstate.cs_ResultTupleSlot);
+}
diff --git a/src/backend/executor/nodeResult.h b/src/backend/executor/nodeResult.h
new file mode 100644
index 00000000000..a2ab286c089
--- /dev/null
+++ b/src/backend/executor/nodeResult.h
@@ -0,0 +1,21 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeResult.h--
+ *
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id: nodeResult.h,v 1.1.1.1 1996/07/09 06:21:27 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef NODERESULT_H
+#define NODERESULT_H
+
+extern TupleTableSlot *ExecResult(Result *node);
+extern bool ExecInitResult(Result *node, EState *estate, Plan *parent);
+extern int ExecCountSlotsResult(Result *node);
+extern void ExecEndResult(Result *node);
+
+#endif /* NODERESULT_H */
diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c
new file mode 100644
index 00000000000..a0e434c4bb0
--- /dev/null
+++ b/src/backend/executor/nodeSeqscan.c
@@ -0,0 +1,449 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeSeqscan.c--
+ * Support routines for sequential scans of relations.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeSeqscan.c,v 1.1.1.1 1996/07/09 06:21:27 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+ * INTERFACE ROUTINES
+ * ExecSeqScan sequentially scans a relation.
+ * ExecSeqNext retrieve next tuple in sequential order.
+ * ExecInitSeqScan creates and initializes a seqscan node.
+ * ExecEndSeqScan releases any storage allocated.
+ * ExecSeqReScan rescans the relation
+ * ExecMarkPos marks scan position
+ * ExecRestrPos restores scan position
+ *
+ */
+#include "executor/executor.h"
+#include "executor/nodeSeqscan.h"
+#include "parser/parsetree.h"
+
+/* ----------------------------------------------------------------
+ * Scan Support
+ * ----------------------------------------------------------------
+ */
+/* ----------------------------------------------------------------
+ * SeqNext
+ *
+ * This is a workhorse for ExecSeqScan
+ * ----------------------------------------------------------------
+ */
+TupleTableSlot *
+SeqNext(SeqScan *node)
+{
+ HeapTuple tuple;
+ HeapScanDesc scandesc;
+ CommonScanState *scanstate;
+ EState *estate;
+ ScanDirection direction;
+ TupleTableSlot *slot;
+ Buffer buffer;
+
+ /* ----------------
+ * get information from the estate and scan state
+ * ----------------
+ */
+ estate = node->plan.state;
+ scanstate = node->scanstate;
+ scandesc = scanstate->css_currentScanDesc;
+ direction = estate->es_direction;
+
+ /* ----------------
+ * get the next tuple from the access methods
+ * ----------------
+ */
+ tuple = heap_getnext(scandesc, /* scan desc */
+ ScanDirectionIsBackward(direction), /*backward flag*/
+ &buffer); /* return: buffer */
+
+ /* ----------------
+ * save the tuple and the buffer returned to us by the access methods
+ * in our scan tuple slot and return the slot. Note: we pass 'false'
+ * because tuples returned by heap_getnext() are pointers onto
+ * disk pages and were not created with palloc() and so should not
+ * be pfree()'d.
+ * ----------------
+ */
+ slot = scanstate->css_ScanTupleSlot;
+
+ slot = ExecStoreTuple(tuple, /* tuple to store */
+ slot, /* slot to store in */
+ buffer, /* buffer associated with this tuple */
+ false); /* don't pfree this pointer */
+
+ /* ----------------
+ * XXX -- mao says: The sequential scan for heap relations will
+ * automatically unpin the buffer this tuple is on when we cross
+ * a page boundary. The clearslot code also does this. We bump
+ * the pin count on the page here, since we actually have two
+ * pointers to it -- one in the scan desc and one in the tuple
+ * table slot. --mar 20 91
+ * ----------------
+ */
+ ExecIncrSlotBufferRefcnt(slot);
+
+ return slot;
+}
+
+/* ----------------------------------------------------------------
+ * ExecSeqScan(node)
+ *
+ * Scans the relation sequentially and returns the next qualifying
+ * tuple.
+ * It calls the ExecScan() routine and passes it the access method
+ * which retrieve tuples sequentially.
+ *
+ */
+
+TupleTableSlot *
+ExecSeqScan(SeqScan *node)
+{
+ TupleTableSlot *slot;
+ Plan *outerPlan;
+
+S_printf("ExecSeqScan: scanning node: "); S_nodeDisplay(node);
+
+ /* ----------------
+ * if there is an outer subplan, get a tuple from it
+ * else, scan the relation
+ * ----------------
+ */
+ outerPlan = outerPlan((Plan *) node);
+ if (outerPlan) {
+ slot = ExecProcNode(outerPlan, (Plan*) node);
+ } else {
+ slot = ExecScan(node, SeqNext);
+ }
+
+S1_printf("ExecSeqScan: returned tuple slot: %d\n", slot);
+
+ return slot;
+}
+
+/* ----------------------------------------------------------------
+ * InitScanRelation
+ *
+ * This does the initialization for scan relations and
+ * subplans of scans.
+ * ----------------------------------------------------------------
+ */
+Oid
+InitScanRelation(SeqScan *node, EState *estate,
+ CommonScanState *scanstate, Plan *outerPlan)
+{
+ Index relid;
+ List *rangeTable;
+ RangeTblEntry *rtentry;
+ Oid reloid;
+ TimeQual timeQual;
+ ScanDirection direction;
+ Relation currentRelation;
+ HeapScanDesc currentScanDesc;
+ RelationInfo *resultRelationInfo;
+
+ if (outerPlan == NULL) {
+ /* ----------------
+ * if the outer node is nil then we are doing a simple
+ * sequential scan of a relation...
+ *
+ * get the relation object id from the relid'th entry
+ * in the range table, open that relation and initialize
+ * the scan state...
+ * ----------------
+ */
+ relid = node->scanrelid;
+ rangeTable = estate->es_range_table;
+ rtentry = rt_fetch(relid, rangeTable);
+ reloid = rtentry->relid;
+ timeQual = rtentry->timeQual;
+ direction = estate->es_direction;
+ resultRelationInfo = estate->es_result_relation_info;
+
+ ExecOpenScanR(reloid, /* relation */
+ 0, /* nkeys */
+ NULL, /* scan key */
+ 0, /* is index */
+ direction, /* scan direction */
+ timeQual, /* time qual */
+ &currentRelation, /* return: rel desc */
+ (Pointer *) &currentScanDesc); /* return: scan desc */
+
+ scanstate->css_currentRelation = currentRelation;
+ scanstate->css_currentScanDesc = currentScanDesc;
+
+ ExecAssignScanType(scanstate,
+ RelationGetTupleDescriptor(currentRelation));
+ } else {
+ /* ----------------
+ * otherwise we are scanning tuples from the
+ * outer subplan so we initialize the outer plan
+ * and nullify
+ * ----------------
+ */
+ ExecInitNode(outerPlan, estate, (Plan*)node);
+
+ node->scanrelid = 0;
+ scanstate->css_currentRelation = NULL;
+ scanstate->css_currentScanDesc = NULL;
+ ExecAssignScanType(scanstate, NULL);
+ reloid = InvalidOid;
+ }
+
+ /* ----------------
+ * return the relation
+ * ----------------
+ */
+ return reloid;
+}
+
+
+/* ----------------------------------------------------------------
+ * ExecInitSeqScan
+ *
+ * old comments
+ * Creates the run-time state information for the seqscan node
+ * and sets the relation id to contain relevant descriptors.
+ *
+ * If there is a outer subtree (sort), the outer subtree
+ * is initialized and the relation id is set to the descriptors
+ * returned by the subtree.
+ * ----------------------------------------------------------------
+ */
+bool
+ExecInitSeqScan(SeqScan *node, EState *estate, Plan *parent)
+{
+ CommonScanState *scanstate;
+ Plan *outerPlan;
+ Oid reloid;
+ HeapScanDesc scandesc;
+
+ /* ----------------
+ * assign the node's execution state
+ * ----------------
+ */
+ node->plan.state = estate;
+
+ /* ----------------
+ * create new CommonScanState for node
+ * ----------------
+ */
+ scanstate = makeNode(CommonScanState);
+ node->scanstate = scanstate;
+
+ /* ----------------
+ * Miscellanious initialization
+ *
+ * + assign node's base_id
+ * + create expression context for node
+ * ----------------
+ */
+ ExecAssignNodeBaseInfo(estate, &scanstate->cstate, parent);
+ ExecAssignExprContext(estate, &scanstate->cstate);
+
+#define SEQSCAN_NSLOTS 3
+ /* ----------------
+ * tuple table initialization
+ * ----------------
+ */
+ ExecInitResultTupleSlot(estate, &scanstate->cstate);
+ ExecInitScanTupleSlot(estate, scanstate);
+
+ /* ----------------
+ * initialize scan relation or outer subplan
+ * ----------------
+ */
+ outerPlan = outerPlan((Plan *)node);
+
+ reloid = InitScanRelation(node, estate, scanstate, outerPlan);
+
+ scandesc = scanstate->css_currentScanDesc;
+ scanstate->cstate.cs_TupFromTlist = false;
+
+ /* ----------------
+ * initialize tuple type
+ * ----------------
+ */
+ ExecAssignResultTypeFromTL((Plan*)node, &scanstate->cstate);
+ ExecAssignProjectionInfo((Plan*)node, &scanstate->cstate);
+
+ return TRUE;
+}
+
+int
+ExecCountSlotsSeqScan(SeqScan *node)
+{
+ return ExecCountSlotsNode(outerPlan(node)) +
+ ExecCountSlotsNode(innerPlan(node)) +
+ SEQSCAN_NSLOTS;
+}
+
+/* ----------------------------------------------------------------
+ * ExecEndSeqScan
+ *
+ * frees any storage allocated through C routines.
+ *| ...and also closes relations and/or shuts down outer subplan
+ *| -cim 8/14/89
+ * ----------------------------------------------------------------
+ */
+void
+ExecEndSeqScan(SeqScan *node)
+{
+ CommonScanState *scanstate;
+ Plan *outerPlan;
+
+ /* ----------------
+ * get information from node
+ * ----------------
+ */
+ scanstate = node->scanstate;
+
+ /* ----------------
+ * Free the projection info and the scan attribute info
+ *
+ * Note: we don't ExecFreeResultType(scanstate)
+ * because the rule manager depends on the tupType
+ * returned by ExecMain(). So for now, this
+ * is freed at end-transaction time. -cim 6/2/91
+ * ----------------
+ */
+ ExecFreeProjectionInfo(&scanstate->cstate);
+
+ /* ----------------
+ * close scan relation
+ * ----------------
+ */
+ ExecCloseR((Plan*) node);
+
+ /* ----------------
+ * clean up outer subtree (does nothing if there is no outerPlan)
+ * ----------------
+ */
+ outerPlan = outerPlan((Plan *)node);
+ ExecEndNode(outerPlan, (Plan*)node);
+
+ /* ----------------
+ * clean out the tuple table
+ * ----------------
+ */
+ ExecClearTuple(scanstate->cstate.cs_ResultTupleSlot);
+ ExecClearTuple(scanstate->css_ScanTupleSlot);
+}
+
+/* ----------------------------------------------------------------
+ * Join Support
+ * ----------------------------------------------------------------
+ */
+/* ----------------------------------------------------------------
+ * ExecSeqReScan
+ *
+ * Rescans the relation.
+ * ----------------------------------------------------------------
+ */
+void
+ExecSeqReScan(SeqScan *node, ExprContext *exprCtxt, Plan* parent)
+{
+ CommonScanState *scanstate;
+ EState *estate;
+ Plan *outerPlan;
+ Relation rdesc;
+ HeapScanDesc sdesc;
+ ScanDirection direction;
+
+ scanstate = node->scanstate;
+ estate = node->plan.state;
+
+ outerPlan = outerPlan((Plan*)node);
+ if (outerPlan) {
+ /* we are scanning a subplan */
+ outerPlan = outerPlan((Plan *)node);
+ ExecReScan(outerPlan, exprCtxt, parent);
+ } else {
+ /* otherwise, we are scanning a relation */
+ rdesc = scanstate->css_currentRelation;
+ sdesc = scanstate->css_currentScanDesc;
+ direction = estate->es_direction;
+ sdesc = ExecReScanR(rdesc, sdesc, direction, 0, NULL);
+ scanstate->css_currentScanDesc = sdesc;
+ }
+}
+
+/* ----------------------------------------------------------------
+ * ExecSeqMarkPos(node)
+ *
+ * Marks scan position.
+ * ----------------------------------------------------------------
+ */
+void
+ExecSeqMarkPos(SeqScan *node)
+{
+ CommonScanState *scanstate;
+ Plan *outerPlan;
+ HeapScanDesc sdesc;
+
+ scanstate = node->scanstate;
+
+ /* ----------------
+ * if we are scanning a subplan then propagate
+ * the ExecMarkPos() request to the subplan
+ * ----------------
+ */
+ outerPlan = outerPlan((Plan*)node);
+ if (outerPlan) {
+ ExecMarkPos(outerPlan);
+ return;
+ }
+
+ /* ----------------
+ * otherwise we are scanning a relation so mark the
+ * position using the access methods..
+ *
+ * ----------------
+ */
+ sdesc = scanstate->css_currentScanDesc;
+ heap_markpos(sdesc);
+
+ return;
+}
+
+/* ----------------------------------------------------------------
+ * ExecSeqRestrPos
+ *
+ * Restores scan position.
+ * ----------------------------------------------------------------
+ */
+void
+ExecSeqRestrPos(SeqScan *node)
+{
+ CommonScanState *scanstate;
+ Plan *outerPlan;
+ HeapScanDesc sdesc;
+
+ scanstate = node->scanstate;
+
+ /* ----------------
+ * if we are scanning a subplan then propagate
+ * the ExecRestrPos() request to the subplan
+ * ----------------
+ */
+ outerPlan = outerPlan((Plan*)node);
+ if (outerPlan) {
+ ExecRestrPos(outerPlan);
+ return;
+ }
+
+ /* ----------------
+ * otherwise we are scanning a relation so restore the
+ * position using the access methods..
+ * ----------------
+ */
+ sdesc = scanstate->css_currentScanDesc;
+ heap_restrpos(sdesc);
+}
diff --git a/src/backend/executor/nodeSeqscan.h b/src/backend/executor/nodeSeqscan.h
new file mode 100644
index 00000000000..cce029d40b7
--- /dev/null
+++ b/src/backend/executor/nodeSeqscan.h
@@ -0,0 +1,27 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeSeqscan.h--
+ *
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id: nodeSeqscan.h,v 1.1.1.1 1996/07/09 06:21:27 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef NODESEQSCAN_H
+#define NODESEQSCAN_H
+
+extern TupleTableSlot *SeqNext(SeqScan *node);
+extern TupleTableSlot *ExecSeqScan(SeqScan *node);
+extern Oid InitScanRelation(SeqScan *node, EState *estate,
+ CommonScanState *scanstate, Plan *outerPlan);
+extern bool ExecInitSeqScan(SeqScan *node, EState *estate, Plan *parent);
+extern int ExecCountSlotsSeqScan(SeqScan *node);
+extern void ExecEndSeqScan(SeqScan *node);
+extern void ExecSeqReScan(SeqScan *node, ExprContext *exprCtxt, Plan* parent);
+extern void ExecSeqMarkPos(SeqScan *node);
+extern void ExecSeqRestrPos(SeqScan *node);
+
+#endif /* NODESEQSCAN_H */
diff --git a/src/backend/executor/nodeSort.c b/src/backend/executor/nodeSort.c
new file mode 100644
index 00000000000..9a7f4f9456f
--- /dev/null
+++ b/src/backend/executor/nodeSort.c
@@ -0,0 +1,523 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeSort.c--
+ * Routines to handle sorting of relations into temporaries.
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeSort.c,v 1.1.1.1 1996/07/09 06:21:27 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "executor/executor.h"
+#include "executor/nodeSort.h"
+#include "utils/palloc.h"
+#include "utils/psort.h"
+#include "catalog/catalog.h"
+#include "storage/bufmgr.h"
+#include "optimizer/internal.h" /* for _TEMP_RELATION_ID_ */
+
+/* ----------------------------------------------------------------
+ * FormSortKeys(node)
+ *
+ * Forms the structure containing information used to sort the relation.
+ *
+ * Returns an array of ScanKeyData.
+ * ----------------------------------------------------------------
+ */
+static ScanKey
+FormSortKeys(Sort *sortnode)
+{
+ ScanKey sortkeys;
+ List *targetList;
+ List *tl;
+ int keycount;
+ Resdom *resdom;
+ AttrNumber resno;
+ Index reskey;
+ Oid reskeyop;
+
+ /* ----------------
+ * get information from the node
+ * ----------------
+ */
+ targetList = sortnode->plan.targetlist;
+ keycount = sortnode->keycount;
+
+ /* ----------------
+ * first allocate space for scan keys
+ * ----------------
+ */
+ if (keycount <= 0)
+ elog(WARN, "FormSortKeys: keycount <= 0");
+ sortkeys = (ScanKey) palloc(keycount * sizeof(ScanKeyData));
+
+ /* ----------------
+ * form each scan key from the resdom info in the target list
+ * ----------------
+ */
+ foreach(tl, targetList) {
+ TargetEntry *target = (TargetEntry *)lfirst(tl);
+ resdom = target->resdom;
+ resno = resdom->resno;
+ reskey = resdom->reskey;
+ reskeyop = resdom->reskeyop;
+
+ if (reskey > 0) {
+ ScanKeyEntryInitialize(&sortkeys[reskey-1],
+ 0,
+ resno,
+ (RegProcedure) DatumGetInt32(reskeyop),
+ (Datum) 0);
+ }
+ }
+
+ return sortkeys;
+}
+
+/* ----------------------------------------------------------------
+ * ExecSort
+ *
+ * old comments
+ * Retrieves tuples fron the outer subtree and insert them into a
+ * temporary relation. The temporary relation is then sorted and
+ * the sorted relation is stored in the relation whose ID is indicated
+ * in the 'tempid' field of this node.
+ * Assumes that heap access method is used.
+ *
+ * Conditions:
+ * -- none.
+ *
+ * Initial States:
+ * -- the outer child is prepared to return the first tuple.
+ * ----------------------------------------------------------------
+ */
+TupleTableSlot *
+ExecSort(Sort *node)
+{
+ EState *estate;
+ SortState *sortstate;
+ Plan *outerNode;
+ ScanDirection dir;
+ int keycount;
+ ScanKey sortkeys;
+ Relation tempRelation;
+ Relation currentRelation;
+ HeapScanDesc currentScanDesc;
+ HeapTuple heapTuple;
+ TupleTableSlot *slot;
+ Buffer buffer;
+ int tupCount = 0;
+
+ /* ----------------
+ * get state info from node
+ * ----------------
+ */
+ SO1_printf("ExecSort: %s\n",
+ "entering routine");
+
+ sortstate = node->sortstate;
+ estate = node->plan.state;
+ dir = estate->es_direction;
+
+ /* ----------------
+ * the first time we call this, we retrieve all tuples
+ * from the subplan into a temporary relation and then
+ * we sort the relation. Subsequent calls return tuples
+ * from the temporary relation.
+ * ----------------
+ */
+
+ if (sortstate->sort_Flag == false) {
+ SO1_printf("ExecSort: %s\n",
+ "sortstate == false -> sorting subplan");
+ /* ----------------
+ * set all relations to be scanned in the forward direction
+ * while creating the temporary relation.
+ * ----------------
+ */
+ estate->es_direction = EXEC_FRWD;
+
+ /* ----------------
+ * if we couldn't create the temp or current relations then
+ * we print a warning and return NULL.
+ * ----------------
+ */
+ tempRelation = sortstate->sort_TempRelation;
+ if (tempRelation == NULL) {
+ elog(DEBUG, "ExecSort: temp relation is NULL! aborting...");
+ return NULL;
+ }
+
+ currentRelation = sortstate->csstate.css_currentRelation;
+ if (currentRelation == NULL) {
+ elog(DEBUG, "ExecSort: current relation is NULL! aborting...");
+ return NULL;
+ }
+
+ /* ----------------
+ * retrieve tuples from the subplan and
+ * insert them in the temporary relation
+ * ----------------
+ */
+ outerNode = outerPlan((Plan *) node);
+ SO1_printf("ExecSort: %s\n",
+ "inserting tuples into tempRelation");
+
+ for (;;) {
+ slot = ExecProcNode(outerNode, (Plan*)node);
+
+ if (TupIsNull(slot))
+ break;
+
+ tupCount++;
+
+ heapTuple = slot->val;
+
+ heap_insert(tempRelation, /* relation desc */
+ heapTuple); /* heap tuple to insert */
+
+ ExecClearTuple(slot);
+ }
+
+ /* ----------------
+ * now sort the tuples in our temporary relation
+ * into a new sorted relation using psort()
+ *
+ * psort() seems to require that the relations
+ * are created and opened in advance.
+ * -cim 1/25/90
+ * ----------------
+ */
+ keycount = node->keycount;
+ sortkeys = (ScanKey)sortstate->sort_Keys;
+ SO1_printf("ExecSort: %s\n",
+ "calling psort");
+
+ /*
+ * If no tuples were fetched from the proc node return NULL now
+ * psort dumps it if 0 tuples are in the relation and I don't want
+ * to try to debug *that* routine!!
+ */
+ if (tupCount == 0)
+ return NULL;
+
+ psort(tempRelation, /* old relation */
+ currentRelation, /* new relation */
+ keycount, /* number keys */
+ sortkeys); /* keys */
+
+ if (currentRelation == NULL) {
+ elog(DEBUG, "ExecSort: sorted relation is NULL! aborting...");
+ return NULL;
+ }
+
+ /* ----------------
+ * restore to user specified direction
+ * ----------------
+ */
+ estate->es_direction = dir;
+
+ /* ----------------
+ * now initialize the scan descriptor to scan the
+ * sorted relation and update the sortstate information
+ * ----------------
+ */
+ currentScanDesc = heap_beginscan(currentRelation, /* relation */
+ ScanDirectionIsBackward(dir),
+ /* bkwd flag */
+ NowTimeQual, /* time qual */
+ 0, /* num scan keys */
+ NULL); /* scan keys */
+
+ sortstate->csstate.css_currentRelation = currentRelation;
+ sortstate->csstate.css_currentScanDesc = currentScanDesc;
+
+ /* ----------------
+ * make sure the tuple descriptor is up to date
+ * ----------------
+ */
+ slot = sortstate->csstate.css_ScanTupleSlot;
+
+ slot->ttc_tupleDescriptor =
+ RelationGetTupleDescriptor(currentRelation);
+
+ /* ----------------
+ * finally set the sorted flag to true
+ * ----------------
+ */
+ sortstate->sort_Flag = true;
+ }
+ else {
+ slot = sortstate->csstate.css_ScanTupleSlot;
+ }
+
+ SO1_printf("ExecSort: %s\n",
+ "retrieveing tuple from sorted relation");
+
+ /* ----------------
+ * at this point we know we have a sorted relation so
+ * we preform a simple scan on it with amgetnext()..
+ * ----------------
+ */
+ currentScanDesc = sortstate->csstate.css_currentScanDesc;
+
+ heapTuple = heap_getnext(currentScanDesc, /* scan desc */
+ ScanDirectionIsBackward(dir),
+ /* bkwd flag */
+ &buffer); /* return: 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.
+ - 01/09/93 */
+
+ if (buffer != InvalidBuffer)
+ IncrBufferRefCount(buffer);
+
+ return ExecStoreTuple(heapTuple, /* tuple to store */
+ slot, /* slot to store in */
+ buffer, /* this tuple's buffer */
+ false); /* don't free stuff from amgetnext */
+}
+
+/* ----------------------------------------------------------------
+ * ExecInitSort
+ *
+ * old comments
+ * Creates the run-time state information for the sort node
+ * produced by the planner and initailizes its outer subtree.
+ * ----------------------------------------------------------------
+ */
+bool
+ExecInitSort(Sort *node, EState *estate, Plan *parent)
+{
+ SortState *sortstate;
+ Plan *outerPlan;
+ ScanKey sortkeys;
+ TupleDesc tupType;
+ Oid tempOid;
+ Oid sortOid;
+ Relation tempDesc;
+ Relation sortedDesc;
+
+ SO1_printf("ExecInitSort: %s\n",
+ "initializing sort node");
+
+ /* ----------------
+ * assign the node's execution state
+ * ----------------
+ */
+ node->plan.state = estate;
+
+ /* ----------------
+ * create state structure
+ * ----------------
+ */
+ sortstate = makeNode(SortState);
+ sortstate->sort_Flag = 0;
+ sortstate->sort_Keys = NULL;
+ sortstate->sort_TempRelation = NULL;
+
+ node->sortstate = sortstate;
+
+ /* ----------------
+ * Miscellanious initialization
+ *
+ * + assign node's base_id
+ * + assign debugging hooks
+ *
+ * Sort nodes don't initialize their ExprContexts because
+ * they never call ExecQual or ExecTargetList.
+ * ----------------
+ */
+ ExecAssignNodeBaseInfo(estate, &sortstate->csstate.cstate, parent);
+
+#define SORT_NSLOTS 1
+ /* ----------------
+ * tuple table initialization
+ *
+ * sort nodes only return scan tuples from their sorted
+ * relation.
+ * ----------------
+ */
+ ExecInitScanTupleSlot(estate, &sortstate->csstate);
+ ExecInitResultTupleSlot(estate, &sortstate->csstate.cstate);
+
+ /* ----------------
+ * initializes child nodes
+ * ----------------
+ */
+ outerPlan = outerPlan((Plan *) node);
+ ExecInitNode(outerPlan, estate, (Plan *) node);
+
+ /* ----------------
+ * initialize sortstate information
+ * ----------------
+ */
+ sortkeys = FormSortKeys(node);
+ sortstate->sort_Keys = sortkeys;
+ sortstate->sort_Flag = false;
+
+ /* ----------------
+ * initialize tuple type. no need to initialize projection
+ * info because this node doesn't do projections.
+ * ----------------
+ */
+ ExecAssignScanTypeFromOuterPlan((Plan *) node, &sortstate->csstate);
+ sortstate->csstate.cstate.cs_ProjInfo = NULL;
+
+ /* ----------------
+ * get type information needed for ExecCreatR
+ * ----------------
+ */
+ tupType = ExecGetScanType(&sortstate->csstate);
+
+ /* ----------------
+ * ExecCreatR wants its second argument to be an object id of
+ * a relation in the range table or _TEMP_RELATION_ID_
+ * indicating that the relation is not in the range table.
+ *
+ * In the second case ExecCreatR creates a temp relation.
+ * (currently this is the only case we support -cim 10/16/89)
+ * ----------------
+ */
+ tempOid = node->tempid;
+ sortOid = _TEMP_RELATION_ID_;
+
+ /* ----------------
+ * create the temporary relations
+ * ----------------
+ */
+/* len = ExecTargetListLength(node->plan.targetlist); */
+ tempDesc = ExecCreatR(tupType, tempOid);
+ sortedDesc = ExecCreatR(tupType, sortOid);
+
+ /* ----------------
+ * save the relation descriptor in the sortstate
+ * ----------------
+ */
+ sortstate->sort_TempRelation = tempDesc;
+ sortstate->csstate.css_currentRelation = sortedDesc;
+ SO1_printf("ExecInitSort: %s\n",
+ "sort node initialized");
+
+ /* ----------------
+ * return relation oid of temporary sort relation in a list
+ * (someday -- for now we return LispTrue... cim 10/12/89)
+ * ----------------
+ */
+ return TRUE;
+}
+
+int
+ExecCountSlotsSort(Sort *node)
+{
+ return ExecCountSlotsNode(outerPlan((Plan *)node)) +
+ ExecCountSlotsNode(innerPlan((Plan *)node)) +
+ SORT_NSLOTS;
+}
+
+/* ----------------------------------------------------------------
+ * ExecEndSort(node)
+ *
+ * old comments
+ * destroys the temporary relation.
+ * ----------------------------------------------------------------
+ */
+void
+ExecEndSort(Sort *node)
+{
+ SortState *sortstate;
+ Relation tempRelation;
+ Relation sortedRelation;
+ Plan *outerPlan;
+
+ /* ----------------
+ * get info from the sort state
+ * ----------------
+ */
+ SO1_printf("ExecEndSort: %s\n",
+ "shutting down sort node");
+
+ sortstate = node->sortstate;
+ tempRelation = sortstate->sort_TempRelation;
+ sortedRelation = sortstate->csstate.css_currentRelation;
+
+ heap_destroyr(tempRelation);
+ heap_destroyr(sortedRelation);
+
+
+ /* ----------------
+ * close the sorted relation and shut down the scan.
+ * ----------------
+ */
+ ExecCloseR((Plan *) node);
+
+ /* ----------------
+ * shut down the subplan
+ * ----------------
+ */
+ outerPlan = outerPlan((Plan *) node);
+ ExecEndNode(outerPlan, (Plan*)node);
+
+ /* ----------------
+ * clean out the tuple table
+ * ----------------
+ */
+ ExecClearTuple(sortstate->csstate.css_ScanTupleSlot);
+
+ SO1_printf("ExecEndSort: %s\n",
+ "sort node shutdown");
+}
+
+/* ----------------------------------------------------------------
+ * ExecSortMarkPos
+ * ----------------------------------------------------------------
+ */
+void
+ExecSortMarkPos(Sort *node)
+{
+ SortState *sortstate;
+ HeapScanDesc sdesc;
+
+ /* ----------------
+ * if we haven't sorted yet, just return
+ * ----------------
+ */
+ sortstate = node->sortstate;
+ if (sortstate->sort_Flag == false)
+ return;
+
+ sdesc = sortstate->csstate.css_currentScanDesc;
+ heap_markpos(sdesc);
+ return;
+}
+
+/* ----------------------------------------------------------------
+ * ExecSortRestrPos
+ * ----------------------------------------------------------------
+ */
+void
+ExecSortRestrPos(Sort *node)
+{
+ SortState *sortstate;
+ HeapScanDesc sdesc;
+
+ /* ----------------
+ * if we haven't sorted yet, just return.
+ * ----------------
+ */
+ sortstate = node->sortstate;
+ if (sortstate->sort_Flag == false)
+ return;
+
+ /* ----------------
+ * restore the scan to the previously marked position
+ * ----------------
+ */
+ sdesc = sortstate->csstate.css_currentScanDesc;
+ heap_restrpos(sdesc);
+}
diff --git a/src/backend/executor/nodeSort.h b/src/backend/executor/nodeSort.h
new file mode 100644
index 00000000000..504b8a1f19e
--- /dev/null
+++ b/src/backend/executor/nodeSort.h
@@ -0,0 +1,23 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeSort.h--
+ *
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id: nodeSort.h,v 1.1.1.1 1996/07/09 06:21:27 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef NODESORT_H
+#define NODESORT_H
+
+extern TupleTableSlot *ExecSort(Sort *node);
+extern bool ExecInitSort(Sort *node, EState *estate, Plan *parent);
+extern int ExecCountSlotsSort(Sort *node);
+extern void ExecEndSort(Sort *node);
+extern void ExecSortMarkPos(Sort *node);
+extern void ExecSortRestrPos(Sort *node);
+
+#endif /* NODESORT_H */
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;
+ }
+ }
+ }
+
+}
+
diff --git a/src/backend/executor/nodeTee.h b/src/backend/executor/nodeTee.h
new file mode 100644
index 00000000000..aa50efdead4
--- /dev/null
+++ b/src/backend/executor/nodeTee.h
@@ -0,0 +1,22 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeTee.h--
+ * support functions for a Tee executor node
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id: nodeTee.h,v 1.1.1.1 1996/07/09 06:21:27 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef NODETEE_H
+#define NODETEE_H
+
+extern TupleTableSlot* ExecTee(Tee* node, Plan* parent);
+extern bool ExecInitTee(Tee* node, EState* estate, Plan* parent);
+extern void ExecTeeReScan(Tee *node, ExprContext *exprCtxt, Plan *parent);
+extern void ExecEndTee(Tee* node, Plan* parent);
+extern int ExecCountSlotsTee(Tee* node);
+
+#endif /* NODETEE_H */
diff --git a/src/backend/executor/nodeUnique.c b/src/backend/executor/nodeUnique.c
new file mode 100644
index 00000000000..8be0bd8497c
--- /dev/null
+++ b/src/backend/executor/nodeUnique.c
@@ -0,0 +1,316 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeUnique.c--
+ * Routines to handle unique'ing of queries where appropriate
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeUnique.c,v 1.1.1.1 1996/07/09 06:21:27 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+ * INTERFACE ROUTINES
+ * ExecUnique - generate a unique'd temporary relation
+ * ExecInitUnique - initialize node and subnodes..
+ * ExecEndUnique - shutdown node and subnodes
+ *
+ * NOTES
+ * Assumes tuples returned from subplan arrive in
+ * sorted order.
+ *
+ */
+#include "executor/executor.h"
+#include "executor/nodeUnique.h"
+#include "optimizer/clauses.h"
+#include "access/printtup.h" /* for typtoout() */
+#include "utils/builtins.h" /* for namecpy()*/
+
+/* ----------------------------------------------------------------
+ * ExecIdenticalTuples
+ *
+ * This is a hack function used by ExecUnique to see if
+ * two tuples are identical. This should be provided
+ * by the heap tuple code but isn't. The real problem
+ * is that we assume we can byte compare tuples to determine
+ * if they are "equal". In fact, if we have user defined
+ * types there may be problems because it's possible that
+ * an ADT may have multiple representations with the
+ * same ADT value. -cim
+ * ----------------------------------------------------------------
+ */
+static bool /* true if tuples are identical, false otherwise */
+ExecIdenticalTuples(TupleTableSlot *t1, TupleTableSlot *t2)
+{
+ HeapTuple h1;
+ HeapTuple h2;
+ char *d1;
+ char *d2;
+ int len;
+
+ h1 = t1->val;
+ h2 = t2->val;
+
+ /* ----------------
+ * if tuples aren't the same length then they are
+ * obviously different (one may have null attributes).
+ * ----------------
+ */
+ if (h1->t_len != h2->t_len)
+ return false;
+
+ /* ----------------
+ * if the tuples have different header offsets then
+ * they are different. This will prevent us from returning
+ * true when comparing tuples of one attribute where one of
+ * two we're looking at is null (t_len - t_hoff == 0).
+ * THE t_len FIELDS CAN BE THE SAME IN THIS CASE!!
+ * ----------------
+ */
+ if (h1->t_hoff != h2->t_hoff)
+ return false;
+
+ /* ----------------
+ * ok, now get the pointers to the data and the
+ * size of the attribute portion of the tuple.
+ * ----------------
+ */
+ d1 = (char *) GETSTRUCT(h1);
+ d2 = (char *) GETSTRUCT(h2);
+ len = (int) h1->t_len - (int) h1->t_hoff;
+
+ /* ----------------
+ * byte compare the data areas and return the result.
+ * ----------------
+ */
+ if (memcmp(d1, d2, len) != 0)
+ return false;
+
+ return true;
+}
+
+/* ----------------------------------------------------------------
+ * ExecUnique
+ *
+ * This is a very simple node which filters out duplicate
+ * tuples from a stream of sorted tuples from a subplan.
+ *
+ * XXX see comments below regarding freeing tuples.
+ * ----------------------------------------------------------------
+ */
+TupleTableSlot * /* return: a tuple or NULL */
+ExecUnique(Unique *node)
+{
+ UniqueState *uniquestate;
+ TupleTableSlot *resultTupleSlot;
+ TupleTableSlot *slot;
+ Plan *outerPlan;
+ char *uniqueAttr;
+ AttrNumber uniqueAttrNum;
+ TupleDesc tupDesc;
+ Oid typoutput;
+
+ /* ----------------
+ * get information from the node
+ * ----------------
+ */
+ uniquestate = node->uniquestate;
+ outerPlan = outerPlan((Plan *) node);
+ resultTupleSlot = uniquestate->cs_ResultTupleSlot;
+ uniqueAttr = node->uniqueAttr;
+ uniqueAttrNum = node->uniqueAttrNum;
+
+ if (uniqueAttr) {
+ tupDesc = ExecGetResultType(uniquestate);
+ typoutput = typtoout((Oid)tupDesc->attrs[uniqueAttrNum]->atttypid);
+ }
+
+ /* ----------------
+ * now loop, returning only non-duplicate tuples.
+ * We assume that the tuples arrive in sorted order
+ * so we can detect duplicates easily.
+ * ----------------
+ */
+ for (;;) {
+ /* ----------------
+ * fetch a tuple from the outer subplan
+ * ----------------
+ */
+ slot = ExecProcNode(outerPlan, (Plan*)node);
+ if (TupIsNull(slot))
+ return NULL;
+
+ /* ----------------
+ * we use the result tuple slot to hold our saved tuples.
+ * if we haven't a saved tuple to compare our new tuple with,
+ * then we exit the loop. This new tuple as the saved tuple
+ * the next time we get here.
+ * ----------------
+ */
+ if (TupIsNull(resultTupleSlot))
+ break;
+
+ /* ----------------
+ * now test if the new tuple and the previous
+ * tuple match. If so then we loop back and fetch
+ * another new tuple from the subplan.
+ * ----------------
+ */
+
+ if (uniqueAttr) {
+ /* to check equality, we check to see if the typoutput
+ of the attributes are equal */
+ bool isNull1,isNull2;
+ char *attr1, *attr2;
+ char *val1, *val2;
+
+ attr1 = heap_getattr(slot->val, InvalidBuffer,
+ uniqueAttrNum, tupDesc,&isNull1);
+ attr2 = heap_getattr(resultTupleSlot->val, InvalidBuffer,
+ uniqueAttrNum, tupDesc,&isNull2);
+
+ if (isNull1 == isNull2) {
+ if (isNull1) /* both are null, they are equal */
+ continue;
+ val1 = fmgr(typoutput, attr1, gettypelem(tupDesc->attrs[uniqueAttrNum]->atttypid));
+ val2 = fmgr(typoutput, attr2, gettypelem(tupDesc->attrs[uniqueAttrNum]->atttypid));
+ /* now, val1 and val2 are ascii representations so we can
+ use strcmp for comparison */
+ if (strcmp(val1,val2) == 0) /* they are equal */
+ continue;
+ else
+ break;
+ }
+ else /* one is null and the other isn't, they aren't equal */
+ break;
+
+ }
+ else {
+ if (! ExecIdenticalTuples(slot, resultTupleSlot))
+ break;
+ }
+
+ }
+
+ /* ----------------
+ * we have a new tuple different from the previous saved tuple
+ * so we save it in the saved tuple slot. We copy the tuple
+ * so we don't increment the buffer ref count.
+ * ----------------
+ */
+ ExecStoreTuple(heap_copytuple(slot->val),
+ resultTupleSlot,
+ InvalidBuffer,
+ true);
+
+ return resultTupleSlot;
+}
+
+/* ----------------------------------------------------------------
+ * ExecInitUnique
+ *
+ * This initializes the unique node state structures and
+ * the node's subplan.
+ * ----------------------------------------------------------------
+ */
+bool /* return: initialization status */
+ExecInitUnique(Unique *node, EState *estate, Plan *parent)
+{
+ UniqueState *uniquestate;
+ Plan *outerPlan;
+ char *uniqueAttr;
+
+ /* ----------------
+ * assign execution state to node
+ * ----------------
+ */
+ node->plan.state = estate;
+
+ /* ----------------
+ * create new UniqueState for node
+ * ----------------
+ */
+ uniquestate = makeNode(UniqueState);
+ node->uniquestate = uniquestate;
+ uniqueAttr = node->uniqueAttr;
+
+ /* ----------------
+ * Miscellanious initialization
+ *
+ * + assign node's base_id
+ * + assign debugging hooks and
+ *
+ * Unique nodes have no ExprContext initialization because
+ * they never call ExecQual or ExecTargetList.
+ * ----------------
+ */
+ ExecAssignNodeBaseInfo(estate, uniquestate, parent);
+
+#define UNIQUE_NSLOTS 1
+ /* ------------
+ * Tuple table initialization
+ * ------------
+ */
+ ExecInitResultTupleSlot(estate, uniquestate);
+
+ /* ----------------
+ * then initialize outer plan
+ * ----------------
+ */
+ outerPlan = outerPlan((Plan *) node);
+ ExecInitNode(outerPlan, estate, (Plan *) node);
+
+ /* ----------------
+ * unique nodes do no projections, so initialize
+ * projection info for this node appropriately
+ * ----------------
+ */
+ ExecAssignResultTypeFromOuterPlan((Plan *)node,uniquestate);
+ uniquestate->cs_ProjInfo = NULL;
+
+ if (uniqueAttr) {
+ TupleDesc tupDesc;
+ int i = 0;
+
+ tupDesc = ExecGetResultType(uniquestate);
+ /* the parser should have ensured that uniqueAttr is a legal attribute name*/
+ while ( strcmp((tupDesc->attrs[i]->attname).data, uniqueAttr) != 0)
+ i++;
+ node->uniqueAttrNum = i+1; /* attribute numbers start from 1 */
+ }
+ else
+ node->uniqueAttrNum = InvalidAttrNumber;
+
+ /* ----------------
+ * all done.
+ * ----------------
+ */
+ return TRUE;
+}
+
+int
+ExecCountSlotsUnique(Unique *node)
+{
+ return ExecCountSlotsNode(outerPlan(node)) +
+ ExecCountSlotsNode(innerPlan(node)) +
+ UNIQUE_NSLOTS;
+}
+
+/* ----------------------------------------------------------------
+ * ExecEndUnique
+ *
+ * This shuts down the subplan and frees resources allocated
+ * to this node.
+ * ----------------------------------------------------------------
+ */
+void
+ExecEndUnique(Unique *node)
+{
+ UniqueState *uniquestate;
+
+ uniquestate = node->uniquestate;
+ ExecEndNode(outerPlan((Plan *) node), (Plan*)node);
+ ExecClearTuple(uniquestate->cs_ResultTupleSlot);
+}
diff --git a/src/backend/executor/nodeUnique.h b/src/backend/executor/nodeUnique.h
new file mode 100644
index 00000000000..a8dfc9bd6b9
--- /dev/null
+++ b/src/backend/executor/nodeUnique.h
@@ -0,0 +1,21 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeUnique.h--
+ *
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id: nodeUnique.h,v 1.1.1.1 1996/07/09 06:21:28 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef NODEUNIQUE_H
+#define NODEUNIQUE_H
+
+extern TupleTableSlot *ExecUnique(Unique *node);
+extern bool ExecInitUnique(Unique *node, EState *estate, Plan *parent);
+extern int ExecCountSlotsUnique(Unique *node);
+extern void ExecEndUnique(Unique *node);
+
+#endif /* NODEUNIQUE_H */
diff --git a/src/backend/executor/tuptable.h b/src/backend/executor/tuptable.h
new file mode 100644
index 00000000000..33f7de33589
--- /dev/null
+++ b/src/backend/executor/tuptable.h
@@ -0,0 +1,72 @@
+/*-------------------------------------------------------------------------
+ *
+ * tuptable.h--
+ * tuple table support stuff
+ *
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id: tuptable.h,v 1.1.1.1 1996/07/09 06:21:28 scrappy Exp $
+ *
+ * NOTES
+ * The tuple table interface is getting pretty ugly.
+ * It should be redesigned soon.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef TUPTABLE_H
+#define TUPTABLE_H
+
+/* ----------------
+ * Note: the executor tuple table is managed and manipulated by special
+ * code and macros in executor/execTuples.c and tupTable.h
+ *
+ * TupleTableSlot information
+ *
+ * shouldFree boolean - should we call pfree() on tuple
+ * descIsNew boolean - true when tupleDescriptor changes
+ * tupleDescriptor type information kept regarding the tuple data
+ * buffer the buffer for tuples pointing to disk pages
+ *
+ * The executor stores pointers to tuples in a ``tuple table''
+ * which is composed of TupleTableSlot's. Some of the tuples
+ * are pointers to buffer pages and others are pointers to
+ * palloc'ed memory and the shouldFree variable tells us when
+ * we may call pfree() on a tuple. -cim 9/23/90
+ *
+ * In the implementation of nested-dot queries such as
+ * "retrieve (EMP.hobbies.all)", a single scan may return tuples
+ * of many types, so now we return pointers to tuple descriptors
+ * along with tuples returned via the tuple table. -cim 1/18/90
+ * ----------------
+ */
+typedef struct TupleTableSlot {
+ NodeTag type;
+ HeapTuple val;
+ bool ttc_shouldFree;
+ bool ttc_descIsNew;
+ TupleDesc ttc_tupleDescriptor;
+ Buffer ttc_buffer;
+ int ttc_whichplan;
+} TupleTableSlot;
+
+/* ----------------
+ * tuple table data structure
+ * ----------------
+ */
+typedef struct TupleTableData {
+ int size; /* size of the table */
+ int next; /* next available slot number */
+ TupleTableSlot *array; /* array of TupleTableSlot's */
+} TupleTableData;
+
+typedef TupleTableData *TupleTable;
+
+/*
+ tuple table macros are all excised from the system now
+ see executor.h for decls of functions defined in execTuples.c
+
+ - jolly
+*/
+
+#endif /* TUPTABLE_H */