diff options
author | Marc G. Fournier <scrappy@hub.org> | 1996-07-09 06:22:35 +0000 |
---|---|---|
committer | Marc G. Fournier <scrappy@hub.org> | 1996-07-09 06:22:35 +0000 |
commit | d31084e9d1118b25fd16580d9d8c2924b5740dff (patch) | |
tree | 3179e66307d54df9c7b966543550e601eb55e668 /src/backend/executor/nodeAppend.c | |
download | postgresql-d31084e9d1118b25fd16580d9d8c2924b5740dff.tar.gz postgresql-d31084e9d1118b25fd16580d9d8c2924b5740dff.zip |
Postgres95 1.01 Distribution - Virgin SourcesPG95-1_01
Diffstat (limited to 'src/backend/executor/nodeAppend.c')
-rw-r--r-- | src/backend/executor/nodeAppend.c | 483 |
1 files changed, 483 insertions, 0 deletions
diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c new file mode 100644 index 00000000000..0a6cd5d01bb --- /dev/null +++ b/src/backend/executor/nodeAppend.c @@ -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 */ +} + |