aboutsummaryrefslogtreecommitdiff
path: root/src/backend/executor
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/executor')
-rw-r--r--src/backend/executor/execAmi.c693
-rw-r--r--src/backend/executor/execFlatten.c372
-rw-r--r--src/backend/executor/execJunk.c638
-rw-r--r--src/backend/executor/execMain.c2324
-rw-r--r--src/backend/executor/execProcnode.c820
-rw-r--r--src/backend/executor/execQual.c2665
-rw-r--r--src/backend/executor/execScan.c208
-rw-r--r--src/backend/executor/execTuples.c1526
-rw-r--r--src/backend/executor/execUtils.c1816
-rw-r--r--src/backend/executor/functions.c695
-rw-r--r--src/backend/executor/nodeAgg.c1055
-rw-r--r--src/backend/executor/nodeAppend.c852
-rw-r--r--src/backend/executor/nodeGroup.c632
-rw-r--r--src/backend/executor/nodeHash.c1420
-rw-r--r--src/backend/executor/nodeHashjoin.c1377
-rw-r--r--src/backend/executor/nodeIndexscan.c1623
-rw-r--r--src/backend/executor/nodeMaterial.c631
-rw-r--r--src/backend/executor/nodeMergejoin.c2165
-rw-r--r--src/backend/executor/nodeNestloop.c632
-rw-r--r--src/backend/executor/nodeResult.c454
-rw-r--r--src/backend/executor/nodeSeqscan.c726
-rw-r--r--src/backend/executor/nodeSort.c632
-rw-r--r--src/backend/executor/nodeTee.c899
-rw-r--r--src/backend/executor/nodeUnique.c532
-rw-r--r--src/backend/executor/spi.c1537
25 files changed, 13807 insertions, 13117 deletions
diff --git a/src/backend/executor/execAmi.c b/src/backend/executor/execAmi.c
index 84b33d4f1e1..401924485e0 100644
--- a/src/backend/executor/execAmi.c
+++ b/src/backend/executor/execAmi.c
@@ -1,32 +1,32 @@
/*-------------------------------------------------------------------------
*
* execAmi.c--
- * miscellanious executor access method routines
+ * 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.5 1997/08/19 21:30:51 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/execAmi.c,v 1.6 1997/09/07 04:41:09 momjian Exp $
*
*-------------------------------------------------------------------------
*/
/*
- * INTERFACE ROUTINES
+ * INTERFACE ROUTINES
*
- * ExecOpenScanR \ / amopen
- * ExecBeginScan \ / ambeginscan
- * ExecCloseR \ / amclose
- * ExecInsert \ executor interface / aminsert
- * ExecReScanNode / to access methods \ amrescan
- * ExecReScanR / \ amrescan
- * ExecMarkPos / \ ammarkpos
- * ExecRestrPos / \ amrestpos
+ * 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
+ * ExecCreatR function to create temporary relations
*
*/
-#include <stdio.h> /* for sprintf() */
+#include <stdio.h> /* for sprintf() */
#include "postgres.h"
@@ -43,409 +43,430 @@
#include "access/heapam.h"
#include "catalog/heap.h"
-static Pointer ExecBeginScan(Relation relation, int nkeys, ScanKey skeys,
- bool isindex, ScanDirection dir, TimeQual time_range);
+static Pointer
+ExecBeginScan(Relation relation, int nkeys, ScanKey skeys,
+ bool isindex, ScanDirection dir, TimeQual time_range);
static Relation ExecOpenR(Oid relationOid, bool isindex);
/* ----------------------------------------------------------------
- * ExecOpenScanR
+ * 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.
+ * 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 */
+ 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;
+ 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
+ * ExecOpenR
*
- * returns a relation descriptor given an object id.
+ * returns a relation descriptor given an object id.
* ----------------------------------------------------------------
*/
-static Relation
+static 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;
+ 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
+ * ExecBeginScan
*
- * beginscans a relation in current direction.
+ * 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
+ * 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
* ----------------------------------------------------------------
*/
-static Pointer
+static Pointer
ExecBeginScan(Relation relation,
- int nkeys,
- ScanKey skeys,
- bool isindex,
- ScanDirection dir,
- TimeQual time_range)
+ 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;
+ 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
+ * ExecCloseR
*
- * closes the relation and scan descriptor for a scan or sort
- * node. Also closes index relations and scans for index scans.
+ * 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'
+ * closes the relation indicated in 'relID'
* ----------------------------------------------------------------
*/
void
-ExecCloseR(Plan *node)
+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]);
+ 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
+ * ExecReScan
*
- * XXX this should be extended to cope with all the node types..
+ * 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
+ * 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)
+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;
+ switch (nodeTag(node))
+ {
+ case T_SeqScan:
+ ExecSeqReScan((SeqScan *) 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_IndexScan:
+ ExecIndexReScan((IndexScan *) node, exprCtxt, parent);
+ return;
- case T_Tee:
- ExecTeeReScan((Tee*) node, exprCtxt, parent);
- break;
+ case T_Material:
- default:
- elog(WARN, "ExecReScan: not a seqscan or indexscan node.");
- return;
- }
+ /*
+ * 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
+ * ExecReScanR
*
- * XXX this does not do the right thing with indices yet.
+ * 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)
+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;
+ if (scanDesc != NULL)
+ heap_rescan(scanDesc, /* scan desc */
+ ScanDirectionIsBackward(direction), /* backward flag */
+ skeys); /* scan keys */
+
+ return scanDesc;
}
-
+
/* ----------------------------------------------------------------
- * ExecMarkPos
+ * ExecMarkPos
*
- * Marks the current scan position.
+ * Marks the current scan position.
*
- * XXX Needs to be extended to include all the node types.
+ * XXX Needs to be extended to include all the node types.
* ----------------------------------------------------------------
*/
void
-ExecMarkPos(Plan *node)
+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;
+ 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
+ * ExecRestrPos
*
- * restores the scan position previously saved with ExecMarkPos()
+ * restores the scan position previously saved with ExecMarkPos()
* ----------------------------------------------------------------
*/
void
-ExecRestrPos(Plan *node)
+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;
+ switch (nodeTag(node))
+ {
+ case T_SeqScan:
+ ExecSeqRestrPos((SeqScan *) node);
+ return;
- default:
- /* elog(DEBUG, "ExecRestrPos: node type not supported"); */
- 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
+ * ExecCreatR
*
* old comments
- * Creates a relation.
+ * 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.
+ * 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)
+ Oid relationOid)
{
- Relation relDesc;
-
- EU3_printf("ExecCreatR: %s type=%d oid=%d\n",
- "entering: ", 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)
- * ----------------
- */
+ Relation relDesc;
+
+ EU3_printf("ExecCreatR: %s type=%d oid=%d\n",
+ "entering: ", 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);
+ 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;
+
+ /*
+ * 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
index c9bde2ff663..43d616712fa 100644
--- a/src/backend/executor/execFlatten.c
+++ b/src/backend/executor/execFlatten.c
@@ -1,29 +1,29 @@
/*-------------------------------------------------------------------------
*
* execFlatten.c--
- * This file handles the nodes associated with flattening sets in the
- * target list of queries containing functions returning sets.
+ * 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.2 1997/08/19 21:30:56 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/Attic/execFlatten.c,v 1.3 1997/09/07 04:41:12 momjian 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.
+ * 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.
+ * 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"
@@ -33,208 +33,216 @@
#include "executor/execFlatten.h"
#ifdef SETS_FIXED
-static bool FjoinBumpOuterNodes(TargetEntry *tlist, ExprContext *econtext,
- DatumPtr results, char *nulls);
+static bool
+FjoinBumpOuterNodes(TargetEntry * tlist, ExprContext * econtext,
+ DatumPtr results, char *nulls);
+
#endif
Datum
-ExecEvalIter(Iter *iterNode,
- ExprContext *econtext,
- bool *resultIsNull,
- bool *iterIsDone)
+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);
+ 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)
+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)
+ 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))
+
+ /*
+ * 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++;
+ 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;
+
+ /*
+ * 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
+ 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)
+
+ /*
+ * 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;
+ *fj_isDone = FjoinBumpOuterNodes(tlist,
+ econtext,
+ resVect,
+ isNullVect);
+ return;
}
- else
- resVect[0] = ExecEvalIter((Iter*)fjNode->fj_innerNode->expr,
- econtext,
- &isNullVect[0],
- &isDone);
+ else
+ resVect[0] = ExecEvalIter((Iter *) fjNode->fj_innerNode->expr,
+ econtext,
+ &isNullVect[0],
+ &isDone);
}
-
- /*
- * if the inner node is done
- */
- if (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);
-
+ *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;
+ return;
}
#ifdef SETS_FIXED
-static bool
-FjoinBumpOuterNodes(TargetEntry *tlist,
- ExprContext *econtext,
- DatumPtr results,
- char *nulls)
+static bool
+FjoinBumpOuterNodes(TargetEntry * tlist,
+ ExprContext * econtext,
+ DatumPtr results,
+ char *nulls)
{
- 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))
+ 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);
+ 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)
+
+ /*
+ * 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);
+ 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)
+
+ /*
+ * 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);
+ TargetEntry *tle = lfirst(trailers);
+
+ if (alwaysDone[trailNode] != true)
+ results[trailNode] = ExecEvalIter((Iter) tle->expr,
+ econtext,
+ &nulls[trailNode],
+ &funcIsDone);
+ trailNode++;
+ trailers = lnext(trailers);
}
- return false;
+ return false;
}
+
#endif
diff --git a/src/backend/executor/execJunk.c b/src/backend/executor/execJunk.c
index 8779647a113..3ad41bd393f 100644
--- a/src/backend/executor/execJunk.c
+++ b/src/backend/executor/execJunk.c
@@ -1,13 +1,13 @@
/*-------------------------------------------------------------------------
*
* junk.c--
- * Junk attribute support stuff....
+ * Junk attribute support stuff....
*
* Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/execJunk.c,v 1.5 1997/08/26 23:31:37 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/execJunk.c,v 1.6 1997/09/07 04:41:14 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -20,37 +20,37 @@
#include "access/heapam.h"
#include "executor/executor.h"
#include "nodes/relation.h"
-#include "optimizer/tlist.h" /* for MakeTLE */
+#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
- *
+ * 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.
- *
+ *
*-------------------------------------------------------------------------
*/
@@ -60,174 +60,196 @@
* Initialize the Junk filter.
*-------------------------------------------------------------------------
*/
-JunkFilter *
-ExecInitJunkFilter(List *targetList)
+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 {
+ 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);
+ 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 {
+ }
+
+ /* ---------------------
+ * 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);
+ List fjListP;
+ List fjList = lfirst(t);
+ Fjoin fjNode = (Fjoin) lfirst(fjList);
- /* what the hell is this????? */
- resdom = (Resdom) lfirst(get_fj_innerNode(fjNode));
+ /* what the hell is this????? */
+ resdom = (Resdom) lfirst(get_fj_innerNode(fjNode));
#endif
- cleanMap[cleanResno-1] = tle->resdom->resno;
- cleanResno++;
+ cleanMap[cleanResno - 1] = tle->resdom->resno;
+ cleanResno++;
#ifdef SETS_FIXED
- foreach(fjListP, lnext(fjList)) {
- TargetEntry *tle = lfirst(fjListP);
+ foreach(fjListP, lnext(fjList))
+ {
+ TargetEntry *tle = lfirst(fjListP);
- resdom = tle->resdom;
- cleanMap[cleanResno-1] = resdom->resno;
- cleanResno++;
- }
+ resdom = tle->resdom;
+ cleanMap[cleanResno - 1] = resdom->resno;
+ cleanResno++;
+ }
#endif
- }
+ }
+ }
+ }
+ else
+ {
+ cleanMap = NULL;
}
- } 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);
-
+
+ /* ---------------------
+ * 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);
+
}
/*-------------------------------------------------------------------------
@@ -242,57 +264,61 @@ ExecInitJunkFilter(List *targetList)
*-------------------------------------------------------------------------
*/
bool
-ExecGetJunkAttribute(JunkFilter *junkfilter,
- TupleTableSlot *slot,
- char *attrName,
- Datum *value,
- bool *isNull)
+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;
+ 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);
}
- }
-
- 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;
+
+ /* ---------------------
+ * 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;
}
/*-------------------------------------------------------------------------
@@ -302,94 +328,98 @@ ExecGetJunkAttribute(JunkFilter *junkfilter,
*-------------------------------------------------------------------------
*/
HeapTuple
-ExecRemoveJunk(JunkFilter *junkfilter, TupleTableSlot *slot)
+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';
+ 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
- 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);
-}
+ {
+ 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
index 48bf84ba095..2bf0edaf35e 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -1,32 +1,32 @@
/*-------------------------------------------------------------------------
*
* execMain.c--
- * top level executor interface routines
+ * top level executor interface routines
*
* INTERFACE ROUTINES
- * ExecutorStart()
- * ExecutorRun()
- * ExecutorEnd()
+ * ExecutorStart()
+ * ExecutorRun()
+ * ExecutorEnd()
*
- * The old ExecutorMain() has been replaced by ExecutorStart(),
- * ExecutorRun() and 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.
*
- * 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.22 1997/09/04 13:22:36 vadim Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.23 1997/09/07 04:41:18 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -42,7 +42,7 @@
#include "utils/palloc.h"
#include "utils/acl.h"
#include "utils/syscache.h"
-#include "parser/parsetree.h" /* rt_fetch() */
+#include "parser/parsetree.h" /* rt_fetch() */
#include "storage/bufmgr.h"
#include "storage/lmgr.h"
#include "storage/smgr.h"
@@ -56,28 +56,35 @@
/* 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, ScanDirection direction,
- void (*printfunc)());
-static void ExecRetrieve(TupleTableSlot *slot, void (*printfunc)(),
- EState *estate);
-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);
+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, ScanDirection direction,
+ void (*printfunc) ());
+static void ExecRetrieve(TupleTableSlot * slot, void (*printfunc) (),
+ EState * estate);
+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 */
#ifdef QUERY_LIMIT
-static int queryLimit = ALL_TUPLES;
+static int queryLimit = ALL_TUPLES;
+
#undef ALL_TUPLES
#define ALL_TUPLES queryLimit
@@ -85,574 +92,605 @@ static int queryLimit = ALL_TUPLES;
int
ExecutorLimit(int limit)
{
- return queryLimit = limit;
+ return queryLimit = limit;
}
+
#endif
#endif
/* ----------------------------------------------------------------
- * 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.
+ * 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)
+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;
+ 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.
+ * 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
+ * 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)
+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);
- estate->es_processed = 0;
- estate->es_lastoid = InvalidOid;
+ CmdType operation;
+ Query *parseTree;
+ Plan *plan;
+ TupleTableSlot *result;
+ CommandDest dest;
+ void (*destination) ();
-#if 0
- /*
- * It doesn't work in common case (i.g. if function has a aggregate).
- * Now we store parameter values before ExecutorStart. - vadim 01/22/97
- */
-#ifdef INDEXSCAN_PATCH
- /*
- * If the plan is an index scan and some of the scan key are
- * function arguments rescan the indices after the parameter
- * values have been stored in the execution state. DZ - 27-8-1996
- */
- if ((nodeTag(plan) == T_IndexScan) &&
- (((IndexScan *)plan)->indxstate->iss_RuntimeKeyInfo != NULL)) {
- ExprContext *econtext;
- econtext = ((IndexScan *)plan)->scan.scanstate->cstate.cs_ExprContext;
- ExecIndexReScan((IndexScan *)plan, econtext, plan);
- }
-#endif
-#endif
-
- switch(feature) {
-
- case EXEC_RUN:
- result = ExecutePlan(estate,
- plan,
- parseTree,
- operation,
- ALL_TUPLES,
- ForwardScanDirection,
- destination);
- break;
- case EXEC_FOR:
- result = ExecutePlan(estate,
- plan,
- parseTree,
- operation,
- count,
- ForwardScanDirection,
- destination);
- break;
-
/* ----------------
- * retrieve next n "backward" tuples
+ * sanity checks
* ----------------
*/
- case EXEC_BACK:
- result = ExecutePlan(estate,
- plan,
- parseTree,
- operation,
- count,
- BackwardScanDirection,
- destination);
- break;
-
+ Assert(queryDesc != NULL);
+
/* ----------------
- * return one tuple but don't "retrieve" it.
- * (this is used by the rule manager..) -cim 9/14/89
+ * extract information from the query descriptor
+ * and the query feature.
* ----------------
*/
- case EXEC_RETONE:
- result = ExecutePlan(estate,
- plan,
- parseTree,
- operation,
- ONE_TUPLE,
- ForwardScanDirection,
- destination);
- break;
- default:
- result = NULL;
- elog(DEBUG, "ExecutorRun: Unknown feature %d", feature);
- break;
- }
+ operation = queryDesc->operation;
+ parseTree = queryDesc->parsetree;
+ plan = queryDesc->plantree;
+ dest = queryDesc->dest;
+ destination = (void (*) ()) DestToFunction(dest);
+ estate->es_processed = 0;
+ estate->es_lastoid = InvalidOid;
+
+#if 0
+
+ /*
+ * It doesn't work in common case (i.g. if function has a aggregate).
+ * Now we store parameter values before ExecutorStart. - vadim
+ * 01/22/97
+ */
+#ifdef INDEXSCAN_PATCH
+
+ /*
+ * If the plan is an index scan and some of the scan key are function
+ * arguments rescan the indices after the parameter values have been
+ * stored in the execution state. DZ - 27-8-1996
+ */
+ if ((nodeTag(plan) == T_IndexScan) &&
+ (((IndexScan *) plan)->indxstate->iss_RuntimeKeyInfo != NULL))
+ {
+ ExprContext *econtext;
+
+ econtext = ((IndexScan *) plan)->scan.scanstate->cstate.cs_ExprContext;
+ ExecIndexReScan((IndexScan *) plan, econtext, plan);
+ }
+#endif
+#endif
+
+ switch (feature)
+ {
+
+ case EXEC_RUN:
+ result = ExecutePlan(estate,
+ plan,
+ parseTree,
+ operation,
+ ALL_TUPLES,
+ ForwardScanDirection,
+ destination);
+ break;
+ case EXEC_FOR:
+ result = ExecutePlan(estate,
+ plan,
+ parseTree,
+ operation,
+ count,
+ ForwardScanDirection,
+ destination);
+ break;
- return result;
+ /* ----------------
+ * retrieve next n "backward" tuples
+ * ----------------
+ */
+ case EXEC_BACK:
+ result = ExecutePlan(estate,
+ plan,
+ parseTree,
+ operation,
+ count,
+ BackwardScanDirection,
+ 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,
+ ForwardScanDirection,
+ destination);
+ break;
+ default:
+ result = NULL;
+ 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.
+ * 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)
+ExecutorEnd(QueryDesc * queryDesc, EState * estate)
{
- /* sanity checks */
- Assert(queryDesc!=NULL);
+ /* sanity checks */
+ Assert(queryDesc != NULL);
- EndPlan(queryDesc->plantree, estate);
+ EndPlan(queryDesc->plantree, estate);
- /* restore saved refcounts. */
- BufferRefCountRestore(estate->es_refcount);
+ /* restore saved refcounts. */
+ BufferRefCountRestore(estate->es_refcount);
}
/* ===============================================================
* ===============================================================
- static routines follow
+ static routines follow
* ===============================================================
* ===============================================================
*/
static void
ExecCheckPerms(CmdType operation,
- int resultRelation,
- List *rangeTable,
- Query *parseTree)
+ int resultRelation,
+ List * rangeTable,
+ Query * parseTree)
{
- int i = 1;
- Oid relid;
- HeapTuple htp;
- List *lp;
- List *qvars, *tvars;
- int32 ok = 1, aclcheck_result = -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-1);
- 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 = ((aclcheck_result = CHECK(ACL_RD)) == ACLCHECK_OK);
- opstr = "read";
+ int i = 1;
+ Oid relid;
+ HeapTuple htp;
+ List *lp;
+ List *qvars,
+ *tvars;
+ int32 ok = 1,
+ aclcheck_result = -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 - 1);
+ 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 = ((aclcheck_result = CHECK(ACL_RD)) == ACLCHECK_OK);
+ opstr = "read";
+ if (!ok)
+ break;
+ }
+ switch (operation)
+ {
+ case CMD_INSERT:
+ ok = ((aclcheck_result = CHECK(ACL_AP)) == ACLCHECK_OK) ||
+ ((aclcheck_result = CHECK(ACL_WR)) == ACLCHECK_OK);
+ opstr = "append";
+ break;
+ case CMD_NOTIFY: /* what does this mean?? -- jw, 1/6/94 */
+ case CMD_DELETE:
+ case CMD_UPDATE:
+ ok = ((aclcheck_result = CHECK(ACL_WR)) == ACLCHECK_OK);
+ opstr = "write";
+ break;
+ default:
+ elog(WARN, "ExecCheckPerms: bogus operation %d",
+ operation);
+ }
+ }
+ else
+ {
+ /* XXX NOTIFY?? */
+ ok = ((aclcheck_result = CHECK(ACL_RD)) == ACLCHECK_OK);
+ opstr = "read";
+ }
if (!ok)
- break;
- }
- switch (operation) {
- case CMD_INSERT:
- ok = ((aclcheck_result = CHECK(ACL_AP)) == ACLCHECK_OK) ||
- ((aclcheck_result = CHECK(ACL_WR)) == ACLCHECK_OK);
- opstr = "append";
- break;
- case CMD_NOTIFY: /* what does this mean?? -- jw, 1/6/94 */
- case CMD_DELETE:
- case CMD_UPDATE:
- ok = ((aclcheck_result = CHECK(ACL_WR)) == ACLCHECK_OK);
- opstr = "write";
- break;
- default:
- elog(WARN, "ExecCheckPerms: bogus operation %d",
- operation);
- }
- } else {
- /* XXX NOTIFY?? */
- ok = ((aclcheck_result = CHECK(ACL_RD)) == ACLCHECK_OK);
- opstr = "read";
+ break;
+ ++i;
}
if (!ok)
- break;
- ++i;
- }
- if (!ok) {
- elog(WARN, "%s: %s", rname.data, aclcheck_error_strings[aclcheck_result]);
- }
+ {
+ elog(WARN, "%s: %s", rname.data, aclcheck_error_strings[aclcheck_result]);
+ }
}
/* ----------------------------------------------------------------
- * InitPlan
- *
- * Initializes the query plan: open files, allocate storage
- * and start up the rule manager
+ * 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
+static TupleDesc
+InitPlan(CmdType operation, Query * parseTree, Plan * plan, EState * estate)
+{
+ List *rangeTable;
+ int resultRelation;
+ Relation intoRelationDesc;
+
+ TupleDesc tupType;
+ List *targetList;
+ int len;
- * initialize the result relation info stuff.
+ /* ----------------
+ * get information from query descriptor
* ----------------
*/
- RelationInfo *resultRelationInfo;
- Index resultRelationIndex;
- RangeTblEntry *rtentry;
- Oid resultRelationOid;
- Relation resultRelationDesc;
-
- resultRelationIndex = resultRelation;
- rtentry = rt_fetch(resultRelationIndex, rangeTable);
- resultRelationOid = rtentry->relid;
- resultRelationDesc = heap_open(resultRelationOid);
-
- if ( resultRelationDesc->rd_rel->relkind == RELKIND_SEQUENCE )
- elog (WARN, "You can't change sequence relation %s",
- resultRelationDesc->rd_rel->relname.data);
-
- /* 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;
+ rangeTable = parseTree->rtable;
+ resultRelation = parseTree->resultRelation;
/* ----------------
- * open indices on result relation and save descriptors
- * in the result relation information..
+ * initialize the node's execution state
* ----------------
*/
- ExecOpenIndices(resultRelationOid, resultRelationInfo);
-
- estate->es_result_relation_info = resultRelationInfo;
- } else {
+ estate->es_range_table = rangeTable;
+
/* ----------------
- * if no result relation, then set state appropriately
+ * 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_result_relation_info = NULL;
- }
+ estate->es_BaseId = 1;
-#ifndef NO_SECURITY
- ExecCheckPerms(operation, resultRelation, rangeTable, parseTree);
-#endif
+ /* ----------------
+ * initialize result relation stuff
+ * ----------------
+ */
- /* ----------------
- * 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;
- TupleDesc tupdesc;
-
- if (!parseTree->isPortal) {
- /*
- * a select into table
- */
- if (parseTree->into != NULL) {
+ if (resultRelation != 0 && operation != CMD_SELECT)
+ {
/* ----------------
- * create the "into" relation
- *
- * note: there is currently no way for the user to
- * specify the desired archive mode of the
- * "into" relation...
+ * if we have a result relation, open it and
+
+ * initialize the result relation info stuff.
* ----------------
*/
- intoName = parseTree->into;
- archiveMode = 'n';
-
+ RelationInfo *resultRelationInfo;
+ Index resultRelationIndex;
+ RangeTblEntry *rtentry;
+ Oid resultRelationOid;
+ Relation resultRelationDesc;
+
+ resultRelationIndex = resultRelation;
+ rtentry = rt_fetch(resultRelationIndex, rangeTable);
+ resultRelationOid = rtentry->relid;
+ resultRelationDesc = heap_open(resultRelationOid);
+
+ if (resultRelationDesc->rd_rel->relkind == RELKIND_SEQUENCE)
+ elog(WARN, "You can't change sequence relation %s",
+ resultRelationDesc->rd_rel->relname.data);
+
/*
- * have to copy tupType to get rid of constraints
+ * 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
*/
- tupdesc = CreateTupleDescCopy (tupType);
-
- /* fixup to prevent zero-length columns in create */
- setVarAttrLenForCreateTable(tupdesc, targetList, rangeTable);
-
- intoRelationId = heap_create(intoName,
- intoName, /* not used */
- archiveMode,
- DEFAULT_SMGR,
- tupdesc);
-#ifdef NOT_USED /* it's copy ... */
- resetVarAttrLenForCreateTable(tupdesc);
-#endif
- FreeTupleDesc (tupdesc);
+ 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;
/* ----------------
- * XXX rather than having to call setheapoverride(true)
- * and then back to false, we should change the
- * arguments to heap_open() instead..
+ * open indices on result relation and save descriptors
+ * in the result relation information..
* ----------------
*/
- setheapoverride(true);
-
- intoRelationDesc = heap_open(intoRelationId);
-
- setheapoverride(false);
- }
+ 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_into_relation_descriptor = intoRelationDesc;
+ estate->es_tupleTable = tupleTable;
+ }
- /* ----------------
- * return the type information..
- * ----------------
- */
+ /* ----------------
+ * 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;
+ TupleDesc tupdesc;
+
+ 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';
+
+ /*
+ * have to copy tupType to get rid of constraints
+ */
+ tupdesc = CreateTupleDescCopy(tupType);
+
+ /* fixup to prevent zero-length columns in create */
+ setVarAttrLenForCreateTable(tupdesc, targetList, rangeTable);
+
+ intoRelationId = heap_create(intoName,
+ intoName, /* not used */
+ archiveMode,
+ DEFAULT_SMGR,
+ tupdesc);
+#ifdef NOT_USED /* it's copy ... */
+ resetVarAttrLenForCreateTable(tupdesc);
+#endif
+ FreeTupleDesc(tupdesc);
+
+ /* ----------------
+ * 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;
+ attinfo = (AttrInfo *)palloc(sizeof(AttrInfo));
+ attinfo->numAttr = len;
+ attinfo->attrs = tupType->attrs;
*/
- return tupType;
+ return tupType;
}
/* ----------------------------------------------------------------
- * EndPlan
- *
- * Cleans up the query plan -- closes files and free up storages
+ * EndPlan
+ *
+ * Cleans up the query plan -- closes files and free up storages
* ----------------------------------------------------------------
*/
static void
-EndPlan(Plan *plan, EState *estate)
+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);
-
+ RelationInfo *resultRelationInfo;
+ Relation intoRelationDesc;
+
/* ----------------
- * close indices on the result relation
+ * get information from state
* ----------------
*/
- ExecCloseIndices(resultRelationInfo);
- }
-
- /* ----------------
- * close the "into" relation if necessary
- * ----------------
- */
- if (intoRelationDesc != NULL) {
- heap_close(intoRelationDesc);
- }
+ 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
+ * 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.
*
- * result is either a slot containing a tuple in the case
- * of a RETRIEVE or NULL otherwise.
- *
* ----------------------------------------------------------------
*/
@@ -660,688 +698,704 @@ EndPlan(Plan *plan, EState *estate)
user can see it*/
static TupleTableSlot *
-ExecutePlan(EState *estate,
- Plan *plan,
- Query *parseTree,
- CmdType operation,
- int numberTuples,
- ScanDirection direction,
- void (*printfunc)())
+ExecutePlan(EState * estate,
+ Plan * plan,
+ Query * parseTree,
+ CmdType operation,
+ int numberTuples,
+ ScanDirection direction,
+ void (*printfunc) ())
{
- JunkFilter *junkfilter;
-
- TupleTableSlot *slot;
- ItemPointer tupleid = NULL;
- ItemPointerData tuple_ctid;
- int current_tuple_count;
- TupleTableSlot *result;
-
- /* ----------------
- * 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;
- }
- }
-
+ JunkFilter *junkfilter;
+
+ TupleTableSlot *slot;
+ ItemPointer tupleid = NULL;
+ ItemPointerData tuple_ctid;
+ int current_tuple_count;
+ TupleTableSlot *result;
+
/* ----------------
- * 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.
+ * initialize local variables
* ----------------
*/
- 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... */
-
+ slot = NULL;
+ current_tuple_count = 0;
+ result = NULL;
+
/* ----------------
- * 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.
+ * Set the direction.
* ----------------
*/
-
- switch(operation) {
- case CMD_SELECT:
- ExecRetrieve(slot, /* slot containing tuple */
- printfunc, /* print function */
- estate); /* */
- 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;
+ 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 */
+ estate); /* */
+ 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;
}
+
/* ----------------
- * check our tuple count.. if we've returned the
- * proper number then return, else loop again and
- * process more tuples..
+ * here, result is either a slot containing a tuple in the case
+ * of a RETRIEVE or NULL otherwise.
* ----------------
*/
- 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;
+ return result;
}
/* ----------------------------------------------------------------
- * ExecRetrieve
+ * 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.)
+ * 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)(),
- EState *estate)
+ExecRetrieve(TupleTableSlot * slot,
+ void (*printfunc) (),
+ EState * estate)
{
- 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 ( estate->es_into_relation_descriptor != NULL )
- {
- heap_insert (estate->es_into_relation_descriptor, tuple);
- IncrAppended();
- }
-
- /* ----------------
- * send the tuple to the front end (or the screen)
- * ----------------
- */
- (*printfunc)(tuple, attrtype);
- IncrRetrieved();
- (estate->es_processed)++;
+ 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 (estate->es_into_relation_descriptor != NULL)
+ {
+ heap_insert(estate->es_into_relation_descriptor, tuple);
+ IncrAppended();
+ }
+
+ /* ----------------
+ * send the tuple to the front end (or the screen)
+ * ----------------
+ */
+ (*printfunc) (tuple, attrtype);
+ IncrRetrieved();
+ (estate->es_processed)++;
}
/* ----------------------------------------------------------------
- * ExecAppend
+ * ExecAppend
*
- * APPENDs are trickier.. we have to insert the tuple into
- * the base relation and insert appropriate tuples into the
- * index relations.
+ * 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)
+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
- * ----------------
- */
-
- /* BEFORE ROW INSERT Triggers */
- if ( resultRelationDesc->trigdesc &&
- resultRelationDesc->trigdesc->n_before_row[TRIGGER_EVENT_INSERT] > 0 )
- {
- HeapTuple newtuple;
-
- newtuple = ExecBRInsertTriggers (resultRelationDesc, tuple);
-
- if ( newtuple == NULL ) /* "do nothing" */
- return;
-
- if ( newtuple != tuple ) /* modified by Trigger(s) */
- {
- Assert ( slot->ttc_shouldFree );
- pfree (tuple);
- slot->val = tuple = newtuple;
- }
- }
-
- /* ----------------
- * Check the constraints of a tuple
- * ----------------
- */
-
- if ( resultRelationDesc->rd_att->constr )
- {
- HeapTuple newtuple;
-
- newtuple = ExecConstraints ("ExecAppend", resultRelationDesc, tuple);
-
- if ( newtuple != tuple ) /* modified by DEFAULT */
- {
- Assert ( slot->ttc_shouldFree );
- pfree (tuple);
- slot->val = tuple = newtuple;
- }
- }
-
- /* ----------------
- * insert the tuple
- * ----------------
- */
- newId = heap_insert(resultRelationDesc, /* relation desc */
- tuple); /* heap tuple */
- IncrAppended();
-
- /* ----------------
- * 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, false);
- }
- (estate->es_processed)++;
- estate->es_lastoid = newId;
-
- /* AFTER ROW INSERT Triggers */
- if ( resultRelationDesc->trigdesc &&
- resultRelationDesc->trigdesc->n_after_row[TRIGGER_EVENT_INSERT] > 0 )
- ExecARInsertTriggers (resultRelationDesc, tuple);
+ 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
+ * ----------------
+ */
+
+ /* BEFORE ROW INSERT Triggers */
+ if (resultRelationDesc->trigdesc &&
+ resultRelationDesc->trigdesc->n_before_row[TRIGGER_EVENT_INSERT] > 0)
+ {
+ HeapTuple newtuple;
+
+ newtuple = ExecBRInsertTriggers(resultRelationDesc, tuple);
+
+ if (newtuple == NULL) /* "do nothing" */
+ return;
+
+ if (newtuple != tuple) /* modified by Trigger(s) */
+ {
+ Assert(slot->ttc_shouldFree);
+ pfree(tuple);
+ slot->val = tuple = newtuple;
+ }
+ }
+
+ /* ----------------
+ * Check the constraints of a tuple
+ * ----------------
+ */
+
+ if (resultRelationDesc->rd_att->constr)
+ {
+ HeapTuple newtuple;
+
+ newtuple = ExecConstraints("ExecAppend", resultRelationDesc, tuple);
+
+ if (newtuple != tuple) /* modified by DEFAULT */
+ {
+ Assert(slot->ttc_shouldFree);
+ pfree(tuple);
+ slot->val = tuple = newtuple;
+ }
+ }
+
+ /* ----------------
+ * insert the tuple
+ * ----------------
+ */
+ newId = heap_insert(resultRelationDesc, /* relation desc */
+ tuple); /* heap tuple */
+ IncrAppended();
+
+ /* ----------------
+ * 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, false);
+ }
+ (estate->es_processed)++;
+ estate->es_lastoid = newId;
+
+ /* AFTER ROW INSERT Triggers */
+ if (resultRelationDesc->trigdesc &&
+ resultRelationDesc->trigdesc->n_after_row[TRIGGER_EVENT_INSERT] > 0)
+ ExecARInsertTriggers(resultRelationDesc, tuple);
}
/* ----------------------------------------------------------------
- * ExecDelete
+ * ExecDelete
*
- * DELETE is like append, we delete the tuple and its
- * index tuples.
+ * DELETE is like append, we delete the tuple and its
+ * index tuples.
* ----------------------------------------------------------------
*/
static void
-ExecDelete(TupleTableSlot *slot,
- ItemPointer tupleid,
- EState *estate)
+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;
-
- /* BEFORE ROW DELETE Triggers */
- if ( resultRelationDesc->trigdesc &&
- resultRelationDesc->trigdesc->n_before_row[TRIGGER_EVENT_DELETE] > 0 )
- {
- bool dodelete;
-
- dodelete = ExecBRDeleteTriggers (resultRelationDesc, tupleid);
-
- if ( !dodelete ) /* "do nothing" */
- return;
- }
-
- /* ----------------
- * delete the tuple
- * ----------------
- */
- if ( heap_delete(resultRelationDesc, /* relation desc */
- tupleid) ) /* item pointer to tuple */
- return;
-
- IncrDeleted();
- (estate->es_processed)++;
-
- /* ----------------
- * 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
- * ----------------
- */
-
- /* AFTER ROW DELETE Triggers */
- if ( resultRelationDesc->trigdesc &&
- resultRelationDesc->trigdesc->n_after_row[TRIGGER_EVENT_DELETE] > 0 )
- ExecARDeleteTriggers (resultRelationDesc, tupleid);
+ RelationInfo *resultRelationInfo;
+ Relation resultRelationDesc;
+
+ /* ----------------
+ * get the result relation information
+ * ----------------
+ */
+ resultRelationInfo = estate->es_result_relation_info;
+ resultRelationDesc = resultRelationInfo->ri_RelationDesc;
+
+ /* BEFORE ROW DELETE Triggers */
+ if (resultRelationDesc->trigdesc &&
+ resultRelationDesc->trigdesc->n_before_row[TRIGGER_EVENT_DELETE] > 0)
+ {
+ bool dodelete;
+
+ dodelete = ExecBRDeleteTriggers(resultRelationDesc, tupleid);
+
+ if (!dodelete) /* "do nothing" */
+ return;
+ }
+
+ /* ----------------
+ * delete the tuple
+ * ----------------
+ */
+ if (heap_delete(resultRelationDesc, /* relation desc */
+ tupleid)) /* item pointer to tuple */
+ return;
+
+ IncrDeleted();
+ (estate->es_processed)++;
+
+ /* ----------------
+ * 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
+ * ----------------
+ */
+
+ /* AFTER ROW DELETE Triggers */
+ if (resultRelationDesc->trigdesc &&
+ resultRelationDesc->trigdesc->n_after_row[TRIGGER_EVENT_DELETE] > 0)
+ ExecARDeleteTriggers(resultRelationDesc, tupleid);
}
/* ----------------------------------------------------------------
- * ExecReplace
+ * 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..
+ * 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)
+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
- * ----------------
- */
-
- /* BEFORE ROW UPDATE Triggers */
- if ( resultRelationDesc->trigdesc &&
- resultRelationDesc->trigdesc->n_before_row[TRIGGER_EVENT_UPDATE] > 0 )
- {
- HeapTuple newtuple;
-
- newtuple = ExecBRUpdateTriggers (resultRelationDesc, tupleid, tuple);
-
- if ( newtuple == NULL ) /* "do nothing" */
- return;
-
- if ( newtuple != tuple ) /* modified by Trigger(s) */
- {
- Assert ( slot->ttc_shouldFree );
- pfree (tuple);
- slot->val = tuple = newtuple;
- }
- }
-
- /* ----------------
- * Check the constraints of a tuple
- * ----------------
- */
-
- if ( resultRelationDesc->rd_att->constr )
- {
- HeapTuple newtuple;
-
- newtuple = ExecConstraints ("ExecReplace", resultRelationDesc, tuple);
-
- if ( newtuple != tuple ) /* modified by DEFAULT */
- {
- Assert ( slot->ttc_shouldFree );
- pfree (tuple);
- slot->val = tuple = newtuple;
- }
- }
-
- /* ----------------
- * 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();
- (estate->es_processed)++;
-
- /* ----------------
- * 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, true);
- }
-
- /* AFTER ROW UPDATE Triggers */
- if ( resultRelationDesc->trigdesc &&
- resultRelationDesc->trigdesc->n_after_row[TRIGGER_EVENT_UPDATE] > 0 )
- ExecARUpdateTriggers (resultRelationDesc, tupleid, tuple);
+ 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
+ * ----------------
+ */
+
+ /* BEFORE ROW UPDATE Triggers */
+ if (resultRelationDesc->trigdesc &&
+ resultRelationDesc->trigdesc->n_before_row[TRIGGER_EVENT_UPDATE] > 0)
+ {
+ HeapTuple newtuple;
+
+ newtuple = ExecBRUpdateTriggers(resultRelationDesc, tupleid, tuple);
+
+ if (newtuple == NULL) /* "do nothing" */
+ return;
+
+ if (newtuple != tuple) /* modified by Trigger(s) */
+ {
+ Assert(slot->ttc_shouldFree);
+ pfree(tuple);
+ slot->val = tuple = newtuple;
+ }
+ }
+
+ /* ----------------
+ * Check the constraints of a tuple
+ * ----------------
+ */
+
+ if (resultRelationDesc->rd_att->constr)
+ {
+ HeapTuple newtuple;
+
+ newtuple = ExecConstraints("ExecReplace", resultRelationDesc, tuple);
+
+ if (newtuple != tuple) /* modified by DEFAULT */
+ {
+ Assert(slot->ttc_shouldFree);
+ pfree(tuple);
+ slot->val = tuple = newtuple;
+ }
+ }
+
+ /* ----------------
+ * 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();
+ (estate->es_processed)++;
+
+ /* ----------------
+ * 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, true);
+ }
+
+ /* AFTER ROW UPDATE Triggers */
+ if (resultRelationDesc->trigdesc &&
+ resultRelationDesc->trigdesc->n_after_row[TRIGGER_EVENT_UPDATE] > 0)
+ ExecARUpdateTriggers(resultRelationDesc, tupleid, tuple);
}
-static HeapTuple
-ExecAttrDefault (Relation rel, HeapTuple tuple)
+static HeapTuple
+ExecAttrDefault(Relation rel, HeapTuple tuple)
{
- int ndef = rel->rd_att->constr->num_defval;
- AttrDefault *attrdef = rel->rd_att->constr->defval;
- ExprContext *econtext = makeNode (ExprContext);
- HeapTuple newtuple;
- Node *expr;
- bool isnull;
- bool isdone;
- Datum val;
- Datum *replValue = NULL;
- char *replNull = NULL;
- char *repl = NULL;
- int i;
-
- 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 = NULL; /* param list info */
- econtext->ecxt_range_table = NULL; /* range table */
- for (i = 0; i < ndef; i++)
- {
- if ( !heap_attisnull (tuple, attrdef[i].adnum) )
- continue;
- expr = (Node*) stringToNode (attrdef[i].adbin);
-
- val = ExecEvalExpr (expr, econtext, &isnull, &isdone);
-
- pfree (expr);
-
- if ( isnull )
- continue;
-
- if ( repl == NULL )
- {
- repl = (char*) palloc (rel->rd_att->natts * sizeof (char));
- replNull = (char*) palloc (rel->rd_att->natts * sizeof (char));
- replValue = (Datum*) palloc (rel->rd_att->natts * sizeof (Datum));
- memset (repl, ' ', rel->rd_att->natts * sizeof (char));
- }
-
- repl[attrdef[i].adnum - 1] = 'r';
- replNull[attrdef[i].adnum - 1] = ' ';
- replValue[attrdef[i].adnum - 1] = val;
-
- }
-
- pfree (econtext);
-
- if ( repl == NULL )
- return (tuple);
-
- newtuple = heap_modifytuple (tuple, InvalidBuffer, rel, replValue, replNull, repl);
-
- pfree (repl);
- pfree (replNull);
- pfree (replValue);
-
- return (newtuple);
-
+ int ndef = rel->rd_att->constr->num_defval;
+ AttrDefault *attrdef = rel->rd_att->constr->defval;
+ ExprContext *econtext = makeNode(ExprContext);
+ HeapTuple newtuple;
+ Node *expr;
+ bool isnull;
+ bool isdone;
+ Datum val;
+ Datum *replValue = NULL;
+ char *replNull = NULL;
+ char *repl = NULL;
+ int i;
+
+ 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 = NULL; /* param list info */
+ econtext->ecxt_range_table = NULL; /* range table */
+ for (i = 0; i < ndef; i++)
+ {
+ if (!heap_attisnull(tuple, attrdef[i].adnum))
+ continue;
+ expr = (Node *) stringToNode(attrdef[i].adbin);
+
+ val = ExecEvalExpr(expr, econtext, &isnull, &isdone);
+
+ pfree(expr);
+
+ if (isnull)
+ continue;
+
+ if (repl == NULL)
+ {
+ repl = (char *) palloc(rel->rd_att->natts * sizeof(char));
+ replNull = (char *) palloc(rel->rd_att->natts * sizeof(char));
+ replValue = (Datum *) palloc(rel->rd_att->natts * sizeof(Datum));
+ memset(repl, ' ', rel->rd_att->natts * sizeof(char));
+ }
+
+ repl[attrdef[i].adnum - 1] = 'r';
+ replNull[attrdef[i].adnum - 1] = ' ';
+ replValue[attrdef[i].adnum - 1] = val;
+
+ }
+
+ pfree(econtext);
+
+ if (repl == NULL)
+ return (tuple);
+
+ newtuple = heap_modifytuple(tuple, InvalidBuffer, rel, replValue, replNull, repl);
+
+ pfree(repl);
+ pfree(replNull);
+ pfree(replValue);
+
+ return (newtuple);
+
}
-static char *
-ExecRelCheck (Relation rel, HeapTuple tuple)
+static char *
+ExecRelCheck(Relation rel, HeapTuple tuple)
{
- int ncheck = rel->rd_att->constr->num_check;
- ConstrCheck *check = rel->rd_att->constr->check;
- ExprContext *econtext = makeNode (ExprContext);
- TupleTableSlot *slot = makeNode (TupleTableSlot);
- RangeTblEntry *rte = makeNode (RangeTblEntry);
- List *rtlist;
- List *qual;
- bool res;
- int i;
-
- slot->val = tuple;
- slot->ttc_shouldFree = false;
- slot->ttc_descIsNew = true;
- slot->ttc_tupleDescriptor = rel->rd_att;
- slot->ttc_buffer = InvalidBuffer;
- slot->ttc_whichplan = -1;
- rte->relname = nameout (&(rel->rd_rel->relname));
- rte->timeRange = NULL;
- rte->refname = rte->relname;
- rte->relid = rel->rd_id;
- rte->inh = false;
- rte->archive = false;
- rte->inFromCl = true;
- rte->timeQual = NULL;
- rtlist = lcons (rte, NIL);
- econtext->ecxt_scantuple = slot; /* scan tuple slot */
- econtext->ecxt_innertuple = NULL; /* inner tuple slot */
- econtext->ecxt_outertuple = NULL; /* outer tuple slot */
- econtext->ecxt_relation = rel; /* relation */
- econtext->ecxt_relid = 0; /* relid */
- econtext->ecxt_param_list_info = NULL; /* param list info */
- econtext->ecxt_range_table = rtlist; /* range table */
-
- for (i = 0; i < ncheck; i++)
- {
- qual = (List*) stringToNode (check[i].ccbin);
-
- res = ExecQual (qual, econtext);
-
- pfree (qual);
-
- if ( !res )
- return (check[i].ccname);
- }
-
- pfree (slot);
- pfree (rte->relname);
- pfree (rte);
- pfree (rtlist);
- pfree (econtext);
-
- return ((char *) NULL);
-
+ int ncheck = rel->rd_att->constr->num_check;
+ ConstrCheck *check = rel->rd_att->constr->check;
+ ExprContext *econtext = makeNode(ExprContext);
+ TupleTableSlot *slot = makeNode(TupleTableSlot);
+ RangeTblEntry *rte = makeNode(RangeTblEntry);
+ List *rtlist;
+ List *qual;
+ bool res;
+ int i;
+
+ slot->val = tuple;
+ slot->ttc_shouldFree = false;
+ slot->ttc_descIsNew = true;
+ slot->ttc_tupleDescriptor = rel->rd_att;
+ slot->ttc_buffer = InvalidBuffer;
+ slot->ttc_whichplan = -1;
+ rte->relname = nameout(&(rel->rd_rel->relname));
+ rte->timeRange = NULL;
+ rte->refname = rte->relname;
+ rte->relid = rel->rd_id;
+ rte->inh = false;
+ rte->archive = false;
+ rte->inFromCl = true;
+ rte->timeQual = NULL;
+ rtlist = lcons(rte, NIL);
+ econtext->ecxt_scantuple = slot; /* scan tuple slot */
+ econtext->ecxt_innertuple = NULL; /* inner tuple slot */
+ econtext->ecxt_outertuple = NULL; /* outer tuple slot */
+ econtext->ecxt_relation = rel; /* relation */
+ econtext->ecxt_relid = 0; /* relid */
+ econtext->ecxt_param_list_info = NULL; /* param list info */
+ econtext->ecxt_range_table = rtlist; /* range table */
+
+ for (i = 0; i < ncheck; i++)
+ {
+ qual = (List *) stringToNode(check[i].ccbin);
+
+ res = ExecQual(qual, econtext);
+
+ pfree(qual);
+
+ if (!res)
+ return (check[i].ccname);
+ }
+
+ pfree(slot);
+ pfree(rte->relname);
+ pfree(rte);
+ pfree(rtlist);
+ pfree(econtext);
+
+ return ((char *) NULL);
+
}
HeapTuple
-ExecConstraints (char *caller, Relation rel, HeapTuple tuple)
+ExecConstraints(char *caller, Relation rel, HeapTuple tuple)
{
- HeapTuple newtuple = tuple;
-
- Assert ( rel->rd_att->constr );
-
- if ( rel->rd_att->constr->num_defval > 0 )
- newtuple = tuple = ExecAttrDefault (rel, tuple);
-
- if ( rel->rd_att->constr->has_not_null )
- {
- int attrChk;
-
- for (attrChk = 1; attrChk <= rel->rd_att->natts; attrChk++)
+ HeapTuple newtuple = tuple;
+
+ Assert(rel->rd_att->constr);
+
+ if (rel->rd_att->constr->num_defval > 0)
+ newtuple = tuple = ExecAttrDefault(rel, tuple);
+
+ if (rel->rd_att->constr->has_not_null)
{
- if (rel->rd_att->attrs[attrChk-1]->attnotnull && heap_attisnull (tuple,attrChk))
- elog(WARN,"%s: Fail to add null value in not null attribute %s",
- caller, rel->rd_att->attrs[attrChk-1]->attname.data);
- }
- }
-
- if ( rel->rd_att->constr->num_check > 0 )
- {
- char *failed;
-
- if ( ( failed = ExecRelCheck (rel, tuple) ) != NULL )
- elog(WARN,"%s: rejected due to CHECK constraint %s", caller, failed);
- }
-
- return (newtuple);
+ int attrChk;
+
+ for (attrChk = 1; attrChk <= rel->rd_att->natts; attrChk++)
+ {
+ if (rel->rd_att->attrs[attrChk - 1]->attnotnull && heap_attisnull(tuple, attrChk))
+ elog(WARN, "%s: Fail to add null value in not null attribute %s",
+ caller, rel->rd_att->attrs[attrChk - 1]->attname.data);
+ }
+ }
+
+ if (rel->rd_att->constr->num_check > 0)
+ {
+ char *failed;
+
+ if ((failed = ExecRelCheck(rel, tuple)) != NULL)
+ elog(WARN, "%s: rejected due to CHECK constraint %s", caller, failed);
+ }
+
+ return (newtuple);
}
diff --git a/src/backend/executor/execProcnode.c b/src/backend/executor/execProcnode.c
index f9e18d948f0..89caefd162e 100644
--- a/src/backend/executor/execProcnode.c
+++ b/src/backend/executor/execProcnode.c
@@ -1,75 +1,75 @@
/*-------------------------------------------------------------------------
*
* 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..
+ * 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.2 1996/10/31 10:11:27 scrappy Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/execProcnode.c,v 1.3 1997/09/07 04:41:19 momjian 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
+ * 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:
+ * 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.
*
- * 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.
+ * 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:
*
- * * 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.
+ * retrieve (DEPT.no_emps, EMP.age)
+ * where EMP.name = DEPT.mgr and
+ * DEPT.name = "shoe"
*
- * * 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().
+ * Suppose the planner gives us the following plan:
*
- * 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.
+ * 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 "postgres.h"
@@ -91,389 +91,393 @@
#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
+ * 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)
+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;
-
+ bool result;
+
/* ----------------
- * materialization nodes
+ * do nothing when we get to the end
+ * of a leaf on tree.
* ----------------
*/
- 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;
+ 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.
+ * ExecProcNode
+ *
+ * Initial States:
+ * the query tree must be initialized once by calling ExecInit.
* ----------------------------------------------------------------
*/
TupleTableSlot *
-ExecProcNode(Plan *node, Plan *parent)
+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;
-
+ TupleTableSlot *result;
+
/* ----------------
- * materialization nodes
+ * deal with NULL 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;
+ 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)
+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);
+ if (node == (Plan *) NULL)
+ return 0;
- 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;
+ 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'.
+/* ----------------------------------------------------------------
+ * ExecEndNode
*
- * 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.
- * ----------------------------------------------------------------
+ * 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)
+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
+ * do nothing when we get to the end
+ * of a leaf on tree.
* ----------------
*/
- 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;
- }
+ 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
index 536b0068342..7b8cb18ef25 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -1,32 +1,32 @@
/*-------------------------------------------------------------------------
*
* execQual.c--
- * Routines to evaluate qualification and targetlist expressions
+ * 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.12 1997/08/19 21:31:03 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.13 1997/09/07 04:41:20 momjian 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
+ * 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.
+ * 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
+ * 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 <string.h>
@@ -56,7 +56,7 @@
#include "utils/mcxt.h"
/* ----------------
- * externs and constants
+ * externs and constants
* ----------------
*/
@@ -65,1492 +65,1577 @@
* Currently only used by ExecHashGetBucket and set only by ExecMakeVarConst
* and by ExecEvalArrayRef.
*/
-bool execConstByVal;
-int execConstLen;
+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);
-static Datum ExecEvalAnd(Expr *andExpr, ExprContext *econtext, bool *isNull);
-static Datum ExecEvalFunc(Expr *funcClause, ExprContext *econtext,
- bool *isNull, bool *isDone);
-static void ExecEvalFuncArgs(FunctionCachePtr fcache, ExprContext *econtext,
- List *argList, Datum argV[], bool *argIsDone);
-static Datum ExecEvalNot(Expr *notclause, ExprContext *econtext, bool *isNull);
-static Datum ExecEvalOper(Expr *opClause, ExprContext *econtext,
- bool *isNull);
-static Datum ExecEvalOr(Expr *orExpr, ExprContext *econtext, bool *isNull);
-static Datum ExecEvalVar(Var *variable, ExprContext *econtext, bool *isNull);
-static Datum ExecMakeFunctionResult(Node *node, List *arguments,
- ExprContext *econtext, bool *isNull, bool *isDone);
-static bool ExecQualClause(Node *clause, ExprContext *econtext);
+static Datum ExecEvalAggreg(Aggreg * agg, ExprContext * econtext, bool * isNull);
+static Datum
+ExecEvalArrayRef(ArrayRef * arrayRef, ExprContext * econtext,
+ bool * isNull, bool * isDone);
+static Datum ExecEvalAnd(Expr * andExpr, ExprContext * econtext, bool * isNull);
+static Datum
+ExecEvalFunc(Expr * funcClause, ExprContext * econtext,
+ bool * isNull, bool * isDone);
+static void
+ExecEvalFuncArgs(FunctionCachePtr fcache, ExprContext * econtext,
+ List * argList, Datum argV[], bool * argIsDone);
+static Datum ExecEvalNot(Expr * notclause, ExprContext * econtext, bool * isNull);
+static Datum
+ExecEvalOper(Expr * opClause, ExprContext * econtext,
+ bool * isNull);
+static Datum ExecEvalOr(Expr * orExpr, ExprContext * econtext, bool * isNull);
+static Datum ExecEvalVar(Var * variable, ExprContext * econtext, bool * isNull);
+static Datum
+ExecMakeFunctionResult(Node * node, List * arguments,
+ ExprContext * econtext, bool * isNull, bool * isDone);
+static bool ExecQualClause(Node * clause, ExprContext * econtext);
/* --------------------------------
- * ExecEvalArrayRef
+ * 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.
+ * 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)
+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;
-
- *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;
+ bool dummy;
+ int i = 0,
+ j = 0;
+ ArrayType *array_scanner;
+ List *upperIndexpr,
+ *lowerIndexpr;
+ Node *assgnexpr;
+ List *elt;
+ IntArray upper,
+ lower;
+ int *lIndex;
+ char *dataPtr;
+
+ *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;
}
- 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;
- execConstByVal = arrayRef->refelembyval;
- execConstLen = arrayRef->refelemlength;
+
+ assgnexpr = arrayRef->refassgnexpr;
+ if (assgnexpr != NULL)
+ {
+ dataPtr = (char *) ExecEvalExpr((Node *)
+ assgnexpr, econtext,
+ isNull, &dummy);
+ if (*isNull)
+ return (Datum) NULL;
+ execConstByVal = arrayRef->refelembyval;
+ execConstLen = arrayRef->refelemlength;
+ 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);
+ }
+ execConstByVal = arrayRef->refelembyval;
+ execConstLen = arrayRef->refelemlength;
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);
- }
- execConstByVal = arrayRef->refelembyval;
- execConstLen = arrayRef->refelemlength;
- 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);
+ 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.
+ * 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)
+static Datum
+ExecEvalAggreg(Aggreg * agg, ExprContext * econtext, bool * isNull)
{
-
- *isNull = econtext->ecxt_nulls[agg->aggno];
- return econtext->ecxt_values[agg->aggno];
+
+ *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.
+ * ExecEvalVar
+ *
+ * Returns a Datum whose value is the value of a range
+ * variable with respect to given expression context.
+ *
+ *
+ * As an entry condition, we expect that the the datatype the
+ * plan expects to get (as told by our "variable" argument) is in
+ * fact the datatype of the attribute the plan says to fetch (as
+ * seen in the current context, identified by our "econtext"
+ * argument).
+ *
+ * If we fetch a Type A attribute and Caller treats it as if it
+ * were Type B, there will be undefined results (e.g. crash).
+ * One way these might mismatch now is that we're accessing a
+ * catalog class and the type information in the pg_attribute
+ * class does not match the hardcoded pg_attribute information
+ * (in pg_attribute.h) for the class in question.
*
+ * We have an Assert to make sure this entry condition is met.
*
- * As an entry condition, we expect that the the datatype the
- * plan expects to get (as told by our "variable" argument) is in
- * fact the datatype of the attribute the plan says to fetch (as
- * seen in the current context, identified by our "econtext"
- * argument).
- *
- * If we fetch a Type A attribute and Caller treats it as if it
- * were Type B, there will be undefined results (e.g. crash).
- * One way these might mismatch now is that we're accessing a
- * catalog class and the type information in the pg_attribute
- * class does not match the hardcoded pg_attribute information
- * (in pg_attribute.h) for the class in question.
- *
- * We have an Assert to make sure this entry condition is met.
- *
* ---------------------------------------------------------------- */
-static Datum
-ExecEvalVar(Var *variable, ExprContext *econtext, bool *isNull)
+static 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;
-
- /* (See prolog for explanation of this Assert) */
- Assert(attnum <= 0 ||
- (attnum - 1 <= tuple_type->natts - 1 &&
- tuple_type->attrs[attnum-1] != NULL &&
- variable->vartype == tuple_type->attrs[attnum-1]->atttypid))
-
- /*
- * 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)
+ 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;
+
+ /* (See prolog for explanation of this Assert) */
+ Assert(attnum <= 0 ||
+ (attnum - 1 <= tuple_type->natts - 1 &&
+ tuple_type->attrs[attnum - 1] != NULL &&
+ variable->vartype == tuple_type->attrs[attnum - 1]->atttypid))
+
+ /*
+ * 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;
+ 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) {
+
+ 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? */
+
/* ----------------
- * 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
+ * return null if att is null
* ----------------
*/
- 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;
+ 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
+ * 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").
+ * 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
+ * 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.
+ * 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)
+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) {
+
+ 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 for an entry in 'paramList' that matches
- * the `expression'.
+ * search the list with the parameter info to find a matching name. An
+ * entry with an InvalidName denotes the last element in the array.
*/
- 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");
+ 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);
}
- }
- break;
- default:
+ if (!matchFound)
+ {
+ paramList++;
+ }
+ } /* while */
+ } /* if */
+
+ if (!matchFound)
+ {
+
/*
- * oops! this is not supposed to happen!
+ * ooops! we couldn't find this parameter in the parameter list.
+ * Signal an error
*/
- elog(WARN, "ExecEvalParam: invalid paramkind %d",
- thisParameterKind);
- }
- if (! matchFound) {
- paramList++;
- }
- } /*while*/
- } /*if*/
-
- if (!matchFound) {
+ elog(WARN, "ExecEvalParam: Unknown value for parameter %s",
+ thisParameterName);
+ }
+
/*
- * ooops! we couldn't find this parameter
- * in the parameter list. Signal an error
+ * return the value.
*/
- elog(WARN, "ExecEvalParam: Unknown value for parameter %s",
- thisParameterName);
- }
-
- /*
- * return the value.
- */
- if (paramList->isnull)
+ if (paramList->isnull)
{
- *isNull = true;
- return (Datum)NULL;
+ *isNull = true;
+ return (Datum) NULL;
}
-
- if (expression->param_tlist != NIL)
+
+ 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;
+ 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);
+ return (paramList->value);
}
/* ----------------------------------------------------------------
- * ExecEvalOper / ExecEvalFunc support routines
+ * ExecEvalOper / ExecEvalFunc support routines
* ----------------------------------------------------------------
*/
/* ----------------
- * GetAttributeByName
- * GetAttributeByNum
+ * 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().
+ * 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().
* ----------------
*/
#ifdef NOT_USED
-static char *
-GetAttributeByNum(TupleTableSlot *slot,
- AttrNumber attrno,
- bool *isNull)
+static 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))
+ 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;
+ *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;
+
+ retval = (Datum)
+ heap_getattr(slot->val,
+ slot->ttc_buffer,
+ attrno,
+ slot->ttc_tupleDescriptor,
+ isNull);
+ if (*isNull)
+ return (char *) NULL;
+ return (char *) retval;
}
+
#endif
/* XXX char16 name for catalogs */
#ifdef NOT_USED
-char *
-att_by_num(TupleTableSlot *slot,
- AttrNumber attrno,
- bool *isNull)
+char *
+att_by_num(TupleTableSlot * slot,
+ AttrNumber attrno,
+ bool * isNull)
{
- return(GetAttributeByNum(slot, attrno, isNull));
+ return (GetAttributeByNum(slot, attrno, isNull));
}
+
#endif
-char *
-GetAttributeByName(TupleTableSlot *slot, char *attname, bool *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))
+ 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;
+ *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;
+
+ 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;
+
+ 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 */
#ifdef NOT_USED
-char *
-att_by_name(TupleTableSlot *slot, char *attname, bool *isNull)
+char *
+att_by_name(TupleTableSlot * slot, char *attname, bool * isNull)
{
- return(GetAttributeByName(slot, attname, isNull));
+ return (GetAttributeByName(slot, attname, isNull));
}
+
#endif
static void
ExecEvalFuncArgs(FunctionCachePtr fcache,
- ExprContext *econtext,
- List *argList,
- Datum argV[],
- bool *argIsDone)
+ 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++;
- }
+ 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
+ * ExecMakeFunctionResult
* ----------------
*/
-static Datum
-ExecMakeFunctionResult(Node *node,
- List *arguments,
- ExprContext *econtext,
- bool *isNull,
- bool *isDone)
+static 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))
+ 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;
+ funcNode = (Func *) node;
+ fcache = funcNode->func_fcache;
}
- else
+ else
{
- operNode = (Oper *)node;
- fcache = operNode->op_fcache;
+ 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.
+
+ /* ----------------
+ * 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->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;
+ 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;
+ }
}
- }
-
- /* ----------------
- * 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 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 ((*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 (((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;
+ }
}
- 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;
- }
+
+ /* ----------------
+ * 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;
}
- return result;
- }
- else
+ 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));
+ 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.
+ * 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.
+ * 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
+ * ExecEvalOper
* ----------------------------------------------------------------
*/
-static Datum
-ExecEvalOper(Expr *opClause, ExprContext *econtext, bool *isNull)
+static 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);
+ 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
+ * ExecEvalFunc
* ----------------------------------------------------------------
*/
-static Datum
-ExecEvalFunc(Expr *funcClause,
- ExprContext *econtext,
- bool *isNull,
- bool *isDone)
+static 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);
+ 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.
+ * ExecEvalNot
+ * ExecEvalOr
+ * ExecEvalAnd
*
- * 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...
+ * 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...
* ----------------------------------------------------------------
*/
-static Datum
-ExecEvalNot(Expr *notclause, ExprContext *econtext, bool *isNull)
+static 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;
-}
+ Datum expr_value;
+ Node *clause;
+ bool isDone;
+
+ clause = lfirst(notclause->args);
-/* ----------------------------------------------------------------
- * ExecEvalOr
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalOr(Expr *orExpr, ExprContext *econtext, bool *isNull)
-{
- List *clauses;
- List *clause;
- bool isDone;
- bool IsNull;
- Datum const_value = 0;
-
- 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.
+ * 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);
-
+ expr_value = ExecEvalExpr(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 the expression evaluates to null, then we just
+ * cascade the null back to whoever called us.
* ----------------
*/
if (*isNull)
- IsNull = *isNull;
-
+ return expr_value;
+
/* ----------------
- * if we have a true result, then we return it.
+ * evaluation of 'not' is simple.. expr is false, then
+ * return 'true' and vice versa.
* ----------------
*/
- if (DatumGetInt32(const_value) != 0)
- return const_value;
- }
-
- /* IsNull is true if at least one clause evaluated to NULL */
- *isNull = IsNull;
- return const_value;
+ if (DatumGetInt32(expr_value) == 0)
+ return (Datum) true;
+
+ return (Datum) false;
}
/* ----------------------------------------------------------------
- * ExecEvalAnd
+ * ExecEvalOr
* ----------------------------------------------------------------
*/
-static Datum
-ExecEvalAnd(Expr *andExpr, ExprContext *econtext, bool *isNull)
+static Datum
+ExecEvalOr(Expr * orExpr, ExprContext * econtext, bool * isNull)
{
- List *clauses;
- List *clause;
- Datum const_value = 0;
- 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);
-
+ List *clauses;
+ List *clause;
+ bool isDone;
+ bool IsNull;
+ Datum const_value = 0;
+
+ IsNull = false;
+ clauses = orExpr->args;
+
/* ----------------
- * 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.
+ * 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 -
* ----------------
*/
- if (*isNull)
- IsNull = *isNull;
-
+ 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
+ * ----------------------------------------------------------------
+ */
+static Datum
+ExecEvalAnd(Expr * andExpr, ExprContext * econtext, bool * isNull)
+{
+ List *clauses;
+ List *clause;
+ Datum const_value = 0;
+ bool isDone;
+ bool IsNull;
+
+ IsNull = false;
+
+ clauses = andExpr->args;
+
/* ----------------
- * if we have a false result, then we return it, since the
- * conjunction must be false.
+ * 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.
* ----------------
*/
- if (DatumGetInt32(const_value) == 0)
- return const_value;
- }
-
- *isNull = IsNull;
- return const_value;
+ 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.
+/* ----------------------------------------------------------------
+ * ExecEvalExpr
+ *
+ * Recursively evaluate a targetlist or qualification expression.
*
- * This routine is an inner loop routine and should be as fast
- * as possible.
+ * 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.
+ * 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)
+ExecEvalExpr(Node * expression,
+ ExprContext * econtext,
+ bool * isNull,
+ bool * isDone)
{
- Datum retDatum = 0;
-
- *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;
+ Datum retDatum = 0;
+
+ *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;
+ elog(WARN, "ExecEvalExpr: unknown expression type");
+ break;
}
- break;
- }
- default:
- elog(WARN, "ExecEvalExpr: unknown expression type");
- break;
- }
-
- return retDatum;
+
+ return retDatum;
}
/* ----------------------------------------------------------------
- * ExecQual / ExecTargetList
+ * ExecQual / ExecTargetList
* ----------------------------------------------------------------
*/
/* ----------------------------------------------------------------
- * ExecQualClause
+ * 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)
+ * 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)
* ----------------------------------------------------------------
*/
-static bool
-ExecQualClause(Node *clause, ExprContext *econtext)
+static 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;
+ 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).
+ * ExecQual
+ *
+ * Evaluates a conjunctive boolean expression and returns t
+ * iff none of the subexpressions are false (or null).
* ----------------------------------------------------------------
*/
bool
-ExecQual(List *qual, ExprContext *econtext)
+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);
+ 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)
- 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;
+ return false;
+
+ return true;
}
int
-ExecTargetListLength(List *targetlist)
+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;
+ 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.
+ * 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)
+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) {
+ 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;
+
/* ----------------
- * 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
+ * debugging stuff
* ----------------
*/
- 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) {
+ EV_printf("ExecTargetList: tl is ");
+ EV_nodeDisplay(targetlist);
+ EV_printf("\n");
+
/* ----------------
- * 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
+ * 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.
* ----------------
*/
- 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 (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
{
-#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];
- }
+ 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;
+
+ /* ----------------
+ * 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.
+ * 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
+ * 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)
+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);
-}
+ 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
index 8e69f491731..6ea50bb2a93 100644
--- a/src/backend/executor/execScan.c
+++ b/src/backend/executor/execScan.c
@@ -1,17 +1,17 @@
/*-------------------------------------------------------------------------
*
* 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.
+ * 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.3 1997/07/28 00:53:51 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/execScan.c,v 1.4 1997/09/07 04:41:23 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -23,117 +23,123 @@
#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.
+ * ExecScan
*
- * May need to put startmmgr and endmmgr in here.
+ * 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 */
+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);
+ CommonScanState *scanstate;
+ EState *estate;
+ List *qual;
+ bool isDone;
+
+ TupleTableSlot *slot;
+ TupleTableSlot *resultSlot;
+ HeapTuple newTuple;
+
+ ExprContext *econtext;
+ ProjectionInfo *projInfo;
+
/* ----------------
- * 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.
+ * initialize misc variables
* ----------------
*/
- if (TupIsNull(slot)) return slot;
-
+ newTuple = NULL;
+ slot = NULL;
+
+ estate = node->plan.state;
+ scanstate = node->scanstate;
+
/* ----------------
- * place the current tuple into the expr context
+ * get the expression context
* ----------------
*/
- econtext->ecxt_scantuple = slot;
-
+ econtext = scanstate->cstate.cs_ExprContext;
+
/* ----------------
- * check that the current tuple satisfies the qual-clause
- * if our qualification succeeds then we
- * leave the loop.
+ * 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;
+ }
- /* 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;
+ /* ----------------
+ * 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;
+ resultSlot = ExecProject(projInfo, &isDone);
+ scanstate->cstate.cs_TupFromTlist = !isDone;
- return resultSlot;
+ return resultSlot;
}
-
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 0d5e7fda9fb..287f75699af 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -1,119 +1,119 @@
/*-------------------------------------------------------------------------
*
* 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 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.).
*
- * 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.6 1997/08/19 21:31:05 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.7 1997/09/07 04:41:24 momjian Exp $
*
*-------------------------------------------------------------------------
*/
/*
* INTERFACE ROUTINES
*
- * TABLE CREATE/DELETE
- * ExecCreateTupleTable - create a new tuple table
- * ExecDestroyTupleTable - destroy a table
+ * TABLE CREATE/DELETE
+ * ExecCreateTupleTable - create a new tuple table
+ * ExecDestroyTupleTable - destroy a table
*
- * SLOT RESERVERATION
- * ExecAllocTableSlot - find an available slot in the 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 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
+ * 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 /
+ * 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
+ * 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.
+ * 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.
+ * 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.
+ * - 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.
+ * 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.
+ * - 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.
+ * - 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.
+ * 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
+ * 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
+ * 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
+ * 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.
+ * The tuple table stuff depends on the executor/tuptable.h macros,
+ * and the TupleTableSlot node in execnodes.h.
*
*/
#include <string.h>
@@ -131,902 +131,938 @@
#include "parser/catalog_utils.h"
#include "catalog/pg_type.h"
-static TupleTableSlot *NodeGetResultTupleSlot(Plan *node);
+static TupleTableSlot *NodeGetResultTupleSlot(Plan * node);
/* ----------------------------------------------------------------
- * tuple table create/delete functions
+ * tuple table create/delete functions
* ----------------------------------------------------------------
*/
/* --------------------------------
- * ExecCreateTupleTable
+ * 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 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.
+ * 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 /* 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;
+ 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
+ * 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().
+ * 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 */
+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);
+ 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);
-
+
+ /* ----------------
+ * finally free the tuple array and the table itself.
+ * ----------------
+ */
+ pfree(array);
+ pfree(table);
+
}
/* ----------------------------------------------------------------
- * tuple table slot reservation functions
+ * tuple table slot reservation functions
* ----------------------------------------------------------------
*/
/* --------------------------------
- * ExecAllocTableSlot
+ * 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).
+ * 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 */
+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);
+ 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
*
- * pfree(table->array);
- * table->array = (Pointer) palloc(newsize * TableSlotSize);
- * bzero(table->array, newsize * TableSlotSize);
- * table->size = newsize;
+ * 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
+ * ----------------
*/
- 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]);
+ 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
+ * tuple table slot accessor functions
* ----------------------------------------------------------------
*/
/* --------------------------------
- * ExecStoreTuple
+ * 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.
+ * 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. */
+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;
+ /* ----------------
+ * 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
+ * ExecClearTuple
*
- * This function is used to clear out a slot in the tuple table.
+ * 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 */
+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) {
+ HeapTuple oldtuple; /* prior contents of slot */
+
/* ----------------
- * 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
+ * sanity checks
* ----------------
*/
- 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;
+ 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
+ * 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.
+ * 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.
* --------------------------------
*/
#ifdef NOT_USED
-bool /* return: slot policy */
-ExecSlotPolicy(TupleTableSlot* slot) /* slot to inspect */
+bool /* return: slot policy */
+ExecSlotPolicy(TupleTableSlot * slot) /* slot to inspect */
{
- return slot->ttc_shouldFree;
+ return slot->ttc_shouldFree;
}
+
#endif
/* --------------------------------
- * ExecSetSlotPolicy
+ * 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.
+ * 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 /* 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;
+ bool old_shouldFree = slot->ttc_shouldFree;
- return old_shouldFree;
+ slot->ttc_shouldFree = shouldFree;
+
+ return old_shouldFree;
}
/* --------------------------------
- * ExecSlotDescriptor
+ * ExecSlotDescriptor
*
- * This function is used to get the tuple descriptor associated
- * with the slot's tuple.
+ * 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
+ * ExecSetSlotDescriptor
*
- * This function is used to set the tuple descriptor associated
- * with the slot's tuple.
+ * 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 /* return: old slot tuple descriptor */
+ExecSetSlotDescriptor(TupleTableSlot * slot, /* slot to change */
+ TupleDesc tupdesc) /* tuple descriptor */
{
- TupleDesc old_tupdesc = slot->ttc_tupleDescriptor;
+ TupleDesc old_tupdesc = slot->ttc_tupleDescriptor;
- slot->ttc_tupleDescriptor = tupdesc;
- return old_tupdesc;
+ slot->ttc_tupleDescriptor = tupdesc;
+ return old_tupdesc;
}
/* --------------------------------
- * ExecSetSlotDescriptorIsNew
+ * ExecSetSlotDescriptorIsNew
*
- * This function is used to change the setting of the "isNew" flag
+ * This function is used to change the setting of the "isNew" flag
* --------------------------------
*/
void
-ExecSetSlotDescriptorIsNew(TupleTableSlot *slot,/* slot to change */
- bool isNew) /* "isNew" setting */
+ExecSetSlotDescriptorIsNew(TupleTableSlot * slot, /* slot to change */
+ bool isNew) /* "isNew" setting */
{
- slot->ttc_descIsNew = isNew;
+ slot->ttc_descIsNew = isNew;
}
/* --------------------------------
- * ExecSetNewSlotDescriptor
+ * 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.
+ * This function is used to set the tuple descriptor associated
+ * with the slot's tuple, and set the "isNew" flag at the same time.
* --------------------------------
*/
#ifdef NOT_USED
-TupleDesc /* return: old slot tuple descriptor */
-ExecSetNewSlotDescriptor(TupleTableSlot *slot, /* slot to change */
- TupleDesc tupdesc) /* tuple descriptor */
+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;
+ TupleDesc old_tupdesc = slot->ttc_tupleDescriptor;
+
+ slot->ttc_tupleDescriptor = tupdesc;
+ slot->ttc_descIsNew = true;
+
+ return old_tupdesc;
}
+
#endif
/* --------------------------------
- * ExecSlotBuffer
+ * 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().
+ * 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
+ * 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().
+ * 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().
* --------------------------------
*/
#ifdef NOT_USED
-Buffer /* return: old slot buffer */
-ExecSetSlotBuffer(TupleTableSlot *slot, /* slot to change */
- Buffer b) /* tuple descriptor */
+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;
+ Buffer oldb = slot->ttc_buffer;
+
+ slot->ttc_buffer = b;
+
+ return oldb;
}
+
#endif
/* --------------------------------
- * ExecIncrSlotBufferRefcnt
+ * 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.
+ * 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 */
+ExecIncrSlotBufferRefcnt(TupleTableSlot * slot) /* slot to bump refcnt */
{
-/* Buffer b = SlotBuffer((TupleTableSlot*) slot); */
- Buffer b = slot->ttc_buffer;
- if (BufferIsValid(b))
- IncrBufferRefCount(b);
+/* Buffer b = SlotBuffer((TupleTableSlot*) slot); */
+ Buffer b = slot->ttc_buffer;
+
+ if (BufferIsValid(b))
+ IncrBufferRefCount(b);
}
/* ----------------------------------------------------------------
- * tuple table slot status predicates
+ * tuple table slot status predicates
* ----------------------------------------------------------------
*/
/* ----------------
- * TupIsNull
+ * TupIsNull
*
- * This is used mainly to detect when there are no more
- * tuples to process.
+ * 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 */
+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);
+ 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
+ * 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
+ * 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
* --------------------------------
*/
#ifdef NOT_USED
-bool /* return: descriptor "is new" */
-ExecSlotDescriptorIsNew(TupleTableSlot *slot) /* slot to inspect */
+bool /* return: descriptor "is new" */
+ExecSlotDescriptorIsNew(TupleTableSlot * slot) /* slot to inspect */
{
-/* bool isNew = SlotTupleDescriptorIsNew((TupleTableSlot*) slot);
- return isNew; */
- return slot->ttc_descIsNew;
+/* bool isNew = SlotTupleDescriptorIsNew((TupleTableSlot*) slot);
+ return isNew; */
+ return slot->ttc_descIsNew;
}
+
#endif
/* ----------------------------------------------------------------
- * convenience initialization routines
+ * convenience initialization routines
* ----------------------------------------------------------------
*/
/* --------------------------------
- * ExecInit{Result,Scan,Raw,Marked,Outer,Hash}TupleSlot
+ * ExecInit{Result,Scan,Raw,Marked,Outer,Hash}TupleSlot
*
- * These are convenience routines to initialize the specfied slot
- * in nodes inheriting the appropriate state.
+ * These are convenience routines to initialize the specfied slot
+ * in nodes inheriting the appropriate state.
* --------------------------------
*/
#define INIT_SLOT_DEFS \
- TupleTable tupleTable; \
- TupleTableSlot* slot
-
+ 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;
+ 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
+ * ExecInitResultTupleSlot
* ----------------
*/
void
-ExecInitResultTupleSlot(EState *estate, CommonState *commonstate)
+ExecInitResultTupleSlot(EState * estate, CommonState * commonstate)
{
- INIT_SLOT_DEFS;
- INIT_SLOT_ALLOC;
- commonstate->cs_ResultTupleSlot = (TupleTableSlot *) slot;
+ INIT_SLOT_DEFS;
+ INIT_SLOT_ALLOC;
+ commonstate->cs_ResultTupleSlot = (TupleTableSlot *) slot;
}
/* ----------------
- * ExecInitScanTupleSlot
+ * ExecInitScanTupleSlot
* ----------------
*/
void
-ExecInitScanTupleSlot(EState *estate, CommonScanState *commonscanstate)
+ExecInitScanTupleSlot(EState * estate, CommonScanState * commonscanstate)
{
- INIT_SLOT_DEFS;
- INIT_SLOT_ALLOC;
- commonscanstate->css_ScanTupleSlot = (TupleTableSlot *)slot;
+ INIT_SLOT_DEFS;
+ INIT_SLOT_ALLOC;
+ commonscanstate->css_ScanTupleSlot = (TupleTableSlot *) slot;
}
/* ----------------
- * ExecInitMarkedTupleSlot
+ * ExecInitMarkedTupleSlot
* ----------------
*/
void
-ExecInitMarkedTupleSlot(EState *estate, MergeJoinState *mergestate)
+ExecInitMarkedTupleSlot(EState * estate, MergeJoinState * mergestate)
{
- INIT_SLOT_DEFS;
- INIT_SLOT_ALLOC;
- mergestate->mj_MarkedTupleSlot = (TupleTableSlot *) slot;
+ INIT_SLOT_DEFS;
+ INIT_SLOT_ALLOC;
+ mergestate->mj_MarkedTupleSlot = (TupleTableSlot *) slot;
}
/* ----------------
- * ExecInitOuterTupleSlot
+ * ExecInitOuterTupleSlot
* ----------------
*/
void
-ExecInitOuterTupleSlot(EState *estate, HashJoinState *hashstate)
+ExecInitOuterTupleSlot(EState * estate, HashJoinState * hashstate)
{
- INIT_SLOT_DEFS;
- INIT_SLOT_ALLOC;
- hashstate->hj_OuterTupleSlot = slot;
+ INIT_SLOT_DEFS;
+ INIT_SLOT_ALLOC;
+ hashstate->hj_OuterTupleSlot = slot;
}
/* ----------------
- * ExecInitHashTupleSlot
+ * ExecInitHashTupleSlot
* ----------------
*/
#ifdef NOT_USED
void
-ExecInitHashTupleSlot(EState *estate, HashJoinState *hashstate)
+ExecInitHashTupleSlot(EState * estate, HashJoinState * hashstate)
{
- INIT_SLOT_DEFS;
- INIT_SLOT_ALLOC;
- hashstate->hj_HashTupleSlot = slot;
+ INIT_SLOT_DEFS;
+ INIT_SLOT_ALLOC;
+ hashstate->hj_HashTupleSlot = slot;
}
+
#endif
static TupleTableSlot *
-NodeGetResultTupleSlot(Plan *node)
+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:
+ TupleTableSlot *slot;
+
+ switch (nodeTag(node))
{
- HashJoinState *hashjoinstate = ((HashJoin *)node)->hashjoinstate;
- slot = hashjoinstate->jstate.cs_ResultTupleSlot;
+
+ 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;
}
- 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;
+ return slot;
}
/* ----------------------------------------------------------------
- * ExecGetTupType
+ * 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.
+ * 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
+ * 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
+ * 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
+ * Assume that for existential nodes, we get the targetlist out
+ * of the right node's targetlist
* ----------------------------------------------------------------
*/
TupleDesc
-ExecGetTupType(Plan *node)
+ExecGetTupType(Plan * node)
{
- TupleTableSlot *slot;
- TupleDesc tupType;
-
- if (node == NULL)
- return NULL;
-
- slot = NodeGetResultTupleSlot(node);
- tupType = slot->ttc_tupleDescriptor;
- return tupType;
+ TupleTableSlot *slot;
+ TupleDesc tupType;
+
+ if (node == NULL)
+ return NULL;
+
+ slot = NodeGetResultTupleSlot(node);
+ tupType = slot->ttc_tupleDescriptor;
+ return tupType;
}
/*
TupleDesc
-ExecCopyTupType(TupleDesc td, int natts)
+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;
+ 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.
+ * 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)
+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,
- /* fix for SELECT NULL ... */
- get_id_typname(restype?restype:UNKNOWNOID),
- 0,
- false);
+ 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,
+ /* fix for SELECT NULL ... */
+ get_id_typname(restype ? restype : UNKNOWNOID),
+ 0,
+ false);
/*
- ExecSetTypeInfo(resdom->resno - 1,
- typeInfo,
- (Oid) restype,
- resdom->resno,
- resdom->reslen,
- resdom->resname->data,
- get_typbyval(restype),
- get_typalign(restype));
+ 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);
+ }
+ else
+ {
+ Resdom *fjRes;
+ List *fjTlistP;
+ List *fjList = lfirst(tlcdr);
+
#ifdef SETS_FIXED
- TargetEntry *tle;
- Fjoin *fjNode = ((TargetEntry *)lfirst(fjList))->fjoin;
+ TargetEntry *tle;
+ Fjoin *fjNode = ((TargetEntry *) lfirst(fjList))->fjoin;
- tle = fjNode->fj_innerNode; /* ??? */
+ tle = fjNode->fj_innerNode; /* ??? */
#endif
- fjRes = tle->resdom;
- restype = fjRes->restype;
-
- TupleDescInitEntry(typeInfo,
- fjRes->resno,
- fjRes->resname,
- get_id_typname(restype),
- 0,
- false);
+ 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));
+ 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);
-
+
+ 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));
+ 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;
-}
+ }
+ }
+ tlcdr = lnext(tlcdr);
+ }
+ return typeInfo;
+}
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index ac2d3516036..3795c2d1018 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -1,43 +1,43 @@
/*-------------------------------------------------------------------------
*
* execUtils.c--
- * miscellanious executor utility routines
+ * miscellanious executor utility routines
*
* Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.14 1997/08/22 03:12:19 vadim Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.15 1997/09/07 04:41:26 momjian Exp $
*
*-------------------------------------------------------------------------
*/
/*
* INTERFACE ROUTINES
- * ExecAssignNodeBaseInfo \
- * ExecAssignDebugHooks > preforms misc work done in all the
- * ExecAssignExprContext / init node 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 /
+ * 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
+ * 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 /
+ * 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.
*
- * NOTES
- * This file has traditionally been the place to stick misc.
- * executor support stuff that doesn't really go anyplace else.
- *
*/
#include "postgres.h"
@@ -58,1149 +58,1195 @@
#include "catalog/pg_type.h"
#include "parser/parsetree.h"
-static void ExecGetIndexKeyInfo(IndexTupleForm indexTuple, int *numAttsOutP,
- AttrNumber **attsOutP, FuncIndexInfoPtr fInfoP);
+static void
+ExecGetIndexKeyInfo(IndexTupleForm indexTuple, int *numAttsOutP,
+ AttrNumber ** attsOutP, FuncIndexInfoPtr fInfoP);
/* ----------------------------------------------------------------
- * global counters for number of tuples processed, retrieved,
- * appended, replaced, deleted.
+ * 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. */
+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
+ * statistic functions
* ----------------------------------------------------------------
*/
/* ----------------------------------------------------------------
- * ResetTupleCount
+ * ResetTupleCount
* ----------------------------------------------------------------
*/
#ifdef NOT_USED
void
ResetTupleCount(void)
{
- NTupleProcessed = 0;
- NTupleRetrieved = 0;
- NTupleAppended = 0;
- NTupleDeleted = 0;
- NTupleReplaced = 0;
- NIndexTupleProcessed = 0;
+ NTupleProcessed = 0;
+ NTupleRetrieved = 0;
+ NTupleAppended = 0;
+ NTupleDeleted = 0;
+ NTupleReplaced = 0;
+ NIndexTupleProcessed = 0;
}
+
#endif
/* ----------------------------------------------------------------
- * PrintTupleCount
+ * PrintTupleCount
* ----------------------------------------------------------------
*/
#ifdef NOT_USED
void
-DisplayTupleCount(FILE *statfp)
+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");
+ 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");
}
+
#endif
/* ----------------------------------------------------------------
- * miscellanious init node support functions
+ * 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 - assigns the baseid field of the node
+ * ExecAssignDebugHooks - assigns the node's debugging hooks
+ * ExecAssignExprContext - assigns the node's expression context
* ----------------------------------------------------------------
*/
/* ----------------
- * ExecAssignNodeBaseInfo
+ * 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.
+ * 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)
+ExecAssignNodeBaseInfo(EState * estate, CommonState * cstate, Plan * parent)
{
- int baseId;
-
- baseId = estate->es_BaseId;
- cstate->cs_base_id = baseId;
- estate->es_BaseId = baseId + 1;
+ int baseId;
+
+ baseId = estate->es_BaseId;
+ cstate->cs_base_id = baseId;
+ estate->es_BaseId = baseId + 1;
}
/* ----------------
- * ExecAssignExprContext
+ * 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.
+ * 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)
+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;
+ 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
+ * Result slot tuple type and ProjectionInfo support
* ----------------------------------------------------------------
*/
/* ----------------
- * ExecAssignResultType
+ * ExecAssignResultType
* ----------------
*/
void
-ExecAssignResultType(CommonState *commonstate,
- TupleDesc tupDesc)
+ExecAssignResultType(CommonState * commonstate,
+ TupleDesc tupDesc)
{
- TupleTableSlot *slot;
-
- slot = commonstate->cs_ResultTupleSlot;
- slot->ttc_tupleDescriptor = tupDesc;
+ TupleTableSlot *slot;
+
+ slot = commonstate->cs_ResultTupleSlot;
+ slot->ttc_tupleDescriptor = tupDesc;
}
/* ----------------
- * ExecAssignResultTypeFromOuterPlan
+ * ExecAssignResultTypeFromOuterPlan
* ----------------
*/
void
-ExecAssignResultTypeFromOuterPlan(Plan *node, CommonState *commonstate)
+ExecAssignResultTypeFromOuterPlan(Plan * node, CommonState * commonstate)
{
- Plan *outerPlan;
- TupleDesc tupDesc;
-
- outerPlan = outerPlan(node);
- tupDesc = ExecGetTupType(outerPlan);
-
- ExecAssignResultType(commonstate, tupDesc);
+ Plan *outerPlan;
+ TupleDesc tupDesc;
+
+ outerPlan = outerPlan(node);
+ tupDesc = ExecGetTupType(outerPlan);
+
+ ExecAssignResultType(commonstate, tupDesc);
}
/* ----------------
- * ExecAssignResultTypeFromTL
+ * ExecAssignResultTypeFromTL
* ----------------
*/
void
-ExecAssignResultTypeFromTL(Plan *node, CommonState *commonstate)
+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);
- }
+ 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;
- }
+ 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);
+ i++;
+ }
+ if (len > 0)
+ {
+ ExecAssignResultType(commonstate,
+ origTupDesc);
+ }
+ else
+ ExecAssignResultType(commonstate,
+ (TupleDesc) NULL);
}
/* ----------------
- * ExecGetResultType
+ * ExecGetResultType
* ----------------
*/
TupleDesc
-ExecGetResultType(CommonState *commonstate)
+ExecGetResultType(CommonState * commonstate)
{
- TupleTableSlot *slot = commonstate->cs_ResultTupleSlot;
-
- return slot->ttc_tupleDescriptor;
+ TupleTableSlot *slot = commonstate->cs_ResultTupleSlot;
+
+ return slot->ttc_tupleDescriptor;
}
/* ----------------
- * ExecFreeResultType
+ * ExecFreeResultType
* ----------------
*/
#ifdef NOT_USED
void
-ExecFreeResultType(CommonState *commonstate)
+ExecFreeResultType(CommonState * commonstate)
{
- TupleTableSlot *slot;
- TupleDesc tupType;
-
- slot = commonstate->cs_ResultTupleSlot;
- tupType = slot->ttc_tupleDescriptor;
-
-/* ExecFreeTypeInfo(tupType); */
- pfree(tupType);
+ TupleTableSlot *slot;
+ TupleDesc tupType;
+
+ slot = commonstate->cs_ResultTupleSlot;
+ tupType = slot->ttc_tupleDescriptor;
+
+/* ExecFreeTypeInfo(tupType); */
+ pfree(tupType);
}
+
#endif
/* ----------------
- * ExecAssignProjectionInfo
- forms the projection information from the node's targetlist
+ * ExecAssignProjectionInfo
+ forms the projection information from the node's targetlist
* ----------------
*/
void
-ExecAssignProjectionInfo(Plan *node, CommonState *commonstate)
+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;
+ 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
+ * ExecFreeProjectionInfo
* ----------------
*/
void
-ExecFreeProjectionInfo(CommonState *commonstate)
+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;
+ 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
+ * 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
+ * ExecGetScanType
* ----------------
*/
TupleDesc
-ExecGetScanType(CommonScanState *csstate)
+ExecGetScanType(CommonScanState * csstate)
{
- TupleTableSlot *slot = csstate->css_ScanTupleSlot;
- return slot->ttc_tupleDescriptor;
+ TupleTableSlot *slot = csstate->css_ScanTupleSlot;
+
+ return slot->ttc_tupleDescriptor;
}
/* ----------------
- * ExecFreeScanType
+ * ExecFreeScanType
* ----------------
*/
#ifdef NOT_USED
void
-ExecFreeScanType(CommonScanState *csstate)
+ExecFreeScanType(CommonScanState * csstate)
{
- TupleTableSlot *slot;
- TupleDesc tupType;
-
- slot = csstate->css_ScanTupleSlot;
- tupType = slot->ttc_tupleDescriptor;
-
-/* ExecFreeTypeInfo(tupType); */
- pfree(tupType);
+ TupleTableSlot *slot;
+ TupleDesc tupType;
+
+ slot = csstate->css_ScanTupleSlot;
+ tupType = slot->ttc_tupleDescriptor;
+
+/* ExecFreeTypeInfo(tupType); */
+ pfree(tupType);
}
+
#endif
/* ----------------
- * ExecAssignScanType
+ * ExecAssignScanType
* ----------------
*/
void
-ExecAssignScanType(CommonScanState *csstate,
- TupleDesc tupDesc)
+ExecAssignScanType(CommonScanState * csstate,
+ TupleDesc tupDesc)
{
- TupleTableSlot *slot;
-
- slot = (TupleTableSlot *) csstate->css_ScanTupleSlot;
- slot->ttc_tupleDescriptor = tupDesc;
+ TupleTableSlot *slot;
+
+ slot = (TupleTableSlot *) csstate->css_ScanTupleSlot;
+ slot->ttc_tupleDescriptor = tupDesc;
}
/* ----------------
- * ExecAssignScanTypeFromOuterPlan
+ * ExecAssignScanTypeFromOuterPlan
* ----------------
*/
void
-ExecAssignScanTypeFromOuterPlan(Plan *node, CommonScanState *csstate)
+ExecAssignScanTypeFromOuterPlan(Plan * node, CommonScanState * csstate)
{
- Plan *outerPlan;
- TupleDesc tupDesc;
-
- outerPlan = outerPlan(node);
- tupDesc = ExecGetTupType(outerPlan);
+ Plan *outerPlan;
+ TupleDesc tupDesc;
+
+ outerPlan = outerPlan(node);
+ tupDesc = ExecGetTupType(outerPlan);
- ExecAssignScanType(csstate, tupDesc);
+ ExecAssignScanType(csstate, tupDesc);
}
/* ----------------------------------------------------------------
- * ExecTypeFromTL support routines.
+ * ExecTypeFromTL support routines.
*
- * these routines are used mainly from ExecTypeFromTL.
- * -cim 6/12/90
+ * 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:
+ * 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'.
+ * 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
+ * ExecSetTypeInfo
*
- * This initializes fields of a single attribute in a
- * tuple descriptor from the specified parameters.
+ * 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.
+ * 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.
* ----------------
*/
-#ifdef NOT_USED
+#ifdef NOT_USED
void
ExecSetTypeInfo(int index,
- TupleDesc typeInfo,
- Oid typeID,
- int attNum,
- int attLen,
- char *attName,
- bool attbyVal,
- char attalign)
+ 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-1);
- else
- memset(att->attname.data,0,NAMEDATALEN);
-
- att->atttypid = typeID;
- att->attdefrel = 0; /* dummy value */
- att->attdisbursion = 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;
+ 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 - 1);
+ else
+ memset(att->attname.data, 0, NAMEDATALEN);
+
+ att->atttypid = typeID;
+ att->attdefrel = 0; /* dummy value */
+ att->attdisbursion = 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...
+ * 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);
+ /* ----------------
+ * 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
+ * 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
+ *| 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)
+QueryDescGetTypeInfo(QueryDesc * queryDesc)
{
- Plan *plan;
- TupleDesc tupleType;
- List *targetList;
- AttrInfo *attinfo = (AttrInfo *)palloc(sizeof(AttrInfo));
-
- plan = queryDesc->plantree;
- tupleType = (TupleDesc) ExecGetTupType(plan);
+ Plan *plan;
+ TupleDesc tupleType;
+ List *targetList;
+ AttrInfo *attinfo = (AttrInfo *) palloc(sizeof(AttrInfo));
+
+ plan = queryDesc->plantree;
+ tupleType = (TupleDesc) ExecGetTupType(plan);
/*
- targetList = plan->targetlist;
+ targetList = plan->targetlist;
- attinfo->numAttr = ExecTargetListLength(targetList);
- attinfo->attrs = tupleType;
+ attinfo->numAttr = ExecTargetListLength(targetList);
+ attinfo->attrs = tupleType;
*/
- attinfo->numAttr = tupleType->natts;
- attinfo->attrs = tupleType->attrs;
- return attinfo;
+ attinfo->numAttr = tupleType->natts;
+ attinfo->attrs = tupleType->attrs;
+ return attinfo;
}
+
#endif
/* ----------------------------------------------------------------
- * ExecInsertIndexTuples support
+ * ExecInsertIndexTuples support
* ----------------------------------------------------------------
*/
/* ----------------------------------------------------------------
- * ExecGetIndexKeyInfo
+ * 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.
+ * 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.
* ----------------------------------------------------------------
*/
static void
ExecGetIndexKeyInfo(IndexTupleForm indexTuple,
- int *numAttsOutP,
- AttrNumber **attsOutP,
- FuncIndexInfoPtr fInfoP)
+ 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;
-}
+ int i;
+ int numKeys;
+ AttrNumber *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
+ * check parameters
* ----------------
*/
- indexStruct = (IndexTupleForm) GETSTRUCT(tuple);
- indexOid = indexStruct->indexrelid;
-
+ if (numAttsOutP == NULL && attsOutP == NULL)
+ {
+ elog(DEBUG, "ExecGetIndexKeyInfo: %s",
+ "invalid parameters: numAttsOutP and attsOutP must be non-NULL");
+ }
+
/* ----------------
- * allocate space for functional index information.
+ * set the procid for a possible functional index.
* ----------------
*/
- fInfoP = (FuncIndexInfoPtr)palloc( sizeof(*fInfoP) );
-
+ FIsetProcOid(fInfoP, indexTuple->indproc);
+
/* ----------------
- * next get the index key information from the tuple
+ * count the number of keys..
* ----------------
*/
- ExecGetIndexKeyInfo(indexStruct,
- &numKeyAtts,
- &indexKeyAtts,
- fInfoP);
-
+ numKeys = 0;
+ for (i = 0; i < 8 && indexTuple->indkey[i] != 0; i++)
+ numKeys++;
+
/* ----------------
- * next get the index predicate from the tuple
+ * 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 (VARSIZE(&indexStruct->indpred) != 0) {
- predString = fmgr(F_TEXTOUT, &indexStruct->indpred);
- predicate = (PredInfo*)stringToNode(predString);
- pfree(predString);
- } else {
- predicate = NULL;
+ 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;
}
-
+
/* ----------------
- * save the index information into lists
+ * allocate and fill in array of key attribute numbers
* ----------------
*/
- 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) {
+ 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];
+
/* ----------------
- * allocate space for relation descs
+ * return array to caller.
* ----------------
*/
- CXT1_printf("ExecOpenIndices: context is %d\n", CurrentMemoryContext);
- relationDescs = (RelationPtr)
- palloc(len * sizeof(Relation));
-
+ (*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;
+
/* ----------------
- * initialize index info array
+ * open pg_index
* ----------------
*/
- 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;
- }
-
+ indexRd = heap_openr(IndexRelationName);
+
/* ----------------
- * attempt to open each of the indices. If we succeed,
- * then store the index relation descriptor into the
- * relation descriptor array.
+ * form a scan key
* ----------------
*/
- i = 0;
- foreach (indexoid, oidList) {
- Relation indexDesc;
-
- indexOid = lfirsti(indexoid);
- indexDesc = index_open(indexOid);
- if (indexDesc != NULL)
- relationDescs[i++] = indexDesc;
+ 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);
}
-
+
/* ----------------
- * store the relation descriptor array and number of
- * descs into the result relation info.
+ * we have the info we need so close the pg_index relation..
* ----------------
*/
- resultRelationInfo->ri_NumIndices = i;
- resultRelationInfo->ri_IndexRelationDescs = relationDescs;
-
+ heap_endscan(indexSd);
+ heap_close(indexRd);
+
/* ----------------
- * store the index key information collected in our
- * lists into the index info array
+ * 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.
* ----------------
*/
- 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);
+ 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;
}
+
/* ----------------
- * store the index info array into relation info
+ * All done, resultRelationInfo now contains complete information
+ * on the indices associated with the result relation.
* ----------------
*/
- 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);
+
+ /* should free oidList, nkeyList and keyList here */
+ /* OK - let's do it -jolly */
+ freeList(oidList);
+ freeList(nkeyList);
+ freeList(keyList);
+ freeList(fiList);
+ freeList(predList);
}
/* ----------------------------------------------------------------
- * ExecCloseIndices
+ * ExecCloseIndices
*
- * Close the index relations stored in resultRelationInfo
+ * Close the index relations stored in resultRelationInfo
* ----------------------------------------------------------------
*/
void
-ExecCloseIndices(RelationInfo *resultRelationInfo)
+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.
- */
+ 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
+ * 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..
+ * 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..
* ----------------------------------------------------------------
*/
#ifdef NOT_USED
IndexTuple
ExecFormIndexTuple(HeapTuple heapTuple,
- Relation heapRelation,
- Relation indexRelation,
- IndexInfo *indexInfo)
+ 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;
+ 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;
}
+
#endif
/* ----------------------------------------------------------------
- * ExecInsertIndexTuples
+ * 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
+ * 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,
- bool is_update)
+ExecInsertIndexTuples(TupleTableSlot * slot,
+ ItemPointer tupleid,
+ EState * estate,
+ bool is_update)
{
- HeapTuple heapTuple;
- RelationInfo *resultRelationInfo;
- int i;
- int numIndices;
- RelationPtr relationDescs;
- Relation heapRelation;
- IndexInfo **indexInfoArray;
- IndexInfo *indexInfo;
- Node *predicate;
- bool satisfied;
- ExprContext *econtext;
- InsertIndexResult result;
- int numberOfAttributes;
- AttrNumber *keyAttributeNumbers;
- FuncIndexInfoPtr fInfoP;
- TupleDesc heapDescriptor;
- Datum *datum;
- char *nulls;
-
- 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;
-
- indexInfo = indexInfoArray[i];
- predicate = indexInfo->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;
- }
+ HeapTuple heapTuple;
+ RelationInfo *resultRelationInfo;
+ int i;
+ int numIndices;
+ RelationPtr relationDescs;
+ Relation heapRelation;
+ IndexInfo **indexInfoArray;
+ IndexInfo *indexInfo;
+ Node *predicate;
+ bool satisfied;
+ ExprContext *econtext;
+ InsertIndexResult result;
+ int numberOfAttributes;
+ AttrNumber *keyAttributeNumbers;
+ FuncIndexInfoPtr fInfoP;
+ TupleDesc heapDescriptor;
+ Datum *datum;
+ char *nulls;
+
+ heapTuple = slot->val;
/* ----------------
- * get information from index info structure
+ * get information from the result relation info structure.
* ----------------
*/
- numberOfAttributes = indexInfo->ii_NumKeyAttributes;
- keyAttributeNumbers = indexInfo->ii_KeyAttributeNumbers;
- fInfoP = indexInfo->ii_FuncIndexInfo;
- datum = (Datum *) palloc(numberOfAttributes * sizeof *datum);
- nulls = (char *) palloc(numberOfAttributes * sizeof *nulls);
- heapDescriptor = (TupleDesc)RelationGetTupleDescriptor(heapRelation);
-
- 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 */
-
-
- result = index_insert(relationDescs[i], /* index relation */
- datum, /* array of heaptuple Datums */
- nulls, /* info on nulls */
- &(heapTuple->t_ctid), /* oid of heap tuple */
- heapRelation);
-
- /* ----------------
- * keep track of index inserts for debugging
- * ----------------
- */
- IncrIndexInserted();
-
+ resultRelationInfo = estate->es_result_relation_info;
+ numIndices = resultRelationInfo->ri_NumIndices;
+ relationDescs = resultRelationInfo->ri_IndexRelationDescs;
+ indexInfoArray = resultRelationInfo->ri_IndexRelationInfo;
+ heapRelation = resultRelationInfo->ri_RelationDesc;
+
/* ----------------
- * free index tuple after insertion
+ * for each index, form and insert the index tuple
* ----------------
*/
- if (result) pfree(result);
- }
- if (econtext != NULL) pfree(econtext);
+ econtext = NULL;
+ for (i = 0; i < numIndices; i++)
+ {
+ if (relationDescs[i] == NULL)
+ continue;
+
+ indexInfo = indexInfoArray[i];
+ predicate = indexInfo->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;
+ }
+
+ /* ----------------
+ * get information from index info structure
+ * ----------------
+ */
+ numberOfAttributes = indexInfo->ii_NumKeyAttributes;
+ keyAttributeNumbers = indexInfo->ii_KeyAttributeNumbers;
+ fInfoP = indexInfo->ii_FuncIndexInfo;
+ datum = (Datum *) palloc(numberOfAttributes * sizeof *datum);
+ nulls = (char *) palloc(numberOfAttributes * sizeof *nulls);
+ heapDescriptor = (TupleDesc) RelationGetTupleDescriptor(heapRelation);
+
+ 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 */
+
+
+ result = index_insert(relationDescs[i], /* index relation */
+ datum, /* array of heaptuple Datums */
+ nulls, /* info on nulls */
+ &(heapTuple->t_ctid), /* oid of heap tuple */
+ heapRelation);
+
+ /* ----------------
+ * keep track of index inserts for debugging
+ * ----------------
+ */
+ IncrIndexInserted();
+
+ /* ----------------
+ * free index tuple after insertion
+ * ----------------
+ */
+ if (result)
+ pfree(result);
+ }
+ if (econtext != NULL)
+ pfree(econtext);
}
/* ----------------------------------------------------------------
* setVarAttrLenForCreateTable -
- * called when we do a SELECT * INTO TABLE tab
- * needed for attributes that have a defined length, like bpchar and
- * varchar
+ * called when we do a SELECT * INTO TABLE tab
+ * needed for attributes that have a defined length, like bpchar and
+ * varchar
* ----------------------------------------------------------------
*/
void
-setVarAttrLenForCreateTable(TupleDesc tupType, List *targetList,
- List *rangeTable)
+setVarAttrLenForCreateTable(TupleDesc tupType, List * targetList,
+ List * rangeTable)
{
- List *tl;
- TargetEntry *tle;
- Node *expr;
- int varno;
-
- tl = targetList;
-
- for (varno = 0; varno < tupType->natts; varno++) {
- tle = lfirst(tl);
-
- if (tupType->attrs[varno]->atttypid == BPCHAROID ||
- tupType->attrs[varno]->atttypid == VARCHAROID) {
- expr = tle->expr;
- if (expr && IsA(expr,Var)) {
- Var *var;
- RangeTblEntry *rtentry;
- Relation rd;
-
- var = (Var *)expr;
- rtentry = rt_fetch(var->varnoold, rangeTable);
- rd = heap_open(rtentry->relid);
- /* set length to that defined in relation */
- tupType->attrs[varno]->attlen =
- (*rd->rd_att->attrs[var->varoattno-1]).attlen;
- heap_close(rd);
- }
- else
- elog(WARN, "setVarAttrLenForCreateTable: can't get length for variable-length field");
- }
- tl = lnext(tl);
- }
+ List *tl;
+ TargetEntry *tle;
+ Node *expr;
+ int varno;
+
+ tl = targetList;
+
+ for (varno = 0; varno < tupType->natts; varno++)
+ {
+ tle = lfirst(tl);
+
+ if (tupType->attrs[varno]->atttypid == BPCHAROID ||
+ tupType->attrs[varno]->atttypid == VARCHAROID)
+ {
+ expr = tle->expr;
+ if (expr && IsA(expr, Var))
+ {
+ Var *var;
+ RangeTblEntry *rtentry;
+ Relation rd;
+
+ var = (Var *) expr;
+ rtentry = rt_fetch(var->varnoold, rangeTable);
+ rd = heap_open(rtentry->relid);
+ /* set length to that defined in relation */
+ tupType->attrs[varno]->attlen =
+ (*rd->rd_att->attrs[var->varoattno - 1]).attlen;
+ heap_close(rd);
+ }
+ else
+ elog(WARN, "setVarAttrLenForCreateTable: can't get length for variable-length field");
+ }
+ tl = lnext(tl);
+ }
}
-#ifdef NOT_USED /* look at execMain.c */
+#ifdef NOT_USED /* look at execMain.c */
/* ----------------------------------------------------------------
* resetVarAttrLenForCreateTable -
- * called when we do a SELECT * INTO TABLE tab
- * needed for attributes that have a defined length, like bpchar and
- * varchar
- * resets length to -1 for those types
+ * called when we do a SELECT * INTO TABLE tab
+ * needed for attributes that have a defined length, like bpchar and
+ * varchar
+ * resets length to -1 for those types
* ----------------------------------------------------------------
*/
void
resetVarAttrLenForCreateTable(TupleDesc tupType)
{
- int varno;
-
- for (varno = 0; varno < tupType->natts; varno++) {
- if (tupType->attrs[varno]->atttypid == BPCHAROID ||
- tupType->attrs[varno]->atttypid == VARCHAROID)
- /* set length to original -1 */
- tupType->attrs[varno]->attlen = -1;
- }
+ int varno;
+
+ for (varno = 0; varno < tupType->natts; varno++)
+ {
+ if (tupType->attrs[varno]->atttypid == BPCHAROID ||
+ tupType->attrs[varno]->atttypid == VARCHAROID)
+ /* set length to original -1 */
+ tupType->attrs[varno]->attlen = -1;
+ }
}
+
#endif
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index 79f8bede085..96b9b19dcb6 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -1,14 +1,14 @@
/*-------------------------------------------------------------------------
*
* functions.c--
- * Routines to handle functions called from the executor
- * Putting this stuff in fmgr makes the postmaster a mess....
+ * 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.7 1997/08/29 09:02:50 vadim Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.8 1997/09/07 04:41:27 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -41,401 +41,438 @@
#undef new
-typedef enum {F_EXEC_START, F_EXEC_RUN, F_EXEC_DONE} ExecStatus;
+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;
+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);
-
+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)
+ 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;
+ 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[])
+ 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;
+ 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);
}
- else
- estate->es_param_list_info = (ParamListInfo)NULL;
- nextes->estate = estate;
- preves = nextes;
- nextes = (execution_state *)NULL;
-
- planTree_list = lnext(planTree_list);
- }
-
- return newes;
+
+ return newes;
}
-static TupleDesc
-postquel_start(execution_state *es)
+static TupleDesc
+postquel_start(execution_state * es)
{
#ifdef FUNC_UTIL_PATCH
- /*
- * Do nothing for utility commands. (create, destroy...) DZ - 30-8-1996
- */
- if (es->qd->operation == CMD_UTILITY) {
- return (TupleDesc) NULL;
- }
+
+ /*
+ * Do nothing for utility commands. (create, destroy...) DZ -
+ * 30-8-1996
+ */
+ if (es->qd->operation == CMD_UTILITY)
+ {
+ return (TupleDesc) NULL;
+ }
#endif
- return ExecutorStart(es->qd, es->estate);
+ return ExecutorStart(es->qd, es->estate);
}
static TupleTableSlot *
-postquel_getnext(execution_state *es)
+postquel_getnext(execution_state * es)
{
- int feature;
-
+ int feature;
+
#ifdef FUNC_UTIL_PATCH
- if (es->qd->operation == CMD_UTILITY) {
- /*
- * Process an utility command. (create, destroy...) DZ - 30-8-1996
- */
- ProcessUtility(es->qd->parsetree->utilityStmt, es->qd->dest);
- if (!LAST_POSTQUEL_COMMAND(es)) CommandCounterIncrement();
- return (TupleTableSlot*) NULL;
- }
+ if (es->qd->operation == CMD_UTILITY)
+ {
+
+ /*
+ * Process an utility command. (create, destroy...) DZ -
+ * 30-8-1996
+ */
+ ProcessUtility(es->qd->parsetree->utilityStmt, es->qd->dest);
+ if (!LAST_POSTQUEL_COMMAND(es))
+ CommandCounterIncrement();
+ return (TupleTableSlot *) NULL;
+ }
#endif
- feature = (LAST_POSTQUEL_COMMAND(es)) ? EXEC_RETONE : EXEC_RUN;
-
- return ExecutorRun(es->qd, es->estate, feature, 0);
+ feature = (LAST_POSTQUEL_COMMAND(es)) ? EXEC_RETONE : EXEC_RUN;
+
+ return ExecutorRun(es->qd, es->estate, feature, 0);
}
static void
-postquel_end(execution_state *es)
+postquel_end(execution_state * es)
{
#ifdef FUNC_UTIL_PATCH
- /*
- * Do nothing for utility commands. (create, destroy...) DZ - 30-8-1996
- */
- if (es->qd->operation == CMD_UTILITY) {
- return;
- }
+
+ /*
+ * Do nothing for utility commands. (create, destroy...) DZ -
+ * 30-8-1996
+ */
+ if (es->qd->operation == CMD_UTILITY)
+ {
+ return;
+ }
#endif
- ExecutorEnd(es->qd, es->estate);
+ ExecutorEnd(es->qd, es->estate);
}
static void
-postquel_sub_params(execution_state *es,
- int nargs,
- char *args[],
- bool *nullV)
+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)];
+ 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++;
}
- paramLI++;
- }
}
static TupleTableSlot *
copy_function_result(FunctionCachePtr fcache,
- TupleTableSlot *resultSlot)
+ 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++;
+ 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);
+
+ 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)
+static Datum
+postquel_execute(execution_state * es,
+ FunctionCachePtr fcache,
+ List * fTlist,
+ char **args,
+ bool * isNull)
{
- TupleTableSlot *slot;
- Datum value;
+ TupleTableSlot *slot;
+ Datum value;
#ifdef INDEXSCAN_PATCH
- /*
- * It's more right place to do it (before postquel_start->ExecutorStart).
- * Now ExecutorStart->ExecInitIndexScan->ExecEvalParam works ok.
- * (But note: I HOPE we can do it here). - vadim 01/22/97
- */
- if (fcache->nargs > 0)
- postquel_sub_params(es, fcache->nargs, args, fcache->nullVect);
+
+ /*
+ * It's more right place to do it (before
+ * postquel_start->ExecutorStart). Now
+ * ExecutorStart->ExecInitIndexScan->ExecEvalParam works ok. (But
+ * note: I HOPE we can do it here). - vadim 01/22/97
+ */
+ if (fcache->nargs > 0)
+ postquel_sub_params(es, fcache->nargs, args, fcache->nullVect);
#endif
-
- if (es->status == F_EXEC_START)
+
+ if (es->status == F_EXEC_START)
{
- postquel_start(es);
- es->status = F_EXEC_RUN;
+ postquel_start(es);
+ es->status = F_EXEC_RUN;
}
#ifndef INDEXSCAN_PATCH
- if (fcache->nargs > 0)
- postquel_sub_params(es, fcache->nargs, args, fcache->nullVect);
+ if (fcache->nargs > 0)
+ postquel_sub_params(es, fcache->nargs, args, fcache->nullVect);
#endif
-
- 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;
+
+ 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 is a single valued function we have to end the
- * function execution now.
+ * 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.
*/
- 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;
+ CommandCounterIncrement();
+ return (Datum) NULL;
}
Datum
-postquel_function(Func *funcNode, char **args, bool *isNull, bool *isDone)
+postquel_function(Func * funcNode, char **args, bool * isNull, bool * isDone)
{
- execution_state *es;
- Datum result = 0;
- FunctionCachePtr fcache = funcNode->func_fcache;
- CommandId savedId;
-
- /*
- * Before we start do anything we must save CurrentScanCommandId
- * to restore it before return to upper Executor. Also, we have to
- * set CurrentScanCommandId equal to CurrentCommandId.
- * - vadim 08/29/97
- */
- savedId = GetScanCommandId ();
- SetScanCommandId (GetCurrentCommandId ());
-
- es = (execution_state *) fcache->func_state;
- if (es == NULL)
+ execution_state *es;
+ Datum result = 0;
+ FunctionCachePtr fcache = funcNode->func_fcache;
+ CommandId savedId;
+
+ /*
+ * Before we start do anything we must save CurrentScanCommandId to
+ * restore it before return to upper Executor. Also, we have to set
+ * CurrentScanCommandId equal to CurrentCommandId. - vadim 08/29/97
+ */
+ savedId = GetScanCommandId();
+ SetScanCommandId(GetCurrentCommandId());
+
+ es = (execution_state *) fcache->func_state;
+ if (es == NULL)
{
- es = init_execution_state(fcache, args);
- fcache->func_state = (char *) es;
+ 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)
+
+ 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;
+ 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
+ * If we've gone through every command in this function, we are done.
*/
- es = (execution_state *)fcache->func_state;
- while (es)
+ if (es == (execution_state *) NULL)
{
- es->status = F_EXEC_START;
- es = es->next;
+
+ /*
+ * 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;
+ SetScanCommandId(savedId);
+ return (fcache->oneResult) ? result : (Datum) NULL;
}
+
/*
- * Let caller know we're finished.
+ * 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.
*/
- *isDone = true;
- SetScanCommandId (savedId);
- 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;
-
- SetScanCommandId (savedId);
- return result;
+ Assert(LAST_POSTQUEL_COMMAND(es));
+ *isDone = false;
+
+ SetScanCommandId(savedId);
+ return result;
}
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index c6e5b269cde..ee03f6854d9 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -1,17 +1,17 @@
/*-------------------------------------------------------------------------
*
* nodeAgg.c--
- * Routines to handle aggregate nodes.
+ * 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
+ * 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
+ * /usr/local/devel/pglite/cvs/src/backend/executor/nodeAgg.c,v 1.13 1995/08/01 20:19:07 jolly Exp
*
*-------------------------------------------------------------------------
*/
@@ -32,570 +32,625 @@
/*
* AggFuncInfo -
- * keeps the transition functions information around
+ * 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;
+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);
+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.)
+ * 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])
+ * 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.
+ * If the outer subplan is a Group node, ExecAgg returns as many tuples
+ * as there are groups.
*
- * XXX handling of NULL doesn't work
+ * XXX handling of NULL doesn't work
*
- * OLD COMMENTS
+ * 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
+ * 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)
+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];
+ 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;
/* ---------------------
- * find transfer functions of all the aggregates and initialize
- * their initial values
+ * get state info from node
* ---------------------
*/
- 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;
- }
+ 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(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(tupType->natts);
- for (i=0;i<tupType->natts;i++)
- null_array[i] = 'n';
- oneTuple = heap_formtuple(tupType, tupValue, null_array);
- free(null_array);
- }
- break;
+ 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(i = 0; i < nagg; i++) {
- AttrNumber attnum;
- int2 attlen;
- Datum newVal = (Datum) NULL;
- AggFuncInfo *aggfns = &aggFuncInfo[i];
- Datum args[2];
- Node *tagnode = NULL;
-
- switch(nodeTag(aggregates[i]->target))
- {
- case T_Var:
- tagnode = NULL;
- newVal = aggGetAttr(outerslot,
- aggregates[i],
- &isNull);
- break;
- case T_Expr:
- tagnode = ((Expr*)aggregates[i]->target)->oper;
- econtext->ecxt_scantuple = outerslot;
- newVal = ExecEvalExpr (aggregates[i]->target, econtext,
- &isNull, NULL);
+ /* ----------------
+ * 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(tupType->natts);
+ for (i = 0; i < tupType->natts; i++)
+ null_array[i] = 'n';
+ oneTuple = heap_formtuple(tupType, tupValue, null_array);
+ free(null_array);
+ }
break;
- default:
- elog(WARN, "ExecAgg: Bad Agg->Target for Agg %d", i);
- }
-
- if (isNull)
- continue; /* ignore this tuple for this agg */
-
- if (aggfns->xfn1) {
- if (noInitValue[i]) {
- int byVal;
-
- /*
- * 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 */
- if ( tagnode != NULL )
- {
- FunctionCachePtr fcache_ptr;
-
- if ( nodeTag(tagnode) == T_Func )
- fcache_ptr = ((Func*)tagnode)->func_fcache;
- else
- fcache_ptr = ((Oper*)tagnode)->op_fcache;
- attlen = fcache_ptr->typlen;
- byVal = fcache_ptr->typbyval;
- }
- else
- {
- attnum = ((Var*)aggregates[i]->target)->varattno;
- attlen = outerslot->ttc_tupleDescriptor->attrs[attnum-1]->attlen;
- byVal = outerslot->ttc_tupleDescriptor->attrs[attnum-1]->attbyval;
- }
- if (attlen == -1) {
- /* variable length */
- attlen = VARSIZE((struct varlena*) newVal);
- }
- value1[i] = (Datum)palloc(attlen);
- if ( byVal )
- 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);
- }
+
+ for (i = 0; i < nagg; i++)
+ {
+ AttrNumber attnum;
+ int2 attlen;
+ Datum newVal = (Datum) NULL;
+ AggFuncInfo *aggfns = &aggFuncInfo[i];
+ Datum args[2];
+ Node *tagnode = NULL;
+
+ switch (nodeTag(aggregates[i]->target))
+ {
+ case T_Var:
+ tagnode = NULL;
+ newVal = aggGetAttr(outerslot,
+ aggregates[i],
+ &isNull);
+ break;
+ case T_Expr:
+ tagnode = ((Expr *) aggregates[i]->target)->oper;
+ econtext->ecxt_scantuple = outerslot;
+ newVal = ExecEvalExpr(aggregates[i]->target, econtext,
+ &isNull, NULL);
+ break;
+ default:
+ elog(WARN, "ExecAgg: Bad Agg->Target for Agg %d", i);
+ }
+
+ if (isNull)
+ continue; /* ignore this tuple for this agg */
+
+ if (aggfns->xfn1)
+ {
+ if (noInitValue[i])
+ {
+ int byVal;
+
+ /*
+ * 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
+ */
+ if (tagnode != NULL)
+ {
+ FunctionCachePtr fcache_ptr;
+
+ if (nodeTag(tagnode) == T_Func)
+ fcache_ptr = ((Func *) tagnode)->func_fcache;
+ else
+ fcache_ptr = ((Oper *) tagnode)->op_fcache;
+ attlen = fcache_ptr->typlen;
+ byVal = fcache_ptr->typbyval;
+ }
+ else
+ {
+ attnum = ((Var *) aggregates[i]->target)->varattno;
+ attlen = outerslot->ttc_tupleDescriptor->attrs[attnum - 1]->attlen;
+ byVal = outerslot->ttc_tupleDescriptor->attrs[attnum - 1]->attbyval;
+ }
+ if (attlen == -1)
+ {
+ /* variable length */
+ attlen = VARSIZE((struct varlena *) newVal);
+ }
+ value1[i] = (Datum) palloc(attlen);
+ if (byVal)
+ 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 (noInitValue[i])
+ {
+
+ /*
+ * No values found for this agg; return current state. This
+ * seems to fix behavior for avg() aggregate. -tgl 12/96
+ */
+ }
+ else 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??");
}
/*
- * keep this for the projection (we only need one of these -
- * all the tuples we aggregate over share the same group column)
+ * whether the aggregation is done depends on whether we are doing
+ * aggregation over groups or the entire table
*/
- if (!oneTuple) {
- oneTuple = heap_copytuple(outerslot->val);
+ if (nodeTag(outerPlan) == T_Group)
+ {
+ /* aggregation over groups */
+ aggstate->agg_done = ((Group *) outerPlan)->grpstate->grp_done;
+ }
+ else
+ {
+ aggstate->agg_done = TRUE;
}
- 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 (noInitValue[i]) {
- /*
- * No values found for this agg; return current state.
- * This seems to fix behavior for avg() aggregate. -tgl 12/96
- */
- } else 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;
+ /* ----------------
+ * 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
+ * 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)
+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);
-
+ 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;
+
+ /*
+ * 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)
+ExecCountSlotsAgg(Agg * node)
{
- return ExecCountSlotsNode(outerPlan(node)) +
+ return ExecCountSlotsNode(outerPlan(node)) +
ExecCountSlotsNode(innerPlan(node)) +
- AGG_NSLOTS;
+ AGG_NSLOTS;
}
/* ------------------------
- * ExecEndAgg(node)
+ * ExecEndAgg(node)
*
* -----------------------
*/
void
-ExecEndAgg(Agg *node)
+ExecEndAgg(Agg * node)
{
- AggState *aggstate;
- Plan *outerPlan;
+ AggState *aggstate;
+ Plan *outerPlan;
- aggstate = node->aggstate;
+ aggstate = node->aggstate;
- ExecFreeProjectionInfo(&aggstate->csstate.cstate);
+ ExecFreeProjectionInfo(&aggstate->csstate.cstate);
- outerPlan = outerPlan(node);
- ExecEndNode(outerPlan, (Plan*)node);
-
- /* clean up tuple table */
- ExecClearTuple(aggstate->csstate.css_ScanTupleSlot);
+ outerPlan = outerPlan(node);
+ ExecEndNode(outerPlan, (Plan *) node);
+
+ /* clean up tuple table */
+ ExecClearTuple(aggstate->csstate.css_ScanTupleSlot);
}
/*****************************************************************************
- * Support Routines
+ * Support Routines
*****************************************************************************/
/*
* aggGetAttr -
- * get the attribute (specified in the Var node in agg) to aggregate
- * over from the tuple
+ * 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)
+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;
+ 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/nodeAppend.c b/src/backend/executor/nodeAppend.c
index 7abc6d91744..043ad5d9743 100644
--- a/src/backend/executor/nodeAppend.c
+++ b/src/backend/executor/nodeAppend.c
@@ -1,56 +1,56 @@
/*-------------------------------------------------------------------------
*
* nodeAppend.c--
- * routines to handle append nodes.
+ * 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.5 1997/08/19 21:31:07 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeAppend.c,v 1.6 1997/09/07 04:41:30 momjian Exp $
*
*-------------------------------------------------------------------------
*/
/* INTERFACE ROUTINES
- * ExecInitAppend - initialize the append node
- * ExecProcAppend - retrieve the next tuple from the node
- * ExecEndAppend - shut down the append node
+ * 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.
+ * 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 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 -------+------+------+--- 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:
+ * 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*
+ * retrieve (e.name) from e in person*
*
- * generates the plan:
+ * generates the plan:
*
- * |
- * Append -------+-------+--------+--------+
- * / \ | | | |
- * nil nil Scan Scan Scan Scan
- * | | | |
- * person employee student student-emp
+ * |
+ * Append -------+-------+--------+--------+
+ * / \ | | | |
+ * nil nil Scan Scan Scan Scan
+ * | | | |
+ * person employee student student-emp
*/
#include "postgres.h"
@@ -62,429 +62,451 @@
#include "executor/nodeIndexscan.h"
#include "utils/palloc.h"
#include "utils/mcxt.h"
-#include "parser/parsetree.h" /* for rt_store() macro */
+#include "parser/parsetree.h" /* for rt_store() macro */
-static bool exec_append_initialize_next(Append *node);
+static bool exec_append_initialize_next(Append * node);
/* ----------------------------------------------------------------
- * 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.
+ * 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.
* ----------------------------------------------------------------
*/
-static bool
-exec_append_initialize_next(Append *node)
+static 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;
+ 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;
}
-
- 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.
+ * 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.
+ * (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)
+ExecInitAppend(Append * node, EState * estate, Plan * parent)
{
- AppendState *unionstate;
- int nplans;
- List *resultList = NULL;
- 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);
-
+ AppendState *unionstate;
+ int nplans;
+ List *resultList = NULL;
+ 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))
+ /* ----------------
+ * 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)
+ 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);
+ 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;
+ 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
- * ---------------
+ /* ----------------
+ * call ExecInitNode on each of the plans in our list
+ * and save the results into the array "initialized"
+ * ----------------
*/
- if ((es_rri != (RelationInfo*)NULL) &&
- (node->unionrelid == es_rri->ri_RangeTableIndex)) {
-
- targetList = initNode->targetlist;
- j = (JunkFilter *) ExecInitJunkFilter(targetList);
- junkList = lappend(junkList, j);
+ 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);
+ 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;
+ result = (List *) initialized[0];
+#endif
+ return TRUE;
}
int
-ExecCountSlotsAppend(Append *node)
+ExecCountSlotsAppend(Append * node)
{
- List *plan;
- List *unionplans = node->unionplans;
- int nSlots = 0;
-
- foreach (plan,unionplans) {
- nSlots += ExecCountSlotsNode((Plan *)lfirst(plan));
- }
- return nSlots + APPEND_NSLOTS;
+ 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
+ * 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)
+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;
- }
-
+ EState *estate;
+ AppendState *unionstate;
+
+ int whichplan;
+ List *unionplans;
+ Plan *subnode;
+ TupleTableSlot *result;
+ TupleTableSlot *result_slot;
+ ScanDirection direction;
+
/* ----------------
- * return something from next node or an empty slot
- * all of our subplans have been exhausted.
+ * get information from the node
* ----------------
*/
- if (exec_append_initialize_next(node)) {
- ExecSetSlotDescriptorIsNew(result_slot, true);
- return
- ExecProcAppend(node);
- } else
- return ExecClearTuple(result_slot);
- }
+ 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.
+ * ExecEndAppend
+ *
+ * Shuts down the subscans of the append node.
+ *
+ * Returns nothing of interest.
* ----------------------------------------------------------------
*/
void
-ExecEndAppend(Append *node)
+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 */
-}
+ 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/nodeGroup.c b/src/backend/executor/nodeGroup.c
index 0637a8dd282..1a96a1ee911 100644
--- a/src/backend/executor/nodeGroup.c
+++ b/src/backend/executor/nodeGroup.c
@@ -1,19 +1,19 @@
/*-------------------------------------------------------------------------
*
* nodeGroup.c--
- * Routines to handle group nodes (used for queries with GROUP BY clause).
+ * 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)
+ * 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.5 1997/01/10 20:17:35 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeGroup.c,v 1.6 1997/09/07 04:41:31 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -28,329 +28,348 @@
#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);
+static TupleTableSlot *ExecGroupEveryTuple(Group * node);
+static TupleTableSlot *ExecGroupOneTuple(Group * node);
+static bool
+sameGroup(TupleTableSlot * oldslot, TupleTableSlot * newslot,
+ int numCols, AttrNumber * grpColIdx, TupleDesc tupdesc);
/* ---------------------------------------
- * ExecGroup -
+ * 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)
+ * 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)
+ * 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)
+ExecGroup(Group * node)
{
- if (node->tuplePerGroup)
- return ExecGroupEveryTuple(node);
- else
- return ExecGroupOneTuple(node);
+ if (node->tuplePerGroup)
+ return ExecGroupEveryTuple(node);
+ else
+ return ExecGroupOneTuple(node);
}
/*
* ExecGroupEveryTuple -
- * return every tuple with a NULL between each group
+ * return every tuple with a NULL between each group
*/
static TupleTableSlot *
-ExecGroupEveryTuple(Group *node)
+ExecGroupEveryTuple(Group * node)
{
- GroupState *grpstate;
- EState *estate;
- ExprContext *econtext;
+ GroupState *grpstate;
+ EState *estate;
+ ExprContext *econtext;
- HeapTuple outerTuple = NULL;
- TupleTableSlot *outerslot, *lastslot;
- ProjectionInfo *projInfo;
- TupleTableSlot *resultSlot;
+ HeapTuple outerTuple = NULL;
+ TupleTableSlot *outerslot,
+ *lastslot;
+ ProjectionInfo *projInfo;
+ TupleTableSlot *resultSlot;
- bool isDone;
+ bool isDone;
- /* ---------------------
- * get state info from node
- * ---------------------
- */
- grpstate = node->grpstate;
- if (grpstate->grp_done)
- return NULL;
+ /* ---------------------
+ * get state info from node
+ * ---------------------
+ */
+ grpstate = node->grpstate;
+ if (grpstate->grp_done)
+ return NULL;
- estate = node->plan.state;
+ estate = node->plan.state;
- econtext = grpstate->csstate.cstate.cs_ExprContext;
+ 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;
+ 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;
+ 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);
}
/* ----------------
- * Compare with last tuple and see if this tuple is of
- * the same group.
+ * form a projection tuple, store it in the result tuple
+ * slot and return it.
* ----------------
*/
- 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;
+ projInfo = grpstate->csstate.cstate.cs_ProjInfo;
- /* save it for next time */
- grpstate->grp_lastSlot = outerslot;
+ econtext->ecxt_scantuple = grpstate->csstate.css_ScanTupleSlot;
+ resultSlot = ExecProject(projInfo, &isDone);
- /*
- * 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;
+ return resultSlot;
}
/*
* ExecGroupOneTuple -
- * returns one tuple per group, a NULL at the end when there are no more
- * tuples.
+ * returns one tuple per group, a NULL at the end when there are no more
+ * tuples.
*/
static TupleTableSlot *
-ExecGroupOneTuple(Group *node)
+ExecGroupOneTuple(Group * node)
{
- GroupState *grpstate;
- EState *estate;
- ExprContext *econtext;
+ GroupState *grpstate;
+ EState *estate;
+ ExprContext *econtext;
- HeapTuple outerTuple = NULL;
- TupleTableSlot *outerslot, *lastslot;
- ProjectionInfo *projInfo;
- TupleTableSlot *resultSlot;
+ HeapTuple outerTuple = NULL;
+ TupleTableSlot *outerslot,
+ *lastslot;
+ ProjectionInfo *projInfo;
+ TupleTableSlot *resultSlot;
- bool isDone;
+ bool isDone;
- /* ---------------------
- * get state info from node
- * ---------------------
- */
- grpstate = node->grpstate;
- if (grpstate->grp_done)
- return NULL;
+ /* ---------------------
+ * get state info from node
+ * ---------------------
+ */
+ grpstate = node->grpstate;
+ if (grpstate->grp_done)
+ return NULL;
- estate = node->plan.state;
+ estate = node->plan.state;
- econtext = node->grpstate->csstate.cstate.cs_ExprContext;
+ 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;
+ if (grpstate->grp_useLastTuple)
+ {
+ grpstate->grp_useLastTuple = FALSE;
+ ExecStoreTuple(grpstate->grp_lastSlot->val,
+ grpstate->csstate.css_ScanTupleSlot,
+ grpstate->grp_lastSlot->ttc_buffer,
+ false);
}
- 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;
+ 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;
- /* ----------------
- * Compare with last tuple and see if this tuple is of
- * the same group.
- * ----------------
+ /*
+ * find all tuples that belong to a group
*/
- if ((!sameGroup(lastslot, outerslot,
- node->numCols, node->grpColIdx,
- ExecGetScanType(&grpstate->csstate)))) {
-/* ExecGetResultType(&grpstate->csstate.cstate)))) {*/
+ 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;
+ }
- grpstate->grp_useLastTuple = TRUE;
+ ExecStoreTuple(lastslot->val,
+ grpstate->csstate.css_ScanTupleSlot,
+ lastslot->ttc_buffer,
+ false);
- /* save it for next time */
- grpstate->grp_lastSlot = outerslot;
+ /* ----------------
+ * form a projection tuple, store it in the result tuple
+ * slot and return it.
+ * ----------------
+ */
+ projInfo = grpstate->csstate.cstate.cs_ProjInfo;
- /* return lastslot */
- break;
- }
-
- ExecStoreTuple(outerTuple,
- grpstate->csstate.css_ScanTupleSlot,
- outerslot->ttc_buffer,
- false);
+ econtext->ecxt_scantuple = lastslot;
+ resultSlot = ExecProject(projInfo, &isDone);
- 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;
+ return resultSlot;
}
/* -----------------
- * ExecInitGroup
+ * ExecInitGroup
*
- * Creates the run-time information for the group node produced by the
- * planner and initializes its outer subtree
+ * 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)
+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);
-
+ 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;
+
+ /*
+ * 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)
+ExecCountSlotsGroup(Group * node)
{
- return ExecCountSlotsNode(outerPlan(node)) + GROUP_NSLOTS;
+ return ExecCountSlotsNode(outerPlan(node)) + GROUP_NSLOTS;
}
/* ------------------------
- * ExecEndGroup(node)
+ * ExecEndGroup(node)
*
* -----------------------
*/
void
-ExecEndGroup(Group *node)
+ExecEndGroup(Group * node)
{
- GroupState *grpstate;
- Plan *outerPlan;
+ GroupState *grpstate;
+ Plan *outerPlan;
+
+ grpstate = node->grpstate;
- grpstate = node->grpstate;
+ ExecFreeProjectionInfo(&grpstate->csstate.cstate);
- ExecFreeProjectionInfo(&grpstate->csstate.cstate);
+ outerPlan = outerPlan(node);
+ ExecEndNode(outerPlan, (Plan *) node);
- outerPlan = outerPlan(node);
- ExecEndNode(outerPlan, (Plan*)node);
-
- /* clean up tuple table */
- ExecClearTuple(grpstate->csstate.css_ScanTupleSlot);
+ /* clean up tuple table */
+ ExecClearTuple(grpstate->csstate.css_ScanTupleSlot);
}
/*****************************************************************************
@@ -360,54 +379,63 @@ ExecEndGroup(Group *node)
/*
* code swiped from nodeUnique.c
*/
-static bool
-sameGroup(TupleTableSlot *oldslot,
- TupleTableSlot *newslot,
- int numCols,
- AttrNumber *grpColIdx,
- TupleDesc tupdesc)
+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;
+ 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;
+ return TRUE;
}
diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c
index 10bfe9842cf..b25939fa832 100644
--- a/src/backend/executor/nodeHash.c
+++ b/src/backend/executor/nodeHash.c
@@ -1,26 +1,26 @@
/*-------------------------------------------------------------------------
*
* nodeHash.c--
- * Routines to hash relations for hashjoin
+ * 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.10 1997/08/19 21:31:08 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeHash.c,v 1.11 1997/09/07 04:41:32 momjian Exp $
*
*-------------------------------------------------------------------------
*/
/*
* INTERFACE ROUTINES
- * ExecHash - generate an in-memory hash table of the relation
- * ExecInitHash - initialize node and subnodes..
- * ExecEndHash - shutdown node and subnodes
+ * ExecHash - generate an in-memory hash table of the relation
+ * ExecInitHash - initialize node and subnodes..
+ * ExecEndHash - shutdown node and subnodes
*
*/
#include <sys/types.h>
-#include <stdio.h> /* for sprintf() */
+#include <stdio.h> /* for sprintf() */
#include <math.h>
#include <string.h>
#include <sys/file.h>
@@ -32,9 +32,9 @@
#include "postgres.h"
-#include "storage/fd.h" /* for SEEK_ */
+#include "storage/fd.h" /* for SEEK_ */
#include "storage/ipc.h"
-#include "storage/bufmgr.h" /* for BLCKSZ */
+#include "storage/bufmgr.h" /* for BLCKSZ */
#include "executor/executor.h"
#include "executor/nodeHash.h"
#include "executor/nodeHashjoin.h"
@@ -42,827 +42,855 @@
#include "utils/palloc.h"
#include "utils/hsearch.h"
-extern int NBuffers;
-static int HashTBSize;
+extern int NBuffers;
+static int HashTBSize;
-static void mk_hj_temp(char *tempname);
-static int hashFunc(char *key, int len);
-static int ExecHashPartition(Hash *node);
+static void mk_hj_temp(char *tempname);
+static int hashFunc(char *key, int len);
+static int ExecHashPartition(Hash * node);
static RelativeAddr hashTableAlloc(int size, HashJoinTable hashtable);
-static void ExecHashOverflowInsert(HashJoinTable hashtable,
- HashBucket bucket,
- HeapTuple heapTuple);
+static void
+ExecHashOverflowInsert(HashJoinTable hashtable,
+ HashBucket bucket,
+ HeapTuple heapTuple);
/* ----------------------------------------------------------------
- * ExecHash
+ * ExecHash
*
- * build hash table for hashjoin, all do partitioning if more
- * than one batches are required.
+ * build hash table for hashjoin, all do partitioning if more
+ * than one batches are required.
* ----------------------------------------------------------------
*/
TupleTableSlot *
-ExecHash(Hash *node)
+ExecHash(Hash * node)
{
- EState *estate;
- HashState *hashstate;
- Plan *outerNode;
- Var *hashkey;
- HashJoinTable hashtable;
- TupleTableSlot *slot;
- ExprContext *econtext;
-
- int nbatch;
- File *batches = NULL;
- 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);
+ EState *estate;
+ HashState *hashstate;
+ Plan *outerNode;
+ Var *hashkey;
+ HashJoinTable hashtable;
+ TupleTableSlot *slot;
+ ExprContext *econtext;
+
+ int nbatch;
+ File *batches = NULL;
+ 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++;
}
- 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;
+
+ /* ---------------------
+ * Return the slot so that we have the tuple descriptor
+ * when we need to save/restore them. -Jeff 11 July 1991
+ * ---------------------
+ */
+ return slot;
}
/* ----------------------------------------------------------------
- * ExecInitHash
+ * ExecInitHash
*
- * Init routine for Hash node
+ * Init routine for Hash node
* ----------------------------------------------------------------
*/
bool
-ExecInitHash(Hash *node, EState *estate, Plan *parent)
+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);
-
+ 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;
+ /* ----------------
+ * 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)
+ExecCountSlotsHash(Hash * node)
{
- return ExecCountSlotsNode(outerPlan(node)) +
+ return ExecCountSlotsNode(outerPlan(node)) +
ExecCountSlotsNode(innerPlan(node)) +
- HASH_NSLOTS;
+ HASH_NSLOTS;
}
/* ---------------------------------------------------------------
- * ExecEndHash
+ * ExecEndHash
*
- * clean up routine for Hash node
+ * clean up routine for Hash node
* ----------------------------------------------------------------
*/
void
-ExecEndHash(Hash *node)
+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);
-}
-
-static RelativeAddr
+ 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);
+}
+
+static RelativeAddr
hashTableAlloc(int size, HashJoinTable hashtable)
{
- RelativeAddr p;
- p = hashtable->top;
- hashtable->top += size;
- return p;
+ RelativeAddr p;
+
+ p = hashtable->top;
+ hashtable->top += size;
+ return p;
}
/* ----------------------------------------------------------------
- * ExecHashTableCreate
+ * ExecHashTableCreate
*
- * create a hashtable in shared memory for hashjoin.
+ * create a hashtable in shared memory for hashjoin.
* ----------------------------------------------------------------
*/
-#define NTUP_PER_BUCKET 10
-#define FUDGE_FAC 1.5
+#define NTUP_PER_BUCKET 10
+#define FUDGE_FAC 1.5
HashJoinTable
-ExecHashTableCreate(Hash *node)
+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) {
+ 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));
+
/*
- * 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;
+ * 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);
+ 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;
+
+ /* ----------------
+ * 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);
}
- 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;
+ else
+ {
+ hashtable->outerbatchNames = (RelativeAddr) NULL;
+ hashtable->outerbatchPos = (RelativeAddr) NULL;
+ hashtable->innerbatchNames = (RelativeAddr) NULL;
+ hashtable->innerbatchPos = (RelativeAddr) NULL;
+ hashtable->innerbatchSizes = (RelativeAddr) NULL;
}
- 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);
+
+ 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
+ * ExecHashTableInsert
*
- * insert a tuple into the hash table depending on the hash value
- * it may just go to a tmp file for other batches
+ * 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)
+ 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;
-
+ 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 ");
+ 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);
+
+ 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);
}
- }
- 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
+ * ExecHashTableDestroy
*
- * destroy a hash table
+ * destroy a hash table
* ----------------------------------------------------------------
*/
void
ExecHashTableDestroy(HashJoinTable hashtable)
{
- pfree(hashtable);
+ pfree(hashtable);
}
/* ----------------------------------------------------------------
- * ExecHashGetBucket
+ * ExecHashGetBucket
*
- * Get the hash value for a tuple
+ * Get the hash value for a tuple
* ----------------------------------------------------------------
*/
int
ExecHashGetBucket(HashJoinTable hashtable,
- ExprContext *econtext,
- Var *hashkey)
+ ExprContext * econtext,
+ Var * hashkey)
{
- int bucketno;
- Datum keyval;
- bool isNull;
-
-
- /* ----------------
- * Get the join attribute value of the tuple
- * ----------------
- * ...It's quick hack - use ExecEvalExpr instead of ExecEvalVar:
- * hashkey may be T_ArrayRef, not just T_Var. - vadim 04/22/97
- */
- keyval = ExecEvalExpr((Node*)hashkey, econtext, &isNull, NULL);
-
- /*
- * keyval could be null, so we better point it to something
- * valid before trying to run hashFunc on it. --djm 8/17/96
- */
- if(isNull) {
- execConstByVal = 0;
- execConstLen = 0;
- keyval = (Datum)"";
- }
-
- /* ------------------
- * compute the hash function
- * ------------------
- */
- if (execConstByVal)
- bucketno =
- hashFunc((char *) &keyval, execConstLen) % hashtable->totalbuckets;
- else
- bucketno =
- hashFunc((char *) keyval, execConstLen) % hashtable->totalbuckets;
+ int bucketno;
+ Datum keyval;
+ bool isNull;
+
+
+ /* ----------------
+ * Get the join attribute value of the tuple
+ * ----------------
+ * ...It's quick hack - use ExecEvalExpr instead of ExecEvalVar:
+ * hashkey may be T_ArrayRef, not just T_Var. - vadim 04/22/97
+ */
+ keyval = ExecEvalExpr((Node *) hashkey, econtext, &isNull, NULL);
+
+ /*
+ * keyval could be null, so we better point it to something valid
+ * before trying to run hashFunc on it. --djm 8/17/96
+ */
+ if (isNull)
+ {
+ execConstByVal = 0;
+ execConstLen = 0;
+ keyval = (Datum) "";
+ }
+
+ /* ------------------
+ * 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);
+ if (bucketno >= hashtable->nbuckets)
+ printf("hash(%d) = %d SAVED\n", keyval, bucketno);
+ else
+ printf("hash(%d) = %d\n", keyval, bucketno);
#endif
-
- return(bucketno);
+
+ return (bucketno);
}
/* ----------------------------------------------------------------
- * ExecHashOverflowInsert
+ * ExecHashOverflowInsert
*
- * insert into the overflow area of a hash bucket
+ * insert into the overflow area of a hash bucket
* ----------------------------------------------------------------
*/
static void
ExecHashOverflowInsert(HashJoinTable hashtable,
- HashBucket bucket,
- HeapTuple heapTuple)
+ 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) {
-#if 0
- elog(DEBUG, "hash table out of memory. expanding.");
- /* ------------------
- * XXX this is a temporary hack
- * eventually, recursive hash partitioning will be
- * implemented
- * ------------------
+ 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
+ * ----------------
*/
- 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.");
- }
+ newend = (RelativeAddr) LONGALIGN(hashtable->overflownext + sizeof(*otuple)
+ + heapTuple->t_len);
+ if (newend > hashtable->bottom)
+ {
+#if 0
+ 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.");
+ }
#else
- /* ------------------
- * XXX the temporary hack above doesn't work because things
- * above us don't know that we've moved the hash table!
- * - Chris Dunlop, <chris@onthe.net.au>
- * ------------------
- */
- elog(WARN, "hash table out of memory. Use -B parameter to increase buffers.");
+ /* ------------------
+ * XXX the temporary hack above doesn't work because things
+ * above us don't know that we've moved the hash table!
+ * - Chris Dunlop, <chris@onthe.net.au>
+ * ------------------
+ */
+ elog(WARN, "hash table out of memory. Use -B parameter to increase buffers.");
#endif
- }
-
- /* ----------------
- * 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);
+ }
+
+ /* ----------------
+ * 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
+ * ExecScanHashBucket
*
- * scan a hash bucket of matches
+ * scan a hash bucket of matches
* ----------------------------------------------------------------
*/
HeapTuple
-ExecScanHashBucket(HashJoinState *hjstate,
- HashBucket bucket,
- HeapTuple curtuple,
- List *hjclauses,
- ExprContext *econtext)
+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));
+ 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;
}
-
- 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;
+
+ /* ----------------
+ * 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);
}
-
- otuple = (OverflowTuple)ABSADDR(otuple->next);
- }
-
- /* ----------------
- * no match
- * ----------------
- */
- return NULL;
+
+ /* ----------------
+ * no match
+ * ----------------
+ */
+ return NULL;
}
/* ----------------------------------------------------------------
- * hashFunc
+ * hashFunc
*
- * the hash function, copied from Margo
+ * 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);
+ 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
+ * ExecHashPartition
*
- * determine the number of batches needed for a hashjoin
+ * determine the number of batches needed for a hashjoin
* ----------------------------------------------------------------
*/
static int
-ExecHashPartition(Hash *node)
+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;
+ 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
+ * ExecHashTableReset
*
- * reset hash table header for new batch
+ * 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;
+ 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 int hjtmpcnt = 0;
static void
mk_hj_temp(char *tempname)
{
- sprintf(tempname, "HJ%d.%d", (int)getpid(), hjtmpcnt);
- hjtmpcnt = (hjtmpcnt + 1) % 1000;
+ sprintf(tempname, "HJ%d.%d", (int) getpid(), hjtmpcnt);
+ hjtmpcnt = (hjtmpcnt + 1) % 1000;
}
-
-
-
diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c
index c9f24efe193..3548e38cc86 100644
--- a/src/backend/executor/nodeHashjoin.c
+++ b/src/backend/executor/nodeHashjoin.c
@@ -1,13 +1,13 @@
/*-------------------------------------------------------------------------
*
* nodeHashjoin.c--
- * Routines to handle hash join nodes
+ * 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.5 1997/08/19 21:31:09 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeHashjoin.c,v 1.6 1997/09/07 04:41:33 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -20,8 +20,8 @@
#include "postgres.h"
-#include "storage/bufmgr.h" /* for BLCKSZ */
-#include "storage/fd.h" /* for SEEK_ */
+#include "storage/bufmgr.h" /* for BLCKSZ */
+#include "storage/fd.h" /* for SEEK_ */
#include "executor/executor.h"
#include "executor/execdebug.h"
#include "executor/nodeHash.h"
@@ -33,775 +33,818 @@
#include "utils/palloc.h"
static TupleTableSlot *
-ExecHashJoinOuterGetTuple(Plan *node, Plan* parent, HashJoinState *hjstate);
+ ExecHashJoinOuterGetTuple(Plan * node, Plan * parent, HashJoinState * hjstate);
static TupleTableSlot *
-ExecHashJoinGetSavedTuple(HashJoinState *hjstate, char *buffer,
- File file, TupleTableSlot *tupleSlot, int *block, char **position);
+ExecHashJoinGetSavedTuple(HashJoinState * hjstate, char *buffer,
+ File file, TupleTableSlot * tupleSlot, int *block, char **position);
-static int ExecHashJoinGetBatch(int bucketno, HashJoinTable hashtable,
- int nbatch);
+static int
+ExecHashJoinGetBatch(int bucketno, HashJoinTable hashtable,
+ int nbatch);
-static int ExecHashJoinNewBatch(HashJoinState *hjstate);
+static int ExecHashJoinNewBatch(HashJoinState * hjstate);
/* ----------------------------------------------------------------
- * ExecHashJoin
+ * 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.
+ * 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)
+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 */
+ 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;
+
/* -----------------
- * allocate space for file descriptors of outer batch files
- * then open the batch files in the current process
+ * get information from HashJoin state
* -----------------
*/
- 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;
+ hashtable = hjstate->hj_HashTable;
+ bucket = hjstate->hj_CurBucket;
+ curtuple = hjstate->hj_CurTuple;
- /* ------------------
- * 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
+ /* --------------------
+ * initialize expression context
+ * --------------------
*/
- 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);
+ 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;
}
- /*
- * 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 this is the first call, build the hash table for inner relation
* ----------------
*/
- 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);
+ 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;
}
- else if (bucket != NULL) {
- do {
- /*
- * scan the hash bucket for matches
+ 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
+ * -----------------
*/
- 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;
- }
+ 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);
}
- }
- while (curtuple != NULL);
+ 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 the current outer tuple has run out of matches,
- * so we free it and get a new outer tuple.
+ * Now get an outer tuple and probe into the hash table for matches
* ----------------
*/
- 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) {
+ outerTupleSlot = hjstate->jstate.cs_OuterTupleSlot;
+ outerVar = get_leftop(clause);
+
+ bucketno = -1; /* if bucketno remains -1, means use old
+ * outer tuple */
+ if (TupIsNull(outerTupleSlot))
+ {
+
/*
- * when the last batch runs out, clean up
+ * if the current outer tuple is nil, get a new one
*/
- ExecHashTableDestroy(hashtable);
- hjstate->hj_HashTable = NULL;
- return NULL;
- }
- else
- outerTupleSlot = (TupleTableSlot*)
- ExecHashJoinOuterGetTuple(outerNode, (Plan*)node, hjstate);
+ 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);
}
-
- /* ----------------
- * Now get the corresponding hash bucket for the new
- * outer tuple.
- * ----------------
- */
- econtext->ecxt_outertuple = outerTupleSlot;
+
+ 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 ");
+ printf("Probing ");
#endif
- bucketno = ExecHashGetBucket(hashtable, econtext, outerVar);
- bucket=(HashBucket)(ABSADDR(hashtable->top)
- + bucketno * hashtable->bucketsize);
- curtuple = NULL;
- }
+ bucketno = ExecHashGetBucket(hashtable, econtext, outerVar);
+ bucket = (HashBucket) (ABSADDR(hashtable->top)
+ + bucketno * hashtable->bucketsize);
+ curtuple = NULL;
+ }
}
/* ----------------------------------------------------------------
- * ExecInitHashJoin
+ * ExecInitHashJoin
*
- * Init routine for HashJoin node.
+ * Init routine for HashJoin node.
* ----------------------------------------------------------------
*/
-bool /* return: initialization status */
-ExecInitHashJoin(HashJoin *node, EState *estate, Plan *parent)
+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);
-
+ 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);
-
+ /* ----------------
+ * 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);
+ 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;
+
+ /* ----------------
+ * 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)
+ExecCountSlotsHashJoin(HashJoin * node)
{
- return ExecCountSlotsNode(outerPlan(node)) +
+ return ExecCountSlotsNode(outerPlan(node)) +
ExecCountSlotsNode(innerPlan(node)) +
- HASHJOIN_NSLOTS;
+ HASHJOIN_NSLOTS;
}
/* ----------------------------------------------------------------
- * ExecEndHashJoin
+ * ExecEndHashJoin
*
- * clean up routine for HashJoin node
+ * clean up routine for HashJoin node
* ----------------------------------------------------------------
*/
void
-ExecEndHashJoin(HashJoin *node)
+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);
-
+ 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
+ * 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.
+ * 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)
+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);
+ 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;
- }
-
- /*
- * 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
+ * ExecHashJoinGetSavedTuple
*
- * read the next tuple from a tmp file using a certain buffer
+ * 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 */
+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);
+ 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
+ * ExecHashJoinNewBatch
*
- * switch to a new hashjoin batch
+ * switch to a new hashjoin batch
* ----------------------------------------------------------------
*/
static int
-ExecHashJoinNewBatch(HashJoinState *hjstate)
+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");
- }
+ 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) {
+ if (newbatch > 1)
+ {
+
+ /*
+ * remove the previous outer batch
+ */
+ FileUnlink(outerBatches[newbatch - 2]);
+ }
+
/*
- * remove the previous outer batch
+ * rebuild the hash table for the new inner batch
+ */
+ innerBatchSizes = (int *) ABSADDR(hashtable->innerbatchSizes);
+ /* --------------
+ * skip over empty inner batches
+ * --------------
*/
- 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++;
+ while (newbatch <= nbatch && innerBatchSizes[newbatch - 1] == 0)
+ {
+ FileUnlink(outerBatches[newbatch - 1]);
+ FileUnlink(innerBatches[newbatch - 1]);
+ newbatch++;
}
- if (newbatch > nbatch) {
+ 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;
- }
- 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
+ * ExecHashJoinGetBatch
*
- * determine the batch number for a bucketno
- * +----------------+-------+-------+ ... +-------+
- * 0 nbuckets totalbuckets
- * batch 0 1 2 ...
+ * determine the batch number for a bucketno
+ * +----------------+-------+-------+ ... +-------+
+ * 0 nbuckets totalbuckets
+ * batch 0 1 2 ...
* ----------------------------------------------------------------
*/
static 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;
+ 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
+ * 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.
+ * 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 *
+char *
ExecHashJoinSaveTuple(HeapTuple heapTuple,
- char *buffer,
- File file,
- char *position)
+ 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;
+ 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/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index bc0c4c3d288..c89a4fcb081 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -1,31 +1,31 @@
/*-------------------------------------------------------------------------
*
* nodeIndexscan.c--
- * Routines to support indexes and indexed scans of relations
+ * 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.7 1997/03/12 20:58:26 scrappy Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeIndexscan.c,v 1.8 1997/09/07 04:41:35 momjian Exp $
*
*-------------------------------------------------------------------------
*/
/*
* INTERFACE ROUTINES
- * ExecInsertIndexTuples inserts tuples into indices on result relation
+ * 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.
+ * 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.
+ * NOTES
+ * the code supporting ExecInsertIndexTuples should be
+ * collected and merged with the genam stuff.
*
*/
#include "postgres.h"
@@ -48,894 +48,939 @@
#include "nodes/nodeFuncs.h"
/* ----------------
- * Misc stuff to move to executor.h soon -cim 6/5/90
+ * Misc stuff to move to executor.h soon -cim 6/5/90
* ----------------
*/
-#define NO_OP 0
-#define LEFT_OP 1
-#define RIGHT_OP 2
+#define NO_OP 0
+#define LEFT_OP 1
+#define RIGHT_OP 2
-static TupleTableSlot *IndexNext(IndexScan *node);
+static TupleTableSlot *IndexNext(IndexScan * node);
/* ----------------------------------------------------------------
- * IndexNext
+ * IndexNext
*
- * Retrieve a tuple from the IndexScan node's currentRelation
- * using the indices in the IndexScanState information.
+ * 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
+ * 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.
+ * 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)
+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);
+ 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;
+
/* ----------------
- * if scanning this index succeeded then return the
- * appropriate heap tuple.. else return NULL.
+ * extract necessary information from index scan node
* ----------------
*/
- if (result) {
- iptr = &result->heap_iptr;
- tuple = heap_fetch(heapRelation,
- NowTimeQual,
- iptr,
- &buffer);
- /* be tidy */
- pfree(result);
-
- if (tuple == NULL) {
+ 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;
+ }
+
/* ----------------
- * we found a deleted tuple, so keep on scanning..
+ * if we get here it means the index scan failed so we
+ * are at the end of the scan..
* ----------------
*/
- 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;
+ return ExecClearTuple(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)
+ * 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.
+ * 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)
+ExecIndexScan(IndexScan * node)
{
- TupleTableSlot *returnTuple;
+ TupleTableSlot *returnTuple;
- /* ----------------
- * use IndexNext as access method
- * ----------------
- */
- returnTuple = ExecScan(&node->scan, IndexNext);
- return returnTuple;
-}
+ /* ----------------
+ * use IndexNext as access method
+ * ----------------
+ */
+ returnTuple = ExecScan(&node->scan, IndexNext);
+ return returnTuple;
+}
/* ----------------------------------------------------------------
- * ExecIndexReScan(node)
+ * 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.
+ * 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)
+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;
- if (isNull) {
- scan_keys[j].sk_flags |= SK_ISNULL;
- } else {
- scan_keys[j].sk_flags &= ~SK_ISNULL;
+ 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;
+ if (isNull)
+ {
+ scan_keys[j].sk_flags |= SK_ISNULL;
+ }
+ else
+ {
+ scan_keys[j].sk_flags &= ~SK_ISNULL;
+ }
+ }
}
- }
}
- }
-
- /*
- * 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;
+
+ /*
+ * 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
+ * ExecEndIndexScan
*
* old comments
- * Releases any storage allocated through C routines.
- * Returns nothing.
+ * Releases any storage allocated through C routines.
+ * Returns nothing.
* ----------------------------------------------------------------
*/
void
-ExecEndIndexScan(IndexScan *node)
+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); */
+ 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
+ * ExecIndexMarkPos
*
* old comments
- * Marks scan position by marking the current index.
- * Returns nothing.
+ * Marks scan position by marking the current index.
+ * Returns nothing.
* ----------------------------------------------------------------
*/
void
-ExecIndexMarkPos(IndexScan *node)
+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;
+ 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
+ * ExecIndexRestrPos
*
* old comments
- * Restores scan position by restoring the current index.
- * Returns nothing.
- *
- * XXX Assumes previously marked scan position belongs to current index
+ * Restores scan position by restoring the current index.
+ * Returns nothing.
+ *
+ * XXX Assumes previously marked scan position belongs to current index
* ----------------------------------------------------------------
*/
void
-ExecIndexRestrPos(IndexScan *node)
+ExecIndexRestrPos(IndexScan * node)
{
- IndexScanState *indexstate;
- IndexScanDescPtr indexScanDescs;
- IndexScanDesc scanDesc;
- int indexPtr;
+ IndexScanState *indexstate;
+ IndexScanDescPtr indexScanDescs;
+ IndexScanDesc scanDesc;
+ int indexPtr;
- indexstate = node->indxstate;
- indexPtr = indexstate->iss_IndexPtr;
- indexScanDescs = indexstate->iss_ScanDescs;
- scanDesc = indexScanDescs[ indexPtr ];
+ indexstate = node->indxstate;
+ indexPtr = indexstate->iss_IndexPtr;
+ indexScanDescs = indexstate->iss_ScanDescs;
+ scanDesc = indexScanDescs[indexPtr];
- IndexScanRestorePosition( scanDesc );
+ IndexScanRestorePosition(scanDesc);
}
/* ----------------------------------------------------------------
- * ExecInitIndexScan
+ * ExecInitIndexScan
+ *
+ * Initializes the index scan's state information, creates
+ * scan keys, and opens the base and index relations.
*
- * 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.
*
- * 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.
+ * 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)
+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);
+ 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;
+ scanstate->ss_ProcOuterFlag = false;
+ scanstate->ss_OldRelId = 0;
*/
- node->scan.scanstate = scanstate;
+ 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;
+ /* ----------------
+ * 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);
+ /* ----------------
+ * 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));
- run_keys = (n_keys <= 0) ? NULL :
- (int *)palloc(n_keys * sizeof(int));
-
- CXT1_printf("ExecInitIndexScan: context is %d\n",
- CurrentMemoryContext);
-
/* ----------------
- * for each opclause in the given qual,
- * convert each qual's opclause into a single scan key
+ * tuple table initialization
* ----------------
*/
- 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 ... */
- bits16 flags = 0;
-
- int scanvar; /* which var identifies varattno */
- AttrNumber varattno = 0; /* att number used in scan */
- Oid opid; /* operator id used in scan */
- Datum scanvalue = 0; /* 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)) {
+ 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));
+ run_keys = (n_keys <= 0) ? NULL :
+ (int *) palloc(n_keys * sizeof(int));
+
+ CXT1_printf("ExecInitIndexScan: context is %d\n",
+ CurrentMemoryContext);
+
/* ----------------
- * if the leftop is a const node then it means
- * it identifies the value to place in our scan key.
+ * for each opclause in the given qual,
+ * convert each qual's opclause into a single scan key
* ----------------
*/
- run_keys[ j ] = NO_OP;
- scanvalue = ((Const*) leftop)->constvalue;
+ 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 ... */
+ bits16 flags = 0;
+
+ int scanvar; /* which var identifies varattno */
+ AttrNumber varattno = 0; /* att number used in scan */
+ Oid opid; /* operator id used in scan */
+ Datum scanvalue = 0; /* 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;
#ifdef INDEXSCAN_PATCH
- } else if (IsA(leftop,Param)) {
- bool isnull;
- /* ----------------
- * if the leftop is a Param node then it means
- * it identifies the value to place in our scan key.
- * ----------------
- */
- run_keys[ j ] = NO_OP;
- scanvalue = ExecEvalParam((Param*) leftop,
- scanstate->cstate.cs_ExprContext,
- &isnull);
- if ( isnull )
- flags |= SK_ISNULL;
+ }
+ else if (IsA(leftop, Param))
+ {
+ bool isnull;
+
+ /* ----------------
+ * if the leftop is a Param node then it means
+ * it identifies the value to place in our scan key.
+ * ----------------
+ */
+ run_keys[j] = NO_OP;
+ scanvalue = ExecEvalParam((Param *) leftop,
+ scanstate->cstate.cs_ExprContext,
+ &isnull);
+ if (isnull)
+ flags |= SK_ISNULL;
#endif
- } 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 (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;
#ifdef INDEXSCAN_PATCH
- } else if (IsA(rightop,Param)) {
- bool isnull;
- /* ----------------
- * if the rightop is a Param node then it means
- * it identifies the value to place in our scan key.
- * ----------------
- */
- run_keys[ j ] = NO_OP;
- scanvalue = ExecEvalParam((Param*) rightop,
- scanstate->cstate.cs_ExprContext,
- &isnull);
- if ( isnull )
- flags |= SK_ISNULL;
+ }
+ else if (IsA(rightop, Param))
+ {
+ bool isnull;
+
+ /* ----------------
+ * if the rightop is a Param node then it means
+ * it identifies the value to place in our scan key.
+ * ----------------
+ */
+ run_keys[j] = NO_OP;
+ scanvalue = ExecEvalParam((Param *) rightop,
+ scanstate->cstate.cs_ExprContext,
+ &isnull);
+ if (isnull)
+ flags |= SK_ISNULL;
#endif
- } 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 {
+ }
+ 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],
+ flags,
+ varattno, /* attribute number to
+ * scan */
+ (RegProcedure) opid, /* reg proc to use */
+ (Datum) scanvalue); /* constant */
+ }
+
/* ----------------
- * otherwise, the leftop contains information usable
- * at runtime to figure out the value to place in our
- * scan key.
+ * store the key information into our array.
* ----------------
*/
- 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],
- flags,
- varattno, /* attribute number to scan */
- (RegProcedure) opid, /* reg proc to use */
- (Datum) scanvalue); /* constant */
+ 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;
+
/* ----------------
- * store the key information into our array.
+ * 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
* ----------------
*/
- 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)
+ if (have_runtime_keys)
{
- indexstate->iss_RuntimeKeyInfo = (Pointer) runtimeKeyInfo;
+ 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]);
+ 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);
}
- 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)nthi(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 */
+
+ /* ----------------
+ * 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) nthi(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->iss_RelationDescs = relationDescs;
+ indexstate->iss_ScanDescs = scanDescs;
- indexstate->cstate.cs_TupFromTlist = false;
+ indexstate->cstate.cs_TupFromTlist = false;
- /* ----------------
- * all done.
- * ----------------
- */
- return TRUE;
+ /* ----------------
+ * all done.
+ * ----------------
+ */
+ return TRUE;
}
int
-ExecCountSlotsIndexScan(IndexScan *node)
+ExecCountSlotsIndexScan(IndexScan * node)
{
- return ExecCountSlotsNode(outerPlan((Plan *)node)) +
- ExecCountSlotsNode(innerPlan((Plan *)node)) +
- INDEXSCAN_NSLOTS;
+ return ExecCountSlotsNode(outerPlan((Plan *) node)) +
+ ExecCountSlotsNode(innerPlan((Plan *) node)) +
+ INDEXSCAN_NSLOTS;
}
diff --git a/src/backend/executor/nodeMaterial.c b/src/backend/executor/nodeMaterial.c
index daf324bb77a..49ba73d3bf0 100644
--- a/src/backend/executor/nodeMaterial.c
+++ b/src/backend/executor/nodeMaterial.c
@@ -1,21 +1,21 @@
/*-------------------------------------------------------------------------
*
* nodeMaterial.c--
- * Routines to handle materialization nodes.
+ * 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.6 1997/08/20 14:53:24 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeMaterial.c,v 1.7 1997/09/07 04:41:36 momjian Exp $
*
*-------------------------------------------------------------------------
*/
/*
* INTERFACE ROUTINES
- * ExecMaterial - generate a temporary relation
- * ExecInitMaterial - initialize node and subnodes..
- * ExecEndMaterial - shutdown node and subnodes
+ * ExecMaterial - generate a temporary relation
+ * ExecInitMaterial - initialize node and subnodes..
+ * ExecEndMaterial - shutdown node and subnodes
*
*/
#include "postgres.h"
@@ -29,368 +29,373 @@
#include "access/heapam.h"
/* ----------------------------------------------------------------
- * ExecMaterial
+ * 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.
+ * 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:
+ * Initial State:
*
- * ExecMaterial assumes the temporary relation has been
- * created and openend by ExecInitMaterial during the prior
- * InitPlan() phase.
+ * 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)
+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) {
+ EState *estate;
+ MaterialState *matstate;
+ Plan *outerNode;
+ ScanDirection dir;
+ Relation tempRelation;
+ Relation currentRelation;
+ HeapScanDesc currentScanDesc;
+ HeapTuple heapTuple;
+ TupleTableSlot *slot;
+ Buffer buffer;
+
/* ----------------
- * set all relations to be scanned in the forward direction
- * while creating the temporary relation.
+ * get state info from node
* ----------------
*/
- estate->es_direction = ForwardScanDirection;
-
+ matstate = node->matstate;
+ estate = node->plan.state;
+ dir = estate->es_direction;
+
/* ----------------
- * if we couldn't create the temp or current relations then
- * we print a warning and return NULL.
+ * 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.
* ----------------
*/
- 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;
+
+ if (matstate->mat_Flag == false)
+ {
+ /* ----------------
+ * set all relations to be scanned in the forward direction
+ * while creating the temporary relation.
+ * ----------------
+ */
+ estate->es_direction = ForwardScanDirection;
+
+ /* ----------------
+ * 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;
}
-
+
/* ----------------
- * retrieve tuples from the subplan and
- * insert them in the temporary relation
+ * at this point we know we have a sorted relation so
+ * we preform a simple scan on it with amgetnext()..
* ----------------
*/
- 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;
-
+ currentScanDesc = matstate->csstate.css_currentScanDesc;
+
+ heapTuple = heap_getnext(currentScanDesc, /* scan desc */
+ ScanDirectionIsBackward(dir),
+ /* bkwd flag */
+ &buffer); /* return: buffer */
+
/* ----------------
- * restore to user specified direction
+ * 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..
* ----------------
*/
- estate->es_direction = dir;
-
+ 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; */
+
/* ----------------
- * now initialize the scan descriptor to scan the
- * sorted relation and update the sortstate information
+ * assign the node's execution state
* ----------------
*/
- 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));
-
+ node->plan.state = estate;
+
/* ----------------
- * finally set the sorted flag to true
+ * create state structure
* ----------------
*/
- 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 */
-
-}
+ 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);
-/* ----------------------------------------------------------------
- * 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;
+ /* ----------------
+ * 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)
+ExecCountSlotsMaterial(Material * node)
{
- return ExecCountSlotsNode(outerPlan((Plan *)node)) +
- ExecCountSlotsNode(innerPlan((Plan *)node)) +
- MATERIAL_NSLOTS;
+ return ExecCountSlotsNode(outerPlan((Plan *) node)) +
+ ExecCountSlotsNode(innerPlan((Plan *) node)) +
+ MATERIAL_NSLOTS;
}
/* ----------------------------------------------------------------
- * ExecEndMaterial
+ * ExecEndMaterial
*
* old comments
- * destroys the temporary relation.
+ * destroys the temporary relation.
* ----------------------------------------------------------------
*/
void
-ExecEndMaterial(Material *node)
+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);
-}
-
-#ifdef NOT_USED /* not used */
+ 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);
+}
+
+#ifdef NOT_USED /* not used */
/* ----------------------------------------------------------------
- * ExecMaterialMarkPos
+ * ExecMaterialMarkPos
* ----------------------------------------------------------------
*/
-List /* nothing of interest */
+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)
+ 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;
-
- /* ----------------
- * 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
+ * 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);
+ 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
+#endif
diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c
index 9151479d306..348d3fa1e00 100644
--- a/src/backend/executor/nodeMergejoin.c
+++ b/src/backend/executor/nodeMergejoin.c
@@ -1,78 +1,78 @@
/*-------------------------------------------------------------------------
*
* nodeMergejoin.c--
- * routines supporting merge joins
+ * routines supporting merge joins
*
* Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/nodeMergejoin.c,v 1.8 1997/08/19 21:31:10 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeMergejoin.c,v 1.9 1997/09/07 04:41:37 momjian Exp $
*
*-------------------------------------------------------------------------
*/
/*
* INTERFACE ROUTINES
- * ExecMergeJoin mergejoin outer and inner relations.
- * ExecInitMergeJoin creates and initializes run time states
- * ExecEndMergeJoin cleand up the node.
+ * 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).
+ * 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
- * } -
- * } -
- * } -
+ * 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 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
- * } -
+ * 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
+ * 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
+ * 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
+ * Update: The executor tuple table has long since alleviated the
+ * problem described above -cim 4/23/91
*
*/
#include "postgres.h"
@@ -84,1134 +84,1151 @@
#include "utils/lsyscache.h"
#include "utils/psort.h"
-static bool MergeCompare(List *eqQual, List *compareQual, ExprContext *econtext);
+static bool MergeCompare(List * eqQual, List * compareQual, ExprContext * econtext);
/* ----------------------------------------------------------------
- * MarkInnerTuple and RestoreInnerTuple macros
+ * 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.
+ * 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
+ * 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.
+ * 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); \
+ 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)
+ ExecStoreTuple(markedTupleSlot->val, \
+ innerTupleSlot, \
+ markedTupleSlot->ttc_buffer, \
+ false); \
+ ExecIncrSlotBufferRefcnt(innerTupleSlot)
/* ----------------------------------------------------------------
- * MJFormOSortopI
+ * 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.
+ * 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".
+ * 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)
+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);
-
+ List *qualCopy;
+ List *qualcdr;
+ Expr *qual;
+ Oper *op;
+
/* ----------------
- * now get at the 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()...
* ----------------
*/
- op = (Oper*)qual->oper;
- if (!IsA(op,Oper)) {
- elog(DEBUG, "MJFormOSortopI: op not an Oper!");
- return NIL;
+ 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;
}
-
- /* ----------------
- * 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;
+
+ return qualCopy;
}
-
+
/* ----------------------------------------------------------------
- * MJFormISortopO
+ * 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))
+ * 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.
- * ----------------------------------------------------------------
+ * 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.
+ * ----------------------------------------------------------------
*/
-static List *
-MJFormISortopO(List *qualList, Oid sortOp)
+static 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;
+ 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) ...}.
+ * MergeCompare
*
- * (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.
+ * 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.
* ----------------------------------------------------------------
*/
-static bool
-MergeCompare(List *eqQual, List *compareQual, ExprContext *econtext)
+static 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) {
+ List *clause;
+ List *eqclause;
+ Datum const_value;
+ bool isNull;
+ bool isDone;
+
/* ----------------
- * first test if our compare clause is satisified.
- * if so then return true. ignore isDone, don't iterate in
- * quals.
+ * if we have no compare qualification, return nil
* ----------------
*/
- const_value = (Datum)
- ExecEvalExpr((Node*) lfirst(clause), econtext, &isNull, &isDone);
-
- if (DatumGetInt32(const_value) != 0)
- return true;
-
+ if (compareQual == NIL)
+ return false;
+
/* ----------------
- * 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.
+ * for each pair of clauses, test them until
+ * our compare conditions are satisified
* ----------------
*/
- 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;
+ 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
+ * ExecMergeTupleDump
*
- * This function is called through the MJ_dump() macro
- * when EXEC_MERGEJOINDEBUG is defined
+ * This function is called through the MJ_dump() macro
+ * when EXEC_MERGEJOINDEBUG is defined
* ----------------------------------------------------------------
*/
#ifdef EXEC_MERGEJOINDEBUG
void
-ExecMergeTupleDumpInner(ExprContext *econtext)
+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);
+ 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)
+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);
+ 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)
+ExecMergeTupleDumpMarked(ExprContext * econtext,
+ MergeJoinState * mergestate)
{
- TupleTableSlot *markedSlot;
+ TupleTableSlot *markedSlot;
- printf("==== marked tuple ====\n");
- markedSlot = mergestate->mj_MarkedTupleSlot;
+ printf("==== marked tuple ====\n");
+ markedSlot = mergestate->mj_MarkedTupleSlot;
- if (TupIsNull(markedSlot))
- printf("(nil)\n");
- else
- debugtup(markedSlot->val,
- markedSlot->ttc_tupleDescriptor);
+ if (TupIsNull(markedSlot))
+ printf("(nil)\n");
+ else
+ debugtup(markedSlot->val,
+ markedSlot->ttc_tupleDescriptor);
}
void
-ExecMergeTupleDump(ExprContext *econtext, MergeJoinState *mergestate)
+ExecMergeTupleDump(ExprContext * econtext, MergeJoinState * mergestate)
{
- printf("******** ExecMergeTupleDump ********\n");
-
- ExecMergeTupleDumpInner(econtext);
- ExecMergeTupleDumpOuter(econtext);
- ExecMergeTupleDumpMarked(econtext, mergestate);
-
- printf("******** \n");
+ printf("******** ExecMergeTupleDump ********\n");
+
+ ExecMergeTupleDumpInner(econtext);
+ ExecMergeTupleDumpOuter(econtext);
+ ExecMergeTupleDumpMarked(econtext, mergestate);
+
+ printf("******** \n");
}
+
#endif
-
+
static void
-CleanUpSort(Plan *plan) {
-
- if (plan == NULL)
- return;
-
- if (plan->type == T_Sort) {
- Sort *sort = (Sort *)plan;
- psort_end(sort);
- }
+CleanUpSort(Plan * plan)
+{
+
+ if (plan == NULL)
+ return;
+
+ if (plan->type == T_Sort)
+ {
+ Sort *sort = (Sort *) plan;
+
+ psort_end(sort);
+ }
}
/* ----------------------------------------------------------------
- * ExecMergeJoin
+ * 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.
+ * 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)
+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 (;;) {
+ 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 the current state of the join and do things accordingly.
- * Note: The join states are highlighted with 32-* comments for
- * improved readability.
+ * get information from node
* ----------------
*/
- 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).
- * ********************************
+ 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
+ * ----------------
*/
- 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;
+ if (mergestate->jstate.cs_TupFromTlist)
+ {
TupleTableSlot *result;
- bool isDone;
-
- MJ_printf("ExecMergeJoin: **** returning tuple ****\n");
-
+ ProjectionInfo *projInfo;
+ bool isDone;
+
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");
- CleanUpSort(node->join.lefttree->lefttree);
- CleanUpSort(node->join.righttree->lefttree);
- 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 (!isDone)
+ return result;
+ }
+ for (;;)
+ {
/* ----------------
- * if the inner tuple is null then we know
- * we have to restore the inner scan
- * and advance to the next outer tuple
+ * get the current state of the join and do things accordingly.
+ * Note: The join states are highlighted with 32-* comments for
+ * improved readability.
* ----------------
*/
- 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;
+ 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");
+ CleanUpSort(node->join.lefttree->lefttree);
+ CleanUpSort(node->join.righttree->lefttree);
+ 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;
}
-
- /* ----------------
- * 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
+ * ExecInitMergeJoin
*
* old comments
- * Creates the run-time state information for the node and
- * sets the relation id to contain relevant decriptors.
+ * 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)
+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);
+ 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;
+ /* ----------------
+ * 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)
+ExecCountSlotsMergeJoin(MergeJoin * node)
{
- return ExecCountSlotsNode(outerPlan((Plan *)node)) +
- ExecCountSlotsNode(innerPlan((Plan *)node)) +
- MERGEJOIN_NSLOTS;
+ return ExecCountSlotsNode(outerPlan((Plan *) node)) +
+ ExecCountSlotsNode(innerPlan((Plan *) node)) +
+ MERGEJOIN_NSLOTS;
}
-
+
/* ----------------------------------------------------------------
- * ExecEndMergeJoin
+ * ExecEndMergeJoin
*
* old comments
- * frees storage allocated through C routines.
+ * frees storage allocated through C routines.
* ----------------------------------------------------------------
*/
void
-ExecEndMergeJoin(MergeJoin *node)
+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");
+ 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/nodeNestloop.c b/src/backend/executor/nodeNestloop.c
index d83d306bba9..e7cba2e756e 100644
--- a/src/backend/executor/nodeNestloop.c
+++ b/src/backend/executor/nodeNestloop.c
@@ -1,21 +1,21 @@
/*-------------------------------------------------------------------------
*
* nodeNestloop.c--
- * routines to support nest-loop joins
+ * 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.3 1996/11/08 05:56:15 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeNestloop.c,v 1.4 1997/09/07 04:41:41 momjian Exp $
*
*-------------------------------------------------------------------------
*/
/*
- * INTERFACE ROUTINES
- * ExecNestLoop - process a nestloop join of two plans
- * ExecInitNestLoop - initialize the join
- * ExecEndNestLoop - shut down the join
+ * INTERFACE ROUTINES
+ * ExecNestLoop - process a nestloop join of two plans
+ * ExecInitNestLoop - initialize the join
+ * ExecEndNestLoop - shut down the join
*/
#include "postgres.h"
@@ -25,349 +25,363 @@
#include "executor/nodeIndexscan.h"
/* ----------------------------------------------------------------
- * ExecNestLoop(node)
+ * ExecNestLoop(node)
*
* old comments
- * Returns the tuple joined from inner and outer tuples which
- * satisfies the qualification clause.
+ * 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.
+ * 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.
+ * 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 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.
+ * 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)
+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(;;) {
+ NestLoopState *nlstate;
+ Plan *innerPlan;
+ Plan *outerPlan;
+ bool needNewOuterTuple;
+
+ TupleTableSlot *outerTupleSlot;
+ TupleTableSlot *innerTupleSlot;
+
+ List *qual;
+ bool qualResult;
+ ExprContext *econtext;
+
/* ----------------
- * The essential idea now is to get the next inner tuple
- * and join it with the current outer tuple.
+ * get information from the node
* ----------------
*/
- needNewOuterTuple = false;
-
+ ENL1_printf("getting info from node");
+
+ nlstate = node->nlstate;
+ qual = node->join.qual;
+ outerPlan = outerPlan(&node->join);
+ innerPlan = innerPlan(&node->join);
+
/* ----------------
- * 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.
+ * initialize expression context
* ----------------
*/
- 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.
+ econtext = nlstate->jstate.cs_ExprContext;
+
+ /* ---------------- * get the current outer 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;
- }
- }
-
+ outerTupleSlot = nlstate->jstate.cs_OuterTupleSlot;
+ econtext->ecxt_outertuple = outerTupleSlot;
+
/* ----------------
- * loop until we have a new outer tuple and a new
- * inner tuple.
+ * Ok, everything is setup for the join so now loop until
+ * we return a qualifying join 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");
+
+ 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;
- }
- } /* while (needNewOuterTuple) */
-
+
+ /* ----------------
+ * 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");
+
/* ----------------
- * 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.
+ * assign execution state to node
* ----------------
*/
- 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;
- }
-
+ node->join.state = estate;
+
/* ----------------
- * qualification failed so we have to try again..
+ * create new nest loop state
* ----------------
*/
- ENL1_printf("qualification failed, looping");
- }
-}
+ nlstate = makeNode(NestLoopState);
+ nlstate->nl_PortalFlag = false;
+ node->nlstate = nlstate;
-/* ----------------------------------------------------------------
- * 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);
+ /* ----------------
+ * 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;
+ /* ----------------
+ * 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)
+ExecCountSlotsNestLoop(NestLoop * node)
{
- return ExecCountSlotsNode(outerPlan(node)) +
- ExecCountSlotsNode(innerPlan(node)) +
- NESTLOOP_NSLOTS;
+ return ExecCountSlotsNode(outerPlan(node)) +
+ ExecCountSlotsNode(innerPlan(node)) +
+ NESTLOOP_NSLOTS;
}
/* ----------------------------------------------------------------
- * ExecEndNestLoop
- *
- * closes down scans and frees allocated storage
+ * ExecEndNestLoop
+ *
+ * closes down scans and frees allocated storage
* ----------------------------------------------------------------
*/
void
-ExecEndNestLoop(NestLoop *node)
+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");
+ 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/nodeResult.c b/src/backend/executor/nodeResult.c
index f4553dcc7b7..743bd73f2b3 100644
--- a/src/backend/executor/nodeResult.c
+++ b/src/backend/executor/nodeResult.c
@@ -1,33 +1,33 @@
/*-------------------------------------------------------------------------
*
* nodeResult.c--
- * support for constant nodes needing special code.
+ * support for constant nodes needing special code.
*
* Copyright (c) 1994, Regents of the University of California
*
*
- * DESCRIPTION
+ * DESCRIPTION
*
- * Example: in constant queries where no relations are scanned,
- * the planner generates result nodes. Examples of such queries are:
+ * 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)
+ * retrieve (x = 1)
+ * and
+ * append emp (name = "mike", salary = 15000)
*
- * Result nodes are also used to optimise queries
- * with tautological qualifications like:
+ * Result nodes are also used to optimise queries
+ * with tautological qualifications like:
*
- * retrieve (emp.all) where 2 > 1
+ * retrieve (emp.all) where 2 > 1
*
- * In this case, the plan generated is
+ * In this case, the plan generated is
*
- * Result (with 2 > 1 qual)
- * /
- * SeqScan (emp.all)
+ * Result (with 2 > 1 qual)
+ * /
+ * SeqScan (emp.all)
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/nodeResult.c,v 1.2 1996/10/31 10:12:18 scrappy Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeResult.c,v 1.3 1997/09/07 04:41:42 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -38,253 +38,259 @@
#include "executor/nodeResult.h"
/* ----------------------------------------------------------------
- * ExecResult(node)
+ * 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
+ * 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.
+ * 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)
+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) {
+ ResultState *resstate;
+ TupleTableSlot *outerTupleSlot;
+ TupleTableSlot *resultSlot;
+ Plan *outerPlan;
+ ExprContext *econtext;
+ Node *qual;
+ bool qualResult;
+ bool isDone;
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.
+ * initialize the result node's state
* ----------------
*/
- 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;
- }
-
+ resstate = node->resstate;
+
/* ----------------
- * get the information to place into the expr context
+ * get the expression context
* ----------------
*/
- resstate = node->resstate;
-
- outerTupleSlot = resstate->cstate.cs_OuterTupleSlot;
-
+ econtext = resstate->cstate.cs_ExprContext;
+
/* ----------------
- * fill in the information in the expression context
- * XXX gross hack. use outer tuple as scan tuple
+ * check tautological qualifications like (2 > 1)
* ----------------
*/
- econtext->ecxt_outertuple = outerTupleSlot;
- econtext->ecxt_scantuple = outerTupleSlot;
-
+ 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;
+ }
+
/* ----------------
- * form the result tuple and pass it back using ExecProject()
+ * 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.
* ----------------
*/
- projInfo = resstate->cstate.cs_ProjInfo;
- resultSlot = ExecProject(projInfo, &isDone);
- resstate->cstate.cs_TupFromTlist = !isDone;
- return resultSlot;
- }
+ 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;
- 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).
+ * 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)
+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);
-
+ 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;
+ /* ----------------
+ * 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)
+ExecCountSlotsResult(Result * node)
{
- return ExecCountSlotsNode(outerPlan(node)) + RESULT_NSLOTS;
+ return ExecCountSlotsNode(outerPlan(node)) + RESULT_NSLOTS;
}
/* ----------------------------------------------------------------
- * ExecEndResult
- *
- * fees up storage allocated through C routines
+ * ExecEndResult
+ *
+ * fees up storage allocated through C routines
* ----------------------------------------------------------------
*/
void
-ExecEndResult(Result *node)
+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);
+ 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/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c
index b94bb58d260..d3451f8026f 100644
--- a/src/backend/executor/nodeSeqscan.c
+++ b/src/backend/executor/nodeSeqscan.c
@@ -1,25 +1,25 @@
/*-------------------------------------------------------------------------
*
* nodeSeqscan.c--
- * Support routines for sequential scans of relations.
+ * 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.4 1997/08/19 21:31:12 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeSeqscan.c,v 1.5 1997/09/07 04:41:44 momjian 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
+ * 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 "postgres.h"
@@ -30,429 +30,443 @@
#include "access/heapam.h"
#include "parser/parsetree.h"
-static Oid InitScanRelation(SeqScan *node, EState *estate,
- CommonScanState *scanstate, Plan *outerPlan);
+static Oid
+InitScanRelation(SeqScan * node, EState * estate,
+ CommonScanState * scanstate, Plan * outerPlan);
-static TupleTableSlot *SeqNext(SeqScan *node);
+static TupleTableSlot *SeqNext(SeqScan * node);
/* ----------------------------------------------------------------
- * Scan Support
+ * Scan Support
* ----------------------------------------------------------------
*/
/* ----------------------------------------------------------------
- * SeqNext
+ * SeqNext
*
- * This is a workhorse for ExecSeqScan
+ * This is a workhorse for ExecSeqScan
* ----------------------------------------------------------------
*/
static TupleTableSlot *
-SeqNext(SeqScan *node)
+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;
+ 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)
+ * 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.
*
- * 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)
+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;
+ 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
+ * InitScanRelation
*
- * This does the initialization for scan relations and
- * subplans of scans.
+ * This does the initialization for scan relations and
+ * subplans of scans.
* ----------------------------------------------------------------
*/
-static Oid
-InitScanRelation(SeqScan *node, EState *estate,
- CommonScanState *scanstate, Plan *outerPlan)
+static 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 {
+ 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;
+ }
+
/* ----------------
- * otherwise we are scanning tuples from the
- * outer subplan so we initialize the outer plan
- * and nullify
+ * return the relation
* ----------------
*/
- 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;
+ return reloid;
}
/* ----------------------------------------------------------------
- * ExecInitSeqScan
+ * 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.
+ * 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)
+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);
+ 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;
+ /* ----------------
+ * 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)
+ExecCountSlotsSeqScan(SeqScan * node)
{
- return ExecCountSlotsNode(outerPlan(node)) +
+ return ExecCountSlotsNode(outerPlan(node)) +
ExecCountSlotsNode(innerPlan(node)) +
- SEQSCAN_NSLOTS;
+ SEQSCAN_NSLOTS;
}
/* ----------------------------------------------------------------
- * ExecEndSeqScan
- *
- * frees any storage allocated through C routines.
- *| ...and also closes relations and/or shuts down outer subplan
- *| -cim 8/14/89
+ * 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)
+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);
+ 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
+ * Join Support
* ----------------------------------------------------------------
*/
/* ----------------------------------------------------------------
- * ExecSeqReScan
- *
- * Rescans the relation.
+ * ExecSeqReScan
+ *
+ * Rescans the relation.
* ----------------------------------------------------------------
*/
void
-ExecSeqReScan(SeqScan *node, ExprContext *exprCtxt, Plan* parent)
+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;
- }
+ 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.
+ * ExecSeqMarkPos(node)
+ *
+ * Marks scan position.
* ----------------------------------------------------------------
*/
void
-ExecSeqMarkPos(SeqScan *node)
+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);
+ 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;
- }
-
- /* ----------------
- * 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.
+ * ExecSeqRestrPos
+ *
+ * Restores scan position.
* ----------------------------------------------------------------
*/
void
-ExecSeqRestrPos(SeqScan *node)
+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);
+ 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/nodeSort.c b/src/backend/executor/nodeSort.c
index 0955108b2fb..eb2e2e7b180 100644
--- a/src/backend/executor/nodeSort.c
+++ b/src/backend/executor/nodeSort.c
@@ -1,13 +1,13 @@
/*-------------------------------------------------------------------------
*
* nodeSort.c--
- * Routines to handle sorting of relations.
+ * Routines to handle sorting of relations.
*
* Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/nodeSort.c,v 1.6 1997/08/21 02:28:06 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeSort.c,v 1.7 1997/09/07 04:41:45 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -25,383 +25,389 @@
#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.
+ * FormSortKeys(node)
+ *
+ * Forms the structure containing information used to sort the relation.
+ *
+ * Returns an array of ScanKeyData.
* ----------------------------------------------------------------
*/
-static ScanKey
-FormSortKeys(Sort *sortnode)
+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);
+ 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;
+
+ return sortkeys;
}
/* ----------------------------------------------------------------
- * ExecSort
+ * ExecSort
*
* old comments
- * Sorts tuples from the outer subtree of the node in psort,
- * which saves the results in a temporary file or memory. After the
- * initial call, returns a tuple from the file with each call.
- * Assumes that heap access method is used.
- *
- * Conditions:
- * -- none.
- *
- * Initial States:
- * -- the outer child is prepared to return the first tuple.
+ * Sorts tuples from the outer subtree of the node in psort,
+ * which saves the results in a temporary file or memory. After the
+ * initial call, returns a tuple from the file with each call.
+ * 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)
+ExecSort(Sort * node)
{
- EState *estate;
- SortState *sortstate;
- Plan *outerNode;
- ScanDirection dir;
- int keycount;
- ScanKey sortkeys;
- HeapTuple heapTuple;
- TupleTableSlot *slot;
-
- /* ----------------
- * 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, psort sorts this into a file.
- * Subsequent calls return tuples from psort.
- * ----------------
- */
-
- if (sortstate->sort_Flag == false) {
- SO1_printf("ExecSort: %s\n",
- "sortstate == false -> sorting subplan");
+ EState *estate;
+ SortState *sortstate;
+ Plan *outerNode;
+ ScanDirection dir;
+ int keycount;
+ ScanKey sortkeys;
+ HeapTuple heapTuple;
+ TupleTableSlot *slot;
+
/* ----------------
- * set all relations to be scanned in the forward direction
- * while creating the temporary relation.
+ * get state info from node
* ----------------
*/
- estate->es_direction = ForwardScanDirection;
+ SO1_printf("ExecSort: %s\n",
+ "entering routine");
+
+ sortstate = node->sortstate;
+ estate = node->plan.state;
+ dir = estate->es_direction;
/* ----------------
- * prepare information for psort_begin()
+ * the first time we call this, psort sorts this into a file.
+ * Subsequent calls return tuples from psort.
* ----------------
*/
- outerNode = outerPlan((Plan *) node);
- keycount = node->keycount;
- sortkeys = (ScanKey)sortstate->sort_Keys;
- SO1_printf("ExecSort: %s\n",
- "calling psort_begin");
-
- if (!psort_begin(node, /* this node */
- keycount, /* number keys */
- sortkeys)) /* keys */
+ if (sortstate->sort_Flag == false)
{
- /* Psort says, there are no tuples to be sorted */
- return NULL;
- }
-
- /* ----------------
- * restore to user specified direction
- * ----------------
- */
- estate->es_direction = dir;
-
- /* ----------------
- * make sure the tuple descriptor is up to date
- * ----------------
- */
- slot = (TupleTableSlot*)sortstate->csstate.cstate.cs_ResultTupleSlot;
- /* *** get_cs_ResultTupleSlot((CommonState) sortstate); */
+ 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 = ForwardScanDirection;
+
+ /* ----------------
+ * prepare information for psort_begin()
+ * ----------------
+ */
+ outerNode = outerPlan((Plan *) node);
+
+ keycount = node->keycount;
+ sortkeys = (ScanKey) sortstate->sort_Keys;
+ SO1_printf("ExecSort: %s\n",
+ "calling psort_begin");
+
+ if (!psort_begin(node, /* this node */
+ keycount, /* number keys */
+ sortkeys)) /* keys */
+ {
+ /* Psort says, there are no tuples to be sorted */
+ return NULL;
+ }
+
+ /* ----------------
+ * restore to user specified direction
+ * ----------------
+ */
+ estate->es_direction = dir;
- slot->ttc_tupleDescriptor = ExecGetTupType(outerNode);
+ /* ----------------
+ * make sure the tuple descriptor is up to date
+ * ----------------
+ */
+ slot = (TupleTableSlot *) sortstate->csstate.cstate.cs_ResultTupleSlot;
+ /* *** get_cs_ResultTupleSlot((CommonState) sortstate); */
+
+ slot->ttc_tupleDescriptor = ExecGetTupType(outerNode);
#if 0
- slot->ttc_execTupDescriptor = ExecGetExecTupDesc(outerNode);
+ slot->ttc_execTupDescriptor = ExecGetExecTupDesc(outerNode);
#endif
+ /* ----------------
+ * finally set the sorted flag to true
+ * ----------------
+ */
+ sortstate->sort_Flag = true;
+ SO1_printf(stderr, "ExecSort: sorting done.\n");
+ }
+ else
+ {
+ slot = (TupleTableSlot *) sortstate->csstate.cstate.cs_ResultTupleSlot;
+ /* *** get_cs_ResultTupleSlot((CommonState) sortstate); */
+/* slot = sortstate->csstate.css_ScanTupleSlot; orig */
+ }
+
+ SO1_printf("ExecSort: %s\n",
+ "retrieving tuple from sorted relation");
+
/* ----------------
- * finally set the sorted flag to true
+ * at this point we grab a tuple from psort
* ----------------
*/
- sortstate->sort_Flag = true;
- SO1_printf(stderr, "ExecSort: sorting done.\n");
- }
- else {
- slot = (TupleTableSlot*)sortstate->csstate.cstate.cs_ResultTupleSlot;
- /* *** get_cs_ResultTupleSlot((CommonState) sortstate); */
-/* slot = sortstate->csstate.css_ScanTupleSlot; orig */
- }
-
- SO1_printf("ExecSort: %s\n",
- "retrieving tuple from sorted relation");
-
- /* ----------------
- * at this point we grab a tuple from psort
- * ----------------
- */
- heapTuple = psort_grabtuple(node);
-
- if (heapTuple == NULL) {
-/* psort_end(node); */
- return (ExecClearTuple(slot));
- }
-
- ExecStoreTuple(heapTuple, /* tuple to store */
- slot, /* slot to store in */
- InvalidBuffer, /* no buffer */
- true); /* free the palloc'd tuple */
-/* printf("ExecSort: (%x)",node);print_slot(slot);printf("\n");*/
- return slot;
+ heapTuple = psort_grabtuple(node);
+
+ if (heapTuple == NULL)
+ {
+/* psort_end(node); */
+ return (ExecClearTuple(slot));
+ }
+
+ ExecStoreTuple(heapTuple, /* tuple to store */
+ slot, /* slot to store in */
+ InvalidBuffer, /* no buffer */
+ true); /* free the palloc'd tuple */
+/* printf("ExecSort: (%x)",node);print_slot(slot);printf("\n");*/
+ return slot;
#if 0
- return ExecStoreTuple(heapTuple, /* tuple to store */
- slot, /* slot to store in */
- InvalidBuffer, /* no buffer */
- true); /* free the palloc'd tuple */
+ return ExecStoreTuple(heapTuple, /* tuple to store */
+ slot, /* slot to store in */
+ InvalidBuffer, /* no buffer */
+ true);/* free the palloc'd tuple */
#endif
}
/* ----------------------------------------------------------------
- * ExecInitSort
+ * ExecInitSort
*
* old comments
- * Creates the run-time state information for the sort node
- * produced by the planner and initailizes its outer subtree.
+ * 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)
+ExecInitSort(Sort * node, EState * estate, Plan * parent)
{
- SortState *sortstate;
- Plan *outerPlan;
- ScanKey sortkeys;
-
- 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;
- node->cleaned = FALSE;
-
- 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);
-
+ SortState *sortstate;
+ Plan *outerPlan;
+ ScanKey sortkeys;
+
+ 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;
+ node->cleaned = FALSE;
+
+ 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.
- * ----------------
- */
- ExecInitResultTupleSlot(estate, &sortstate->csstate.cstate);
- ExecInitScanTupleSlot(estate, &sortstate->csstate);
-
- /* ----------------
- * 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.
- * ----------------
- */
- ExecAssignResultTypeFromOuterPlan((Plan *)node, &sortstate->csstate.cstate);
- ExecAssignScanTypeFromOuterPlan((Plan *) node, &sortstate->csstate);
- sortstate->csstate.cstate.cs_ProjInfo = NULL;
-
- 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;
+ /* ----------------
+ * tuple table initialization
+ *
+ * sort nodes only return scan tuples from their sorted
+ * relation.
+ * ----------------
+ */
+ ExecInitResultTupleSlot(estate, &sortstate->csstate.cstate);
+ ExecInitScanTupleSlot(estate, &sortstate->csstate);
+
+ /* ----------------
+ * 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.
+ * ----------------
+ */
+ ExecAssignResultTypeFromOuterPlan((Plan *) node, &sortstate->csstate.cstate);
+ ExecAssignScanTypeFromOuterPlan((Plan *) node, &sortstate->csstate);
+ sortstate->csstate.cstate.cs_ProjInfo = NULL;
+
+ 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)
+ExecCountSlotsSort(Sort * node)
{
- return ExecCountSlotsNode(outerPlan((Plan *)node)) +
- ExecCountSlotsNode(innerPlan((Plan *)node)) +
- SORT_NSLOTS;
+ return ExecCountSlotsNode(outerPlan((Plan *) node)) +
+ ExecCountSlotsNode(innerPlan((Plan *) node)) +
+ SORT_NSLOTS;
}
/* ----------------------------------------------------------------
- * ExecEndSort(node)
+ * ExecEndSort(node)
*
* old comments
* ----------------------------------------------------------------
*/
void
-ExecEndSort(Sort *node)
+ExecEndSort(Sort * node)
{
- SortState *sortstate;
- Plan *outerPlan;
-
- /* ----------------
- * get info from the sort state
- * ----------------
- */
- SO1_printf("ExecEndSort: %s\n",
- "shutting down sort node");
-
- sortstate = node->sortstate;
-
- /* ----------------
- * shut down the subplan
- * ----------------
- */
- outerPlan = outerPlan((Plan *) node);
- ExecEndNode(outerPlan, (Plan*)node);
-
- /* ----------------
- * clean out the tuple table
- * ----------------
- */
- ExecClearTuple(sortstate->csstate.css_ScanTupleSlot);
-
- /* Clean up after psort */
- psort_end(node);
-
- SO1_printf("ExecEndSort: %s\n",
- "sort node shutdown");
-}
+ SortState *sortstate;
+ Plan *outerPlan;
+
+ /* ----------------
+ * get info from the sort state
+ * ----------------
+ */
+ SO1_printf("ExecEndSort: %s\n",
+ "shutting down sort node");
+
+ sortstate = node->sortstate;
+
+ /* ----------------
+ * shut down the subplan
+ * ----------------
+ */
+ outerPlan = outerPlan((Plan *) node);
+ ExecEndNode(outerPlan, (Plan *) node);
+
+ /* ----------------
+ * clean out the tuple table
+ * ----------------
+ */
+ ExecClearTuple(sortstate->csstate.css_ScanTupleSlot);
+
+ /* Clean up after psort */
+ psort_end(node);
+
+ SO1_printf("ExecEndSort: %s\n",
+ "sort node shutdown");
+}
/* ----------------------------------------------------------------
- * ExecSortMarkPos
+ * ExecSortMarkPos
*
- * Calls psort to save the current position in the sorted file.
+ * Calls psort to save the current position in the sorted file.
* ----------------------------------------------------------------
*/
void
-ExecSortMarkPos(Sort *node)
+ExecSortMarkPos(Sort * node)
{
- SortState *sortstate;
-
- /* ----------------
- * if we haven't sorted yet, just return
- * ----------------
- */
- sortstate = node->sortstate;
- if (sortstate->sort_Flag == false)
- return;
-
- psort_markpos(node);
-
- return;
+ SortState *sortstate;
+
+ /* ----------------
+ * if we haven't sorted yet, just return
+ * ----------------
+ */
+ sortstate = node->sortstate;
+ if (sortstate->sort_Flag == false)
+ return;
+
+ psort_markpos(node);
+
+ return;
}
/* ----------------------------------------------------------------
- * ExecSortRestrPos
+ * ExecSortRestrPos
*
- * Calls psort to restore the last saved sort file position.
+ * Calls psort to restore the last saved sort file position.
* ----------------------------------------------------------------
*/
void
-ExecSortRestrPos(Sort *node)
+ExecSortRestrPos(Sort * node)
{
- SortState *sortstate;
-
- /* ----------------
- * 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
- * ----------------
- */
- psort_restorepos(node);
+ SortState *sortstate;
+
+ /* ----------------
+ * 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
+ * ----------------
+ */
+ psort_restorepos(node);
}
diff --git a/src/backend/executor/nodeTee.c b/src/backend/executor/nodeTee.c
index 6ddbecc3a49..8a1e233125a 100644
--- a/src/backend/executor/nodeTee.c
+++ b/src/backend/executor/nodeTee.c
@@ -1,21 +1,21 @@
/*-------------------------------------------------------------------------
*
* 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
+ * 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.6 1997/07/28 00:54:11 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/Attic/nodeTee.c,v 1.7 1997/09/07 04:41:46 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -25,9 +25,9 @@
#include "postgres.h"
#include "utils/palloc.h"
-#include "utils/relcache.h"
+#include "utils/relcache.h"
#include "utils/mcxt.h"
-#include "storage/bufmgr.h" /* for IncrBufferRefCount */
+#include "storage/bufmgr.h" /* for IncrBufferRefCount */
#include "storage/smgr.h"
#include "optimizer/internal.h"
#include "executor/executor.h"
@@ -38,475 +38,520 @@
#include "access/heapam.h"
/* ------------------------------------------------------------------
- * ExecInitTee
+ * ExecInitTee
*
- * Create tee state
+ * Create tee state
*
* ------------------------------------------------------------------
*/
bool
-ExecInitTee(Tee* node, EState *currentEstate, Plan * parent)
+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));
+ 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);
+ /* ----------------
+ * 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
- 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;
+ {
+ 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)
+int
+ExecCountSlotsTee(Tee * node)
{
- /* Tee nodes can't have innerPlans */
- return ExecCountSlotsNode(outerPlan(node)) + TEE_NSLOTS;
+ /* 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
+ 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
+ must open two separate scan descriptors,
+ because the left and right scans may be at different points
* ----------------------------------------------------------------
*/
-static void
-initTeeScanDescs(Tee* node)
+static 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);
+ 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)
+ * ExecTee(node)
+ *
*
+ * A Tee serves to connect a subplan to multiple parents.
+ * the subplan is always the outplan of the Tee 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
+ * 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)
+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*/
+ 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)
{
- 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;
+ teeState->tee_leftPlace = leftPlace + 1;
+ }
+ else
+ {
+ teeState->tee_rightPlace = rightPlace + 1;
+ }
+
+ return result;
}
/* ----------------------------------------------------------------
- * ExecTeeReScan(node)
- *
- * Rescans the relation.
+ * ExecTeeReScan(node)
+ *
+ * Rescans the relation.
* ----------------------------------------------------------------
*/
void
-ExecTeeReScan(Tee *node, ExprContext *exprCtxt, Plan *parent)
+ExecTeeReScan(Tee * node, ExprContext * exprCtxt, Plan * parent)
{
- EState *estate;
- TeeState *teeState;
- ScanDirection dir;
+ EState *estate;
+ TeeState *teeState;
+ ScanDirection dir;
- estate = ((Plan*)node)->state;
- teeState = node->teestate;
+ estate = ((Plan *) node)->state;
+ teeState = node->teestate;
- dir = estate->es_direction;
-
- /* XXX doesn't handle backwards direction yet */
+ dir = estate->es_direction;
- if (parent == node->leftParent) {
- if (teeState->tee_leftScanDesc)
+ /* 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
{
- heap_rescan(teeState->tee_leftScanDesc,
- ScanDirectionIsBackward(dir),
- NULL);
- teeState->tee_leftPlace = 0;
+ if (teeState->tee_rightScanDesc)
+ {
+ heap_rescan(teeState->tee_leftScanDesc,
+ ScanDirectionIsBackward(dir),
+ NULL);
+ teeState->tee_rightPlace = 0;
+ }
}
- }
- else
- {
- if (teeState->tee_rightScanDesc)
- {
- heap_rescan(teeState->tee_leftScanDesc,
- ScanDirectionIsBackward(dir),
- NULL);
- teeState->tee_rightPlace = 0;
- }
- }
}
/* ---------------------------------------------------------------------
- * ExecEndTee
+ * ExecEndTee
*
- * End the Tee node, and free up any storage
+ * 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)
+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);
- }
- else
- orig = 0;
-
- 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;
- }
+ 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);
+ }
+ else
+ orig = 0;
+
+ 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/nodeUnique.c b/src/backend/executor/nodeUnique.c
index 65c3bea76e0..75e40ccad96 100644
--- a/src/backend/executor/nodeUnique.c
+++ b/src/backend/executor/nodeUnique.c
@@ -1,25 +1,25 @@
/*-------------------------------------------------------------------------
*
* nodeUnique.c--
- * Routines to handle unique'ing of queries where appropriate
+ * 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.7 1997/08/26 23:31:44 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeUnique.c,v 1.8 1997/09/07 04:41:50 momjian Exp $
*
*-------------------------------------------------------------------------
*/
/*
* INTERFACE ROUTINES
- * ExecUnique - generate a unique'd temporary relation
- * ExecInitUnique - initialize node and subnodes..
- * ExecEndUnique - shutdown node and subnodes
+ * 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.
+ * Assumes tuples returned from subplan arrive in
+ * sorted order.
*
*/
#include <string.h>
@@ -31,296 +31,318 @@
#include "executor/nodeUnique.h"
#include "optimizer/clauses.h"
#include "access/heapam.h"
-#include "access/printtup.h" /* for typtoout() */
-#include "utils/builtins.h" /* for namecpy()*/
+#include "access/printtup.h" /* for typtoout() */
+#include "utils/builtins.h" /* for namecpy() */
/* ----------------------------------------------------------------
- * ExecIdenticalTuples
+ * 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
+ * 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)
+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;
+ 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
+ * ExecUnique
*
- * This is a very simple node which filters out duplicate
- * tuples from a stream of sorted tuples from a subplan.
+ * 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.
+ * XXX see comments below regarding freeing tuples.
* ----------------------------------------------------------------
*/
-TupleTableSlot * /* return: a tuple or NULL */
-ExecUnique(Unique *node)
+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-1]->atttypid);
- }
- else { /* keep compiler quiet */
- tupDesc = NULL;
- typoutput = 0;
- }
-
- /* ----------------
- * now loop, returning only non-duplicate tuples.
- * We assume that the tuples arrive in sorted order
- * so we can detect duplicates easily.
- * ----------------
- */
- for (;;) {
+ UniqueState *uniquestate;
+ TupleTableSlot *resultTupleSlot;
+ TupleTableSlot *slot;
+ Plan *outerPlan;
+ char *uniqueAttr;
+ AttrNumber uniqueAttrNum;
+ TupleDesc tupDesc;
+ Oid typoutput;
+
/* ----------------
- * fetch a tuple from the outer subplan
+ * get information from the node
* ----------------
*/
- slot = ExecProcNode(outerPlan, (Plan*)node);
- if (TupIsNull(slot))
- return NULL;
-
+ 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 - 1]->atttypid);
+ }
+ else
+ { /* keep compiler quiet */
+ tupDesc = NULL;
+ typoutput = 0;
+ }
+
/* ----------------
- * 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.
+ * now loop, returning only non-duplicate tuples.
+ * We assume that the tuples arrive in sorted order
+ * so we can detect duplicates easily.
* ----------------
*/
- if (TupIsNull(resultTupleSlot))
- break;
-
+ 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 - 1]->atttypid));
+ val2 = fmgr(typoutput, attr2, gettypelem(tupDesc->attrs[uniqueAttrNum - 1]->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;
+ }
+
+ }
+
/* ----------------
- * 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.
+ * 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);
- 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-1]->atttypid));
- val2 = fmgr(typoutput, attr2, gettypelem(tupDesc->attrs[uniqueAttrNum-1]->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;
+ return resultTupleSlot;
}
/* ----------------------------------------------------------------
- * ExecInitUnique
+ * ExecInitUnique
*
- * This initializes the unique node state structures and
- * the node's subplan.
+ * This initializes the unique node state structures and
+ * the node's subplan.
* ----------------------------------------------------------------
*/
-bool /* return: initialization status */
-ExecInitUnique(Unique *node, EState *estate, Plan *parent)
+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);
-
+ 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;
+ /* ------------
+ * 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)
+ExecCountSlotsUnique(Unique * node)
{
- return ExecCountSlotsNode(outerPlan(node)) +
+ return ExecCountSlotsNode(outerPlan(node)) +
ExecCountSlotsNode(innerPlan(node)) +
- UNIQUE_NSLOTS;
+ UNIQUE_NSLOTS;
}
/* ----------------------------------------------------------------
- * ExecEndUnique
+ * ExecEndUnique
*
- * This shuts down the subplan and frees resources allocated
- * to this node.
+ * This shuts down the subplan and frees resources allocated
+ * to this node.
* ----------------------------------------------------------------
*/
void
-ExecEndUnique(Unique *node)
+ExecEndUnique(Unique * node)
{
- UniqueState *uniquestate;
-
- uniquestate = node->uniquestate;
- ExecEndNode(outerPlan((Plan *) node), (Plan*)node);
- ExecClearTuple(uniquestate->cs_ResultTupleSlot);
-}
+ UniqueState *uniquestate;
+
+ uniquestate = node->uniquestate;
+ ExecEndNode(outerPlan((Plan *) node), (Plan *) node);
+ ExecClearTuple(uniquestate->cs_ResultTupleSlot);
+}
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index 74ace81171a..1d05a752d24 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -1,7 +1,7 @@
/*-------------------------------------------------------------------------
*
* spi.c--
- * Server Programming Interface
+ * Server Programming Interface
*
*-------------------------------------------------------------------------
*/
@@ -9,440 +9,447 @@
#include "access/printtup.h"
#include "fmgr.h"
-typedef struct {
- QueryTreeList *qtlist; /* malloced */
- uint32 processed; /* by Executor */
- SPITupleTable *tuptable;
- Portal portal; /* portal per procedure */
- MemoryContext savedcxt;
- CommandId savedId;
-} _SPI_connection;
-
-static Portal _SPI_portal = (Portal) NULL;
+typedef struct
+{
+ QueryTreeList *qtlist; /* malloced */
+ uint32 processed; /* by Executor */
+ SPITupleTable *tuptable;
+ Portal portal; /* portal per procedure */
+ MemoryContext savedcxt;
+ CommandId savedId;
+} _SPI_connection;
+
+static Portal _SPI_portal = (Portal) NULL;
static _SPI_connection *_SPI_stack = NULL;
static _SPI_connection *_SPI_current = NULL;
-static int _SPI_connected = -1;
-static int _SPI_curid = -1;
+static int _SPI_connected = -1;
+static int _SPI_curid = -1;
+
+uint32 SPI_processed = 0;
+SPITupleTable *SPI_tuptable;
+int SPI_result;
-uint32 SPI_processed = 0;
-SPITupleTable *SPI_tuptable;
-int SPI_result;
+void spi_printtup(HeapTuple tuple, TupleDesc tupdesc);
-void spi_printtup (HeapTuple tuple, TupleDesc tupdesc);
+typedef struct
+{
+ QueryTreeList *qtlist;
+ List *ptlist;
+ int nargs;
+ Oid *argtypes;
+} _SPI_plan;
-typedef struct {
- QueryTreeList *qtlist;
- List *ptlist;
- int nargs;
- Oid *argtypes;
-} _SPI_plan;
+static int _SPI_execute(char *src, int tcount, _SPI_plan * plan);
+static int _SPI_pquery(QueryDesc * queryDesc, EState * state, int tcount);
-static int _SPI_execute (char *src, int tcount, _SPI_plan *plan);
-static int _SPI_pquery (QueryDesc *queryDesc, EState *state, int tcount);
#if 0
-static void _SPI_fetch (FetchStmt *stmt);
+static void _SPI_fetch(FetchStmt * stmt);
+
#endif
-static int _SPI_execute_plan (_SPI_plan *plan,
- char **Values, char *Nulls, int tcount);
+static int
+_SPI_execute_plan(_SPI_plan * plan,
+ char **Values, char *Nulls, int tcount);
-static _SPI_plan *_SPI_copy_plan (_SPI_plan *plan, bool local);
+static _SPI_plan *_SPI_copy_plan(_SPI_plan * plan, bool local);
-static int _SPI_begin_call (bool execmem);
-static int _SPI_end_call (bool procmem);
-static MemoryContext _SPI_execmem (void);
-static MemoryContext _SPI_procmem (void);
-static bool _SPI_checktuples (bool isRetrieveIntoRelation);
+static int _SPI_begin_call(bool execmem);
+static int _SPI_end_call(bool procmem);
+static MemoryContext _SPI_execmem(void);
+static MemoryContext _SPI_procmem(void);
+static bool _SPI_checktuples(bool isRetrieveIntoRelation);
#ifdef SPI_EXECUTOR_STATS
-extern int ShowExecutorStats;
-extern void ResetUsage (void);
-extern void ShowUsage (void);
+extern int ShowExecutorStats;
+extern void ResetUsage(void);
+extern void ShowUsage(void);
+
#endif
int
-SPI_connect ()
+SPI_connect()
{
- char pname[64];
- PortalVariableMemory pvmem;
-
- /*
- * It's possible on startup and after commit/abort.
- * In future we'll catch commit/abort in some way...
- */
- strcpy (pname, "<SPI manager>");
- _SPI_portal = GetPortalByName (pname);
- if ( !PortalIsValid (_SPI_portal) )
- {
- if ( _SPI_stack != NULL ) /* there was abort */
- free (_SPI_stack);
- _SPI_current = _SPI_stack = NULL;
- _SPI_connected = _SPI_curid = -1;
- SPI_processed = 0;
- SPI_tuptable = NULL;
- _SPI_portal = CreatePortal (pname);
- if ( !PortalIsValid (_SPI_portal) )
- elog (FATAL, "SPI_connect: global initialization failed");
- }
-
- /*
- * When procedure called by Executor _SPI_curid expected to be
- * equal to _SPI_connected
- */
- if ( _SPI_curid != _SPI_connected )
- return (SPI_ERROR_CONNECT);
-
- if ( _SPI_stack == NULL )
- {
- if ( _SPI_connected != -1 )
- elog (FATAL, "SPI_connect: no connection(s) expected");
- _SPI_stack = (_SPI_connection *) malloc (sizeof (_SPI_connection));
- }
- else
- {
- if ( _SPI_connected <= -1 )
- elog (FATAL, "SPI_connect: some connection(s) expected");
- _SPI_stack = (_SPI_connection *) realloc (_SPI_stack,
- (_SPI_connected + 1) * sizeof (_SPI_connection));
- }
- /*
- * We' returning to procedure where _SPI_curid == _SPI_connected - 1
- */
- _SPI_connected++;
-
- _SPI_current = &(_SPI_stack[_SPI_connected]);
- _SPI_current->qtlist = NULL;
- _SPI_current->processed = 0;
- _SPI_current->tuptable = NULL;
-
- /* Create Portal for this procedure ... */
- sprintf (pname, "<SPI %d>", _SPI_connected);
- _SPI_current->portal = CreatePortal (pname);
- if ( !PortalIsValid (_SPI_current->portal) )
- elog (FATAL, "SPI_connect: initialization failed");
-
- /* ... and switch to Portal' Variable memory - procedure' context */
- pvmem = PortalGetVariableMemory (_SPI_current->portal);
- _SPI_current->savedcxt = MemoryContextSwitchTo ((MemoryContext)pvmem);
-
- _SPI_current->savedId = GetScanCommandId ();
- SetScanCommandId (GetCurrentCommandId ());
-
- return (SPI_OK_CONNECT);
-
+ char pname[64];
+ PortalVariableMemory pvmem;
+
+ /*
+ * It's possible on startup and after commit/abort. In future we'll
+ * catch commit/abort in some way...
+ */
+ strcpy(pname, "<SPI manager>");
+ _SPI_portal = GetPortalByName(pname);
+ if (!PortalIsValid(_SPI_portal))
+ {
+ if (_SPI_stack != NULL) /* there was abort */
+ free(_SPI_stack);
+ _SPI_current = _SPI_stack = NULL;
+ _SPI_connected = _SPI_curid = -1;
+ SPI_processed = 0;
+ SPI_tuptable = NULL;
+ _SPI_portal = CreatePortal(pname);
+ if (!PortalIsValid(_SPI_portal))
+ elog(FATAL, "SPI_connect: global initialization failed");
+ }
+
+ /*
+ * When procedure called by Executor _SPI_curid expected to be equal
+ * to _SPI_connected
+ */
+ if (_SPI_curid != _SPI_connected)
+ return (SPI_ERROR_CONNECT);
+
+ if (_SPI_stack == NULL)
+ {
+ if (_SPI_connected != -1)
+ elog(FATAL, "SPI_connect: no connection(s) expected");
+ _SPI_stack = (_SPI_connection *) malloc(sizeof(_SPI_connection));
+ }
+ else
+ {
+ if (_SPI_connected <= -1)
+ elog(FATAL, "SPI_connect: some connection(s) expected");
+ _SPI_stack = (_SPI_connection *) realloc(_SPI_stack,
+ (_SPI_connected + 1) * sizeof(_SPI_connection));
+ }
+
+ /*
+ * We' returning to procedure where _SPI_curid == _SPI_connected - 1
+ */
+ _SPI_connected++;
+
+ _SPI_current = &(_SPI_stack[_SPI_connected]);
+ _SPI_current->qtlist = NULL;
+ _SPI_current->processed = 0;
+ _SPI_current->tuptable = NULL;
+
+ /* Create Portal for this procedure ... */
+ sprintf(pname, "<SPI %d>", _SPI_connected);
+ _SPI_current->portal = CreatePortal(pname);
+ if (!PortalIsValid(_SPI_current->portal))
+ elog(FATAL, "SPI_connect: initialization failed");
+
+ /* ... and switch to Portal' Variable memory - procedure' context */
+ pvmem = PortalGetVariableMemory(_SPI_current->portal);
+ _SPI_current->savedcxt = MemoryContextSwitchTo((MemoryContext) pvmem);
+
+ _SPI_current->savedId = GetScanCommandId();
+ SetScanCommandId(GetCurrentCommandId());
+
+ return (SPI_OK_CONNECT);
+
}
int
-SPI_finish ()
+SPI_finish()
{
- int res;
-
- res = _SPI_begin_call (false); /* live in procedure memory */
- if ( res < 0 )
- return (res);
-
- /* Restore memory context as it was before procedure call */
- MemoryContextSwitchTo (_SPI_current->savedcxt);
- PortalDestroy (&(_SPI_current->portal));
-
- SetScanCommandId (_SPI_current->savedId);
-
- /*
- * After _SPI_begin_call _SPI_connected == _SPI_curid.
- * Now we are closing connection to SPI and returning to upper
- * Executor and so _SPI_connected must be equal to _SPI_curid.
- */
- _SPI_connected--;
- _SPI_curid--;
- if ( _SPI_connected == -1 )
- {
- free (_SPI_stack);
- _SPI_stack = NULL;
- }
- else
- {
- _SPI_stack = (_SPI_connection *) realloc (_SPI_stack,
- (_SPI_connected + 1) * sizeof (_SPI_connection));
- _SPI_current = &(_SPI_stack[_SPI_connected]);
- }
-
- return (SPI_OK_FINISH);
-
+ int res;
+
+ res = _SPI_begin_call(false); /* live in procedure memory */
+ if (res < 0)
+ return (res);
+
+ /* Restore memory context as it was before procedure call */
+ MemoryContextSwitchTo(_SPI_current->savedcxt);
+ PortalDestroy(&(_SPI_current->portal));
+
+ SetScanCommandId(_SPI_current->savedId);
+
+ /*
+ * After _SPI_begin_call _SPI_connected == _SPI_curid. Now we are
+ * closing connection to SPI and returning to upper Executor and so
+ * _SPI_connected must be equal to _SPI_curid.
+ */
+ _SPI_connected--;
+ _SPI_curid--;
+ if (_SPI_connected == -1)
+ {
+ free(_SPI_stack);
+ _SPI_stack = NULL;
+ }
+ else
+ {
+ _SPI_stack = (_SPI_connection *) realloc(_SPI_stack,
+ (_SPI_connected + 1) * sizeof(_SPI_connection));
+ _SPI_current = &(_SPI_stack[_SPI_connected]);
+ }
+
+ return (SPI_OK_FINISH);
+
}
int
-SPI_exec (char *src, int tcount)
+SPI_exec(char *src, int tcount)
{
- int res;
-
- if ( src == NULL || tcount < 0 )
- return (SPI_ERROR_ARGUMENT);
-
- res = _SPI_begin_call (true);
- if ( res < 0 )
- return (res);
-
- res = _SPI_execute (src, tcount, NULL);
-
- _SPI_end_call (true);
- return (res);
+ int res;
+
+ if (src == NULL || tcount < 0)
+ return (SPI_ERROR_ARGUMENT);
+
+ res = _SPI_begin_call(true);
+ if (res < 0)
+ return (res);
+
+ res = _SPI_execute(src, tcount, NULL);
+
+ _SPI_end_call(true);
+ return (res);
}
-int
-SPI_execp (void *plan, char **Values, char *Nulls, int tcount)
+int
+SPI_execp(void *plan, char **Values, char *Nulls, int tcount)
{
- int res;
-
- if ( plan == NULL || tcount < 0 )
- return (SPI_ERROR_ARGUMENT);
-
- if ( ((_SPI_plan *)plan)->nargs > 0 &&
- ( Values == NULL || Nulls == NULL ) )
- return (SPI_ERROR_PARAM);
-
- res = _SPI_begin_call (true);
- if ( res < 0 )
- return (res);
-
- res = _SPI_execute_plan ((_SPI_plan *)plan, Values, Nulls, tcount);
-
- _SPI_end_call (true);
- return (res);
+ int res;
+
+ if (plan == NULL || tcount < 0)
+ return (SPI_ERROR_ARGUMENT);
+
+ if (((_SPI_plan *) plan)->nargs > 0 &&
+ (Values == NULL || Nulls == NULL))
+ return (SPI_ERROR_PARAM);
+
+ res = _SPI_begin_call(true);
+ if (res < 0)
+ return (res);
+
+ res = _SPI_execute_plan((_SPI_plan *) plan, Values, Nulls, tcount);
+
+ _SPI_end_call(true);
+ return (res);
}
-void *
-SPI_prepare (char *src, int nargs, Oid *argtypes)
+void *
+SPI_prepare(char *src, int nargs, Oid * argtypes)
{
- _SPI_plan *plan;
-
- if ( nargs < 0 || ( nargs > 0 && argtypes == NULL ) )
- {
- SPI_result = SPI_ERROR_ARGUMENT;
- return (NULL);
- }
-
- SPI_result = _SPI_begin_call (true);
- if ( SPI_result < 0 )
- return (NULL);
-
- plan = (_SPI_plan *) palloc (sizeof (_SPI_plan)); /* Executor context */
- plan->argtypes = argtypes;
- plan->nargs = nargs;
-
- SPI_result = _SPI_execute (src, 0, plan);
-
- if ( SPI_result >= 0 ) /* copy plan to local space */
- plan = _SPI_copy_plan (plan, true);
- else
- plan = NULL;
-
- _SPI_end_call (true);
-
- return ((void *)plan);
-
+ _SPI_plan *plan;
+
+ if (nargs < 0 || (nargs > 0 && argtypes == NULL))
+ {
+ SPI_result = SPI_ERROR_ARGUMENT;
+ return (NULL);
+ }
+
+ SPI_result = _SPI_begin_call(true);
+ if (SPI_result < 0)
+ return (NULL);
+
+ plan = (_SPI_plan *) palloc(sizeof(_SPI_plan)); /* Executor context */
+ plan->argtypes = argtypes;
+ plan->nargs = nargs;
+
+ SPI_result = _SPI_execute(src, 0, plan);
+
+ if (SPI_result >= 0) /* copy plan to local space */
+ plan = _SPI_copy_plan(plan, true);
+ else
+ plan = NULL;
+
+ _SPI_end_call(true);
+
+ return ((void *) plan);
+
}
-void *
-SPI_saveplan (void *plan)
+void *
+SPI_saveplan(void *plan)
{
- _SPI_plan *newplan;
-
- if ( plan == NULL )
- {
- SPI_result = SPI_ERROR_ARGUMENT;
- return (NULL);
- }
-
- SPI_result = _SPI_begin_call (false); /* don't change context */
- if ( SPI_result < 0 )
- return (NULL);
-
- newplan = _SPI_copy_plan ((_SPI_plan *)plan, false);
-
- _SPI_curid--;
- SPI_result = 0;
-
- return ((void *)newplan);
-
+ _SPI_plan *newplan;
+
+ if (plan == NULL)
+ {
+ SPI_result = SPI_ERROR_ARGUMENT;
+ return (NULL);
+ }
+
+ SPI_result = _SPI_begin_call(false); /* don't change context */
+ if (SPI_result < 0)
+ return (NULL);
+
+ newplan = _SPI_copy_plan((_SPI_plan *) plan, false);
+
+ _SPI_curid--;
+ SPI_result = 0;
+
+ return ((void *) newplan);
+
}
int
-SPI_fnumber (TupleDesc tupdesc, char *fname)
+SPI_fnumber(TupleDesc tupdesc, char *fname)
{
- int res;
-
- if ( _SPI_curid + 1 != _SPI_connected )
- return (SPI_ERROR_UNCONNECTED);
-
- for (res = 0; res < tupdesc->natts; res++)
- {
- if ( strcmp (tupdesc->attrs[res]->attname.data, fname) == 0 )
- return (res + 1);
- }
-
- return (SPI_ERROR_NOATTRIBUTE);
+ int res;
+
+ if (_SPI_curid + 1 != _SPI_connected)
+ return (SPI_ERROR_UNCONNECTED);
+
+ for (res = 0; res < tupdesc->natts; res++)
+ {
+ if (strcmp(tupdesc->attrs[res]->attname.data, fname) == 0)
+ return (res + 1);
+ }
+
+ return (SPI_ERROR_NOATTRIBUTE);
}
-char *
-SPI_getvalue (HeapTuple tuple, TupleDesc tupdesc, int fnumber)
+char *
+SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber)
{
- char *val;
- bool isnull;
- Oid foutoid;
-
- SPI_result = 0;
- if ( _SPI_curid + 1 != _SPI_connected )
- {
- SPI_result = SPI_ERROR_UNCONNECTED;
- return (NULL);
- }
-
- if ( tuple->t_natts < fnumber || fnumber <= 0 )
- return (NULL);
-
- val = heap_getattr (tuple, InvalidBuffer, fnumber, tupdesc, &isnull);
- if ( isnull )
- return (NULL);
- foutoid = typtoout ((Oid) tupdesc->attrs[fnumber - 1]->atttypid);
- if ( !OidIsValid (foutoid) )
- {
- SPI_result = SPI_ERROR_NOOUTFUNC;
- return (NULL);
- }
-
- return (fmgr (foutoid, val, gettypelem (tupdesc->attrs[fnumber - 1]->atttypid)));
+ char *val;
+ bool isnull;
+ Oid foutoid;
+
+ SPI_result = 0;
+ if (_SPI_curid + 1 != _SPI_connected)
+ {
+ SPI_result = SPI_ERROR_UNCONNECTED;
+ return (NULL);
+ }
+
+ if (tuple->t_natts < fnumber || fnumber <= 0)
+ return (NULL);
+
+ val = heap_getattr(tuple, InvalidBuffer, fnumber, tupdesc, &isnull);
+ if (isnull)
+ return (NULL);
+ foutoid = typtoout((Oid) tupdesc->attrs[fnumber - 1]->atttypid);
+ if (!OidIsValid(foutoid))
+ {
+ SPI_result = SPI_ERROR_NOOUTFUNC;
+ return (NULL);
+ }
+
+ return (fmgr(foutoid, val, gettypelem(tupdesc->attrs[fnumber - 1]->atttypid)));
}
-char *
-SPI_getbinval (HeapTuple tuple, TupleDesc tupdesc, int fnumber, bool *isnull)
+char *
+SPI_getbinval(HeapTuple tuple, TupleDesc tupdesc, int fnumber, bool * isnull)
{
- char *val;
-
- *isnull = true;
- SPI_result = 0;
- if ( _SPI_curid + 1 != _SPI_connected )
- {
- SPI_result = SPI_ERROR_UNCONNECTED;
- return (NULL);
- }
-
- if ( tuple->t_natts < fnumber || fnumber <= 0 )
- return (NULL);
-
- val = heap_getattr (tuple, InvalidBuffer, fnumber, tupdesc, isnull);
-
- return (val);
+ char *val;
+
+ *isnull = true;
+ SPI_result = 0;
+ if (_SPI_curid + 1 != _SPI_connected)
+ {
+ SPI_result = SPI_ERROR_UNCONNECTED;
+ return (NULL);
+ }
+
+ if (tuple->t_natts < fnumber || fnumber <= 0)
+ return (NULL);
+
+ val = heap_getattr(tuple, InvalidBuffer, fnumber, tupdesc, isnull);
+
+ return (val);
}
-char *
-SPI_gettype (TupleDesc tupdesc, int fnumber)
+char *
+SPI_gettype(TupleDesc tupdesc, int fnumber)
{
- HeapTuple typeTuple;
-
- SPI_result = 0;
- if ( _SPI_curid + 1 != _SPI_connected )
- {
- SPI_result = SPI_ERROR_UNCONNECTED;
- return (NULL);
- }
-
- if ( tupdesc->natts < fnumber || fnumber <= 0 )
- {
- SPI_result = SPI_ERROR_NOATTRIBUTE;
- return (NULL);
- }
-
- typeTuple = SearchSysCacheTuple (TYPOID,
- ObjectIdGetDatum (tupdesc->attrs[fnumber - 1]->atttypid),
- 0, 0, 0);
-
- if ( !HeapTupleIsValid (typeTuple) )
- {
- SPI_result = SPI_ERROR_TYPUNKNOWN;
- return (NULL);
- }
-
- return (pstrdup (((TypeTupleForm) GETSTRUCT (typeTuple))->typname.data));
+ HeapTuple typeTuple;
+
+ SPI_result = 0;
+ if (_SPI_curid + 1 != _SPI_connected)
+ {
+ SPI_result = SPI_ERROR_UNCONNECTED;
+ return (NULL);
+ }
+
+ if (tupdesc->natts < fnumber || fnumber <= 0)
+ {
+ SPI_result = SPI_ERROR_NOATTRIBUTE;
+ return (NULL);
+ }
+
+ typeTuple = SearchSysCacheTuple(TYPOID,
+ ObjectIdGetDatum(tupdesc->attrs[fnumber - 1]->atttypid),
+ 0, 0, 0);
+
+ if (!HeapTupleIsValid(typeTuple))
+ {
+ SPI_result = SPI_ERROR_TYPUNKNOWN;
+ return (NULL);
+ }
+
+ return (pstrdup(((TypeTupleForm) GETSTRUCT(typeTuple))->typname.data));
}
Oid
-SPI_gettypeid (TupleDesc tupdesc, int fnumber)
+SPI_gettypeid(TupleDesc tupdesc, int fnumber)
{
-
- SPI_result = 0;
- if ( _SPI_curid + 1 != _SPI_connected )
- {
- SPI_result = SPI_ERROR_UNCONNECTED;
- return (InvalidOid);
- }
-
- if ( tupdesc->natts < fnumber || fnumber <= 0 )
- {
- SPI_result = SPI_ERROR_NOATTRIBUTE;
- return (InvalidOid);
- }
-
- return (tupdesc->attrs[fnumber - 1]->atttypid);
+
+ SPI_result = 0;
+ if (_SPI_curid + 1 != _SPI_connected)
+ {
+ SPI_result = SPI_ERROR_UNCONNECTED;
+ return (InvalidOid);
+ }
+
+ if (tupdesc->natts < fnumber || fnumber <= 0)
+ {
+ SPI_result = SPI_ERROR_NOATTRIBUTE;
+ return (InvalidOid);
+ }
+
+ return (tupdesc->attrs[fnumber - 1]->atttypid);
}
-char *
-SPI_getrelname (Relation rel)
+char *
+SPI_getrelname(Relation rel)
{
-
- SPI_result = 0;
- if ( _SPI_curid + 1 != _SPI_connected )
- {
- SPI_result = SPI_ERROR_UNCONNECTED;
- return (NULL);
- }
-
- return (pstrdup (rel->rd_rel->relname.data));
+
+ SPI_result = 0;
+ if (_SPI_curid + 1 != _SPI_connected)
+ {
+ SPI_result = SPI_ERROR_UNCONNECTED;
+ return (NULL);
+ }
+
+ return (pstrdup(rel->rd_rel->relname.data));
}
/*
* spi_printtup --
- * store tuple retrieved by Executor into SPITupleTable
- * of current SPI procedure
+ * store tuple retrieved by Executor into SPITupleTable
+ * of current SPI procedure
*
*/
void
-spi_printtup (HeapTuple tuple, TupleDesc tupdesc)
+spi_printtup(HeapTuple tuple, TupleDesc tupdesc)
{
- SPITupleTable *tuptable;
- MemoryContext oldcxt;
-
- /*
- * When called by Executor _SPI_curid expected to be
- * equal to _SPI_connected
- */
- if ( _SPI_curid != _SPI_connected || _SPI_connected < 0 )
- elog (FATAL, "SPI: improper call to spi_printtup");
- if ( _SPI_current != &(_SPI_stack[_SPI_curid]) )
- elog (FATAL, "SPI: stack corrupted in spi_printtup");
-
- oldcxt = _SPI_procmem (); /* switch to procedure memory context */
-
- tuptable = _SPI_current->tuptable;
- if ( tuptable == NULL )
- {
- _SPI_current->tuptable = tuptable = (SPITupleTable *)
- palloc (sizeof (SPITupleTable));
- tuptable->alloced = tuptable->free = 128;
- tuptable->vals = (HeapTuple *) palloc (tuptable->alloced * sizeof (HeapTuple));
- tuptable->tupdesc = CreateTupleDescCopy (tupdesc);
- }
- else if ( tuptable->free == 0 )
- {
- tuptable->free = 256;
- tuptable->alloced += tuptable->free;
- tuptable->vals = (HeapTuple *) repalloc (tuptable->vals,
- tuptable->alloced * sizeof (HeapTuple));
- }
-
- tuptable->vals[tuptable->alloced - tuptable->free] = heap_copytuple (tuple);
- (tuptable->free)--;
-
- MemoryContextSwitchTo (oldcxt);
- return;
+ SPITupleTable *tuptable;
+ MemoryContext oldcxt;
+
+ /*
+ * When called by Executor _SPI_curid expected to be equal to
+ * _SPI_connected
+ */
+ if (_SPI_curid != _SPI_connected || _SPI_connected < 0)
+ elog(FATAL, "SPI: improper call to spi_printtup");
+ if (_SPI_current != &(_SPI_stack[_SPI_curid]))
+ elog(FATAL, "SPI: stack corrupted in spi_printtup");
+
+ oldcxt = _SPI_procmem(); /* switch to procedure memory context */
+
+ tuptable = _SPI_current->tuptable;
+ if (tuptable == NULL)
+ {
+ _SPI_current->tuptable = tuptable = (SPITupleTable *)
+ palloc(sizeof(SPITupleTable));
+ tuptable->alloced = tuptable->free = 128;
+ tuptable->vals = (HeapTuple *) palloc(tuptable->alloced * sizeof(HeapTuple));
+ tuptable->tupdesc = CreateTupleDescCopy(tupdesc);
+ }
+ else if (tuptable->free == 0)
+ {
+ tuptable->free = 256;
+ tuptable->alloced += tuptable->free;
+ tuptable->vals = (HeapTuple *) repalloc(tuptable->vals,
+ tuptable->alloced * sizeof(HeapTuple));
+ }
+
+ tuptable->vals[tuptable->alloced - tuptable->free] = heap_copytuple(tuple);
+ (tuptable->free)--;
+
+ MemoryContextSwitchTo(oldcxt);
+ return;
}
/*
@@ -450,330 +457,334 @@ spi_printtup (HeapTuple tuple, TupleDesc tupdesc)
*/
static int
-_SPI_execute (char *src, int tcount, _SPI_plan *plan)
+_SPI_execute(char *src, int tcount, _SPI_plan * plan)
{
- QueryTreeList *queryTree_list;
- List *planTree_list;
- List *ptlist;
- QueryDesc *qdesc;
- Query *queryTree;
- Plan *planTree;
- EState *state;
- int qlen;
- int nargs = 0;
- Oid *argtypes = NULL;
- int res;
- int i;
-
- /* Increment CommandCounter to see changes made by now */
- CommandCounterIncrement ();
-
- SPI_processed = 0;
- SPI_tuptable = NULL;
- _SPI_current->tuptable = NULL;
- _SPI_current->qtlist = NULL;
-
- if ( plan )
- {
- nargs = plan->nargs;
- argtypes = plan->argtypes;
- }
- ptlist = planTree_list = (List *)
- pg_plan (src, argtypes, nargs, &queryTree_list, None);
-
- _SPI_current->qtlist = queryTree_list;
-
- qlen = queryTree_list->len;
- for (i=0; ;i++)
- {
- queryTree = (Query*) (queryTree_list->qtrees[i]);
- planTree = lfirst(planTree_list);
-
- planTree_list = lnext (planTree_list);
-
- if ( queryTree->commandType == CMD_UTILITY )
- {
- if ( nodeTag (queryTree->utilityStmt ) == T_CopyStmt )
- {
- CopyStmt *stmt = (CopyStmt *)(queryTree->utilityStmt);
-
- if ( stmt->filename == NULL )
- return (SPI_ERROR_COPY);
- }
- else if ( nodeTag (queryTree->utilityStmt ) == T_ClosePortalStmt ||
- nodeTag (queryTree->utilityStmt ) == T_FetchStmt )
- return (SPI_ERROR_CURSOR);
- else if ( nodeTag (queryTree->utilityStmt ) == T_TransactionStmt )
- return (SPI_ERROR_TRANSACTION);
- res = SPI_OK_UTILITY;
- if ( plan == NULL )
- {
- ProcessUtility (queryTree->utilityStmt, None);
- if ( i < qlen - 1 )
- CommandCounterIncrement ();
- else
- return (res);
- }
- else if ( i >= qlen - 1 )
- break;
- }
- else if ( plan == NULL )
- {
- qdesc = CreateQueryDesc (queryTree, planTree,
- ( i < qlen - 1 ) ? None : SPI);
- state = CreateExecutorState();
- res = _SPI_pquery (qdesc, state, ( i < qlen - 1 ) ? 0 : tcount);
- if ( res < 0 || i >= qlen - 1 )
- return (res);
- CommandCounterIncrement ();
+ QueryTreeList *queryTree_list;
+ List *planTree_list;
+ List *ptlist;
+ QueryDesc *qdesc;
+ Query *queryTree;
+ Plan *planTree;
+ EState *state;
+ int qlen;
+ int nargs = 0;
+ Oid *argtypes = NULL;
+ int res;
+ int i;
+
+ /* Increment CommandCounter to see changes made by now */
+ CommandCounterIncrement();
+
+ SPI_processed = 0;
+ SPI_tuptable = NULL;
+ _SPI_current->tuptable = NULL;
+ _SPI_current->qtlist = NULL;
+
+ if (plan)
+ {
+ nargs = plan->nargs;
+ argtypes = plan->argtypes;
}
- else
+ ptlist = planTree_list = (List *)
+ pg_plan(src, argtypes, nargs, &queryTree_list, None);
+
+ _SPI_current->qtlist = queryTree_list;
+
+ qlen = queryTree_list->len;
+ for (i = 0;; i++)
{
- qdesc = CreateQueryDesc (queryTree, planTree,
- ( i < qlen - 1 ) ? None : SPI);
- res = _SPI_pquery (qdesc, NULL, ( i < qlen - 1 ) ? 0 : tcount);
- if ( res < 0 )
- return (res);
- if ( i >= qlen - 1 )
- break;
- }
- }
-
- plan->qtlist = queryTree_list;
- plan->ptlist = ptlist;
-
- return (res);
-
+ queryTree = (Query *) (queryTree_list->qtrees[i]);
+ planTree = lfirst(planTree_list);
+
+ planTree_list = lnext(planTree_list);
+
+ if (queryTree->commandType == CMD_UTILITY)
+ {
+ if (nodeTag(queryTree->utilityStmt) == T_CopyStmt)
+ {
+ CopyStmt *stmt = (CopyStmt *) (queryTree->utilityStmt);
+
+ if (stmt->filename == NULL)
+ return (SPI_ERROR_COPY);
+ }
+ else if (nodeTag(queryTree->utilityStmt) == T_ClosePortalStmt ||
+ nodeTag(queryTree->utilityStmt) == T_FetchStmt)
+ return (SPI_ERROR_CURSOR);
+ else if (nodeTag(queryTree->utilityStmt) == T_TransactionStmt)
+ return (SPI_ERROR_TRANSACTION);
+ res = SPI_OK_UTILITY;
+ if (plan == NULL)
+ {
+ ProcessUtility(queryTree->utilityStmt, None);
+ if (i < qlen - 1)
+ CommandCounterIncrement();
+ else
+ return (res);
+ }
+ else if (i >= qlen - 1)
+ break;
+ }
+ else if (plan == NULL)
+ {
+ qdesc = CreateQueryDesc(queryTree, planTree,
+ (i < qlen - 1) ? None : SPI);
+ state = CreateExecutorState();
+ res = _SPI_pquery(qdesc, state, (i < qlen - 1) ? 0 : tcount);
+ if (res < 0 || i >= qlen - 1)
+ return (res);
+ CommandCounterIncrement();
+ }
+ else
+ {
+ qdesc = CreateQueryDesc(queryTree, planTree,
+ (i < qlen - 1) ? None : SPI);
+ res = _SPI_pquery(qdesc, NULL, (i < qlen - 1) ? 0 : tcount);
+ if (res < 0)
+ return (res);
+ if (i >= qlen - 1)
+ break;
+ }
+ }
+
+ plan->qtlist = queryTree_list;
+ plan->ptlist = ptlist;
+
+ return (res);
+
}
static int
-_SPI_execute_plan (_SPI_plan *plan, char **Values, char *Nulls, int tcount)
+_SPI_execute_plan(_SPI_plan * plan, char **Values, char *Nulls, int tcount)
{
- QueryTreeList *queryTree_list = plan->qtlist;
- List *planTree_list = plan->ptlist;
- QueryDesc *qdesc;
- Query *queryTree;
- Plan *planTree;
- EState *state;
- int nargs = plan->nargs;
- int qlen = queryTree_list->len;
- int res;
- int i, k;
-
- /* Increment CommandCounter to see changes made by now */
- CommandCounterIncrement ();
-
- SPI_processed = 0;
- SPI_tuptable = NULL;
- _SPI_current->tuptable = NULL;
- _SPI_current->qtlist = NULL;
-
- for (i=0; ;i++)
- {
- queryTree = (Query*) (queryTree_list->qtrees[i]);
- planTree = lfirst(planTree_list);
-
- planTree_list = lnext (planTree_list);
-
- if ( queryTree->commandType == CMD_UTILITY )
- {
- ProcessUtility (queryTree->utilityStmt, None);
- if ( i < qlen - 1 )
- CommandCounterIncrement ();
- else
- return (SPI_OK_UTILITY);
- }
- else
+ QueryTreeList *queryTree_list = plan->qtlist;
+ List *planTree_list = plan->ptlist;
+ QueryDesc *qdesc;
+ Query *queryTree;
+ Plan *planTree;
+ EState *state;
+ int nargs = plan->nargs;
+ int qlen = queryTree_list->len;
+ int res;
+ int i,
+ k;
+
+ /* Increment CommandCounter to see changes made by now */
+ CommandCounterIncrement();
+
+ SPI_processed = 0;
+ SPI_tuptable = NULL;
+ _SPI_current->tuptable = NULL;
+ _SPI_current->qtlist = NULL;
+
+ for (i = 0;; i++)
{
- qdesc = CreateQueryDesc (queryTree, planTree,
- ( i < qlen - 1 ) ? None : SPI);
- state = CreateExecutorState();
- if ( nargs > 0 )
- {
- ParamListInfo paramLI = (ParamListInfo) palloc ((nargs + 1) *
- sizeof (ParamListInfoData));
- state->es_param_list_info = paramLI;
- for (k = 0; k < plan->nargs; paramLI++, k++)
- {
- paramLI->kind = PARAM_NUM;
- paramLI->id = k+1;
- paramLI->isnull = (Nulls[k] != 0);
- paramLI->value = (Datum) Values[k];
- }
- paramLI->kind = PARAM_INVALID;
- }
- else
- state->es_param_list_info = NULL;
- res = _SPI_pquery (qdesc, state, ( i < qlen - 1 ) ? 0 : tcount);
- if ( res < 0 || i >= qlen - 1 )
- return (res);
- CommandCounterIncrement ();
- }
- }
-
- return (res);
-
+ queryTree = (Query *) (queryTree_list->qtrees[i]);
+ planTree = lfirst(planTree_list);
+
+ planTree_list = lnext(planTree_list);
+
+ if (queryTree->commandType == CMD_UTILITY)
+ {
+ ProcessUtility(queryTree->utilityStmt, None);
+ if (i < qlen - 1)
+ CommandCounterIncrement();
+ else
+ return (SPI_OK_UTILITY);
+ }
+ else
+ {
+ qdesc = CreateQueryDesc(queryTree, planTree,
+ (i < qlen - 1) ? None : SPI);
+ state = CreateExecutorState();
+ if (nargs > 0)
+ {
+ ParamListInfo paramLI = (ParamListInfo) palloc((nargs + 1) *
+ sizeof(ParamListInfoData));
+
+ state->es_param_list_info = paramLI;
+ for (k = 0; k < plan->nargs; paramLI++, k++)
+ {
+ paramLI->kind = PARAM_NUM;
+ paramLI->id = k + 1;
+ paramLI->isnull = (Nulls[k] != 0);
+ paramLI->value = (Datum) Values[k];
+ }
+ paramLI->kind = PARAM_INVALID;
+ }
+ else
+ state->es_param_list_info = NULL;
+ res = _SPI_pquery(qdesc, state, (i < qlen - 1) ? 0 : tcount);
+ if (res < 0 || i >= qlen - 1)
+ return (res);
+ CommandCounterIncrement();
+ }
+ }
+
+ return (res);
+
}
static int
-_SPI_pquery (QueryDesc *queryDesc, EState *state, int tcount)
+_SPI_pquery(QueryDesc * queryDesc, EState * state, int tcount)
{
- Query *parseTree;
- Plan *plan;
- int operation;
- TupleDesc tupdesc;
- bool isRetrieveIntoPortal = false;
- bool isRetrieveIntoRelation = false;
- char* intoName = NULL;
- int res;
-
- parseTree = queryDesc->parsetree;
- plan = queryDesc->plantree;
- operation = queryDesc->operation;
-
- switch (operation)
- {
- case CMD_SELECT:
- res = SPI_OK_SELECT;
- if (parseTree->isPortal)
- {
- isRetrieveIntoPortal = true;
- intoName = parseTree->into;
- parseTree->isBinary = false; /* */
-
- return (SPI_ERROR_CURSOR);
-
- }
- else if (parseTree->into != NULL) /* select into table */
- {
- res = SPI_OK_SELINTO;
- isRetrieveIntoRelation = true;
- }
- break;
- case CMD_INSERT:
- res = SPI_OK_INSERT;
- break;
- case CMD_DELETE:
- res = SPI_OK_DELETE;
- break;
- case CMD_UPDATE:
- res = SPI_OK_UPDATE;
- break;
- default:
- return (SPI_ERROR_OPUNKNOWN);
- }
-
- if ( state == NULL ) /* plan preparation */
- return (res);
+ Query *parseTree;
+ Plan *plan;
+ int operation;
+ TupleDesc tupdesc;
+ bool isRetrieveIntoPortal = false;
+ bool isRetrieveIntoRelation = false;
+ char *intoName = NULL;
+ int res;
+
+ parseTree = queryDesc->parsetree;
+ plan = queryDesc->plantree;
+ operation = queryDesc->operation;
+
+ switch (operation)
+ {
+ case CMD_SELECT:
+ res = SPI_OK_SELECT;
+ if (parseTree->isPortal)
+ {
+ isRetrieveIntoPortal = true;
+ intoName = parseTree->into;
+ parseTree->isBinary = false; /* */
+
+ return (SPI_ERROR_CURSOR);
+
+ }
+ else if (parseTree->into != NULL) /* select into table */
+ {
+ res = SPI_OK_SELINTO;
+ isRetrieveIntoRelation = true;
+ }
+ break;
+ case CMD_INSERT:
+ res = SPI_OK_INSERT;
+ break;
+ case CMD_DELETE:
+ res = SPI_OK_DELETE;
+ break;
+ case CMD_UPDATE:
+ res = SPI_OK_UPDATE;
+ break;
+ default:
+ return (SPI_ERROR_OPUNKNOWN);
+ }
+
+ if (state == NULL) /* plan preparation */
+ return (res);
#ifdef SPI_EXECUTOR_STATS
- if ( ShowExecutorStats )
- ResetUsage ();
+ if (ShowExecutorStats)
+ ResetUsage();
#endif
- tupdesc = ExecutorStart (queryDesc, state);
-
- /* Don't work currently */
- if (isRetrieveIntoPortal)
- {
- ProcessPortal(intoName,
- parseTree,
- plan,
- state,
- tupdesc,
- None);
- return (SPI_OK_CURSOR);
- }
-
- ExecutorRun (queryDesc, state, EXEC_RUN, tcount);
-
- _SPI_current->processed = state->es_processed;
- if ( operation == CMD_SELECT && queryDesc->dest == SPI )
- {
- if ( _SPI_checktuples (isRetrieveIntoRelation) )
- elog (FATAL, "SPI_select: # of processed tuples check failed");
- }
-
- ExecutorEnd (queryDesc, state);
-
+ tupdesc = ExecutorStart(queryDesc, state);
+
+ /* Don't work currently */
+ if (isRetrieveIntoPortal)
+ {
+ ProcessPortal(intoName,
+ parseTree,
+ plan,
+ state,
+ tupdesc,
+ None);
+ return (SPI_OK_CURSOR);
+ }
+
+ ExecutorRun(queryDesc, state, EXEC_RUN, tcount);
+
+ _SPI_current->processed = state->es_processed;
+ if (operation == CMD_SELECT && queryDesc->dest == SPI)
+ {
+ if (_SPI_checktuples(isRetrieveIntoRelation))
+ elog(FATAL, "SPI_select: # of processed tuples check failed");
+ }
+
+ ExecutorEnd(queryDesc, state);
+
#ifdef SPI_EXECUTOR_STATS
- if ( ShowExecutorStats )
- {
- fprintf (stderr, "! Executor Stats:\n");
- ShowUsage ();
- }
+ if (ShowExecutorStats)
+ {
+ fprintf(stderr, "! Executor Stats:\n");
+ ShowUsage();
+ }
#endif
-
- if ( queryDesc->dest == SPI )
- {
- SPI_processed = _SPI_current->processed;
- SPI_tuptable = _SPI_current->tuptable;
- }
-
- return (res);
+
+ if (queryDesc->dest == SPI)
+ {
+ SPI_processed = _SPI_current->processed;
+ SPI_tuptable = _SPI_current->tuptable;
+ }
+
+ return (res);
}
#if 0
static void
-_SPI_fetch (FetchStmt *stmt)
+_SPI_fetch(FetchStmt * stmt)
{
- char *name = stmt->portalname;
- int feature = ( stmt->direction == FORWARD ) ? EXEC_FOR : EXEC_BACK;
- int count = stmt->howMany;
- Portal portal;
- QueryDesc *queryDesc;
- EState *state;
- MemoryContext context;
-
- if ( name == NULL)
- elog (FATAL, "SPI_fetch from blank portal unsupported");
-
- portal = GetPortalByName (name);
- if ( !PortalIsValid (portal) )
- elog (FATAL, "SPI_fetch: portal \"%s\" not found", name);
-
- context = MemoryContextSwitchTo((MemoryContext)PortalGetHeapMemory(portal));
-
- queryDesc = PortalGetQueryDesc(portal);
- state = PortalGetState(portal);
-
- ExecutorRun(queryDesc, state, feature, count);
-
- MemoryContextSwitchTo (context); /* switch to the normal Executor context */
-
- _SPI_current->processed = state->es_processed;
- if ( _SPI_checktuples (false) )
- elog (FATAL, "SPI_fetch: # of processed tuples check failed");
-
- SPI_processed = _SPI_current->processed;
- SPI_tuptable = _SPI_current->tuptable;
-
+ char *name = stmt->portalname;
+ int feature = (stmt->direction == FORWARD) ? EXEC_FOR : EXEC_BACK;
+ int count = stmt->howMany;
+ Portal portal;
+ QueryDesc *queryDesc;
+ EState *state;
+ MemoryContext context;
+
+ if (name == NULL)
+ elog(FATAL, "SPI_fetch from blank portal unsupported");
+
+ portal = GetPortalByName(name);
+ if (!PortalIsValid(portal))
+ elog(FATAL, "SPI_fetch: portal \"%s\" not found", name);
+
+ context = MemoryContextSwitchTo((MemoryContext) PortalGetHeapMemory(portal));
+
+ queryDesc = PortalGetQueryDesc(portal);
+ state = PortalGetState(portal);
+
+ ExecutorRun(queryDesc, state, feature, count);
+
+ MemoryContextSwitchTo(context); /* switch to the normal Executor
+ * context */
+
+ _SPI_current->processed = state->es_processed;
+ if (_SPI_checktuples(false))
+ elog(FATAL, "SPI_fetch: # of processed tuples check failed");
+
+ SPI_processed = _SPI_current->processed;
+ SPI_tuptable = _SPI_current->tuptable;
+
}
+
#endif
-static MemoryContext
-_SPI_execmem ()
+static MemoryContext
+_SPI_execmem()
{
- MemoryContext oldcxt;
- PortalHeapMemory phmem;
-
- phmem = PortalGetHeapMemory (_SPI_current->portal);
- oldcxt = MemoryContextSwitchTo ((MemoryContext)phmem);
-
- return (oldcxt);
-
+ MemoryContext oldcxt;
+ PortalHeapMemory phmem;
+
+ phmem = PortalGetHeapMemory(_SPI_current->portal);
+ oldcxt = MemoryContextSwitchTo((MemoryContext) phmem);
+
+ return (oldcxt);
+
}
-static MemoryContext
-_SPI_procmem ()
+static MemoryContext
+_SPI_procmem()
{
- MemoryContext oldcxt;
- PortalVariableMemory pvmem;
-
- pvmem = PortalGetVariableMemory (_SPI_current->portal);
- oldcxt = MemoryContextSwitchTo ((MemoryContext)pvmem);
-
- return (oldcxt);
-
+ MemoryContext oldcxt;
+ PortalVariableMemory pvmem;
+
+ pvmem = PortalGetVariableMemory(_SPI_current->portal);
+ oldcxt = MemoryContextSwitchTo((MemoryContext) pvmem);
+
+ return (oldcxt);
+
}
/*
@@ -781,108 +792,110 @@ _SPI_procmem ()
*
*/
static int
-_SPI_begin_call (bool execmem)
+_SPI_begin_call(bool execmem)
{
- if ( _SPI_curid + 1 != _SPI_connected )
- return (SPI_ERROR_UNCONNECTED);
- _SPI_curid++;
- if ( _SPI_current != &(_SPI_stack[_SPI_curid]) )
- elog (FATAL, "SPI: stack corrupted");
-
- if ( execmem ) /* switch to the Executor memory context */
- {
- _SPI_execmem ();
- StartPortalAllocMode (DefaultAllocMode, 0);
- }
-
- return (0);
+ if (_SPI_curid + 1 != _SPI_connected)
+ return (SPI_ERROR_UNCONNECTED);
+ _SPI_curid++;
+ if (_SPI_current != &(_SPI_stack[_SPI_curid]))
+ elog(FATAL, "SPI: stack corrupted");
+
+ if (execmem) /* switch to the Executor memory context */
+ {
+ _SPI_execmem();
+ StartPortalAllocMode(DefaultAllocMode, 0);
+ }
+
+ return (0);
}
static int
-_SPI_end_call (bool procmem)
+_SPI_end_call(bool procmem)
{
- /*
- * We' returning to procedure where _SPI_curid == _SPI_connected - 1
- */
- _SPI_curid--;
-
- if ( _SPI_current->qtlist) /* free _SPI_plan allocations */
- {
- free (_SPI_current->qtlist->qtrees);
- free (_SPI_current->qtlist);
- _SPI_current->qtlist = NULL;
- }
-
- if ( procmem ) /* switch to the procedure memory context */
- { /* but free Executor memory before */
- EndPortalAllocMode ();
- _SPI_procmem ();
- }
-
- return (0);
+
+ /*
+ * We' returning to procedure where _SPI_curid == _SPI_connected - 1
+ */
+ _SPI_curid--;
+
+ if (_SPI_current->qtlist) /* free _SPI_plan allocations */
+ {
+ free(_SPI_current->qtlist->qtrees);
+ free(_SPI_current->qtlist);
+ _SPI_current->qtlist = NULL;
+ }
+
+ if (procmem) /* switch to the procedure memory context */
+ { /* but free Executor memory before */
+ EndPortalAllocMode();
+ _SPI_procmem();
+ }
+
+ return (0);
}
-static bool
-_SPI_checktuples (bool isRetrieveIntoRelation)
+static bool
+_SPI_checktuples(bool isRetrieveIntoRelation)
{
- uint32 processed = _SPI_current->processed;
- SPITupleTable *tuptable = _SPI_current->tuptable;
- bool failed = false;
-
- if ( processed == 0 )
- {
- if ( tuptable != NULL )
- failed = true;
- }
- else /* some tuples were processed */
- {
- if ( tuptable == NULL ) /* spi_printtup was not called */
- {
- if ( !isRetrieveIntoRelation )
- failed = true;
- }
- else if ( isRetrieveIntoRelation )
- failed = true;
- else if ( processed != ( tuptable->alloced - tuptable->free ) )
- failed = true;
- }
-
- return (failed);
+ uint32 processed = _SPI_current->processed;
+ SPITupleTable *tuptable = _SPI_current->tuptable;
+ bool failed = false;
+
+ if (processed == 0)
+ {
+ if (tuptable != NULL)
+ failed = true;
+ }
+ else
+/* some tuples were processed */
+ {
+ if (tuptable == NULL) /* spi_printtup was not called */
+ {
+ if (!isRetrieveIntoRelation)
+ failed = true;
+ }
+ else if (isRetrieveIntoRelation)
+ failed = true;
+ else if (processed != (tuptable->alloced - tuptable->free))
+ failed = true;
+ }
+
+ return (failed);
}
-
+
static _SPI_plan *
-_SPI_copy_plan (_SPI_plan *plan, bool local)
+_SPI_copy_plan(_SPI_plan * plan, bool local)
{
- _SPI_plan *newplan;
- MemoryContext oldcxt;
- int i;
-
- if ( local )
- oldcxt = MemoryContextSwitchTo ((MemoryContext)
- PortalGetVariableMemory (_SPI_current->portal));
- else
- oldcxt = MemoryContextSwitchTo (TopMemoryContext);
-
- newplan = (_SPI_plan *) palloc (sizeof (_SPI_plan));
- newplan->qtlist = (QueryTreeList*) palloc (sizeof (QueryTreeList));
- newplan->qtlist->len = plan->qtlist->len;
- newplan->qtlist->qtrees = (Query**) palloc (plan->qtlist->len *
- sizeof (Query*));
- for (i = 0; i < plan->qtlist->len; i++)
- newplan->qtlist->qtrees[i] = (Query *)
- copyObject (plan->qtlist->qtrees[i]);
-
- newplan->ptlist = (List *) copyObject (plan->ptlist);
- newplan->nargs = plan->nargs;
- if ( plan->nargs > 0 )
- {
- newplan->argtypes = (Oid *) palloc (plan->nargs * sizeof (Oid));
- memcpy (newplan->argtypes, plan->argtypes, plan->nargs * sizeof (Oid));
- }
- else
- newplan->argtypes = NULL;
-
- MemoryContextSwitchTo (oldcxt);
-
- return (newplan);
+ _SPI_plan *newplan;
+ MemoryContext oldcxt;
+ int i;
+
+ if (local)
+ oldcxt = MemoryContextSwitchTo((MemoryContext)
+ PortalGetVariableMemory(_SPI_current->portal));
+ else
+ oldcxt = MemoryContextSwitchTo(TopMemoryContext);
+
+ newplan = (_SPI_plan *) palloc(sizeof(_SPI_plan));
+ newplan->qtlist = (QueryTreeList *) palloc(sizeof(QueryTreeList));
+ newplan->qtlist->len = plan->qtlist->len;
+ newplan->qtlist->qtrees = (Query **) palloc(plan->qtlist->len *
+ sizeof(Query *));
+ for (i = 0; i < plan->qtlist->len; i++)
+ newplan->qtlist->qtrees[i] = (Query *)
+ copyObject(plan->qtlist->qtrees[i]);
+
+ newplan->ptlist = (List *) copyObject(plan->ptlist);
+ newplan->nargs = plan->nargs;
+ if (plan->nargs > 0)
+ {
+ newplan->argtypes = (Oid *) palloc(plan->nargs * sizeof(Oid));
+ memcpy(newplan->argtypes, plan->argtypes, plan->nargs * sizeof(Oid));
+ }
+ else
+ newplan->argtypes = NULL;
+
+ MemoryContextSwitchTo(oldcxt);
+
+ return (newplan);
}