1
0
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:
Marc G. Fournier
1996-07-09 06:22:35 +00:00
commit d31084e9d1
868 changed files with 242656 additions and 0 deletions

View 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

View 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;
}

View 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;
}

View 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 */

View 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);
}

File diff suppressed because it is too large Load Diff

View 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;
}
}

File diff suppressed because it is too large Load Diff

View 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;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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 */

View 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 */

View 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 */

View 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 */

View 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;
}

View 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 */

View 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 */

View 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;
}

View 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 */

View 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 */
}

View 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 */

View 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;
}

View 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 */

View 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;
}

View 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 */

View 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;
}

View 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 */

View 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 */
&currentRelation, /* return: rel desc */
(Pointer *) &currentScanDesc); /* return: scan desc */
scanstate->css_currentRelation = currentRelation;
scanstate->css_currentScanDesc = currentScanDesc;
/* ----------------
* get the scan type from the relation descriptor.
* ----------------
*/
ExecAssignScanType(scanstate, RelationGetTupleDescriptor(currentRelation));
ExecAssignResultTypeFromTL((Plan *) node, &scanstate->cstate);
/* ----------------
* index scans don't have subtrees..
* ----------------
*/
/* scanstate->ss_ProcOuterFlag = false; */
/* ----------------
* open the index relations and initialize
* relation and scan descriptors.
* ----------------
*/
for (i=0; i < numIndices; i++) {
Oid indexOid;
indexOid = (Oid)nth(i, indxid);
if (indexOid != 0) {
ExecOpenScanR(indexOid, /* relation */
numScanKeys[ i ], /* nkeys */
scanKeys[ i ], /* scan key */
true, /* is index */
direction, /* scan direction */
timeQual, /* time qual */
&(relationDescs[ i ]), /* return: rel desc */
(Pointer *) &(scanDescs[ i ]));
/* return: scan desc */
}
}
indexstate->iss_RelationDescs = relationDescs;
indexstate->iss_ScanDescs = scanDescs;
indexstate->cstate.cs_TupFromTlist = false;
/* ----------------
* all done.
* ----------------
*/
return TRUE;
}
int
ExecCountSlotsIndexScan(IndexScan *node)
{
return ExecCountSlotsNode(outerPlan((Plan *)node)) +
ExecCountSlotsNode(innerPlan((Plan *)node)) +
INDEXSCAN_NSLOTS;
}

View 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 */

View 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

View 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 */

File diff suppressed because it is too large Load Diff

View 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; */

View 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");
}

View 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 */

View 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);
}

View 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 */

View 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 */
&currentRelation, /* return: rel desc */
(Pointer *) &currentScanDesc); /* return: scan desc */
scanstate->css_currentRelation = currentRelation;
scanstate->css_currentScanDesc = currentScanDesc;
ExecAssignScanType(scanstate,
RelationGetTupleDescriptor(currentRelation));
} else {
/* ----------------
* otherwise we are scanning tuples from the
* outer subplan so we initialize the outer plan
* and nullify
* ----------------
*/
ExecInitNode(outerPlan, estate, (Plan*)node);
node->scanrelid = 0;
scanstate->css_currentRelation = NULL;
scanstate->css_currentScanDesc = NULL;
ExecAssignScanType(scanstate, NULL);
reloid = InvalidOid;
}
/* ----------------
* return the relation
* ----------------
*/
return reloid;
}
/* ----------------------------------------------------------------
* ExecInitSeqScan
*
* old comments
* Creates the run-time state information for the seqscan node
* and sets the relation id to contain relevant descriptors.
*
* If there is a outer subtree (sort), the outer subtree
* is initialized and the relation id is set to the descriptors
* returned by the subtree.
* ----------------------------------------------------------------
*/
bool
ExecInitSeqScan(SeqScan *node, EState *estate, Plan *parent)
{
CommonScanState *scanstate;
Plan *outerPlan;
Oid reloid;
HeapScanDesc scandesc;
/* ----------------
* assign the node's execution state
* ----------------
*/
node->plan.state = estate;
/* ----------------
* create new CommonScanState for node
* ----------------
*/
scanstate = makeNode(CommonScanState);
node->scanstate = scanstate;
/* ----------------
* Miscellanious initialization
*
* + assign node's base_id
* + create expression context for node
* ----------------
*/
ExecAssignNodeBaseInfo(estate, &scanstate->cstate, parent);
ExecAssignExprContext(estate, &scanstate->cstate);
#define SEQSCAN_NSLOTS 3
/* ----------------
* tuple table initialization
* ----------------
*/
ExecInitResultTupleSlot(estate, &scanstate->cstate);
ExecInitScanTupleSlot(estate, scanstate);
/* ----------------
* initialize scan relation or outer subplan
* ----------------
*/
outerPlan = outerPlan((Plan *)node);
reloid = InitScanRelation(node, estate, scanstate, outerPlan);
scandesc = scanstate->css_currentScanDesc;
scanstate->cstate.cs_TupFromTlist = false;
/* ----------------
* initialize tuple type
* ----------------
*/
ExecAssignResultTypeFromTL((Plan*)node, &scanstate->cstate);
ExecAssignProjectionInfo((Plan*)node, &scanstate->cstate);
return TRUE;
}
int
ExecCountSlotsSeqScan(SeqScan *node)
{
return ExecCountSlotsNode(outerPlan(node)) +
ExecCountSlotsNode(innerPlan(node)) +
SEQSCAN_NSLOTS;
}
/* ----------------------------------------------------------------
* ExecEndSeqScan
*
* frees any storage allocated through C routines.
*| ...and also closes relations and/or shuts down outer subplan
*| -cim 8/14/89
* ----------------------------------------------------------------
*/
void
ExecEndSeqScan(SeqScan *node)
{
CommonScanState *scanstate;
Plan *outerPlan;
/* ----------------
* get information from node
* ----------------
*/
scanstate = node->scanstate;
/* ----------------
* Free the projection info and the scan attribute info
*
* Note: we don't ExecFreeResultType(scanstate)
* because the rule manager depends on the tupType
* returned by ExecMain(). So for now, this
* is freed at end-transaction time. -cim 6/2/91
* ----------------
*/
ExecFreeProjectionInfo(&scanstate->cstate);
/* ----------------
* close scan relation
* ----------------
*/
ExecCloseR((Plan*) node);
/* ----------------
* clean up outer subtree (does nothing if there is no outerPlan)
* ----------------
*/
outerPlan = outerPlan((Plan *)node);
ExecEndNode(outerPlan, (Plan*)node);
/* ----------------
* clean out the tuple table
* ----------------
*/
ExecClearTuple(scanstate->cstate.cs_ResultTupleSlot);
ExecClearTuple(scanstate->css_ScanTupleSlot);
}
/* ----------------------------------------------------------------
* Join Support
* ----------------------------------------------------------------
*/
/* ----------------------------------------------------------------
* ExecSeqReScan
*
* Rescans the relation.
* ----------------------------------------------------------------
*/
void
ExecSeqReScan(SeqScan *node, ExprContext *exprCtxt, Plan* parent)
{
CommonScanState *scanstate;
EState *estate;
Plan *outerPlan;
Relation rdesc;
HeapScanDesc sdesc;
ScanDirection direction;
scanstate = node->scanstate;
estate = node->plan.state;
outerPlan = outerPlan((Plan*)node);
if (outerPlan) {
/* we are scanning a subplan */
outerPlan = outerPlan((Plan *)node);
ExecReScan(outerPlan, exprCtxt, parent);
} else {
/* otherwise, we are scanning a relation */
rdesc = scanstate->css_currentRelation;
sdesc = scanstate->css_currentScanDesc;
direction = estate->es_direction;
sdesc = ExecReScanR(rdesc, sdesc, direction, 0, NULL);
scanstate->css_currentScanDesc = sdesc;
}
}
/* ----------------------------------------------------------------
* ExecSeqMarkPos(node)
*
* Marks scan position.
* ----------------------------------------------------------------
*/
void
ExecSeqMarkPos(SeqScan *node)
{
CommonScanState *scanstate;
Plan *outerPlan;
HeapScanDesc sdesc;
scanstate = node->scanstate;
/* ----------------
* if we are scanning a subplan then propagate
* the ExecMarkPos() request to the subplan
* ----------------
*/
outerPlan = outerPlan((Plan*)node);
if (outerPlan) {
ExecMarkPos(outerPlan);
return;
}
/* ----------------
* otherwise we are scanning a relation so mark the
* position using the access methods..
*
* ----------------
*/
sdesc = scanstate->css_currentScanDesc;
heap_markpos(sdesc);
return;
}
/* ----------------------------------------------------------------
* ExecSeqRestrPos
*
* Restores scan position.
* ----------------------------------------------------------------
*/
void
ExecSeqRestrPos(SeqScan *node)
{
CommonScanState *scanstate;
Plan *outerPlan;
HeapScanDesc sdesc;
scanstate = node->scanstate;
/* ----------------
* if we are scanning a subplan then propagate
* the ExecRestrPos() request to the subplan
* ----------------
*/
outerPlan = outerPlan((Plan*)node);
if (outerPlan) {
ExecRestrPos(outerPlan);
return;
}
/* ----------------
* otherwise we are scanning a relation so restore the
* position using the access methods..
* ----------------
*/
sdesc = scanstate->css_currentScanDesc;
heap_restrpos(sdesc);
}

View 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 */

View 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);
}

View 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 */

View 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;
}
}
}
}

View 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 */

View 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);
}

View 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 */

View 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 */