mirror of
https://github.com/postgres/postgres.git
synced 2025-06-30 21:42:05 +03:00
Postgres95 1.01 Distribution - Virgin Sources
This commit is contained in:
29
src/backend/executor/Makefile.inc
Normal file
29
src/backend/executor/Makefile.inc
Normal file
@ -0,0 +1,29 @@
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# Makefile.inc--
|
||||
# Makefile for the executor module
|
||||
#
|
||||
# Copyright (c) 1994, Regents of the University of California
|
||||
#
|
||||
#
|
||||
# IDENTIFICATION
|
||||
# $Header: /cvsroot/pgsql/src/backend/executor/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:21:24 scrappy Exp $
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
VPATH:= $(VPATH):$(CURDIR)/executor
|
||||
|
||||
SRCS_EXECUTOR= execAmi.c execFlatten.c execJunk.c execMain.c \
|
||||
execProcnode.c execQual.c execScan.c execTuples.c \
|
||||
execUtils.c functions.c nodeAppend.c nodeAgg.c nodeHash.c \
|
||||
nodeHashjoin.c nodeIndexscan.c nodeMaterial.c nodeMergejoin.c \
|
||||
nodeNestloop.c nodeResult.c nodeSeqscan.c nodeSort.c \
|
||||
nodeUnique.c nodeTee.c nodeGroup.c
|
||||
|
||||
HEADERS+= execFlatten.h execdebug.h execdefs.h execdesc.h \
|
||||
executor.h functions.h hashjoin.h nodeAgg.h nodeAppend.h \
|
||||
nodeHash.h nodeHashjoin.h nodeIndexscan.h nodeMaterial.h \
|
||||
nodeMergejoin.h nodeNestloop.h nodeResult.h \
|
||||
nodeSeqscan.h nodeSort.h nodeUnique.h tuptable.h nodeTee.h \
|
||||
nodeGroup.h
|
||||
|
439
src/backend/executor/execAmi.c
Normal file
439
src/backend/executor/execAmi.c
Normal file
@ -0,0 +1,439 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* execAmi.c--
|
||||
* miscellanious executor access method routines
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/execAmi.c,v 1.1.1.1 1996/07/09 06:21:24 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
/*
|
||||
* INTERFACE ROUTINES
|
||||
*
|
||||
* ExecOpenScanR \ / amopen
|
||||
* ExecBeginScan \ / ambeginscan
|
||||
* ExecCloseR \ / amclose
|
||||
* ExecInsert \ executor interface / aminsert
|
||||
* ExecReScanNode / to access methods \ amrescan
|
||||
* ExecReScanR / \ amrescan
|
||||
* ExecMarkPos / \ ammarkpos
|
||||
* ExecRestrPos / \ amrestpos
|
||||
*
|
||||
* ExecCreatR function to create temporary relations
|
||||
*
|
||||
*/
|
||||
#include <stdio.h> /* for sprintf() */
|
||||
#include "executor/executor.h"
|
||||
#include "storage/smgr.h"
|
||||
#include "executor/nodeSeqscan.h"
|
||||
#include "executor/nodeIndexscan.h"
|
||||
#include "executor/nodeSort.h"
|
||||
#include "executor/nodeTee.h"
|
||||
#include "optimizer/internal.h" /* for _TEMP_RELATION_ID_ */
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecOpenScanR
|
||||
*
|
||||
* old comments:
|
||||
* Parameters:
|
||||
* relation -- relation to be opened and scanned.
|
||||
* nkeys -- number of keys
|
||||
* skeys -- keys to restrict scanning
|
||||
* isindex -- if this is true, the relation is the relid of
|
||||
* an index relation, else it is an index into the
|
||||
* range table.
|
||||
* Returns the relation as(relDesc scanDesc)
|
||||
* If this structure is changed, need to modify the access macros
|
||||
* defined in execInt.h.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
void
|
||||
ExecOpenScanR(Oid relOid,
|
||||
int nkeys,
|
||||
ScanKey skeys,
|
||||
bool isindex,
|
||||
ScanDirection dir,
|
||||
TimeQual timeRange,
|
||||
Relation *returnRelation, /* return */
|
||||
Pointer *returnScanDesc) /* return */
|
||||
{
|
||||
Relation relation;
|
||||
Pointer scanDesc;
|
||||
|
||||
/* ----------------
|
||||
* note: scanDesc returned by ExecBeginScan can be either
|
||||
* a HeapScanDesc or an IndexScanDesc so for now we
|
||||
* make it a Pointer. There should be a better scan
|
||||
* abstraction someday -cim 9/9/89
|
||||
* ----------------
|
||||
*/
|
||||
relation = ExecOpenR(relOid, isindex);
|
||||
scanDesc = ExecBeginScan(relation,
|
||||
nkeys,
|
||||
skeys,
|
||||
isindex,
|
||||
dir,
|
||||
timeRange);
|
||||
|
||||
if (returnRelation != NULL)
|
||||
*returnRelation = relation;
|
||||
if (scanDesc != NULL)
|
||||
*returnScanDesc = scanDesc;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecOpenR
|
||||
*
|
||||
* returns a relation descriptor given an object id.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
Relation
|
||||
ExecOpenR(Oid relationOid, bool isindex)
|
||||
{
|
||||
Relation relation;
|
||||
relation = (Relation) NULL;
|
||||
|
||||
/* ----------------
|
||||
* open the relation with the correct call depending
|
||||
* on whether this is a heap relation or an index relation.
|
||||
* ----------------
|
||||
*/
|
||||
if (isindex) {
|
||||
relation = index_open(relationOid);
|
||||
} else
|
||||
relation = heap_open(relationOid);
|
||||
|
||||
if (relation == NULL)
|
||||
elog(DEBUG, "ExecOpenR: relation == NULL, heap_open failed.");
|
||||
|
||||
return relation;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecBeginScan
|
||||
*
|
||||
* beginscans a relation in current direction.
|
||||
*
|
||||
* XXX fix parameters to AMbeginscan (and btbeginscan)
|
||||
* currently we need to pass a flag stating whether
|
||||
* or not the scan should begin at an endpoint of
|
||||
* the relation.. Right now we always pass false
|
||||
* -cim 9/14/89
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
Pointer
|
||||
ExecBeginScan(Relation relation,
|
||||
int nkeys,
|
||||
ScanKey skeys,
|
||||
bool isindex,
|
||||
ScanDirection dir,
|
||||
TimeQual time_range)
|
||||
{
|
||||
Pointer scanDesc;
|
||||
|
||||
scanDesc = NULL;
|
||||
|
||||
/* ----------------
|
||||
* open the appropriate type of scan.
|
||||
*
|
||||
* Note: ambeginscan()'s second arg is a boolean indicating
|
||||
* that the scan should be done in reverse.. That is,
|
||||
* if you pass it true, then the scan is backward.
|
||||
* ----------------
|
||||
*/
|
||||
if (isindex) {
|
||||
scanDesc = (Pointer) index_beginscan(relation,
|
||||
false, /* see above comment */
|
||||
nkeys,
|
||||
skeys);
|
||||
} else {
|
||||
scanDesc = (Pointer) heap_beginscan(relation,
|
||||
ScanDirectionIsBackward(dir),
|
||||
time_range,
|
||||
nkeys,
|
||||
skeys);
|
||||
}
|
||||
|
||||
if (scanDesc == NULL)
|
||||
elog(DEBUG, "ExecBeginScan: scanDesc = NULL, heap_beginscan failed.");
|
||||
|
||||
|
||||
return scanDesc;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecCloseR
|
||||
*
|
||||
* closes the relation and scan descriptor for a scan or sort
|
||||
* node. Also closes index relations and scans for index scans.
|
||||
*
|
||||
* old comments
|
||||
* closes the relation indicated in 'relID'
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
void
|
||||
ExecCloseR(Plan *node)
|
||||
{
|
||||
CommonScanState *state;
|
||||
Relation relation;
|
||||
HeapScanDesc scanDesc;
|
||||
|
||||
/* ----------------
|
||||
* shut down the heap scan and close the heap relation
|
||||
* ----------------
|
||||
*/
|
||||
switch (nodeTag(node)) {
|
||||
|
||||
case T_SeqScan:
|
||||
state = ((SeqScan *)node)->scanstate;
|
||||
break;
|
||||
|
||||
case T_IndexScan:
|
||||
state = ((IndexScan *)node)->scan.scanstate;
|
||||
break;
|
||||
|
||||
case T_Material:
|
||||
state = &(((Material *)node)->matstate->csstate);
|
||||
break;
|
||||
|
||||
case T_Sort:
|
||||
state = &(((Sort *)node)->sortstate->csstate);
|
||||
break;
|
||||
|
||||
case T_Agg:
|
||||
state = &(((Agg *)node)->aggstate->csstate);
|
||||
break;
|
||||
|
||||
default:
|
||||
elog(DEBUG, "ExecCloseR: not a scan, material, or sort node!");
|
||||
return;
|
||||
}
|
||||
|
||||
relation = state->css_currentRelation;
|
||||
scanDesc = state->css_currentScanDesc;
|
||||
|
||||
if (scanDesc != NULL)
|
||||
heap_endscan(scanDesc);
|
||||
|
||||
if (relation != NULL)
|
||||
heap_close(relation);
|
||||
|
||||
/* ----------------
|
||||
* if this is an index scan then we have to take care
|
||||
* of the index relations as well..
|
||||
* ----------------
|
||||
*/
|
||||
if (nodeTag(node) == T_IndexScan) {
|
||||
IndexScan *iscan= (IndexScan *)node;
|
||||
IndexScanState *indexstate;
|
||||
int numIndices;
|
||||
RelationPtr indexRelationDescs;
|
||||
IndexScanDescPtr indexScanDescs;
|
||||
int i;
|
||||
|
||||
indexstate = iscan->indxstate;
|
||||
numIndices = indexstate->iss_NumIndices;
|
||||
indexRelationDescs = indexstate->iss_RelationDescs;
|
||||
indexScanDescs = indexstate->iss_ScanDescs;
|
||||
|
||||
for (i = 0; i<numIndices; i++) {
|
||||
/* ----------------
|
||||
* shut down each of the scans and
|
||||
* close each of the index relations
|
||||
* ----------------
|
||||
*/
|
||||
if (indexScanDescs[i] != NULL)
|
||||
index_endscan(indexScanDescs[i]);
|
||||
|
||||
if (indexRelationDescs[i] != NULL)
|
||||
index_close(indexRelationDescs[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecReScan
|
||||
*
|
||||
* XXX this should be extended to cope with all the node types..
|
||||
*
|
||||
* takes the new expression context as an argument, so that
|
||||
* index scans needn't have their scan keys updated separately
|
||||
* - marcel 09/20/94
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
void
|
||||
ExecReScan(Plan *node, ExprContext *exprCtxt, Plan *parent)
|
||||
{
|
||||
switch(nodeTag(node)) {
|
||||
case T_SeqScan:
|
||||
ExecSeqReScan((SeqScan *) node, exprCtxt, parent);
|
||||
return;
|
||||
|
||||
case T_IndexScan:
|
||||
ExecIndexReScan((IndexScan *) node, exprCtxt, parent);
|
||||
return;
|
||||
|
||||
case T_Material:
|
||||
/* the first call to ExecReScan should have no effect because
|
||||
* everything is initialized properly already. the following
|
||||
* calls will be handled by ExecSeqReScan() because the nodes
|
||||
* below the Material node have already been materialized into
|
||||
* a temp relation.
|
||||
*/
|
||||
return;
|
||||
|
||||
case T_Tee:
|
||||
ExecTeeReScan((Tee*) node, exprCtxt, parent);
|
||||
break;
|
||||
|
||||
default:
|
||||
elog(WARN, "ExecReScan: not a seqscan or indexscan node.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecReScanR
|
||||
*
|
||||
* XXX this does not do the right thing with indices yet.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
HeapScanDesc
|
||||
ExecReScanR(Relation relDesc, /* LLL relDesc unused */
|
||||
HeapScanDesc scanDesc,
|
||||
ScanDirection direction,
|
||||
int nkeys, /* LLL nkeys unused */
|
||||
ScanKey skeys)
|
||||
{
|
||||
if (scanDesc != NULL)
|
||||
heap_rescan(scanDesc, /* scan desc */
|
||||
ScanDirectionIsBackward(direction), /* backward flag */
|
||||
skeys); /* scan keys */
|
||||
|
||||
return scanDesc;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecMarkPos
|
||||
*
|
||||
* Marks the current scan position.
|
||||
*
|
||||
* XXX Needs to be extended to include all the node types.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
void
|
||||
ExecMarkPos(Plan *node)
|
||||
{
|
||||
switch(nodeTag(node)) {
|
||||
case T_SeqScan:
|
||||
ExecSeqMarkPos((SeqScan *) node);
|
||||
break;
|
||||
|
||||
case T_IndexScan:
|
||||
ExecIndexMarkPos((IndexScan *) node);
|
||||
break;
|
||||
|
||||
case T_Sort:
|
||||
ExecSortMarkPos((Sort *) node);
|
||||
break;
|
||||
|
||||
default:
|
||||
/* elog(DEBUG, "ExecMarkPos: unsupported node type"); */
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecRestrPos
|
||||
*
|
||||
* restores the scan position previously saved with ExecMarkPos()
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
void
|
||||
ExecRestrPos(Plan *node)
|
||||
{
|
||||
switch(nodeTag(node)) {
|
||||
case T_SeqScan:
|
||||
ExecSeqRestrPos((SeqScan *) node);
|
||||
return;
|
||||
|
||||
case T_IndexScan:
|
||||
ExecIndexRestrPos((IndexScan *) node);
|
||||
return;
|
||||
|
||||
case T_Sort:
|
||||
ExecSortRestrPos((Sort *) node);
|
||||
return;
|
||||
|
||||
default:
|
||||
/* elog(DEBUG, "ExecRestrPos: node type not supported"); */
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecCreatR
|
||||
*
|
||||
* old comments
|
||||
* Creates a relation.
|
||||
*
|
||||
* Parameters:
|
||||
* attrType -- type information on the attributes.
|
||||
* accessMtd -- access methods used to access the created relation.
|
||||
* relation -- optional. Either an index to the range table or
|
||||
* negative number indicating a temporary relation.
|
||||
* A temporary relation is assume is this field is absent.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
|
||||
Relation
|
||||
ExecCreatR(TupleDesc tupType,
|
||||
Oid relationOid)
|
||||
{
|
||||
Relation relDesc;
|
||||
|
||||
EU4_printf("ExecCreatR: %s numatts=%d type=%d oid=%d\n",
|
||||
"entering: ", numberAttributes, tupType, relationOid);
|
||||
CXT1_printf("ExecCreatR: context is %d\n", CurrentMemoryContext);
|
||||
|
||||
relDesc = NULL;
|
||||
|
||||
if (relationOid == _TEMP_RELATION_ID_ ) {
|
||||
/* ----------------
|
||||
* create a temporary relation
|
||||
* (currently the planner always puts a _TEMP_RELATION_ID
|
||||
* in the relation argument so we expect this to be the case although
|
||||
* it's possible that someday we'll get the name from
|
||||
* from the range table.. -cim 10/12/89)
|
||||
* ----------------
|
||||
*/
|
||||
/*
|
||||
sprintf(tempname, "temp_%d.%d", getpid(), tmpcnt++);
|
||||
EU1_printf("ExecCreatR: attempting to create %s\n", tempname);
|
||||
*/
|
||||
/* heap_creatr creates a name if the argument to heap_creatr is '\0 ' */
|
||||
relDesc = heap_creatr("",
|
||||
DEFAULT_SMGR,
|
||||
tupType);
|
||||
} else {
|
||||
/* ----------------
|
||||
* use a relation from the range table
|
||||
* ----------------
|
||||
*/
|
||||
elog(DEBUG, "ExecCreatR: %s",
|
||||
"stuff using range table id's is not functional");
|
||||
}
|
||||
|
||||
if (relDesc == NULL)
|
||||
elog(DEBUG, "ExecCreatR: failed to create relation.");
|
||||
|
||||
EU1_printf("ExecCreatR: returning relDesc=%d\n", relDesc);
|
||||
|
||||
return relDesc;
|
||||
}
|
||||
|
236
src/backend/executor/execFlatten.c
Normal file
236
src/backend/executor/execFlatten.c
Normal file
@ -0,0 +1,236 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* execFlatten.c--
|
||||
* This file handles the nodes associated with flattening sets in the
|
||||
* target list of queries containing functions returning sets.
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/Attic/execFlatten.c,v 1.1.1.1 1996/07/09 06:21:24 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/*
|
||||
* ExecEvalIter() -
|
||||
* Iterate through the all return tuples/base types from a function one
|
||||
* at time (i.e. one per ExecEvalIter call). Not really needed for
|
||||
* postquel functions, but for reasons of orthogonality, these nodes
|
||||
* exist above pq functions as well as c functions.
|
||||
*
|
||||
* ExecEvalFjoin() -
|
||||
* Given N Iter nodes return a vector of all combinations of results
|
||||
* one at a time (i.e. one result vector per ExecEvalFjoin call). This
|
||||
* node does the actual flattening work.
|
||||
*/
|
||||
#include "postgres.h"
|
||||
#include "nodes/primnodes.h"
|
||||
#include "nodes/relation.h"
|
||||
#include "nodes/execnodes.h"
|
||||
#include "executor/executor.h"
|
||||
#include "executor/execFlatten.h"
|
||||
|
||||
Datum
|
||||
ExecEvalIter(Iter *iterNode,
|
||||
ExprContext *econtext,
|
||||
bool *resultIsNull,
|
||||
bool *iterIsDone)
|
||||
{
|
||||
Node *expression;
|
||||
|
||||
expression = iterNode->iterexpr;
|
||||
|
||||
/*
|
||||
* Really Iter nodes are only needed for C functions, postquel function
|
||||
* by their nature return 1 result at a time. For now we are only worrying
|
||||
* about postquel functions, c functions will come later.
|
||||
*/
|
||||
return ExecEvalExpr(expression, econtext, resultIsNull, iterIsDone);
|
||||
}
|
||||
|
||||
void
|
||||
ExecEvalFjoin(TargetEntry *tlist,
|
||||
ExprContext *econtext,
|
||||
bool *isNullVect,
|
||||
bool *fj_isDone)
|
||||
{
|
||||
|
||||
#ifdef SETS_FIXED
|
||||
bool isDone;
|
||||
int curNode;
|
||||
List *tlistP;
|
||||
|
||||
Fjoin *fjNode = tlist->fjoin;
|
||||
DatumPtr resVect = fjNode->fj_results;
|
||||
BoolPtr alwaysDone = fjNode->fj_alwaysDone;
|
||||
|
||||
if (fj_isDone) *fj_isDone = false;
|
||||
/*
|
||||
* For the next tuple produced by the plan, we need to re-initialize
|
||||
* the Fjoin node.
|
||||
*/
|
||||
if (!fjNode->fj_initialized)
|
||||
{
|
||||
/*
|
||||
* Initialize all of the Outer nodes
|
||||
*/
|
||||
curNode = 1;
|
||||
foreach(tlistP, lnext(tlist))
|
||||
{
|
||||
TargetEntry *tle = lfirst(tlistP);
|
||||
|
||||
resVect[curNode] = ExecEvalIter((Iter*)tle->expr,
|
||||
econtext,
|
||||
&isNullVect[curNode],
|
||||
&isDone);
|
||||
if (isDone)
|
||||
isNullVect[curNode] = alwaysDone[curNode] = true;
|
||||
else
|
||||
alwaysDone[curNode] = false;
|
||||
|
||||
curNode++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize the inner node
|
||||
*/
|
||||
resVect[0] = ExecEvalIter((Iter*)fjNode->fj_innerNode->expr,
|
||||
econtext,
|
||||
&isNullVect[0],
|
||||
&isDone);
|
||||
if (isDone)
|
||||
isNullVect[0] = alwaysDone[0] = true;
|
||||
else
|
||||
alwaysDone[0] = false;
|
||||
|
||||
/*
|
||||
* Mark the Fjoin as initialized now.
|
||||
*/
|
||||
fjNode->fj_initialized = TRUE;
|
||||
|
||||
/*
|
||||
* If the inner node is always done, then we are done for now
|
||||
*/
|
||||
if (isDone)
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* If we're already initialized, all we need to do is get the
|
||||
* next inner result and pair it up with the existing outer node
|
||||
* result vector. Watch out for the degenerate case, where the
|
||||
* inner node never returns results.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Fill in nulls for every function that is always done.
|
||||
*/
|
||||
for (curNode=fjNode->fj_nNodes-1; curNode >= 0; curNode--)
|
||||
isNullVect[curNode] = alwaysDone[curNode];
|
||||
|
||||
if (alwaysDone[0] == true)
|
||||
{
|
||||
*fj_isDone = FjoinBumpOuterNodes(tlist,
|
||||
econtext,
|
||||
resVect,
|
||||
isNullVect);
|
||||
return;
|
||||
}
|
||||
else
|
||||
resVect[0] = ExecEvalIter((Iter*)fjNode->fj_innerNode->expr,
|
||||
econtext,
|
||||
&isNullVect[0],
|
||||
&isDone);
|
||||
}
|
||||
|
||||
/*
|
||||
* if the inner node is done
|
||||
*/
|
||||
if (isDone)
|
||||
{
|
||||
*fj_isDone = FjoinBumpOuterNodes(tlist,
|
||||
econtext,
|
||||
resVect,
|
||||
isNullVect);
|
||||
if (*fj_isDone)
|
||||
return;
|
||||
|
||||
resVect[0] = ExecEvalIter((Iter*)fjNode->fj_innerNode->expr,
|
||||
econtext,
|
||||
&isNullVect[0],
|
||||
&isDone);
|
||||
|
||||
}
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
bool
|
||||
FjoinBumpOuterNodes(TargetEntry *tlist,
|
||||
ExprContext *econtext,
|
||||
DatumPtr results,
|
||||
char *nulls)
|
||||
{
|
||||
#ifdef SETS_FIXED
|
||||
bool funcIsDone = true;
|
||||
Fjoin *fjNode = tlist->fjoin;
|
||||
char *alwaysDone = fjNode->fj_alwaysDone;
|
||||
List *outerList = lnext(tlist);
|
||||
List *trailers = lnext(tlist);
|
||||
int trailNode = 1;
|
||||
int curNode = 1;
|
||||
|
||||
/*
|
||||
* Run through list of functions until we get to one that isn't yet
|
||||
* done returning values. Watch out for funcs that are always done.
|
||||
*/
|
||||
while ((funcIsDone == true) && (outerList != NIL))
|
||||
{
|
||||
TargetEntry *tle = lfirst(outerList);
|
||||
|
||||
if (alwaysDone[curNode] == true)
|
||||
nulls[curNode] = 'n';
|
||||
else
|
||||
results[curNode] = ExecEvalIter((Iter)tle->expr,
|
||||
econtext,
|
||||
&nulls[curNode],
|
||||
&funcIsDone);
|
||||
curNode++;
|
||||
outerList = lnext(outerList);
|
||||
}
|
||||
|
||||
/*
|
||||
* If every function is done, then we are done flattening.
|
||||
* Mark the Fjoin node unitialized, it is time to get the
|
||||
* next tuple from the plan and redo all of the flattening.
|
||||
*/
|
||||
if (funcIsDone)
|
||||
{
|
||||
set_fj_initialized(fjNode, false);
|
||||
return (true);
|
||||
}
|
||||
|
||||
/*
|
||||
* We found a function that wasn't done. Now re-run every function
|
||||
* before it. As usual watch out for functions that are always done.
|
||||
*/
|
||||
trailNode = 1;
|
||||
while (trailNode != curNode-1)
|
||||
{
|
||||
TargetEntry *tle = lfirst(trailers);
|
||||
|
||||
if (alwaysDone[trailNode] != true)
|
||||
results[trailNode] = ExecEvalIter((Iter)tle->expr,
|
||||
econtext,
|
||||
&nulls[trailNode],
|
||||
&funcIsDone);
|
||||
trailNode++;
|
||||
trailers = lnext(trailers);
|
||||
}
|
||||
return false;
|
||||
#endif
|
||||
return false;
|
||||
}
|
26
src/backend/executor/execFlatten.h
Normal file
26
src/backend/executor/execFlatten.h
Normal file
@ -0,0 +1,26 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* execFlatten.h--
|
||||
* prototypes for execFlatten.c.
|
||||
*
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: execFlatten.h,v 1.1.1.1 1996/07/09 06:21:24 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef EXECFLATTEN_H
|
||||
#define EXECFLATTEN_H
|
||||
|
||||
extern Datum ExecEvalIter(Iter *iterNode, ExprContext *econtext, bool *resultIsNull, bool *iterIsDone);
|
||||
|
||||
extern void ExecEvalFjoin(TargetEntry *tlist, ExprContext *econtext, bool *isNullVect, bool *fj_isDone);
|
||||
|
||||
extern bool FjoinBumpOuterNodes(TargetEntry *tlist, ExprContext *econtext, DatumPtr results, char *nulls);
|
||||
|
||||
|
||||
#endif /* EXECFLATTEN_H */
|
||||
|
||||
|
||||
|
389
src/backend/executor/execJunk.c
Normal file
389
src/backend/executor/execJunk.c
Normal file
@ -0,0 +1,389 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* junk.c--
|
||||
* Junk attribute support stuff....
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/execJunk.c,v 1.1.1.1 1996/07/09 06:21:24 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include "utils/palloc.h"
|
||||
#include "executor/executor.h"
|
||||
#include "nodes/relation.h"
|
||||
#include "optimizer/tlist.h" /* for MakeTLE */
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* XXX this stuff should be rewritten to take advantage
|
||||
* of ExecProject() and the ProjectionInfo node.
|
||||
* -cim 6/3/91
|
||||
*
|
||||
* An attribute of a tuple living inside the executor, can be
|
||||
* either a normal attribute or a "junk" attribute. "junk" attributes
|
||||
* never make it out of the executor, i.e. they are never printed,
|
||||
* returned or stored in disk. Their only purpose in life is to
|
||||
* store some information useful only to the executor, mainly the values
|
||||
* of some system attributes like "ctid" or rule locks.
|
||||
*
|
||||
* The general idea is the following: A target list consists of a list of
|
||||
* Resdom nodes & expression pairs. Each Resdom node has an attribute
|
||||
* called 'resjunk'. If the value of this attribute is 1 then the
|
||||
* corresponding attribute is a "junk" attribute.
|
||||
*
|
||||
* When we initialize a plan we call 'ExecInitJunkFilter' to create
|
||||
* and store the appropriate information in the 'es_junkFilter' attribute of
|
||||
* EState.
|
||||
*
|
||||
* We then execute the plan ignoring the "resjunk" attributes.
|
||||
*
|
||||
* Finally, when at the top level we get back a tuple, we can call
|
||||
* 'ExecGetJunkAttribute' to retrieve the value of the junk attributes we
|
||||
* are interested in, and 'ExecRemoveJunk' to remove all the junk attributes
|
||||
* from a tuple. This new "clean" tuple is then printed, replaced, deleted
|
||||
* or inserted.
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* ExecInitJunkFilter
|
||||
*
|
||||
* Initialize the Junk filter.
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
JunkFilter *
|
||||
ExecInitJunkFilter(List *targetList)
|
||||
{
|
||||
JunkFilter *junkfilter;
|
||||
List *cleanTargetList;
|
||||
int len, cleanLength;
|
||||
TupleDesc tupType, cleanTupType;
|
||||
List *t;
|
||||
TargetEntry *tle;
|
||||
Resdom *resdom, *cleanResdom;
|
||||
int resjunk;
|
||||
AttrNumber cleanResno;
|
||||
AttrNumber *cleanMap;
|
||||
Size size;
|
||||
Node *expr;
|
||||
|
||||
/* ---------------------
|
||||
* First find the "clean" target list, i.e. all the entries
|
||||
* in the original target list which have a zero 'resjunk'
|
||||
* NOTE: make copy of the Resdom nodes, because we have
|
||||
* to change the 'resno's...
|
||||
* ---------------------
|
||||
*/
|
||||
cleanTargetList = NIL;
|
||||
cleanResno = 1;
|
||||
|
||||
foreach (t, targetList) {
|
||||
TargetEntry *rtarget = lfirst(t);
|
||||
if (rtarget->resdom != NULL) {
|
||||
resdom = rtarget->resdom;
|
||||
expr = rtarget->expr;
|
||||
resjunk = resdom->resjunk;
|
||||
if (resjunk == 0) {
|
||||
/*
|
||||
* make a copy of the resdom node, changing its resno.
|
||||
*/
|
||||
cleanResdom = (Resdom *) copyObject(resdom);
|
||||
cleanResdom->resno = cleanResno;
|
||||
cleanResno ++;
|
||||
/*
|
||||
* create a new target list entry
|
||||
*/
|
||||
tle = makeNode(TargetEntry);
|
||||
tle->resdom = cleanResdom;
|
||||
tle->expr = expr;
|
||||
cleanTargetList = lappend(cleanTargetList, tle);
|
||||
}
|
||||
}
|
||||
else {
|
||||
#ifdef SETS_FIXED
|
||||
List *fjListP;
|
||||
Fjoin *cleanFjoin;
|
||||
List *cleanFjList;
|
||||
List *fjList = lfirst(t);
|
||||
Fjoin *fjNode = (Fjoin *)tl_node(fjList);
|
||||
|
||||
cleanFjoin = (Fjoin)copyObject((Node) fjNode);
|
||||
cleanFjList = lcons(cleanFjoin, NIL);
|
||||
|
||||
resdom = (Resdom) lfirst(get_fj_innerNode(fjNode));
|
||||
expr = lsecond(get_fj_innerNode(fjNode));
|
||||
cleanResdom = (Resdom) copyObject((Node) resdom);
|
||||
set_resno(cleanResdom, cleanResno);
|
||||
cleanResno++;
|
||||
tle = (List) MakeTLE(cleanResdom, (Expr) expr);
|
||||
set_fj_innerNode(cleanFjoin, tle);
|
||||
|
||||
foreach(fjListP, lnext(fjList)) {
|
||||
TargetEntry *tle = lfirst(fjListP);
|
||||
|
||||
resdom = tle->resdom;
|
||||
expr = tle->expr;
|
||||
cleanResdom = (Resdom*) copyObject((Node) resdom);
|
||||
cleanResno++;
|
||||
cleanResdom->Resno = cleanResno;
|
||||
/*
|
||||
* create a new target list entry
|
||||
*/
|
||||
tle = (List) MakeTLE(cleanResdom, (Expr) expr);
|
||||
cleanFjList = lappend(cleanFjList, tle);
|
||||
}
|
||||
lappend(cleanTargetList, cleanFjList);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------------------
|
||||
* Now calculate the tuple types for the original and the clean tuple
|
||||
*
|
||||
* XXX ExecTypeFromTL should be used sparingly. Don't we already
|
||||
* have the tupType corresponding to the targetlist we are passed?
|
||||
* -cim 5/31/91
|
||||
* ---------------------
|
||||
*/
|
||||
tupType = (TupleDesc) ExecTypeFromTL(targetList);
|
||||
cleanTupType = (TupleDesc) ExecTypeFromTL(cleanTargetList);
|
||||
|
||||
len = ExecTargetListLength(targetList);
|
||||
cleanLength = ExecTargetListLength(cleanTargetList);
|
||||
|
||||
/* ---------------------
|
||||
* Now calculate the "map" between the original tuples attributes
|
||||
* and the "clean" tuple's attributes.
|
||||
*
|
||||
* The "map" is an array of "cleanLength" attribute numbers, i.e.
|
||||
* one entry for every attribute of the "clean" tuple.
|
||||
* The value of this entry is the attribute number of the corresponding
|
||||
* attribute of the "original" tuple.
|
||||
* ---------------------
|
||||
*/
|
||||
if (cleanLength > 0) {
|
||||
size = cleanLength * sizeof(AttrNumber);
|
||||
cleanMap = (AttrNumber*) palloc(size);
|
||||
cleanResno = 1;
|
||||
foreach (t, targetList) {
|
||||
TargetEntry *tle = lfirst(t);
|
||||
if (tle->resdom != NULL) {
|
||||
resdom = tle->resdom;
|
||||
expr = tle->expr;
|
||||
resjunk = resdom->resjunk;
|
||||
if (resjunk == 0) {
|
||||
cleanMap[cleanResno-1] = resdom->resno;
|
||||
cleanResno ++;
|
||||
}
|
||||
} else {
|
||||
#ifdef SETS_FIXED
|
||||
List fjListP;
|
||||
List fjList = lfirst(t);
|
||||
Fjoin fjNode = (Fjoin)lfirst(fjList);
|
||||
|
||||
/* what the hell is this????? */
|
||||
resdom = (Resdom) lfirst(get_fj_innerNode(fjNode));
|
||||
#endif
|
||||
|
||||
cleanMap[cleanResno-1] = tle->resdom->resno;
|
||||
cleanResno++;
|
||||
|
||||
#ifdef SETS_FIXED
|
||||
foreach(fjListP, lnext(fjList)) {
|
||||
TargetEntry *tle = lfirst(fjListP);
|
||||
|
||||
resdom = tle->resdom;
|
||||
cleanMap[cleanResno-1] = resdom->resno;
|
||||
cleanResno++;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
} else {
|
||||
cleanMap = NULL;
|
||||
}
|
||||
|
||||
/* ---------------------
|
||||
* Finally create and initialize the JunkFilter.
|
||||
* ---------------------
|
||||
*/
|
||||
junkfilter = makeNode(JunkFilter);
|
||||
|
||||
junkfilter->jf_targetList = targetList;
|
||||
junkfilter->jf_length = len;
|
||||
junkfilter->jf_tupType = tupType;
|
||||
junkfilter->jf_cleanTargetList = cleanTargetList;
|
||||
junkfilter->jf_cleanLength = cleanLength;
|
||||
junkfilter->jf_cleanTupType = cleanTupType;
|
||||
junkfilter->jf_cleanMap = cleanMap;
|
||||
|
||||
return(junkfilter);
|
||||
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* ExecGetJunkAttribute
|
||||
*
|
||||
* Given a tuple (slot), the junk filter and a junk attribute's name,
|
||||
* extract & return the value of this attribute.
|
||||
*
|
||||
* It returns false iff no junk attribute with such name was found.
|
||||
*
|
||||
* NOTE: isNull might be NULL !
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
bool
|
||||
ExecGetJunkAttribute(JunkFilter *junkfilter,
|
||||
TupleTableSlot *slot,
|
||||
char *attrName,
|
||||
Datum *value,
|
||||
bool *isNull)
|
||||
{
|
||||
List *targetList;
|
||||
List *t;
|
||||
Resdom *resdom;
|
||||
AttrNumber resno;
|
||||
char *resname;
|
||||
int resjunk;
|
||||
TupleDesc tupType;
|
||||
HeapTuple tuple;
|
||||
|
||||
/* ---------------------
|
||||
* first look in the junkfilter's target list for
|
||||
* an attribute with the given name
|
||||
* ---------------------
|
||||
*/
|
||||
resno = InvalidAttrNumber;
|
||||
targetList = junkfilter->jf_targetList;
|
||||
|
||||
foreach (t, targetList) {
|
||||
TargetEntry *tle = lfirst(t);
|
||||
resdom = tle->resdom;
|
||||
resname = resdom->resname;
|
||||
resjunk = resdom->resjunk;
|
||||
if (resjunk != 0 && (strcmp(resname, attrName) == 0)) {
|
||||
/* We found it ! */
|
||||
resno = resdom->resno;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (resno == InvalidAttrNumber) {
|
||||
/* Ooops! We couldn't find this attribute... */
|
||||
return(false);
|
||||
}
|
||||
|
||||
/* ---------------------
|
||||
* Now extract the attribute value from the tuple.
|
||||
* ---------------------
|
||||
*/
|
||||
tuple = slot->val;
|
||||
tupType = (TupleDesc) junkfilter->jf_tupType;
|
||||
|
||||
*value = (Datum)
|
||||
heap_getattr(tuple, InvalidBuffer, resno, tupType, isNull);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* ExecRemoveJunk
|
||||
*
|
||||
* Construct and return a tuple with all the junk attributes removed.
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
HeapTuple
|
||||
ExecRemoveJunk(JunkFilter *junkfilter, TupleTableSlot *slot)
|
||||
{
|
||||
HeapTuple tuple;
|
||||
HeapTuple cleanTuple;
|
||||
AttrNumber *cleanMap;
|
||||
TupleDesc cleanTupType;
|
||||
TupleDesc tupType;
|
||||
int cleanLength;
|
||||
bool isNull;
|
||||
int i;
|
||||
Size size;
|
||||
Datum *values;
|
||||
char *nulls;
|
||||
Datum values_array[64];
|
||||
char nulls_array[64];
|
||||
|
||||
/* ----------------
|
||||
* get info from the slot and the junk filter
|
||||
* ----------------
|
||||
*/
|
||||
tuple = slot->val;
|
||||
|
||||
tupType = (TupleDesc) junkfilter->jf_tupType;
|
||||
cleanTupType = (TupleDesc) junkfilter->jf_cleanTupType;
|
||||
cleanLength = junkfilter->jf_cleanLength;
|
||||
cleanMap = junkfilter->jf_cleanMap;
|
||||
|
||||
/* ---------------------
|
||||
* Handle the trivial case first.
|
||||
* ---------------------
|
||||
*/
|
||||
if (cleanLength == 0)
|
||||
return (HeapTuple) NULL;
|
||||
|
||||
/* ---------------------
|
||||
* Create the arrays that will hold the attribute values
|
||||
* and the null information for the new "clean" tuple.
|
||||
*
|
||||
* Note: we use memory on the stack to optimize things when
|
||||
* we are dealing with a small number of tuples.
|
||||
* for large tuples we just use palloc.
|
||||
* ---------------------
|
||||
*/
|
||||
if (cleanLength > 64) {
|
||||
size = cleanLength * sizeof(Datum);
|
||||
values = (Datum *) palloc(size);
|
||||
|
||||
size = cleanLength * sizeof(char);
|
||||
nulls = (char *) palloc(size);
|
||||
} else {
|
||||
values = values_array;
|
||||
nulls = nulls_array;
|
||||
}
|
||||
|
||||
/* ---------------------
|
||||
* Exctract one by one all the values of the "clean" tuple.
|
||||
* ---------------------
|
||||
*/
|
||||
for (i=0; i<cleanLength; i++) {
|
||||
Datum d = (Datum)
|
||||
heap_getattr(tuple, InvalidBuffer, cleanMap[i], tupType, &isNull);
|
||||
|
||||
values[i] = d;
|
||||
|
||||
if (isNull)
|
||||
nulls[i] = 'n';
|
||||
else
|
||||
nulls[i] = ' ';
|
||||
}
|
||||
|
||||
/* ---------------------
|
||||
* Now form the new tuple.
|
||||
* ---------------------
|
||||
*/
|
||||
cleanTuple = heap_formtuple(cleanTupType,
|
||||
values,
|
||||
nulls);
|
||||
|
||||
/* ---------------------
|
||||
* We are done. Free any space allocated for 'values' and 'nulls'
|
||||
* and return the new tuple.
|
||||
* ---------------------
|
||||
*/
|
||||
if (cleanLength > 64) {
|
||||
pfree(values);
|
||||
pfree(nulls);
|
||||
}
|
||||
|
||||
return(cleanTuple);
|
||||
}
|
||||
|
1023
src/backend/executor/execMain.c
Normal file
1023
src/backend/executor/execMain.c
Normal file
File diff suppressed because it is too large
Load Diff
477
src/backend/executor/execProcnode.c
Normal file
477
src/backend/executor/execProcnode.c
Normal file
@ -0,0 +1,477 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* execProcnode.c--
|
||||
* contains dispatch functions which call the appropriate "initialize",
|
||||
* "get a tuple", and "cleanup" routines for the given node type.
|
||||
* If the node has children, then it will presumably call ExecInitNode,
|
||||
* ExecProcNode, or ExecEndNode on it's subnodes and do the appropriate
|
||||
* processing..
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/execProcnode.c,v 1.1.1.1 1996/07/09 06:21:25 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
/*
|
||||
* INTERFACE ROUTINES
|
||||
* ExecInitNode - initialize a plan node and it's subplans
|
||||
* ExecProcNode - get a tuple by executing the plan node
|
||||
* ExecEndNode - shut down a plan node and it's subplans
|
||||
*
|
||||
* NOTES
|
||||
* This used to be three files. It is now all combined into
|
||||
* one file so that it is easier to keep ExecInitNode, ExecProcNode,
|
||||
* and ExecEndNode in sync when new nodes are added.
|
||||
*
|
||||
* EXAMPLE
|
||||
* suppose we want the age of the manager of the shoe department and
|
||||
* the number of employees in that department. so we have the query:
|
||||
*
|
||||
* retrieve (DEPT.no_emps, EMP.age)
|
||||
* where EMP.name = DEPT.mgr and
|
||||
* DEPT.name = "shoe"
|
||||
*
|
||||
* Suppose the planner gives us the following plan:
|
||||
*
|
||||
* Nest Loop (DEPT.mgr = EMP.name)
|
||||
* / \
|
||||
* / \
|
||||
* Seq Scan Seq Scan
|
||||
* DEPT EMP
|
||||
* (name = "shoe")
|
||||
*
|
||||
* ExecStart() is called first.
|
||||
* It calls InitPlan() which calls ExecInitNode() on
|
||||
* the root of the plan -- the nest loop node.
|
||||
*
|
||||
* * ExecInitNode() notices that it is looking at a nest loop and
|
||||
* as the code below demonstrates, it calls ExecInitNestLoop().
|
||||
* Eventually this calls ExecInitNode() on the right and left subplans
|
||||
* and so forth until the entire plan is initialized.
|
||||
*
|
||||
* * Then when ExecRun() is called, it calls ExecutePlan() which
|
||||
* calls ExecProcNode() repeatedly on the top node of the plan.
|
||||
* Each time this happens, ExecProcNode() will end up calling
|
||||
* ExecNestLoop(), which calls ExecProcNode() on its subplans.
|
||||
* Each of these subplans is a sequential scan so ExecSeqScan() is
|
||||
* called. The slots returned by ExecSeqScan() may contain
|
||||
* tuples which contain the attributes ExecNestLoop() uses to
|
||||
* form the tuples it returns.
|
||||
*
|
||||
* * Eventually ExecSeqScan() stops returning tuples and the nest
|
||||
* loop join ends. Lastly, ExecEnd() calls ExecEndNode() which
|
||||
* calls ExecEndNestLoop() which in turn calls ExecEndNode() on
|
||||
* its subplans which result in ExecEndSeqScan().
|
||||
*
|
||||
* This should show how the executor works by having
|
||||
* ExecInitNode(), ExecProcNode() and ExecEndNode() dispatch
|
||||
* their work to the appopriate node support routines which may
|
||||
* in turn call these routines themselves on their subplans.
|
||||
*
|
||||
*/
|
||||
#include "executor/executor.h"
|
||||
#include "executor/nodeResult.h"
|
||||
#include "executor/nodeAppend.h"
|
||||
#include "executor/nodeSeqscan.h"
|
||||
#include "executor/nodeIndexscan.h"
|
||||
#include "executor/nodeNestloop.h"
|
||||
#include "executor/nodeMergejoin.h"
|
||||
#include "executor/nodeMaterial.h"
|
||||
#include "executor/nodeSort.h"
|
||||
#include "executor/nodeUnique.h"
|
||||
#include "executor/nodeGroup.h"
|
||||
#include "executor/nodeAgg.h"
|
||||
#include "executor/nodeHash.h"
|
||||
#include "executor/nodeHashjoin.h"
|
||||
#include "executor/nodeTee.h"
|
||||
|
||||
/* ------------------------------------------------------------------------
|
||||
* ExecInitNode
|
||||
*
|
||||
* Recursively initializes all the nodes in the plan rooted
|
||||
* at 'node'.
|
||||
*
|
||||
* Initial States:
|
||||
* 'node' is the plan produced by the query planner
|
||||
*
|
||||
* returns TRUE/FALSE on whether the plan was successfully initialized
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
bool
|
||||
ExecInitNode(Plan *node, EState *estate, Plan *parent)
|
||||
{
|
||||
bool result;
|
||||
|
||||
/* ----------------
|
||||
* do nothing when we get to the end
|
||||
* of a leaf on tree.
|
||||
* ----------------
|
||||
*/
|
||||
if (node == NULL)
|
||||
return FALSE;
|
||||
|
||||
switch(nodeTag(node)) {
|
||||
/* ----------------
|
||||
* control nodes
|
||||
* ----------------
|
||||
*/
|
||||
case T_Result:
|
||||
result = ExecInitResult((Result *)node, estate, parent);
|
||||
break;
|
||||
|
||||
case T_Append:
|
||||
result = ExecInitAppend((Append *)node, estate, parent);
|
||||
break;
|
||||
|
||||
/* ----------------
|
||||
* scan nodes
|
||||
* ----------------
|
||||
*/
|
||||
case T_SeqScan:
|
||||
result = ExecInitSeqScan((SeqScan *)node, estate, parent);
|
||||
break;
|
||||
|
||||
case T_IndexScan:
|
||||
result = ExecInitIndexScan((IndexScan *)node, estate, parent);
|
||||
break;
|
||||
|
||||
/* ----------------
|
||||
* join nodes
|
||||
* ----------------
|
||||
*/
|
||||
case T_NestLoop:
|
||||
result = ExecInitNestLoop((NestLoop *)node, estate, parent);
|
||||
break;
|
||||
|
||||
case T_MergeJoin:
|
||||
result = ExecInitMergeJoin((MergeJoin *)node, estate, parent);
|
||||
break;
|
||||
|
||||
/* ----------------
|
||||
* materialization nodes
|
||||
* ----------------
|
||||
*/
|
||||
case T_Material:
|
||||
result = ExecInitMaterial((Material *)node, estate, parent);
|
||||
break;
|
||||
|
||||
case T_Sort:
|
||||
result = ExecInitSort((Sort *)node, estate, parent);
|
||||
break;
|
||||
|
||||
case T_Unique:
|
||||
result = ExecInitUnique((Unique *)node, estate, parent);
|
||||
break;
|
||||
|
||||
case T_Group:
|
||||
result = ExecInitGroup((Group *)node, estate, parent);
|
||||
break;
|
||||
|
||||
case T_Agg:
|
||||
result = ExecInitAgg((Agg *)node, estate, parent);
|
||||
break;
|
||||
|
||||
case T_Hash:
|
||||
result = ExecInitHash((Hash *)node, estate, parent);
|
||||
break;
|
||||
|
||||
case T_HashJoin:
|
||||
result = ExecInitHashJoin((HashJoin *)node, estate, parent);
|
||||
break;
|
||||
|
||||
case T_Tee:
|
||||
result = ExecInitTee((Tee*)node, estate, parent);
|
||||
break;
|
||||
|
||||
default:
|
||||
elog(DEBUG, "ExecInitNode: node not yet supported: %d",
|
||||
nodeTag(node));
|
||||
result = FALSE;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecProcNode
|
||||
*
|
||||
* Initial States:
|
||||
* the query tree must be initialized once by calling ExecInit.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
TupleTableSlot *
|
||||
ExecProcNode(Plan *node, Plan *parent)
|
||||
{
|
||||
TupleTableSlot *result;
|
||||
|
||||
/* ----------------
|
||||
* deal with NULL nodes..
|
||||
* ----------------
|
||||
*/
|
||||
if (node == NULL)
|
||||
return NULL;
|
||||
|
||||
switch(nodeTag(node)) {
|
||||
/* ----------------
|
||||
* control nodes
|
||||
* ----------------
|
||||
*/
|
||||
case T_Result:
|
||||
result = ExecResult((Result *)node);
|
||||
break;
|
||||
|
||||
case T_Append:
|
||||
result = ExecProcAppend((Append *)node);
|
||||
break;
|
||||
|
||||
/* ----------------
|
||||
* scan nodes
|
||||
* ----------------
|
||||
*/
|
||||
case T_SeqScan:
|
||||
result = ExecSeqScan((SeqScan *)node);
|
||||
break;
|
||||
|
||||
case T_IndexScan:
|
||||
result = ExecIndexScan((IndexScan *)node);
|
||||
break;
|
||||
|
||||
/* ----------------
|
||||
* join nodes
|
||||
* ----------------
|
||||
*/
|
||||
case T_NestLoop:
|
||||
result = ExecNestLoop((NestLoop *)node, parent);
|
||||
break;
|
||||
|
||||
case T_MergeJoin:
|
||||
result = ExecMergeJoin((MergeJoin *)node);
|
||||
break;
|
||||
|
||||
/* ----------------
|
||||
* materialization nodes
|
||||
* ----------------
|
||||
*/
|
||||
case T_Material:
|
||||
result = ExecMaterial((Material *)node);
|
||||
break;
|
||||
|
||||
case T_Sort:
|
||||
result = ExecSort((Sort *)node);
|
||||
break;
|
||||
|
||||
case T_Unique:
|
||||
result = ExecUnique((Unique *)node);
|
||||
break;
|
||||
|
||||
case T_Group:
|
||||
result = ExecGroup((Group *)node);
|
||||
break;
|
||||
|
||||
case T_Agg:
|
||||
result = ExecAgg((Agg *)node);
|
||||
break;
|
||||
|
||||
case T_Hash:
|
||||
result = ExecHash((Hash *)node);
|
||||
break;
|
||||
|
||||
case T_HashJoin:
|
||||
result = ExecHashJoin((HashJoin *)node);
|
||||
break;
|
||||
|
||||
case T_Tee:
|
||||
result = ExecTee((Tee*)node, parent);
|
||||
break;
|
||||
|
||||
default:
|
||||
elog(DEBUG, "ExecProcNode: node not yet supported: %d",
|
||||
nodeTag(node));
|
||||
result = FALSE;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int
|
||||
ExecCountSlotsNode(Plan *node)
|
||||
{
|
||||
if (node == (Plan *)NULL)
|
||||
return 0;
|
||||
|
||||
switch(nodeTag(node)) {
|
||||
/* ----------------
|
||||
* control nodes
|
||||
* ----------------
|
||||
*/
|
||||
case T_Result:
|
||||
return ExecCountSlotsResult((Result *)node);
|
||||
|
||||
case T_Append:
|
||||
return ExecCountSlotsAppend((Append *)node);
|
||||
|
||||
/* ----------------
|
||||
* scan nodes
|
||||
* ----------------
|
||||
*/
|
||||
case T_SeqScan:
|
||||
return ExecCountSlotsSeqScan((SeqScan *)node);
|
||||
|
||||
case T_IndexScan:
|
||||
return ExecCountSlotsIndexScan((IndexScan *)node);
|
||||
|
||||
/* ----------------
|
||||
* join nodes
|
||||
* ----------------
|
||||
*/
|
||||
case T_NestLoop:
|
||||
return ExecCountSlotsNestLoop((NestLoop *)node);
|
||||
|
||||
case T_MergeJoin:
|
||||
return ExecCountSlotsMergeJoin((MergeJoin *)node);
|
||||
|
||||
/* ----------------
|
||||
* materialization nodes
|
||||
* ----------------
|
||||
*/
|
||||
case T_Material:
|
||||
return ExecCountSlotsMaterial((Material *)node);
|
||||
|
||||
case T_Sort:
|
||||
return ExecCountSlotsSort((Sort *)node);
|
||||
|
||||
case T_Unique:
|
||||
return ExecCountSlotsUnique((Unique *)node);
|
||||
|
||||
case T_Group:
|
||||
return ExecCountSlotsGroup((Group *)node);
|
||||
|
||||
case T_Agg:
|
||||
return ExecCountSlotsAgg((Agg *)node);
|
||||
|
||||
case T_Hash:
|
||||
return ExecCountSlotsHash((Hash *)node);
|
||||
|
||||
case T_HashJoin:
|
||||
return ExecCountSlotsHashJoin((HashJoin *)node);
|
||||
|
||||
case T_Tee:
|
||||
return ExecCountSlotsTee((Tee*)node);
|
||||
|
||||
default:
|
||||
elog(WARN, "ExecCountSlotsNode: node not yet supported: %d",
|
||||
nodeTag(node));
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecEndNode
|
||||
*
|
||||
* Recursively cleans up all the nodes in the plan rooted
|
||||
* at 'node'.
|
||||
*
|
||||
* After this operation, the query plan will not be able to
|
||||
* processed any further. This should be called only after
|
||||
* the query plan has been fully executed.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
void
|
||||
ExecEndNode(Plan *node, Plan *parent)
|
||||
{
|
||||
/* ----------------
|
||||
* do nothing when we get to the end
|
||||
* of a leaf on tree.
|
||||
* ----------------
|
||||
*/
|
||||
if (node == NULL)
|
||||
return;
|
||||
|
||||
switch(nodeTag(node)) {
|
||||
/* ----------------
|
||||
* control nodes
|
||||
* ----------------
|
||||
*/
|
||||
case T_Result:
|
||||
ExecEndResult((Result *)node);
|
||||
break;
|
||||
|
||||
case T_Append:
|
||||
ExecEndAppend((Append *)node);
|
||||
break;
|
||||
|
||||
/* ----------------
|
||||
* scan nodes
|
||||
* ----------------
|
||||
*/
|
||||
case T_SeqScan:
|
||||
ExecEndSeqScan((SeqScan *)node);
|
||||
break;
|
||||
|
||||
case T_IndexScan:
|
||||
ExecEndIndexScan((IndexScan *)node);
|
||||
break;
|
||||
|
||||
/* ----------------
|
||||
* join nodes
|
||||
* ----------------
|
||||
*/
|
||||
case T_NestLoop:
|
||||
ExecEndNestLoop((NestLoop *)node);
|
||||
break;
|
||||
|
||||
case T_MergeJoin:
|
||||
ExecEndMergeJoin((MergeJoin *)node);
|
||||
break;
|
||||
|
||||
/* ----------------
|
||||
* materialization nodes
|
||||
* ----------------
|
||||
*/
|
||||
case T_Material:
|
||||
ExecEndMaterial((Material *)node);
|
||||
break;
|
||||
|
||||
case T_Sort:
|
||||
ExecEndSort((Sort *)node);
|
||||
break;
|
||||
|
||||
case T_Unique:
|
||||
ExecEndUnique((Unique *)node);
|
||||
break;
|
||||
|
||||
case T_Group:
|
||||
ExecEndGroup((Group *)node);
|
||||
break;
|
||||
|
||||
case T_Agg:
|
||||
ExecEndAgg((Agg *)node);
|
||||
break;
|
||||
|
||||
/* ----------------
|
||||
* XXX add hooks to these
|
||||
* ----------------
|
||||
*/
|
||||
case T_Hash:
|
||||
ExecEndHash((Hash *) node);
|
||||
break;
|
||||
|
||||
case T_HashJoin:
|
||||
ExecEndHashJoin((HashJoin *) node);
|
||||
break;
|
||||
|
||||
case T_Tee:
|
||||
ExecEndTee((Tee*) node, parent);
|
||||
break;
|
||||
|
||||
default:
|
||||
elog(DEBUG, "ExecEndNode: node not yet supported",
|
||||
nodeTag(node));
|
||||
break;
|
||||
}
|
||||
}
|
1504
src/backend/executor/execQual.c
Normal file
1504
src/backend/executor/execQual.c
Normal file
File diff suppressed because it is too large
Load Diff
136
src/backend/executor/execScan.c
Normal file
136
src/backend/executor/execScan.c
Normal file
@ -0,0 +1,136 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* execScan.c--
|
||||
* This code provides support for generalized relation scans. ExecScan
|
||||
* is passed a node and a pointer to a function to "do the right thing"
|
||||
* and return a tuple from the relation. ExecScan then does the tedious
|
||||
* stuff - checking the qualification and projecting the tuple
|
||||
* appropriately.
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/execScan.c,v 1.1.1.1 1996/07/09 06:21:25 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include <sys/file.h>
|
||||
#include "executor/executor.h"
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecScan
|
||||
*
|
||||
* Scans the relation using the 'access method' indicated and
|
||||
* returns the next qualifying tuple in the direction specified
|
||||
* in the global variable ExecDirection.
|
||||
* The access method returns the next tuple and execScan() is
|
||||
* responisble for checking the tuple returned against the qual-clause.
|
||||
*
|
||||
* Conditions:
|
||||
* -- the "cursor" maintained by the AMI is positioned at the tuple
|
||||
* returned previously.
|
||||
*
|
||||
* Initial States:
|
||||
* -- the relation indicated is opened for scanning so that the
|
||||
* "cursor" is positioned before the first qualifying tuple.
|
||||
*
|
||||
* May need to put startmmgr and endmmgr in here.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
TupleTableSlot *
|
||||
ExecScan(Scan *node,
|
||||
TupleTableSlot* (*accessMtd)()) /* function returning a tuple */
|
||||
{
|
||||
CommonScanState *scanstate;
|
||||
EState *estate;
|
||||
List *qual;
|
||||
bool isDone;
|
||||
|
||||
TupleTableSlot *slot;
|
||||
TupleTableSlot *resultSlot;
|
||||
HeapTuple newTuple;
|
||||
|
||||
ExprContext *econtext;
|
||||
ProjectionInfo *projInfo;
|
||||
|
||||
|
||||
/* ----------------
|
||||
* initialize misc variables
|
||||
* ----------------
|
||||
*/
|
||||
newTuple = NULL;
|
||||
slot = NULL;
|
||||
|
||||
estate = node->plan.state;
|
||||
scanstate = node->scanstate;
|
||||
|
||||
/* ----------------
|
||||
* get the expression context
|
||||
* ----------------
|
||||
*/
|
||||
econtext = scanstate->cstate.cs_ExprContext;
|
||||
|
||||
/* ----------------
|
||||
* initialize fields in ExprContext which don't change
|
||||
* in the course of the scan..
|
||||
* ----------------
|
||||
*/
|
||||
qual = node->plan.qual;
|
||||
econtext->ecxt_relation = scanstate->css_currentRelation;
|
||||
econtext->ecxt_relid = node->scanrelid;
|
||||
|
||||
if (scanstate->cstate.cs_TupFromTlist) {
|
||||
projInfo = scanstate->cstate.cs_ProjInfo;
|
||||
resultSlot = ExecProject(projInfo, &isDone);
|
||||
if (!isDone)
|
||||
return resultSlot;
|
||||
}
|
||||
/*
|
||||
* get a tuple from the access method
|
||||
* loop until we obtain a tuple which passes the qualification.
|
||||
*/
|
||||
for(;;) {
|
||||
slot = (TupleTableSlot *) (*accessMtd)(node);
|
||||
|
||||
/* ----------------
|
||||
* if the slot returned by the accessMtd contains
|
||||
* NULL, then it means there is nothing more to scan
|
||||
* so we just return the empty slot.
|
||||
* ----------------
|
||||
*/
|
||||
if (TupIsNull(slot)) return slot;
|
||||
|
||||
/* ----------------
|
||||
* place the current tuple into the expr context
|
||||
* ----------------
|
||||
*/
|
||||
econtext->ecxt_scantuple = slot;
|
||||
|
||||
/* ----------------
|
||||
* check that the current tuple satisfies the qual-clause
|
||||
* if our qualification succeeds then we
|
||||
* leave the loop.
|
||||
* ----------------
|
||||
*/
|
||||
|
||||
/* add a check for non-nil qual here to avoid a
|
||||
function call to ExecQual() when the qual is nil */
|
||||
if (!qual || ExecQual(qual, econtext) == true)
|
||||
break;
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* form a projection tuple, store it in the result tuple
|
||||
* slot and return it.
|
||||
* ----------------
|
||||
*/
|
||||
projInfo = scanstate->cstate.cs_ProjInfo;
|
||||
|
||||
resultSlot = ExecProject(projInfo, &isDone);
|
||||
scanstate->cstate.cs_TupFromTlist = !isDone;
|
||||
|
||||
return resultSlot;
|
||||
}
|
||||
|
1013
src/backend/executor/execTuples.c
Normal file
1013
src/backend/executor/execTuples.c
Normal file
File diff suppressed because it is too large
Load Diff
1092
src/backend/executor/execUtils.c
Normal file
1092
src/backend/executor/execUtils.c
Normal file
File diff suppressed because it is too large
Load Diff
377
src/backend/executor/execdebug.h
Normal file
377
src/backend/executor/execdebug.h
Normal file
@ -0,0 +1,377 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* execdebug.h--
|
||||
* #defines governing debugging behaviour in the executor
|
||||
*
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: execdebug.h,v 1.1.1.1 1996/07/09 06:21:25 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef EXECDEBUG_H
|
||||
#define EXECDEBUG_H
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* debugging defines.
|
||||
*
|
||||
* If you want certain debugging behaviour, then #define
|
||||
* the variable to 1, else #undef it. -cim 10/26/89
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/* ----------------
|
||||
* EXEC_DEBUGSTORETUP is for tuple table debugging - this
|
||||
* will print a message every time we call ExecStoreTuple.
|
||||
* -cim 3/20/91
|
||||
* ----------------
|
||||
*/
|
||||
#undef EXEC_DEBUGSTORETUP
|
||||
|
||||
/* ----------------
|
||||
* EXEC_TUPLECOUNT is a #define which causes the
|
||||
* executor keep track of tuple counts. This might be
|
||||
* causing some problems with the decstation stuff so
|
||||
* you might want to undefine this if you are doing work
|
||||
* on the decs - cim 10/20/89
|
||||
* ----------------
|
||||
*/
|
||||
#undef EXEC_TUPLECOUNT
|
||||
|
||||
/* ----------------
|
||||
* EXEC_SHOWBUFSTATS controls whether or not buffer statistics
|
||||
* are shown for each query. -cim 2/9/89
|
||||
* ----------------
|
||||
*/
|
||||
#undef EXEC_SHOWBUFSTATS
|
||||
|
||||
/* ----------------
|
||||
* EXEC_CONTEXTDEBUG turns on the printing of debugging information
|
||||
* by CXT_printf() calls regarding which memory context is the
|
||||
* CurrentMemoryContext for palloc() calls.
|
||||
* ----------------
|
||||
*/
|
||||
#undef EXEC_CONTEXTDEBUG
|
||||
|
||||
/* ----------------
|
||||
* EXEC_RETURNSIZE is a compile flag governing the
|
||||
* behaviour of lispFmgr.. See ExecMakeFunctionResult().
|
||||
* Undefining this avoids a problem in the system cache.
|
||||
*
|
||||
* Note: undefining this means that there is incorrect
|
||||
* information in the const nodes corresponding
|
||||
* to function (or operator) results. The thing is,
|
||||
* 99% of the time this is fine because when you do
|
||||
* something like x = emp.sal + 1, you already know
|
||||
* the type and size of x so the fact that + didn't
|
||||
* return the correct size doesn't matter.
|
||||
* With variable length stuff the size is stored in
|
||||
* the first few bytes of the data so again, it's
|
||||
* not likely to matter.
|
||||
* ----------------
|
||||
*/
|
||||
#undef EXEC_RETURNSIZE
|
||||
|
||||
/* ----------------
|
||||
* EXEC_UTILSDEBUG is a flag which turns on debugging of the
|
||||
* executor utilities by EU_printf() in eutils.c
|
||||
* ----------------
|
||||
*/
|
||||
#undef EXEC_UTILSDEBUG
|
||||
|
||||
/* ----------------
|
||||
* EXEC_NESTLOOPDEBUG is a flag which turns on debugging of the
|
||||
* nest loop node by NL_printf() and ENL_printf() in nestloop.c
|
||||
* ----------------
|
||||
*/
|
||||
#undef EXEC_NESTLOOPDEBUG
|
||||
|
||||
/* ----------------
|
||||
* EXEC_PROCDEBUG is a flag which turns on debugging of
|
||||
* ExecProcNode() by PN_printf() in procnode.c
|
||||
* ----------------
|
||||
*/
|
||||
#undef EXEC_PROCDEBUG
|
||||
|
||||
/* ----------------
|
||||
* EXEC_EVALDEBUG is a flag which turns on debugging of
|
||||
* ExecEval and ExecTargetList() stuff by EV_printf() in qual.c
|
||||
* ----------------
|
||||
*/
|
||||
#undef EXEC_EVALDEBUG
|
||||
|
||||
/* ----------------
|
||||
* EXEC_SCANDEBUG is a flag which turns on debugging of
|
||||
* the ExecSeqScan() stuff by S_printf() in seqscan.c
|
||||
* ----------------
|
||||
*/
|
||||
#undef EXEC_SCANDEBUG
|
||||
|
||||
/* ----------------
|
||||
* EXEC_SORTDEBUG is a flag which turns on debugging of
|
||||
* the ExecSort() stuff by SO_printf() in sort.c
|
||||
* ----------------
|
||||
*/
|
||||
#undef EXEC_SORTDEBUG
|
||||
|
||||
/* ----------------
|
||||
* EXEC_MERGEJOINDEBUG is a flag which turns on debugging of
|
||||
* the ExecMergeJoin() stuff by MJ_printf() in mergejoin.c
|
||||
* ----------------
|
||||
*/
|
||||
#undef EXEC_MERGEJOINDEBUG
|
||||
|
||||
/* ----------------
|
||||
* EXEC_MERGEJOINPFREE is a flag which causes merge joins
|
||||
* to pfree intermittant tuples (which is the proper thing)
|
||||
* Not defining this means we avoid menory management problems
|
||||
* at the cost of doing deallocation of stuff only at the
|
||||
* end of the transaction
|
||||
* ----------------
|
||||
*/
|
||||
#undef EXEC_MERGEJOINPFREE
|
||||
|
||||
/* ----------------
|
||||
* EXEC_DEBUGINTERACTIVE is a flag which enables the
|
||||
* user to issue "DEBUG" commands from an interactive
|
||||
* backend.
|
||||
* ----------------
|
||||
*/
|
||||
#undef EXEC_DEBUGINTERACTIVE
|
||||
|
||||
/* ----------------
|
||||
* EXEC_DEBUGVARIABLEFILE is string, which if defined will
|
||||
* be loaded when the executor is initialized. If this
|
||||
* string is not defined then nothing will be loaded..
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* #define EXEC_DEBUGVARIABLEFILE "/a/postgres/cimarron/.pg_debugvars"
|
||||
#
|
||||
* Note: since these variables are read at execution time,
|
||||
* they can't affect the first query.. this hack should be
|
||||
* replaced by something better sometime. -cim 11/2/89
|
||||
* ----------------
|
||||
*/
|
||||
#undef EXEC_DEBUGVARIABLEFILE
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* #defines controlled by above definitions
|
||||
*
|
||||
* Note: most of these are "incomplete" because I didn't
|
||||
* need the ones not defined. More should be added
|
||||
* only as necessary -cim 10/26/89
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
#define T_OR_F(b) (b ? "true" : "false")
|
||||
#define NULL_OR_TUPLE(slot) (TupIsNull(slot) ? "null" : "a tuple")
|
||||
|
||||
|
||||
/* #define EXEC_TUPLECOUNT - XXX take out for now for executor stubbing -- jolly*/
|
||||
/* ----------------
|
||||
* tuple count debugging defines
|
||||
* ----------------
|
||||
*/
|
||||
#ifdef EXEC_TUPLECOUNT
|
||||
extern int NTupleProcessed;
|
||||
extern int NTupleRetrieved;
|
||||
extern int NTupleReplaced;
|
||||
extern int NTupleAppended;
|
||||
extern int NTupleDeleted;
|
||||
extern int NIndexTupleProcessed;
|
||||
extern int NIndexTupleInserted;
|
||||
|
||||
#define IncrRetrieved() NTupleRetrieved++
|
||||
#define IncrAppended() NTupleAppended++
|
||||
#define IncrDeleted() NTupleDeleted++
|
||||
#define IncrReplaced() NTupleReplaced++
|
||||
#define IncrInserted() NTupleInserted++
|
||||
#define IncrProcessed() NTupleProcessed++
|
||||
#define IncrIndexProcessed() NIndexTupleProcessed++
|
||||
#define IncrIndexInserted() NIndexTupleInserted++
|
||||
#else
|
||||
#define IncrRetrieved()
|
||||
#define IncrAppended()
|
||||
#define IncrDeleted()
|
||||
#define IncrReplaced()
|
||||
#define IncrInserted()
|
||||
#define IncrProcessed()
|
||||
#define IncrIndexProcessed()
|
||||
#define IncrIndexInserted()
|
||||
#endif /* EXEC_TUPLECOUNT */
|
||||
|
||||
/* ----------------
|
||||
* memory context debugging defines
|
||||
* ----------------
|
||||
*/
|
||||
#ifdef EXEC_CONTEXTDEBUG
|
||||
#define CXT_printf(s) printf(s)
|
||||
#define CXT1_printf(s, a) printf(s, a)
|
||||
#else
|
||||
#define CXT_printf(s)
|
||||
#define CXT1_printf(s, a)
|
||||
#endif /* EXEC_CONTEXTDEBUG */
|
||||
|
||||
/* ----------------
|
||||
* eutils debugging defines
|
||||
* ----------------
|
||||
*/
|
||||
#ifdef EXEC_UTILSDEBUG
|
||||
#define EU_nodeDisplay(l) nodeDisplay(l, 0)
|
||||
#define EU_printf(s) printf(s)
|
||||
#define EU1_printf(s, a) printf(s, a)
|
||||
#define EU4_printf(s, a, b, c, d) printf(s, a, b, c, d)
|
||||
#else
|
||||
#define EU_nodeDisplay(l)
|
||||
#define EU_printf(s)
|
||||
#define EU1_printf(s, a)
|
||||
#define EU4_printf(s, a, b, c, d)
|
||||
#endif /* EXEC_UTILSDEBUG */
|
||||
|
||||
|
||||
/* ----------------
|
||||
* nest loop debugging defines
|
||||
* ----------------
|
||||
*/
|
||||
#ifdef EXEC_NESTLOOPDEBUG
|
||||
#define NL_nodeDisplay(l) nodeDisplay(l, 0)
|
||||
#define NL_printf(s) printf(s)
|
||||
#define NL1_printf(s, a) printf(s, a)
|
||||
#define NL4_printf(s, a, b, c, d) printf(s, a, b, c, d)
|
||||
#define ENL1_printf(message) printf("ExecNestLoop: %s\n", message)
|
||||
#else
|
||||
#define NL_nodeDisplay(l)
|
||||
#define NL_printf(s)
|
||||
#define NL1_printf(s, a)
|
||||
#define NL4_printf(s, a, b, c, d)
|
||||
#define ENL1_printf(message)
|
||||
#endif /* EXEC_NESTLOOPDEBUG */
|
||||
|
||||
/* ----------------
|
||||
* proc node debugging defines
|
||||
* ----------------
|
||||
*/
|
||||
#ifdef EXEC_PROCDEBUG
|
||||
#define PN_printf(s) printf(s)
|
||||
#define PN1_printf(s, p) printf(s, p)
|
||||
#else
|
||||
#define PN_printf(s)
|
||||
#define PN1_printf(s, p)
|
||||
#endif /* EXEC_PROCDEBUG */
|
||||
|
||||
/* ----------------
|
||||
* exec eval / target list debugging defines
|
||||
* ----------------
|
||||
*/
|
||||
#ifdef EXEC_EVALDEBUG
|
||||
#define EV_nodeDisplay(l) nodeDisplay(l, 0)
|
||||
#define EV_printf(s) printf(s)
|
||||
#define EV1_printf(s, a) printf(s, a)
|
||||
#define EV5_printf(s, a, b, c, d, e) printf(s, a, b, c, d, e)
|
||||
#else
|
||||
#define EV_nodeDisplay(l)
|
||||
#define EV_printf(s)
|
||||
#define EV1_printf(s, a)
|
||||
#define EV5_printf(s, a, b, c, d, e)
|
||||
#endif /* EXEC_EVALDEBUG */
|
||||
|
||||
/* ----------------
|
||||
* scan debugging defines
|
||||
* ----------------
|
||||
*/
|
||||
#ifdef EXEC_SCANDEBUG
|
||||
#define S_nodeDisplay(l) nodeDisplay(l, 0)
|
||||
#define S_printf(s) printf(s)
|
||||
#define S1_printf(s, p) printf(s, p)
|
||||
#else
|
||||
#define S_nodeDisplay(l)
|
||||
#define S_printf(s)
|
||||
#define S1_printf(s, p)
|
||||
#endif /* EXEC_SCANDEBUG */
|
||||
|
||||
/* ----------------
|
||||
* sort node debugging defines
|
||||
* ----------------
|
||||
*/
|
||||
#ifdef EXEC_SORTDEBUG
|
||||
#define SO_nodeDisplay(l) nodeDisplay(l, 0)
|
||||
#define SO_printf(s) printf(s)
|
||||
#define SO1_printf(s, p) printf(s, p)
|
||||
#else
|
||||
#define SO_nodeDisplay(l)
|
||||
#define SO_printf(s)
|
||||
#define SO1_printf(s, p)
|
||||
#endif /* EXEC_SORTDEBUG */
|
||||
|
||||
/* ----------------
|
||||
* merge join debugging defines
|
||||
* ----------------
|
||||
*/
|
||||
#ifdef EXEC_MERGEJOINDEBUG
|
||||
#define MJ_nodeDisplay(l) nodeDisplay(l, 0)
|
||||
#define MJ_printf(s) printf(s)
|
||||
#define MJ1_printf(s, p) printf(s, p)
|
||||
#define MJ2_printf(s, p1, p2) printf(s, p1, p2)
|
||||
#define MJ_debugtup(tuple, type) debugtup(tuple, type)
|
||||
#define MJ_dump(context, state) ExecMergeTupleDump(econtext, state)
|
||||
#define MJ_DEBUG_QUAL(clause, res) \
|
||||
MJ2_printf(" ExecQual(%s, econtext) returns %s\n", \
|
||||
CppAsString(clause), T_OR_F(res));
|
||||
|
||||
#define MJ_DEBUG_MERGE_COMPARE(qual, res) \
|
||||
MJ2_printf(" MergeCompare(mergeclauses, %s, ..) returns %s\n", \
|
||||
CppAsString(qual), T_OR_F(res));
|
||||
|
||||
#define MJ_DEBUG_PROC_NODE(slot) \
|
||||
MJ2_printf(" %s = ExecProcNode(innerPlan) returns %s\n", \
|
||||
CppAsString(slot), NULL_OR_TUPLE(slot));
|
||||
#else
|
||||
#define MJ_nodeDisplay(l)
|
||||
#define MJ_printf(s)
|
||||
#define MJ1_printf(s, p)
|
||||
#define MJ2_printf(s, p1, p2)
|
||||
#define MJ_debugtup(tuple, type)
|
||||
#define MJ_dump(context, state)
|
||||
#define MJ_DEBUG_QUAL(clause, res)
|
||||
#define MJ_DEBUG_MERGE_COMPARE(qual, res)
|
||||
#define MJ_DEBUG_PROC_NODE(slot)
|
||||
#endif /* EXEC_MERGEJOINDEBUG */
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* DO NOT DEFINE THESE EVER OR YOU WILL BURN!
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
/* ----------------
|
||||
* DOESNOTWORK is currently placed around memory manager
|
||||
* code that is known to cause problems. Code in between
|
||||
* is likely not converted and probably won't work anyways.
|
||||
* ----------------
|
||||
*/
|
||||
#undef DOESNOTWORK
|
||||
|
||||
/* ----------------
|
||||
* PERHAPSNEVER is placed around the "scan attribute"
|
||||
* support code for the rule manager because for now we
|
||||
* do things inefficiently. The correct solution to our
|
||||
* problem is to add code to the parser/planner to save
|
||||
* attribute information for the rule manager rather than
|
||||
* have the executor have to grope through the entire plan
|
||||
* for it so if we ever decide to make things better,
|
||||
* we should probably delete the stuff in between PERHAPSNEVER..
|
||||
* ----------------
|
||||
*/
|
||||
#undef PERHAPSNEVER
|
||||
|
||||
/* ----------------
|
||||
* NOTYET is placed around any code not yet implemented
|
||||
* in the executor. Only remove these when actually implementing
|
||||
* said code.
|
||||
* ----------------
|
||||
*/
|
||||
#undef NOTYET
|
||||
|
||||
extern long NDirectFileRead;
|
||||
extern long NDirectFileWrite;
|
||||
|
||||
#endif /* ExecDebugIncluded */
|
54
src/backend/executor/execdefs.h
Normal file
54
src/backend/executor/execdefs.h
Normal file
@ -0,0 +1,54 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* execdefs.h--
|
||||
*
|
||||
*
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: execdefs.h,v 1.1.1.1 1996/07/09 06:21:25 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef EXECDEFS_H
|
||||
#define EXECDEFS_H
|
||||
|
||||
/* ----------------
|
||||
* executor scan direction definitions
|
||||
* ----------------
|
||||
*/
|
||||
#define EXEC_FRWD 1 /* Scan forward */
|
||||
#define EXEC_BKWD -1 /* Scan backward */
|
||||
|
||||
/* ----------------
|
||||
* ExecutePlan() tuplecount definitions
|
||||
* ----------------
|
||||
*/
|
||||
#define ALL_TUPLES 0 /* return all tuples */
|
||||
#define ONE_TUPLE 1 /* return only one tuple */
|
||||
|
||||
/* ----------------
|
||||
* constants used by ExecMain
|
||||
* ----------------
|
||||
*/
|
||||
#define EXEC_RUN 3
|
||||
#define EXEC_FOR 4
|
||||
#define EXEC_BACK 5
|
||||
#define EXEC_RETONE 6
|
||||
#define EXEC_RESULT 7
|
||||
|
||||
/* ----------------
|
||||
* Merge Join states
|
||||
* ----------------
|
||||
*/
|
||||
#define EXEC_MJ_INITIALIZE 1
|
||||
#define EXEC_MJ_JOINMARK 2
|
||||
#define EXEC_MJ_JOINTEST 3
|
||||
#define EXEC_MJ_JOINTUPLES 4
|
||||
#define EXEC_MJ_NEXTOUTER 5
|
||||
#define EXEC_MJ_TESTOUTER 6
|
||||
#define EXEC_MJ_NEXTINNER 7
|
||||
#define EXEC_MJ_SKIPINNER 8
|
||||
#define EXEC_MJ_SKIPOUTER 9
|
||||
|
||||
#endif /* EXECDEFS_H */
|
38
src/backend/executor/execdesc.h
Normal file
38
src/backend/executor/execdesc.h
Normal file
@ -0,0 +1,38 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* execdesc.h--
|
||||
* plan and query descriptor accessor macros used by the executor
|
||||
* and related modules.
|
||||
*
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: execdesc.h,v 1.1.1.1 1996/07/09 06:21:25 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef EXECDESC_H
|
||||
#define EXECDESC_H
|
||||
|
||||
#include "nodes/parsenodes.h"
|
||||
#include "nodes/plannodes.h"
|
||||
#include "tcop/dest.h"
|
||||
|
||||
/* ----------------
|
||||
* query descriptor:
|
||||
* a QueryDesc encapsulates everything that the executor
|
||||
* needs to execute the query
|
||||
* ---------------------
|
||||
*/
|
||||
typedef struct QueryDesc {
|
||||
CmdType operation; /* CMD_SELECT, CMD_UPDATE, etc. */
|
||||
Query *parsetree;
|
||||
Plan *plantree;
|
||||
CommandDest dest; /* the destination output of the execution */
|
||||
} QueryDesc;
|
||||
|
||||
/* in pquery.c */
|
||||
extern QueryDesc *CreateQueryDesc(Query *parsetree, Plan *plantree,
|
||||
CommandDest dest);
|
||||
|
||||
#endif /* EXECDESC_H */
|
229
src/backend/executor/executor.h
Normal file
229
src/backend/executor/executor.h
Normal file
@ -0,0 +1,229 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* executor.h--
|
||||
* support for the POSTGRES executor module
|
||||
*
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: executor.h,v 1.1.1.1 1996/07/09 06:21:25 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef EXECUTOR_H
|
||||
#define EXECUTOR_H
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* #includes
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "postgres.h"
|
||||
#include "nodes/pg_list.h"
|
||||
|
||||
/* ----------------
|
||||
* executor debugging definitions are kept in a separate file
|
||||
* so people can customize what debugging they want to see and not
|
||||
* have this information clobbered every time a new version of
|
||||
* executor.h is checked in -cim 10/26/89
|
||||
* ----------------
|
||||
*/
|
||||
#include "executor/execdebug.h"
|
||||
|
||||
#include "access/heapam.h"
|
||||
#include "access/htup.h"
|
||||
#include "access/istrat.h"
|
||||
#include "access/itup.h"
|
||||
#include "access/skey.h"
|
||||
#include "utils/tqual.h"
|
||||
#include "catalog/catname.h"
|
||||
#include "utils/syscache.h"
|
||||
#include "executor/execdefs.h"
|
||||
#include "executor/tuptable.h"
|
||||
|
||||
#include "nodes/parsenodes.h"
|
||||
|
||||
#include "storage/buf.h"
|
||||
#include "miscadmin.h"
|
||||
#include "fmgr.h"
|
||||
#include "utils/elog.h"
|
||||
#include "utils/mcxt.h"
|
||||
#include "utils/memutils.h"
|
||||
#include "utils/rel.h"
|
||||
|
||||
#include "catalog/pg_index.h"
|
||||
#include "catalog/pg_proc.h"
|
||||
#include "catalog/pg_type.h"
|
||||
#include "catalog/pg_aggregate.h"
|
||||
|
||||
#include "access/printtup.h"
|
||||
#include "nodes/primnodes.h"
|
||||
#include "nodes/plannodes.h"
|
||||
#include "nodes/execnodes.h"
|
||||
|
||||
#include "tcop/dest.h"
|
||||
#include "storage/smgr.h"
|
||||
|
||||
#include "access/genam.h"
|
||||
#include "executor/execdesc.h"
|
||||
|
||||
/*
|
||||
* prototypes from functions in execAmi.c
|
||||
*/
|
||||
extern void ExecOpenScanR(Oid relOid, int nkeys, ScanKey skeys, bool isindex,
|
||||
ScanDirection dir, TimeQual timeRange,
|
||||
Relation *returnRelation, Pointer *returnScanDesc);
|
||||
extern Relation ExecOpenR(Oid relationOid, bool isindex);
|
||||
extern Pointer ExecBeginScan(Relation relation, int nkeys, ScanKey skeys,
|
||||
bool isindex, ScanDirection dir, TimeQual time_range);
|
||||
extern void ExecCloseR(Plan *node);
|
||||
extern void ExecReScan(Plan *node, ExprContext *exprCtxt, Plan *parent);
|
||||
extern HeapScanDesc ExecReScanR(Relation relDesc, HeapScanDesc scanDesc,
|
||||
ScanDirection direction, int nkeys, ScanKey skeys);
|
||||
extern void ExecMarkPos(Plan *node);
|
||||
extern void ExecRestrPos(Plan *node);
|
||||
extern Relation ExecCreatR(TupleDesc tupType, Oid relationOid);
|
||||
|
||||
/*
|
||||
* prototypes from functions in execJunk.c
|
||||
*/
|
||||
extern JunkFilter *ExecInitJunkFilter(List *targetList);
|
||||
extern bool ExecGetJunkAttribute(JunkFilter *junkfilter, TupleTableSlot *slot,
|
||||
char *attrName, Datum *value, bool *isNull);
|
||||
extern HeapTuple ExecRemoveJunk(JunkFilter *junkfilter, TupleTableSlot *slot);
|
||||
|
||||
|
||||
/*
|
||||
* prototypes from functions in execMain.c
|
||||
*/
|
||||
extern TupleDesc ExecutorStart(QueryDesc *queryDesc, EState *estate);
|
||||
extern TupleTableSlot* ExecutorRun(QueryDesc *queryDesc, EState *estate, int feature, int count);
|
||||
extern void ExecutorEnd(QueryDesc *queryDesc, EState *estate);
|
||||
|
||||
/*
|
||||
* prototypes from functions in execProcnode.c
|
||||
*/
|
||||
extern bool ExecInitNode(Plan *node, EState *estate, Plan *parent);
|
||||
extern TupleTableSlot *ExecProcNode(Plan *node, Plan *parent);
|
||||
extern int ExecCountSlotsNode(Plan *node);
|
||||
extern void ExecEndNode(Plan *node, Plan *parent);
|
||||
|
||||
/*
|
||||
* prototypes from functions in execQual.c
|
||||
*/
|
||||
extern bool execConstByVal;
|
||||
extern int execConstLen;
|
||||
|
||||
extern Datum ExecExtractResult(TupleTableSlot *slot, AttrNumber attnum,
|
||||
bool *isNull);
|
||||
extern Datum ExecEvalVar(Var *variable, ExprContext *econtext, bool *isNull);
|
||||
extern Datum ExecEvalParam(Param *expression, ExprContext *econtext,
|
||||
bool *isNull);
|
||||
extern char *GetAttributeByNum(TupleTableSlot *slot, AttrNumber attrno,
|
||||
bool *isNull);
|
||||
extern char *att_by_num(TupleTableSlot *slot, AttrNumber attrno,
|
||||
bool *isNull);
|
||||
/* stop here */
|
||||
extern char *GetAttributeByName(TupleTableSlot *slot, char *attname,
|
||||
bool *isNull);
|
||||
extern char *att_by_name(TupleTableSlot *slot, char *attname, bool *isNull);
|
||||
extern void ExecEvalFuncArgs(FunctionCachePtr fcache, ExprContext *econtext,
|
||||
List *argList, Datum argV[], bool *argIsDone);
|
||||
extern Datum ExecMakeFunctionResult(Node *node, List *arguments,
|
||||
ExprContext *econtext, bool *isNull, bool *isDone);
|
||||
extern Datum ExecEvalOper(Expr *opClause, ExprContext *econtext,
|
||||
bool *isNull);
|
||||
extern Datum ExecEvalFunc(Expr *funcClause, ExprContext *econtext,
|
||||
bool *isNull, bool *isDone);
|
||||
extern Datum ExecEvalNot(Expr *notclause, ExprContext *econtext, bool *isNull);
|
||||
extern Datum ExecEvalOr(Expr *orExpr, ExprContext *econtext, bool *isNull);
|
||||
extern Datum ExecEvalAnd(Expr *andExpr, ExprContext *econtext, bool *isNull);
|
||||
extern Datum ExecEvalExpr(Node *expression, ExprContext *econtext, bool *isNull,
|
||||
bool *isDone);
|
||||
extern bool ExecQualClause(Node *clause, ExprContext *econtext);
|
||||
extern bool ExecQual(List *qual, ExprContext *econtext);
|
||||
extern int ExecTargetListLength(List *targetlist);
|
||||
extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo, bool *isDone);
|
||||
|
||||
/*
|
||||
* prototypes from functions in execScan.c
|
||||
*/
|
||||
extern TupleTableSlot *ExecScan(Scan *node, TupleTableSlot* (*accessMtd)());
|
||||
|
||||
/*
|
||||
* prototypes from functions in execTuples.c
|
||||
*/
|
||||
extern TupleTable ExecCreateTupleTable(int initialSize);
|
||||
extern void ExecDestroyTupleTable(TupleTable table, bool shouldFree);
|
||||
extern TupleTableSlot* ExecAllocTableSlot(TupleTable table);
|
||||
extern TupleTableSlot* ExecStoreTuple(HeapTuple tuple,
|
||||
TupleTableSlot *slot,
|
||||
Buffer buffer,
|
||||
bool shouldFree);
|
||||
extern TupleTableSlot* ExecClearTuple(TupleTableSlot* slot);
|
||||
extern bool ExecSlotPolicy(TupleTableSlot *slot);
|
||||
extern bool ExecSetSlotPolicy(TupleTableSlot *slot, bool shouldFree);
|
||||
extern TupleDesc ExecSetSlotDescriptor(TupleTableSlot *slot,
|
||||
TupleDesc tupdesc);
|
||||
extern void ExecSetSlotDescriptorIsNew(TupleTableSlot *slot, bool isNew);
|
||||
extern TupleDesc ExecSetNewSlotDescriptor(TupleTableSlot *slot,
|
||||
TupleDesc tupdesc);
|
||||
extern Buffer ExecSetSlotBuffer(TupleTableSlot *slot, Buffer b);
|
||||
extern void ExecIncrSlotBufferRefcnt(TupleTableSlot *slot);
|
||||
extern bool TupIsNull(TupleTableSlot* slot);
|
||||
extern bool ExecSlotDescriptorIsNew(TupleTableSlot *slot);
|
||||
extern void ExecInitResultTupleSlot(EState *estate, CommonState *commonstate);
|
||||
extern void ExecInitScanTupleSlot(EState *estate,
|
||||
CommonScanState *commonscanstate);
|
||||
extern void ExecInitMarkedTupleSlot(EState *estate, MergeJoinState *mergestate);
|
||||
extern void ExecInitOuterTupleSlot(EState *estate, HashJoinState *hashstate);
|
||||
extern void ExecInitHashTupleSlot(EState *estate, HashJoinState *hashstate);
|
||||
extern TupleTableSlot *NodeGetResultTupleSlot(Plan *node);
|
||||
|
||||
extern TupleDesc ExecGetTupType(Plan *node);
|
||||
extern TupleDesc ExecTypeFromTL(List *targetList);
|
||||
|
||||
/*
|
||||
* prototypes from functions in execTuples.c
|
||||
*/
|
||||
extern void ResetTupleCount();
|
||||
extern void DisplayTupleCount(FILE *statfp);
|
||||
extern void ExecAssignNodeBaseInfo(EState *estate, CommonState *basenode,
|
||||
Plan *parent);
|
||||
extern void ExecAssignExprContext(EState *estate, CommonState *commonstate);
|
||||
extern void ExecAssignResultType(CommonState *commonstate,
|
||||
TupleDesc tupDesc);
|
||||
extern void ExecAssignResultTypeFromOuterPlan(Plan *node,
|
||||
CommonState *commonstate);
|
||||
extern void ExecAssignResultTypeFromTL(Plan *node, CommonState *commonstate);
|
||||
extern TupleDesc ExecGetResultType(CommonState *commonstate);
|
||||
extern void ExecFreeResultType(CommonState *commonstate);
|
||||
extern void ExecAssignProjectionInfo(Plan *node, CommonState *commonstate);
|
||||
extern void ExecFreeProjectionInfo(CommonState *commonstate);
|
||||
extern TupleDesc ExecGetScanType(CommonScanState *csstate);
|
||||
extern void ExecFreeScanType(CommonScanState *csstate);
|
||||
extern void ExecAssignScanType(CommonScanState *csstate,
|
||||
TupleDesc tupDesc);
|
||||
extern void ExecAssignScanTypeFromOuterPlan(Plan *node,
|
||||
CommonScanState *csstate);
|
||||
extern AttributeTupleForm ExecGetTypeInfo(Relation relDesc);
|
||||
|
||||
extern void ExecGetIndexKeyInfo(IndexTupleForm indexTuple, int *numAttsOutP,
|
||||
AttrNumber **attsOutP, FuncIndexInfoPtr fInfoP);
|
||||
extern void ExecOpenIndices(Oid resultRelationOid,
|
||||
RelationInfo *resultRelationInfo);
|
||||
extern void ExecCloseIndices(RelationInfo *resultRelationInfo);
|
||||
extern IndexTuple ExecFormIndexTuple(HeapTuple heapTuple,
|
||||
Relation heapRelation, Relation indexRelation, IndexInfo *indexInfo);
|
||||
extern void ExecInsertIndexTuples(TupleTableSlot *slot, ItemPointer tupleid,
|
||||
EState *estate);
|
||||
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* the end
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#endif /* EXECUTOR_H */
|
388
src/backend/executor/functions.c
Normal file
388
src/backend/executor/functions.c
Normal file
@ -0,0 +1,388 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* functions.c--
|
||||
* Routines to handle functions called from the executor
|
||||
* Putting this stuff in fmgr makes the postmaster a mess....
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.1.1.1 1996/07/09 06:21:25 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include "postgres.h"
|
||||
|
||||
#include "nodes/primnodes.h"
|
||||
#include "nodes/relation.h"
|
||||
#include "nodes/execnodes.h"
|
||||
#include "nodes/plannodes.h"
|
||||
|
||||
#include "catalog/pg_proc.h"
|
||||
#include "parser/parse_query.h"
|
||||
#include "tcop/pquery.h"
|
||||
#include "tcop/tcopprot.h"
|
||||
#include "nodes/params.h"
|
||||
#include "fmgr.h"
|
||||
#include "utils/fcache.h"
|
||||
#include "utils/datum.h"
|
||||
#include "utils/elog.h"
|
||||
#include "utils/palloc.h"
|
||||
#include "utils/syscache.h"
|
||||
#include "catalog/pg_language.h"
|
||||
#include "access/heapam.h"
|
||||
#include "access/xact.h"
|
||||
#include "executor/executor.h"
|
||||
#include "executor/functions.h"
|
||||
|
||||
#undef new
|
||||
|
||||
typedef enum {F_EXEC_START, F_EXEC_RUN, F_EXEC_DONE} ExecStatus;
|
||||
|
||||
typedef struct local_es {
|
||||
QueryDesc *qd;
|
||||
EState *estate;
|
||||
struct local_es *next;
|
||||
ExecStatus status;
|
||||
} execution_state;
|
||||
|
||||
#define LAST_POSTQUEL_COMMAND(es) ((es)->next == (execution_state *)NULL)
|
||||
|
||||
/* non-export function prototypes */
|
||||
static TupleDesc postquel_start(execution_state *es);
|
||||
static execution_state *init_execution_state(FunctionCachePtr fcache,
|
||||
char *args[]);
|
||||
static TupleTableSlot *postquel_getnext(execution_state *es);
|
||||
static void postquel_end(execution_state *es);
|
||||
static void postquel_sub_params(execution_state *es, int nargs,
|
||||
char *args[], bool *nullV);
|
||||
static Datum postquel_execute(execution_state *es, FunctionCachePtr fcache,
|
||||
List *fTlist, char **args, bool *isNull);
|
||||
|
||||
|
||||
Datum
|
||||
ProjectAttribute(TupleDesc TD,
|
||||
TargetEntry *tlist,
|
||||
HeapTuple tup,
|
||||
bool *isnullP)
|
||||
{
|
||||
Datum val,valueP;
|
||||
Var *attrVar = (Var *)tlist->expr;
|
||||
AttrNumber attrno = attrVar->varattno;
|
||||
|
||||
|
||||
val = PointerGetDatum(heap_getattr(tup,
|
||||
InvalidBuffer,
|
||||
attrno,
|
||||
TD,
|
||||
isnullP));
|
||||
if (*isnullP)
|
||||
return (Datum) NULL;
|
||||
|
||||
valueP = datumCopy(val,
|
||||
TD->attrs[attrno-1]->atttypid,
|
||||
TD->attrs[attrno-1]->attbyval,
|
||||
(Size) TD->attrs[attrno-1]->attlen);
|
||||
return valueP;
|
||||
}
|
||||
|
||||
static execution_state *
|
||||
init_execution_state(FunctionCachePtr fcache,
|
||||
char *args[])
|
||||
{
|
||||
execution_state *newes;
|
||||
execution_state *nextes;
|
||||
execution_state *preves;
|
||||
QueryTreeList *queryTree_list;
|
||||
int i;
|
||||
List *planTree_list;
|
||||
int nargs;
|
||||
|
||||
nargs = fcache->nargs;
|
||||
|
||||
newes = (execution_state *) palloc(sizeof(execution_state));
|
||||
nextes = newes;
|
||||
preves = (execution_state *)NULL;
|
||||
|
||||
|
||||
planTree_list = (List *)
|
||||
pg_plan(fcache->src, fcache->argOidVect, nargs, &queryTree_list, None);
|
||||
|
||||
for (i=0; i < queryTree_list->len; i++) {
|
||||
EState *estate;
|
||||
Query *queryTree = (Query*) (queryTree_list->qtrees[i]);
|
||||
Plan *planTree = lfirst(planTree_list);
|
||||
|
||||
if (!nextes)
|
||||
nextes = (execution_state *) palloc(sizeof(execution_state));
|
||||
if (preves)
|
||||
preves->next = nextes;
|
||||
|
||||
nextes->next = NULL;
|
||||
nextes->status = F_EXEC_START;
|
||||
nextes->qd = CreateQueryDesc(queryTree,
|
||||
planTree,
|
||||
None);
|
||||
estate = CreateExecutorState();
|
||||
|
||||
if (nargs > 0) {
|
||||
int i;
|
||||
ParamListInfo paramLI;
|
||||
|
||||
paramLI =
|
||||
(ParamListInfo)palloc((nargs+1)*sizeof(ParamListInfoData));
|
||||
|
||||
memset(paramLI, 0, nargs*sizeof(ParamListInfoData));
|
||||
|
||||
estate->es_param_list_info = paramLI;
|
||||
|
||||
for (i=0; i<nargs; paramLI++, i++) {
|
||||
paramLI->kind = PARAM_NUM;
|
||||
paramLI->id = i+1;
|
||||
paramLI->isnull = false;
|
||||
paramLI->value = (Datum) NULL;
|
||||
}
|
||||
paramLI->kind = PARAM_INVALID;
|
||||
}
|
||||
else
|
||||
estate->es_param_list_info = (ParamListInfo)NULL;
|
||||
nextes->estate = estate;
|
||||
preves = nextes;
|
||||
nextes = (execution_state *)NULL;
|
||||
|
||||
planTree_list = lnext(planTree_list);
|
||||
}
|
||||
|
||||
return newes;
|
||||
}
|
||||
|
||||
static TupleDesc
|
||||
postquel_start(execution_state *es)
|
||||
{
|
||||
return ExecutorStart(es->qd, es->estate);
|
||||
}
|
||||
|
||||
static TupleTableSlot *
|
||||
postquel_getnext(execution_state *es)
|
||||
{
|
||||
int feature;
|
||||
|
||||
feature = (LAST_POSTQUEL_COMMAND(es)) ? EXEC_RETONE : EXEC_RUN;
|
||||
|
||||
return ExecutorRun(es->qd, es->estate, feature, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
postquel_end(execution_state *es)
|
||||
{
|
||||
ExecutorEnd(es->qd, es->estate);
|
||||
}
|
||||
|
||||
static void
|
||||
postquel_sub_params(execution_state *es,
|
||||
int nargs,
|
||||
char *args[],
|
||||
bool *nullV)
|
||||
{
|
||||
ParamListInfo paramLI;
|
||||
EState *estate;
|
||||
|
||||
estate = es->estate;
|
||||
paramLI = estate->es_param_list_info;
|
||||
|
||||
while (paramLI->kind != PARAM_INVALID) {
|
||||
if (paramLI->kind == PARAM_NUM) {
|
||||
Assert(paramLI->id <= nargs);
|
||||
paramLI->value = (Datum)args[(paramLI->id - 1)];
|
||||
paramLI->isnull = nullV[(paramLI->id - 1)];
|
||||
}
|
||||
paramLI++;
|
||||
}
|
||||
}
|
||||
|
||||
static TupleTableSlot *
|
||||
copy_function_result(FunctionCachePtr fcache,
|
||||
TupleTableSlot *resultSlot)
|
||||
{
|
||||
TupleTableSlot *funcSlot;
|
||||
TupleDesc resultTd;
|
||||
HeapTuple newTuple;
|
||||
HeapTuple oldTuple;
|
||||
|
||||
Assert(! TupIsNull(resultSlot));
|
||||
oldTuple = resultSlot->val;
|
||||
|
||||
funcSlot = (TupleTableSlot*)fcache->funcSlot;
|
||||
|
||||
if (funcSlot == (TupleTableSlot*)NULL)
|
||||
return resultSlot;
|
||||
|
||||
resultTd = resultSlot->ttc_tupleDescriptor;
|
||||
/*
|
||||
* When the funcSlot is NULL we have to initialize the funcSlot's
|
||||
* tuple descriptor.
|
||||
*/
|
||||
if (TupIsNull(funcSlot)) {
|
||||
int i= 0;
|
||||
TupleDesc funcTd = funcSlot->ttc_tupleDescriptor;
|
||||
|
||||
while (i < oldTuple->t_natts) {
|
||||
funcTd->attrs[i] =
|
||||
(AttributeTupleForm)palloc(ATTRIBUTE_TUPLE_SIZE);
|
||||
memmove(funcTd->attrs[i],
|
||||
resultTd->attrs[i],
|
||||
ATTRIBUTE_TUPLE_SIZE);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
newTuple = heap_copytuple(oldTuple);
|
||||
|
||||
return ExecStoreTuple(newTuple,funcSlot,InvalidBuffer,true);
|
||||
}
|
||||
|
||||
static Datum
|
||||
postquel_execute(execution_state *es,
|
||||
FunctionCachePtr fcache,
|
||||
List *fTlist,
|
||||
char **args,
|
||||
bool *isNull)
|
||||
{
|
||||
TupleTableSlot *slot;
|
||||
Datum value;
|
||||
|
||||
if (es->status == F_EXEC_START)
|
||||
{
|
||||
(void) postquel_start(es);
|
||||
es->status = F_EXEC_RUN;
|
||||
}
|
||||
|
||||
if (fcache->nargs > 0)
|
||||
postquel_sub_params(es, fcache->nargs, args, fcache->nullVect);
|
||||
|
||||
slot = postquel_getnext(es);
|
||||
|
||||
if (TupIsNull(slot)) {
|
||||
postquel_end(es);
|
||||
es->status = F_EXEC_DONE;
|
||||
*isNull = true;
|
||||
/*
|
||||
* If this isn't the last command for the function
|
||||
* we have to increment the command
|
||||
* counter so that subsequent commands can see changes made
|
||||
* by previous ones.
|
||||
*/
|
||||
if (!LAST_POSTQUEL_COMMAND(es)) CommandCounterIncrement();
|
||||
return (Datum)NULL;
|
||||
}
|
||||
|
||||
if (LAST_POSTQUEL_COMMAND(es)) {
|
||||
TupleTableSlot *resSlot;
|
||||
|
||||
/*
|
||||
* Copy the result. copy_function_result is smart enough
|
||||
* to do nothing when no action is called for. This helps
|
||||
* reduce the logic and code redundancy here.
|
||||
*/
|
||||
resSlot = copy_function_result(fcache, slot);
|
||||
if (fTlist != NIL) {
|
||||
HeapTuple tup;
|
||||
TargetEntry *tle = lfirst(fTlist);
|
||||
|
||||
tup = resSlot->val;
|
||||
value = ProjectAttribute(resSlot->ttc_tupleDescriptor,
|
||||
tle,
|
||||
tup,
|
||||
isNull);
|
||||
}else {
|
||||
value = (Datum)resSlot;
|
||||
*isNull = false;
|
||||
}
|
||||
|
||||
/*
|
||||
* If this is a single valued function we have to end the
|
||||
* function execution now.
|
||||
*/
|
||||
if (fcache->oneResult) {
|
||||
postquel_end(es);
|
||||
es->status = F_EXEC_DONE;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
/*
|
||||
* If this isn't the last command for the function, we don't
|
||||
* return any results, but we have to increment the command
|
||||
* counter so that subsequent commands can see changes made
|
||||
* by previous ones.
|
||||
*/
|
||||
CommandCounterIncrement();
|
||||
return (Datum)NULL;
|
||||
}
|
||||
|
||||
Datum
|
||||
postquel_function(Func *funcNode, char **args, bool *isNull, bool *isDone)
|
||||
{
|
||||
execution_state *es;
|
||||
Datum result;
|
||||
FunctionCachePtr fcache = funcNode->func_fcache;
|
||||
|
||||
es = (execution_state *) fcache->func_state;
|
||||
if (es == NULL)
|
||||
{
|
||||
es = init_execution_state(fcache, args);
|
||||
fcache->func_state = (char *) es;
|
||||
}
|
||||
|
||||
while (es && es->status == F_EXEC_DONE)
|
||||
es = es->next;
|
||||
|
||||
Assert(es);
|
||||
/*
|
||||
* Execute each command in the function one after another until we're
|
||||
* executing the final command and get a result or we run out of
|
||||
* commands.
|
||||
*/
|
||||
while (es != (execution_state *)NULL)
|
||||
{
|
||||
result = postquel_execute(es,
|
||||
fcache,
|
||||
funcNode->func_tlist,
|
||||
args,
|
||||
isNull);
|
||||
if (es->status != F_EXEC_DONE)
|
||||
break;
|
||||
es = es->next;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we've gone through every command in this function, we are done.
|
||||
*/
|
||||
if (es == (execution_state *)NULL)
|
||||
{
|
||||
/*
|
||||
* Reset the execution states to start over again
|
||||
*/
|
||||
es = (execution_state *)fcache->func_state;
|
||||
while (es)
|
||||
{
|
||||
es->status = F_EXEC_START;
|
||||
es = es->next;
|
||||
}
|
||||
/*
|
||||
* Let caller know we're finished.
|
||||
*/
|
||||
*isDone = true;
|
||||
return (fcache->oneResult) ? result : (Datum)NULL;
|
||||
}
|
||||
/*
|
||||
* If we got a result from a command within the function it has
|
||||
* to be the final command. All others shouldn't be returing
|
||||
* anything.
|
||||
*/
|
||||
Assert ( LAST_POSTQUEL_COMMAND(es) );
|
||||
*isDone = false;
|
||||
|
||||
return result;
|
||||
}
|
22
src/backend/executor/functions.h
Normal file
22
src/backend/executor/functions.h
Normal file
@ -0,0 +1,22 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* functions.h--
|
||||
*
|
||||
*
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: functions.h,v 1.1.1.1 1996/07/09 06:21:25 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef FUNCTIONS_H
|
||||
#define FUNCTIONS_H
|
||||
|
||||
extern Datum ProjectAttribute(TupleDesc TD, TargetEntry *tlist,
|
||||
HeapTuple tup, bool *isnullP);
|
||||
|
||||
extern Datum postquel_function(Func *funcNode, char **args,
|
||||
bool *isNull, bool *isDone);
|
||||
|
||||
#endif /* FUNCTIONS_H */
|
82
src/backend/executor/hashjoin.h
Normal file
82
src/backend/executor/hashjoin.h
Normal file
@ -0,0 +1,82 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* hashjoin.h--
|
||||
* internal structures for hash table and buckets
|
||||
*
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: hashjoin.h,v 1.1.1.1 1996/07/09 06:21:25 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef HASHJOIN_H
|
||||
#define HASHJOIN_H
|
||||
|
||||
#include "access/htup.h"
|
||||
#include "storage/ipc.h"
|
||||
|
||||
/* -----------------
|
||||
* have to use relative address as pointers in the hashtable
|
||||
* because the hashtable may reallocate in difference processes
|
||||
* -----------------
|
||||
*/
|
||||
typedef int RelativeAddr;
|
||||
|
||||
/* ------------------
|
||||
* the relative addresses are always relative to the head of the
|
||||
* hashtable, the following macro converts them to absolute address.
|
||||
* ------------------
|
||||
*/
|
||||
#define ABSADDR(X) ((X) < 0 ? NULL: (char*)hashtable + X)
|
||||
#define RELADDR(X) (RelativeAddr)((char*)(X) - (char*)hashtable)
|
||||
|
||||
typedef char **charPP;
|
||||
typedef int *intP;
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* hash-join hash table structures
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
typedef struct HashTableData {
|
||||
int nbuckets;
|
||||
int totalbuckets;
|
||||
int bucketsize;
|
||||
IpcMemoryId shmid;
|
||||
RelativeAddr top; /* char* */
|
||||
RelativeAddr bottom; /* char* */
|
||||
RelativeAddr overflownext; /* char* */
|
||||
RelativeAddr batch; /* char* */
|
||||
RelativeAddr readbuf; /* char* */
|
||||
int nbatch;
|
||||
RelativeAddr outerbatchNames; /* RelativeAddr* */
|
||||
RelativeAddr outerbatchPos; /* RelativeAddr* */
|
||||
RelativeAddr innerbatchNames; /* RelativeAddr* */
|
||||
RelativeAddr innerbatchPos; /* RelativeAddr* */
|
||||
RelativeAddr innerbatchSizes; /* int* */
|
||||
int curbatch;
|
||||
int nprocess;
|
||||
int pcount;
|
||||
} HashTableData; /* real hash table follows here */
|
||||
|
||||
typedef HashTableData *HashJoinTable;
|
||||
|
||||
typedef struct OverflowTupleData {
|
||||
RelativeAddr tuple; /* HeapTuple */
|
||||
RelativeAddr next; /* struct OverflowTupleData * */
|
||||
} OverflowTupleData; /* real tuple follows here */
|
||||
|
||||
typedef OverflowTupleData *OverflowTuple;
|
||||
|
||||
typedef struct HashBucketData {
|
||||
RelativeAddr top; /* HeapTuple */
|
||||
RelativeAddr bottom; /* HeapTuple */
|
||||
RelativeAddr firstotuple; /* OverflowTuple */
|
||||
RelativeAddr lastotuple; /* OverflowTuple */
|
||||
} HashBucketData; /* real bucket follows here */
|
||||
|
||||
typedef HashBucketData *HashBucket;
|
||||
|
||||
#define HASH_PERMISSION 0700
|
||||
|
||||
#endif /* HASHJOIN_H */
|
558
src/backend/executor/nodeAgg.c
Normal file
558
src/backend/executor/nodeAgg.c
Normal file
@ -0,0 +1,558 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* nodeAgg.c--
|
||||
* Routines to handle aggregate nodes.
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* NOTE
|
||||
* The implementation of Agg node has been reworked to handle legal
|
||||
* SQL aggregates. (Do not expect POSTQUEL semantics.) -- ay 2/95
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* /usr/local/devel/pglite/cvs/src/backend/executor/nodeAgg.c,v 1.13 1995/08/01 20:19:07 jolly Exp
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include "access/heapam.h"
|
||||
#include "catalog/pg_aggregate.h"
|
||||
#include "catalog/catalog.h"
|
||||
#include "executor/executor.h"
|
||||
#include "executor/nodeAgg.h"
|
||||
#include "storage/bufmgr.h"
|
||||
#include "utils/palloc.h"
|
||||
#include "parser/catalog_utils.h"
|
||||
|
||||
/*
|
||||
* AggFuncInfo -
|
||||
* keeps the transition functions information around
|
||||
*/
|
||||
typedef struct AggFuncInfo {
|
||||
Oid xfn1_oid;
|
||||
Oid xfn2_oid;
|
||||
Oid finalfn_oid;
|
||||
func_ptr xfn1;
|
||||
func_ptr xfn2;
|
||||
func_ptr finalfn;
|
||||
int xfn1_nargs;
|
||||
int xfn2_nargs;
|
||||
int finalfn_nargs;
|
||||
} AggFuncInfo;
|
||||
|
||||
static Datum aggGetAttr(TupleTableSlot *tuple, Aggreg *agg, bool *isNull);
|
||||
|
||||
|
||||
/* ---------------------------------------
|
||||
*
|
||||
* ExecAgg -
|
||||
*
|
||||
* ExecAgg receives tuples from its outer subplan and aggregates over
|
||||
* the appropriate attribute for each (unique) aggregate in the target
|
||||
* list. (The number of tuples to aggregate over depends on whether a
|
||||
* GROUP BY clause is present. It might be the number of tuples in a
|
||||
* group or all the tuples that satisfy the qualifications.) The value of
|
||||
* each aggregate is stored in the expression context for ExecProject to
|
||||
* evaluate the result tuple.
|
||||
*
|
||||
* ExecAgg evaluates each aggregate in the following steps: (initcond1,
|
||||
* initcond2 are the initial values and sfunc1, sfunc2, and finalfunc are
|
||||
* the transition functions.)
|
||||
*
|
||||
* value1[i] = initcond1
|
||||
* value2[i] = initcond2
|
||||
* forall tuples do
|
||||
* value1[i] = sfunc1(aggregate_attribute, value1[i])
|
||||
* value2[i] = sfunc2(value2[i])
|
||||
* value1[i] = finalfunc(value1[i], value2[i])
|
||||
*
|
||||
* If the outer subplan is a Group node, ExecAgg returns as many tuples
|
||||
* as there are groups.
|
||||
*
|
||||
* XXX handling of NULL doesn't work
|
||||
*
|
||||
* OLD COMMENTS
|
||||
*
|
||||
* XXX Aggregates should probably have another option: what to do
|
||||
* with transfn2 if we hit a null value. "count" (transfn1 = null,
|
||||
* transfn2 = increment) will want to have transfn2 called; "avg"
|
||||
* (transfn1 = add, transfn2 = increment) will not. -pma 1/3/93
|
||||
*
|
||||
* ------------------------------------------
|
||||
*/
|
||||
TupleTableSlot *
|
||||
ExecAgg(Agg *node)
|
||||
{
|
||||
AggState *aggstate;
|
||||
EState *estate;
|
||||
Aggreg **aggregates;
|
||||
Plan *outerPlan;
|
||||
int i, nagg;
|
||||
Datum *value1, *value2;
|
||||
int *noInitValue;
|
||||
AggFuncInfo *aggFuncInfo;
|
||||
long nTuplesAgged = 0;
|
||||
ExprContext *econtext;
|
||||
ProjectionInfo *projInfo;
|
||||
TupleTableSlot *resultSlot;
|
||||
HeapTuple oneTuple;
|
||||
char* nulls;
|
||||
bool isDone;
|
||||
bool isNull = FALSE, isNull1 = FALSE, isNull2 = FALSE;
|
||||
|
||||
/* ---------------------
|
||||
* get state info from node
|
||||
* ---------------------
|
||||
*/
|
||||
aggstate = node->aggstate;
|
||||
if (aggstate->agg_done)
|
||||
return NULL;
|
||||
|
||||
estate = node->plan.state;
|
||||
econtext = aggstate->csstate.cstate.cs_ExprContext;
|
||||
aggregates = node->aggs;
|
||||
nagg = node->numAgg;
|
||||
|
||||
value1 = node->aggstate->csstate.cstate.cs_ExprContext->ecxt_values;
|
||||
nulls = node->aggstate->csstate.cstate.cs_ExprContext->ecxt_nulls;
|
||||
|
||||
value2 = (Datum *)palloc(sizeof(Datum) * nagg);
|
||||
memset(value2, 0, sizeof(Datum) * nagg);
|
||||
|
||||
aggFuncInfo = (AggFuncInfo *)palloc(sizeof(AggFuncInfo) * nagg);
|
||||
memset(aggFuncInfo, 0, sizeof(AggFuncInfo) * nagg);
|
||||
|
||||
noInitValue = (int *)palloc(sizeof(int) * nagg);
|
||||
memset(noInitValue, 0, sizeof(noInitValue) * nagg);
|
||||
|
||||
outerPlan = outerPlan(node);
|
||||
oneTuple = NULL;
|
||||
|
||||
projInfo = aggstate->csstate.cstate.cs_ProjInfo;
|
||||
|
||||
for(i = 0; i < nagg; i++) {
|
||||
Aggreg *agg;
|
||||
char *aggname;
|
||||
HeapTuple aggTuple;
|
||||
Form_pg_aggregate aggp;
|
||||
Oid xfn1_oid, xfn2_oid, finalfn_oid;
|
||||
func_ptr xfn1_ptr, xfn2_ptr, finalfn_ptr;
|
||||
int xfn1_nargs, xfn2_nargs, finalfn_nargs;
|
||||
|
||||
agg = aggregates[i];
|
||||
|
||||
/* ---------------------
|
||||
* find transfer functions of all the aggregates and initialize
|
||||
* their initial values
|
||||
* ---------------------
|
||||
*/
|
||||
aggname = agg->aggname;
|
||||
aggTuple = SearchSysCacheTuple(AGGNAME,
|
||||
PointerGetDatum(aggname),
|
||||
ObjectIdGetDatum(agg->basetype),
|
||||
0,0);
|
||||
if (!HeapTupleIsValid(aggTuple))
|
||||
elog(WARN, "ExecAgg: cache lookup failed for aggregate \"%s\"(%s)",
|
||||
aggname,
|
||||
tname(get_id_type(agg->basetype)));
|
||||
aggp = (Form_pg_aggregate) GETSTRUCT(aggTuple);
|
||||
|
||||
xfn1_oid = aggp->aggtransfn1;
|
||||
xfn2_oid = aggp->aggtransfn2;
|
||||
finalfn_oid = aggp->aggfinalfn;
|
||||
|
||||
if (OidIsValid(finalfn_oid)) {
|
||||
fmgr_info(finalfn_oid, &finalfn_ptr, &finalfn_nargs);
|
||||
aggFuncInfo[i].finalfn_oid = finalfn_oid;
|
||||
aggFuncInfo[i].finalfn = finalfn_ptr;
|
||||
aggFuncInfo[i].finalfn_nargs = finalfn_nargs;
|
||||
}
|
||||
|
||||
if (OidIsValid(xfn2_oid)) {
|
||||
fmgr_info(xfn2_oid, &xfn2_ptr, &xfn2_nargs);
|
||||
aggFuncInfo[i].xfn2_oid = xfn2_oid;
|
||||
aggFuncInfo[i].xfn2 = xfn2_ptr;
|
||||
aggFuncInfo[i].xfn2_nargs = xfn2_nargs;
|
||||
value2[i] = (Datum)AggNameGetInitVal((char*)aggname,
|
||||
aggp->aggbasetype,
|
||||
2,
|
||||
&isNull2);
|
||||
/* ------------------------------------------
|
||||
* If there is a second transition function, its initial
|
||||
* value must exist -- as it does not depend on data values,
|
||||
* we have no other way of determining an initial value.
|
||||
* ------------------------------------------
|
||||
*/
|
||||
if (isNull2)
|
||||
elog(WARN, "ExecAgg: agginitval2 is null");
|
||||
}
|
||||
|
||||
if (OidIsValid(xfn1_oid)) {
|
||||
fmgr_info(xfn1_oid, &xfn1_ptr, &xfn1_nargs);
|
||||
aggFuncInfo[i].xfn1_oid = xfn1_oid;
|
||||
aggFuncInfo[i].xfn1 = xfn1_ptr;
|
||||
aggFuncInfo[i].xfn1_nargs = xfn1_nargs;
|
||||
value1[i] = (Datum)AggNameGetInitVal((char*)aggname,
|
||||
aggp->aggbasetype,
|
||||
1,
|
||||
&isNull1);
|
||||
|
||||
/* ------------------------------------------
|
||||
* If the initial value for the first transition function
|
||||
* doesn't exist in the pg_aggregate table then we let
|
||||
* the first value returned from the outer procNode become
|
||||
* the initial value. (This is useful for aggregates like
|
||||
* max{} and min{}.)
|
||||
* ------------------------------------------
|
||||
*/
|
||||
if (isNull1) {
|
||||
noInitValue[i] = 1;
|
||||
nulls[i] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* for each tuple from the the outer plan, apply all the aggregates
|
||||
* ----------------
|
||||
*/
|
||||
for (;;) {
|
||||
HeapTuple outerTuple = NULL;
|
||||
TupleTableSlot *outerslot;
|
||||
|
||||
isNull = isNull1 = isNull2 = 0;
|
||||
outerslot = ExecProcNode(outerPlan, (Plan*)node);
|
||||
if (outerslot) outerTuple = outerslot->val;
|
||||
if (!HeapTupleIsValid(outerTuple)) {
|
||||
/* when the outerplan doesn't return a single tuple,
|
||||
create a dummy heaptuple anyway
|
||||
because we still need to return a valid aggregate value.
|
||||
The value returned will be the initial values of the
|
||||
transition functions */
|
||||
if (nTuplesAgged == 0) {
|
||||
TupleDesc tupType;
|
||||
Datum *tupValue;
|
||||
char* null_array;
|
||||
|
||||
tupType = aggstate->csstate.css_ScanTupleSlot->ttc_tupleDescriptor;
|
||||
tupValue = projInfo->pi_tupValue;
|
||||
|
||||
/* initially, set all the values to NULL */
|
||||
null_array = malloc(nagg);
|
||||
for (i=0;i<nagg;i++)
|
||||
null_array[i] = 'n';
|
||||
oneTuple = heap_formtuple(tupType, tupValue, null_array);
|
||||
free(null_array);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
for(i = 0; i < nagg; i++) {
|
||||
AttrNumber attnum;
|
||||
int2 attlen;
|
||||
Datum newVal;
|
||||
AggFuncInfo *aggfns = &aggFuncInfo[i];
|
||||
Datum args[2];
|
||||
|
||||
newVal = aggGetAttr(outerslot,
|
||||
aggregates[i],
|
||||
&isNull);
|
||||
|
||||
if (isNull)
|
||||
continue; /* ignore this tuple for this agg */
|
||||
|
||||
if (aggfns->xfn1) {
|
||||
if (noInitValue[i]) {
|
||||
/*
|
||||
* value1 and value2 has not been initialized. This
|
||||
* is the first non-NULL value. We use it as the
|
||||
* initial value.
|
||||
*/
|
||||
/* but we can't just use it straight, we have
|
||||
to make a copy of it since the tuple from which
|
||||
it came will be freed on the next iteration
|
||||
of the scan */
|
||||
attnum = ((Var*)aggregates[i]->target)->varattno;
|
||||
attlen = outerslot->ttc_tupleDescriptor->attrs[attnum-1]->attlen;
|
||||
if (attlen == -1) {
|
||||
/* variable length */
|
||||
attlen = VARSIZE((struct varlena*) newVal);
|
||||
}
|
||||
value1[i] = (Datum)palloc(attlen);
|
||||
if (outerslot->ttc_tupleDescriptor->attrs[attnum-1]->attbyval)
|
||||
value1[i] = newVal;
|
||||
else
|
||||
memmove((char*) (value1[i]), (char*) (newVal), attlen);
|
||||
/* value1[i] = newVal; */
|
||||
noInitValue[i] = 0;
|
||||
nulls[i] = 0;
|
||||
} else {
|
||||
/*
|
||||
* apply the transition functions.
|
||||
*/
|
||||
args[0] = value1[i];
|
||||
args[1] = newVal;
|
||||
value1[i] =
|
||||
(Datum)fmgr_c(aggfns->xfn1, aggfns->xfn1_oid,
|
||||
aggfns->xfn1_nargs, (FmgrValues *)args,
|
||||
&isNull1);
|
||||
Assert(!isNull1);
|
||||
}
|
||||
}
|
||||
|
||||
if (aggfns->xfn2) {
|
||||
Datum xfn2_val = value2[i];
|
||||
|
||||
value2[i] =
|
||||
(Datum)fmgr_c(aggfns->xfn2, aggfns->xfn2_oid,
|
||||
aggfns->xfn2_nargs,
|
||||
(FmgrValues *)&xfn2_val, &isNull2);
|
||||
Assert(!isNull2);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* keep this for the projection (we only need one of these -
|
||||
* all the tuples we aggregate over share the same group column)
|
||||
*/
|
||||
if (!oneTuple) {
|
||||
oneTuple = heap_copytuple(outerslot->val);
|
||||
}
|
||||
|
||||
nTuplesAgged++;
|
||||
}
|
||||
|
||||
/* --------------
|
||||
* finalize the aggregate (if necessary), and get the resultant value
|
||||
* --------------
|
||||
*/
|
||||
for(i = 0; i < nagg; i++) {
|
||||
char *args[2];
|
||||
AggFuncInfo *aggfns = &aggFuncInfo[i];
|
||||
|
||||
if (aggfns->finalfn && nTuplesAgged > 0) {
|
||||
if (aggfns->finalfn_nargs > 1) {
|
||||
args[0] = (char*)value1[i];
|
||||
args[1] = (char*)value2[i];
|
||||
} else if (aggfns->xfn1) {
|
||||
args[0] = (char*)value1[i];
|
||||
} else if (aggfns->xfn2) {
|
||||
args[0] = (char*)value2[i];
|
||||
} else
|
||||
elog(WARN, "ExecAgg: no valid transition functions??");
|
||||
value1[i] =
|
||||
(Datum)fmgr_c(aggfns->finalfn, aggfns->finalfn_oid,
|
||||
aggfns->finalfn_nargs, (FmgrValues *) args,
|
||||
&(nulls[i]));
|
||||
} else if (aggfns->xfn1) {
|
||||
/*
|
||||
* value in the right place, ignore. (If you remove this
|
||||
* case, fix the else part. -ay 2/95)
|
||||
*/
|
||||
} else if (aggfns->xfn2) {
|
||||
value1[i] = value2[i];
|
||||
} else
|
||||
elog(WARN, "ExecAgg: no valid transition functions??");
|
||||
}
|
||||
|
||||
/*
|
||||
* whether the aggregation is done depends on whether we are doing
|
||||
* aggregation over groups or the entire table
|
||||
*/
|
||||
if (nodeTag(outerPlan)==T_Group) {
|
||||
/* aggregation over groups */
|
||||
aggstate->agg_done = ((Group*)outerPlan)->grpstate->grp_done;
|
||||
} else {
|
||||
aggstate->agg_done = TRUE;
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* form a projection tuple, store it in the result tuple
|
||||
* slot and return it.
|
||||
* ----------------
|
||||
*/
|
||||
ExecStoreTuple(oneTuple,
|
||||
aggstate->csstate.css_ScanTupleSlot,
|
||||
InvalidBuffer,
|
||||
false);
|
||||
econtext->ecxt_scantuple = aggstate->csstate.css_ScanTupleSlot;
|
||||
resultSlot = ExecProject(projInfo, &isDone);
|
||||
|
||||
if (oneTuple)
|
||||
pfree(oneTuple);
|
||||
|
||||
return resultSlot;
|
||||
}
|
||||
|
||||
/* -----------------
|
||||
* ExecInitAgg
|
||||
*
|
||||
* Creates the run-time information for the agg node produced by the
|
||||
* planner and initializes its outer subtree
|
||||
* -----------------
|
||||
*/
|
||||
bool
|
||||
ExecInitAgg(Agg *node, EState *estate, Plan *parent)
|
||||
{
|
||||
AggState *aggstate;
|
||||
Plan *outerPlan;
|
||||
ExprContext *econtext;
|
||||
|
||||
/*
|
||||
* assign the node's execution state
|
||||
*/
|
||||
node->plan.state = estate;
|
||||
|
||||
/*
|
||||
* create state structure
|
||||
*/
|
||||
aggstate = makeNode(AggState);
|
||||
node->aggstate = aggstate;
|
||||
aggstate->agg_done = FALSE;
|
||||
|
||||
/*
|
||||
* assign node's base id and create expression context
|
||||
*/
|
||||
ExecAssignNodeBaseInfo(estate, &aggstate->csstate.cstate,
|
||||
(Plan*) parent);
|
||||
ExecAssignExprContext(estate, &aggstate->csstate.cstate);
|
||||
|
||||
#define AGG_NSLOTS 2
|
||||
/*
|
||||
* tuple table initialization
|
||||
*/
|
||||
ExecInitScanTupleSlot(estate, &aggstate->csstate);
|
||||
ExecInitResultTupleSlot(estate, &aggstate->csstate.cstate);
|
||||
|
||||
econtext = aggstate->csstate.cstate.cs_ExprContext;
|
||||
econtext->ecxt_values =
|
||||
(Datum *)palloc(sizeof(Datum) * node->numAgg);
|
||||
memset(econtext->ecxt_values, 0, sizeof(Datum) * node->numAgg);
|
||||
econtext->ecxt_nulls = (char *)palloc(node->numAgg);
|
||||
memset(econtext->ecxt_nulls, 0, node->numAgg);
|
||||
|
||||
/*
|
||||
* initializes child nodes
|
||||
*/
|
||||
outerPlan = outerPlan(node);
|
||||
ExecInitNode(outerPlan, estate, (Plan *)node);
|
||||
|
||||
/* ----------------
|
||||
* initialize tuple type.
|
||||
* ----------------
|
||||
*/
|
||||
ExecAssignScanTypeFromOuterPlan((Plan *) node, &aggstate->csstate);
|
||||
|
||||
/*
|
||||
* Initialize tuple type for both result and scan.
|
||||
* This node does no projection
|
||||
*/
|
||||
ExecAssignResultTypeFromTL((Plan*) node, &aggstate->csstate.cstate);
|
||||
ExecAssignProjectionInfo((Plan*)node, &aggstate->csstate.cstate);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int
|
||||
ExecCountSlotsAgg(Agg *node)
|
||||
{
|
||||
return ExecCountSlotsNode(outerPlan(node)) +
|
||||
ExecCountSlotsNode(innerPlan(node)) +
|
||||
AGG_NSLOTS;
|
||||
}
|
||||
|
||||
/* ------------------------
|
||||
* ExecEndAgg(node)
|
||||
*
|
||||
* -----------------------
|
||||
*/
|
||||
void
|
||||
ExecEndAgg(Agg *node)
|
||||
{
|
||||
AggState *aggstate;
|
||||
Plan *outerPlan;
|
||||
|
||||
aggstate = node->aggstate;
|
||||
|
||||
ExecFreeProjectionInfo(&aggstate->csstate.cstate);
|
||||
|
||||
outerPlan = outerPlan(node);
|
||||
ExecEndNode(outerPlan, (Plan*)node);
|
||||
|
||||
/* clean up tuple table */
|
||||
ExecClearTuple(aggstate->csstate.css_ScanTupleSlot);
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* Support Routines
|
||||
*****************************************************************************/
|
||||
|
||||
/*
|
||||
* aggGetAttr -
|
||||
* get the attribute (specified in the Var node in agg) to aggregate
|
||||
* over from the tuple
|
||||
*/
|
||||
static Datum
|
||||
aggGetAttr(TupleTableSlot *slot,
|
||||
Aggreg *agg,
|
||||
bool *isNull)
|
||||
{
|
||||
Datum result;
|
||||
AttrNumber attnum;
|
||||
HeapTuple heapTuple;
|
||||
TupleDesc tuple_type;
|
||||
Buffer buffer;
|
||||
|
||||
/* ----------------
|
||||
* extract tuple information from the slot
|
||||
* ----------------
|
||||
*/
|
||||
heapTuple = slot->val;
|
||||
tuple_type = slot->ttc_tupleDescriptor;
|
||||
buffer = slot->ttc_buffer;
|
||||
|
||||
attnum = ((Var*)agg->target)->varattno;
|
||||
|
||||
/*
|
||||
* If the attribute number is invalid, then we are supposed to
|
||||
* return the entire tuple, we give back a whole slot so that
|
||||
* callers know what the tuple looks like.
|
||||
*/
|
||||
if (attnum == InvalidAttrNumber) {
|
||||
TupleTableSlot *tempSlot;
|
||||
TupleDesc td;
|
||||
HeapTuple tup;
|
||||
|
||||
tempSlot = makeNode(TupleTableSlot);
|
||||
tempSlot->ttc_shouldFree = false;
|
||||
tempSlot->ttc_descIsNew = true;
|
||||
tempSlot->ttc_tupleDescriptor = (TupleDesc)NULL,
|
||||
tempSlot->ttc_buffer = InvalidBuffer;
|
||||
tempSlot->ttc_whichplan = -1;
|
||||
|
||||
tup = heap_copytuple(slot->val);
|
||||
td = CreateTupleDescCopy(slot->ttc_tupleDescriptor);
|
||||
|
||||
ExecSetSlotDescriptor(tempSlot, td);
|
||||
|
||||
ExecStoreTuple(tup, tempSlot, InvalidBuffer, true);
|
||||
return (Datum) tempSlot;
|
||||
}
|
||||
|
||||
result = (Datum)
|
||||
heap_getattr(heapTuple, /* tuple containing attribute */
|
||||
buffer, /* buffer associated with tuple */
|
||||
attnum, /* attribute number of desired attribute */
|
||||
tuple_type, /* tuple descriptor of tuple */
|
||||
isNull); /* return: is attribute null? */
|
||||
|
||||
/* ----------------
|
||||
* return null if att is null
|
||||
* ----------------
|
||||
*/
|
||||
if (*isNull)
|
||||
return (Datum) NULL;
|
||||
|
||||
return result;
|
||||
}
|
21
src/backend/executor/nodeAgg.h
Normal file
21
src/backend/executor/nodeAgg.h
Normal file
@ -0,0 +1,21 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* nodeAgg.h--
|
||||
*
|
||||
*
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: nodeAgg.h,v 1.1.1.1 1996/07/09 06:21:26 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef NODEAGG_H
|
||||
#define NODEAGG_H
|
||||
|
||||
extern TupleTableSlot *ExecAgg(Agg *node);
|
||||
extern bool ExecInitAgg(Agg *node, EState *estate, Plan *parent);
|
||||
extern int ExecCountSlotsAgg(Agg *node);
|
||||
extern void ExecEndAgg(Agg *node);
|
||||
|
||||
#endif /* NODEAGG_H */
|
483
src/backend/executor/nodeAppend.c
Normal file
483
src/backend/executor/nodeAppend.c
Normal file
@ -0,0 +1,483 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* nodeAppend.c--
|
||||
* routines to handle append nodes.
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/nodeAppend.c,v 1.1.1.1 1996/07/09 06:21:26 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
/* INTERFACE ROUTINES
|
||||
* ExecInitAppend - initialize the append node
|
||||
* ExecProcAppend - retrieve the next tuple from the node
|
||||
* ExecEndAppend - shut down the append node
|
||||
*
|
||||
* NOTES
|
||||
* Each append node contains a list of one or more subplans which
|
||||
* must be iteratively processed (forwards or backwards).
|
||||
* Tuples are retrieved by executing the 'whichplan'th subplan
|
||||
* until the subplan stops returning tuples, at which point that
|
||||
* plan is shut down and the next started up.
|
||||
*
|
||||
* Append nodes don't make use of their left and right
|
||||
* subtrees, rather they maintain a list of subplans so
|
||||
* a typical append node looks like this in the plan tree:
|
||||
*
|
||||
* ...
|
||||
* /
|
||||
* Append -------+------+------+--- nil
|
||||
* / \ | | |
|
||||
* nil nil ... ... ...
|
||||
* subplans
|
||||
*
|
||||
* Append nodes are currently used to support inheritance
|
||||
* queries, where several relations need to be scanned.
|
||||
* For example, in our standard person/student/employee/student-emp
|
||||
* example, where student and employee inherit from person
|
||||
* and student-emp inherits from student and employee, the
|
||||
* query:
|
||||
*
|
||||
* retrieve (e.name) from e in person*
|
||||
*
|
||||
* generates the plan:
|
||||
*
|
||||
* |
|
||||
* Append -------+-------+--------+--------+
|
||||
* / \ | | | |
|
||||
* nil nil Scan Scan Scan Scan
|
||||
* | | | |
|
||||
* person employee student student-emp
|
||||
*/
|
||||
|
||||
#include "executor/executor.h"
|
||||
#include "executor/nodeAppend.h"
|
||||
#include "executor/nodeIndexscan.h"
|
||||
#include "utils/palloc.h"
|
||||
#include "parser/parsetree.h" /* for rt_store() macro */
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* exec-append-initialize-next
|
||||
*
|
||||
* Sets up the append node state (i.e. the append state node)
|
||||
* for the "next" scan.
|
||||
*
|
||||
* Returns t iff there is a "next" scan to process.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
bool
|
||||
exec_append_initialize_next(Append *node)
|
||||
{
|
||||
EState *estate;
|
||||
AppendState *unionstate;
|
||||
TupleTableSlot *result_slot;
|
||||
List *rangeTable;
|
||||
|
||||
int whichplan;
|
||||
int nplans;
|
||||
List *rtentries;
|
||||
ResTarget *rtentry;
|
||||
|
||||
Index unionrelid;
|
||||
|
||||
/* ----------------
|
||||
* get information from the append node
|
||||
* ----------------
|
||||
*/
|
||||
estate = node->plan.state;
|
||||
unionstate = node->unionstate;
|
||||
result_slot = unionstate->cstate.cs_ResultTupleSlot;
|
||||
rangeTable = estate->es_range_table;
|
||||
|
||||
whichplan = unionstate->as_whichplan;
|
||||
nplans = unionstate->as_nplans;
|
||||
rtentries = node->unionrtentries;
|
||||
|
||||
if (whichplan < 0) {
|
||||
/* ----------------
|
||||
* if scanning in reverse, we start at
|
||||
* the last scan in the list and then
|
||||
* proceed back to the first.. in any case
|
||||
* we inform ExecProcAppend that we are
|
||||
* at the end of the line by returning FALSE
|
||||
* ----------------
|
||||
*/
|
||||
unionstate->as_whichplan = 0;
|
||||
return FALSE;
|
||||
|
||||
} else if (whichplan >= nplans) {
|
||||
/* ----------------
|
||||
* as above, end the scan if we go beyond
|
||||
* the last scan in our list..
|
||||
* ----------------
|
||||
*/
|
||||
unionstate->as_whichplan = nplans - 1;
|
||||
return FALSE;
|
||||
|
||||
} else {
|
||||
/* ----------------
|
||||
* initialize the scan
|
||||
* (and update the range table appropriately)
|
||||
* (doesn't this leave the range table hosed for anybody upstream
|
||||
* of the Append node??? - jolly )
|
||||
* ----------------
|
||||
*/
|
||||
if (node->unionrelid > 0) {
|
||||
rtentry = nth(whichplan, rtentries);
|
||||
if (rtentry == NULL)
|
||||
elog(DEBUG, "exec_append_initialize_next: rtentry is nil");
|
||||
|
||||
unionrelid = node->unionrelid;
|
||||
|
||||
rt_store(unionrelid, rangeTable, rtentry);
|
||||
|
||||
if (unionstate->as_junkFilter_list) {
|
||||
estate->es_junkFilter =
|
||||
(JunkFilter*)nth(whichplan,
|
||||
unionstate->as_junkFilter_list);
|
||||
}
|
||||
if (unionstate->as_result_relation_info_list) {
|
||||
estate->es_result_relation_info =
|
||||
(RelationInfo*) nth(whichplan,
|
||||
unionstate->as_result_relation_info_list);
|
||||
}
|
||||
result_slot->ttc_whichplan = whichplan;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecInitAppend
|
||||
*
|
||||
* Begins all of the subscans of the append node, storing the
|
||||
* scan structures in the 'initialized' vector of the append-state
|
||||
* structure.
|
||||
*
|
||||
* (This is potentially wasteful, since the entire result of the
|
||||
* append node may not be scanned, but this way all of the
|
||||
* structures get allocated in the executor's top level memory
|
||||
* block instead of that of the call to ExecProcAppend.)
|
||||
*
|
||||
* Returns the scan result of the first scan.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
bool
|
||||
ExecInitAppend(Append *node, EState *estate, Plan *parent)
|
||||
{
|
||||
AppendState *unionstate;
|
||||
int nplans;
|
||||
List *resultList;
|
||||
List *rtentries;
|
||||
List *unionplans;
|
||||
bool *initialized;
|
||||
int i;
|
||||
Plan *initNode;
|
||||
List *junkList;
|
||||
RelationInfo *es_rri = estate->es_result_relation_info;
|
||||
|
||||
/* ----------------
|
||||
* assign execution state to node and get information
|
||||
* for append state
|
||||
* ----------------
|
||||
*/
|
||||
node->plan.state = estate;
|
||||
|
||||
unionplans = node->unionplans;
|
||||
nplans = length(unionplans);
|
||||
rtentries = node->unionrtentries;
|
||||
|
||||
CXT1_printf("ExecInitAppend: context is %d\n", CurrentMemoryContext);
|
||||
initialized = (bool *)palloc(nplans * sizeof(bool));
|
||||
|
||||
/* ----------------
|
||||
* create new AppendState for our append node
|
||||
* ----------------
|
||||
*/
|
||||
unionstate = makeNode(AppendState);
|
||||
unionstate->as_whichplan = 0;
|
||||
unionstate->as_nplans = nplans;
|
||||
unionstate->as_initialized = initialized;
|
||||
unionstate->as_rtentries = rtentries;
|
||||
|
||||
node->unionstate = unionstate;
|
||||
|
||||
/* ----------------
|
||||
* Miscellanious initialization
|
||||
*
|
||||
* + assign node's base_id
|
||||
* + assign debugging hooks
|
||||
*
|
||||
* Append plans don't have expression contexts because they
|
||||
* never call ExecQual or ExecTargetList.
|
||||
* ----------------
|
||||
*/
|
||||
ExecAssignNodeBaseInfo(estate, &unionstate->cstate, parent);
|
||||
|
||||
#define APPEND_NSLOTS 1
|
||||
/* ----------------
|
||||
* append nodes still have Result slots, which hold pointers
|
||||
* to tuples, so we have to initialize them..
|
||||
* ----------------
|
||||
*/
|
||||
ExecInitResultTupleSlot(estate, &unionstate->cstate);
|
||||
|
||||
/*
|
||||
* If the inherits rtentry is the result relation, we have to make
|
||||
* a result relation info list for all inheritors so we can update
|
||||
* their indices and put the result tuples in the right place etc.
|
||||
*
|
||||
* e.g. replace p (age = p.age + 1) from p in person*
|
||||
*/
|
||||
if ((es_rri != (RelationInfo*)NULL) &&
|
||||
(node->unionrelid == es_rri->ri_RangeTableIndex))
|
||||
{
|
||||
RelationInfo *rri;
|
||||
List *rtentryP;
|
||||
|
||||
foreach(rtentryP,rtentries)
|
||||
{
|
||||
Oid reloid;
|
||||
RangeTblEntry *rtentry = lfirst(rtentryP);
|
||||
|
||||
reloid = rtentry->relid;
|
||||
rri = makeNode(RelationInfo);
|
||||
rri->ri_RangeTableIndex = es_rri->ri_RangeTableIndex;
|
||||
rri->ri_RelationDesc = heap_open(reloid);
|
||||
rri->ri_NumIndices = 0;
|
||||
rri->ri_IndexRelationDescs = NULL; /* index descs */
|
||||
rri->ri_IndexRelationInfo = NULL; /* index key info */
|
||||
|
||||
resultList = lcons(rri,resultList);
|
||||
ExecOpenIndices(reloid, rri);
|
||||
}
|
||||
unionstate->as_result_relation_info_list = resultList;
|
||||
}
|
||||
/* ----------------
|
||||
* call ExecInitNode on each of the plans in our list
|
||||
* and save the results into the array "initialized"
|
||||
* ----------------
|
||||
*/
|
||||
junkList = NIL;
|
||||
|
||||
for(i = 0; i < nplans ; i++ ) {
|
||||
JunkFilter *j;
|
||||
List *targetList;
|
||||
/* ----------------
|
||||
* NOTE: we first modify range table in
|
||||
* exec_append_initialize_next() and
|
||||
* then initialize the subnode,
|
||||
* since it may use the range table.
|
||||
* ----------------
|
||||
*/
|
||||
unionstate->as_whichplan = i;
|
||||
exec_append_initialize_next(node);
|
||||
|
||||
initNode = (Plan *) nth(i, unionplans);
|
||||
initialized[i] = ExecInitNode(initNode, estate, (Plan*) node);
|
||||
|
||||
/* ---------------
|
||||
* Each targetlist in the subplan may need its own junk filter
|
||||
*
|
||||
* This is true only when the reln being replaced/deleted is
|
||||
* the one that we're looking at the subclasses of
|
||||
* ---------------
|
||||
*/
|
||||
if ((es_rri != (RelationInfo*)NULL) &&
|
||||
(node->unionrelid == es_rri->ri_RangeTableIndex)) {
|
||||
|
||||
targetList = initNode->targetlist;
|
||||
j = (JunkFilter *) ExecInitJunkFilter(targetList);
|
||||
junkList = lappend(junkList, j);
|
||||
}
|
||||
|
||||
}
|
||||
unionstate->as_junkFilter_list = junkList;
|
||||
if (junkList != NIL)
|
||||
estate->es_junkFilter = (JunkFilter *)lfirst(junkList);
|
||||
|
||||
/* ----------------
|
||||
* initialize the return type from the appropriate subplan.
|
||||
* ----------------
|
||||
*/
|
||||
initNode = (Plan *) nth(0, unionplans);
|
||||
ExecAssignResultType(&unionstate->cstate,
|
||||
/* ExecGetExecTupDesc(initNode), */
|
||||
ExecGetTupType(initNode));
|
||||
unionstate->cstate.cs_ProjInfo = NULL;
|
||||
|
||||
/* ----------------
|
||||
* return the result from the first subplan's initialization
|
||||
* ----------------
|
||||
*/
|
||||
unionstate->as_whichplan = 0;
|
||||
exec_append_initialize_next(node);
|
||||
#if 0
|
||||
result = (List *) initialized[0];
|
||||
#endif
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int
|
||||
ExecCountSlotsAppend(Append *node)
|
||||
{
|
||||
List *plan;
|
||||
List *unionplans = node->unionplans;
|
||||
int nSlots = 0;
|
||||
|
||||
foreach (plan,unionplans) {
|
||||
nSlots += ExecCountSlotsNode((Plan *)lfirst(plan));
|
||||
}
|
||||
return nSlots + APPEND_NSLOTS;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecProcAppend
|
||||
*
|
||||
* Handles the iteration over the multiple scans.
|
||||
*
|
||||
* NOTE: Can't call this ExecAppend, that name is used in execMain.l
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
TupleTableSlot *
|
||||
ExecProcAppend(Append *node)
|
||||
{
|
||||
EState *estate;
|
||||
AppendState *unionstate;
|
||||
|
||||
int whichplan;
|
||||
List *unionplans;
|
||||
Plan *subnode;
|
||||
TupleTableSlot *result;
|
||||
TupleTableSlot *result_slot;
|
||||
ScanDirection direction;
|
||||
|
||||
/* ----------------
|
||||
* get information from the node
|
||||
* ----------------
|
||||
*/
|
||||
unionstate = node->unionstate;
|
||||
estate = node->plan.state;
|
||||
direction = estate->es_direction;
|
||||
|
||||
unionplans = node->unionplans;
|
||||
whichplan = unionstate->as_whichplan;
|
||||
result_slot = unionstate->cstate.cs_ResultTupleSlot;
|
||||
|
||||
/* ----------------
|
||||
* figure out which subplan we are currently processing
|
||||
* ----------------
|
||||
*/
|
||||
subnode = (Plan *) nth(whichplan, unionplans);
|
||||
|
||||
if (subnode == NULL)
|
||||
elog(DEBUG, "ExecProcAppend: subnode is NULL");
|
||||
|
||||
/* ----------------
|
||||
* get a tuple from the subplan
|
||||
* ----------------
|
||||
*/
|
||||
result = ExecProcNode(subnode, (Plan*)node);
|
||||
|
||||
if (! TupIsNull(result)) {
|
||||
/* ----------------
|
||||
* if the subplan gave us something then place a copy of
|
||||
* whatever we get into our result slot and return it, else..
|
||||
* ----------------
|
||||
*/
|
||||
return ExecStoreTuple(result->val,
|
||||
result_slot, result->ttc_buffer, false);
|
||||
|
||||
} else {
|
||||
/* ----------------
|
||||
* .. go on to the "next" subplan in the appropriate
|
||||
* direction and try processing again (recursively)
|
||||
* ----------------
|
||||
*/
|
||||
whichplan = unionstate->as_whichplan;
|
||||
|
||||
if (ScanDirectionIsForward(direction))
|
||||
{
|
||||
unionstate->as_whichplan = whichplan + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
unionstate->as_whichplan = whichplan - 1;
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* return something from next node or an empty slot
|
||||
* all of our subplans have been exhausted.
|
||||
* ----------------
|
||||
*/
|
||||
if (exec_append_initialize_next(node)) {
|
||||
ExecSetSlotDescriptorIsNew(result_slot, true);
|
||||
return
|
||||
ExecProcAppend(node);
|
||||
} else
|
||||
return ExecClearTuple(result_slot);
|
||||
}
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecEndAppend
|
||||
*
|
||||
* Shuts down the subscans of the append node.
|
||||
*
|
||||
* Returns nothing of interest.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
void
|
||||
ExecEndAppend(Append *node)
|
||||
{
|
||||
AppendState *unionstate;
|
||||
int nplans;
|
||||
List *unionplans;
|
||||
bool *initialized;
|
||||
int i;
|
||||
List *resultRelationInfoList;
|
||||
RelationInfo *resultRelationInfo;
|
||||
|
||||
/* ----------------
|
||||
* get information from the node
|
||||
* ----------------
|
||||
*/
|
||||
unionstate = node->unionstate;
|
||||
unionplans = node->unionplans;
|
||||
nplans = unionstate->as_nplans;
|
||||
initialized = unionstate->as_initialized;
|
||||
|
||||
/* ----------------
|
||||
* shut down each of the subscans
|
||||
* ----------------
|
||||
*/
|
||||
for(i = 0; i < nplans; i++) {
|
||||
if (initialized[i]==TRUE) {
|
||||
ExecEndNode( (Plan *) nth(i, unionplans), (Plan*)node );
|
||||
}
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* close out the different result relations
|
||||
* ----------------
|
||||
*/
|
||||
resultRelationInfoList = unionstate->as_result_relation_info_list;
|
||||
while (resultRelationInfoList != NIL) {
|
||||
Relation resultRelationDesc;
|
||||
|
||||
resultRelationInfo = (RelationInfo*) lfirst(resultRelationInfoList);
|
||||
resultRelationDesc = resultRelationInfo->ri_RelationDesc;
|
||||
heap_close(resultRelationDesc);
|
||||
pfree(resultRelationInfo);
|
||||
resultRelationInfoList = lnext(resultRelationInfoList);
|
||||
}
|
||||
if (unionstate->as_result_relation_info_list)
|
||||
pfree(unionstate->as_result_relation_info_list);
|
||||
|
||||
/* XXX should free unionstate->as_rtentries and unionstate->as_junkfilter_list here */
|
||||
}
|
||||
|
22
src/backend/executor/nodeAppend.h
Normal file
22
src/backend/executor/nodeAppend.h
Normal file
@ -0,0 +1,22 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* nodeAppend.h--
|
||||
*
|
||||
*
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: nodeAppend.h,v 1.1.1.1 1996/07/09 06:21:26 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef NODEAPPEND_H
|
||||
#define NODEAPPEND_H
|
||||
|
||||
extern bool exec_append_initialize_next(Append *node);
|
||||
extern bool ExecInitAppend(Append *node, EState *estate, Plan *parent);
|
||||
extern int ExecCountSlotsAppend(Append *node);
|
||||
extern TupleTableSlot *ExecProcAppend(Append *node);
|
||||
extern void ExecEndAppend(Append *node);
|
||||
|
||||
#endif /* NODEAPPEND_H */
|
407
src/backend/executor/nodeGroup.c
Normal file
407
src/backend/executor/nodeGroup.c
Normal file
@ -0,0 +1,407 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* nodeGroup.c--
|
||||
* Routines to handle group nodes (used for queries with GROUP BY clause).
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
* The Group node is designed for handling queries with a GROUP BY clause.
|
||||
* It's outer plan must be a sort node. It assumes that the tuples it gets
|
||||
* back from the outer plan is sorted in the order specified by the group
|
||||
* columns. (ie. tuples from the same group are consecutive)
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/nodeGroup.c,v 1.1.1.1 1996/07/09 06:21:26 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include "access/heapam.h"
|
||||
#include "catalog/catalog.h"
|
||||
#include "executor/executor.h"
|
||||
#include "executor/nodeGroup.h"
|
||||
|
||||
static TupleTableSlot *ExecGroupEveryTuple(Group *node);
|
||||
static TupleTableSlot *ExecGroupOneTuple(Group *node);
|
||||
static bool sameGroup(TupleTableSlot *oldslot, TupleTableSlot *newslot,
|
||||
int numCols, AttrNumber *grpColIdx, TupleDesc tupdesc);
|
||||
|
||||
/* ---------------------------------------
|
||||
* ExecGroup -
|
||||
*
|
||||
* There are two modes in which tuples are returned by ExecGroup. If
|
||||
* tuplePerGroup is TRUE, every tuple from the same group will be
|
||||
* returned, followed by a NULL at the end of each group. This is
|
||||
* useful for Agg node which needs to aggregate over tuples of the same
|
||||
* group. (eg. SELECT salary, count{*} FROM emp GROUP BY salary)
|
||||
*
|
||||
* If tuplePerGroup is FALSE, only one tuple per group is returned. The
|
||||
* tuple returned contains only the group columns. NULL is returned only
|
||||
* at the end when no more groups is present. This is useful when
|
||||
* the query does not involve aggregates. (eg. SELECT salary FROM emp
|
||||
* GROUP BY salary)
|
||||
* ------------------------------------------
|
||||
*/
|
||||
TupleTableSlot *
|
||||
ExecGroup(Group *node)
|
||||
{
|
||||
if (node->tuplePerGroup)
|
||||
return ExecGroupEveryTuple(node);
|
||||
else
|
||||
return ExecGroupOneTuple(node);
|
||||
}
|
||||
|
||||
/*
|
||||
* ExecGroupEveryTuple -
|
||||
* return every tuple with a NULL between each group
|
||||
*/
|
||||
static TupleTableSlot *
|
||||
ExecGroupEveryTuple(Group *node)
|
||||
{
|
||||
GroupState *grpstate;
|
||||
EState *estate;
|
||||
ExprContext *econtext;
|
||||
|
||||
HeapTuple outerTuple = NULL;
|
||||
TupleTableSlot *outerslot, *lastslot;
|
||||
ProjectionInfo *projInfo;
|
||||
TupleTableSlot *resultSlot;
|
||||
|
||||
bool isDone;
|
||||
|
||||
/* ---------------------
|
||||
* get state info from node
|
||||
* ---------------------
|
||||
*/
|
||||
grpstate = node->grpstate;
|
||||
if (grpstate->grp_done)
|
||||
return NULL;
|
||||
|
||||
estate = node->plan.state;
|
||||
|
||||
econtext = grpstate->csstate.cstate.cs_ExprContext;
|
||||
|
||||
if (grpstate->grp_useLastTuple) {
|
||||
/*
|
||||
* we haven't returned last tuple yet because it is not of the
|
||||
* same group
|
||||
*/
|
||||
grpstate->grp_useLastTuple = FALSE;
|
||||
|
||||
ExecStoreTuple(grpstate->grp_lastSlot->val,
|
||||
grpstate->csstate.css_ScanTupleSlot,
|
||||
grpstate->grp_lastSlot->ttc_buffer,
|
||||
false);
|
||||
} else {
|
||||
outerslot = ExecProcNode(outerPlan(node), (Plan*)node);
|
||||
if (outerslot)
|
||||
outerTuple = outerslot->val;
|
||||
if (!HeapTupleIsValid(outerTuple)) {
|
||||
grpstate->grp_done = TRUE;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* Compare with last tuple and see if this tuple is of
|
||||
* the same group.
|
||||
* ----------------
|
||||
*/
|
||||
lastslot = grpstate->csstate.css_ScanTupleSlot;
|
||||
|
||||
if (lastslot->val != NULL &&
|
||||
(!sameGroup(lastslot, outerslot,
|
||||
node->numCols, node->grpColIdx,
|
||||
ExecGetScanType(&grpstate->csstate)))) {
|
||||
/* ExecGetResultType(&grpstate->csstate.cstate)))) {*/
|
||||
|
||||
grpstate->grp_useLastTuple = TRUE;
|
||||
|
||||
/* save it for next time */
|
||||
grpstate->grp_lastSlot = outerslot;
|
||||
|
||||
/*
|
||||
* signifies the end of the group
|
||||
*/
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ExecStoreTuple(outerTuple,
|
||||
grpstate->csstate.css_ScanTupleSlot,
|
||||
outerslot->ttc_buffer,
|
||||
false);
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* form a projection tuple, store it in the result tuple
|
||||
* slot and return it.
|
||||
* ----------------
|
||||
*/
|
||||
projInfo = grpstate->csstate.cstate.cs_ProjInfo;
|
||||
|
||||
econtext->ecxt_scantuple = grpstate->csstate.css_ScanTupleSlot;
|
||||
resultSlot = ExecProject(projInfo, &isDone);
|
||||
|
||||
return resultSlot;
|
||||
}
|
||||
|
||||
/*
|
||||
* ExecGroupOneTuple -
|
||||
* returns one tuple per group, a NULL at the end when there are no more
|
||||
* tuples.
|
||||
*/
|
||||
static TupleTableSlot *
|
||||
ExecGroupOneTuple(Group *node)
|
||||
{
|
||||
GroupState *grpstate;
|
||||
EState *estate;
|
||||
ExprContext *econtext;
|
||||
|
||||
HeapTuple outerTuple = NULL;
|
||||
TupleTableSlot *outerslot, *lastslot;
|
||||
ProjectionInfo *projInfo;
|
||||
TupleTableSlot *resultSlot;
|
||||
|
||||
bool isDone;
|
||||
|
||||
/* ---------------------
|
||||
* get state info from node
|
||||
* ---------------------
|
||||
*/
|
||||
grpstate = node->grpstate;
|
||||
if (grpstate->grp_done)
|
||||
return NULL;
|
||||
|
||||
estate = node->plan.state;
|
||||
|
||||
econtext = node->grpstate->csstate.cstate.cs_ExprContext;
|
||||
|
||||
if (grpstate->grp_useLastTuple) {
|
||||
grpstate->grp_useLastTuple = FALSE;
|
||||
ExecStoreTuple(grpstate->grp_lastSlot->val,
|
||||
grpstate->csstate.css_ScanTupleSlot,
|
||||
grpstate->grp_lastSlot->ttc_buffer,
|
||||
false);
|
||||
} else {
|
||||
outerslot = ExecProcNode(outerPlan(node), (Plan*)node);
|
||||
if (outerslot) outerTuple = outerslot->val;
|
||||
if (!HeapTupleIsValid(outerTuple)) {
|
||||
grpstate->grp_done = TRUE;
|
||||
return NULL;
|
||||
}
|
||||
ExecStoreTuple(outerTuple,
|
||||
grpstate->csstate.css_ScanTupleSlot,
|
||||
outerslot->ttc_buffer,
|
||||
false);
|
||||
}
|
||||
lastslot = grpstate->csstate.css_ScanTupleSlot;
|
||||
|
||||
/*
|
||||
* find all tuples that belong to a group
|
||||
*/
|
||||
for(;;) {
|
||||
outerslot = ExecProcNode(outerPlan(node), (Plan*)node);
|
||||
outerTuple = (outerslot) ? outerslot->val : NULL;
|
||||
if (!HeapTupleIsValid(outerTuple)) {
|
||||
/*
|
||||
* we have at least one tuple (lastslot) if we reach here
|
||||
*/
|
||||
grpstate->grp_done = TRUE;
|
||||
|
||||
/* return lastslot */
|
||||
break;
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* Compare with last tuple and see if this tuple is of
|
||||
* the same group.
|
||||
* ----------------
|
||||
*/
|
||||
if ((!sameGroup(lastslot, outerslot,
|
||||
node->numCols, node->grpColIdx,
|
||||
ExecGetScanType(&grpstate->csstate)))) {
|
||||
/* ExecGetResultType(&grpstate->csstate.cstate)))) {*/
|
||||
|
||||
grpstate->grp_useLastTuple = TRUE;
|
||||
|
||||
/* save it for next time */
|
||||
grpstate->grp_lastSlot = outerslot;
|
||||
|
||||
/* return lastslot */
|
||||
break;
|
||||
}
|
||||
|
||||
ExecStoreTuple(outerTuple,
|
||||
grpstate->csstate.css_ScanTupleSlot,
|
||||
outerslot->ttc_buffer,
|
||||
false);
|
||||
|
||||
lastslot = grpstate->csstate.css_ScanTupleSlot;
|
||||
}
|
||||
|
||||
ExecStoreTuple(lastslot->val,
|
||||
grpstate->csstate.css_ScanTupleSlot,
|
||||
lastslot->ttc_buffer,
|
||||
false);
|
||||
|
||||
/* ----------------
|
||||
* form a projection tuple, store it in the result tuple
|
||||
* slot and return it.
|
||||
* ----------------
|
||||
*/
|
||||
projInfo = grpstate->csstate.cstate.cs_ProjInfo;
|
||||
|
||||
econtext->ecxt_scantuple = lastslot;
|
||||
resultSlot = ExecProject(projInfo, &isDone);
|
||||
|
||||
return resultSlot;
|
||||
}
|
||||
|
||||
/* -----------------
|
||||
* ExecInitGroup
|
||||
*
|
||||
* Creates the run-time information for the group node produced by the
|
||||
* planner and initializes its outer subtree
|
||||
* -----------------
|
||||
*/
|
||||
bool
|
||||
ExecInitGroup(Group *node, EState *estate, Plan *parent)
|
||||
{
|
||||
GroupState *grpstate;
|
||||
Plan *outerPlan;
|
||||
|
||||
/*
|
||||
* assign the node's execution state
|
||||
*/
|
||||
node->plan.state = estate;
|
||||
|
||||
/*
|
||||
* create state structure
|
||||
*/
|
||||
grpstate = makeNode(GroupState);
|
||||
node->grpstate = grpstate;
|
||||
grpstate->grp_useLastTuple = FALSE;
|
||||
grpstate->grp_done = FALSE;
|
||||
|
||||
/*
|
||||
* assign node's base id and create expression context
|
||||
*/
|
||||
ExecAssignNodeBaseInfo(estate, &grpstate->csstate.cstate,
|
||||
(Plan*) parent);
|
||||
ExecAssignExprContext(estate, &grpstate->csstate.cstate);
|
||||
|
||||
#define GROUP_NSLOTS 2
|
||||
/*
|
||||
* tuple table initialization
|
||||
*/
|
||||
ExecInitScanTupleSlot(estate, &grpstate->csstate);
|
||||
ExecInitResultTupleSlot(estate, &grpstate->csstate.cstate);
|
||||
|
||||
/*
|
||||
* initializes child nodes
|
||||
*/
|
||||
outerPlan = outerPlan(node);
|
||||
ExecInitNode(outerPlan, estate, (Plan *)node);
|
||||
|
||||
/* ----------------
|
||||
* initialize tuple type.
|
||||
* ----------------
|
||||
*/
|
||||
ExecAssignScanTypeFromOuterPlan((Plan *) node, &grpstate->csstate);
|
||||
|
||||
/*
|
||||
* Initialize tuple type for both result and scan.
|
||||
* This node does no projection
|
||||
*/
|
||||
ExecAssignResultTypeFromTL((Plan*) node, &grpstate->csstate.cstate);
|
||||
ExecAssignProjectionInfo((Plan*)node, &grpstate->csstate.cstate);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int
|
||||
ExecCountSlotsGroup(Group *node)
|
||||
{
|
||||
return ExecCountSlotsNode(outerPlan(node)) + GROUP_NSLOTS;
|
||||
}
|
||||
|
||||
/* ------------------------
|
||||
* ExecEndGroup(node)
|
||||
*
|
||||
* -----------------------
|
||||
*/
|
||||
void
|
||||
ExecEndGroup(Group *node)
|
||||
{
|
||||
GroupState *grpstate;
|
||||
Plan *outerPlan;
|
||||
|
||||
grpstate = node->grpstate;
|
||||
|
||||
ExecFreeProjectionInfo(&grpstate->csstate.cstate);
|
||||
|
||||
outerPlan = outerPlan(node);
|
||||
ExecEndNode(outerPlan, (Plan*)node);
|
||||
|
||||
/* clean up tuple table */
|
||||
ExecClearTuple(grpstate->csstate.css_ScanTupleSlot);
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
/*
|
||||
* code swiped from nodeUnique.c
|
||||
*/
|
||||
static bool
|
||||
sameGroup(TupleTableSlot *oldslot,
|
||||
TupleTableSlot *newslot,
|
||||
int numCols,
|
||||
AttrNumber *grpColIdx,
|
||||
TupleDesc tupdesc)
|
||||
{
|
||||
bool isNull1,isNull2;
|
||||
char *attr1, *attr2;
|
||||
char *val1, *val2;
|
||||
int i;
|
||||
AttrNumber att;
|
||||
Oid typoutput;
|
||||
|
||||
for(i = 0; i < numCols; i++) {
|
||||
att = grpColIdx[i];
|
||||
typoutput = typtoout((Oid)tupdesc->attrs[att-1]->atttypid);
|
||||
|
||||
attr1 = heap_getattr(oldslot->val,
|
||||
InvalidBuffer,
|
||||
att,
|
||||
tupdesc,
|
||||
&isNull1);
|
||||
|
||||
attr2 = heap_getattr(newslot->val,
|
||||
InvalidBuffer,
|
||||
att,
|
||||
tupdesc,
|
||||
&isNull2);
|
||||
|
||||
if (isNull1 == isNull2) {
|
||||
if (isNull1) /* both are null, they are equal */
|
||||
continue;
|
||||
|
||||
val1 = fmgr(typoutput, attr1,
|
||||
gettypelem(tupdesc->attrs[att-1]->atttypid));
|
||||
val2 = fmgr(typoutput, attr2,
|
||||
gettypelem(tupdesc->attrs[att-1]->atttypid));
|
||||
|
||||
/* now, val1 and val2 are ascii representations so we can
|
||||
use strcmp for comparison */
|
||||
if (strcmp(val1,val2) != 0)
|
||||
return FALSE;
|
||||
} else {
|
||||
/* one is null and the other isn't, they aren't equal */
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
21
src/backend/executor/nodeGroup.h
Normal file
21
src/backend/executor/nodeGroup.h
Normal file
@ -0,0 +1,21 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* nodeGroup.h--
|
||||
* prototypes for nodeGroup.c
|
||||
*
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: nodeGroup.h,v 1.1.1.1 1996/07/09 06:21:26 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef NODEGROUP_H
|
||||
#define NODEGROUP_H
|
||||
|
||||
extern TupleTableSlot *ExecGroup(Group *node);
|
||||
extern bool ExecInitGroup(Group *node, EState *estate, Plan *parent);
|
||||
extern int ExecCountSlotsGroup(Group *node);
|
||||
extern void ExecEndGroup(Group *node);
|
||||
|
||||
#endif /* NODEGROUP_H */
|
828
src/backend/executor/nodeHash.c
Normal file
828
src/backend/executor/nodeHash.c
Normal file
@ -0,0 +1,828 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* nodeHash.c--
|
||||
* Routines to hash relations for hashjoin
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/nodeHash.c,v 1.1.1.1 1996/07/09 06:21:26 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
/*
|
||||
* INTERFACE ROUTINES
|
||||
* ExecHash - generate an in-memory hash table of the relation
|
||||
* ExecInitHash - initialize node and subnodes..
|
||||
* ExecEndHash - shutdown node and subnodes
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h> /* for sprintf() */
|
||||
#include <math.h>
|
||||
#include <sys/file.h>
|
||||
#include "storage/fd.h" /* for SEEK_ */
|
||||
#include "storage/ipc.h"
|
||||
#include "storage/bufmgr.h" /* for BLCKSZ */
|
||||
#include "executor/executor.h"
|
||||
#include "executor/nodeHash.h"
|
||||
#include "executor/nodeHashjoin.h"
|
||||
#include "utils/palloc.h"
|
||||
|
||||
extern int NBuffers;
|
||||
static int HashTBSize;
|
||||
|
||||
static void mk_hj_temp(char *tempname);
|
||||
static int hashFunc(char *key, int len);
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecHash
|
||||
*
|
||||
* build hash table for hashjoin, all do partitioning if more
|
||||
* than one batches are required.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
TupleTableSlot *
|
||||
ExecHash(Hash *node)
|
||||
{
|
||||
EState *estate;
|
||||
HashState *hashstate;
|
||||
Plan *outerNode;
|
||||
Var *hashkey;
|
||||
HashJoinTable hashtable;
|
||||
TupleTableSlot *slot;
|
||||
ExprContext *econtext;
|
||||
|
||||
int nbatch;
|
||||
File *batches;
|
||||
RelativeAddr *batchPos;
|
||||
int *batchSizes;
|
||||
int i;
|
||||
RelativeAddr *innerbatchNames;
|
||||
|
||||
/* ----------------
|
||||
* get state info from node
|
||||
* ----------------
|
||||
*/
|
||||
|
||||
hashstate = node->hashstate;
|
||||
estate = node->plan.state;
|
||||
outerNode = outerPlan(node);
|
||||
|
||||
hashtable = node->hashtable;
|
||||
if (hashtable == NULL)
|
||||
elog(WARN, "ExecHash: hash table is NULL.");
|
||||
|
||||
nbatch = hashtable->nbatch;
|
||||
|
||||
if (nbatch > 0) { /* if needs hash partition */
|
||||
innerbatchNames = (RelativeAddr *) ABSADDR(hashtable->innerbatchNames);
|
||||
|
||||
/* --------------
|
||||
* allocate space for the file descriptors of batch files
|
||||
* then open the batch files in the current processes.
|
||||
* --------------
|
||||
*/
|
||||
batches = (File*)palloc(nbatch * sizeof(File));
|
||||
for (i=0; i<nbatch; i++) {
|
||||
batches[i] = FileNameOpenFile(ABSADDR(innerbatchNames[i]),
|
||||
O_CREAT | O_RDWR, 0600);
|
||||
}
|
||||
hashstate->hashBatches = batches;
|
||||
batchPos = (RelativeAddr*) ABSADDR(hashtable->innerbatchPos);
|
||||
batchSizes = (int*) ABSADDR(hashtable->innerbatchSizes);
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* set expression context
|
||||
* ----------------
|
||||
*/
|
||||
hashkey = node->hashkey;
|
||||
econtext = hashstate->cstate.cs_ExprContext;
|
||||
|
||||
/* ----------------
|
||||
* get tuple and insert into the hash table
|
||||
* ----------------
|
||||
*/
|
||||
for (;;) {
|
||||
slot = ExecProcNode(outerNode, (Plan*)node);
|
||||
if (TupIsNull(slot))
|
||||
break;
|
||||
|
||||
econtext->ecxt_innertuple = slot;
|
||||
ExecHashTableInsert(hashtable, econtext, hashkey,
|
||||
hashstate->hashBatches);
|
||||
|
||||
ExecClearTuple(slot);
|
||||
}
|
||||
|
||||
/*
|
||||
* end of build phase, flush all the last pages of the batches.
|
||||
*/
|
||||
for (i=0; i<nbatch; i++) {
|
||||
if (FileSeek(batches[i], 0L, SEEK_END) < 0)
|
||||
perror("FileSeek");
|
||||
if (FileWrite(batches[i],ABSADDR(hashtable->batch)+i*BLCKSZ,BLCKSZ) < 0)
|
||||
perror("FileWrite");
|
||||
NDirectFileWrite++;
|
||||
}
|
||||
|
||||
/* ---------------------
|
||||
* Return the slot so that we have the tuple descriptor
|
||||
* when we need to save/restore them. -Jeff 11 July 1991
|
||||
* ---------------------
|
||||
*/
|
||||
return slot;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecInitHash
|
||||
*
|
||||
* Init routine for Hash node
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
bool
|
||||
ExecInitHash(Hash *node, EState *estate, Plan *parent)
|
||||
{
|
||||
HashState *hashstate;
|
||||
Plan *outerPlan;
|
||||
|
||||
SO1_printf("ExecInitHash: %s\n",
|
||||
"initializing hash node");
|
||||
|
||||
/* ----------------
|
||||
* assign the node's execution state
|
||||
* ----------------
|
||||
*/
|
||||
node->plan.state = estate;
|
||||
|
||||
/* ----------------
|
||||
* create state structure
|
||||
* ----------------
|
||||
*/
|
||||
hashstate = makeNode(HashState);
|
||||
node->hashstate = hashstate;
|
||||
hashstate->hashBatches = NULL;
|
||||
|
||||
/* ----------------
|
||||
* Miscellanious initialization
|
||||
*
|
||||
* + assign node's base_id
|
||||
* + assign debugging hooks and
|
||||
* + create expression context for node
|
||||
* ----------------
|
||||
*/
|
||||
ExecAssignNodeBaseInfo(estate, &hashstate->cstate, parent);
|
||||
ExecAssignExprContext(estate, &hashstate->cstate);
|
||||
|
||||
#define HASH_NSLOTS 1
|
||||
/* ----------------
|
||||
* initialize our result slot
|
||||
* ----------------
|
||||
*/
|
||||
ExecInitResultTupleSlot(estate, &hashstate->cstate);
|
||||
|
||||
/* ----------------
|
||||
* initializes child nodes
|
||||
* ----------------
|
||||
*/
|
||||
outerPlan = outerPlan(node);
|
||||
ExecInitNode(outerPlan, estate, (Plan *)node);
|
||||
|
||||
/* ----------------
|
||||
* initialize tuple type. no need to initialize projection
|
||||
* info because this node doesn't do projections
|
||||
* ----------------
|
||||
*/
|
||||
ExecAssignResultTypeFromOuterPlan((Plan *) node, &hashstate->cstate);
|
||||
hashstate->cstate.cs_ProjInfo = NULL;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int
|
||||
ExecCountSlotsHash(Hash *node)
|
||||
{
|
||||
return ExecCountSlotsNode(outerPlan(node)) +
|
||||
ExecCountSlotsNode(innerPlan(node)) +
|
||||
HASH_NSLOTS;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------
|
||||
* ExecEndHash
|
||||
*
|
||||
* clean up routine for Hash node
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
void
|
||||
ExecEndHash(Hash *node)
|
||||
{
|
||||
HashState *hashstate;
|
||||
Plan *outerPlan;
|
||||
File *batches;
|
||||
|
||||
/* ----------------
|
||||
* get info from the hash state
|
||||
* ----------------
|
||||
*/
|
||||
hashstate = node->hashstate;
|
||||
batches = hashstate->hashBatches;
|
||||
if (batches != NULL)
|
||||
pfree(batches);
|
||||
|
||||
/* ----------------
|
||||
* free projection info. no need to free result type info
|
||||
* because that came from the outer plan...
|
||||
* ----------------
|
||||
*/
|
||||
ExecFreeProjectionInfo(&hashstate->cstate);
|
||||
|
||||
/* ----------------
|
||||
* shut down the subplan
|
||||
* ----------------
|
||||
*/
|
||||
outerPlan = outerPlan(node);
|
||||
ExecEndNode(outerPlan, (Plan*)node);
|
||||
}
|
||||
|
||||
RelativeAddr
|
||||
hashTableAlloc(int size, HashJoinTable hashtable)
|
||||
{
|
||||
RelativeAddr p;
|
||||
p = hashtable->top;
|
||||
hashtable->top += size;
|
||||
return p;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecHashTableCreate
|
||||
*
|
||||
* create a hashtable in shared memory for hashjoin.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
#define NTUP_PER_BUCKET 10
|
||||
#define FUDGE_FAC 1.5
|
||||
|
||||
HashJoinTable
|
||||
ExecHashTableCreate(Hash *node)
|
||||
{
|
||||
Plan *outerNode;
|
||||
int nbatch;
|
||||
int ntuples;
|
||||
int tupsize;
|
||||
IpcMemoryId shmid;
|
||||
HashJoinTable hashtable;
|
||||
HashBucket bucket;
|
||||
int nbuckets;
|
||||
int totalbuckets;
|
||||
int bucketsize;
|
||||
int i;
|
||||
RelativeAddr *outerbatchNames;
|
||||
RelativeAddr *outerbatchPos;
|
||||
RelativeAddr *innerbatchNames;
|
||||
RelativeAddr *innerbatchPos;
|
||||
int *innerbatchSizes;
|
||||
RelativeAddr tempname;
|
||||
|
||||
nbatch = -1;
|
||||
HashTBSize = NBuffers/2;
|
||||
while (nbatch < 0) {
|
||||
/*
|
||||
* determine number of batches for the hashjoin
|
||||
*/
|
||||
HashTBSize *= 2;
|
||||
nbatch = ExecHashPartition(node);
|
||||
}
|
||||
/* ----------------
|
||||
* get information about the size of the relation
|
||||
* ----------------
|
||||
*/
|
||||
outerNode = outerPlan(node);
|
||||
ntuples = outerNode->plan_size;
|
||||
if (ntuples <= 0)
|
||||
ntuples = 1000; /* XXX just a hack */
|
||||
tupsize = outerNode->plan_width + sizeof(HeapTupleData);
|
||||
|
||||
/*
|
||||
* totalbuckets is the total number of hash buckets needed for
|
||||
* the entire relation
|
||||
*/
|
||||
totalbuckets = ceil((double)ntuples/NTUP_PER_BUCKET);
|
||||
bucketsize = LONGALIGN (NTUP_PER_BUCKET * tupsize + sizeof(*bucket));
|
||||
|
||||
/*
|
||||
* nbuckets is the number of hash buckets for the first pass
|
||||
* of hybrid hashjoin
|
||||
*/
|
||||
nbuckets = (HashTBSize - nbatch) * BLCKSZ / (bucketsize * FUDGE_FAC);
|
||||
if (totalbuckets < nbuckets)
|
||||
totalbuckets = nbuckets;
|
||||
if (nbatch == 0)
|
||||
nbuckets = totalbuckets;
|
||||
#ifdef HJDEBUG
|
||||
printf("nbatch = %d, totalbuckets = %d, nbuckets = %d\n", nbatch, totalbuckets, nbuckets);
|
||||
#endif
|
||||
|
||||
/* ----------------
|
||||
* in non-parallel machines, we don't need to put the hash table
|
||||
* in the shared memory. We just palloc it.
|
||||
* ----------------
|
||||
*/
|
||||
hashtable = (HashJoinTable)palloc((HashTBSize+1)*BLCKSZ);
|
||||
shmid = 0;
|
||||
|
||||
if (hashtable == NULL) {
|
||||
elog(WARN, "not enough memory for hashjoin.");
|
||||
}
|
||||
/* ----------------
|
||||
* initialize the hash table header
|
||||
* ----------------
|
||||
*/
|
||||
hashtable->nbuckets = nbuckets;
|
||||
hashtable->totalbuckets = totalbuckets;
|
||||
hashtable->bucketsize = bucketsize;
|
||||
hashtable->shmid = shmid;
|
||||
hashtable->top = sizeof(HashTableData);
|
||||
hashtable->bottom = HashTBSize * BLCKSZ;
|
||||
/*
|
||||
* hashtable->readbuf has to be long aligned!!!
|
||||
*/
|
||||
hashtable->readbuf = hashtable->bottom;
|
||||
hashtable->nbatch = nbatch;
|
||||
hashtable->curbatch = 0;
|
||||
hashtable->pcount = hashtable->nprocess = 0;
|
||||
if (nbatch > 0) {
|
||||
/* ---------------
|
||||
* allocate and initialize the outer batches
|
||||
* ---------------
|
||||
*/
|
||||
outerbatchNames = (RelativeAddr*)ABSADDR(
|
||||
hashTableAlloc(nbatch * sizeof(RelativeAddr), hashtable));
|
||||
outerbatchPos = (RelativeAddr*)ABSADDR(
|
||||
hashTableAlloc(nbatch * sizeof(RelativeAddr), hashtable));
|
||||
for (i=0; i<nbatch; i++) {
|
||||
tempname = hashTableAlloc(12, hashtable);
|
||||
mk_hj_temp(ABSADDR(tempname));
|
||||
outerbatchNames[i] = tempname;
|
||||
outerbatchPos[i] = -1;
|
||||
}
|
||||
hashtable->outerbatchNames = RELADDR(outerbatchNames);
|
||||
hashtable->outerbatchPos = RELADDR(outerbatchPos);
|
||||
/* ---------------
|
||||
* allocate and initialize the inner batches
|
||||
* ---------------
|
||||
*/
|
||||
innerbatchNames = (RelativeAddr*)ABSADDR(
|
||||
hashTableAlloc(nbatch * sizeof(RelativeAddr), hashtable));
|
||||
innerbatchPos = (RelativeAddr*)ABSADDR(
|
||||
hashTableAlloc(nbatch * sizeof(RelativeAddr), hashtable));
|
||||
innerbatchSizes = (int*)ABSADDR(
|
||||
hashTableAlloc(nbatch * sizeof(int), hashtable));
|
||||
for (i=0; i<nbatch; i++) {
|
||||
tempname = hashTableAlloc(12, hashtable);
|
||||
mk_hj_temp(ABSADDR(tempname));
|
||||
innerbatchNames[i] = tempname;
|
||||
innerbatchPos[i] = -1;
|
||||
innerbatchSizes[i] = 0;
|
||||
}
|
||||
hashtable->innerbatchNames = RELADDR(innerbatchNames);
|
||||
hashtable->innerbatchPos = RELADDR(innerbatchPos);
|
||||
hashtable->innerbatchSizes = RELADDR(innerbatchSizes);
|
||||
}
|
||||
else {
|
||||
hashtable->outerbatchNames = (RelativeAddr)NULL;
|
||||
hashtable->outerbatchPos = (RelativeAddr)NULL;
|
||||
hashtable->innerbatchNames = (RelativeAddr)NULL;
|
||||
hashtable->innerbatchPos = (RelativeAddr)NULL;
|
||||
hashtable->innerbatchSizes = (RelativeAddr)NULL;
|
||||
}
|
||||
|
||||
hashtable->batch = (RelativeAddr)LONGALIGN(hashtable->top +
|
||||
bucketsize * nbuckets);
|
||||
hashtable->overflownext=hashtable->batch + nbatch * BLCKSZ;
|
||||
/* ----------------
|
||||
* initialize each hash bucket
|
||||
* ----------------
|
||||
*/
|
||||
bucket = (HashBucket)ABSADDR(hashtable->top);
|
||||
for (i=0; i<nbuckets; i++) {
|
||||
bucket->top = RELADDR((char*)bucket + sizeof(*bucket));
|
||||
bucket->bottom = bucket->top;
|
||||
bucket->firstotuple = bucket->lastotuple = -1;
|
||||
bucket = (HashBucket)LONGALIGN(((char*)bucket + bucketsize));
|
||||
}
|
||||
return(hashtable);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecHashTableInsert
|
||||
*
|
||||
* insert a tuple into the hash table depending on the hash value
|
||||
* it may just go to a tmp file for other batches
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
void
|
||||
ExecHashTableInsert(HashJoinTable hashtable,
|
||||
ExprContext *econtext,
|
||||
Var *hashkey,
|
||||
File *batches)
|
||||
{
|
||||
TupleTableSlot *slot;
|
||||
HeapTuple heapTuple;
|
||||
HashBucket bucket;
|
||||
int bucketno;
|
||||
int nbatch;
|
||||
int batchno;
|
||||
char *buffer;
|
||||
RelativeAddr *batchPos;
|
||||
int *batchSizes;
|
||||
char *pos;
|
||||
|
||||
nbatch = hashtable->nbatch;
|
||||
batchPos = (RelativeAddr*)ABSADDR(hashtable->innerbatchPos);
|
||||
batchSizes = (int*)ABSADDR(hashtable->innerbatchSizes);
|
||||
|
||||
slot = econtext->ecxt_innertuple;
|
||||
heapTuple = slot->val;
|
||||
|
||||
#ifdef HJDEBUG
|
||||
printf("Inserting ");
|
||||
#endif
|
||||
|
||||
bucketno = ExecHashGetBucket(hashtable, econtext, hashkey);
|
||||
|
||||
/* ----------------
|
||||
* decide whether to put the tuple in the hash table or a tmp file
|
||||
* ----------------
|
||||
*/
|
||||
if (bucketno < hashtable->nbuckets) {
|
||||
/* ---------------
|
||||
* put the tuple in hash table
|
||||
* ---------------
|
||||
*/
|
||||
bucket = (HashBucket)
|
||||
(ABSADDR(hashtable->top) + bucketno * hashtable->bucketsize);
|
||||
if ((char*)LONGALIGN(ABSADDR(bucket->bottom))
|
||||
-(char*)bucket+heapTuple->t_len > hashtable->bucketsize)
|
||||
ExecHashOverflowInsert(hashtable, bucket, heapTuple);
|
||||
else {
|
||||
memmove((char*)LONGALIGN(ABSADDR(bucket->bottom)),
|
||||
heapTuple,
|
||||
heapTuple->t_len);
|
||||
bucket->bottom =
|
||||
((RelativeAddr)LONGALIGN(bucket->bottom) + heapTuple->t_len);
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* -----------------
|
||||
* put the tuple into a tmp file for other batches
|
||||
* -----------------
|
||||
*/
|
||||
batchno = (float)(bucketno - hashtable->nbuckets)/
|
||||
(float)(hashtable->totalbuckets - hashtable->nbuckets)
|
||||
* nbatch;
|
||||
buffer = ABSADDR(hashtable->batch) + batchno * BLCKSZ;
|
||||
batchSizes[batchno]++;
|
||||
pos= (char *)
|
||||
ExecHashJoinSaveTuple(heapTuple,
|
||||
buffer,
|
||||
batches[batchno],
|
||||
(char*)ABSADDR(batchPos[batchno]));
|
||||
batchPos[batchno] = RELADDR(pos);
|
||||
}
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecHashTableDestroy
|
||||
*
|
||||
* destroy a hash table
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
void
|
||||
ExecHashTableDestroy(HashJoinTable hashtable)
|
||||
{
|
||||
pfree(hashtable);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecHashGetBucket
|
||||
*
|
||||
* Get the hash value for a tuple
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
int
|
||||
ExecHashGetBucket(HashJoinTable hashtable,
|
||||
ExprContext *econtext,
|
||||
Var *hashkey)
|
||||
{
|
||||
int bucketno;
|
||||
Datum keyval;
|
||||
bool isNull;
|
||||
|
||||
|
||||
/* ----------------
|
||||
* Get the join attribute value of the tuple
|
||||
* ----------------
|
||||
*/
|
||||
keyval = ExecEvalVar(hashkey, econtext, &isNull);
|
||||
|
||||
/* ------------------
|
||||
* compute the hash function
|
||||
* ------------------
|
||||
*/
|
||||
if (execConstByVal)
|
||||
bucketno =
|
||||
hashFunc((char *) &keyval, execConstLen) % hashtable->totalbuckets;
|
||||
else
|
||||
bucketno =
|
||||
hashFunc((char *) keyval, execConstLen) % hashtable->totalbuckets;
|
||||
#ifdef HJDEBUG
|
||||
if (bucketno >= hashtable->nbuckets)
|
||||
printf("hash(%d) = %d SAVED\n", keyval, bucketno);
|
||||
else
|
||||
printf("hash(%d) = %d\n", keyval, bucketno);
|
||||
#endif
|
||||
|
||||
return(bucketno);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecHashOverflowInsert
|
||||
*
|
||||
* insert into the overflow area of a hash bucket
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
void
|
||||
ExecHashOverflowInsert(HashJoinTable hashtable,
|
||||
HashBucket bucket,
|
||||
HeapTuple heapTuple)
|
||||
{
|
||||
OverflowTuple otuple;
|
||||
RelativeAddr newend;
|
||||
OverflowTuple firstotuple;
|
||||
OverflowTuple lastotuple;
|
||||
|
||||
firstotuple = (OverflowTuple)ABSADDR(bucket->firstotuple);
|
||||
lastotuple = (OverflowTuple)ABSADDR(bucket->lastotuple);
|
||||
/* ----------------
|
||||
* see if we run out of overflow space
|
||||
* ----------------
|
||||
*/
|
||||
newend = (RelativeAddr)LONGALIGN(hashtable->overflownext + sizeof(*otuple)
|
||||
+ heapTuple->t_len);
|
||||
if (newend > hashtable->bottom) {
|
||||
elog(DEBUG, "hash table out of memory. expanding.");
|
||||
/* ------------------
|
||||
* XXX this is a temporary hack
|
||||
* eventually, recursive hash partitioning will be
|
||||
* implemented
|
||||
* ------------------
|
||||
*/
|
||||
hashtable->readbuf = hashtable->bottom = 2 * hashtable->bottom;
|
||||
hashtable =
|
||||
(HashJoinTable)repalloc(hashtable, hashtable->bottom+BLCKSZ);
|
||||
if (hashtable == NULL) {
|
||||
perror("repalloc");
|
||||
elog(WARN, "can't expand hashtable.");
|
||||
}
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* establish the overflow chain
|
||||
* ----------------
|
||||
*/
|
||||
otuple = (OverflowTuple)ABSADDR(hashtable->overflownext);
|
||||
hashtable->overflownext = newend;
|
||||
if (firstotuple == NULL)
|
||||
bucket->firstotuple = bucket->lastotuple = RELADDR(otuple);
|
||||
else {
|
||||
lastotuple->next = RELADDR(otuple);
|
||||
bucket->lastotuple = RELADDR(otuple);
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* copy the tuple into the overflow area
|
||||
* ----------------
|
||||
*/
|
||||
otuple->next = -1;
|
||||
otuple->tuple = RELADDR(LONGALIGN(((char*)otuple + sizeof(*otuple))));
|
||||
memmove(ABSADDR(otuple->tuple),
|
||||
heapTuple,
|
||||
heapTuple->t_len);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecScanHashBucket
|
||||
*
|
||||
* scan a hash bucket of matches
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
HeapTuple
|
||||
ExecScanHashBucket(HashJoinState *hjstate,
|
||||
HashBucket bucket,
|
||||
HeapTuple curtuple,
|
||||
List *hjclauses,
|
||||
ExprContext *econtext)
|
||||
{
|
||||
HeapTuple heapTuple;
|
||||
bool qualResult;
|
||||
OverflowTuple otuple = NULL;
|
||||
OverflowTuple curotuple;
|
||||
TupleTableSlot *inntuple;
|
||||
OverflowTuple firstotuple;
|
||||
OverflowTuple lastotuple;
|
||||
HashJoinTable hashtable;
|
||||
|
||||
hashtable = hjstate->hj_HashTable;
|
||||
firstotuple = (OverflowTuple)ABSADDR(bucket->firstotuple);
|
||||
lastotuple = (OverflowTuple)ABSADDR(bucket->lastotuple);
|
||||
|
||||
/* ----------------
|
||||
* search the hash bucket
|
||||
* ----------------
|
||||
*/
|
||||
if (curtuple == NULL || curtuple < (HeapTuple)ABSADDR(bucket->bottom)) {
|
||||
if (curtuple == NULL)
|
||||
heapTuple = (HeapTuple)
|
||||
LONGALIGN(ABSADDR(bucket->top));
|
||||
else
|
||||
heapTuple = (HeapTuple)
|
||||
LONGALIGN(((char*)curtuple+curtuple->t_len));
|
||||
|
||||
while (heapTuple < (HeapTuple)ABSADDR(bucket->bottom)) {
|
||||
|
||||
inntuple = ExecStoreTuple(heapTuple, /* tuple to store */
|
||||
hjstate->hj_HashTupleSlot, /* slot */
|
||||
InvalidBuffer,/* tuple has no buffer */
|
||||
false); /* do not pfree this tuple */
|
||||
|
||||
econtext->ecxt_innertuple = inntuple;
|
||||
qualResult = ExecQual((List*)hjclauses, econtext);
|
||||
|
||||
if (qualResult)
|
||||
return heapTuple;
|
||||
|
||||
heapTuple = (HeapTuple)
|
||||
LONGALIGN(((char*)heapTuple+heapTuple->t_len));
|
||||
}
|
||||
|
||||
if (firstotuple == NULL)
|
||||
return NULL;
|
||||
otuple = firstotuple;
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* search the overflow area of the hash bucket
|
||||
* ----------------
|
||||
*/
|
||||
if (otuple == NULL) {
|
||||
curotuple = hjstate->hj_CurOTuple;
|
||||
otuple = (OverflowTuple)ABSADDR(curotuple->next);
|
||||
}
|
||||
|
||||
while (otuple != NULL) {
|
||||
heapTuple = (HeapTuple)ABSADDR(otuple->tuple);
|
||||
|
||||
inntuple = ExecStoreTuple(heapTuple, /* tuple to store */
|
||||
hjstate->hj_HashTupleSlot, /* slot */
|
||||
InvalidBuffer, /* SP?? this tuple has no buffer */
|
||||
false); /* do not pfree this tuple */
|
||||
|
||||
econtext->ecxt_innertuple = inntuple;
|
||||
qualResult = ExecQual((List*)hjclauses, econtext);
|
||||
|
||||
if (qualResult) {
|
||||
hjstate->hj_CurOTuple = otuple;
|
||||
return heapTuple;
|
||||
}
|
||||
|
||||
otuple = (OverflowTuple)ABSADDR(otuple->next);
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* no match
|
||||
* ----------------
|
||||
*/
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* hashFunc
|
||||
*
|
||||
* the hash function, copied from Margo
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
static int
|
||||
hashFunc(char *key, int len)
|
||||
{
|
||||
register unsigned int h;
|
||||
register int l;
|
||||
register unsigned char *k;
|
||||
|
||||
/*
|
||||
* If this is a variable length type, then 'k' points
|
||||
* to a "struct varlena" and len == -1.
|
||||
* NOTE:
|
||||
* VARSIZE returns the "real" data length plus the sizeof the
|
||||
* "vl_len" attribute of varlena (the length information).
|
||||
* 'k' points to the beginning of the varlena struct, so
|
||||
* we have to use "VARDATA" to find the beginning of the "real"
|
||||
* data.
|
||||
*/
|
||||
if (len == -1) {
|
||||
l = VARSIZE(key) - VARHDRSZ;
|
||||
k = (unsigned char*) VARDATA(key);
|
||||
} else {
|
||||
l = len;
|
||||
k = (unsigned char *) key;
|
||||
}
|
||||
|
||||
h = 0;
|
||||
|
||||
/*
|
||||
* Convert string to integer
|
||||
*/
|
||||
while (l--) h = h * PRIME1 ^ (*k++);
|
||||
h %= PRIME2;
|
||||
|
||||
return (h);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecHashPartition
|
||||
*
|
||||
* determine the number of batches needed for a hashjoin
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
int
|
||||
ExecHashPartition(Hash *node)
|
||||
{
|
||||
Plan *outerNode;
|
||||
int b;
|
||||
int pages;
|
||||
int ntuples;
|
||||
int tupsize;
|
||||
|
||||
/*
|
||||
* get size information for plan node
|
||||
*/
|
||||
outerNode = outerPlan(node);
|
||||
ntuples = outerNode->plan_size;
|
||||
if (ntuples == 0) ntuples = 1000;
|
||||
tupsize = outerNode->plan_width + sizeof(HeapTupleData);
|
||||
pages = ceil((double)ntuples * tupsize * FUDGE_FAC / BLCKSZ);
|
||||
|
||||
/*
|
||||
* if amount of buffer space below hashjoin threshold,
|
||||
* return negative
|
||||
*/
|
||||
if (ceil(sqrt((double)pages)) > HashTBSize)
|
||||
return -1;
|
||||
if (pages <= HashTBSize)
|
||||
b = 0; /* fit in memory, no partitioning */
|
||||
else
|
||||
b = ceil((double)(pages - HashTBSize)/(double)(HashTBSize - 1));
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecHashTableReset
|
||||
*
|
||||
* reset hash table header for new batch
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
void
|
||||
ExecHashTableReset(HashJoinTable hashtable, int ntuples)
|
||||
{
|
||||
int i;
|
||||
HashBucket bucket;
|
||||
|
||||
hashtable->nbuckets = hashtable->totalbuckets
|
||||
= ceil((double)ntuples/NTUP_PER_BUCKET);
|
||||
|
||||
hashtable->overflownext = hashtable->top + hashtable->bucketsize *
|
||||
hashtable->nbuckets;
|
||||
|
||||
bucket = (HashBucket)ABSADDR(hashtable->top);
|
||||
for (i=0; i<hashtable->nbuckets; i++) {
|
||||
bucket->top = RELADDR((char*)bucket + sizeof(*bucket));
|
||||
bucket->bottom = bucket->top;
|
||||
bucket->firstotuple = bucket->lastotuple = -1;
|
||||
bucket = (HashBucket)((char*)bucket + hashtable->bucketsize);
|
||||
}
|
||||
hashtable->pcount = hashtable->nprocess;
|
||||
}
|
||||
|
||||
static int hjtmpcnt = 0;
|
||||
|
||||
static void
|
||||
mk_hj_temp(char *tempname)
|
||||
{
|
||||
sprintf(tempname, "HJ%d.%d", getpid(), hjtmpcnt);
|
||||
hjtmpcnt = (hjtmpcnt + 1) % 1000;
|
||||
}
|
||||
|
||||
|
||||
|
35
src/backend/executor/nodeHash.h
Normal file
35
src/backend/executor/nodeHash.h
Normal file
@ -0,0 +1,35 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* nodeHash.h--
|
||||
*
|
||||
*
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: nodeHash.h,v 1.1.1.1 1996/07/09 06:21:26 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef NODEHASH_H
|
||||
#define NODEHASH_H
|
||||
|
||||
extern TupleTableSlot *ExecHash(Hash *node);
|
||||
extern bool ExecInitHash(Hash *node, EState *estate, Plan *parent);
|
||||
extern int ExecCountSlotsHash(Hash *node);
|
||||
extern void ExecEndHash(Hash *node);
|
||||
extern RelativeAddr hashTableAlloc(int size, HashJoinTable hashtable);
|
||||
extern HashJoinTable ExecHashTableCreate(Hash *node);
|
||||
extern void ExecHashTableInsert(HashJoinTable hashtable, ExprContext *econtext,
|
||||
Var *hashkey, File *batches);
|
||||
extern void ExecHashTableDestroy(HashJoinTable hashtable);
|
||||
extern int ExecHashGetBucket(HashJoinTable hashtable, ExprContext *econtext,
|
||||
Var *hashkey);
|
||||
extern void ExecHashOverflowInsert(HashJoinTable hashtable, HashBucket bucket,
|
||||
HeapTuple heapTuple);
|
||||
extern HeapTuple ExecScanHashBucket(HashJoinState *hjstate, HashBucket bucket,
|
||||
HeapTuple curtuple, List *hjclauses,
|
||||
ExprContext *econtext);
|
||||
extern int ExecHashPartition(Hash *node);
|
||||
extern void ExecHashTableReset(HashJoinTable hashtable, int ntuples);
|
||||
|
||||
#endif /* NODEHASH_H */
|
792
src/backend/executor/nodeHashjoin.c
Normal file
792
src/backend/executor/nodeHashjoin.c
Normal file
@ -0,0 +1,792 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* nodeHashjoin.c--
|
||||
* Routines to handle hash join nodes
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/nodeHashjoin.c,v 1.1.1.1 1996/07/09 06:21:26 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include <sys/file.h>
|
||||
|
||||
#include "storage/bufmgr.h" /* for BLCKSZ */
|
||||
#include "storage/fd.h" /* for SEEK_ */
|
||||
#include "executor/executor.h"
|
||||
#include "executor/nodeHash.h"
|
||||
#include "executor/nodeHashjoin.h"
|
||||
|
||||
#include "optimizer/clauses.h" /* for get_leftop */
|
||||
|
||||
|
||||
#include "utils/palloc.h"
|
||||
|
||||
static TupleTableSlot *
|
||||
ExecHashJoinOuterGetTuple(Plan *node, Plan* parent, HashJoinState *hjstate);
|
||||
|
||||
static TupleTableSlot *
|
||||
ExecHashJoinGetSavedTuple(HashJoinState *hjstate, char *buffer,
|
||||
File file, TupleTableSlot *tupleSlot, int *block, char **position);
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecHashJoin
|
||||
*
|
||||
* This function implements the Hybrid Hashjoin algorithm.
|
||||
* recursive partitioning remains to be added.
|
||||
* Note: the relation we build hash table on is the inner
|
||||
* the other one is outer.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
TupleTableSlot * /* return: a tuple or NULL */
|
||||
ExecHashJoin(HashJoin *node)
|
||||
{
|
||||
HashJoinState *hjstate;
|
||||
EState *estate;
|
||||
Plan *outerNode;
|
||||
Hash *hashNode;
|
||||
List *hjclauses;
|
||||
Expr *clause;
|
||||
List *qual;
|
||||
ScanDirection dir;
|
||||
TupleTableSlot *inntuple;
|
||||
Var *outerVar;
|
||||
ExprContext *econtext;
|
||||
|
||||
HashJoinTable hashtable;
|
||||
int bucketno;
|
||||
HashBucket bucket;
|
||||
HeapTuple curtuple;
|
||||
|
||||
bool qualResult;
|
||||
|
||||
TupleTableSlot *outerTupleSlot;
|
||||
TupleTableSlot *innerTupleSlot;
|
||||
int nbatch;
|
||||
int curbatch;
|
||||
File *outerbatches;
|
||||
RelativeAddr *outerbatchNames;
|
||||
RelativeAddr *outerbatchPos;
|
||||
Var *innerhashkey;
|
||||
int batch;
|
||||
int batchno;
|
||||
char *buffer;
|
||||
int i;
|
||||
bool hashPhaseDone;
|
||||
char *pos;
|
||||
|
||||
/* ----------------
|
||||
* get information from HashJoin node
|
||||
* ----------------
|
||||
*/
|
||||
hjstate = node->hashjoinstate;
|
||||
hjclauses = node->hashclauses;
|
||||
clause = lfirst(hjclauses);
|
||||
estate = node->join.state;
|
||||
qual = node->join.qual;
|
||||
hashNode = (Hash *)innerPlan(node);
|
||||
outerNode = outerPlan(node);
|
||||
hashPhaseDone = node->hashdone;
|
||||
|
||||
dir = estate->es_direction;
|
||||
|
||||
/* -----------------
|
||||
* get information from HashJoin state
|
||||
* -----------------
|
||||
*/
|
||||
hashtable = hjstate->hj_HashTable;
|
||||
bucket = hjstate->hj_CurBucket;
|
||||
curtuple = hjstate->hj_CurTuple;
|
||||
|
||||
/* --------------------
|
||||
* initialize expression context
|
||||
* --------------------
|
||||
*/
|
||||
econtext = hjstate->jstate.cs_ExprContext;
|
||||
|
||||
if (hjstate->jstate.cs_TupFromTlist) {
|
||||
TupleTableSlot *result;
|
||||
bool isDone;
|
||||
|
||||
result = ExecProject(hjstate->jstate.cs_ProjInfo, &isDone);
|
||||
if (!isDone)
|
||||
return result;
|
||||
}
|
||||
/* ----------------
|
||||
* if this is the first call, build the hash table for inner relation
|
||||
* ----------------
|
||||
*/
|
||||
if (!hashPhaseDone) { /* if the hash phase not completed */
|
||||
hashtable = node->hashjointable;
|
||||
if (hashtable == NULL) { /* if the hash table has not been created */
|
||||
/* ----------------
|
||||
* create the hash table
|
||||
* ----------------
|
||||
*/
|
||||
hashtable = ExecHashTableCreate(hashNode);
|
||||
hjstate->hj_HashTable = hashtable;
|
||||
innerhashkey = hashNode->hashkey;
|
||||
hjstate->hj_InnerHashKey = innerhashkey;
|
||||
|
||||
/* ----------------
|
||||
* execute the Hash node, to build the hash table
|
||||
* ----------------
|
||||
*/
|
||||
hashNode->hashtable = hashtable;
|
||||
innerTupleSlot = ExecProcNode((Plan *)hashNode, (Plan*) node);
|
||||
}
|
||||
bucket = NULL;
|
||||
curtuple = NULL;
|
||||
curbatch = 0;
|
||||
node->hashdone = true;
|
||||
}
|
||||
nbatch = hashtable->nbatch;
|
||||
outerbatches = hjstate->hj_OuterBatches;
|
||||
if (nbatch > 0 && outerbatches == NULL) { /* if needs hash partition */
|
||||
/* -----------------
|
||||
* allocate space for file descriptors of outer batch files
|
||||
* then open the batch files in the current process
|
||||
* -----------------
|
||||
*/
|
||||
innerhashkey = hashNode->hashkey;
|
||||
hjstate->hj_InnerHashKey = innerhashkey;
|
||||
outerbatchNames = (RelativeAddr*)
|
||||
ABSADDR(hashtable->outerbatchNames);
|
||||
outerbatches = (File*)
|
||||
palloc(nbatch * sizeof(File));
|
||||
for (i=0; i<nbatch; i++) {
|
||||
outerbatches[i] = FileNameOpenFile(
|
||||
ABSADDR(outerbatchNames[i]),
|
||||
O_CREAT | O_RDWR, 0600);
|
||||
}
|
||||
hjstate->hj_OuterBatches = outerbatches;
|
||||
|
||||
/* ------------------
|
||||
* get the inner batch file descriptors from the
|
||||
* hash node
|
||||
* ------------------
|
||||
*/
|
||||
hjstate->hj_InnerBatches =
|
||||
hashNode->hashstate->hashBatches;
|
||||
}
|
||||
outerbatchPos = (RelativeAddr*)ABSADDR(hashtable->outerbatchPos);
|
||||
curbatch = hashtable->curbatch;
|
||||
outerbatchNames = (RelativeAddr*)ABSADDR(hashtable->outerbatchNames);
|
||||
|
||||
/* ----------------
|
||||
* Now get an outer tuple and probe into the hash table for matches
|
||||
* ----------------
|
||||
*/
|
||||
outerTupleSlot = hjstate->jstate.cs_OuterTupleSlot;
|
||||
outerVar = get_leftop(clause);
|
||||
|
||||
bucketno = -1; /* if bucketno remains -1, means use old outer tuple */
|
||||
if (TupIsNull(outerTupleSlot)) {
|
||||
/*
|
||||
* if the current outer tuple is nil, get a new one
|
||||
*/
|
||||
outerTupleSlot = (TupleTableSlot*)
|
||||
ExecHashJoinOuterGetTuple(outerNode, (Plan*)node, hjstate);
|
||||
|
||||
while (curbatch <= nbatch && TupIsNull(outerTupleSlot)) {
|
||||
/*
|
||||
* if the current batch runs out, switch to new batch
|
||||
*/
|
||||
curbatch = ExecHashJoinNewBatch(hjstate);
|
||||
if (curbatch > nbatch) {
|
||||
/*
|
||||
* when the last batch runs out, clean up
|
||||
*/
|
||||
ExecHashTableDestroy(hashtable);
|
||||
hjstate->hj_HashTable = NULL;
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
outerTupleSlot = (TupleTableSlot*)
|
||||
ExecHashJoinOuterGetTuple(outerNode, (Plan*)node, hjstate);
|
||||
}
|
||||
/*
|
||||
* now we get an outer tuple, find the corresponding bucket for
|
||||
* this tuple from the hash table
|
||||
*/
|
||||
econtext->ecxt_outertuple = outerTupleSlot;
|
||||
|
||||
#ifdef HJDEBUG
|
||||
printf("Probing ");
|
||||
#endif
|
||||
bucketno = ExecHashGetBucket(hashtable, econtext, outerVar);
|
||||
bucket=(HashBucket)(ABSADDR(hashtable->top)
|
||||
+ bucketno * hashtable->bucketsize);
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
/* ----------------
|
||||
* Now we've got an outer tuple and the corresponding hash bucket,
|
||||
* but this tuple may not belong to the current batch.
|
||||
* ----------------
|
||||
*/
|
||||
if (curbatch == 0 && bucketno != -1) /* if this is the first pass */
|
||||
batch = ExecHashJoinGetBatch(bucketno, hashtable, nbatch);
|
||||
else
|
||||
batch = 0;
|
||||
if (batch > 0) {
|
||||
/*
|
||||
* if the current outer tuple does not belong to
|
||||
* the current batch, save to the tmp file for
|
||||
* the corresponding batch.
|
||||
*/
|
||||
buffer = ABSADDR(hashtable->batch) + (batch - 1) * BLCKSZ;
|
||||
batchno = batch - 1;
|
||||
pos = ExecHashJoinSaveTuple(outerTupleSlot->val,
|
||||
buffer,
|
||||
outerbatches[batchno],
|
||||
ABSADDR(outerbatchPos[batchno]));
|
||||
|
||||
outerbatchPos[batchno] = RELADDR(pos);
|
||||
}
|
||||
else if (bucket != NULL) {
|
||||
do {
|
||||
/*
|
||||
* scan the hash bucket for matches
|
||||
*/
|
||||
curtuple = ExecScanHashBucket(hjstate,
|
||||
bucket,
|
||||
curtuple,
|
||||
hjclauses,
|
||||
econtext);
|
||||
|
||||
if (curtuple != NULL) {
|
||||
/*
|
||||
* we've got a match, but still need to test qpqual
|
||||
*/
|
||||
inntuple = ExecStoreTuple(curtuple,
|
||||
hjstate->hj_HashTupleSlot,
|
||||
InvalidBuffer,
|
||||
false); /* don't pfree this tuple */
|
||||
|
||||
econtext->ecxt_innertuple = inntuple;
|
||||
|
||||
/* ----------------
|
||||
* test to see if we pass the qualification
|
||||
* ----------------
|
||||
*/
|
||||
qualResult = ExecQual((List*)qual, econtext);
|
||||
|
||||
/* ----------------
|
||||
* if we pass the qual, then save state for next call and
|
||||
* have ExecProject form the projection, store it
|
||||
* in the tuple table, and return the slot.
|
||||
* ----------------
|
||||
*/
|
||||
if (qualResult) {
|
||||
ProjectionInfo *projInfo;
|
||||
TupleTableSlot *result;
|
||||
bool isDone;
|
||||
|
||||
hjstate->hj_CurBucket = bucket;
|
||||
hjstate->hj_CurTuple = curtuple;
|
||||
hashtable->curbatch = curbatch;
|
||||
hjstate->jstate.cs_OuterTupleSlot = outerTupleSlot;
|
||||
|
||||
projInfo = hjstate->jstate.cs_ProjInfo;
|
||||
result = ExecProject(projInfo, &isDone);
|
||||
hjstate->jstate.cs_TupFromTlist = !isDone;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
while (curtuple != NULL);
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* Now the current outer tuple has run out of matches,
|
||||
* so we free it and get a new outer tuple.
|
||||
* ----------------
|
||||
*/
|
||||
outerTupleSlot = (TupleTableSlot*)
|
||||
ExecHashJoinOuterGetTuple(outerNode, (Plan*) node, hjstate);
|
||||
|
||||
while (curbatch <= nbatch && TupIsNull(outerTupleSlot)) {
|
||||
/*
|
||||
* if the current batch runs out, switch to new batch
|
||||
*/
|
||||
curbatch = ExecHashJoinNewBatch(hjstate);
|
||||
if (curbatch > nbatch) {
|
||||
/*
|
||||
* when the last batch runs out, clean up
|
||||
*/
|
||||
ExecHashTableDestroy(hashtable);
|
||||
hjstate->hj_HashTable = NULL;
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
outerTupleSlot = (TupleTableSlot*)
|
||||
ExecHashJoinOuterGetTuple(outerNode, (Plan*)node, hjstate);
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* Now get the corresponding hash bucket for the new
|
||||
* outer tuple.
|
||||
* ----------------
|
||||
*/
|
||||
econtext->ecxt_outertuple = outerTupleSlot;
|
||||
#ifdef HJDEBUG
|
||||
printf("Probing ");
|
||||
#endif
|
||||
bucketno = ExecHashGetBucket(hashtable, econtext, outerVar);
|
||||
bucket=(HashBucket)(ABSADDR(hashtable->top)
|
||||
+ bucketno * hashtable->bucketsize);
|
||||
curtuple = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecInitHashJoin
|
||||
*
|
||||
* Init routine for HashJoin node.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
bool /* return: initialization status */
|
||||
ExecInitHashJoin(HashJoin *node, EState *estate, Plan *parent)
|
||||
{
|
||||
HashJoinState *hjstate;
|
||||
Plan *outerNode;
|
||||
Hash *hashNode;
|
||||
|
||||
/* ----------------
|
||||
* assign the node's execution state
|
||||
* ----------------
|
||||
*/
|
||||
node->join.state = estate;
|
||||
|
||||
/* ----------------
|
||||
* create state structure
|
||||
* ----------------
|
||||
*/
|
||||
hjstate = makeNode(HashJoinState);
|
||||
|
||||
node->hashjoinstate = hjstate;
|
||||
|
||||
/* ----------------
|
||||
* Miscellanious initialization
|
||||
*
|
||||
* + assign node's base_id
|
||||
* + assign debugging hooks and
|
||||
* + create expression context for node
|
||||
* ----------------
|
||||
*/
|
||||
ExecAssignNodeBaseInfo(estate, &hjstate->jstate, parent);
|
||||
ExecAssignExprContext(estate, &hjstate->jstate);
|
||||
|
||||
#define HASHJOIN_NSLOTS 2
|
||||
/* ----------------
|
||||
* tuple table initialization
|
||||
* ----------------
|
||||
*/
|
||||
ExecInitResultTupleSlot(estate, &hjstate->jstate);
|
||||
ExecInitOuterTupleSlot(estate, hjstate);
|
||||
|
||||
/* ----------------
|
||||
* initializes child nodes
|
||||
* ----------------
|
||||
*/
|
||||
outerNode = outerPlan((Plan *)node);
|
||||
hashNode = (Hash*)innerPlan((Plan *)node);
|
||||
|
||||
ExecInitNode(outerNode, estate, (Plan *) node);
|
||||
ExecInitNode((Plan*)hashNode, estate, (Plan *) node);
|
||||
|
||||
/* ----------------
|
||||
* now for some voodoo. our temporary tuple slot
|
||||
* is actually the result tuple slot of the Hash node
|
||||
* (which is our inner plan). we do this because Hash
|
||||
* nodes don't return tuples via ExecProcNode() -- instead
|
||||
* the hash join node uses ExecScanHashBucket() to get
|
||||
* at the contents of the hash table. -cim 6/9/91
|
||||
* ----------------
|
||||
*/
|
||||
{
|
||||
HashState *hashstate = hashNode->hashstate;
|
||||
TupleTableSlot *slot =
|
||||
hashstate->cstate.cs_ResultTupleSlot;
|
||||
hjstate->hj_HashTupleSlot = slot;
|
||||
}
|
||||
hjstate->hj_OuterTupleSlot->ttc_tupleDescriptor =
|
||||
ExecGetTupType(outerNode);
|
||||
|
||||
/*
|
||||
hjstate->hj_OuterTupleSlot->ttc_execTupDescriptor =
|
||||
ExecGetExecTupDesc(outerNode);
|
||||
*/
|
||||
|
||||
/* ----------------
|
||||
* initialize tuple type and projection info
|
||||
* ----------------
|
||||
*/
|
||||
ExecAssignResultTypeFromTL((Plan*) node, &hjstate->jstate);
|
||||
ExecAssignProjectionInfo((Plan*) node, &hjstate->jstate);
|
||||
|
||||
/* ----------------
|
||||
* XXX comment me
|
||||
* ----------------
|
||||
*/
|
||||
|
||||
node->hashdone = false;
|
||||
|
||||
hjstate->hj_HashTable = (HashJoinTable)NULL;
|
||||
hjstate->hj_HashTableShmId = (IpcMemoryId)0;
|
||||
hjstate->hj_CurBucket = (HashBucket )NULL;
|
||||
hjstate->hj_CurTuple = (HeapTuple )NULL;
|
||||
hjstate->hj_CurOTuple = (OverflowTuple )NULL;
|
||||
hjstate->hj_InnerHashKey = (Var*)NULL;
|
||||
hjstate->hj_OuterBatches = (File*)NULL;
|
||||
hjstate->hj_InnerBatches = (File*)NULL;
|
||||
hjstate->hj_OuterReadPos = (char*)NULL;
|
||||
hjstate->hj_OuterReadBlk = (int)0;
|
||||
|
||||
hjstate->jstate.cs_OuterTupleSlot = (TupleTableSlot*) NULL;
|
||||
hjstate->jstate.cs_TupFromTlist = (bool) false;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int
|
||||
ExecCountSlotsHashJoin(HashJoin *node)
|
||||
{
|
||||
return ExecCountSlotsNode(outerPlan(node)) +
|
||||
ExecCountSlotsNode(innerPlan(node)) +
|
||||
HASHJOIN_NSLOTS;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecEndHashJoin
|
||||
*
|
||||
* clean up routine for HashJoin node
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
void
|
||||
ExecEndHashJoin(HashJoin *node)
|
||||
{
|
||||
HashJoinState *hjstate;
|
||||
|
||||
/* ----------------
|
||||
* get info from the HashJoin state
|
||||
* ----------------
|
||||
*/
|
||||
hjstate = node->hashjoinstate;
|
||||
|
||||
/* ----------------
|
||||
* free hash table in case we end plan before all tuples are retrieved
|
||||
* ---------------
|
||||
*/
|
||||
if (hjstate->hj_HashTable) {
|
||||
ExecHashTableDestroy(hjstate->hj_HashTable);
|
||||
hjstate->hj_HashTable = NULL;
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* Free the projection info and the scan attribute info
|
||||
*
|
||||
* Note: we don't ExecFreeResultType(hjstate)
|
||||
* because the rule manager depends on the tupType
|
||||
* returned by ExecMain(). So for now, this
|
||||
* is freed at end-transaction time. -cim 6/2/91
|
||||
* ----------------
|
||||
*/
|
||||
ExecFreeProjectionInfo(&hjstate->jstate);
|
||||
|
||||
/* ----------------
|
||||
* clean up subtrees
|
||||
* ----------------
|
||||
*/
|
||||
ExecEndNode(outerPlan((Plan *) node), (Plan*)node);
|
||||
ExecEndNode(innerPlan((Plan *) node), (Plan*)node);
|
||||
|
||||
/* ----------------
|
||||
* clean out the tuple table
|
||||
* ----------------
|
||||
*/
|
||||
ExecClearTuple(hjstate->jstate.cs_ResultTupleSlot);
|
||||
ExecClearTuple(hjstate->hj_OuterTupleSlot);
|
||||
ExecClearTuple(hjstate->hj_HashTupleSlot);
|
||||
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecHashJoinOuterGetTuple
|
||||
*
|
||||
* get the next outer tuple for hashjoin: either by
|
||||
* executing a plan node as in the first pass, or from
|
||||
* the tmp files for the hashjoin batches.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
|
||||
static TupleTableSlot *
|
||||
ExecHashJoinOuterGetTuple(Plan *node, Plan* parent, HashJoinState *hjstate)
|
||||
{
|
||||
TupleTableSlot *slot;
|
||||
HashJoinTable hashtable;
|
||||
int curbatch;
|
||||
File *outerbatches;
|
||||
char *outerreadPos;
|
||||
int batchno;
|
||||
char *outerreadBuf;
|
||||
int outerreadBlk;
|
||||
|
||||
hashtable = hjstate->hj_HashTable;
|
||||
curbatch = hashtable->curbatch;
|
||||
|
||||
if (curbatch == 0) { /* if it is the first pass */
|
||||
slot = ExecProcNode(node, parent);
|
||||
return slot;
|
||||
}
|
||||
|
||||
/*
|
||||
* otherwise, read from the tmp files
|
||||
*/
|
||||
outerbatches = hjstate->hj_OuterBatches;
|
||||
outerreadPos = hjstate->hj_OuterReadPos;
|
||||
outerreadBlk = hjstate->hj_OuterReadBlk;
|
||||
outerreadBuf = ABSADDR(hashtable->readbuf);
|
||||
batchno = curbatch - 1;
|
||||
|
||||
slot = ExecHashJoinGetSavedTuple(hjstate,
|
||||
outerreadBuf,
|
||||
outerbatches[batchno],
|
||||
hjstate->hj_OuterTupleSlot,
|
||||
&outerreadBlk,
|
||||
&outerreadPos);
|
||||
|
||||
hjstate->hj_OuterReadPos = outerreadPos;
|
||||
hjstate->hj_OuterReadBlk = outerreadBlk;
|
||||
|
||||
return slot;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecHashJoinGetSavedTuple
|
||||
*
|
||||
* read the next tuple from a tmp file using a certain buffer
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
|
||||
static TupleTableSlot *
|
||||
ExecHashJoinGetSavedTuple(HashJoinState *hjstate,
|
||||
char *buffer,
|
||||
File file,
|
||||
TupleTableSlot *tupleSlot,
|
||||
int *block, /* return parameter */
|
||||
char **position) /* return parameter */
|
||||
{
|
||||
char *bufstart;
|
||||
char *bufend;
|
||||
int cc;
|
||||
HeapTuple heapTuple;
|
||||
HashJoinTable hashtable;
|
||||
|
||||
hashtable = hjstate->hj_HashTable;
|
||||
bufend = buffer + *(long*)buffer;
|
||||
bufstart = (char*)(buffer + sizeof(long));
|
||||
if ((*position == NULL) || (*position >= bufend)) {
|
||||
if (*position == NULL)
|
||||
(*block) = 0;
|
||||
else
|
||||
(*block)++;
|
||||
FileSeek(file, *block * BLCKSZ, SEEK_SET);
|
||||
cc = FileRead(file, buffer, BLCKSZ);
|
||||
NDirectFileRead++;
|
||||
if (cc < 0)
|
||||
perror("FileRead");
|
||||
if (cc == 0) /* end of file */
|
||||
return NULL;
|
||||
else
|
||||
(*position) = bufstart;
|
||||
}
|
||||
heapTuple = (HeapTuple) (*position);
|
||||
(*position) = (char*)LONGALIGN(*position + heapTuple->t_len);
|
||||
|
||||
return ExecStoreTuple(heapTuple,tupleSlot,InvalidBuffer,false);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecHashJoinNewBatch
|
||||
*
|
||||
* switch to a new hashjoin batch
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
int
|
||||
ExecHashJoinNewBatch(HashJoinState *hjstate)
|
||||
{
|
||||
File *innerBatches;
|
||||
File *outerBatches;
|
||||
int *innerBatchSizes;
|
||||
Var *innerhashkey;
|
||||
HashJoinTable hashtable;
|
||||
int nbatch;
|
||||
char *readPos;
|
||||
int readBlk;
|
||||
char *readBuf;
|
||||
TupleTableSlot *slot;
|
||||
ExprContext *econtext;
|
||||
int i;
|
||||
int cc;
|
||||
int newbatch;
|
||||
|
||||
hashtable = hjstate->hj_HashTable;
|
||||
outerBatches = hjstate->hj_OuterBatches;
|
||||
innerBatches = hjstate->hj_InnerBatches;
|
||||
nbatch = hashtable->nbatch;
|
||||
newbatch = hashtable->curbatch + 1;
|
||||
|
||||
/* ------------------
|
||||
* this is the last process, so it will do the cleanup and
|
||||
* batch-switching.
|
||||
* ------------------
|
||||
*/
|
||||
if (newbatch == 1) {
|
||||
/*
|
||||
* if it is end of the first pass, flush all the last pages for
|
||||
* the batches.
|
||||
*/
|
||||
outerBatches = hjstate->hj_OuterBatches;
|
||||
for (i=0; i<nbatch; i++) {
|
||||
cc = FileSeek(outerBatches[i], 0L, SEEK_END);
|
||||
if (cc < 0)
|
||||
perror("FileSeek");
|
||||
cc = FileWrite(outerBatches[i],
|
||||
ABSADDR(hashtable->batch) + i * BLCKSZ, BLCKSZ);
|
||||
NDirectFileWrite++;
|
||||
if (cc < 0)
|
||||
perror("FileWrite");
|
||||
}
|
||||
}
|
||||
if (newbatch > 1) {
|
||||
/*
|
||||
* remove the previous outer batch
|
||||
*/
|
||||
FileUnlink(outerBatches[newbatch - 2]);
|
||||
}
|
||||
/*
|
||||
* rebuild the hash table for the new inner batch
|
||||
*/
|
||||
innerBatchSizes = (int*)ABSADDR(hashtable->innerbatchSizes);
|
||||
/* --------------
|
||||
* skip over empty inner batches
|
||||
* --------------
|
||||
*/
|
||||
while (newbatch <= nbatch && innerBatchSizes[newbatch - 1] == 0) {
|
||||
FileUnlink(outerBatches[newbatch-1]);
|
||||
FileUnlink(innerBatches[newbatch-1]);
|
||||
newbatch++;
|
||||
}
|
||||
if (newbatch > nbatch) {
|
||||
hashtable->pcount = hashtable->nprocess;
|
||||
|
||||
return newbatch;
|
||||
}
|
||||
ExecHashTableReset(hashtable, innerBatchSizes[newbatch - 1]);
|
||||
|
||||
|
||||
econtext = hjstate->jstate.cs_ExprContext;
|
||||
innerhashkey = hjstate->hj_InnerHashKey;
|
||||
readPos = NULL;
|
||||
readBlk = 0;
|
||||
readBuf = ABSADDR(hashtable->readbuf);
|
||||
|
||||
while ((slot = ExecHashJoinGetSavedTuple(hjstate,
|
||||
readBuf,
|
||||
innerBatches[newbatch-1],
|
||||
hjstate->hj_HashTupleSlot,
|
||||
&readBlk,
|
||||
&readPos))
|
||||
&& ! TupIsNull(slot)) {
|
||||
econtext->ecxt_innertuple = slot;
|
||||
ExecHashTableInsert(hashtable, econtext, innerhashkey,NULL);
|
||||
/* possible bug - glass */
|
||||
}
|
||||
|
||||
|
||||
/* -----------------
|
||||
* only the last process comes to this branch
|
||||
* now all the processes have finished the build phase
|
||||
* ----------------
|
||||
*/
|
||||
|
||||
/*
|
||||
* after we build the hash table, the inner batch is no longer needed
|
||||
*/
|
||||
FileUnlink(innerBatches[newbatch - 1]);
|
||||
hjstate->hj_OuterReadPos = NULL;
|
||||
hashtable->pcount = hashtable->nprocess;
|
||||
|
||||
hashtable->curbatch = newbatch;
|
||||
return newbatch;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecHashJoinGetBatch
|
||||
*
|
||||
* determine the batch number for a bucketno
|
||||
* +----------------+-------+-------+ ... +-------+
|
||||
* 0 nbuckets totalbuckets
|
||||
* batch 0 1 2 ...
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
int
|
||||
ExecHashJoinGetBatch(int bucketno, HashJoinTable hashtable, int nbatch)
|
||||
{
|
||||
int b;
|
||||
if (bucketno < hashtable->nbuckets || nbatch == 0)
|
||||
return 0;
|
||||
|
||||
b = (float)(bucketno - hashtable->nbuckets) /
|
||||
(float)(hashtable->totalbuckets - hashtable->nbuckets) *
|
||||
nbatch;
|
||||
return b+1;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecHashJoinSaveTuple
|
||||
*
|
||||
* save a tuple to a tmp file using a buffer.
|
||||
* the first few bytes in a page is an offset to the end
|
||||
* of the page.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
|
||||
char *
|
||||
ExecHashJoinSaveTuple(HeapTuple heapTuple,
|
||||
char *buffer,
|
||||
File file,
|
||||
char *position)
|
||||
{
|
||||
long *pageend;
|
||||
char *pagestart;
|
||||
char *pagebound;
|
||||
int cc;
|
||||
|
||||
pageend = (long*)buffer;
|
||||
pagestart = (char*)(buffer + sizeof(long));
|
||||
pagebound = buffer + BLCKSZ;
|
||||
if (position == NULL)
|
||||
position = pagestart;
|
||||
|
||||
if (position + heapTuple->t_len >= pagebound) {
|
||||
cc = FileSeek(file, 0L, SEEK_END);
|
||||
if (cc < 0)
|
||||
perror("FileSeek");
|
||||
cc = FileWrite(file, buffer, BLCKSZ);
|
||||
NDirectFileWrite++;
|
||||
if (cc < 0)
|
||||
perror("FileWrite");
|
||||
position = pagestart;
|
||||
*pageend = 0;
|
||||
}
|
||||
memmove(position, heapTuple, heapTuple->t_len);
|
||||
position = (char*)LONGALIGN(position + heapTuple->t_len);
|
||||
*pageend = position - buffer;
|
||||
|
||||
return position;
|
||||
}
|
33
src/backend/executor/nodeHashjoin.h
Normal file
33
src/backend/executor/nodeHashjoin.h
Normal file
@ -0,0 +1,33 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* nodeHashjoin.h--
|
||||
*
|
||||
*
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: nodeHashjoin.h,v 1.1.1.1 1996/07/09 06:21:26 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef NODEHASHJOIN_H
|
||||
#define NODEHASHJOIN_H
|
||||
|
||||
extern TupleTableSlot *ExecHashJoin(HashJoin *node);
|
||||
|
||||
extern bool ExecInitHashJoin(HashJoin *node, EState *estate, Plan *parent);
|
||||
|
||||
extern int ExecCountSlotsHashJoin(HashJoin *node);
|
||||
|
||||
extern void ExecEndHashJoin(HashJoin *node);
|
||||
|
||||
extern int ExecHashJoinNewBatch(HashJoinState *hjstate);
|
||||
|
||||
extern char *ExecHashJoinSaveTuple(HeapTuple heapTuple, char *buffer,
|
||||
File file, char *position);
|
||||
|
||||
extern int ExecHashJoinGetBatch(int bucketno, HashJoinTable hashtable,
|
||||
int nbatch);
|
||||
|
||||
|
||||
#endif /* NODEHASHJOIN_H */
|
902
src/backend/executor/nodeIndexscan.c
Normal file
902
src/backend/executor/nodeIndexscan.c
Normal file
@ -0,0 +1,902 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* nodeIndexscan.c--
|
||||
* Routines to support indexes and indexed scans of relations
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/nodeIndexscan.c,v 1.1.1.1 1996/07/09 06:21:26 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
/*
|
||||
* INTERFACE ROUTINES
|
||||
* ExecInsertIndexTuples inserts tuples into indices on result relation
|
||||
*
|
||||
* ExecIndexScan scans a relation using indices
|
||||
* ExecIndexNext using index to retrieve next tuple
|
||||
* ExecInitIndexScan creates and initializes state info.
|
||||
* ExecIndexReScan rescans the indexed relation.
|
||||
* ExecEndIndexScan releases all storage.
|
||||
* ExecIndexMarkPos marks scan position.
|
||||
* ExecIndexRestrPos restores scan position.
|
||||
*
|
||||
* NOTES
|
||||
* the code supporting ExecInsertIndexTuples should be
|
||||
* collected and merged with the genam stuff.
|
||||
*
|
||||
*/
|
||||
#include "executor/executor.h"
|
||||
#include "executor/nodeIndexscan.h"
|
||||
|
||||
#include "optimizer/clauses.h" /* for get_op, get_leftop, get_rightop */
|
||||
#include "parser/parsetree.h" /* for rt_fetch() */
|
||||
|
||||
#include "access/skey.h"
|
||||
#include "utils/palloc.h"
|
||||
#include "catalog/index.h"
|
||||
#include "storage/bufmgr.h"
|
||||
#include "storage/lmgr.h"
|
||||
#include "nodes/nodeFuncs.h"
|
||||
|
||||
/* ----------------
|
||||
* Misc stuff to move to executor.h soon -cim 6/5/90
|
||||
* ----------------
|
||||
*/
|
||||
#define NO_OP 0
|
||||
#define LEFT_OP 1
|
||||
#define RIGHT_OP 2
|
||||
|
||||
static TupleTableSlot *IndexNext(IndexScan *node);
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* IndexNext
|
||||
*
|
||||
* Retrieve a tuple from the IndexScan node's currentRelation
|
||||
* using the indices in the IndexScanState information.
|
||||
*
|
||||
* note: the old code mentions 'Primary indices'. to my knowledge
|
||||
* we only support a single secondary index. -cim 9/11/89
|
||||
*
|
||||
* old comments:
|
||||
* retrieve a tuple from relation using the indices given.
|
||||
* The indices are used in the order they appear in 'indices'.
|
||||
* The indices may be primary or secondary indices:
|
||||
* * primary index -- scan the relation 'relID' using keys supplied.
|
||||
* * secondary index -- scan the index relation to get the 'tid' for
|
||||
* a tuple in the relation 'relID'.
|
||||
* If the current index(pointed by 'indexPtr') fails to return a
|
||||
* tuple, the next index in the indices is used.
|
||||
*
|
||||
* bug fix so that it should retrieve on a null scan key.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
static TupleTableSlot *
|
||||
IndexNext(IndexScan *node)
|
||||
{
|
||||
EState *estate;
|
||||
CommonScanState *scanstate;
|
||||
IndexScanState *indexstate;
|
||||
ScanDirection direction;
|
||||
int indexPtr;
|
||||
IndexScanDescPtr scanDescs;
|
||||
IndexScanDesc scandesc;
|
||||
Relation heapRelation;
|
||||
RetrieveIndexResult result;
|
||||
ItemPointer iptr;
|
||||
HeapTuple tuple;
|
||||
TupleTableSlot *slot;
|
||||
Buffer buffer = InvalidBuffer;
|
||||
|
||||
/* ----------------
|
||||
* extract necessary information from index scan node
|
||||
* ----------------
|
||||
*/
|
||||
estate = node->scan.plan.state;
|
||||
direction = estate->es_direction;
|
||||
scanstate = node->scan.scanstate;
|
||||
indexstate = node->indxstate;
|
||||
indexPtr = indexstate->iss_IndexPtr;
|
||||
scanDescs = indexstate->iss_ScanDescs;
|
||||
scandesc = scanDescs[ indexPtr ];
|
||||
heapRelation = scanstate->css_currentRelation;
|
||||
|
||||
slot = scanstate->css_ScanTupleSlot;
|
||||
|
||||
/* ----------------
|
||||
* ok, now that we have what we need, fetch an index tuple.
|
||||
* ----------------
|
||||
*/
|
||||
|
||||
for(;;) {
|
||||
result = index_getnext(scandesc, direction);
|
||||
/* ----------------
|
||||
* if scanning this index succeeded then return the
|
||||
* appropriate heap tuple.. else return NULL.
|
||||
* ----------------
|
||||
*/
|
||||
if (result) {
|
||||
iptr = &result->heap_iptr;
|
||||
tuple = heap_fetch(heapRelation,
|
||||
NowTimeQual,
|
||||
iptr,
|
||||
&buffer);
|
||||
/* be tidy */
|
||||
pfree(result);
|
||||
|
||||
if (tuple == NULL) {
|
||||
/* ----------------
|
||||
* we found a deleted tuple, so keep on scanning..
|
||||
* ----------------
|
||||
*/
|
||||
if (BufferIsValid(buffer))
|
||||
ReleaseBuffer(buffer);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* store the scanned tuple in the scan tuple slot of
|
||||
* the scan state. Eventually we will only do this and not
|
||||
* return a tuple. Note: we pass 'false' because tuples
|
||||
* returned by amgetnext are pointers onto disk pages and
|
||||
* were not created with palloc() and so should not be pfree()'d.
|
||||
* ----------------
|
||||
*/
|
||||
ExecStoreTuple(tuple, /* tuple to store */
|
||||
slot, /* slot to store in */
|
||||
buffer, /* buffer associated with tuple */
|
||||
false); /* don't pfree */
|
||||
|
||||
return slot;
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* if we get here it means the index scan failed so we
|
||||
* are at the end of the scan..
|
||||
* ----------------
|
||||
*/
|
||||
return ExecClearTuple(slot);
|
||||
}
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecIndexScan(node)
|
||||
*
|
||||
* old comments:
|
||||
* Scans the relation using primary or secondary indices and returns
|
||||
* the next qualifying tuple in the direction specified.
|
||||
* It calls ExecScan() and passes it the access methods which returns
|
||||
* the next tuple using the indices.
|
||||
*
|
||||
* Conditions:
|
||||
* -- the "cursor" maintained by the AMI is positioned at the tuple
|
||||
* returned previously.
|
||||
*
|
||||
* Initial States:
|
||||
* -- the relation indicated is opened for scanning so that the
|
||||
* "cursor" is positioned before the first qualifying tuple.
|
||||
* -- all index realtions are opened for scanning.
|
||||
* -- indexPtr points to the first index.
|
||||
* -- state variable ruleFlag = nil.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
TupleTableSlot *
|
||||
ExecIndexScan(IndexScan *node)
|
||||
{
|
||||
TupleTableSlot *returnTuple;
|
||||
|
||||
/* ----------------
|
||||
* use IndexNext as access method
|
||||
* ----------------
|
||||
*/
|
||||
returnTuple = ExecScan(&node->scan, IndexNext);
|
||||
return returnTuple;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecIndexReScan(node)
|
||||
*
|
||||
* Recalculates the value of the scan keys whose value depends on
|
||||
* information known at runtime and rescans the indexed relation.
|
||||
* Updating the scan key was formerly done separately in
|
||||
* ExecUpdateIndexScanKeys. Integrating it into ReScan
|
||||
* makes rescans of indices and
|
||||
* relations/general streams more uniform.
|
||||
*
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
void
|
||||
ExecIndexReScan(IndexScan *node, ExprContext *exprCtxt, Plan* parent)
|
||||
{
|
||||
EState *estate;
|
||||
IndexScanState *indexstate;
|
||||
ScanDirection direction;
|
||||
IndexScanDescPtr scanDescs;
|
||||
ScanKey *scanKeys;
|
||||
IndexScanDesc sdesc;
|
||||
ScanKey skey;
|
||||
int numIndices;
|
||||
int i;
|
||||
|
||||
Pointer *runtimeKeyInfo;
|
||||
int indexPtr;
|
||||
int *numScanKeys;
|
||||
List *indxqual;
|
||||
List *qual;
|
||||
int n_keys;
|
||||
ScanKey scan_keys;
|
||||
int *run_keys;
|
||||
int j;
|
||||
Expr *clause;
|
||||
Node *scanexpr;
|
||||
Datum scanvalue;
|
||||
bool isNull;
|
||||
bool isDone;
|
||||
|
||||
indexstate = node->indxstate;
|
||||
estate = node->scan.plan.state;
|
||||
direction = estate->es_direction;
|
||||
indexstate = node->indxstate;
|
||||
numIndices = indexstate->iss_NumIndices;
|
||||
scanDescs = indexstate->iss_ScanDescs;
|
||||
scanKeys = indexstate->iss_ScanKeys;
|
||||
|
||||
runtimeKeyInfo = (Pointer *) indexstate->iss_RuntimeKeyInfo;
|
||||
|
||||
if (runtimeKeyInfo != NULL) {
|
||||
/*
|
||||
* get the index qualifications and
|
||||
* recalculate the appropriate values
|
||||
*/
|
||||
indexPtr = indexstate->iss_IndexPtr;
|
||||
indxqual = node->indxqual;
|
||||
qual = nth(indexPtr, indxqual);
|
||||
numScanKeys = indexstate->iss_NumScanKeys;
|
||||
n_keys = numScanKeys[indexPtr];
|
||||
run_keys = (int *) runtimeKeyInfo[indexPtr];
|
||||
scan_keys = (ScanKey) scanKeys[indexPtr];
|
||||
|
||||
for (j=0; j < n_keys; j++) {
|
||||
/*
|
||||
* If we have a run-time key, then extract the run-time
|
||||
* expression and evaluate it with respect to the current
|
||||
* outer tuple. We then stick the result into the scan
|
||||
* key.
|
||||
*/
|
||||
if (run_keys[j] != NO_OP) {
|
||||
clause = nth(j, qual);
|
||||
scanexpr = (run_keys[j] == RIGHT_OP) ?
|
||||
(Node*) get_rightop(clause) : (Node*) get_leftop(clause) ;
|
||||
/* pass in isDone but ignore it. We don't iterate in quals */
|
||||
scanvalue = (Datum)
|
||||
ExecEvalExpr(scanexpr, exprCtxt, &isNull, &isDone);
|
||||
scan_keys[j].sk_argument = scanvalue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* rescans all indices
|
||||
*
|
||||
* note: AMrescan assumes only one scan key. This may have
|
||||
* to change if we ever decide to support multiple keys.
|
||||
*/
|
||||
for (i = 0; i < numIndices; i++) {
|
||||
sdesc = scanDescs[ i ];
|
||||
skey = scanKeys[ i ];
|
||||
index_rescan(sdesc, direction, skey);
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* perhaps return something meaningful
|
||||
* ----------------
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecEndIndexScan
|
||||
*
|
||||
* old comments
|
||||
* Releases any storage allocated through C routines.
|
||||
* Returns nothing.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
void
|
||||
ExecEndIndexScan(IndexScan *node)
|
||||
{
|
||||
CommonScanState *scanstate;
|
||||
IndexScanState *indexstate;
|
||||
ScanKey *scanKeys;
|
||||
int numIndices;
|
||||
int i;
|
||||
|
||||
scanstate = node->scan.scanstate;
|
||||
indexstate = node->indxstate;
|
||||
|
||||
/* ----------------
|
||||
* extract information from the node
|
||||
* ----------------
|
||||
*/
|
||||
numIndices = indexstate->iss_NumIndices;
|
||||
scanKeys = indexstate->iss_ScanKeys;
|
||||
|
||||
/* ----------------
|
||||
* Free the projection info and the scan attribute info
|
||||
*
|
||||
* Note: we don't ExecFreeResultType(scanstate)
|
||||
* because the rule manager depends on the tupType
|
||||
* returned by ExecMain(). So for now, this
|
||||
* is freed at end-transaction time. -cim 6/2/91
|
||||
* ----------------
|
||||
*/
|
||||
ExecFreeProjectionInfo(&scanstate->cstate);
|
||||
|
||||
/* ----------------
|
||||
* close the heap and index relations
|
||||
* ----------------
|
||||
*/
|
||||
ExecCloseR((Plan *) node);
|
||||
|
||||
/* ----------------
|
||||
* free the scan keys used in scanning the indices
|
||||
* ----------------
|
||||
*/
|
||||
for (i=0; i<numIndices; i++) {
|
||||
if (scanKeys[i]!=NULL)
|
||||
pfree(scanKeys[i]);
|
||||
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* clear out tuple table slots
|
||||
* ----------------
|
||||
*/
|
||||
ExecClearTuple(scanstate->cstate.cs_ResultTupleSlot);
|
||||
ExecClearTuple(scanstate->css_ScanTupleSlot);
|
||||
/* ExecClearTuple(scanstate->css_RawTupleSlot); */
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecIndexMarkPos
|
||||
*
|
||||
* old comments
|
||||
* Marks scan position by marking the current index.
|
||||
* Returns nothing.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
void
|
||||
ExecIndexMarkPos(IndexScan *node)
|
||||
{
|
||||
IndexScanState *indexstate;
|
||||
IndexScanDescPtr indexScanDescs;
|
||||
IndexScanDesc scanDesc;
|
||||
int indexPtr;
|
||||
|
||||
indexstate = node->indxstate;
|
||||
indexPtr = indexstate->iss_IndexPtr;
|
||||
indexScanDescs = indexstate->iss_ScanDescs;
|
||||
scanDesc = indexScanDescs[ indexPtr ];
|
||||
|
||||
/* ----------------
|
||||
* XXX access methods don't return marked positions so
|
||||
* ----------------
|
||||
*/
|
||||
IndexScanMarkPosition( scanDesc );
|
||||
return;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecIndexRestrPos
|
||||
*
|
||||
* old comments
|
||||
* Restores scan position by restoring the current index.
|
||||
* Returns nothing.
|
||||
*
|
||||
* XXX Assumes previously marked scan position belongs to current index
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
void
|
||||
ExecIndexRestrPos(IndexScan *node)
|
||||
{
|
||||
IndexScanState *indexstate;
|
||||
IndexScanDescPtr indexScanDescs;
|
||||
IndexScanDesc scanDesc;
|
||||
int indexPtr;
|
||||
|
||||
indexstate = node->indxstate;
|
||||
indexPtr = indexstate->iss_IndexPtr;
|
||||
indexScanDescs = indexstate->iss_ScanDescs;
|
||||
scanDesc = indexScanDescs[ indexPtr ];
|
||||
|
||||
IndexScanRestorePosition( scanDesc );
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecInitIndexScan
|
||||
*
|
||||
* Initializes the index scan's state information, creates
|
||||
* scan keys, and opens the base and index relations.
|
||||
*
|
||||
* Note: index scans have 2 sets of state information because
|
||||
* we have to keep track of the base relation and the
|
||||
* index relations.
|
||||
*
|
||||
* old comments
|
||||
* Creates the run-time state information for the node and
|
||||
* sets the relation id to contain relevant decriptors.
|
||||
*
|
||||
* Parameters:
|
||||
* node: IndexNode node produced by the planner.
|
||||
* estate: the execution state initialized in InitPlan.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
bool
|
||||
ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
|
||||
{
|
||||
IndexScanState *indexstate;
|
||||
CommonScanState *scanstate;
|
||||
List *indxqual;
|
||||
List *indxid;
|
||||
int i;
|
||||
int numIndices;
|
||||
int indexPtr;
|
||||
ScanKey *scanKeys;
|
||||
int *numScanKeys;
|
||||
RelationPtr relationDescs;
|
||||
IndexScanDescPtr scanDescs;
|
||||
Pointer *runtimeKeyInfo;
|
||||
bool have_runtime_keys;
|
||||
List *rangeTable;
|
||||
RangeTblEntry *rtentry;
|
||||
Index relid;
|
||||
Oid reloid;
|
||||
TimeQual timeQual;
|
||||
|
||||
Relation currentRelation;
|
||||
HeapScanDesc currentScanDesc;
|
||||
ScanDirection direction;
|
||||
int baseid;
|
||||
|
||||
/* ----------------
|
||||
* assign execution state to node
|
||||
* ----------------
|
||||
*/
|
||||
node->scan.plan.state = estate;
|
||||
|
||||
/* --------------------------------
|
||||
* Part 1) initialize scan state
|
||||
*
|
||||
* create new CommonScanState for node
|
||||
* --------------------------------
|
||||
*/
|
||||
scanstate = makeNode(CommonScanState);
|
||||
/*
|
||||
scanstate->ss_ProcOuterFlag = false;
|
||||
scanstate->ss_OldRelId = 0;
|
||||
*/
|
||||
|
||||
node->scan.scanstate = scanstate;
|
||||
|
||||
/* ----------------
|
||||
* assign node's base_id .. we don't use AssignNodeBaseid() because
|
||||
* the increment is done later on after we assign the index scan's
|
||||
* scanstate. see below.
|
||||
* ----------------
|
||||
*/
|
||||
baseid = estate->es_BaseId;
|
||||
/* scanstate->csstate.cstate.bnode.base_id = baseid; */
|
||||
scanstate->cstate.cs_base_id = baseid;
|
||||
|
||||
/* ----------------
|
||||
* create expression context for node
|
||||
* ----------------
|
||||
*/
|
||||
ExecAssignExprContext(estate, &scanstate->cstate);
|
||||
|
||||
#define INDEXSCAN_NSLOTS 3
|
||||
/* ----------------
|
||||
* tuple table initialization
|
||||
* ----------------
|
||||
*/
|
||||
ExecInitResultTupleSlot(estate, &scanstate->cstate);
|
||||
ExecInitScanTupleSlot(estate, scanstate);
|
||||
/* ExecInitRawTupleSlot(estate, scanstate); */
|
||||
|
||||
/* ----------------
|
||||
* initialize projection info. result type comes from scan desc
|
||||
* below..
|
||||
* ----------------
|
||||
*/
|
||||
ExecAssignProjectionInfo((Plan *) node, &scanstate->cstate);
|
||||
|
||||
/* --------------------------------
|
||||
* Part 2) initialize index scan state
|
||||
*
|
||||
* create new IndexScanState for node
|
||||
* --------------------------------
|
||||
*/
|
||||
indexstate = makeNode(IndexScanState);
|
||||
indexstate->iss_NumIndices = 0;
|
||||
indexstate->iss_IndexPtr = 0;
|
||||
indexstate->iss_ScanKeys = NULL;
|
||||
indexstate->iss_NumScanKeys = NULL;
|
||||
indexstate->iss_RuntimeKeyInfo = NULL;
|
||||
indexstate->iss_RelationDescs = NULL;
|
||||
indexstate->iss_ScanDescs = NULL;
|
||||
|
||||
node->indxstate = indexstate;
|
||||
|
||||
/* ----------------
|
||||
* assign base id to index scan state also
|
||||
* ----------------
|
||||
*/
|
||||
indexstate->cstate.cs_base_id = baseid;
|
||||
baseid++;
|
||||
estate->es_BaseId = baseid;
|
||||
|
||||
/* ----------------
|
||||
* get the index node information
|
||||
* ----------------
|
||||
*/
|
||||
indxid = node->indxid;
|
||||
indxqual = node->indxqual;
|
||||
numIndices = length(indxid);
|
||||
indexPtr = 0;
|
||||
|
||||
CXT1_printf("ExecInitIndexScan: context is %d\n", CurrentMemoryContext);
|
||||
|
||||
/* ----------------
|
||||
* scanKeys is used to keep track of the ScanKey's. This is needed
|
||||
* because a single scan may use several indices and each index has
|
||||
* its own ScanKey.
|
||||
* ----------------
|
||||
*/
|
||||
numScanKeys = (int *) palloc(numIndices * sizeof(int));
|
||||
scanKeys = (ScanKey *) palloc(numIndices * sizeof(ScanKey));
|
||||
relationDescs = (RelationPtr) palloc(numIndices * sizeof(Relation));
|
||||
scanDescs = (IndexScanDescPtr) palloc(numIndices * sizeof(IndexScanDesc));
|
||||
|
||||
/* ----------------
|
||||
* initialize runtime key info.
|
||||
* ----------------
|
||||
*/
|
||||
have_runtime_keys = false;
|
||||
runtimeKeyInfo = (Pointer *)
|
||||
palloc(numIndices * sizeof(Pointer));
|
||||
|
||||
/* ----------------
|
||||
* build the index scan keys from the index qualification
|
||||
* ----------------
|
||||
*/
|
||||
for (i=0; i < numIndices; i++) {
|
||||
int j;
|
||||
List *qual;
|
||||
int n_keys;
|
||||
ScanKey scan_keys;
|
||||
int *run_keys;
|
||||
|
||||
qual = nth(i, indxqual);
|
||||
n_keys = length(qual);
|
||||
scan_keys = (n_keys <= 0) ? NULL :
|
||||
(ScanKey)palloc(n_keys * sizeof(ScanKeyData));
|
||||
|
||||
CXT1_printf("ExecInitIndexScan: context is %d\n",
|
||||
CurrentMemoryContext);
|
||||
|
||||
if (n_keys > 0) {
|
||||
run_keys = (int *) palloc(n_keys * sizeof(int));
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* for each opclause in the given qual,
|
||||
* convert each qual's opclause into a single scan key
|
||||
* ----------------
|
||||
*/
|
||||
for (j=0; j < n_keys; j++) {
|
||||
Expr *clause; /* one part of index qual */
|
||||
Oper *op; /* operator used in scan.. */
|
||||
Node *leftop; /* expr on lhs of operator */
|
||||
Node *rightop; /* expr on rhs ... */
|
||||
|
||||
int scanvar; /* which var identifies varattno */
|
||||
AttrNumber varattno; /* att number used in scan */
|
||||
Oid opid; /* operator id used in scan */
|
||||
Datum scanvalue; /* value used in scan (if const) */
|
||||
|
||||
/* ----------------
|
||||
* extract clause information from the qualification
|
||||
* ----------------
|
||||
*/
|
||||
clause = nth(j, qual);
|
||||
|
||||
op = (Oper*)clause->oper;
|
||||
if (!IsA(op,Oper))
|
||||
elog(WARN, "ExecInitIndexScan: op not an Oper!");
|
||||
|
||||
opid = op->opid;
|
||||
|
||||
/* ----------------
|
||||
* Here we figure out the contents of the index qual.
|
||||
* The usual case is (op var const) or (op const var)
|
||||
* which means we form a scan key for the attribute
|
||||
* listed in the var node and use the value of the const.
|
||||
*
|
||||
* If we don't have a const node, then it means that
|
||||
* one of the var nodes refers to the "scan" tuple and
|
||||
* is used to determine which attribute to scan, and the
|
||||
* other expression is used to calculate the value used in
|
||||
* scanning the index.
|
||||
*
|
||||
* This means our index scan's scan key is a function of
|
||||
* information obtained during the execution of the plan
|
||||
* in which case we need to recalculate the index scan key
|
||||
* at run time.
|
||||
*
|
||||
* Hence, we set have_runtime_keys to true and then set
|
||||
* the appropriate flag in run_keys to LEFT_OP or RIGHT_OP.
|
||||
* The corresponding scan keys are recomputed at run time.
|
||||
* ----------------
|
||||
*/
|
||||
|
||||
scanvar = NO_OP;
|
||||
|
||||
/* ----------------
|
||||
* determine information in leftop
|
||||
* ----------------
|
||||
*/
|
||||
leftop = (Node*) get_leftop(clause);
|
||||
|
||||
if (IsA(leftop,Var) && var_is_rel((Var*)leftop)) {
|
||||
/* ----------------
|
||||
* if the leftop is a "rel-var", then it means
|
||||
* that it is a var node which tells us which
|
||||
* attribute to use for our scan key.
|
||||
* ----------------
|
||||
*/
|
||||
varattno = ((Var*) leftop)->varattno;
|
||||
scanvar = LEFT_OP;
|
||||
} else if (IsA(leftop,Const)) {
|
||||
/* ----------------
|
||||
* if the leftop is a const node then it means
|
||||
* it identifies the value to place in our scan key.
|
||||
* ----------------
|
||||
*/
|
||||
run_keys[ j ] = NO_OP;
|
||||
scanvalue = ((Const*) leftop)->constvalue;
|
||||
} else if (leftop != NULL &&
|
||||
is_funcclause(leftop) &&
|
||||
var_is_rel(lfirst(((Expr*)leftop)->args))) {
|
||||
/* ----------------
|
||||
* if the leftop is a func node then it means
|
||||
* it identifies the value to place in our scan key.
|
||||
* Since functional indices have only one attribute
|
||||
* the attno must always be set to 1.
|
||||
* ----------------
|
||||
*/
|
||||
varattno = 1;
|
||||
scanvar = LEFT_OP;
|
||||
|
||||
} else {
|
||||
/* ----------------
|
||||
* otherwise, the leftop contains information usable
|
||||
* at runtime to figure out the value to place in our
|
||||
* scan key.
|
||||
* ----------------
|
||||
*/
|
||||
have_runtime_keys = true;
|
||||
run_keys[ j ] = LEFT_OP;
|
||||
scanvalue = Int32GetDatum((int32) true);
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* now determine information in rightop
|
||||
* ----------------
|
||||
*/
|
||||
rightop = (Node*) get_rightop(clause);
|
||||
|
||||
if (IsA(rightop,Var) && var_is_rel((Var*)rightop)) {
|
||||
/* ----------------
|
||||
* here we make sure only one op identifies the
|
||||
* scan-attribute...
|
||||
* ----------------
|
||||
*/
|
||||
if (scanvar == LEFT_OP)
|
||||
elog(WARN, "ExecInitIndexScan: %s",
|
||||
"both left and right op's are rel-vars");
|
||||
|
||||
/* ----------------
|
||||
* if the rightop is a "rel-var", then it means
|
||||
* that it is a var node which tells us which
|
||||
* attribute to use for our scan key.
|
||||
* ----------------
|
||||
*/
|
||||
varattno = ((Var*) rightop)->varattno;
|
||||
scanvar = RIGHT_OP;
|
||||
|
||||
} else if (IsA(rightop,Const)) {
|
||||
/* ----------------
|
||||
* if the leftop is a const node then it means
|
||||
* it identifies the value to place in our scan key.
|
||||
* ----------------
|
||||
*/
|
||||
run_keys[ j ] = NO_OP;
|
||||
scanvalue = ((Const*) rightop)->constvalue;
|
||||
|
||||
} else if (rightop!=NULL &&
|
||||
is_funcclause(rightop) &&
|
||||
var_is_rel(lfirst(((Expr*)rightop)->args))) {
|
||||
/* ----------------
|
||||
* if the rightop is a func node then it means
|
||||
* it identifies the value to place in our scan key.
|
||||
* Since functional indices have only one attribute
|
||||
* the attno must always be set to 1.
|
||||
* ----------------
|
||||
*/
|
||||
if (scanvar == LEFT_OP)
|
||||
elog(WARN, "ExecInitIndexScan: %s",
|
||||
"both left and right ops are rel-vars");
|
||||
|
||||
varattno = 1;
|
||||
scanvar = RIGHT_OP;
|
||||
|
||||
} else {
|
||||
/* ----------------
|
||||
* otherwise, the leftop contains information usable
|
||||
* at runtime to figure out the value to place in our
|
||||
* scan key.
|
||||
* ----------------
|
||||
*/
|
||||
have_runtime_keys = true;
|
||||
run_keys[ j ] = RIGHT_OP;
|
||||
scanvalue = Int32GetDatum((int32) true);
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* now check that at least one op tells us the scan
|
||||
* attribute...
|
||||
* ----------------
|
||||
*/
|
||||
if (scanvar == NO_OP)
|
||||
elog(WARN, "ExecInitIndexScan: %s",
|
||||
"neither leftop nor rightop refer to scan relation");
|
||||
|
||||
/* ----------------
|
||||
* initialize the scan key's fields appropriately
|
||||
* ----------------
|
||||
*/
|
||||
ScanKeyEntryInitialize(&scan_keys[j],
|
||||
0,
|
||||
varattno, /* attribute number to scan */
|
||||
(RegProcedure) opid, /* reg proc to use */
|
||||
(Datum) scanvalue); /* constant */
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* store the key information into our array.
|
||||
* ----------------
|
||||
*/
|
||||
numScanKeys[ i ] = n_keys;
|
||||
scanKeys[ i ] = scan_keys;
|
||||
runtimeKeyInfo[ i ] = (Pointer) run_keys;
|
||||
}
|
||||
|
||||
indexstate->iss_NumIndices = numIndices;
|
||||
indexstate->iss_IndexPtr = indexPtr;
|
||||
indexstate->iss_ScanKeys = scanKeys;
|
||||
indexstate->iss_NumScanKeys = numScanKeys;
|
||||
|
||||
/* ----------------
|
||||
* If all of our keys have the form (op var const) , then we have no
|
||||
* runtime keys so we store NULL in the runtime key info.
|
||||
* Otherwise runtime key info contains an array of pointers
|
||||
* (one for each index) to arrays of flags (one for each key)
|
||||
* which indicate that the qual needs to be evaluated at runtime.
|
||||
* -cim 10/24/89
|
||||
* ----------------
|
||||
*/
|
||||
if (have_runtime_keys)
|
||||
{
|
||||
indexstate->iss_RuntimeKeyInfo = (Pointer) runtimeKeyInfo;
|
||||
}
|
||||
else {
|
||||
indexstate->iss_RuntimeKeyInfo = NULL;
|
||||
for (i=0; i < numIndices; i++) {
|
||||
List *qual;
|
||||
int n_keys;
|
||||
qual = nth(i, indxqual);
|
||||
n_keys = length(qual);
|
||||
if (n_keys > 0)
|
||||
pfree(runtimeKeyInfo[i]);
|
||||
}
|
||||
pfree(runtimeKeyInfo);
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* get the range table and direction information
|
||||
* from the execution state (these are needed to
|
||||
* open the relations).
|
||||
* ----------------
|
||||
*/
|
||||
rangeTable = estate->es_range_table;
|
||||
direction = estate->es_direction;
|
||||
|
||||
/* ----------------
|
||||
* open the base relation
|
||||
* ----------------
|
||||
*/
|
||||
relid = node->scan.scanrelid;
|
||||
rtentry = rt_fetch(relid, rangeTable);
|
||||
reloid = rtentry->relid;
|
||||
timeQual = rtentry->timeQual;
|
||||
|
||||
ExecOpenScanR(reloid, /* relation */
|
||||
0, /* nkeys */
|
||||
(ScanKey) NULL, /* scan key */
|
||||
0, /* is index */
|
||||
direction, /* scan direction */
|
||||
timeQual, /* time qual */
|
||||
¤tRelation, /* return: rel desc */
|
||||
(Pointer *) ¤tScanDesc); /* return: scan desc */
|
||||
|
||||
scanstate->css_currentRelation = currentRelation;
|
||||
scanstate->css_currentScanDesc = currentScanDesc;
|
||||
|
||||
|
||||
/* ----------------
|
||||
* get the scan type from the relation descriptor.
|
||||
* ----------------
|
||||
*/
|
||||
ExecAssignScanType(scanstate, RelationGetTupleDescriptor(currentRelation));
|
||||
ExecAssignResultTypeFromTL((Plan *) node, &scanstate->cstate);
|
||||
|
||||
/* ----------------
|
||||
* index scans don't have subtrees..
|
||||
* ----------------
|
||||
*/
|
||||
/* scanstate->ss_ProcOuterFlag = false; */
|
||||
|
||||
/* ----------------
|
||||
* open the index relations and initialize
|
||||
* relation and scan descriptors.
|
||||
* ----------------
|
||||
*/
|
||||
for (i=0; i < numIndices; i++) {
|
||||
Oid indexOid;
|
||||
|
||||
indexOid = (Oid)nth(i, indxid);
|
||||
|
||||
if (indexOid != 0) {
|
||||
ExecOpenScanR(indexOid, /* relation */
|
||||
numScanKeys[ i ], /* nkeys */
|
||||
scanKeys[ i ], /* scan key */
|
||||
true, /* is index */
|
||||
direction, /* scan direction */
|
||||
timeQual, /* time qual */
|
||||
&(relationDescs[ i ]), /* return: rel desc */
|
||||
(Pointer *) &(scanDescs[ i ]));
|
||||
/* return: scan desc */
|
||||
}
|
||||
}
|
||||
|
||||
indexstate->iss_RelationDescs = relationDescs;
|
||||
indexstate->iss_ScanDescs = scanDescs;
|
||||
|
||||
indexstate->cstate.cs_TupFromTlist = false;
|
||||
|
||||
/* ----------------
|
||||
* all done.
|
||||
* ----------------
|
||||
*/
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int
|
||||
ExecCountSlotsIndexScan(IndexScan *node)
|
||||
{
|
||||
return ExecCountSlotsNode(outerPlan((Plan *)node)) +
|
||||
ExecCountSlotsNode(innerPlan((Plan *)node)) +
|
||||
INDEXSCAN_NSLOTS;
|
||||
}
|
32
src/backend/executor/nodeIndexscan.h
Normal file
32
src/backend/executor/nodeIndexscan.h
Normal file
@ -0,0 +1,32 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* nodeIndexscan.h--
|
||||
*
|
||||
*
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: nodeIndexscan.h,v 1.1.1.1 1996/07/09 06:21:26 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef NODEINDEXSCAN_H
|
||||
#define NODEINDEXSCAN_H
|
||||
|
||||
extern TupleTableSlot *ExecIndexScan(IndexScan *node);
|
||||
|
||||
extern void ExecIndexReScan(IndexScan *node, ExprContext *exprCtxt, Plan *parent);
|
||||
|
||||
extern void ExecEndIndexScan(IndexScan *node);
|
||||
|
||||
extern void ExecIndexMarkPos(IndexScan *node);
|
||||
|
||||
extern void ExecIndexRestrPos(IndexScan *node);
|
||||
|
||||
extern void ExecUpdateIndexScanKeys(IndexScan *node, ExprContext *econtext);
|
||||
|
||||
extern bool ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent);
|
||||
|
||||
extern int ExecCountSlotsIndexScan(IndexScan *node);
|
||||
|
||||
#endif /* NODEINDEXSCAN_H */
|
392
src/backend/executor/nodeMaterial.c
Normal file
392
src/backend/executor/nodeMaterial.c
Normal file
@ -0,0 +1,392 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* nodeMaterial.c--
|
||||
* Routines to handle materialization nodes.
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/nodeMaterial.c,v 1.1.1.1 1996/07/09 06:21:26 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
/*
|
||||
* INTERFACE ROUTINES
|
||||
* ExecMaterial - generate a temporary relation
|
||||
* ExecInitMaterial - initialize node and subnodes..
|
||||
* ExecEndMaterial - shutdown node and subnodes
|
||||
*
|
||||
*/
|
||||
|
||||
#include "executor/executor.h"
|
||||
#include "executor/nodeMaterial.h"
|
||||
#include "catalog/catalog.h"
|
||||
#include "optimizer/internal.h" /* for _TEMP_RELATION_ID_ */
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecMaterial
|
||||
*
|
||||
* The first time this is called, ExecMaterial retrieves tuples
|
||||
* this node's outer subplan and inserts them into a temporary
|
||||
* relation. After this is done, a flag is set indicating that
|
||||
* the subplan has been materialized. Once the relation is
|
||||
* materialized, the first tuple is then returned. Successive
|
||||
* calls to ExecMaterial return successive tuples from the temp
|
||||
* relation.
|
||||
*
|
||||
* Initial State:
|
||||
*
|
||||
* ExecMaterial assumes the temporary relation has been
|
||||
* created and openend by ExecInitMaterial during the prior
|
||||
* InitPlan() phase.
|
||||
*
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
TupleTableSlot * /* result tuple from subplan */
|
||||
ExecMaterial(Material *node)
|
||||
{
|
||||
EState *estate;
|
||||
MaterialState *matstate;
|
||||
Plan *outerNode;
|
||||
ScanDirection dir;
|
||||
Relation tempRelation;
|
||||
Relation currentRelation;
|
||||
HeapScanDesc currentScanDesc;
|
||||
HeapTuple heapTuple;
|
||||
TupleTableSlot *slot;
|
||||
Buffer buffer;
|
||||
|
||||
/* ----------------
|
||||
* get state info from node
|
||||
* ----------------
|
||||
*/
|
||||
matstate = node->matstate;
|
||||
estate = node->plan.state;
|
||||
dir = estate->es_direction;
|
||||
|
||||
/* ----------------
|
||||
* the first time we call this, we retrieve all tuples
|
||||
* from the subplan into a temporary relation and then
|
||||
* we sort the relation. Subsequent calls return tuples
|
||||
* from the temporary relation.
|
||||
* ----------------
|
||||
*/
|
||||
|
||||
if (matstate->mat_Flag == false) {
|
||||
/* ----------------
|
||||
* set all relations to be scanned in the forward direction
|
||||
* while creating the temporary relation.
|
||||
* ----------------
|
||||
*/
|
||||
estate->es_direction = EXEC_FRWD;
|
||||
|
||||
/* ----------------
|
||||
* if we couldn't create the temp or current relations then
|
||||
* we print a warning and return NULL.
|
||||
* ----------------
|
||||
*/
|
||||
tempRelation = matstate->mat_TempRelation;
|
||||
if (tempRelation == NULL) {
|
||||
elog(DEBUG, "ExecMaterial: temp relation is NULL! aborting...");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
currentRelation = matstate->csstate.css_currentRelation;
|
||||
if (currentRelation == NULL) {
|
||||
elog(DEBUG, "ExecMaterial: current relation is NULL! aborting...");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* retrieve tuples from the subplan and
|
||||
* insert them in the temporary relation
|
||||
* ----------------
|
||||
*/
|
||||
outerNode = outerPlan((Plan *) node);
|
||||
for (;;) {
|
||||
slot = ExecProcNode(outerNode, (Plan*) node);
|
||||
|
||||
heapTuple = slot->val;
|
||||
if (heapTuple == NULL)
|
||||
break;
|
||||
|
||||
heap_insert(tempRelation, /* relation desc */
|
||||
heapTuple); /* heap tuple to insert */
|
||||
|
||||
ExecClearTuple( slot);
|
||||
}
|
||||
currentRelation = tempRelation;
|
||||
|
||||
/* ----------------
|
||||
* restore to user specified direction
|
||||
* ----------------
|
||||
*/
|
||||
estate->es_direction = dir;
|
||||
|
||||
/* ----------------
|
||||
* now initialize the scan descriptor to scan the
|
||||
* sorted relation and update the sortstate information
|
||||
* ----------------
|
||||
*/
|
||||
currentScanDesc = heap_beginscan(currentRelation, /* relation */
|
||||
ScanDirectionIsBackward(dir),
|
||||
/* bkwd flag */
|
||||
NowTimeQual, /* time qual */
|
||||
0, /* num scan keys */
|
||||
NULL); /* scan keys */
|
||||
matstate->csstate.css_currentRelation = currentRelation;
|
||||
matstate->csstate.css_currentScanDesc = currentScanDesc;
|
||||
|
||||
ExecAssignScanType(&matstate->csstate,
|
||||
RelationGetTupleDescriptor(currentRelation));
|
||||
|
||||
/* ----------------
|
||||
* finally set the sorted flag to true
|
||||
* ----------------
|
||||
*/
|
||||
matstate->mat_Flag = true;
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* at this point we know we have a sorted relation so
|
||||
* we preform a simple scan on it with amgetnext()..
|
||||
* ----------------
|
||||
*/
|
||||
currentScanDesc = matstate->csstate.css_currentScanDesc;
|
||||
|
||||
heapTuple = heap_getnext(currentScanDesc, /* scan desc */
|
||||
ScanDirectionIsBackward(dir),
|
||||
/* bkwd flag */
|
||||
&buffer); /* return: buffer */
|
||||
|
||||
/* ----------------
|
||||
* put the tuple into the scan tuple slot and return the slot.
|
||||
* Note: since the tuple is really a pointer to a page, we don't want
|
||||
* to call pfree() on it..
|
||||
* ----------------
|
||||
*/
|
||||
slot = (TupleTableSlot *)matstate->csstate.css_ScanTupleSlot;
|
||||
|
||||
return ExecStoreTuple(heapTuple, /* tuple to store */
|
||||
slot, /* slot to store in */
|
||||
buffer, /* buffer for this tuple */
|
||||
false); /* don't pfree this pointer */
|
||||
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecInitMaterial
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
bool /* initialization status */
|
||||
ExecInitMaterial(Material *node, EState *estate, Plan *parent)
|
||||
{
|
||||
MaterialState *matstate;
|
||||
Plan *outerPlan;
|
||||
TupleDesc tupType;
|
||||
Relation tempDesc;
|
||||
int len;
|
||||
|
||||
/* ----------------
|
||||
* assign the node's execution state
|
||||
* ----------------
|
||||
*/
|
||||
node->plan.state = estate;
|
||||
|
||||
/* ----------------
|
||||
* create state structure
|
||||
* ----------------
|
||||
*/
|
||||
matstate = makeNode(MaterialState);
|
||||
matstate->mat_Flag = false;
|
||||
matstate->mat_TempRelation = NULL;
|
||||
node->matstate = matstate;
|
||||
|
||||
/* ----------------
|
||||
* Miscellanious initialization
|
||||
*
|
||||
* + assign node's base_id
|
||||
* + assign debugging hooks and
|
||||
* + assign result tuple slot
|
||||
*
|
||||
* Materialization nodes don't need ExprContexts because
|
||||
* they never call ExecQual or ExecTargetList.
|
||||
* ----------------
|
||||
*/
|
||||
ExecAssignNodeBaseInfo(estate, &matstate->csstate.cstate, parent);
|
||||
|
||||
#define MATERIAL_NSLOTS 1
|
||||
/* ----------------
|
||||
* tuple table initialization
|
||||
* ----------------
|
||||
*/
|
||||
ExecInitScanTupleSlot(estate, &matstate->csstate);
|
||||
|
||||
/* ----------------
|
||||
* initializes child nodes
|
||||
* ----------------
|
||||
*/
|
||||
outerPlan = outerPlan((Plan *) node);
|
||||
ExecInitNode(outerPlan, estate, (Plan *) node);
|
||||
|
||||
/* ----------------
|
||||
* initialize matstate information
|
||||
* ----------------
|
||||
*/
|
||||
matstate->mat_Flag = false;
|
||||
|
||||
/* ----------------
|
||||
* initialize tuple type. no need to initialize projection
|
||||
* info because this node doesn't do projections.
|
||||
* ----------------
|
||||
*/
|
||||
ExecAssignScanTypeFromOuterPlan((Plan *) node, &matstate->csstate);
|
||||
matstate->csstate.cstate.cs_ProjInfo = NULL;
|
||||
|
||||
/* ----------------
|
||||
* get type information needed for ExecCreatR
|
||||
* ----------------
|
||||
*/
|
||||
tupType = ExecGetScanType(&matstate->csstate);
|
||||
|
||||
/* ----------------
|
||||
* ExecCreatR wants it's second argument to be an object id of
|
||||
* a relation in the range table or a _TEMP_RELATION_ID
|
||||
* indicating that the relation is not in the range table.
|
||||
*
|
||||
* In the second case ExecCreatR creates a temp relation.
|
||||
* (currently this is the only case we support -cim 10/16/89)
|
||||
* ----------------
|
||||
*/
|
||||
/* ----------------
|
||||
* create the temporary relation
|
||||
* ----------------
|
||||
*/
|
||||
/* len = ExecTargetListLength(node->plan.targetlist); */
|
||||
tempDesc = ExecCreatR(tupType, _TEMP_RELATION_ID_);
|
||||
|
||||
/* ----------------
|
||||
* save the relation descriptor in the sortstate
|
||||
* ----------------
|
||||
*/
|
||||
matstate->mat_TempRelation = tempDesc;
|
||||
matstate->csstate.css_currentRelation = tempDesc;
|
||||
|
||||
/* ----------------
|
||||
* return relation oid of temporary relation in a list
|
||||
* (someday -- for now we return LispTrue... cim 10/12/89)
|
||||
* ----------------
|
||||
*/
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int
|
||||
ExecCountSlotsMaterial(Material *node)
|
||||
{
|
||||
return ExecCountSlotsNode(outerPlan((Plan *)node)) +
|
||||
ExecCountSlotsNode(innerPlan((Plan *)node)) +
|
||||
MATERIAL_NSLOTS;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecEndMaterial
|
||||
*
|
||||
* old comments
|
||||
* destroys the temporary relation.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
void
|
||||
ExecEndMaterial(Material *node)
|
||||
{
|
||||
MaterialState *matstate;
|
||||
Relation tempRelation;
|
||||
Plan *outerPlan;
|
||||
|
||||
/* ----------------
|
||||
* get info from the material state
|
||||
* ----------------
|
||||
*/
|
||||
matstate = node->matstate;
|
||||
tempRelation = matstate->mat_TempRelation;
|
||||
|
||||
heap_destroyr(tempRelation);
|
||||
|
||||
/* ----------------
|
||||
* close the temp relation and shut down the scan.
|
||||
* ----------------
|
||||
*/
|
||||
ExecCloseR((Plan *) node);
|
||||
|
||||
/* ----------------
|
||||
* shut down the subplan
|
||||
* ----------------
|
||||
*/
|
||||
outerPlan = outerPlan((Plan *) node);
|
||||
ExecEndNode(outerPlan, (Plan*) node);
|
||||
|
||||
/* ----------------
|
||||
* clean out the tuple table
|
||||
* ----------------
|
||||
*/
|
||||
ExecClearTuple(matstate->csstate.css_ScanTupleSlot);
|
||||
}
|
||||
|
||||
#if 0 /* not used */
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecMaterialMarkPos
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
List /* nothing of interest */
|
||||
ExecMaterialMarkPos(Material node)
|
||||
{
|
||||
MaterialState matstate;
|
||||
HeapScanDesc sdesc;
|
||||
|
||||
/* ----------------
|
||||
* if we haven't materialized yet, just return NIL.
|
||||
* ----------------
|
||||
*/
|
||||
matstate = get_matstate(node);
|
||||
if (get_mat_Flag(matstate) == false)
|
||||
return NIL;
|
||||
|
||||
/* ----------------
|
||||
* XXX access methods don't return positions yet so
|
||||
* for now we return NIL. It's possible that
|
||||
* they will never return positions for all I know -cim 10/16/89
|
||||
* ----------------
|
||||
*/
|
||||
sdesc = get_css_currentScanDesc((CommonScanState)matstate);
|
||||
heap_markpos(sdesc);
|
||||
|
||||
return NIL;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecMaterialRestrPos
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
void
|
||||
ExecMaterialRestrPos(Material node)
|
||||
{
|
||||
MaterialState matstate;
|
||||
HeapScanDesc sdesc;
|
||||
|
||||
/* ----------------
|
||||
* if we haven't materialized yet, just return.
|
||||
* ----------------
|
||||
*/
|
||||
matstate = get_matstate(node);
|
||||
if (get_mat_Flag(matstate) == false)
|
||||
return;
|
||||
|
||||
/* ----------------
|
||||
* restore the scan to the previously marked position
|
||||
* ----------------
|
||||
*/
|
||||
sdesc = get_css_currentScanDesc((CommonScanState)matstate);
|
||||
heap_restrpos(sdesc);
|
||||
}
|
||||
#endif
|
||||
|
23
src/backend/executor/nodeMaterial.h
Normal file
23
src/backend/executor/nodeMaterial.h
Normal file
@ -0,0 +1,23 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* nodeMaterial.h--
|
||||
*
|
||||
*
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: nodeMaterial.h,v 1.1.1.1 1996/07/09 06:21:27 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef NODEMATERIAL_H
|
||||
#define NODEMATERIAL_H
|
||||
|
||||
extern TupleTableSlot *ExecMaterial(Material *node);
|
||||
extern bool ExecInitMaterial(Material *node, EState *estate, Plan *parent);
|
||||
extern int ExecCountSlotsMaterial(Material *node);
|
||||
extern void ExecEndMaterial(Material *node);
|
||||
extern List ExecMaterialMarkPos(Material *node);
|
||||
extern void ExecMaterialRestrPos(Material *node);
|
||||
|
||||
#endif /* NODEMATERIAL_H */
|
1194
src/backend/executor/nodeMergejoin.c
Normal file
1194
src/backend/executor/nodeMergejoin.c
Normal file
File diff suppressed because it is too large
Load Diff
40
src/backend/executor/nodeMergejoin.h
Normal file
40
src/backend/executor/nodeMergejoin.h
Normal file
@ -0,0 +1,40 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* nodeMergejoin.h--
|
||||
*
|
||||
*
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: nodeMergejoin.h,v 1.1.1.1 1996/07/09 06:21:27 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef NODEMERGEJOIN_H
|
||||
#define NODEMERGEJOIN_H
|
||||
|
||||
#if 0 /* aren't these static? */
|
||||
extern List MJFormOSortopI(List qualList, Oid sortOp);
|
||||
extern List MJFormISortopO(List qualList, Oid sortOp);
|
||||
#endif
|
||||
extern bool MergeCompare(List *eqQual, List *compareQual, ExprContext *econtext);
|
||||
|
||||
extern void ExecMergeTupleDumpInner(ExprContext *econtext);
|
||||
|
||||
extern void ExecMergeTupleDumpOuter(ExprContext *econtext);
|
||||
|
||||
extern void ExecMergeTupleDumpMarked(ExprContext *econtext,
|
||||
MergeJoinState *mergestate);
|
||||
|
||||
extern void ExecMergeTupleDump(ExprContext *econtext,
|
||||
MergeJoinState *mergestate);
|
||||
|
||||
extern TupleTableSlot *ExecMergeJoin(MergeJoin *node);
|
||||
|
||||
extern bool ExecInitMergeJoin(MergeJoin *node, EState *estate, Plan *parent);
|
||||
|
||||
extern int ExecCountSlotsMergeJoin(MergeJoin *node);
|
||||
|
||||
extern void ExecEndMergeJoin(MergeJoin *node);
|
||||
|
||||
#endif /* NODEMERGEJOIN_H; */
|
370
src/backend/executor/nodeNestloop.c
Normal file
370
src/backend/executor/nodeNestloop.c
Normal file
@ -0,0 +1,370 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* nodeNestloop.c--
|
||||
* routines to support nest-loop joins
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/nodeNestloop.c,v 1.1.1.1 1996/07/09 06:21:27 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
/*
|
||||
* INTERFACE ROUTINES
|
||||
* ExecNestLoop - process a nestloop join of two plans
|
||||
* ExecInitNestLoop - initialize the join
|
||||
* ExecEndNestLoop - shut down the join
|
||||
*/
|
||||
#include "executor/executor.h"
|
||||
#include "executor/nodeNestloop.h"
|
||||
#include "executor/nodeIndexscan.h"
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecNestLoop(node)
|
||||
*
|
||||
* old comments
|
||||
* Returns the tuple joined from inner and outer tuples which
|
||||
* satisfies the qualification clause.
|
||||
*
|
||||
* It scans the inner relation to join with current outer tuple.
|
||||
*
|
||||
* If none is found, next tuple form the outer relation is retrieved
|
||||
* and the inner relation is scanned from the beginning again to join
|
||||
* with the outer tuple.
|
||||
*
|
||||
* Nil is returned if all the remaining outer tuples are tried and
|
||||
* all fail to join with the inner tuples.
|
||||
*
|
||||
* Nil is also returned if there is no tuple from inner realtion.
|
||||
*
|
||||
* Conditions:
|
||||
* -- outerTuple contains current tuple from outer relation and
|
||||
* the right son(inner realtion) maintains "cursor" at the tuple
|
||||
* returned previously.
|
||||
* This is achieved by maintaining a scan position on the outer
|
||||
* relation.
|
||||
*
|
||||
* Initial States:
|
||||
* -- the outer child and the inner child
|
||||
* are prepared to return the first tuple.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
TupleTableSlot *
|
||||
ExecNestLoop(NestLoop *node, Plan* parent)
|
||||
{
|
||||
NestLoopState *nlstate;
|
||||
Plan *innerPlan;
|
||||
Plan *outerPlan;
|
||||
bool needNewOuterTuple;
|
||||
|
||||
TupleTableSlot *outerTupleSlot;
|
||||
TupleTableSlot *innerTupleSlot;
|
||||
|
||||
List *qual;
|
||||
bool qualResult;
|
||||
ExprContext *econtext;
|
||||
|
||||
/* ----------------
|
||||
* get information from the node
|
||||
* ----------------
|
||||
*/
|
||||
ENL1_printf("getting info from node");
|
||||
|
||||
nlstate = node->nlstate;
|
||||
qual = node->join.qual;
|
||||
outerPlan = outerPlan(&node->join);
|
||||
innerPlan = innerPlan(&node->join);
|
||||
|
||||
/* ----------------
|
||||
* initialize expression context
|
||||
* ----------------
|
||||
*/
|
||||
econtext = nlstate->jstate.cs_ExprContext;
|
||||
|
||||
/* ---------------- * get the current outer tuple
|
||||
* ----------------
|
||||
*/
|
||||
outerTupleSlot = nlstate->jstate.cs_OuterTupleSlot;
|
||||
econtext->ecxt_outertuple = outerTupleSlot;
|
||||
|
||||
/* ----------------
|
||||
* Ok, everything is setup for the join so now loop until
|
||||
* we return a qualifying join tuple..
|
||||
* ----------------
|
||||
*/
|
||||
|
||||
if (nlstate->jstate.cs_TupFromTlist) {
|
||||
TupleTableSlot *result;
|
||||
bool isDone;
|
||||
|
||||
result = ExecProject(nlstate->jstate.cs_ProjInfo, &isDone);
|
||||
if (!isDone)
|
||||
return result;
|
||||
}
|
||||
|
||||
ENL1_printf("entering main loop");
|
||||
for(;;) {
|
||||
/* ----------------
|
||||
* The essential idea now is to get the next inner tuple
|
||||
* and join it with the current outer tuple.
|
||||
* ----------------
|
||||
*/
|
||||
needNewOuterTuple = false;
|
||||
|
||||
/* ----------------
|
||||
* If outer tuple is not null then that means
|
||||
* we are in the middle of a scan and we should
|
||||
* restore our previously saved scan position.
|
||||
* ----------------
|
||||
*/
|
||||
if (! TupIsNull(outerTupleSlot)) {
|
||||
ENL1_printf("have outer tuple, restoring outer plan");
|
||||
ExecRestrPos(outerPlan);
|
||||
} else {
|
||||
ENL1_printf("outer tuple is nil, need new outer tuple");
|
||||
needNewOuterTuple = true;
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* if we have an outerTuple, try to get the next inner tuple.
|
||||
* ----------------
|
||||
*/
|
||||
if (!needNewOuterTuple) {
|
||||
ENL1_printf("getting new inner tuple");
|
||||
|
||||
innerTupleSlot = ExecProcNode(innerPlan, (Plan*)node);
|
||||
econtext->ecxt_innertuple = innerTupleSlot;
|
||||
|
||||
if (TupIsNull(innerTupleSlot)) {
|
||||
ENL1_printf("no inner tuple, need new outer tuple");
|
||||
needNewOuterTuple = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* loop until we have a new outer tuple and a new
|
||||
* inner tuple.
|
||||
* ----------------
|
||||
*/
|
||||
while (needNewOuterTuple) {
|
||||
/* ----------------
|
||||
* now try to get the next outer tuple
|
||||
* ----------------
|
||||
*/
|
||||
ENL1_printf("getting new outer tuple");
|
||||
outerTupleSlot = ExecProcNode(outerPlan, (Plan*)node);
|
||||
econtext->ecxt_outertuple = outerTupleSlot;
|
||||
|
||||
/* ----------------
|
||||
* if there are no more outer tuples, then the join
|
||||
* is complete..
|
||||
* ----------------
|
||||
*/
|
||||
if (TupIsNull(outerTupleSlot)) {
|
||||
ENL1_printf("no outer tuple, ending join");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* we have a new outer tuple so we mark our position
|
||||
* in the outer scan and save the outer tuple in the
|
||||
* NestLoop state
|
||||
* ----------------
|
||||
*/
|
||||
ENL1_printf("saving new outer tuple information");
|
||||
ExecMarkPos(outerPlan);
|
||||
nlstate->jstate.cs_OuterTupleSlot = outerTupleSlot;
|
||||
|
||||
/* ----------------
|
||||
* now rescan the inner plan and get a new inner tuple
|
||||
* ----------------
|
||||
*/
|
||||
|
||||
ENL1_printf("rescanning inner plan");
|
||||
/*
|
||||
* The scan key of the inner plan might depend on the current
|
||||
* outer tuple (e.g. in index scans), that's why we pass our
|
||||
* expr context.
|
||||
*/
|
||||
ExecReScan(innerPlan, econtext, parent);
|
||||
|
||||
ENL1_printf("getting new inner tuple");
|
||||
|
||||
innerTupleSlot = ExecProcNode(innerPlan, (Plan*)node);
|
||||
econtext->ecxt_innertuple = innerTupleSlot;
|
||||
|
||||
if (TupIsNull(innerTupleSlot)) {
|
||||
ENL1_printf("couldn't get inner tuple - need new outer tuple");
|
||||
} else {
|
||||
ENL1_printf("got inner and outer tuples");
|
||||
needNewOuterTuple = false;
|
||||
}
|
||||
} /* while (needNewOuterTuple) */
|
||||
|
||||
/* ----------------
|
||||
* at this point we have a new pair of inner and outer
|
||||
* tuples so we test the inner and outer tuples to see
|
||||
* if they satisify the node's qualification.
|
||||
* ----------------
|
||||
*/
|
||||
ENL1_printf("testing qualification");
|
||||
qualResult = ExecQual((List*)qual, econtext);
|
||||
|
||||
if (qualResult) {
|
||||
/* ----------------
|
||||
* qualification was satisified so we project and
|
||||
* return the slot containing the result tuple
|
||||
* using ExecProject().
|
||||
* ----------------
|
||||
*/
|
||||
ProjectionInfo *projInfo;
|
||||
TupleTableSlot *result;
|
||||
bool isDone;
|
||||
|
||||
ENL1_printf("qualification succeeded, projecting tuple");
|
||||
|
||||
projInfo = nlstate->jstate.cs_ProjInfo;
|
||||
result = ExecProject(projInfo, &isDone);
|
||||
nlstate->jstate.cs_TupFromTlist = !isDone;
|
||||
return result;
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* qualification failed so we have to try again..
|
||||
* ----------------
|
||||
*/
|
||||
ENL1_printf("qualification failed, looping");
|
||||
}
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecInitNestLoop
|
||||
*
|
||||
* Creates the run-time state information for the nestloop node
|
||||
* produced by the planner and initailizes inner and outer relations
|
||||
* (child nodes).
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
bool
|
||||
ExecInitNestLoop(NestLoop *node, EState *estate, Plan *parent)
|
||||
{
|
||||
NestLoopState *nlstate;
|
||||
|
||||
NL1_printf("ExecInitNestLoop: %s\n",
|
||||
"initializing node");
|
||||
|
||||
/* ----------------
|
||||
* assign execution state to node
|
||||
* ----------------
|
||||
*/
|
||||
node->join.state = estate;
|
||||
|
||||
/* ----------------
|
||||
* create new nest loop state
|
||||
* ----------------
|
||||
*/
|
||||
nlstate = makeNode(NestLoopState);
|
||||
nlstate->nl_PortalFlag = false;
|
||||
node->nlstate = nlstate;
|
||||
|
||||
/* ----------------
|
||||
* Miscellanious initialization
|
||||
*
|
||||
* + assign node's base_id
|
||||
* + assign debugging hooks and
|
||||
* + create expression context for node
|
||||
* ----------------
|
||||
*/
|
||||
ExecAssignNodeBaseInfo(estate, &nlstate->jstate, parent);
|
||||
ExecAssignExprContext(estate, &nlstate->jstate);
|
||||
|
||||
#define NESTLOOP_NSLOTS 1
|
||||
/* ----------------
|
||||
* tuple table initialization
|
||||
* ----------------
|
||||
*/
|
||||
ExecInitResultTupleSlot(estate, &nlstate->jstate);
|
||||
|
||||
/* ----------------
|
||||
* now initialize children
|
||||
* ----------------
|
||||
*/
|
||||
ExecInitNode(outerPlan((Plan*)node), estate, (Plan*)node);
|
||||
ExecInitNode(innerPlan((Plan*)node), estate, (Plan*)node);
|
||||
|
||||
/* ----------------
|
||||
* initialize tuple type and projection info
|
||||
* ----------------
|
||||
*/
|
||||
ExecAssignResultTypeFromTL((Plan *) node, &nlstate->jstate);
|
||||
ExecAssignProjectionInfo((Plan *) node, &nlstate->jstate);
|
||||
|
||||
/* ----------------
|
||||
* finally, wipe the current outer tuple clean.
|
||||
* ----------------
|
||||
*/
|
||||
nlstate->jstate.cs_OuterTupleSlot = NULL;
|
||||
nlstate->jstate.cs_TupFromTlist = false;
|
||||
|
||||
NL1_printf("ExecInitNestLoop: %s\n",
|
||||
"node initialized");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int
|
||||
ExecCountSlotsNestLoop(NestLoop *node)
|
||||
{
|
||||
return ExecCountSlotsNode(outerPlan(node)) +
|
||||
ExecCountSlotsNode(innerPlan(node)) +
|
||||
NESTLOOP_NSLOTS;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecEndNestLoop
|
||||
*
|
||||
* closes down scans and frees allocated storage
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
void
|
||||
ExecEndNestLoop(NestLoop *node)
|
||||
{
|
||||
NestLoopState *nlstate;
|
||||
|
||||
NL1_printf("ExecEndNestLoop: %s\n",
|
||||
"ending node processing");
|
||||
|
||||
/* ----------------
|
||||
* get info from the node
|
||||
* ----------------
|
||||
*/
|
||||
nlstate = node->nlstate;
|
||||
|
||||
/* ----------------
|
||||
* Free the projection info
|
||||
*
|
||||
* Note: we don't ExecFreeResultType(nlstate)
|
||||
* because the rule manager depends on the tupType
|
||||
* returned by ExecMain(). So for now, this
|
||||
* is freed at end-transaction time. -cim 6/2/91
|
||||
* ----------------
|
||||
*/
|
||||
ExecFreeProjectionInfo(&nlstate->jstate);
|
||||
|
||||
/* ----------------
|
||||
* close down subplans
|
||||
* ----------------
|
||||
*/
|
||||
ExecEndNode(outerPlan((Plan *) node), (Plan*)node);
|
||||
ExecEndNode(innerPlan((Plan *) node), (Plan*)node);
|
||||
|
||||
/* ----------------
|
||||
* clean out the tuple table
|
||||
* ----------------
|
||||
*/
|
||||
ExecClearTuple(nlstate->jstate.cs_ResultTupleSlot);
|
||||
|
||||
NL1_printf("ExecEndNestLoop: %s\n",
|
||||
"node processing ended");
|
||||
}
|
21
src/backend/executor/nodeNestloop.h
Normal file
21
src/backend/executor/nodeNestloop.h
Normal file
@ -0,0 +1,21 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* nodeNestloop.h--
|
||||
*
|
||||
*
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: nodeNestloop.h,v 1.1.1.1 1996/07/09 06:21:27 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef NODENESTLOOP_H
|
||||
#define NODENESTLOOP_H
|
||||
|
||||
extern TupleTableSlot *ExecNestLoop(NestLoop *node, Plan *parent);
|
||||
extern bool ExecInitNestLoop(NestLoop *node, EState *estate, Plan *parent);
|
||||
extern int ExecCountSlotsNestLoop(NestLoop *node);
|
||||
extern void ExecEndNestLoop(NestLoop *node);
|
||||
|
||||
#endif /* NODENESTLOOP_H */
|
288
src/backend/executor/nodeResult.c
Normal file
288
src/backend/executor/nodeResult.c
Normal file
@ -0,0 +1,288 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* nodeResult.c--
|
||||
* support for constant nodes needing special code.
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* DESCRIPTION
|
||||
*
|
||||
* Example: in constant queries where no relations are scanned,
|
||||
* the planner generates result nodes. Examples of such queries are:
|
||||
*
|
||||
* retrieve (x = 1)
|
||||
* and
|
||||
* append emp (name = "mike", salary = 15000)
|
||||
*
|
||||
* Result nodes are also used to optimise queries
|
||||
* with tautological qualifications like:
|
||||
*
|
||||
* retrieve (emp.all) where 2 > 1
|
||||
*
|
||||
* In this case, the plan generated is
|
||||
*
|
||||
* Result (with 2 > 1 qual)
|
||||
* /
|
||||
* SeqScan (emp.all)
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/nodeResult.c,v 1.1.1.1 1996/07/09 06:21:27 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "executor/executor.h"
|
||||
#include "executor/nodeResult.h"
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecResult(node)
|
||||
*
|
||||
* returns the tuples from the outer plan which satisify the
|
||||
* qualification clause. Since result nodes with right
|
||||
* subtrees are never planned, we ignore the right subtree
|
||||
* entirely (for now).. -cim 10/7/89
|
||||
*
|
||||
* The qualification containing only constant clauses are
|
||||
* checked first before any processing is done. It always returns
|
||||
* 'nil' if the constant qualification is not satisfied.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
TupleTableSlot *
|
||||
ExecResult(Result *node)
|
||||
{
|
||||
ResultState *resstate;
|
||||
TupleTableSlot *outerTupleSlot;
|
||||
TupleTableSlot *resultSlot;
|
||||
Plan *outerPlan;
|
||||
ExprContext *econtext;
|
||||
Node *qual;
|
||||
bool qualResult;
|
||||
bool isDone;
|
||||
ProjectionInfo *projInfo;
|
||||
|
||||
/* ----------------
|
||||
* initialize the result node's state
|
||||
* ----------------
|
||||
*/
|
||||
resstate = node->resstate;
|
||||
|
||||
/* ----------------
|
||||
* get the expression context
|
||||
* ----------------
|
||||
*/
|
||||
econtext = resstate->cstate.cs_ExprContext;
|
||||
|
||||
/* ----------------
|
||||
* check tautological qualifications like (2 > 1)
|
||||
* ----------------
|
||||
*/
|
||||
qual = node->resconstantqual;
|
||||
if (qual != NULL) {
|
||||
qualResult = ExecQual((List*)qual, econtext);
|
||||
/* ----------------
|
||||
* if we failed the constant qual, then there
|
||||
* is no need to continue processing because regardless of
|
||||
* what happens, the constant qual will be false..
|
||||
* ----------------
|
||||
*/
|
||||
if (qualResult == false)
|
||||
return NULL;
|
||||
|
||||
/* ----------------
|
||||
* our constant qualification succeeded so now we
|
||||
* throw away the qual because we know it will always
|
||||
* succeed.
|
||||
* ----------------
|
||||
*/
|
||||
node->resconstantqual = NULL;
|
||||
}
|
||||
|
||||
if (resstate->cstate.cs_TupFromTlist) {
|
||||
ProjectionInfo *projInfo;
|
||||
|
||||
projInfo = resstate->cstate.cs_ProjInfo;
|
||||
resultSlot = ExecProject(projInfo, &isDone);
|
||||
if (!isDone)
|
||||
return resultSlot;
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* retrieve a tuple that satisfy the qual from the outer plan until
|
||||
* there are no more.
|
||||
*
|
||||
* if rs_done is 1 then it means that we were asked to return
|
||||
* a constant tuple and we alread did the last time ExecResult()
|
||||
* was called, so now we are through.
|
||||
* ----------------
|
||||
*/
|
||||
outerPlan = outerPlan(node);
|
||||
|
||||
while (!resstate->rs_done) {
|
||||
|
||||
/* ----------------
|
||||
* get next outer tuple if necessary.
|
||||
* ----------------
|
||||
*/
|
||||
if (outerPlan != NULL) {
|
||||
outerTupleSlot = ExecProcNode(outerPlan, (Plan*)node);
|
||||
|
||||
if (TupIsNull(outerTupleSlot))
|
||||
return NULL;
|
||||
|
||||
resstate->cstate.cs_OuterTupleSlot = outerTupleSlot;
|
||||
} else {
|
||||
|
||||
/* ----------------
|
||||
* if we don't have an outer plan, then it's probably
|
||||
* the case that we are doing a retrieve or an append
|
||||
* with a constant target list, so we should only return
|
||||
* the constant tuple once or never if we fail the qual.
|
||||
* ----------------
|
||||
*/
|
||||
resstate->rs_done = 1;
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* get the information to place into the expr context
|
||||
* ----------------
|
||||
*/
|
||||
resstate = node->resstate;
|
||||
|
||||
outerTupleSlot = resstate->cstate.cs_OuterTupleSlot;
|
||||
|
||||
/* ----------------
|
||||
* fill in the information in the expression context
|
||||
* XXX gross hack. use outer tuple as scan tuple
|
||||
* ----------------
|
||||
*/
|
||||
econtext->ecxt_outertuple = outerTupleSlot;
|
||||
econtext->ecxt_scantuple = outerTupleSlot;
|
||||
|
||||
/* ----------------
|
||||
* form the result tuple and pass it back using ExecProject()
|
||||
* ----------------
|
||||
*/
|
||||
projInfo = resstate->cstate.cs_ProjInfo;
|
||||
resultSlot = ExecProject(projInfo, &isDone);
|
||||
resstate->cstate.cs_TupFromTlist = !isDone;
|
||||
return resultSlot;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecInitResult
|
||||
*
|
||||
* Creates the run-time state information for the result node
|
||||
* produced by the planner and initailizes outer relations
|
||||
* (child nodes).
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
bool
|
||||
ExecInitResult(Result *node, EState *estate, Plan *parent)
|
||||
{
|
||||
ResultState *resstate;
|
||||
|
||||
/* ----------------
|
||||
* assign execution state to node
|
||||
* ----------------
|
||||
*/
|
||||
node->plan.state = estate;
|
||||
|
||||
/* ----------------
|
||||
* create new ResultState for node
|
||||
* ----------------
|
||||
*/
|
||||
resstate = makeNode(ResultState);
|
||||
resstate->rs_done = 0;
|
||||
node->resstate = resstate;
|
||||
|
||||
/* ----------------
|
||||
* Miscellanious initialization
|
||||
*
|
||||
* + assign node's base_id
|
||||
* + assign debugging hooks and
|
||||
* + create expression context for node
|
||||
* ----------------
|
||||
*/
|
||||
ExecAssignNodeBaseInfo(estate, &resstate->cstate, parent);
|
||||
ExecAssignExprContext(estate, &resstate->cstate);
|
||||
|
||||
#define RESULT_NSLOTS 1
|
||||
/* ----------------
|
||||
* tuple table initialization
|
||||
* ----------------
|
||||
*/
|
||||
ExecInitResultTupleSlot(estate, &resstate->cstate);
|
||||
|
||||
/* ----------------
|
||||
* then initialize children
|
||||
* ----------------
|
||||
*/
|
||||
ExecInitNode(outerPlan(node), estate, (Plan*)node);
|
||||
|
||||
/*
|
||||
* we don't use inner plan
|
||||
*/
|
||||
Assert(innerPlan(node)==NULL);
|
||||
|
||||
/* ----------------
|
||||
* initialize tuple type and projection info
|
||||
* ----------------
|
||||
*/
|
||||
ExecAssignResultTypeFromTL((Plan*)node, &resstate->cstate);
|
||||
ExecAssignProjectionInfo((Plan*)node, &resstate->cstate);
|
||||
|
||||
/* ----------------
|
||||
* set "are we done yet" to false
|
||||
* ----------------
|
||||
*/
|
||||
resstate->rs_done = 0;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int
|
||||
ExecCountSlotsResult(Result *node)
|
||||
{
|
||||
return ExecCountSlotsNode(outerPlan(node)) + RESULT_NSLOTS;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecEndResult
|
||||
*
|
||||
* fees up storage allocated through C routines
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
void
|
||||
ExecEndResult(Result *node)
|
||||
{
|
||||
ResultState *resstate;
|
||||
|
||||
resstate = node->resstate;
|
||||
|
||||
/* ----------------
|
||||
* Free the projection info
|
||||
*
|
||||
* Note: we don't ExecFreeResultType(resstate)
|
||||
* because the rule manager depends on the tupType
|
||||
* returned by ExecMain(). So for now, this
|
||||
* is freed at end-transaction time. -cim 6/2/91
|
||||
* ----------------
|
||||
*/
|
||||
ExecFreeProjectionInfo(&resstate->cstate);
|
||||
|
||||
/* ----------------
|
||||
* shut down subplans
|
||||
* ----------------
|
||||
*/
|
||||
ExecEndNode(outerPlan(node), (Plan*)node);
|
||||
|
||||
/* ----------------
|
||||
* clean out the tuple table
|
||||
* ----------------
|
||||
*/
|
||||
ExecClearTuple(resstate->cstate.cs_ResultTupleSlot);
|
||||
}
|
21
src/backend/executor/nodeResult.h
Normal file
21
src/backend/executor/nodeResult.h
Normal file
@ -0,0 +1,21 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* nodeResult.h--
|
||||
*
|
||||
*
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: nodeResult.h,v 1.1.1.1 1996/07/09 06:21:27 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef NODERESULT_H
|
||||
#define NODERESULT_H
|
||||
|
||||
extern TupleTableSlot *ExecResult(Result *node);
|
||||
extern bool ExecInitResult(Result *node, EState *estate, Plan *parent);
|
||||
extern int ExecCountSlotsResult(Result *node);
|
||||
extern void ExecEndResult(Result *node);
|
||||
|
||||
#endif /* NODERESULT_H */
|
449
src/backend/executor/nodeSeqscan.c
Normal file
449
src/backend/executor/nodeSeqscan.c
Normal file
@ -0,0 +1,449 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* nodeSeqscan.c--
|
||||
* Support routines for sequential scans of relations.
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/nodeSeqscan.c,v 1.1.1.1 1996/07/09 06:21:27 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
/*
|
||||
* INTERFACE ROUTINES
|
||||
* ExecSeqScan sequentially scans a relation.
|
||||
* ExecSeqNext retrieve next tuple in sequential order.
|
||||
* ExecInitSeqScan creates and initializes a seqscan node.
|
||||
* ExecEndSeqScan releases any storage allocated.
|
||||
* ExecSeqReScan rescans the relation
|
||||
* ExecMarkPos marks scan position
|
||||
* ExecRestrPos restores scan position
|
||||
*
|
||||
*/
|
||||
#include "executor/executor.h"
|
||||
#include "executor/nodeSeqscan.h"
|
||||
#include "parser/parsetree.h"
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* Scan Support
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
/* ----------------------------------------------------------------
|
||||
* SeqNext
|
||||
*
|
||||
* This is a workhorse for ExecSeqScan
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
TupleTableSlot *
|
||||
SeqNext(SeqScan *node)
|
||||
{
|
||||
HeapTuple tuple;
|
||||
HeapScanDesc scandesc;
|
||||
CommonScanState *scanstate;
|
||||
EState *estate;
|
||||
ScanDirection direction;
|
||||
TupleTableSlot *slot;
|
||||
Buffer buffer;
|
||||
|
||||
/* ----------------
|
||||
* get information from the estate and scan state
|
||||
* ----------------
|
||||
*/
|
||||
estate = node->plan.state;
|
||||
scanstate = node->scanstate;
|
||||
scandesc = scanstate->css_currentScanDesc;
|
||||
direction = estate->es_direction;
|
||||
|
||||
/* ----------------
|
||||
* get the next tuple from the access methods
|
||||
* ----------------
|
||||
*/
|
||||
tuple = heap_getnext(scandesc, /* scan desc */
|
||||
ScanDirectionIsBackward(direction), /*backward flag*/
|
||||
&buffer); /* return: buffer */
|
||||
|
||||
/* ----------------
|
||||
* save the tuple and the buffer returned to us by the access methods
|
||||
* in our scan tuple slot and return the slot. Note: we pass 'false'
|
||||
* because tuples returned by heap_getnext() are pointers onto
|
||||
* disk pages and were not created with palloc() and so should not
|
||||
* be pfree()'d.
|
||||
* ----------------
|
||||
*/
|
||||
slot = scanstate->css_ScanTupleSlot;
|
||||
|
||||
slot = ExecStoreTuple(tuple, /* tuple to store */
|
||||
slot, /* slot to store in */
|
||||
buffer, /* buffer associated with this tuple */
|
||||
false); /* don't pfree this pointer */
|
||||
|
||||
/* ----------------
|
||||
* XXX -- mao says: The sequential scan for heap relations will
|
||||
* automatically unpin the buffer this tuple is on when we cross
|
||||
* a page boundary. The clearslot code also does this. We bump
|
||||
* the pin count on the page here, since we actually have two
|
||||
* pointers to it -- one in the scan desc and one in the tuple
|
||||
* table slot. --mar 20 91
|
||||
* ----------------
|
||||
*/
|
||||
ExecIncrSlotBufferRefcnt(slot);
|
||||
|
||||
return slot;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecSeqScan(node)
|
||||
*
|
||||
* Scans the relation sequentially and returns the next qualifying
|
||||
* tuple.
|
||||
* It calls the ExecScan() routine and passes it the access method
|
||||
* which retrieve tuples sequentially.
|
||||
*
|
||||
*/
|
||||
|
||||
TupleTableSlot *
|
||||
ExecSeqScan(SeqScan *node)
|
||||
{
|
||||
TupleTableSlot *slot;
|
||||
Plan *outerPlan;
|
||||
|
||||
S_printf("ExecSeqScan: scanning node: "); S_nodeDisplay(node);
|
||||
|
||||
/* ----------------
|
||||
* if there is an outer subplan, get a tuple from it
|
||||
* else, scan the relation
|
||||
* ----------------
|
||||
*/
|
||||
outerPlan = outerPlan((Plan *) node);
|
||||
if (outerPlan) {
|
||||
slot = ExecProcNode(outerPlan, (Plan*) node);
|
||||
} else {
|
||||
slot = ExecScan(node, SeqNext);
|
||||
}
|
||||
|
||||
S1_printf("ExecSeqScan: returned tuple slot: %d\n", slot);
|
||||
|
||||
return slot;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* InitScanRelation
|
||||
*
|
||||
* This does the initialization for scan relations and
|
||||
* subplans of scans.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
Oid
|
||||
InitScanRelation(SeqScan *node, EState *estate,
|
||||
CommonScanState *scanstate, Plan *outerPlan)
|
||||
{
|
||||
Index relid;
|
||||
List *rangeTable;
|
||||
RangeTblEntry *rtentry;
|
||||
Oid reloid;
|
||||
TimeQual timeQual;
|
||||
ScanDirection direction;
|
||||
Relation currentRelation;
|
||||
HeapScanDesc currentScanDesc;
|
||||
RelationInfo *resultRelationInfo;
|
||||
|
||||
if (outerPlan == NULL) {
|
||||
/* ----------------
|
||||
* if the outer node is nil then we are doing a simple
|
||||
* sequential scan of a relation...
|
||||
*
|
||||
* get the relation object id from the relid'th entry
|
||||
* in the range table, open that relation and initialize
|
||||
* the scan state...
|
||||
* ----------------
|
||||
*/
|
||||
relid = node->scanrelid;
|
||||
rangeTable = estate->es_range_table;
|
||||
rtentry = rt_fetch(relid, rangeTable);
|
||||
reloid = rtentry->relid;
|
||||
timeQual = rtentry->timeQual;
|
||||
direction = estate->es_direction;
|
||||
resultRelationInfo = estate->es_result_relation_info;
|
||||
|
||||
ExecOpenScanR(reloid, /* relation */
|
||||
0, /* nkeys */
|
||||
NULL, /* scan key */
|
||||
0, /* is index */
|
||||
direction, /* scan direction */
|
||||
timeQual, /* time qual */
|
||||
¤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;
|
||||
}
|
||||
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecInitSeqScan
|
||||
*
|
||||
* old comments
|
||||
* Creates the run-time state information for the seqscan node
|
||||
* and sets the relation id to contain relevant descriptors.
|
||||
*
|
||||
* If there is a outer subtree (sort), the outer subtree
|
||||
* is initialized and the relation id is set to the descriptors
|
||||
* returned by the subtree.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
bool
|
||||
ExecInitSeqScan(SeqScan *node, EState *estate, Plan *parent)
|
||||
{
|
||||
CommonScanState *scanstate;
|
||||
Plan *outerPlan;
|
||||
Oid reloid;
|
||||
HeapScanDesc scandesc;
|
||||
|
||||
/* ----------------
|
||||
* assign the node's execution state
|
||||
* ----------------
|
||||
*/
|
||||
node->plan.state = estate;
|
||||
|
||||
/* ----------------
|
||||
* create new CommonScanState for node
|
||||
* ----------------
|
||||
*/
|
||||
scanstate = makeNode(CommonScanState);
|
||||
node->scanstate = scanstate;
|
||||
|
||||
/* ----------------
|
||||
* Miscellanious initialization
|
||||
*
|
||||
* + assign node's base_id
|
||||
* + create expression context for node
|
||||
* ----------------
|
||||
*/
|
||||
ExecAssignNodeBaseInfo(estate, &scanstate->cstate, parent);
|
||||
ExecAssignExprContext(estate, &scanstate->cstate);
|
||||
|
||||
#define SEQSCAN_NSLOTS 3
|
||||
/* ----------------
|
||||
* tuple table initialization
|
||||
* ----------------
|
||||
*/
|
||||
ExecInitResultTupleSlot(estate, &scanstate->cstate);
|
||||
ExecInitScanTupleSlot(estate, scanstate);
|
||||
|
||||
/* ----------------
|
||||
* initialize scan relation or outer subplan
|
||||
* ----------------
|
||||
*/
|
||||
outerPlan = outerPlan((Plan *)node);
|
||||
|
||||
reloid = InitScanRelation(node, estate, scanstate, outerPlan);
|
||||
|
||||
scandesc = scanstate->css_currentScanDesc;
|
||||
scanstate->cstate.cs_TupFromTlist = false;
|
||||
|
||||
/* ----------------
|
||||
* initialize tuple type
|
||||
* ----------------
|
||||
*/
|
||||
ExecAssignResultTypeFromTL((Plan*)node, &scanstate->cstate);
|
||||
ExecAssignProjectionInfo((Plan*)node, &scanstate->cstate);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int
|
||||
ExecCountSlotsSeqScan(SeqScan *node)
|
||||
{
|
||||
return ExecCountSlotsNode(outerPlan(node)) +
|
||||
ExecCountSlotsNode(innerPlan(node)) +
|
||||
SEQSCAN_NSLOTS;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecEndSeqScan
|
||||
*
|
||||
* frees any storage allocated through C routines.
|
||||
*| ...and also closes relations and/or shuts down outer subplan
|
||||
*| -cim 8/14/89
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
void
|
||||
ExecEndSeqScan(SeqScan *node)
|
||||
{
|
||||
CommonScanState *scanstate;
|
||||
Plan *outerPlan;
|
||||
|
||||
/* ----------------
|
||||
* get information from node
|
||||
* ----------------
|
||||
*/
|
||||
scanstate = node->scanstate;
|
||||
|
||||
/* ----------------
|
||||
* Free the projection info and the scan attribute info
|
||||
*
|
||||
* Note: we don't ExecFreeResultType(scanstate)
|
||||
* because the rule manager depends on the tupType
|
||||
* returned by ExecMain(). So for now, this
|
||||
* is freed at end-transaction time. -cim 6/2/91
|
||||
* ----------------
|
||||
*/
|
||||
ExecFreeProjectionInfo(&scanstate->cstate);
|
||||
|
||||
/* ----------------
|
||||
* close scan relation
|
||||
* ----------------
|
||||
*/
|
||||
ExecCloseR((Plan*) node);
|
||||
|
||||
/* ----------------
|
||||
* clean up outer subtree (does nothing if there is no outerPlan)
|
||||
* ----------------
|
||||
*/
|
||||
outerPlan = outerPlan((Plan *)node);
|
||||
ExecEndNode(outerPlan, (Plan*)node);
|
||||
|
||||
/* ----------------
|
||||
* clean out the tuple table
|
||||
* ----------------
|
||||
*/
|
||||
ExecClearTuple(scanstate->cstate.cs_ResultTupleSlot);
|
||||
ExecClearTuple(scanstate->css_ScanTupleSlot);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* Join Support
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecSeqReScan
|
||||
*
|
||||
* Rescans the relation.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
void
|
||||
ExecSeqReScan(SeqScan *node, ExprContext *exprCtxt, Plan* parent)
|
||||
{
|
||||
CommonScanState *scanstate;
|
||||
EState *estate;
|
||||
Plan *outerPlan;
|
||||
Relation rdesc;
|
||||
HeapScanDesc sdesc;
|
||||
ScanDirection direction;
|
||||
|
||||
scanstate = node->scanstate;
|
||||
estate = node->plan.state;
|
||||
|
||||
outerPlan = outerPlan((Plan*)node);
|
||||
if (outerPlan) {
|
||||
/* we are scanning a subplan */
|
||||
outerPlan = outerPlan((Plan *)node);
|
||||
ExecReScan(outerPlan, exprCtxt, parent);
|
||||
} else {
|
||||
/* otherwise, we are scanning a relation */
|
||||
rdesc = scanstate->css_currentRelation;
|
||||
sdesc = scanstate->css_currentScanDesc;
|
||||
direction = estate->es_direction;
|
||||
sdesc = ExecReScanR(rdesc, sdesc, direction, 0, NULL);
|
||||
scanstate->css_currentScanDesc = sdesc;
|
||||
}
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecSeqMarkPos(node)
|
||||
*
|
||||
* Marks scan position.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
void
|
||||
ExecSeqMarkPos(SeqScan *node)
|
||||
{
|
||||
CommonScanState *scanstate;
|
||||
Plan *outerPlan;
|
||||
HeapScanDesc sdesc;
|
||||
|
||||
scanstate = node->scanstate;
|
||||
|
||||
/* ----------------
|
||||
* if we are scanning a subplan then propagate
|
||||
* the ExecMarkPos() request to the subplan
|
||||
* ----------------
|
||||
*/
|
||||
outerPlan = outerPlan((Plan*)node);
|
||||
if (outerPlan) {
|
||||
ExecMarkPos(outerPlan);
|
||||
return;
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* otherwise we are scanning a relation so mark the
|
||||
* position using the access methods..
|
||||
*
|
||||
* ----------------
|
||||
*/
|
||||
sdesc = scanstate->css_currentScanDesc;
|
||||
heap_markpos(sdesc);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecSeqRestrPos
|
||||
*
|
||||
* Restores scan position.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
void
|
||||
ExecSeqRestrPos(SeqScan *node)
|
||||
{
|
||||
CommonScanState *scanstate;
|
||||
Plan *outerPlan;
|
||||
HeapScanDesc sdesc;
|
||||
|
||||
scanstate = node->scanstate;
|
||||
|
||||
/* ----------------
|
||||
* if we are scanning a subplan then propagate
|
||||
* the ExecRestrPos() request to the subplan
|
||||
* ----------------
|
||||
*/
|
||||
outerPlan = outerPlan((Plan*)node);
|
||||
if (outerPlan) {
|
||||
ExecRestrPos(outerPlan);
|
||||
return;
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* otherwise we are scanning a relation so restore the
|
||||
* position using the access methods..
|
||||
* ----------------
|
||||
*/
|
||||
sdesc = scanstate->css_currentScanDesc;
|
||||
heap_restrpos(sdesc);
|
||||
}
|
27
src/backend/executor/nodeSeqscan.h
Normal file
27
src/backend/executor/nodeSeqscan.h
Normal file
@ -0,0 +1,27 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* nodeSeqscan.h--
|
||||
*
|
||||
*
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: nodeSeqscan.h,v 1.1.1.1 1996/07/09 06:21:27 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef NODESEQSCAN_H
|
||||
#define NODESEQSCAN_H
|
||||
|
||||
extern TupleTableSlot *SeqNext(SeqScan *node);
|
||||
extern TupleTableSlot *ExecSeqScan(SeqScan *node);
|
||||
extern Oid InitScanRelation(SeqScan *node, EState *estate,
|
||||
CommonScanState *scanstate, Plan *outerPlan);
|
||||
extern bool ExecInitSeqScan(SeqScan *node, EState *estate, Plan *parent);
|
||||
extern int ExecCountSlotsSeqScan(SeqScan *node);
|
||||
extern void ExecEndSeqScan(SeqScan *node);
|
||||
extern void ExecSeqReScan(SeqScan *node, ExprContext *exprCtxt, Plan* parent);
|
||||
extern void ExecSeqMarkPos(SeqScan *node);
|
||||
extern void ExecSeqRestrPos(SeqScan *node);
|
||||
|
||||
#endif /* NODESEQSCAN_H */
|
523
src/backend/executor/nodeSort.c
Normal file
523
src/backend/executor/nodeSort.c
Normal file
@ -0,0 +1,523 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* nodeSort.c--
|
||||
* Routines to handle sorting of relations into temporaries.
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/nodeSort.c,v 1.1.1.1 1996/07/09 06:21:27 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include "executor/executor.h"
|
||||
#include "executor/nodeSort.h"
|
||||
#include "utils/palloc.h"
|
||||
#include "utils/psort.h"
|
||||
#include "catalog/catalog.h"
|
||||
#include "storage/bufmgr.h"
|
||||
#include "optimizer/internal.h" /* for _TEMP_RELATION_ID_ */
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* FormSortKeys(node)
|
||||
*
|
||||
* Forms the structure containing information used to sort the relation.
|
||||
*
|
||||
* Returns an array of ScanKeyData.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
static ScanKey
|
||||
FormSortKeys(Sort *sortnode)
|
||||
{
|
||||
ScanKey sortkeys;
|
||||
List *targetList;
|
||||
List *tl;
|
||||
int keycount;
|
||||
Resdom *resdom;
|
||||
AttrNumber resno;
|
||||
Index reskey;
|
||||
Oid reskeyop;
|
||||
|
||||
/* ----------------
|
||||
* get information from the node
|
||||
* ----------------
|
||||
*/
|
||||
targetList = sortnode->plan.targetlist;
|
||||
keycount = sortnode->keycount;
|
||||
|
||||
/* ----------------
|
||||
* first allocate space for scan keys
|
||||
* ----------------
|
||||
*/
|
||||
if (keycount <= 0)
|
||||
elog(WARN, "FormSortKeys: keycount <= 0");
|
||||
sortkeys = (ScanKey) palloc(keycount * sizeof(ScanKeyData));
|
||||
|
||||
/* ----------------
|
||||
* form each scan key from the resdom info in the target list
|
||||
* ----------------
|
||||
*/
|
||||
foreach(tl, targetList) {
|
||||
TargetEntry *target = (TargetEntry *)lfirst(tl);
|
||||
resdom = target->resdom;
|
||||
resno = resdom->resno;
|
||||
reskey = resdom->reskey;
|
||||
reskeyop = resdom->reskeyop;
|
||||
|
||||
if (reskey > 0) {
|
||||
ScanKeyEntryInitialize(&sortkeys[reskey-1],
|
||||
0,
|
||||
resno,
|
||||
(RegProcedure) DatumGetInt32(reskeyop),
|
||||
(Datum) 0);
|
||||
}
|
||||
}
|
||||
|
||||
return sortkeys;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecSort
|
||||
*
|
||||
* old comments
|
||||
* Retrieves tuples fron the outer subtree and insert them into a
|
||||
* temporary relation. The temporary relation is then sorted and
|
||||
* the sorted relation is stored in the relation whose ID is indicated
|
||||
* in the 'tempid' field of this node.
|
||||
* Assumes that heap access method is used.
|
||||
*
|
||||
* Conditions:
|
||||
* -- none.
|
||||
*
|
||||
* Initial States:
|
||||
* -- the outer child is prepared to return the first tuple.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
TupleTableSlot *
|
||||
ExecSort(Sort *node)
|
||||
{
|
||||
EState *estate;
|
||||
SortState *sortstate;
|
||||
Plan *outerNode;
|
||||
ScanDirection dir;
|
||||
int keycount;
|
||||
ScanKey sortkeys;
|
||||
Relation tempRelation;
|
||||
Relation currentRelation;
|
||||
HeapScanDesc currentScanDesc;
|
||||
HeapTuple heapTuple;
|
||||
TupleTableSlot *slot;
|
||||
Buffer buffer;
|
||||
int tupCount = 0;
|
||||
|
||||
/* ----------------
|
||||
* get state info from node
|
||||
* ----------------
|
||||
*/
|
||||
SO1_printf("ExecSort: %s\n",
|
||||
"entering routine");
|
||||
|
||||
sortstate = node->sortstate;
|
||||
estate = node->plan.state;
|
||||
dir = estate->es_direction;
|
||||
|
||||
/* ----------------
|
||||
* the first time we call this, we retrieve all tuples
|
||||
* from the subplan into a temporary relation and then
|
||||
* we sort the relation. Subsequent calls return tuples
|
||||
* from the temporary relation.
|
||||
* ----------------
|
||||
*/
|
||||
|
||||
if (sortstate->sort_Flag == false) {
|
||||
SO1_printf("ExecSort: %s\n",
|
||||
"sortstate == false -> sorting subplan");
|
||||
/* ----------------
|
||||
* set all relations to be scanned in the forward direction
|
||||
* while creating the temporary relation.
|
||||
* ----------------
|
||||
*/
|
||||
estate->es_direction = EXEC_FRWD;
|
||||
|
||||
/* ----------------
|
||||
* if we couldn't create the temp or current relations then
|
||||
* we print a warning and return NULL.
|
||||
* ----------------
|
||||
*/
|
||||
tempRelation = sortstate->sort_TempRelation;
|
||||
if (tempRelation == NULL) {
|
||||
elog(DEBUG, "ExecSort: temp relation is NULL! aborting...");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
currentRelation = sortstate->csstate.css_currentRelation;
|
||||
if (currentRelation == NULL) {
|
||||
elog(DEBUG, "ExecSort: current relation is NULL! aborting...");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* retrieve tuples from the subplan and
|
||||
* insert them in the temporary relation
|
||||
* ----------------
|
||||
*/
|
||||
outerNode = outerPlan((Plan *) node);
|
||||
SO1_printf("ExecSort: %s\n",
|
||||
"inserting tuples into tempRelation");
|
||||
|
||||
for (;;) {
|
||||
slot = ExecProcNode(outerNode, (Plan*)node);
|
||||
|
||||
if (TupIsNull(slot))
|
||||
break;
|
||||
|
||||
tupCount++;
|
||||
|
||||
heapTuple = slot->val;
|
||||
|
||||
heap_insert(tempRelation, /* relation desc */
|
||||
heapTuple); /* heap tuple to insert */
|
||||
|
||||
ExecClearTuple(slot);
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* now sort the tuples in our temporary relation
|
||||
* into a new sorted relation using psort()
|
||||
*
|
||||
* psort() seems to require that the relations
|
||||
* are created and opened in advance.
|
||||
* -cim 1/25/90
|
||||
* ----------------
|
||||
*/
|
||||
keycount = node->keycount;
|
||||
sortkeys = (ScanKey)sortstate->sort_Keys;
|
||||
SO1_printf("ExecSort: %s\n",
|
||||
"calling psort");
|
||||
|
||||
/*
|
||||
* If no tuples were fetched from the proc node return NULL now
|
||||
* psort dumps it if 0 tuples are in the relation and I don't want
|
||||
* to try to debug *that* routine!!
|
||||
*/
|
||||
if (tupCount == 0)
|
||||
return NULL;
|
||||
|
||||
psort(tempRelation, /* old relation */
|
||||
currentRelation, /* new relation */
|
||||
keycount, /* number keys */
|
||||
sortkeys); /* keys */
|
||||
|
||||
if (currentRelation == NULL) {
|
||||
elog(DEBUG, "ExecSort: sorted relation is NULL! aborting...");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* restore to user specified direction
|
||||
* ----------------
|
||||
*/
|
||||
estate->es_direction = dir;
|
||||
|
||||
/* ----------------
|
||||
* now initialize the scan descriptor to scan the
|
||||
* sorted relation and update the sortstate information
|
||||
* ----------------
|
||||
*/
|
||||
currentScanDesc = heap_beginscan(currentRelation, /* relation */
|
||||
ScanDirectionIsBackward(dir),
|
||||
/* bkwd flag */
|
||||
NowTimeQual, /* time qual */
|
||||
0, /* num scan keys */
|
||||
NULL); /* scan keys */
|
||||
|
||||
sortstate->csstate.css_currentRelation = currentRelation;
|
||||
sortstate->csstate.css_currentScanDesc = currentScanDesc;
|
||||
|
||||
/* ----------------
|
||||
* make sure the tuple descriptor is up to date
|
||||
* ----------------
|
||||
*/
|
||||
slot = sortstate->csstate.css_ScanTupleSlot;
|
||||
|
||||
slot->ttc_tupleDescriptor =
|
||||
RelationGetTupleDescriptor(currentRelation);
|
||||
|
||||
/* ----------------
|
||||
* finally set the sorted flag to true
|
||||
* ----------------
|
||||
*/
|
||||
sortstate->sort_Flag = true;
|
||||
}
|
||||
else {
|
||||
slot = sortstate->csstate.css_ScanTupleSlot;
|
||||
}
|
||||
|
||||
SO1_printf("ExecSort: %s\n",
|
||||
"retrieveing tuple from sorted relation");
|
||||
|
||||
/* ----------------
|
||||
* at this point we know we have a sorted relation so
|
||||
* we preform a simple scan on it with amgetnext()..
|
||||
* ----------------
|
||||
*/
|
||||
currentScanDesc = sortstate->csstate.css_currentScanDesc;
|
||||
|
||||
heapTuple = heap_getnext(currentScanDesc, /* scan desc */
|
||||
ScanDirectionIsBackward(dir),
|
||||
/* bkwd flag */
|
||||
&buffer); /* return: buffer */
|
||||
|
||||
/* Increase the pin count on the buffer page, because the tuple stored in
|
||||
the slot also points to it (as well as the scan descriptor). If we
|
||||
don't, ExecStoreTuple will decrease the pin count on the next iteration.
|
||||
- 01/09/93 */
|
||||
|
||||
if (buffer != InvalidBuffer)
|
||||
IncrBufferRefCount(buffer);
|
||||
|
||||
return ExecStoreTuple(heapTuple, /* tuple to store */
|
||||
slot, /* slot to store in */
|
||||
buffer, /* this tuple's buffer */
|
||||
false); /* don't free stuff from amgetnext */
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecInitSort
|
||||
*
|
||||
* old comments
|
||||
* Creates the run-time state information for the sort node
|
||||
* produced by the planner and initailizes its outer subtree.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
bool
|
||||
ExecInitSort(Sort *node, EState *estate, Plan *parent)
|
||||
{
|
||||
SortState *sortstate;
|
||||
Plan *outerPlan;
|
||||
ScanKey sortkeys;
|
||||
TupleDesc tupType;
|
||||
Oid tempOid;
|
||||
Oid sortOid;
|
||||
Relation tempDesc;
|
||||
Relation sortedDesc;
|
||||
|
||||
SO1_printf("ExecInitSort: %s\n",
|
||||
"initializing sort node");
|
||||
|
||||
/* ----------------
|
||||
* assign the node's execution state
|
||||
* ----------------
|
||||
*/
|
||||
node->plan.state = estate;
|
||||
|
||||
/* ----------------
|
||||
* create state structure
|
||||
* ----------------
|
||||
*/
|
||||
sortstate = makeNode(SortState);
|
||||
sortstate->sort_Flag = 0;
|
||||
sortstate->sort_Keys = NULL;
|
||||
sortstate->sort_TempRelation = NULL;
|
||||
|
||||
node->sortstate = sortstate;
|
||||
|
||||
/* ----------------
|
||||
* Miscellanious initialization
|
||||
*
|
||||
* + assign node's base_id
|
||||
* + assign debugging hooks
|
||||
*
|
||||
* Sort nodes don't initialize their ExprContexts because
|
||||
* they never call ExecQual or ExecTargetList.
|
||||
* ----------------
|
||||
*/
|
||||
ExecAssignNodeBaseInfo(estate, &sortstate->csstate.cstate, parent);
|
||||
|
||||
#define SORT_NSLOTS 1
|
||||
/* ----------------
|
||||
* tuple table initialization
|
||||
*
|
||||
* sort nodes only return scan tuples from their sorted
|
||||
* relation.
|
||||
* ----------------
|
||||
*/
|
||||
ExecInitScanTupleSlot(estate, &sortstate->csstate);
|
||||
ExecInitResultTupleSlot(estate, &sortstate->csstate.cstate);
|
||||
|
||||
/* ----------------
|
||||
* initializes child nodes
|
||||
* ----------------
|
||||
*/
|
||||
outerPlan = outerPlan((Plan *) node);
|
||||
ExecInitNode(outerPlan, estate, (Plan *) node);
|
||||
|
||||
/* ----------------
|
||||
* initialize sortstate information
|
||||
* ----------------
|
||||
*/
|
||||
sortkeys = FormSortKeys(node);
|
||||
sortstate->sort_Keys = sortkeys;
|
||||
sortstate->sort_Flag = false;
|
||||
|
||||
/* ----------------
|
||||
* initialize tuple type. no need to initialize projection
|
||||
* info because this node doesn't do projections.
|
||||
* ----------------
|
||||
*/
|
||||
ExecAssignScanTypeFromOuterPlan((Plan *) node, &sortstate->csstate);
|
||||
sortstate->csstate.cstate.cs_ProjInfo = NULL;
|
||||
|
||||
/* ----------------
|
||||
* get type information needed for ExecCreatR
|
||||
* ----------------
|
||||
*/
|
||||
tupType = ExecGetScanType(&sortstate->csstate);
|
||||
|
||||
/* ----------------
|
||||
* ExecCreatR wants its second argument to be an object id of
|
||||
* a relation in the range table or _TEMP_RELATION_ID_
|
||||
* indicating that the relation is not in the range table.
|
||||
*
|
||||
* In the second case ExecCreatR creates a temp relation.
|
||||
* (currently this is the only case we support -cim 10/16/89)
|
||||
* ----------------
|
||||
*/
|
||||
tempOid = node->tempid;
|
||||
sortOid = _TEMP_RELATION_ID_;
|
||||
|
||||
/* ----------------
|
||||
* create the temporary relations
|
||||
* ----------------
|
||||
*/
|
||||
/* len = ExecTargetListLength(node->plan.targetlist); */
|
||||
tempDesc = ExecCreatR(tupType, tempOid);
|
||||
sortedDesc = ExecCreatR(tupType, sortOid);
|
||||
|
||||
/* ----------------
|
||||
* save the relation descriptor in the sortstate
|
||||
* ----------------
|
||||
*/
|
||||
sortstate->sort_TempRelation = tempDesc;
|
||||
sortstate->csstate.css_currentRelation = sortedDesc;
|
||||
SO1_printf("ExecInitSort: %s\n",
|
||||
"sort node initialized");
|
||||
|
||||
/* ----------------
|
||||
* return relation oid of temporary sort relation in a list
|
||||
* (someday -- for now we return LispTrue... cim 10/12/89)
|
||||
* ----------------
|
||||
*/
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int
|
||||
ExecCountSlotsSort(Sort *node)
|
||||
{
|
||||
return ExecCountSlotsNode(outerPlan((Plan *)node)) +
|
||||
ExecCountSlotsNode(innerPlan((Plan *)node)) +
|
||||
SORT_NSLOTS;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecEndSort(node)
|
||||
*
|
||||
* old comments
|
||||
* destroys the temporary relation.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
void
|
||||
ExecEndSort(Sort *node)
|
||||
{
|
||||
SortState *sortstate;
|
||||
Relation tempRelation;
|
||||
Relation sortedRelation;
|
||||
Plan *outerPlan;
|
||||
|
||||
/* ----------------
|
||||
* get info from the sort state
|
||||
* ----------------
|
||||
*/
|
||||
SO1_printf("ExecEndSort: %s\n",
|
||||
"shutting down sort node");
|
||||
|
||||
sortstate = node->sortstate;
|
||||
tempRelation = sortstate->sort_TempRelation;
|
||||
sortedRelation = sortstate->csstate.css_currentRelation;
|
||||
|
||||
heap_destroyr(tempRelation);
|
||||
heap_destroyr(sortedRelation);
|
||||
|
||||
|
||||
/* ----------------
|
||||
* close the sorted relation and shut down the scan.
|
||||
* ----------------
|
||||
*/
|
||||
ExecCloseR((Plan *) node);
|
||||
|
||||
/* ----------------
|
||||
* shut down the subplan
|
||||
* ----------------
|
||||
*/
|
||||
outerPlan = outerPlan((Plan *) node);
|
||||
ExecEndNode(outerPlan, (Plan*)node);
|
||||
|
||||
/* ----------------
|
||||
* clean out the tuple table
|
||||
* ----------------
|
||||
*/
|
||||
ExecClearTuple(sortstate->csstate.css_ScanTupleSlot);
|
||||
|
||||
SO1_printf("ExecEndSort: %s\n",
|
||||
"sort node shutdown");
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecSortMarkPos
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
void
|
||||
ExecSortMarkPos(Sort *node)
|
||||
{
|
||||
SortState *sortstate;
|
||||
HeapScanDesc sdesc;
|
||||
|
||||
/* ----------------
|
||||
* if we haven't sorted yet, just return
|
||||
* ----------------
|
||||
*/
|
||||
sortstate = node->sortstate;
|
||||
if (sortstate->sort_Flag == false)
|
||||
return;
|
||||
|
||||
sdesc = sortstate->csstate.css_currentScanDesc;
|
||||
heap_markpos(sdesc);
|
||||
return;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecSortRestrPos
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
void
|
||||
ExecSortRestrPos(Sort *node)
|
||||
{
|
||||
SortState *sortstate;
|
||||
HeapScanDesc sdesc;
|
||||
|
||||
/* ----------------
|
||||
* if we haven't sorted yet, just return.
|
||||
* ----------------
|
||||
*/
|
||||
sortstate = node->sortstate;
|
||||
if (sortstate->sort_Flag == false)
|
||||
return;
|
||||
|
||||
/* ----------------
|
||||
* restore the scan to the previously marked position
|
||||
* ----------------
|
||||
*/
|
||||
sdesc = sortstate->csstate.css_currentScanDesc;
|
||||
heap_restrpos(sdesc);
|
||||
}
|
23
src/backend/executor/nodeSort.h
Normal file
23
src/backend/executor/nodeSort.h
Normal file
@ -0,0 +1,23 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* nodeSort.h--
|
||||
*
|
||||
*
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: nodeSort.h,v 1.1.1.1 1996/07/09 06:21:27 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef NODESORT_H
|
||||
#define NODESORT_H
|
||||
|
||||
extern TupleTableSlot *ExecSort(Sort *node);
|
||||
extern bool ExecInitSort(Sort *node, EState *estate, Plan *parent);
|
||||
extern int ExecCountSlotsSort(Sort *node);
|
||||
extern void ExecEndSort(Sort *node);
|
||||
extern void ExecSortMarkPos(Sort *node);
|
||||
extern void ExecSortRestrPos(Sort *node);
|
||||
|
||||
#endif /* NODESORT_H */
|
503
src/backend/executor/nodeTee.c
Normal file
503
src/backend/executor/nodeTee.c
Normal file
@ -0,0 +1,503 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* nodeTee.c--
|
||||
*
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* DESCRIPTION
|
||||
* This code provides support for a tee node, which allows multiple
|
||||
* parent in a megaplan.
|
||||
*
|
||||
* INTERFACE ROUTINES
|
||||
* ExecTee
|
||||
* ExecInitTee
|
||||
* ExecEndTee
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/Attic/nodeTee.c,v 1.1.1.1 1996/07/09 06:21:27 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include <sys/file.h>
|
||||
#include "utils/palloc.h"
|
||||
#include "utils/relcache.h"
|
||||
#include "storage/bufmgr.h" /* for IncrBufferRefCount */
|
||||
#include "optimizer/internal.h"
|
||||
#include "executor/executor.h"
|
||||
#include "executor/nodeTee.h"
|
||||
#include "catalog/catalog.h"
|
||||
#include "tcop/pquery.h"
|
||||
|
||||
/* ------------------------------------------------------------------
|
||||
* ExecInitTee
|
||||
*
|
||||
* Create tee state
|
||||
*
|
||||
* ------------------------------------------------------------------
|
||||
*/
|
||||
bool
|
||||
ExecInitTee(Tee* node, EState *currentEstate, Plan * parent)
|
||||
{
|
||||
TeeState *teeState;
|
||||
Plan *outerPlan;
|
||||
int len;
|
||||
Relation bufferRel;
|
||||
TupleDesc tupType;
|
||||
EState *estate;
|
||||
|
||||
/* it is possible that the Tee has already been initialized
|
||||
since it can be reached by multiple parents.
|
||||
If it is already initialized, simply return and do
|
||||
not initialize the children nodes again
|
||||
*/
|
||||
if (node->plan.state)
|
||||
return TRUE;
|
||||
|
||||
/* ----------------
|
||||
* assign the node's execution state
|
||||
* ----------------
|
||||
*/
|
||||
/* make a new executor state, because we have a different
|
||||
es_range_table */
|
||||
|
||||
/* node->plan.state = estate;*/
|
||||
|
||||
estate = CreateExecutorState();
|
||||
estate->es_direction = currentEstate->es_direction;
|
||||
estate->es_BaseId = currentEstate->es_BaseId;
|
||||
estate->es_BaseId = currentEstate->es_BaseId;
|
||||
estate->es_tupleTable = currentEstate->es_tupleTable;
|
||||
estate->es_refcount = currentEstate->es_refcount;
|
||||
estate->es_junkFilter = currentEstate->es_junkFilter;
|
||||
|
||||
/* use the range table for Tee subplan since the range tables
|
||||
for the two parents may be different */
|
||||
if (node->rtentries)
|
||||
estate->es_range_table = node->rtentries;
|
||||
else
|
||||
estate->es_range_table = currentEstate->es_range_table;
|
||||
|
||||
node->plan.state = estate;
|
||||
|
||||
|
||||
/* ----------------
|
||||
* create teeState structure
|
||||
* ----------------
|
||||
*/
|
||||
teeState = makeNode(TeeState);
|
||||
teeState->tee_leftPlace = 0;
|
||||
teeState->tee_rightPlace = 0;
|
||||
teeState->tee_lastPlace = 0;
|
||||
teeState->tee_bufferRel = NULL;
|
||||
teeState->tee_leftScanDesc = NULL;
|
||||
teeState->tee_rightScanDesc = NULL;
|
||||
|
||||
|
||||
node->teestate = teeState;
|
||||
|
||||
/* ----------------
|
||||
* Miscellanious initialization
|
||||
*
|
||||
* + assign node's base_id
|
||||
* + assign debugging hooks and
|
||||
* + create expression context for node
|
||||
* ----------------
|
||||
*/
|
||||
ExecAssignNodeBaseInfo(estate, &(teeState->cstate), parent);
|
||||
ExecAssignExprContext(estate, &(teeState->cstate));
|
||||
|
||||
#define TEE_NSLOTS 2
|
||||
/* ----------------
|
||||
* initialize tuple slots
|
||||
* ----------------
|
||||
*/
|
||||
ExecInitResultTupleSlot(estate, &(teeState->cstate));
|
||||
|
||||
/* initialize child nodes */
|
||||
outerPlan = outerPlan((Plan*) node);
|
||||
ExecInitNode(outerPlan, estate, (Plan*) node);
|
||||
|
||||
/* ----------------
|
||||
* the tuple type info is from the outer plan of this node
|
||||
* the result type is also the same as the outerplan
|
||||
*/
|
||||
ExecAssignResultTypeFromOuterPlan((Plan*) node, &(teeState->cstate));
|
||||
ExecAssignProjectionInfo((Plan*)node, &teeState->cstate);
|
||||
|
||||
/* ---------------------------------------
|
||||
initialize temporary relation to buffer tuples
|
||||
*/
|
||||
tupType = ExecGetResultType(&(teeState->cstate));
|
||||
len = ExecTargetListLength(((Plan*)node)->targetlist);
|
||||
|
||||
/* bufferRel = ExecCreatR(len, tupType, _TEMP_RELATION_ID_); */
|
||||
|
||||
/* create a catalogued relation even though this is a temporary relation */
|
||||
/* cleanup of catalogued relations is easier to do */
|
||||
|
||||
if (node->teeTableName[0] != '\0') {
|
||||
Relation r;
|
||||
|
||||
teeState->tee_bufferRelname = pstrdup(node->teeTableName);
|
||||
|
||||
/* we are given an tee table name,
|
||||
if a relation by that name exists, then we open it,
|
||||
else we create it and then open it */
|
||||
r = RelationNameGetRelation(teeState->tee_bufferRelname);
|
||||
|
||||
if (RelationIsValid(r))
|
||||
bufferRel = heap_openr(teeState->tee_bufferRelname);
|
||||
else
|
||||
bufferRel = heap_open(heap_create(teeState->tee_bufferRelname,
|
||||
/*FIX */ NULL,
|
||||
'n',
|
||||
DEFAULT_SMGR,
|
||||
tupType));
|
||||
}
|
||||
else {
|
||||
sprintf(teeState->tee_bufferRelname,
|
||||
"ttemp_%d", /* 'ttemp' for 'tee' temporary*/
|
||||
newoid());
|
||||
/* bufferRel = ExecCreatR(len, tupType, _TEMP_RELATION_ID); */
|
||||
bufferRel = heap_open(heap_create(teeState->tee_bufferRelname,
|
||||
NULL, /*XXX */
|
||||
'n',
|
||||
DEFAULT_SMGR,
|
||||
tupType));
|
||||
}
|
||||
|
||||
teeState->tee_bufferRel = bufferRel;
|
||||
|
||||
/*initialize a memory context for allocating thing like scan descriptors */
|
||||
/* we do this so that on cleanup of the tee, we can free things.
|
||||
if we didn't have our own memory context, we would be in the memory
|
||||
context of the portal that we happen to be using at the moment */
|
||||
|
||||
teeState->tee_mcxt = (MemoryContext)CreateGlobalMemory(teeState->tee_bufferRelname);
|
||||
|
||||
/* don't initialize the scan descriptors here
|
||||
because it's not good to initialize scan descriptors on empty
|
||||
rels. Wait until the scan descriptors are needed
|
||||
before initializing them. */
|
||||
|
||||
teeState->tee_leftScanDesc = NULL;
|
||||
teeState->tee_rightScanDesc = NULL;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int
|
||||
ExecCountSlotsTee(Tee *node)
|
||||
{
|
||||
/* Tee nodes can't have innerPlans */
|
||||
return ExecCountSlotsNode(outerPlan(node)) + TEE_NSLOTS;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
initTeeScanDescs
|
||||
initializes the left and right scandescs on the temporary
|
||||
relation of a Tee node
|
||||
|
||||
must open two separate scan descriptors,
|
||||
because the left and right scans may be at different points
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
void
|
||||
initTeeScanDescs(Tee* node)
|
||||
{
|
||||
TeeState *teeState;
|
||||
Relation bufferRel;
|
||||
ScanDirection dir;
|
||||
MemoryContext orig;
|
||||
|
||||
teeState = node->teestate;
|
||||
if (teeState->tee_leftScanDesc && teeState->tee_rightScanDesc)
|
||||
return;
|
||||
|
||||
orig = CurrentMemoryContext;
|
||||
MemoryContextSwitchTo(teeState->tee_mcxt);
|
||||
|
||||
bufferRel = teeState->tee_bufferRel;
|
||||
dir = ((Plan*)node)->state->es_direction; /* backwards not handled yet XXX */
|
||||
|
||||
if (teeState->tee_leftScanDesc == NULL)
|
||||
{
|
||||
teeState->tee_leftScanDesc = heap_beginscan(bufferRel,
|
||||
ScanDirectionIsBackward(dir),
|
||||
NowTimeQual, /* time qual */
|
||||
0, /* num scan keys */
|
||||
NULL /* scan keys */
|
||||
);
|
||||
}
|
||||
if (teeState->tee_rightScanDesc == NULL)
|
||||
{
|
||||
teeState->tee_rightScanDesc = heap_beginscan(bufferRel,
|
||||
ScanDirectionIsBackward(dir),
|
||||
NowTimeQual, /* time qual */
|
||||
0, /* num scan keys */
|
||||
NULL /* scan keys */
|
||||
);
|
||||
}
|
||||
|
||||
MemoryContextSwitchTo(orig);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecTee(node)
|
||||
*
|
||||
*
|
||||
* A Tee serves to connect a subplan to multiple parents.
|
||||
* the subplan is always the outplan of the Tee node.
|
||||
*
|
||||
* The Tee gets requests from either leftParent or rightParent,
|
||||
* fetches the result tuple from the child, and then
|
||||
* stored the result into a temporary relation (serving as a queue).
|
||||
* leftPlace and rightPlace keep track of where the left and rightParents
|
||||
* are.
|
||||
* If a parent requests a tuple and that parent is not at the end
|
||||
* of the temporary relation, then the request is satisfied from
|
||||
* the queue instead of by executing the child plan
|
||||
*
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
|
||||
TupleTableSlot*
|
||||
ExecTee(Tee *node, Plan *parent)
|
||||
{
|
||||
EState *estate;
|
||||
TeeState *teeState;
|
||||
int leftPlace, rightPlace, lastPlace;
|
||||
int branch;
|
||||
TupleTableSlot* result;
|
||||
TupleTableSlot* slot;
|
||||
Plan *childNode;
|
||||
ScanDirection dir;
|
||||
HeapTuple heapTuple;
|
||||
Relation bufferRel;
|
||||
HeapScanDesc scanDesc;
|
||||
Buffer buffer;
|
||||
|
||||
estate = ((Plan*)node)->state;
|
||||
teeState = node->teestate;
|
||||
leftPlace = teeState->tee_leftPlace;
|
||||
rightPlace = teeState->tee_rightPlace;
|
||||
lastPlace = teeState->tee_lastPlace;
|
||||
bufferRel = teeState->tee_bufferRel;
|
||||
|
||||
childNode = outerPlan(node);
|
||||
|
||||
dir = estate->es_direction;
|
||||
|
||||
/* XXX doesn't handle backwards direction yet */
|
||||
|
||||
if (parent == node->leftParent) {
|
||||
branch = leftPlace;
|
||||
}
|
||||
else
|
||||
if ( (parent == node->rightParent) || (parent == (Plan*) node))
|
||||
/* the tee node could be the root node of the plan,
|
||||
in which case, we treat it like a right-parent pull*/
|
||||
{
|
||||
branch = rightPlace;
|
||||
}
|
||||
else
|
||||
{
|
||||
elog(WARN,"A Tee node can only be executed from its left or right parent\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (branch == lastPlace)
|
||||
{ /* we're at the end of the queue already,
|
||||
- get a new tuple from the child plan,
|
||||
- store it in the queue,
|
||||
- increment lastPlace,
|
||||
- increment leftPlace or rightPlace as appropriate,
|
||||
- and return result
|
||||
*/
|
||||
slot = ExecProcNode(childNode, (Plan*)node);
|
||||
if (!TupIsNull(slot))
|
||||
{
|
||||
heapTuple = slot->val;
|
||||
|
||||
/* insert into temporary relation */
|
||||
heap_insert(bufferRel, heapTuple);
|
||||
|
||||
/* once there is data in the temporary relation,
|
||||
ensure that the left and right scandescs are initialized */
|
||||
initTeeScanDescs(node);
|
||||
|
||||
scanDesc = (parent == node->leftParent) ?
|
||||
teeState->tee_leftScanDesc : teeState->tee_rightScanDesc;
|
||||
|
||||
{
|
||||
/* move the scandesc forward so we don't re-read this tuple later */
|
||||
HeapTuple throwAway;
|
||||
/* Buffer buffer;*/
|
||||
throwAway = heap_getnext(scanDesc,
|
||||
ScanDirectionIsBackward(dir),
|
||||
/* &buffer */
|
||||
(Buffer*)NULL);
|
||||
}
|
||||
|
||||
/* set the shouldFree field of the child's slot so that
|
||||
when the child's slot is free'd, this tuple isn't free'd also */
|
||||
/* does this mean this tuple has to be garbage collected later??*/
|
||||
slot->ttc_shouldFree = false;
|
||||
|
||||
teeState->tee_lastPlace = lastPlace + 1;
|
||||
}
|
||||
result = slot;
|
||||
}
|
||||
else
|
||||
{/* the desired data already exists in the temporary relation */
|
||||
scanDesc = (parent == node->leftParent) ?
|
||||
teeState->tee_leftScanDesc : teeState->tee_rightScanDesc;
|
||||
|
||||
heapTuple = heap_getnext(scanDesc,
|
||||
ScanDirectionIsBackward(dir),
|
||||
&buffer);
|
||||
|
||||
/* Increase the pin count on the buffer page, because the
|
||||
tuple stored in the slot also points to it (as well as
|
||||
the scan descriptor). If we don't, ExecStoreTuple will
|
||||
decrease the pin count on the next iteration. */
|
||||
|
||||
if (buffer != InvalidBuffer)
|
||||
IncrBufferRefCount(buffer);
|
||||
|
||||
slot = teeState->cstate.cs_ResultTupleSlot;
|
||||
slot->ttc_tupleDescriptor = RelationGetTupleDescriptor(bufferRel);
|
||||
|
||||
result = ExecStoreTuple(heapTuple,/* tuple to store */
|
||||
slot, /* slot to store in */
|
||||
buffer,/* this tuple's buffer */
|
||||
false); /* don't free stuff from heap_getnext */
|
||||
|
||||
}
|
||||
|
||||
if (parent == node->leftParent)
|
||||
{
|
||||
teeState->tee_leftPlace = leftPlace+1;
|
||||
}
|
||||
else
|
||||
{
|
||||
teeState->tee_rightPlace = rightPlace+1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecTeeReScan(node)
|
||||
*
|
||||
* Rescans the relation.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
void
|
||||
ExecTeeReScan(Tee *node, ExprContext *exprCtxt, Plan *parent)
|
||||
{
|
||||
|
||||
EState *estate;
|
||||
TeeState *teeState;
|
||||
ScanDirection dir;
|
||||
|
||||
estate = ((Plan*)node)->state;
|
||||
teeState = node->teestate;
|
||||
|
||||
dir = estate->es_direction;
|
||||
|
||||
/* XXX doesn't handle backwards direction yet */
|
||||
|
||||
if (parent == node->leftParent) {
|
||||
if (teeState->tee_leftScanDesc)
|
||||
{
|
||||
heap_rescan(teeState->tee_leftScanDesc,
|
||||
ScanDirectionIsBackward(dir),
|
||||
NULL);
|
||||
teeState->tee_leftPlace = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (teeState->tee_rightScanDesc)
|
||||
{
|
||||
heap_rescan(teeState->tee_leftScanDesc,
|
||||
ScanDirectionIsBackward(dir),
|
||||
NULL);
|
||||
teeState->tee_rightPlace = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------
|
||||
* ExecEndTee
|
||||
*
|
||||
* End the Tee node, and free up any storage
|
||||
* since a Tee node can be downstream of multiple parent nodes,
|
||||
* only free when both parents are done
|
||||
* --------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
void
|
||||
ExecEndTee(Tee* node, Plan* parent)
|
||||
{
|
||||
EState *estate;
|
||||
TeeState *teeState;
|
||||
int leftPlace, rightPlace, lastPlace;
|
||||
Relation bufferRel;
|
||||
MemoryContext orig;
|
||||
|
||||
estate = ((Plan*)node)->state;
|
||||
teeState = node->teestate;
|
||||
leftPlace = teeState->tee_leftPlace;
|
||||
rightPlace = teeState->tee_rightPlace;
|
||||
lastPlace = teeState->tee_lastPlace;
|
||||
|
||||
if (!node->leftParent || parent == node->leftParent)
|
||||
leftPlace = -1;
|
||||
|
||||
if (!node->rightParent || parent == node->rightParent)
|
||||
rightPlace = -1;
|
||||
|
||||
if (parent == (Plan*)node)
|
||||
rightPlace = leftPlace = -1;
|
||||
|
||||
teeState->tee_leftPlace = leftPlace;
|
||||
teeState->tee_rightPlace = rightPlace;
|
||||
if ( (leftPlace == -1) && (rightPlace == -1) )
|
||||
{
|
||||
/* remove the temporary relations */
|
||||
/* and close the scan descriptors */
|
||||
|
||||
bufferRel = teeState->tee_bufferRel;
|
||||
if (bufferRel) {
|
||||
heap_destroyr(bufferRel);
|
||||
teeState->tee_bufferRel = NULL;
|
||||
if (teeState->tee_mcxt) {
|
||||
orig = CurrentMemoryContext;
|
||||
MemoryContextSwitchTo(teeState->tee_mcxt);
|
||||
}
|
||||
|
||||
if (teeState->tee_leftScanDesc)
|
||||
{
|
||||
heap_endscan(teeState->tee_leftScanDesc);
|
||||
teeState->tee_leftScanDesc = NULL;
|
||||
}
|
||||
if (teeState->tee_rightScanDesc)
|
||||
{
|
||||
heap_endscan(teeState->tee_rightScanDesc);
|
||||
teeState->tee_rightScanDesc = NULL;
|
||||
}
|
||||
|
||||
if (teeState->tee_mcxt) {
|
||||
MemoryContextSwitchTo(orig);
|
||||
teeState->tee_mcxt = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
22
src/backend/executor/nodeTee.h
Normal file
22
src/backend/executor/nodeTee.h
Normal file
@ -0,0 +1,22 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* nodeTee.h--
|
||||
* support functions for a Tee executor node
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: nodeTee.h,v 1.1.1.1 1996/07/09 06:21:27 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#ifndef NODETEE_H
|
||||
#define NODETEE_H
|
||||
|
||||
extern TupleTableSlot* ExecTee(Tee* node, Plan* parent);
|
||||
extern bool ExecInitTee(Tee* node, EState* estate, Plan* parent);
|
||||
extern void ExecTeeReScan(Tee *node, ExprContext *exprCtxt, Plan *parent);
|
||||
extern void ExecEndTee(Tee* node, Plan* parent);
|
||||
extern int ExecCountSlotsTee(Tee* node);
|
||||
|
||||
#endif /* NODETEE_H */
|
316
src/backend/executor/nodeUnique.c
Normal file
316
src/backend/executor/nodeUnique.c
Normal file
@ -0,0 +1,316 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* nodeUnique.c--
|
||||
* Routines to handle unique'ing of queries where appropriate
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/nodeUnique.c,v 1.1.1.1 1996/07/09 06:21:27 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
/*
|
||||
* INTERFACE ROUTINES
|
||||
* ExecUnique - generate a unique'd temporary relation
|
||||
* ExecInitUnique - initialize node and subnodes..
|
||||
* ExecEndUnique - shutdown node and subnodes
|
||||
*
|
||||
* NOTES
|
||||
* Assumes tuples returned from subplan arrive in
|
||||
* sorted order.
|
||||
*
|
||||
*/
|
||||
#include "executor/executor.h"
|
||||
#include "executor/nodeUnique.h"
|
||||
#include "optimizer/clauses.h"
|
||||
#include "access/printtup.h" /* for typtoout() */
|
||||
#include "utils/builtins.h" /* for namecpy()*/
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecIdenticalTuples
|
||||
*
|
||||
* This is a hack function used by ExecUnique to see if
|
||||
* two tuples are identical. This should be provided
|
||||
* by the heap tuple code but isn't. The real problem
|
||||
* is that we assume we can byte compare tuples to determine
|
||||
* if they are "equal". In fact, if we have user defined
|
||||
* types there may be problems because it's possible that
|
||||
* an ADT may have multiple representations with the
|
||||
* same ADT value. -cim
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
static bool /* true if tuples are identical, false otherwise */
|
||||
ExecIdenticalTuples(TupleTableSlot *t1, TupleTableSlot *t2)
|
||||
{
|
||||
HeapTuple h1;
|
||||
HeapTuple h2;
|
||||
char *d1;
|
||||
char *d2;
|
||||
int len;
|
||||
|
||||
h1 = t1->val;
|
||||
h2 = t2->val;
|
||||
|
||||
/* ----------------
|
||||
* if tuples aren't the same length then they are
|
||||
* obviously different (one may have null attributes).
|
||||
* ----------------
|
||||
*/
|
||||
if (h1->t_len != h2->t_len)
|
||||
return false;
|
||||
|
||||
/* ----------------
|
||||
* if the tuples have different header offsets then
|
||||
* they are different. This will prevent us from returning
|
||||
* true when comparing tuples of one attribute where one of
|
||||
* two we're looking at is null (t_len - t_hoff == 0).
|
||||
* THE t_len FIELDS CAN BE THE SAME IN THIS CASE!!
|
||||
* ----------------
|
||||
*/
|
||||
if (h1->t_hoff != h2->t_hoff)
|
||||
return false;
|
||||
|
||||
/* ----------------
|
||||
* ok, now get the pointers to the data and the
|
||||
* size of the attribute portion of the tuple.
|
||||
* ----------------
|
||||
*/
|
||||
d1 = (char *) GETSTRUCT(h1);
|
||||
d2 = (char *) GETSTRUCT(h2);
|
||||
len = (int) h1->t_len - (int) h1->t_hoff;
|
||||
|
||||
/* ----------------
|
||||
* byte compare the data areas and return the result.
|
||||
* ----------------
|
||||
*/
|
||||
if (memcmp(d1, d2, len) != 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecUnique
|
||||
*
|
||||
* This is a very simple node which filters out duplicate
|
||||
* tuples from a stream of sorted tuples from a subplan.
|
||||
*
|
||||
* XXX see comments below regarding freeing tuples.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
TupleTableSlot * /* return: a tuple or NULL */
|
||||
ExecUnique(Unique *node)
|
||||
{
|
||||
UniqueState *uniquestate;
|
||||
TupleTableSlot *resultTupleSlot;
|
||||
TupleTableSlot *slot;
|
||||
Plan *outerPlan;
|
||||
char *uniqueAttr;
|
||||
AttrNumber uniqueAttrNum;
|
||||
TupleDesc tupDesc;
|
||||
Oid typoutput;
|
||||
|
||||
/* ----------------
|
||||
* get information from the node
|
||||
* ----------------
|
||||
*/
|
||||
uniquestate = node->uniquestate;
|
||||
outerPlan = outerPlan((Plan *) node);
|
||||
resultTupleSlot = uniquestate->cs_ResultTupleSlot;
|
||||
uniqueAttr = node->uniqueAttr;
|
||||
uniqueAttrNum = node->uniqueAttrNum;
|
||||
|
||||
if (uniqueAttr) {
|
||||
tupDesc = ExecGetResultType(uniquestate);
|
||||
typoutput = typtoout((Oid)tupDesc->attrs[uniqueAttrNum]->atttypid);
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* now loop, returning only non-duplicate tuples.
|
||||
* We assume that the tuples arrive in sorted order
|
||||
* so we can detect duplicates easily.
|
||||
* ----------------
|
||||
*/
|
||||
for (;;) {
|
||||
/* ----------------
|
||||
* fetch a tuple from the outer subplan
|
||||
* ----------------
|
||||
*/
|
||||
slot = ExecProcNode(outerPlan, (Plan*)node);
|
||||
if (TupIsNull(slot))
|
||||
return NULL;
|
||||
|
||||
/* ----------------
|
||||
* we use the result tuple slot to hold our saved tuples.
|
||||
* if we haven't a saved tuple to compare our new tuple with,
|
||||
* then we exit the loop. This new tuple as the saved tuple
|
||||
* the next time we get here.
|
||||
* ----------------
|
||||
*/
|
||||
if (TupIsNull(resultTupleSlot))
|
||||
break;
|
||||
|
||||
/* ----------------
|
||||
* now test if the new tuple and the previous
|
||||
* tuple match. If so then we loop back and fetch
|
||||
* another new tuple from the subplan.
|
||||
* ----------------
|
||||
*/
|
||||
|
||||
if (uniqueAttr) {
|
||||
/* to check equality, we check to see if the typoutput
|
||||
of the attributes are equal */
|
||||
bool isNull1,isNull2;
|
||||
char *attr1, *attr2;
|
||||
char *val1, *val2;
|
||||
|
||||
attr1 = heap_getattr(slot->val, InvalidBuffer,
|
||||
uniqueAttrNum, tupDesc,&isNull1);
|
||||
attr2 = heap_getattr(resultTupleSlot->val, InvalidBuffer,
|
||||
uniqueAttrNum, tupDesc,&isNull2);
|
||||
|
||||
if (isNull1 == isNull2) {
|
||||
if (isNull1) /* both are null, they are equal */
|
||||
continue;
|
||||
val1 = fmgr(typoutput, attr1, gettypelem(tupDesc->attrs[uniqueAttrNum]->atttypid));
|
||||
val2 = fmgr(typoutput, attr2, gettypelem(tupDesc->attrs[uniqueAttrNum]->atttypid));
|
||||
/* now, val1 and val2 are ascii representations so we can
|
||||
use strcmp for comparison */
|
||||
if (strcmp(val1,val2) == 0) /* they are equal */
|
||||
continue;
|
||||
else
|
||||
break;
|
||||
}
|
||||
else /* one is null and the other isn't, they aren't equal */
|
||||
break;
|
||||
|
||||
}
|
||||
else {
|
||||
if (! ExecIdenticalTuples(slot, resultTupleSlot))
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* we have a new tuple different from the previous saved tuple
|
||||
* so we save it in the saved tuple slot. We copy the tuple
|
||||
* so we don't increment the buffer ref count.
|
||||
* ----------------
|
||||
*/
|
||||
ExecStoreTuple(heap_copytuple(slot->val),
|
||||
resultTupleSlot,
|
||||
InvalidBuffer,
|
||||
true);
|
||||
|
||||
return resultTupleSlot;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecInitUnique
|
||||
*
|
||||
* This initializes the unique node state structures and
|
||||
* the node's subplan.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
bool /* return: initialization status */
|
||||
ExecInitUnique(Unique *node, EState *estate, Plan *parent)
|
||||
{
|
||||
UniqueState *uniquestate;
|
||||
Plan *outerPlan;
|
||||
char *uniqueAttr;
|
||||
|
||||
/* ----------------
|
||||
* assign execution state to node
|
||||
* ----------------
|
||||
*/
|
||||
node->plan.state = estate;
|
||||
|
||||
/* ----------------
|
||||
* create new UniqueState for node
|
||||
* ----------------
|
||||
*/
|
||||
uniquestate = makeNode(UniqueState);
|
||||
node->uniquestate = uniquestate;
|
||||
uniqueAttr = node->uniqueAttr;
|
||||
|
||||
/* ----------------
|
||||
* Miscellanious initialization
|
||||
*
|
||||
* + assign node's base_id
|
||||
* + assign debugging hooks and
|
||||
*
|
||||
* Unique nodes have no ExprContext initialization because
|
||||
* they never call ExecQual or ExecTargetList.
|
||||
* ----------------
|
||||
*/
|
||||
ExecAssignNodeBaseInfo(estate, uniquestate, parent);
|
||||
|
||||
#define UNIQUE_NSLOTS 1
|
||||
/* ------------
|
||||
* Tuple table initialization
|
||||
* ------------
|
||||
*/
|
||||
ExecInitResultTupleSlot(estate, uniquestate);
|
||||
|
||||
/* ----------------
|
||||
* then initialize outer plan
|
||||
* ----------------
|
||||
*/
|
||||
outerPlan = outerPlan((Plan *) node);
|
||||
ExecInitNode(outerPlan, estate, (Plan *) node);
|
||||
|
||||
/* ----------------
|
||||
* unique nodes do no projections, so initialize
|
||||
* projection info for this node appropriately
|
||||
* ----------------
|
||||
*/
|
||||
ExecAssignResultTypeFromOuterPlan((Plan *)node,uniquestate);
|
||||
uniquestate->cs_ProjInfo = NULL;
|
||||
|
||||
if (uniqueAttr) {
|
||||
TupleDesc tupDesc;
|
||||
int i = 0;
|
||||
|
||||
tupDesc = ExecGetResultType(uniquestate);
|
||||
/* the parser should have ensured that uniqueAttr is a legal attribute name*/
|
||||
while ( strcmp((tupDesc->attrs[i]->attname).data, uniqueAttr) != 0)
|
||||
i++;
|
||||
node->uniqueAttrNum = i+1; /* attribute numbers start from 1 */
|
||||
}
|
||||
else
|
||||
node->uniqueAttrNum = InvalidAttrNumber;
|
||||
|
||||
/* ----------------
|
||||
* all done.
|
||||
* ----------------
|
||||
*/
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int
|
||||
ExecCountSlotsUnique(Unique *node)
|
||||
{
|
||||
return ExecCountSlotsNode(outerPlan(node)) +
|
||||
ExecCountSlotsNode(innerPlan(node)) +
|
||||
UNIQUE_NSLOTS;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecEndUnique
|
||||
*
|
||||
* This shuts down the subplan and frees resources allocated
|
||||
* to this node.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
void
|
||||
ExecEndUnique(Unique *node)
|
||||
{
|
||||
UniqueState *uniquestate;
|
||||
|
||||
uniquestate = node->uniquestate;
|
||||
ExecEndNode(outerPlan((Plan *) node), (Plan*)node);
|
||||
ExecClearTuple(uniquestate->cs_ResultTupleSlot);
|
||||
}
|
21
src/backend/executor/nodeUnique.h
Normal file
21
src/backend/executor/nodeUnique.h
Normal file
@ -0,0 +1,21 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* nodeUnique.h--
|
||||
*
|
||||
*
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: nodeUnique.h,v 1.1.1.1 1996/07/09 06:21:28 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef NODEUNIQUE_H
|
||||
#define NODEUNIQUE_H
|
||||
|
||||
extern TupleTableSlot *ExecUnique(Unique *node);
|
||||
extern bool ExecInitUnique(Unique *node, EState *estate, Plan *parent);
|
||||
extern int ExecCountSlotsUnique(Unique *node);
|
||||
extern void ExecEndUnique(Unique *node);
|
||||
|
||||
#endif /* NODEUNIQUE_H */
|
72
src/backend/executor/tuptable.h
Normal file
72
src/backend/executor/tuptable.h
Normal file
@ -0,0 +1,72 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* tuptable.h--
|
||||
* tuple table support stuff
|
||||
*
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: tuptable.h,v 1.1.1.1 1996/07/09 06:21:28 scrappy Exp $
|
||||
*
|
||||
* NOTES
|
||||
* The tuple table interface is getting pretty ugly.
|
||||
* It should be redesigned soon.
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef TUPTABLE_H
|
||||
#define TUPTABLE_H
|
||||
|
||||
/* ----------------
|
||||
* Note: the executor tuple table is managed and manipulated by special
|
||||
* code and macros in executor/execTuples.c and tupTable.h
|
||||
*
|
||||
* TupleTableSlot information
|
||||
*
|
||||
* shouldFree boolean - should we call pfree() on tuple
|
||||
* descIsNew boolean - true when tupleDescriptor changes
|
||||
* tupleDescriptor type information kept regarding the tuple data
|
||||
* buffer the buffer for tuples pointing to disk pages
|
||||
*
|
||||
* The executor stores pointers to tuples in a ``tuple table''
|
||||
* which is composed of TupleTableSlot's. Some of the tuples
|
||||
* are pointers to buffer pages and others are pointers to
|
||||
* palloc'ed memory and the shouldFree variable tells us when
|
||||
* we may call pfree() on a tuple. -cim 9/23/90
|
||||
*
|
||||
* In the implementation of nested-dot queries such as
|
||||
* "retrieve (EMP.hobbies.all)", a single scan may return tuples
|
||||
* of many types, so now we return pointers to tuple descriptors
|
||||
* along with tuples returned via the tuple table. -cim 1/18/90
|
||||
* ----------------
|
||||
*/
|
||||
typedef struct TupleTableSlot {
|
||||
NodeTag type;
|
||||
HeapTuple val;
|
||||
bool ttc_shouldFree;
|
||||
bool ttc_descIsNew;
|
||||
TupleDesc ttc_tupleDescriptor;
|
||||
Buffer ttc_buffer;
|
||||
int ttc_whichplan;
|
||||
} TupleTableSlot;
|
||||
|
||||
/* ----------------
|
||||
* tuple table data structure
|
||||
* ----------------
|
||||
*/
|
||||
typedef struct TupleTableData {
|
||||
int size; /* size of the table */
|
||||
int next; /* next available slot number */
|
||||
TupleTableSlot *array; /* array of TupleTableSlot's */
|
||||
} TupleTableData;
|
||||
|
||||
typedef TupleTableData *TupleTable;
|
||||
|
||||
/*
|
||||
tuple table macros are all excised from the system now
|
||||
see executor.h for decls of functions defined in execTuples.c
|
||||
|
||||
- jolly
|
||||
*/
|
||||
|
||||
#endif /* TUPTABLE_H */
|
Reference in New Issue
Block a user