postgresql/src/backend/executor/execFlatten.c

246 lines
5.7 KiB
C

/*-------------------------------------------------------------------------
*
* execFlatten.c
* This file handles the nodes associated with flattening sets in the
* target list of queries containing functions returning sets.
*
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/Attic/execFlatten.c,v 1.12 2000/01/26 05:56:21 momjian Exp $
*
*-------------------------------------------------------------------------
*/
/*
* ExecEvalIter() -
* Iterate through the all return tuples/base types from a function one
* at time (i.e. one per ExecEvalIter call). Not really needed for
* postquel functions, but for reasons of orthogonality, these nodes
* exist above pq functions as well as c functions.
*
* 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 "executor/execFlatten.h"
#include "executor/executor.h"
#ifdef SETS_FIXED
static bool FjoinBumpOuterNodes(TargetEntry *tlist, ExprContext *econtext,
DatumPtr results, char *nulls);
#endif
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;
}
#ifdef SETS_FIXED
static bool
FjoinBumpOuterNodes(TargetEntry *tlist,
ExprContext *econtext,
DatumPtr results,
char *nulls)
{
bool funcIsDone = true;
Fjoin *fjNode = tlist->fjoin;
char *alwaysDone = fjNode->fj_alwaysDone;
List *outerList = lnext(tlist);
List *trailers = lnext(tlist);
int trailNode = 1;
int curNode = 1;
/*
* Run through list of functions until we get to one that isn't yet
* done returning values. Watch out for funcs that are always done.
*/
while ((funcIsDone == true) && (outerList != NIL))
{
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 uninitialized, 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