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