1 /*-------------------------------------------------------------------------
4 * routines to handle append nodes.
6 * Copyright (c) 1994, Regents of the University of California
10 * $Header: /cvsroot/pgsql/src/backend/executor/nodeAppend.c,v 1.15 1998/09/01 04:28:26 momjian Exp $
12 *-------------------------------------------------------------------------
15 * ExecInitAppend - initialize the append node
16 * ExecProcAppend - retrieve the next tuple from the node
17 * ExecEndAppend - shut down the append node
18 * ExecReScanAppend - rescan the append node
21 * Each append node contains a list of one or more subplans which
22 * must be iteratively processed (forwards or backwards).
23 * Tuples are retrieved by executing the 'whichplan'th subplan
24 * until the subplan stops returning tuples, at which point that
25 * plan is shut down and the next started up.
27 * Append nodes don't make use of their left and right
28 * subtrees, rather they maintain a list of subplans so
29 * a typical append node looks like this in the plan tree:
33 * Append -------+------+------+--- nil
38 * Append nodes are currently used for unions, and to support inheritance
39 * queries, where several relations need to be scanned.
40 * For example, in our standard person/student/employee/student-emp
41 * example, where student and employee inherit from person
42 * and student-emp inherits from student and employee, the
45 * retrieve (e.name) from e in person*
50 * Append -------+-------+--------+--------+
52 * nil nil Scan Scan Scan Scan
54 * person employee student student-emp
59 #include "access/heapam.h"
60 #include "executor/executor.h"
61 #include "executor/execdebug.h"
62 #include "executor/nodeAppend.h"
63 #include "executor/nodeIndexscan.h"
64 #include "utils/palloc.h"
65 #include "utils/mcxt.h"
66 #include "parser/parsetree.h" /* for rt_store() macro */
68 static bool exec_append_initialize_next(Append *node);
70 /* ----------------------------------------------------------------
71 * exec-append-initialize-next
73 * Sets up the append node state (i.e. the append state node)
74 * for the "next" scan.
76 * Returns t iff there is a "next" scan to process.
77 * ----------------------------------------------------------------
80 exec_append_initialize_next(Append *node)
83 AppendState *appendstate;
84 TupleTableSlot *result_slot;
94 * get information from the append node
97 estate = node->plan.state;
98 appendstate = node->appendstate;
99 result_slot = appendstate->cstate.cs_ResultTupleSlot;
100 rangeTable = estate->es_range_table;
102 whichplan = appendstate->as_whichplan;
103 nplans = appendstate->as_nplans;
104 rtables = node->unionrtables;
105 rtable = node->inheritrtable;
110 * if scanning in reverse, we start at
111 * the last scan in the list and then
112 * proceed back to the first.. in any case
113 * we inform ExecProcAppend that we are
114 * at the end of the line by returning FALSE
117 appendstate->as_whichplan = 0;
121 else if (whichplan >= nplans)
124 * as above, end the scan if we go beyond
125 * the last scan in our list..
128 appendstate->as_whichplan = nplans - 1;
135 * initialize the scan
136 * (and update the range table appropriately)
137 * (doesn't this leave the range table hosed for anybody upstream
138 * of the Append node??? - jolly )
141 if (node->inheritrelid > 0)
143 rtentry = nth(whichplan, rtable);
144 Assert(rtentry != NULL);
146 rt_store(node->inheritrelid, rangeTable, rtentry);
149 estate->es_range_table = nth(whichplan, rtables);
151 if (appendstate->as_junkFilter_list)
153 estate->es_junkFilter =
154 (JunkFilter *) nth(whichplan,
155 appendstate->as_junkFilter_list);
157 if (appendstate->as_result_relation_info_list)
159 estate->es_result_relation_info =
160 (RelationInfo *) nth(whichplan,
161 appendstate->as_result_relation_info_list);
163 result_slot->ttc_whichplan = whichplan;
169 /* ----------------------------------------------------------------
172 * Begins all of the subscans of the append node, storing the
173 * scan structures in the 'initialized' vector of the append-state
176 * (This is potentially wasteful, since the entire result of the
177 * append node may not be scanned, but this way all of the
178 * structures get allocated in the executor's top level memory
179 * block instead of that of the call to ExecProcAppend.)
181 * Returns the scan result of the first scan.
182 * ----------------------------------------------------------------
185 ExecInitAppend(Append *node, EState *estate, Plan *parent)
187 AppendState *appendstate;
189 List *resultList = NULL;
196 RelationInfo *es_rri = estate->es_result_relation_info;
199 * assign execution state to node and get information
203 node->plan.state = estate;
205 appendplans = node->appendplans;
206 nplans = length(appendplans);
207 rtable = node->inheritrtable;
209 CXT1_printf("ExecInitAppend: context is %d\n", CurrentMemoryContext);
210 initialized = (bool *) palloc(nplans * sizeof(bool));
213 * create new AppendState for our append node
216 appendstate = makeNode(AppendState);
217 appendstate->as_whichplan = 0;
218 appendstate->as_nplans = nplans;
219 appendstate->as_initialized = initialized;
220 appendstate->as_rtentries = rtable;
222 node->appendstate = appendstate;
225 * Miscellanious initialization
227 * + assign node's base_id
228 * + assign debugging hooks
230 * Append plans don't have expression contexts because they
231 * never call ExecQual or ExecTargetList.
234 ExecAssignNodeBaseInfo(estate, &appendstate->cstate, parent);
236 #define APPEND_NSLOTS 1
238 * append nodes still have Result slots, which hold pointers
239 * to tuples, so we have to initialize them..
242 ExecInitResultTupleSlot(estate, &appendstate->cstate);
245 * If the inherits rtentry is the result relation, we have to make a
246 * result relation info list for all inheritors so we can update their
247 * indices and put the result tuples in the right place etc.
249 * e.g. replace p (age = p.age + 1) from p in person*
251 if ((es_rri != (RelationInfo *) NULL) &&
252 (node->inheritrelid == es_rri->ri_RangeTableIndex))
257 foreach(rtentryP, rtable)
260 RangeTblEntry *rtentry = lfirst(rtentryP);
262 reloid = rtentry->relid;
263 rri = makeNode(RelationInfo);
264 rri->ri_RangeTableIndex = es_rri->ri_RangeTableIndex;
265 rri->ri_RelationDesc = heap_open(reloid);
266 rri->ri_NumIndices = 0;
267 rri->ri_IndexRelationDescs = NULL; /* index descs */
268 rri->ri_IndexRelationInfo = NULL; /* index key info */
270 resultList = lcons(rri, resultList);
271 ExecOpenIndices(reloid, rri);
273 appendstate->as_result_relation_info_list = resultList;
276 * call ExecInitNode on each of the plans in our list
277 * and save the results into the array "initialized"
282 for (i = 0; i < nplans; i++)
288 * NOTE: we first modify range table in
289 * exec_append_initialize_next() and
290 * then initialize the subnode,
291 * since it may use the range table.
294 appendstate->as_whichplan = i;
295 exec_append_initialize_next(node);
297 initNode = (Plan *) nth(i, appendplans);
298 initialized[i] = ExecInitNode(initNode, estate, (Plan *) node);
301 * Each targetlist in the subplan may need its own junk filter
303 * This is true only when the reln being replaced/deleted is
304 * the one that we're looking at the subclasses of
307 if ((es_rri != (RelationInfo *) NULL) &&
308 (node->inheritrelid == es_rri->ri_RangeTableIndex))
311 targetList = initNode->targetlist;
312 j = (JunkFilter *) ExecInitJunkFilter(targetList);
313 junkList = lappend(junkList, j);
317 appendstate->as_junkFilter_list = junkList;
319 estate->es_junkFilter = (JunkFilter *) lfirst(junkList);
322 * initialize the return type from the appropriate subplan.
325 initNode = (Plan *) nth(0, appendplans);
326 ExecAssignResultType(&appendstate->cstate,
327 /* ExecGetExecTupDesc(initNode), */
328 ExecGetTupType(initNode));
329 appendstate->cstate.cs_ProjInfo = NULL;
332 * return the result from the first subplan's initialization
335 appendstate->as_whichplan = 0;
336 exec_append_initialize_next(node);
338 result = (List *) initialized[0];
344 ExecCountSlotsAppend(Append *node)
347 List *appendplans = node->appendplans;
350 foreach(plan, appendplans)
351 nSlots += ExecCountSlotsNode((Plan *) lfirst(plan));
352 return nSlots + APPEND_NSLOTS;
355 /* ----------------------------------------------------------------
358 * Handles the iteration over the multiple scans.
360 * NOTE: Can't call this ExecAppend, that name is used in execMain.l
361 * ----------------------------------------------------------------
364 ExecProcAppend(Append *node)
367 AppendState *appendstate;
372 TupleTableSlot *result;
373 TupleTableSlot *result_slot;
374 ScanDirection direction;
377 * get information from the node
380 appendstate = node->appendstate;
381 estate = node->plan.state;
382 direction = estate->es_direction;
384 appendplans = node->appendplans;
385 whichplan = appendstate->as_whichplan;
386 result_slot = appendstate->cstate.cs_ResultTupleSlot;
389 * figure out which subplan we are currently processing
392 subnode = (Plan *) nth(whichplan, appendplans);
395 elog(DEBUG, "ExecProcAppend: subnode is NULL");
398 * get a tuple from the subplan
401 result = ExecProcNode(subnode, (Plan *) node);
403 if (!TupIsNull(result))
406 * if the subplan gave us something then place a copy of
407 * whatever we get into our result slot and return it, else..
410 return ExecStoreTuple(result->val,
411 result_slot, result->ttc_buffer, false);
417 * .. go on to the "next" subplan in the appropriate
418 * direction and try processing again (recursively)
421 whichplan = appendstate->as_whichplan;
423 if (ScanDirectionIsForward(direction))
424 appendstate->as_whichplan = whichplan + 1;
426 appendstate->as_whichplan = whichplan - 1;
429 * return something from next node or an empty slot
430 * all of our subplans have been exhausted.
433 if (exec_append_initialize_next(node))
435 ExecSetSlotDescriptorIsNew(result_slot, true);
436 return ExecProcAppend(node);
439 return ExecClearTuple(result_slot);
443 /* ----------------------------------------------------------------
446 * Shuts down the subscans of the append node.
448 * Returns nothing of interest.
449 * ----------------------------------------------------------------
452 ExecEndAppend(Append *node)
454 AppendState *appendstate;
459 List *resultRelationInfoList;
460 RelationInfo *resultRelationInfo;
463 * get information from the node
466 appendstate = node->appendstate;
467 appendplans = node->appendplans;
468 nplans = appendstate->as_nplans;
469 initialized = appendstate->as_initialized;
472 * shut down each of the subscans
475 for (i = 0; i < nplans; i++)
477 if (initialized[i] == TRUE)
478 ExecEndNode((Plan *) nth(i, appendplans), (Plan *) node);
482 * close out the different result relations
485 resultRelationInfoList = appendstate->as_result_relation_info_list;
486 while (resultRelationInfoList != NIL)
488 Relation resultRelationDesc;
490 resultRelationInfo = (RelationInfo *) lfirst(resultRelationInfoList);
491 resultRelationDesc = resultRelationInfo->ri_RelationDesc;
492 heap_close(resultRelationDesc);
493 pfree(resultRelationInfo);
494 resultRelationInfoList = lnext(resultRelationInfoList);
496 if (appendstate->as_result_relation_info_list)
497 pfree(appendstate->as_result_relation_info_list);
500 * XXX should free appendstate->as_rtentries and
501 * appendstate->as_junkfilter_list here
505 ExecReScanAppend(Append *node, ExprContext *exprCtxt, Plan *parent)
507 AppendState *appendstate = node->appendstate;
508 int nplans = length(node->appendplans);
511 for (i = 0; i < nplans; i++)
515 appendstate->as_whichplan = i;
516 rescanNode = (Plan *) nth(i, node->appendplans);
517 if (rescanNode->chgParam == NULL)
519 exec_append_initialize_next(node);
520 ExecReScan((Plan *) rescanNode, exprCtxt, (Plan *) node);
523 appendstate->as_whichplan = 0;
524 exec_append_initialize_next(node);