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:
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
@ -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
@ -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
@ -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
@ -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
|
||||
*/
|
||||
}
|
||||
|
||||
|
@ -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
@ -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
@ -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");
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 */
|
||||
¤tRelation, /* 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 */
|
||||
¤tRelation, /* return: rel desc */
|
||||
(Pointer *) ¤tScanDesc); /* 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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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
Reference in New Issue
Block a user