1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-02 09:02:37 +03:00

Massive commit to run PGINDENT on all *.c and *.h files.

This commit is contained in:
Bruce Momjian
1997-09-07 05:04:48 +00:00
parent 8fecd4febf
commit 1ccd423235
687 changed files with 150775 additions and 136888 deletions

View File

@ -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;
Relation relation;
/* ----------------
* 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.");
relation = (Relation) NULL;
return relation;
/* ----------------
* 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;
Pointer scanDesc;
/* ----------------
* 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;
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;
CommonScanState *state;
Relation relation;
HeapScanDesc scanDesc;
case T_IndexScan:
state = ((IndexScan *)node)->scan.scanstate;
break;
case T_Material:
state = &(((Material *)node)->matstate->csstate);
break;
case T_Sort:
state = &(((Sort *)node)->sortstate->csstate);
break;
case T_Agg:
state = &(((Agg *)node)->aggstate->csstate);
break;
default:
elog(DEBUG, "ExecCloseR: not a scan, material, or sort node!");
return;
}
relation = state->css_currentRelation;
scanDesc = state->css_currentScanDesc;
if (scanDesc != NULL)
heap_endscan(scanDesc);
if (relation != NULL)
heap_close(relation);
/* ----------------
* if this is an index scan then we have to take care
* of the index relations as well..
* ----------------
*/
if (nodeTag(node) == T_IndexScan) {
IndexScan *iscan= (IndexScan *)node;
IndexScanState *indexstate;
int numIndices;
RelationPtr indexRelationDescs;
IndexScanDescPtr indexScanDescs;
int i;
indexstate = iscan->indxstate;
numIndices = indexstate->iss_NumIndices;
indexRelationDescs = indexstate->iss_RelationDescs;
indexScanDescs = indexstate->iss_ScanDescs;
for (i = 0; i<numIndices; i++) {
/* ----------------
* shut down each of the scans and
* close each of the index relations
* ----------------
*/
if (indexScanDescs[i] != NULL)
index_endscan(indexScanDescs[i]);
if (indexRelationDescs[i] != NULL)
index_close(indexRelationDescs[i]);
}
}
}
/* ----------------------------------------------------------------
* ExecReScan
*
* XXX this should be extended to cope with all the node types..
*
* takes the new expression context as an argument, so that
* index scans needn't have their scan keys updated separately
* - marcel 09/20/94
* ----------------------------------------------------------------
*/
void
ExecReScan(Plan *node, ExprContext *exprCtxt, Plan *parent)
{
switch(nodeTag(node)) {
case T_SeqScan:
ExecSeqReScan((SeqScan *) node, exprCtxt, parent);
return;
case T_IndexScan:
ExecIndexReScan((IndexScan *) node, exprCtxt, parent);
return;
case T_Material:
/* the first call to ExecReScan should have no effect because
* everything is initialized properly already. the following
* calls will be handled by ExecSeqReScan() because the nodes
* below the Material node have already been materialized into
* a temp relation.
/* ----------------
* shut down the heap scan and close the heap relation
* ----------------
*/
return;
switch (nodeTag(node))
{
case T_Tee:
ExecTeeReScan((Tee*) node, exprCtxt, parent);
break;
case T_SeqScan:
state = ((SeqScan *) node)->scanstate;
break;
default:
elog(WARN, "ExecReScan: not a seqscan or indexscan node.");
return;
}
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]);
}
}
}
/* ----------------------------------------------------------------
* ExecReScanR
* ExecReScan
*
* XXX this does not do the right thing with indices yet.
* XXX this should be extended to cope with all the node types..
*
* takes the new expression context as an argument, so that
* index scans needn't have their scan keys updated separately
* - marcel 09/20/94
* ----------------------------------------------------------------
*/
void
ExecReScan(Plan * node, ExprContext * exprCtxt, Plan * parent)
{
switch (nodeTag(node))
{
case T_SeqScan:
ExecSeqReScan((SeqScan *) node, exprCtxt, parent);
return;
case T_IndexScan:
ExecIndexReScan((IndexScan *) node, exprCtxt, parent);
return;
case T_Material:
/*
* the first call to ExecReScan should have no effect because
* everything is initialized properly already. the following
* calls will be handled by ExecSeqReScan() because the nodes
* below the Material node have already been materialized into a
* temp relation.
*/
return;
case T_Tee:
ExecTeeReScan((Tee *) node, exprCtxt, parent);
break;
default:
elog(WARN, "ExecReScan: not a seqscan or indexscan node.");
return;
}
}
/* ----------------------------------------------------------------
* ExecReScanR
*
* XXX this does not do the right thing with indices yet.
* ----------------------------------------------------------------
*/
HeapScanDesc
ExecReScanR(Relation relDesc, /* LLL relDesc unused */
HeapScanDesc scanDesc,
ScanDirection direction,
int nkeys, /* LLL nkeys unused */
ScanKey skeys)
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;
switch (nodeTag(node))
{
case T_SeqScan:
ExecSeqMarkPos((SeqScan *) node);
break;
case T_IndexScan:
ExecIndexMarkPos((IndexScan *) node);
break;
case T_IndexScan:
ExecIndexMarkPos((IndexScan *) node);
break;
case T_Sort:
ExecSortMarkPos((Sort *) node);
break;
case T_Sort:
ExecSortMarkPos((Sort *) node);
break;
default:
/* elog(DEBUG, "ExecMarkPos: unsupported node type"); */
break;
}
return;
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;
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)
* ----------------
*/
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;
}

View File

@ -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;
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)
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

View File

@ -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;
JunkFilter *junkfilter;
List *cleanTargetList;
int len,
cleanLength;
TupleDesc tupType,
cleanTupType;
List *t;
TargetEntry *tle;
Resdom *resdom,
*cleanResdom;
int resjunk;
AttrNumber cleanResno;
AttrNumber *cleanMap;
Size size;
Node *expr;
/* ---------------------
* First find the "clean" target list, i.e. all the entries
* in the original target list which have a zero 'resjunk'
* NOTE: make copy of the Resdom nodes, because we have
* to change the 'resno's...
* ---------------------
*/
cleanTargetList = NIL;
cleanResno = 1;
foreach (t, targetList) {
TargetEntry *rtarget = lfirst(t);
if (rtarget->resdom != NULL) {
resdom = rtarget->resdom;
expr = rtarget->expr;
resjunk = resdom->resjunk;
if (resjunk == 0) {
/*
* make a copy of the resdom node, changing its resno.
*/
cleanResdom = (Resdom *) copyObject(resdom);
cleanResdom->resno = cleanResno;
cleanResno ++;
/*
* create a new target list entry
*/
tle = makeNode(TargetEntry);
tle->resdom = cleanResdom;
tle->expr = expr;
cleanTargetList = lappend(cleanTargetList, tle);
}
}
else {
#ifdef SETS_FIXED
List *fjListP;
Fjoin *cleanFjoin;
List *cleanFjList;
List *fjList = lfirst(t);
Fjoin *fjNode = (Fjoin *)tl_node(fjList);
cleanFjoin = (Fjoin)copyObject((Node) fjNode);
cleanFjList = lcons(cleanFjoin, NIL);
resdom = (Resdom) lfirst(get_fj_innerNode(fjNode));
expr = lsecond(get_fj_innerNode(fjNode));
cleanResdom = (Resdom) copyObject((Node) resdom);
set_resno(cleanResdom, cleanResno);
cleanResno++;
tle = (List) MakeTLE(cleanResdom, (Expr) expr);
set_fj_innerNode(cleanFjoin, tle);
foreach(fjListP, lnext(fjList)) {
TargetEntry *tle = lfirst(fjListP);
resdom = tle->resdom;
expr = tle->expr;
cleanResdom = (Resdom*) copyObject((Node) resdom);
cleanResno++;
cleanResdom->Resno = cleanResno;
/*
* create a new target list entry
*/
tle = (List) MakeTLE(cleanResdom, (Expr) expr);
cleanFjList = lappend(cleanFjList, tle);
}
lappend(cleanTargetList, cleanFjList);
#endif
}
}
/* ---------------------
* Now calculate the tuple types for the original and the clean tuple
*
* XXX ExecTypeFromTL should be used sparingly. Don't we already
* have the tupType corresponding to the targetlist we are passed?
* -cim 5/31/91
* ---------------------
*/
tupType = (TupleDesc) ExecTypeFromTL(targetList);
cleanTupType = (TupleDesc) ExecTypeFromTL(cleanTargetList);
len = ExecTargetListLength(targetList);
cleanLength = ExecTargetListLength(cleanTargetList);
/* ---------------------
* Now calculate the "map" between the original tuples attributes
* and the "clean" tuple's attributes.
*
* The "map" is an array of "cleanLength" attribute numbers, i.e.
* one entry for every attribute of the "clean" tuple.
* The value of this entry is the attribute number of the corresponding
* attribute of the "original" tuple.
* ---------------------
*/
if (cleanLength > 0) {
size = cleanLength * sizeof(AttrNumber);
cleanMap = (AttrNumber*) palloc(size);
/* ---------------------
* 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 *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 ++;
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 {
else
{
#ifdef SETS_FIXED
List fjListP;
List fjList = lfirst(t);
Fjoin fjNode = (Fjoin)lfirst(fjList);
List *fjListP;
Fjoin *cleanFjoin;
List *cleanFjList;
List *fjList = lfirst(t);
Fjoin *fjNode = (Fjoin *) tl_node(fjList);
/* what the hell is this????? */
resdom = (Resdom) lfirst(get_fj_innerNode(fjNode));
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
cleanMap[cleanResno-1] = tle->resdom->resno;
cleanResno++;
#ifdef SETS_FIXED
foreach(fjListP, lnext(fjList)) {
TargetEntry *tle = lfirst(fjListP);
resdom = tle->resdom;
cleanMap[cleanResno-1] = resdom->resno;
cleanResno++;
}
#endif
}
}
} else {
cleanMap = NULL;
}
/* ---------------------
* Finally create and initialize the JunkFilter.
* ---------------------
*/
junkfilter = makeNode(JunkFilter);
junkfilter->jf_targetList = targetList;
junkfilter->jf_length = len;
junkfilter->jf_tupType = tupType;
junkfilter->jf_cleanTargetList = cleanTargetList;
junkfilter->jf_cleanLength = cleanLength;
junkfilter->jf_cleanTupType = cleanTupType;
junkfilter->jf_cleanMap = cleanMap;
return(junkfilter);
/* ---------------------
* Now calculate the tuple types for the original and the clean tuple
*
* XXX ExecTypeFromTL should be used sparingly. Don't we already
* have the tupType corresponding to the targetlist we are passed?
* -cim 5/31/91
* ---------------------
*/
tupType = (TupleDesc) ExecTypeFromTL(targetList);
cleanTupType = (TupleDesc) ExecTypeFromTL(cleanTargetList);
len = ExecTargetListLength(targetList);
cleanLength = ExecTargetListLength(cleanTargetList);
/* ---------------------
* Now calculate the "map" between the original tuples attributes
* and the "clean" tuple's attributes.
*
* The "map" is an array of "cleanLength" attribute numbers, i.e.
* one entry for every attribute of the "clean" tuple.
* The value of this entry is the attribute number of the corresponding
* attribute of the "original" tuple.
* ---------------------
*/
if (cleanLength > 0)
{
size = cleanLength * sizeof(AttrNumber);
cleanMap = (AttrNumber *) palloc(size);
cleanResno = 1;
foreach(t, targetList)
{
TargetEntry *tle = lfirst(t);
if (tle->resdom != NULL)
{
resdom = tle->resdom;
expr = tle->expr;
resjunk = resdom->resjunk;
if (resjunk == 0)
{
cleanMap[cleanResno - 1] = resdom->resno;
cleanResno++;
}
}
else
{
#ifdef SETS_FIXED
List fjListP;
List fjList = lfirst(t);
Fjoin fjNode = (Fjoin) lfirst(fjList);
/* what the hell is this????? */
resdom = (Resdom) lfirst(get_fj_innerNode(fjNode));
#endif
cleanMap[cleanResno - 1] = tle->resdom->resno;
cleanResno++;
#ifdef SETS_FIXED
foreach(fjListP, lnext(fjList))
{
TargetEntry *tle = lfirst(fjListP);
resdom = tle->resdom;
cleanMap[cleanResno - 1] = resdom->resno;
cleanResno++;
}
#endif
}
}
}
else
{
cleanMap = NULL;
}
/* ---------------------
* Finally create and initialize the JunkFilter.
* ---------------------
*/
junkfilter = makeNode(JunkFilter);
junkfilter->jf_targetList = targetList;
junkfilter->jf_length = len;
junkfilter->jf_tupType = tupType;
junkfilter->jf_cleanTargetList = cleanTargetList;
junkfilter->jf_cleanLength = cleanLength;
junkfilter->jf_cleanTupType = cleanTupType;
junkfilter->jf_cleanMap = cleanMap;
return (junkfilter);
}
/*-------------------------------------------------------------------------
@ -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);
}
/* ---------------------
* 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;
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;
}
/*-------------------------------------------------------------------------
@ -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';
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);
}
HeapTuple tuple;
HeapTuple cleanTuple;
AttrNumber *cleanMap;
TupleDesc cleanTupType;
TupleDesc tupType;
int cleanLength;
bool isNull;
int i;
Size size;
Datum *values;
char *nulls;
Datum values_array[64];
char nulls_array[64];
/* ----------------
* get info from the slot and the junk filter
* ----------------
*/
tuple = slot->val;
tupType = (TupleDesc) junkfilter->jf_tupType;
cleanTupType = (TupleDesc) junkfilter->jf_cleanTupType;
cleanLength = junkfilter->jf_cleanLength;
cleanMap = junkfilter->jf_cleanMap;
/* ---------------------
* Handle the trivial case first.
* ---------------------
*/
if (cleanLength == 0)
return (HeapTuple) NULL;
/* ---------------------
* Create the arrays that will hold the attribute values
* and the null information for the new "clean" tuple.
*
* Note: we use memory on the stack to optimize things when
* we are dealing with a small number of tuples.
* for large tuples we just use palloc.
* ---------------------
*/
if (cleanLength > 64)
{
size = cleanLength * sizeof(Datum);
values = (Datum *) palloc(size);
size = cleanLength * sizeof(char);
nulls = (char *) palloc(size);
}
else
{
values = values_array;
nulls = nulls_array;
}
/* ---------------------
* Exctract one by one all the values of the "clean" tuple.
* ---------------------
*/
for (i = 0; i < cleanLength; i++)
{
Datum d = (Datum)
heap_getattr(tuple, InvalidBuffer, cleanMap[i], tupType, &isNull);
values[i] = d;
if (isNull)
nulls[i] = 'n';
else
nulls[i] = ' ';
}
/* ---------------------
* Now form the new tuple.
* ---------------------
*/
cleanTuple = heap_formtuple(cleanTupType,
values,
nulls);
/* ---------------------
* We are done. Free any space allocated for 'values' and 'nulls'
* and return the new tuple.
* ---------------------
*/
if (cleanLength > 64)
{
pfree(values);
pfree(nulls);
}
return (cleanTuple);
}

File diff suppressed because it is too large Load Diff

View File

@ -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;
/* ----------------
* 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;
bool result;
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;
/* ----------------
* do nothing when we get to the end
* of a leaf on tree.
* ----------------
*/
if (node == NULL)
return FALSE;
default:
elog(DEBUG, "ExecInitNode: node not yet supported: %d",
nodeTag(node));
result = FALSE;
}
return result;
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;
TupleTableSlot *result;
case T_IndexScan:
result = ExecIndexScan((IndexScan *)node);
break;
/* ----------------
* join nodes
* deal with NULL 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;
if (node == NULL)
return NULL;
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;
switch (nodeTag(node))
{
/* ----------------
* control nodes
* ----------------
*/
case T_Result:
result = ExecResult((Result *) node);
break;
default:
elog(DEBUG, "ExecProcNode: node not yet supported: %d",
nodeTag(node));
result = FALSE;
}
return result;
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)
if (node == (Plan *) NULL)
return 0;
switch (nodeTag(node))
{
/* ----------------
* control nodes
* ----------------
*/
case T_Result:
return ExecCountSlotsResult((Result *) node);
case T_Append:
return ExecCountSlotsAppend((Append *) node);
/* ----------------
* scan nodes
* ----------------
*/
case T_SeqScan:
return ExecCountSlotsSeqScan((SeqScan *) node);
case T_IndexScan:
return ExecCountSlotsIndexScan((IndexScan *) node);
/* ----------------
* join nodes
* ----------------
*/
case T_NestLoop:
return ExecCountSlotsNestLoop((NestLoop *) node);
case T_MergeJoin:
return ExecCountSlotsMergeJoin((MergeJoin *) node);
/* ----------------
* materialization nodes
* ----------------
*/
case T_Material:
return ExecCountSlotsMaterial((Material *) node);
case T_Sort:
return ExecCountSlotsSort((Sort *) node);
case T_Unique:
return ExecCountSlotsUnique((Unique *) node);
case T_Group:
return ExecCountSlotsGroup((Group *) node);
case T_Agg:
return ExecCountSlotsAgg((Agg *) node);
case T_Hash:
return ExecCountSlotsHash((Hash *) node);
case T_HashJoin:
return ExecCountSlotsHashJoin((HashJoin *) node);
case T_Tee:
return ExecCountSlotsTee((Tee *) node);
default:
elog(WARN, "ExecCountSlotsNode: node not yet supported: %d",
nodeTag(node));
break;
}
return 0;
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
* do nothing when we get to the end
* of a leaf on tree.
* ----------------
*/
case T_Result:
ExecEndResult((Result *)node);
break;
case T_Append:
ExecEndAppend((Append *)node);
break;
/* ----------------
* scan nodes
* ----------------
*/
case T_SeqScan:
ExecEndSeqScan((SeqScan *)node);
break;
if (node == NULL)
return;
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;
switch (nodeTag(node))
{
/* ----------------
* control nodes
* ----------------
*/
case T_Result:
ExecEndResult((Result *) 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;
case T_Append:
ExecEndAppend((Append *) node);
break;
default:
elog(DEBUG, "ExecEndNode: node not yet supported",
nodeTag(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;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -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;
CommonScanState *scanstate;
EState *estate;
List *qual;
bool isDone;
TupleTableSlot *slot;
TupleTableSlot *resultSlot;
HeapTuple newTuple;
TupleTableSlot *slot;
TupleTableSlot *resultSlot;
HeapTuple newTuple;
ExprContext *econtext;
ProjectionInfo *projInfo;
ExprContext *econtext;
ProjectionInfo *projInfo;
/* ----------------
* initialize misc variables
* ----------------
*/
newTuple = NULL;
slot = NULL;
/* ----------------
* initialize misc variables
* ----------------
*/
newTuple = NULL;
slot = NULL;
estate = node->plan.state;
scanstate = node->scanstate;
estate = node->plan.state;
scanstate = node->scanstate;
/* ----------------
* get the expression context
* ----------------
*/
econtext = scanstate->cstate.cs_ExprContext;
/* ----------------
* 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;
/* ----------------
* 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) {
if (scanstate->cstate.cs_TupFromTlist)
{
projInfo = scanstate->cstate.cs_ProjInfo;
resultSlot = ExecProject(projInfo, &isDone);
if (!isDone)
return resultSlot;
}
/*
* get a tuple from the access method loop until we obtain a tuple
* which passes the qualification.
*/
for (;;)
{
slot = (TupleTableSlot *) (*accessMtd) (node);
/* ----------------
* if the slot returned by the accessMtd contains
* NULL, then it means there is nothing more to scan
* so we just return the empty slot.
* ----------------
*/
if (TupIsNull(slot))
return slot;
/* ----------------
* place the current tuple into the expr context
* ----------------
*/
econtext->ecxt_scantuple = slot;
/* ----------------
* check that the current tuple satisfies the qual-clause
* if our qualification succeeds then we
* leave the loop.
* ----------------
*/
/*
* add a check for non-nil qual here to avoid a function call to
* ExecQual() when the qual is nil
*/
if (!qual || ExecQual(qual, econtext) == true)
break;
}
/* ----------------
* form a projection tuple, store it in the result tuple
* slot and return it.
* ----------------
*/
projInfo = scanstate->cstate.cs_ProjInfo;
resultSlot = ExecProject(projInfo, &isDone);
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);
scanstate->cstate.cs_TupFromTlist = !isDone;
/* ----------------
* if the slot returned by the accessMtd contains
* NULL, then it means there is nothing more to scan
* so we just return the empty slot.
* ----------------
*/
if (TupIsNull(slot)) return slot;
/* ----------------
* place the current tuple into the expr context
* ----------------
*/
econtext->ecxt_scantuple = slot;
/* ----------------
* check that the current tuple satisfies the qual-clause
* if our qualification succeeds then we
* leave the loop.
* ----------------
*/
/* add a check for non-nil qual here to avoid a
function call to ExecQual() when the qual is nil */
if (!qual || ExecQual(qual, econtext) == true)
break;
}
/* ----------------
* form a projection tuple, store it in the result tuple
* slot and return it.
* ----------------
*/
projInfo = scanstate->cstate.cs_ProjInfo;
resultSlot = ExecProject(projInfo, &isDone);
scanstate->cstate.cs_TupFromTlist = !isDone;
return resultSlot;
return resultSlot;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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));
execution_state *newes;
execution_state *nextes;
execution_state *preves;
QueryTreeList *queryTree_list;
int i;
List *planTree_list;
int nargs;
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;
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;
}
#endif
if (es->qd->operation == CMD_UTILITY)
{
feature = (LAST_POSTQUEL_COMMAND(es)) ? EXEC_RETONE : EXEC_RUN;
return ExecutorRun(es->qd, es->estate, feature, 0);
}
static void
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;
}
#endif
ExecutorEnd(es->qd, es->estate);
}
static void
postquel_sub_params(execution_state *es,
int nargs,
char *args[],
bool *nullV)
{
ParamListInfo paramLI;
EState *estate;
estate = es->estate;
paramLI = estate->es_param_list_info;
while (paramLI->kind != PARAM_INVALID) {
if (paramLI->kind == PARAM_NUM) {
Assert(paramLI->id <= nargs);
paramLI->value = (Datum)args[(paramLI->id - 1)];
paramLI->isnull = nullV[(paramLI->id - 1)];
/*
* 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);
}
static void
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;
}
#endif
ExecutorEnd(es->qd, es->estate);
}
static void
postquel_sub_params(execution_state * es,
int nargs,
char *args[],
bool * nullV)
{
ParamListInfo paramLI;
EState *estate;
estate = es->estate;
paramLI = estate->es_param_list_info;
while (paramLI->kind != PARAM_INVALID)
{
if (paramLI->kind == PARAM_NUM)
{
Assert(paramLI->id <= nargs);
paramLI->value = (Datum) args[(paramLI->id - 1)];
paramLI->isnull = nullV[(paramLI->id - 1)];
}
paramLI++;
}
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 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;
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;
}
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;
/*
* If this isn't the last command for the function, we don't return
* any results, but we have to increment the command counter so that
* subsequent commands can see changes made by previous ones.
*/
CommandCounterIncrement();
return (Datum) NULL;
}
Datum
postquel_function(Func *funcNode, char **args, bool *isNull, bool *isDone)
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)
{
es = init_execution_state(fcache, args);
fcache->func_state = (char *) es;
}
while (es && es->status == F_EXEC_DONE)
es = es->next;
Assert(es);
/*
* Execute each command in the function one after another until we're
* executing the final command and get a result or we run out of
* commands.
*/
while (es != (execution_state *)NULL)
{
result = postquel_execute(es,
fcache,
funcNode->func_tlist,
args,
isNull);
if (es->status != F_EXEC_DONE)
break;
es = es->next;
}
/*
* If we've gone through every command in this function, we are done.
*/
if (es == (execution_state *)NULL)
{
execution_state *es;
Datum result = 0;
FunctionCachePtr fcache = funcNode->func_fcache;
CommandId savedId;
/*
* Reset the execution states to start over again
* 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
*/
es = (execution_state *)fcache->func_state;
while (es)
savedId = GetScanCommandId();
SetScanCommandId(GetCurrentCommandId());
es = (execution_state *) fcache->func_state;
if (es == NULL)
{
es->status = F_EXEC_START;
es = es->next;
es = init_execution_state(fcache, args);
fcache->func_state = (char *) es;
}
while (es && es->status == F_EXEC_DONE)
es = es->next;
Assert(es);
/*
* Let caller know we're finished.
* 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.
*/
*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;
while (es != (execution_state *) NULL)
{
result = postquel_execute(es,
fcache,
funcNode->func_tlist,
args,
isNull);
if (es->status != F_EXEC_DONE)
break;
es = es->next;
}
/*
* If we've gone through every command in this function, we are done.
*/
if (es == (execution_state *) NULL)
{
/*
* Reset the execution states to start over again
*/
es = (execution_state *) fcache->func_state;
while (es)
{
es->status = F_EXEC_START;
es = es->next;
}
/*
* Let caller know we're finished.
*/
*isDone = true;
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;
}

File diff suppressed because it is too large Load Diff

View File

@ -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;
EState *estate;
AppendState *unionstate;
TupleTableSlot *result_slot;
List *rangeTable;
rt_store(unionrelid, rangeTable, rtentry);
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;
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;
}
else if (whichplan >= nplans)
{
/* ----------------
* as above, end the scan if we go beyond
* the last scan in our list..
* ----------------
*/
unionstate->as_whichplan = nplans - 1;
return FALSE;
}
else
{
/* ----------------
* initialize the scan
* (and update the range table appropriately)
* (doesn't this leave the range table hosed for anybody upstream
* of the Append node??? - jolly )
* ----------------
*/
if (node->unionrelid > 0)
{
rtentry = nth(whichplan, rtentries);
if (rtentry == NULL)
elog(DEBUG, "exec_append_initialize_next: rtentry is nil");
unionrelid = node->unionrelid;
rt_store(unionrelid, rangeTable, rtentry);
if (unionstate->as_junkFilter_list)
{
estate->es_junkFilter =
(JunkFilter *) nth(whichplan,
unionstate->as_junkFilter_list);
}
if (unionstate->as_result_relation_info_list)
{
estate->es_result_relation_info =
(RelationInfo *) nth(whichplan,
unionstate->as_result_relation_info_list);
}
result_slot->ttc_whichplan = whichplan;
}
return TRUE;
}
}
/* ----------------------------------------------------------------
* ExecInitAppend
*
* Begins all of the subscans of the append node, storing the
* scan structures in the 'initialized' vector of the append-state
* structure.
* ExecInitAppend
*
* (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.
* Begins all of the subscans of the append node, storing the
* scan structures in the 'initialized' vector of the append-state
* structure.
*
* (This is potentially wasteful, since the entire result of the
* append node may not be scanned, but this way all of the
* structures get allocated in the executor's top level memory
* block instead of that of the call to ExecProcAppend.)
*
* Returns the scan result of the first scan.
* ----------------------------------------------------------------
*/
bool
ExecInitAppend(Append *node, EState *estate, Plan *parent)
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;
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;
node->unionstate = unionstate;
/* ----------------
* Miscellanious initialization
*
* + assign node's base_id
* + assign debugging hooks
*
* Append plans don't have expression contexts because they
* never call ExecQual or ExecTargetList.
* ----------------
*/
ExecAssignNodeBaseInfo(estate, &unionstate->cstate, parent);
#define APPEND_NSLOTS 1
/* ----------------
* append nodes still have Result slots, which hold pointers
* to tuples, so we have to initialize them..
* ----------------
*/
ExecInitResultTupleSlot(estate, &unionstate->cstate);
/*
* If the inherits rtentry is the result relation, we have to make
* a result relation info list for all inheritors so we can update
* their indices and put the result tuples in the right place etc.
*
* e.g. replace p (age = p.age + 1) from p in person*
*/
if ((es_rri != (RelationInfo*)NULL) &&
(node->unionrelid == es_rri->ri_RangeTableIndex))
{
RelationInfo *rri;
List *rtentryP;
foreach(rtentryP,rtentries)
{
Oid reloid;
RangeTblEntry *rtentry = lfirst(rtentryP);
reloid = rtentry->relid;
rri = makeNode(RelationInfo);
rri->ri_RangeTableIndex = es_rri->ri_RangeTableIndex;
rri->ri_RelationDesc = heap_open(reloid);
rri->ri_NumIndices = 0;
rri->ri_IndexRelationDescs = NULL; /* index descs */
rri->ri_IndexRelationInfo = NULL; /* index key info */
resultList = lcons(rri,resultList);
ExecOpenIndices(reloid, rri);
}
unionstate->as_result_relation_info_list = resultList;
}
/* ----------------
* call ExecInitNode on each of the plans in our list
* and save the results into the array "initialized"
* ----------------
*/
junkList = NIL;
for(i = 0; i < nplans ; i++ ) {
JunkFilter *j;
List *targetList;
/* ----------------
* NOTE: we first modify range table in
* exec_append_initialize_next() and
* then initialize the subnode,
* since it may use the range table.
* ----------------
*/
unionstate->as_whichplan = i;
exec_append_initialize_next(node);
initNode = (Plan *) nth(i, unionplans);
initialized[i] = ExecInitNode(initNode, estate, (Plan*) node);
/* ---------------
* Each targetlist in the subplan may need its own junk filter
*
* This is true only when the reln being replaced/deleted is
* the one that we're looking at the subclasses of
* ---------------
/* ----------------
* assign execution state to node and get information
* for append state
* ----------------
*/
if ((es_rri != (RelationInfo*)NULL) &&
(node->unionrelid == es_rri->ri_RangeTableIndex)) {
targetList = initNode->targetlist;
j = (JunkFilter *) ExecInitJunkFilter(targetList);
junkList = lappend(junkList, j);
node->plan.state = estate;
unionplans = node->unionplans;
nplans = length(unionplans);
rtentries = node->unionrtentries;
CXT1_printf("ExecInitAppend: context is %d\n", CurrentMemoryContext);
initialized = (bool *) palloc(nplans * sizeof(bool));
/* ----------------
* create new AppendState for our append node
* ----------------
*/
unionstate = makeNode(AppendState);
unionstate->as_whichplan = 0;
unionstate->as_nplans = nplans;
unionstate->as_initialized = initialized;
unionstate->as_rtentries = rtentries;
node->unionstate = unionstate;
/* ----------------
* Miscellanious initialization
*
* + assign node's base_id
* + assign debugging hooks
*
* Append plans don't have expression contexts because they
* never call ExecQual or ExecTargetList.
* ----------------
*/
ExecAssignNodeBaseInfo(estate, &unionstate->cstate, parent);
#define APPEND_NSLOTS 1
/* ----------------
* append nodes still have Result slots, which hold pointers
* to tuples, so we have to initialize them..
* ----------------
*/
ExecInitResultTupleSlot(estate, &unionstate->cstate);
/*
* If the inherits rtentry is the result relation, we have to make a
* result relation info list for all inheritors so we can update their
* indices and put the result tuples in the right place etc.
*
* e.g. replace p (age = p.age + 1) from p in person*
*/
if ((es_rri != (RelationInfo *) NULL) &&
(node->unionrelid == es_rri->ri_RangeTableIndex))
{
RelationInfo *rri;
List *rtentryP;
foreach(rtentryP, rtentries)
{
Oid reloid;
RangeTblEntry *rtentry = lfirst(rtentryP);
reloid = rtentry->relid;
rri = makeNode(RelationInfo);
rri->ri_RangeTableIndex = es_rri->ri_RangeTableIndex;
rri->ri_RelationDesc = heap_open(reloid);
rri->ri_NumIndices = 0;
rri->ri_IndexRelationDescs = NULL; /* index descs */
rri->ri_IndexRelationInfo = NULL; /* index key info */
resultList = lcons(rri, resultList);
ExecOpenIndices(reloid, rri);
}
unionstate->as_result_relation_info_list = resultList;
}
}
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);
/* ----------------
* call ExecInitNode on each of the plans in our list
* and save the results into the array "initialized"
* ----------------
*/
junkList = NIL;
for (i = 0; i < nplans; i++)
{
JunkFilter *j;
List *targetList;
/* ----------------
* NOTE: we first modify range table in
* exec_append_initialize_next() and
* then initialize the subnode,
* since it may use the range table.
* ----------------
*/
unionstate->as_whichplan = i;
exec_append_initialize_next(node);
initNode = (Plan *) nth(i, unionplans);
initialized[i] = ExecInitNode(initNode, estate, (Plan *) node);
/* ---------------
* Each targetlist in the subplan may need its own junk filter
*
* This is true only when the reln being replaced/deleted is
* the one that we're looking at the subclasses of
* ---------------
*/
if ((es_rri != (RelationInfo *) NULL) &&
(node->unionrelid == es_rri->ri_RangeTableIndex))
{
targetList = initNode->targetlist;
j = (JunkFilter *) ExecInitJunkFilter(targetList);
junkList = lappend(junkList, j);
}
}
unionstate->as_junkFilter_list = junkList;
if (junkList != NIL)
estate->es_junkFilter = (JunkFilter *) lfirst(junkList);
/* ----------------
* initialize the return type from the appropriate subplan.
* ----------------
*/
initNode = (Plan *) nth(0, unionplans);
ExecAssignResultType(&unionstate->cstate,
/* ExecGetExecTupDesc(initNode), */
ExecGetTupType(initNode));
unionstate->cstate.cs_ProjInfo = NULL;
/* ----------------
* return the result from the first subplan's initialization
* ----------------
*/
unionstate->as_whichplan = 0;
exec_append_initialize_next(node);
#if 0
result = (List *) initialized[0];
#endif
return TRUE;
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;
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);
/* ----------------
* get information from the node
* ----------------
*/
unionstate = node->unionstate;
unionplans = node->unionplans;
nplans = unionstate->as_nplans;
initialized = unionstate->as_initialized;
/* XXX should free unionstate->as_rtentries and unionstate->as_junkfilter_list here */
/* ----------------
* 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
*/
}

View File

@ -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;
estate = node->plan.state;
econtext = grpstate->csstate.cstate.cs_ExprContext;
if (grpstate->grp_useLastTuple) {
/*
* we haven't returned last tuple yet because it is not of the
* same group
/* ---------------------
* get state info from node
* ---------------------
*/
grpstate->grp_useLastTuple = FALSE;
grpstate = node->grpstate;
if (grpstate->grp_done)
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;
estate = node->plan.state;
econtext = grpstate->csstate.cstate.cs_ExprContext;
if (grpstate->grp_useLastTuple)
{
/*
* we haven't returned last tuple yet because it is not of the
* same group
*/
grpstate->grp_useLastTuple = FALSE;
ExecStoreTuple(grpstate->grp_lastSlot->val,
grpstate->csstate.css_ScanTupleSlot,
grpstate->grp_lastSlot->ttc_buffer,
false);
}
else
{
outerslot = ExecProcNode(outerPlan(node), (Plan *) node);
if (outerslot)
outerTuple = outerslot->val;
if (!HeapTupleIsValid(outerTuple))
{
grpstate->grp_done = TRUE;
return NULL;
}
/* ----------------
* Compare with last tuple and see if this tuple is of
* the same group.
* ----------------
*/
lastslot = grpstate->csstate.css_ScanTupleSlot;
if (lastslot->val != NULL &&
(!sameGroup(lastslot, outerslot,
node->numCols, node->grpColIdx,
ExecGetScanType(&grpstate->csstate))))
{
/* ExecGetResultType(&grpstate->csstate.cstate)))) {*/
grpstate->grp_useLastTuple = TRUE;
/* save it for next time */
grpstate->grp_lastSlot = outerslot;
/*
* signifies the end of the group
*/
return NULL;
}
ExecStoreTuple(outerTuple,
grpstate->csstate.css_ScanTupleSlot,
outerslot->ttc_buffer,
false);
}
/* ----------------
* 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;
projInfo = grpstate->csstate.cstate.cs_ProjInfo;
if (lastslot->val != NULL &&
(!sameGroup(lastslot, outerslot,
node->numCols, node->grpColIdx,
ExecGetScanType(&grpstate->csstate)))) {
/* ExecGetResultType(&grpstate->csstate.cstate)))) {*/
econtext->ecxt_scantuple = grpstate->csstate.css_ScanTupleSlot;
resultSlot = ExecProject(projInfo, &isDone);
grpstate->grp_useLastTuple = TRUE;
/* save it for next time */
grpstate->grp_lastSlot = outerslot;
/*
* signifies the end of the group
*/
return NULL;
}
ExecStoreTuple(outerTuple,
grpstate->csstate.css_ScanTupleSlot,
outerslot->ttc_buffer,
false);
}
/* ----------------
* form a projection tuple, store it in the result tuple
* slot and return it.
* ----------------
*/
projInfo = grpstate->csstate.cstate.cs_ProjInfo;
econtext->ecxt_scantuple = grpstate->csstate.css_ScanTupleSlot;
resultSlot = ExecProject(projInfo, &isDone);
return resultSlot;
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;
else
{
outerslot = ExecProcNode(outerPlan(node), (Plan *) node);
if (outerslot)
outerTuple = outerslot->val;
if (!HeapTupleIsValid(outerTuple))
{
grpstate->grp_done = TRUE;
return NULL;
}
ExecStoreTuple(outerTuple,
grpstate->csstate.css_ScanTupleSlot,
outerslot->ttc_buffer,
false);
}
lastslot = grpstate->csstate.css_ScanTupleSlot;
/*
* find all tuples that belong to a group
*/
for(;;) {
outerslot = ExecProcNode(outerPlan(node), (Plan*)node);
outerTuple = (outerslot) ? outerslot->val : NULL;
if (!HeapTupleIsValid(outerTuple)) {
/*
* we have at least one tuple (lastslot) if we reach here
*/
grpstate->grp_done = TRUE;
/*
* find all tuples that belong to a group
*/
for (;;)
{
outerslot = ExecProcNode(outerPlan(node), (Plan *) node);
outerTuple = (outerslot) ? outerslot->val : NULL;
if (!HeapTupleIsValid(outerTuple))
{
/* return lastslot */
break;
/*
* 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;
}
/* ----------------
* Compare with last tuple and see if this tuple is of
* the same group.
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.
* ----------------
*/
if ((!sameGroup(lastslot, outerslot,
node->numCols, node->grpColIdx,
ExecGetScanType(&grpstate->csstate)))) {
/* ExecGetResultType(&grpstate->csstate.cstate)))) {*/
projInfo = grpstate->csstate.cstate.cs_ProjInfo;
grpstate->grp_useLastTuple = TRUE;
econtext->ecxt_scantuple = lastslot;
resultSlot = ExecProject(projInfo, &isDone);
/* save it for next time */
grpstate->grp_lastSlot = outerslot;
/* return lastslot */
break;
}
ExecStoreTuple(outerTuple,
grpstate->csstate.css_ScanTupleSlot,
outerslot->ttc_buffer,
false);
lastslot = grpstate->csstate.css_ScanTupleSlot;
}
ExecStoreTuple(lastslot->val,
grpstate->csstate.css_ScanTupleSlot,
lastslot->ttc_buffer,
false);
/* ----------------
* form a projection tuple, store it in the result tuple
* slot and return it.
* ----------------
*/
projInfo = grpstate->csstate.cstate.cs_ProjInfo;
econtext->ecxt_scantuple = lastslot;
resultSlot = ExecProject(projInfo, &isDone);
return resultSlot;
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;
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);
/*
* 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);
/*
* tuple table initialization
*/
ExecInitScanTupleSlot(estate, &grpstate->csstate);
ExecInitResultTupleSlot(estate, &grpstate->csstate.cstate);
/*
* 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);
/*
* initializes child nodes
*/
outerPlan = outerPlan(node);
ExecInitNode(outerPlan, estate, (Plan *) node);
return TRUE;
/* ----------------
* 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);
/* clean up tuple table */
ExecClearTuple(grpstate->csstate.css_ScanTupleSlot);
outerPlan = outerPlan(node);
ExecEndNode(outerPlan, (Plan *) node);
/* 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;
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);
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);
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;
attr2 = heap_getattr(newslot->val,
InvalidBuffer,
att,
tupdesc,
&isNull2);
val1 = fmgr(typoutput, attr1,
gettypelem(tupdesc->attrs[att-1]->atttypid));
val2 = fmgr(typoutput, attr2,
gettypelem(tupdesc->attrs[att-1]->atttypid));
if (isNull1 == isNull2)
{
if (isNull1) /* both are null, they are equal */
continue;
/* 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;
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;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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) {
/* ----------------
* 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;
EState *estate;
MaterialState *matstate;
Plan *outerNode;
ScanDirection dir;
Relation tempRelation;
Relation currentRelation;
HeapScanDesc currentScanDesc;
HeapTuple heapTuple;
TupleTableSlot *slot;
Buffer buffer;
ExecAssignScanType(&matstate->csstate,
RelationGetTupleDescriptor(currentRelation));
/* ----------------
* finally set the sorted flag to true
* get state info from node
* ----------------
*/
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 = node->matstate;
estate = node->plan.state;
dir = estate->es_direction;
/* ----------------
* the first time we call this, we retrieve all tuples
* from the subplan into a temporary relation and then
* we sort the relation. Subsequent calls return tuples
* from the temporary relation.
* ----------------
*/
if (matstate->mat_Flag == false)
{
/* ----------------
* set all relations to be scanned in the forward direction
* while creating the temporary relation.
* ----------------
*/
estate->es_direction = 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;
}
/* ----------------
* at this point we know we have a sorted relation so
* we preform a simple scan on it with amgetnext()..
* ----------------
*/
currentScanDesc = matstate->csstate.css_currentScanDesc;
heapTuple = heap_getnext(currentScanDesc, /* scan desc */
ScanDirectionIsBackward(dir),
/* bkwd flag */
&buffer); /* return: buffer */
/* ----------------
* put the tuple into the scan tuple slot and return the slot.
* Note: since the tuple is really a pointer to a page, we don't want
* to call pfree() on it..
* ----------------
*/
slot = (TupleTableSlot *) matstate->csstate.css_ScanTupleSlot;
return ExecStoreTuple(heapTuple, /* tuple to store */
slot, /* slot to store in */
buffer, /* buffer for this tuple */
false); /* don't pfree this pointer */
}
/* ----------------------------------------------------------------
* ExecInitMaterial
* ExecInitMaterial
* ----------------------------------------------------------------
*/
bool /* initialization status */
ExecInitMaterial(Material *node, EState *estate, Plan *parent)
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);
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);
}
MaterialState *matstate;
Relation tempRelation;
Plan *outerPlan;
#ifdef NOT_USED /* not used */
/* ----------------
* 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);
}
#endif
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

File diff suppressed because it is too large Load Diff

View File

@ -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;
NestLoopState *nlstate;
Plan *innerPlan;
Plan *outerPlan;
bool needNewOuterTuple;
TupleTableSlot *outerTupleSlot;
TupleTableSlot *innerTupleSlot;
TupleTableSlot *outerTupleSlot;
TupleTableSlot *innerTupleSlot;
List *qual;
bool qualResult;
ExprContext *econtext;
List *qual;
bool qualResult;
ExprContext *econtext;
/* ----------------
* get information from the node
* ----------------
*/
ENL1_printf("getting info from node");
nlstate = node->nlstate;
qual = node->join.qual;
outerPlan = outerPlan(&node->join);
innerPlan = innerPlan(&node->join);
/* ----------------
* initialize expression context
* ----------------
*/
econtext = nlstate->jstate.cs_ExprContext;
/* ---------------- * get the current outer tuple
* ----------------
*/
outerTupleSlot = nlstate->jstate.cs_OuterTupleSlot;
econtext->ecxt_outertuple = outerTupleSlot;
/* ----------------
* Ok, everything is setup for the join so now loop until
* we return a qualifying join tuple..
* ----------------
*/
if (nlstate->jstate.cs_TupFromTlist) {
TupleTableSlot *result;
bool isDone;
result = ExecProject(nlstate->jstate.cs_ProjInfo, &isDone);
if (!isDone)
return result;
}
ENL1_printf("entering main loop");
for(;;) {
/* ----------------
* The essential idea now is to get the next inner tuple
* and join it with the current outer tuple.
* 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;
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;
}
/* ----------------
* 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");
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) */
/* ----------------
* 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");
}
/* ----------------
* 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).
* ----------------------------------------------------------------
* 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)
ExecInitNestLoop(NestLoop * node, EState * estate, Plan * parent)
{
NestLoopState *nlstate;
NestLoopState *nlstate;
NL1_printf("ExecInitNestLoop: %s\n",
"initializing node");
/* ----------------
* assign execution state to node
* ----------------
*/
node->join.state = estate;
NL1_printf("ExecInitNestLoop: %s\n",
"initializing node");
/* ----------------
* create new nest loop state
* ----------------
*/
nlstate = makeNode(NestLoopState);
nlstate->nl_PortalFlag = false;
node->nlstate = nlstate;
/* ----------------
* assign execution state to node
* ----------------
*/
node->join.state = estate;
/* ----------------
* 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);
/* ----------------
* create new nest loop state
* ----------------
*/
nlstate = makeNode(NestLoopState);
nlstate->nl_PortalFlag = false;
node->nlstate = nlstate;
/* ----------------
* Miscellanious initialization
*
* + assign node's base_id
* + assign debugging hooks and
* + create expression context for node
* ----------------
*/
ExecAssignNodeBaseInfo(estate, &nlstate->jstate, parent);
ExecAssignExprContext(estate, &nlstate->jstate);
#define NESTLOOP_NSLOTS 1
/* ----------------
* tuple table initialization
* ----------------
*/
ExecInitResultTupleSlot(estate, &nlstate->jstate);
/* ----------------
* tuple table initialization
* ----------------
*/
ExecInitResultTupleSlot(estate, &nlstate->jstate);
/* ----------------
* now initialize children
* ----------------
*/
ExecInitNode(outerPlan((Plan*)node), estate, (Plan*)node);
ExecInitNode(innerPlan((Plan*)node), estate, (Plan*)node);
/* ----------------
* 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);
/* ----------------
* 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;
/* ----------------
* 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;
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;
NestLoopState *nlstate;
NL1_printf("ExecEndNestLoop: %s\n",
"ending node processing");
NL1_printf("ExecEndNestLoop: %s\n",
"ending node processing");
/* ----------------
* get info from the node
* ----------------
*/
nlstate = node->nlstate;
/* ----------------
* 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);
/* ----------------
* 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);
/* ----------------
* 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);
/* ----------------
* clean out the tuple table
* ----------------
*/
ExecClearTuple(nlstate->jstate.cs_ResultTupleSlot);
NL1_printf("ExecEndNestLoop: %s\n",
"node processing ended");
NL1_printf("ExecEndNestLoop: %s\n",
"node processing ended");
}

View File

@ -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 {
resstate = node->resstate;
/* ----------------
* 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 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;
}
/* ----------------
* 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;
if (resstate->cstate.cs_TupFromTlist)
{
ProjectionInfo *projInfo;
projInfo = resstate->cstate.cs_ProjInfo;
resultSlot = ExecProject(projInfo, &isDone);
if (!isDone)
return resultSlot;
}
/* ----------------
* retrieve a tuple that satisfy the qual from the outer plan until
* there are no more.
*
* if rs_done is 1 then it means that we were asked to return
* a constant tuple and we alread did the last time ExecResult()
* was called, so now we are through.
* ----------------
*/
outerPlan = outerPlan(node);
while (!resstate->rs_done)
{
/* ----------------
* get next outer tuple if necessary.
* ----------------
*/
if (outerPlan != NULL)
{
outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node);
if (TupIsNull(outerTupleSlot))
return NULL;
resstate->cstate.cs_OuterTupleSlot = outerTupleSlot;
}
else
{
/* ----------------
* if we don't have an outer plan, then it's probably
* the case that we are doing a retrieve or an append
* with a constant target list, so we should only return
* the constant tuple once or never if we fail the qual.
* ----------------
*/
resstate->rs_done = 1;
}
/* ----------------
* get the information to place into the expr context
* ----------------
*/
resstate = node->resstate;
outerTupleSlot = resstate->cstate.cs_OuterTupleSlot;
/* ----------------
* fill in the information in the expression context
* XXX gross hack. use outer tuple as scan tuple
* ----------------
*/
econtext->ecxt_outertuple = outerTupleSlot;
econtext->ecxt_scantuple = outerTupleSlot;
/* ----------------
* form the result tuple and pass it back using ExecProject()
* ----------------
*/
projInfo = resstate->cstate.cs_ProjInfo;
resultSlot = ExecProject(projInfo, &isDone);
resstate->cstate.cs_TupFromTlist = !isDone;
return resultSlot;
}
return NULL;
}
/* ----------------------------------------------------------------
* ExecInitResult
*
* Creates the run-time state information for the result node
* produced by the planner and initailizes outer relations
* (child nodes).
* 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);
#define RESULT_NSLOTS 1
/* ----------------
* tuple table initialization
* ----------------
*/
ExecInitResultTupleSlot(estate, &resstate->cstate);
/* ----------------
* then initialize children
* ----------------
*/
ExecInitNode(outerPlan(node), estate, (Plan*)node);
ResultState *resstate;
/*
* 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;
/* ----------------
* assign execution state to node
* ----------------
*/
node->plan.state = estate;
/* ----------------
* create new ResultState for node
* ----------------
*/
resstate = makeNode(ResultState);
resstate->rs_done = 0;
node->resstate = resstate;
/* ----------------
* Miscellanious initialization
*
* + assign node's base_id
* + assign debugging hooks and
* + create expression context for node
* ----------------
*/
ExecAssignNodeBaseInfo(estate, &resstate->cstate, parent);
ExecAssignExprContext(estate, &resstate->cstate);
#define RESULT_NSLOTS 1
/* ----------------
* tuple table initialization
* ----------------
*/
ExecInitResultTupleSlot(estate, &resstate->cstate);
/* ----------------
* then initialize children
* ----------------
*/
ExecInitNode(outerPlan(node), estate, (Plan *) node);
/*
* we don't use inner plan
*/
Assert(innerPlan(node) == NULL);
/* ----------------
* initialize tuple type and projection info
* ----------------
*/
ExecAssignResultTypeFromTL((Plan *) node, &resstate->cstate);
ExecAssignProjectionInfo((Plan *) node, &resstate->cstate);
/* ----------------
* set "are we done yet" to false
* ----------------
*/
resstate->rs_done = 0;
return TRUE;
}
int
ExecCountSlotsResult(Result *node)
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);
ResultState *resstate;
/* ----------------
* clean out the tuple table
* ----------------
*/
ExecClearTuple(resstate->cstate.cs_ResultTupleSlot);
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);
}

View File

@ -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;
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 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 */
/* ----------------
* 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;
/* ----------------
* 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 */
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);
/* ----------------
* 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;
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;
TupleTableSlot *slot;
Plan *outerPlan;
S_printf("ExecSeqScan: scanning node: "); S_nodeDisplay(node);
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);
}
/* ----------------
* 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);
S1_printf("ExecSeqScan: returned tuple slot: %d\n", slot);
return 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;
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;
}
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...
* return the relation
* ----------------
*/
relid = node->scanrelid;
rangeTable = estate->es_range_table;
rtentry = rt_fetch(relid, rangeTable);
reloid = rtentry->relid;
timeQual = rtentry->timeQual;
direction = estate->es_direction;
resultRelationInfo = estate->es_result_relation_info;
ExecOpenScanR(reloid, /* relation */
0, /* nkeys */
NULL, /* scan key */
0, /* is index */
direction, /* scan direction */
timeQual, /* time qual */
&currentRelation, /* return: rel desc */
(Pointer *) &currentScanDesc); /* return: scan desc */
scanstate->css_currentRelation = currentRelation;
scanstate->css_currentScanDesc = currentScanDesc;
ExecAssignScanType(scanstate,
RelationGetTupleDescriptor(currentRelation));
} else {
/* ----------------
* otherwise we are scanning tuples from the
* outer subplan so we initialize the outer plan
* and nullify
* ----------------
*/
ExecInitNode(outerPlan, estate, (Plan*)node);
node->scanrelid = 0;
scanstate->css_currentRelation = NULL;
scanstate->css_currentScanDesc = NULL;
ExecAssignScanType(scanstate, NULL);
reloid = InvalidOid;
}
/* ----------------
* return the relation
* ----------------
*/
return reloid;
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;
CommonScanState *scanstate;
Plan *outerPlan;
Oid reloid;
HeapScanDesc scandesc;
/* ----------------
* assign the node's execution state
* ----------------
*/
node->plan.state = estate;
/* ----------------
* assign the node's execution state
* ----------------
*/
node->plan.state = estate;
/* ----------------
* create new CommonScanState for node
* ----------------
*/
scanstate = makeNode(CommonScanState);
node->scanstate = scanstate;
/* ----------------
* 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);
/* ----------------
* 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);
/* ----------------
* tuple table initialization
* ----------------
*/
ExecInitResultTupleSlot(estate, &scanstate->cstate);
ExecInitScanTupleSlot(estate, scanstate);
/* ----------------
* initialize scan relation or outer subplan
* ----------------
*/
outerPlan = outerPlan((Plan *)node);
/* ----------------
* initialize scan relation or outer subplan
* ----------------
*/
outerPlan = outerPlan((Plan *) node);
reloid = InitScanRelation(node, estate, scanstate, outerPlan);
reloid = InitScanRelation(node, estate, scanstate, outerPlan);
scandesc = scanstate->css_currentScanDesc;
scanstate->cstate.cs_TupFromTlist = false;
scandesc = scanstate->css_currentScanDesc;
scanstate->cstate.cs_TupFromTlist = false;
/* ----------------
* initialize tuple type
* ----------------
*/
ExecAssignResultTypeFromTL((Plan*)node, &scanstate->cstate);
ExecAssignProjectionInfo((Plan*)node, &scanstate->cstate);
/* ----------------
* initialize tuple type
* ----------------
*/
ExecAssignResultTypeFromTL((Plan *) node, &scanstate->cstate);
ExecAssignProjectionInfo((Plan *) node, &scanstate->cstate);
return TRUE;
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;
CommonScanState *scanstate;
Plan *outerPlan;
/* ----------------
* get information from node
* ----------------
*/
scanstate = node->scanstate;
/* ----------------
* 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);
/* ----------------
* 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);
/* ----------------
* 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 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);
/* ----------------
* 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;
CommonScanState *scanstate;
EState *estate;
Plan *outerPlan;
Relation rdesc;
HeapScanDesc sdesc;
ScanDirection direction;
scanstate = node->scanstate;
estate = node->plan.state;
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;
}
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;
CommonScanState *scanstate;
Plan *outerPlan;
HeapScanDesc sdesc;
scanstate = node->scanstate;
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);
/* ----------------
* if we are scanning a subplan then propagate
* the ExecMarkPos() request to the subplan
* ----------------
*/
outerPlan = outerPlan((Plan*)node);
if (outerPlan) {
ExecMarkPos(outerPlan);
return;
}
/* ----------------
* otherwise we are scanning a relation so mark the
* position using the access methods..
*
* ----------------
*/
sdesc = scanstate->css_currentScanDesc;
heap_markpos(sdesc);
return;
}
/* ----------------------------------------------------------------
* ExecSeqRestrPos
*
* Restores scan position.
* ExecSeqRestrPos
*
* Restores scan position.
* ----------------------------------------------------------------
*/
void
ExecSeqRestrPos(SeqScan *node)
ExecSeqRestrPos(SeqScan * node)
{
CommonScanState *scanstate;
Plan *outerPlan;
HeapScanDesc sdesc;
CommonScanState *scanstate;
Plan *outerPlan;
HeapScanDesc sdesc;
scanstate = node->scanstate;
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;
}
/* ----------------
* 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);
/* ----------------
* otherwise we are scanning a relation so restore the
* position using the access methods..
* ----------------
*/
sdesc = scanstate->css_currentScanDesc;
heap_restrpos(sdesc);
}

View File

@ -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));
ScanKey sortkeys;
List *targetList;
List *tl;
int keycount;
Resdom *resdom;
AttrNumber resno;
Index reskey;
Oid reskeyop;
/* ----------------
* 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);
/* ----------------
* 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");
/* ----------------
* set all relations to be scanned in the forward direction
* while creating the temporary relation.
* ----------------
*/
estate->es_direction = ForwardScanDirection;
EState *estate;
SortState *sortstate;
Plan *outerNode;
ScanDirection dir;
int keycount;
ScanKey sortkeys;
HeapTuple heapTuple;
TupleTableSlot *slot;
/* ----------------
* prepare information for psort_begin()
* get state info from node
* ----------------
*/
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 */
"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)
{
/* 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;
slot->ttc_tupleDescriptor = ExecGetTupType(outerNode);
/* ----------------
* 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;
/* ----------------
* 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);
heapTuple = psort_grabtuple(node);
if (heapTuple == NULL) {
/* psort_end(node); */
return (ExecClearTuple(slot));
}
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;
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;
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);
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);
SortState *sortstate;
Plan *outerPlan;
/* Clean up after psort */
psort_end(node);
SO1_printf("ExecEndSort: %s\n",
"sort node shutdown");
}
/* ----------------
* get info from the sort state
* ----------------
*/
SO1_printf("ExecEndSort: %s\n",
"shutting down sort node");
/* ----------------------------------------------------------------
* ExecSortMarkPos
*
* Calls psort to save the current position in the sorted file.
* ----------------------------------------------------------------
*/
void
ExecSortMarkPos(Sort *node)
{
SortState *sortstate;
sortstate = node->sortstate;
/* ----------------
* if we haven't sorted yet, just return
* ----------------
*/
sortstate = node->sortstate;
if (sortstate->sort_Flag == false)
return;
psort_markpos(node);
/* ----------------
* shut down the subplan
* ----------------
*/
outerPlan = outerPlan((Plan *) node);
ExecEndNode(outerPlan, (Plan *) node);
return;
/* ----------------
* 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");
}
/* ----------------------------------------------------------------
* ExecSortRestrPos
* ExecSortMarkPos
*
* Calls psort to restore the last saved sort file position.
* Calls psort to save the current position in the sorted file.
* ----------------------------------------------------------------
*/
void
ExecSortRestrPos(Sort *node)
ExecSortMarkPos(Sort * node)
{
SortState *sortstate;
SortState *sortstate;
/* ----------------
* if we haven't sorted yet, just return
* ----------------
*/
sortstate = node->sortstate;
if (sortstate->sort_Flag == false)
return;
psort_markpos(node);
/* ----------------
* 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);
}
/* ----------------------------------------------------------------
* ExecSortRestrPos
*
* Calls psort to restore the last saved sort file position.
* ----------------------------------------------------------------
*/
void
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);
}

View File

@ -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;
TeeState *teeState;
Plan *outerPlan;
int len;
Relation bufferRel;
TupleDesc tupType;
EState *estate;
/* ----------------
* assign the node's execution state
* ----------------
*/
/* make a new executor state, because we have a different
es_range_table */
/*
* 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;
/* node->plan.state = estate;*/
/* ----------------
* assign the node's execution state
* ----------------
*/
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;
/*
* make a new executor state, because we have a different
* es_range_table
*/
/* 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;*/
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;
/* ----------------
* 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));
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);
/* ----------------
* initialize tuple slots
* ----------------
*/
ExecInitResultTupleSlot(estate, &(teeState->cstate));
/* ----------------
* 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);
/* initialize child nodes */
outerPlan = outerPlan((Plan *) node);
ExecInitNode(outerPlan, estate, (Plan *) node);
/* bufferRel = ExecCreatR(len, tupType, _TEMP_RELATION_ID_); */
/* ----------------
* 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);
/* 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;
/* ---------------------------------------
initialize temporary relation to buffer tuples
*/
tupType = ExecGetResultType(&(teeState->cstate));
len = ExecTargetListLength(((Plan *) node)->targetlist);
teeState->tee_bufferRelname = pstrdup(node->teeTableName);
/* bufferRel = ExecCreatR(len, tupType, _TEMP_RELATION_ID_); */
/* 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);
/*
* create a catalogued relation even though this is a temporary
* relation
*/
/* cleanup of catalogued relations is easier to do */
if (RelationIsValid(r))
bufferRel = heap_openr(teeState->tee_bufferRelname);
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));
}
{
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;
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 */
/*
* initialize a memory context for allocating thing like scan
* descriptors
*/
teeState->tee_mcxt = (MemoryContext)CreateGlobalMemory(teeState->tee_bufferRelname);
/*
* 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
*/
/* 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;
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 *teeState;
Relation bufferRel;
ScanDirection dir;
MemoryContext orig;
teeState = node->teestate;
if (teeState->tee_leftScanDesc && teeState->tee_rightScanDesc)
return;
teeState = node->teestate;
if (teeState->tee_leftScanDesc && teeState->tee_rightScanDesc)
return;
orig = CurrentMemoryContext;
MemoryContextSwitchTo(teeState->tee_mcxt);
orig = CurrentMemoryContext;
MemoryContextSwitchTo(teeState->tee_mcxt);
bufferRel = teeState->tee_bufferRel;
dir = ((Plan*)node)->state->es_direction; /* backwards not handled yet XXX */
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 */
);
}
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);
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.
*
* 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
* A Tee serves to connect a subplan to multiple parents.
* the subplan is always the outplan of the Tee node.
*
* The Tee gets requests from either leftParent or rightParent,
* fetches the result tuple from the child, and then
* stored the result into a temporary relation (serving as a queue).
* leftPlace and rightPlace keep track of where the left and rightParents
* are.
* If a parent requests a tuple and that parent is not at the end
* of the temporary relation, then the request is satisfied from
* the queue instead of by executing the child plan
*
* ----------------------------------------------------------------
*/
TupleTableSlot*
ExecTee(Tee *node, Plan *parent)
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 *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;
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);
childNode = outerPlan(node);
dir = estate->es_direction;
dir = estate->es_direction;
/* XXX doesn't handle backwards direction yet */
/* 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*/
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;
}
branch = leftPlace;
}
else if ((parent == node->rightParent) || (parent == (Plan *) node))
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);
/*
* 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;
}
scanDesc = (parent == node->leftParent) ?
teeState->tee_leftScanDesc : teeState->tee_rightScanDesc;
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;
{
/* 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);
}
/* insert into temporary relation */
heap_insert(bufferRel, heapTuple);
/* 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;
/*
* once there is data in the temporary relation, ensure that
* the left and right scandescs are initialized
*/
initTeeScanDescs(node);
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;
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);
/*
* move the scandesc forward so we don't re-read this
* tuple later
*/
HeapTuple throwAway;
result = ExecStoreTuple(heapTuple,/* tuple to store */
slot, /* slot to store in */
buffer,/* this tuple's buffer */
false); /* don't free stuff from heap_getnext */
}
/* Buffer buffer; */
throwAway = heap_getnext(scanDesc,
ScanDirectionIsBackward(dir),
/* &buffer */
(Buffer *) NULL);
}
if (parent == node->leftParent)
{
teeState->tee_leftPlace = leftPlace+1;
}
else
{
teeState->tee_rightPlace = rightPlace+1;
}
/*
* 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
*/
return result;
/*
* does this mean this tuple has to be garbage collected
* later??
*/
slot->ttc_shouldFree = false;
teeState->tee_lastPlace = lastPlace + 1;
}
result = slot;
}
else
{ /* the desired data already exists in the
* temporary relation */
scanDesc = (parent == node->leftParent) ?
teeState->tee_leftScanDesc : teeState->tee_rightScanDesc;
heapTuple = heap_getnext(scanDesc,
ScanDirectionIsBackward(dir),
&buffer);
/*
* Increase the pin count on the buffer page, because the tuple
* stored in the slot also points to it (as well as the scan
* descriptor). If we don't, ExecStoreTuple will decrease the pin
* count on the next iteration.
*/
if (buffer != InvalidBuffer)
IncrBufferRefCount(buffer);
slot = teeState->cstate.cs_ResultTupleSlot;
slot->ttc_tupleDescriptor = RelationGetTupleDescriptor(bufferRel);
result = ExecStoreTuple(heapTuple, /* tuple to store */
slot, /* slot to store in */
buffer, /* this tuple's buffer */
false); /* don't free stuff from
* heap_getnext */
}
if (parent == node->leftParent)
{
teeState->tee_leftPlace = leftPlace + 1;
}
else
{
teeState->tee_rightPlace = rightPlace + 1;
}
return result;
}
/* ----------------------------------------------------------------
* ExecTeeReScan(node)
*
* Rescans the relation.
* 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)
{
heap_rescan(teeState->tee_leftScanDesc,
ScanDirectionIsBackward(dir),
NULL);
teeState->tee_leftPlace = 0;
if (teeState->tee_leftScanDesc)
{
heap_rescan(teeState->tee_leftScanDesc,
ScanDirectionIsBackward(dir),
NULL);
teeState->tee_leftPlace = 0;
}
}
else
{
if (teeState->tee_rightScanDesc)
{
heap_rescan(teeState->tee_leftScanDesc,
ScanDirectionIsBackward(dir),
NULL);
teeState->tee_rightPlace = 0;
}
}
}
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 *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;
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->leftParent || parent == node->leftParent)
leftPlace = -1;
if (!node->rightParent || parent == node->rightParent)
rightPlace = -1;
if (!node->rightParent || parent == node->rightParent)
rightPlace = -1;
if (parent == (Plan*)node)
rightPlace = leftPlace = -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 */
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;
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_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;
}
if (teeState->tee_mcxt)
{
MemoryContextSwitchTo(orig);
teeState->tee_mcxt = NULL;
}
}
}
}
}
}

View File

@ -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;
UniqueState *uniquestate;
TupleTableSlot *resultTupleSlot;
TupleTableSlot *slot;
Plan *outerPlan;
char *uniqueAttr;
AttrNumber uniqueAttrNum;
TupleDesc tupDesc;
Oid typoutput;
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 (;;) {
/* ----------------
* 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.
* get information from the node
* ----------------
*/
uniquestate = node->uniquestate;
outerPlan = outerPlan((Plan *) node);
resultTupleSlot = uniquestate->cs_ResultTupleSlot;
uniqueAttr = node->uniqueAttr;
uniqueAttrNum = node->uniqueAttrNum;
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;
if (uniqueAttr)
{
tupDesc = ExecGetResultType(uniquestate);
typoutput = typtoout((Oid) tupDesc->attrs[uniqueAttrNum - 1]->atttypid);
}
else {
if (! ExecIdenticalTuples(slot, resultTupleSlot))
break;
else
{ /* keep compiler quiet */
tupDesc = NULL;
typoutput = 0;
}
}
/* ----------------
* 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;
/* ----------------
* now loop, returning only non-duplicate tuples.
* We assume that the tuples arrive in sorted order
* so we can detect duplicates easily.
* ----------------
*/
for (;;)
{
/* ----------------
* fetch a tuple from the outer subplan
* ----------------
*/
slot = ExecProcNode(outerPlan, (Plan *) node);
if (TupIsNull(slot))
return NULL;
/* ----------------
* we use the result tuple slot to hold our saved tuples.
* if we haven't a saved tuple to compare our new tuple with,
* then we exit the loop. This new tuple as the saved tuple
* the next time we get here.
* ----------------
*/
if (TupIsNull(resultTupleSlot))
break;
/* ----------------
* now test if the new tuple and the previous
* tuple match. If so then we loop back and fetch
* another new tuple from the subplan.
* ----------------
*/
if (uniqueAttr)
{
/*
* to check equality, we check to see if the typoutput of the
* attributes are equal
*/
bool isNull1,
isNull2;
char *attr1,
*attr2;
char *val1,
*val2;
attr1 = heap_getattr(slot->val, InvalidBuffer,
uniqueAttrNum, tupDesc, &isNull1);
attr2 = heap_getattr(resultTupleSlot->val, InvalidBuffer,
uniqueAttrNum, tupDesc, &isNull2);
if (isNull1 == isNull2)
{
if (isNull1) /* both are null, they are equal */
continue;
val1 = fmgr(typoutput, attr1, gettypelem(tupDesc->attrs[uniqueAttrNum - 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;
}
/* ----------------------------------------------------------------
* 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;
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);
/* ----------------
* 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;
/* ------------
* Tuple table initialization
* ------------
*/
ExecInitResultTupleSlot(estate, uniquestate);
if (uniqueAttr) {
TupleDesc tupDesc;
int i = 0;
/* ----------------
* then initialize outer plan
* ----------------
*/
outerPlan = outerPlan((Plan *) node);
ExecInitNode(outerPlan, estate, (Plan *) node);
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;
/* ----------------
* unique nodes do no projections, so initialize
* projection info for this node appropriately
* ----------------
*/
ExecAssignResultTypeFromOuterPlan((Plan *) node, uniquestate);
uniquestate->cs_ProjInfo = NULL;
/* ----------------
* all done.
* ----------------
*/
return TRUE;
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);
}

File diff suppressed because it is too large Load Diff