1 /*-------------------------------------------------------------------------
2 *
3 * prepunion.c
4 * Routines to plan set-operation queries. The filename is a leftover
5 * from a time when only UNIONs were implemented.
6 *
7 * There are two code paths in the planner for set-operation queries.
8 * If a subquery consists entirely of simple UNION ALL operations, it
9 * is converted into an "append relation". Otherwise, it is handled
10 * by the general code in this module (plan_set_operations and its
11 * subroutines). There is some support code here for the append-relation
12 * case, but most of the heavy lifting for that is done elsewhere,
13 * notably in prepjointree.c and allpaths.c.
14 *
15 * There is also some code here to support planning of queries that use
16 * inheritance (SELECT FROM foo*). Inheritance trees are converted into
17 * append relations, and thenceforth share code with the UNION ALL case.
18 *
19 *
20 * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
21 * Portions Copyright (c) 1994, Regents of the University of California
22 *
23 *
24 * IDENTIFICATION
25 * src/backend/optimizer/prep/prepunion.c
26 *
27 *-------------------------------------------------------------------------
28 */
29 #include "postgres.h"
30
31 #include <limits.h>
32
33 #include "access/heapam.h"
34 #include "access/htup_details.h"
35 #include "access/sysattr.h"
36 #include "catalog/pg_inherits_fn.h"
37 #include "catalog/pg_type.h"
38 #include "miscadmin.h"
39 #include "nodes/makefuncs.h"
40 #include "nodes/nodeFuncs.h"
41 #include "optimizer/cost.h"
42 #include "optimizer/pathnode.h"
43 #include "optimizer/paths.h"
44 #include "optimizer/planmain.h"
45 #include "optimizer/planner.h"
46 #include "optimizer/prep.h"
47 #include "optimizer/tlist.h"
48 #include "parser/parse_coerce.h"
49 #include "parser/parsetree.h"
50 #include "utils/lsyscache.h"
51 #include "utils/rel.h"
52 #include "utils/selfuncs.h"
53
54
55 typedef struct
56 {
57 PlannerInfo *root;
58 AppendRelInfo *appinfo;
59 int sublevels_up;
60 } adjust_appendrel_attrs_context;
61
62 static Path *recurse_set_operations(Node *setOp, PlannerInfo *root,
63 List *colTypes, List *colCollations,
64 bool junkOK,
65 int flag, List *refnames_tlist,
66 List **pTargetList,
67 double *pNumGroups);
68 static Path *generate_recursion_path(SetOperationStmt *setOp,
69 PlannerInfo *root,
70 List *refnames_tlist,
71 List **pTargetList);
72 static Path *generate_union_path(SetOperationStmt *op, PlannerInfo *root,
73 List *refnames_tlist,
74 List **pTargetList,
75 double *pNumGroups);
76 static Path *generate_nonunion_path(SetOperationStmt *op, PlannerInfo *root,
77 List *refnames_tlist,
78 List **pTargetList,
79 double *pNumGroups);
80 static List *recurse_union_children(Node *setOp, PlannerInfo *root,
81 SetOperationStmt *top_union,
82 List *refnames_tlist,
83 List **tlist_list);
84 static Path *make_union_unique(SetOperationStmt *op, Path *path, List *tlist,
85 PlannerInfo *root);
86 static bool choose_hashed_setop(PlannerInfo *root, List *groupClauses,
87 Path *input_path,
88 double dNumGroups, double dNumOutputRows,
89 const char *construct);
90 static List *generate_setop_tlist(List *colTypes, List *colCollations,
91 int flag,
92 Index varno,
93 bool hack_constants,
94 List *input_tlist,
95 List *refnames_tlist);
96 static List *generate_append_tlist(List *colTypes, List *colCollations,
97 bool flag,
98 List *input_tlists,
99 List *refnames_tlist);
100 static List *generate_setop_grouplist(SetOperationStmt *op, List *targetlist);
101 static void expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte,
102 Index rti);
103 static void make_inh_translation_list(Relation oldrelation,
104 Relation newrelation,
105 Index newvarno,
106 List **translated_vars);
107 static Bitmapset *translate_col_privs(const Bitmapset *parent_privs,
108 List *translated_vars);
109 static Node *adjust_appendrel_attrs_mutator(Node *node,
110 adjust_appendrel_attrs_context *context);
111 static Relids adjust_relid_set(Relids relids, Index oldrelid, Index newrelid);
112 static List *adjust_inherited_tlist(List *tlist,
113 AppendRelInfo *context);
114
115
116 /*
117 * plan_set_operations
118 *
119 * Plans the queries for a tree of set operations (UNION/INTERSECT/EXCEPT)
120 *
121 * This routine only deals with the setOperations tree of the given query.
122 * Any top-level ORDER BY requested in root->parse->sortClause will be handled
123 * when we return to grouping_planner; likewise for LIMIT.
124 *
125 * What we return is an "upperrel" RelOptInfo containing at least one Path
126 * that implements the set-operation tree. In addition, root->processed_tlist
127 * receives a targetlist representing the output of the topmost setop node.
128 */
129 RelOptInfo *
plan_set_operations(PlannerInfo * root)130 plan_set_operations(PlannerInfo *root)
131 {
132 Query *parse = root->parse;
133 SetOperationStmt *topop = (SetOperationStmt *) parse->setOperations;
134 Node *node;
135 RangeTblEntry *leftmostRTE;
136 Query *leftmostQuery;
137 RelOptInfo *setop_rel;
138 Path *path;
139 List *top_tlist;
140
141 Assert(topop && IsA(topop, SetOperationStmt));
142
143 /* check for unsupported stuff */
144 Assert(parse->jointree->fromlist == NIL);
145 Assert(parse->jointree->quals == NULL);
146 Assert(parse->groupClause == NIL);
147 Assert(parse->havingQual == NULL);
148 Assert(parse->windowClause == NIL);
149 Assert(parse->distinctClause == NIL);
150
151 /*
152 * We'll need to build RelOptInfos for each of the leaf subqueries, which
153 * are RTE_SUBQUERY rangetable entries in this Query. Prepare the index
154 * arrays for that.
155 */
156 setup_simple_rel_arrays(root);
157
158 /*
159 * Find the leftmost component Query. We need to use its column names for
160 * all generated tlists (else SELECT INTO won't work right).
161 */
162 node = topop->larg;
163 while (node && IsA(node, SetOperationStmt))
164 node = ((SetOperationStmt *) node)->larg;
165 Assert(node && IsA(node, RangeTblRef));
166 leftmostRTE = root->simple_rte_array[((RangeTblRef *) node)->rtindex];
167 leftmostQuery = leftmostRTE->subquery;
168 Assert(leftmostQuery != NULL);
169
170 /*
171 * We return our results in the (SETOP, NULL) upperrel. For the moment,
172 * this is also the parent rel of all Paths in the setop tree; we may well
173 * change that in future.
174 */
175 setop_rel = fetch_upper_rel(root, UPPERREL_SETOP, NULL);
176
177 /*
178 * We don't currently worry about setting setop_rel's consider_parallel
179 * flag, nor about allowing FDWs to contribute paths to it.
180 */
181
182 /*
183 * If the topmost node is a recursive union, it needs special processing.
184 */
185 if (root->hasRecursion)
186 {
187 path = generate_recursion_path(topop, root,
188 leftmostQuery->targetList,
189 &top_tlist);
190 }
191 else
192 {
193 /*
194 * Recurse on setOperations tree to generate paths for set ops. The
195 * final output path should have just the column types shown as the
196 * output from the top-level node, plus possibly resjunk working
197 * columns (we can rely on upper-level nodes to deal with that).
198 */
199 path = recurse_set_operations((Node *) topop, root,
200 topop->colTypes, topop->colCollations,
201 true, -1,
202 leftmostQuery->targetList,
203 &top_tlist,
204 NULL);
205 }
206
207 /* Must return the built tlist into root->processed_tlist. */
208 root->processed_tlist = top_tlist;
209
210 /* Add only the final path to the SETOP upperrel. */
211 add_path(setop_rel, path);
212
213 /* Let extensions possibly add some more paths */
214 if (create_upper_paths_hook)
215 (*create_upper_paths_hook) (root, UPPERREL_SETOP,
216 NULL, setop_rel);
217
218 /* Select cheapest path */
219 set_cheapest(setop_rel);
220
221 return setop_rel;
222 }
223
224 /*
225 * recurse_set_operations
226 * Recursively handle one step in a tree of set operations
227 *
228 * colTypes: OID list of set-op's result column datatypes
229 * colCollations: OID list of set-op's result column collations
230 * junkOK: if true, child resjunk columns may be left in the result
231 * flag: if >= 0, add a resjunk output column indicating value of flag
232 * refnames_tlist: targetlist to take column names from
233 *
234 * Returns a path for the subtree, as well as these output parameters:
235 * *pTargetList: receives the fully-fledged tlist for the subtree's top plan
236 * *pNumGroups: if not NULL, we estimate the number of distinct groups
237 * in the result, and store it there
238 *
239 * The pTargetList output parameter is mostly redundant with the pathtarget
240 * of the returned path, but for the moment we need it because much of the
241 * logic in this file depends on flag columns being marked resjunk. Pending
242 * a redesign of how that works, this is the easy way out.
243 *
244 * We don't have to care about typmods here: the only allowed difference
245 * between set-op input and output typmods is input is a specific typmod
246 * and output is -1, and that does not require a coercion.
247 */
248 static Path *
recurse_set_operations(Node * setOp,PlannerInfo * root,List * colTypes,List * colCollations,bool junkOK,int flag,List * refnames_tlist,List ** pTargetList,double * pNumGroups)249 recurse_set_operations(Node *setOp, PlannerInfo *root,
250 List *colTypes, List *colCollations,
251 bool junkOK,
252 int flag, List *refnames_tlist,
253 List **pTargetList,
254 double *pNumGroups)
255 {
256 /* Guard against stack overflow due to overly complex setop nests */
257 check_stack_depth();
258
259 if (IsA(setOp, RangeTblRef))
260 {
261 RangeTblRef *rtr = (RangeTblRef *) setOp;
262 RangeTblEntry *rte = root->simple_rte_array[rtr->rtindex];
263 Query *subquery = rte->subquery;
264 RelOptInfo *rel;
265 PlannerInfo *subroot;
266 RelOptInfo *final_rel;
267 Path *subpath;
268 Path *path;
269 List *tlist;
270
271 Assert(subquery != NULL);
272
273 /*
274 * We need to build a RelOptInfo for each leaf subquery. This isn't
275 * used for much here, but it carries the subroot data structures
276 * forward to setrefs.c processing.
277 */
278 rel = build_simple_rel(root, rtr->rtindex, RELOPT_BASEREL);
279
280 /* plan_params should not be in use in current query level */
281 Assert(root->plan_params == NIL);
282
283 /* Generate a subroot and Paths for the subquery */
284 subroot = rel->subroot = subquery_planner(root->glob, subquery,
285 root,
286 false,
287 root->tuple_fraction);
288
289 /*
290 * It should not be possible for the primitive query to contain any
291 * cross-references to other primitive queries in the setop tree.
292 */
293 if (root->plan_params)
294 elog(ERROR, "unexpected outer reference in set operation subquery");
295
296 /*
297 * Mark rel with estimated output rows, width, etc. Note that we have
298 * to do this before generating outer-query paths, else
299 * cost_subqueryscan is not happy.
300 */
301 set_subquery_size_estimates(root, rel);
302
303 /*
304 * For the moment, we consider only a single Path for the subquery.
305 * This should change soon (make it look more like
306 * set_subquery_pathlist).
307 */
308 final_rel = fetch_upper_rel(subroot, UPPERREL_FINAL, NULL);
309 subpath = get_cheapest_fractional_path(final_rel,
310 root->tuple_fraction);
311
312 /*
313 * Stick a SubqueryScanPath atop that.
314 *
315 * We don't bother to determine the subquery's output ordering since
316 * it won't be reflected in the set-op result anyhow; so just label
317 * the SubqueryScanPath with nil pathkeys. (XXX that should change
318 * soon too, likely.)
319 */
320 path = (Path *) create_subqueryscan_path(root, rel, subpath,
321 NIL, NULL);
322
323 /*
324 * Figure out the appropriate target list, and update the
325 * SubqueryScanPath with the PathTarget form of that.
326 */
327 tlist = generate_setop_tlist(colTypes, colCollations,
328 flag,
329 rtr->rtindex,
330 true,
331 subroot->processed_tlist,
332 refnames_tlist);
333
334 path = apply_projection_to_path(root, rel, path,
335 create_pathtarget(root, tlist));
336
337 /* Return the fully-fledged tlist to caller, too */
338 *pTargetList = tlist;
339
340 /*
341 * Estimate number of groups if caller wants it. If the subquery used
342 * grouping or aggregation, its output is probably mostly unique
343 * anyway; otherwise do statistical estimation.
344 *
345 * XXX you don't really want to know about this: we do the estimation
346 * using the subquery's original targetlist expressions, not the
347 * subroot->processed_tlist which might seem more appropriate. The
348 * reason is that if the subquery is itself a setop, it may return a
349 * processed_tlist containing "varno 0" Vars generated by
350 * generate_append_tlist, and those would confuse estimate_num_groups
351 * mightily. We ought to get rid of the "varno 0" hack, but that
352 * requires a redesign of the parsetree representation of setops, so
353 * that there can be an RTE corresponding to each setop's output.
354 */
355 if (pNumGroups)
356 {
357 if (subquery->groupClause || subquery->groupingSets ||
358 subquery->distinctClause ||
359 subroot->hasHavingQual || subquery->hasAggs)
360 *pNumGroups = subpath->rows;
361 else
362 *pNumGroups = estimate_num_groups(subroot,
363 get_tlist_exprs(subquery->targetList, false),
364 subpath->rows,
365 NULL);
366 }
367
368 return (Path *) path;
369 }
370 else if (IsA(setOp, SetOperationStmt))
371 {
372 SetOperationStmt *op = (SetOperationStmt *) setOp;
373 Path *path;
374
375 /* UNIONs are much different from INTERSECT/EXCEPT */
376 if (op->op == SETOP_UNION)
377 path = generate_union_path(op, root,
378 refnames_tlist,
379 pTargetList,
380 pNumGroups);
381 else
382 path = generate_nonunion_path(op, root,
383 refnames_tlist,
384 pTargetList,
385 pNumGroups);
386
387 /*
388 * If necessary, add a Result node to project the caller-requested
389 * output columns.
390 *
391 * XXX you don't really want to know about this: setrefs.c will apply
392 * fix_upper_expr() to the Result node's tlist. This would fail if the
393 * Vars generated by generate_setop_tlist() were not exactly equal()
394 * to the corresponding tlist entries of the subplan. However, since
395 * the subplan was generated by generate_union_plan() or
396 * generate_nonunion_plan(), and hence its tlist was generated by
397 * generate_append_tlist(), this will work. We just tell
398 * generate_setop_tlist() to use varno 0.
399 */
400 if (flag >= 0 ||
401 !tlist_same_datatypes(*pTargetList, colTypes, junkOK) ||
402 !tlist_same_collations(*pTargetList, colCollations, junkOK))
403 {
404 *pTargetList = generate_setop_tlist(colTypes, colCollations,
405 flag,
406 0,
407 false,
408 *pTargetList,
409 refnames_tlist);
410 path = apply_projection_to_path(root,
411 path->parent,
412 path,
413 create_pathtarget(root,
414 *pTargetList));
415 }
416 return path;
417 }
418 else
419 {
420 elog(ERROR, "unrecognized node type: %d",
421 (int) nodeTag(setOp));
422 *pTargetList = NIL;
423 return NULL; /* keep compiler quiet */
424 }
425 }
426
427 /*
428 * Generate path for a recursive UNION node
429 */
430 static Path *
generate_recursion_path(SetOperationStmt * setOp,PlannerInfo * root,List * refnames_tlist,List ** pTargetList)431 generate_recursion_path(SetOperationStmt *setOp, PlannerInfo *root,
432 List *refnames_tlist,
433 List **pTargetList)
434 {
435 RelOptInfo *result_rel = fetch_upper_rel(root, UPPERREL_SETOP, NULL);
436 Path *path;
437 Path *lpath;
438 Path *rpath;
439 List *lpath_tlist;
440 List *rpath_tlist;
441 List *tlist;
442 List *groupList;
443 double dNumGroups;
444
445 /* Parser should have rejected other cases */
446 if (setOp->op != SETOP_UNION)
447 elog(ERROR, "only UNION queries can be recursive");
448 /* Worktable ID should be assigned */
449 Assert(root->wt_param_id >= 0);
450
451 /*
452 * Unlike a regular UNION node, process the left and right inputs
453 * separately without any intention of combining them into one Append.
454 */
455 lpath = recurse_set_operations(setOp->larg, root,
456 setOp->colTypes, setOp->colCollations,
457 false, -1,
458 refnames_tlist,
459 &lpath_tlist,
460 NULL);
461 /* The right path will want to look at the left one ... */
462 root->non_recursive_path = lpath;
463 rpath = recurse_set_operations(setOp->rarg, root,
464 setOp->colTypes, setOp->colCollations,
465 false, -1,
466 refnames_tlist,
467 &rpath_tlist,
468 NULL);
469 root->non_recursive_path = NULL;
470
471 /*
472 * Generate tlist for RecursiveUnion path node --- same as in Append cases
473 */
474 tlist = generate_append_tlist(setOp->colTypes, setOp->colCollations, false,
475 list_make2(lpath_tlist, rpath_tlist),
476 refnames_tlist);
477
478 *pTargetList = tlist;
479
480 /*
481 * If UNION, identify the grouping operators
482 */
483 if (setOp->all)
484 {
485 groupList = NIL;
486 dNumGroups = 0;
487 }
488 else
489 {
490 /* Identify the grouping semantics */
491 groupList = generate_setop_grouplist(setOp, tlist);
492
493 /* We only support hashing here */
494 if (!grouping_is_hashable(groupList))
495 ereport(ERROR,
496 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
497 errmsg("could not implement recursive UNION"),
498 errdetail("All column datatypes must be hashable.")));
499
500 /*
501 * For the moment, take the number of distinct groups as equal to the
502 * total input size, ie, the worst case.
503 */
504 dNumGroups = lpath->rows + rpath->rows * 10;
505 }
506
507 /*
508 * And make the path node.
509 */
510 path = (Path *) create_recursiveunion_path(root,
511 result_rel,
512 lpath,
513 rpath,
514 create_pathtarget(root, tlist),
515 groupList,
516 root->wt_param_id,
517 dNumGroups);
518
519 return path;
520 }
521
522 /*
523 * Generate path for a UNION or UNION ALL node
524 */
525 static Path *
generate_union_path(SetOperationStmt * op,PlannerInfo * root,List * refnames_tlist,List ** pTargetList,double * pNumGroups)526 generate_union_path(SetOperationStmt *op, PlannerInfo *root,
527 List *refnames_tlist,
528 List **pTargetList,
529 double *pNumGroups)
530 {
531 RelOptInfo *result_rel = fetch_upper_rel(root, UPPERREL_SETOP, NULL);
532 double save_fraction = root->tuple_fraction;
533 List *pathlist;
534 List *child_tlists1;
535 List *child_tlists2;
536 List *tlist_list;
537 List *tlist;
538 Path *path;
539
540 /*
541 * If plain UNION, tell children to fetch all tuples.
542 *
543 * Note: in UNION ALL, we pass the top-level tuple_fraction unmodified to
544 * each arm of the UNION ALL. One could make a case for reducing the
545 * tuple fraction for later arms (discounting by the expected size of the
546 * earlier arms' results) but it seems not worth the trouble. The normal
547 * case where tuple_fraction isn't already zero is a LIMIT at top level,
548 * and passing it down as-is is usually enough to get the desired result
549 * of preferring fast-start plans.
550 */
551 if (!op->all)
552 root->tuple_fraction = 0.0;
553
554 /*
555 * If any of my children are identical UNION nodes (same op, all-flag, and
556 * colTypes) then they can be merged into this node so that we generate
557 * only one Append and unique-ification for the lot. Recurse to find such
558 * nodes and compute their children's paths.
559 */
560 pathlist = list_concat(recurse_union_children(op->larg, root,
561 op, refnames_tlist,
562 &child_tlists1),
563 recurse_union_children(op->rarg, root,
564 op, refnames_tlist,
565 &child_tlists2));
566 tlist_list = list_concat(child_tlists1, child_tlists2);
567
568 /*
569 * Generate tlist for Append plan node.
570 *
571 * The tlist for an Append plan isn't important as far as the Append is
572 * concerned, but we must make it look real anyway for the benefit of the
573 * next plan level up.
574 */
575 tlist = generate_append_tlist(op->colTypes, op->colCollations, false,
576 tlist_list, refnames_tlist);
577
578 *pTargetList = tlist;
579
580 /*
581 * Append the child results together.
582 */
583 path = (Path *) create_append_path(result_rel, pathlist, NULL, 0);
584
585 /* We have to manually jam the right tlist into the path; ick */
586 path->pathtarget = create_pathtarget(root, tlist);
587
588 /*
589 * For UNION ALL, we just need the Append path. For UNION, need to add
590 * node(s) to remove duplicates.
591 */
592 if (!op->all)
593 path = make_union_unique(op, path, tlist, root);
594
595 /*
596 * Estimate number of groups if caller wants it. For now we just assume
597 * the output is unique --- this is certainly true for the UNION case, and
598 * we want worst-case estimates anyway.
599 */
600 if (pNumGroups)
601 *pNumGroups = path->rows;
602
603 /* Undo effects of possibly forcing tuple_fraction to 0 */
604 root->tuple_fraction = save_fraction;
605
606 return path;
607 }
608
609 /*
610 * Generate path for an INTERSECT, INTERSECT ALL, EXCEPT, or EXCEPT ALL node
611 */
612 static Path *
generate_nonunion_path(SetOperationStmt * op,PlannerInfo * root,List * refnames_tlist,List ** pTargetList,double * pNumGroups)613 generate_nonunion_path(SetOperationStmt *op, PlannerInfo *root,
614 List *refnames_tlist,
615 List **pTargetList,
616 double *pNumGroups)
617 {
618 RelOptInfo *result_rel = fetch_upper_rel(root, UPPERREL_SETOP, NULL);
619 double save_fraction = root->tuple_fraction;
620 Path *lpath,
621 *rpath,
622 *path;
623 List *lpath_tlist,
624 *rpath_tlist,
625 *tlist_list,
626 *tlist,
627 *groupList,
628 *pathlist;
629 double dLeftGroups,
630 dRightGroups,
631 dNumGroups,
632 dNumOutputRows;
633 bool use_hash;
634 SetOpCmd cmd;
635 int firstFlag;
636
637 /*
638 * Tell children to fetch all tuples.
639 */
640 root->tuple_fraction = 0.0;
641
642 /* Recurse on children, ensuring their outputs are marked */
643 lpath = recurse_set_operations(op->larg, root,
644 op->colTypes, op->colCollations,
645 false, 0,
646 refnames_tlist,
647 &lpath_tlist,
648 &dLeftGroups);
649 rpath = recurse_set_operations(op->rarg, root,
650 op->colTypes, op->colCollations,
651 false, 1,
652 refnames_tlist,
653 &rpath_tlist,
654 &dRightGroups);
655
656 /* Undo effects of forcing tuple_fraction to 0 */
657 root->tuple_fraction = save_fraction;
658
659 /*
660 * For EXCEPT, we must put the left input first. For INTERSECT, either
661 * order should give the same results, and we prefer to put the smaller
662 * input first in order to minimize the size of the hash table in the
663 * hashing case. "Smaller" means the one with the fewer groups.
664 */
665 if (op->op == SETOP_EXCEPT || dLeftGroups <= dRightGroups)
666 {
667 pathlist = list_make2(lpath, rpath);
668 tlist_list = list_make2(lpath_tlist, rpath_tlist);
669 firstFlag = 0;
670 }
671 else
672 {
673 pathlist = list_make2(rpath, lpath);
674 tlist_list = list_make2(rpath_tlist, lpath_tlist);
675 firstFlag = 1;
676 }
677
678 /*
679 * Generate tlist for Append plan node.
680 *
681 * The tlist for an Append plan isn't important as far as the Append is
682 * concerned, but we must make it look real anyway for the benefit of the
683 * next plan level up. In fact, it has to be real enough that the flag
684 * column is shown as a variable not a constant, else setrefs.c will get
685 * confused.
686 */
687 tlist = generate_append_tlist(op->colTypes, op->colCollations, true,
688 tlist_list, refnames_tlist);
689
690 *pTargetList = tlist;
691
692 /*
693 * Append the child results together.
694 */
695 path = (Path *) create_append_path(result_rel, pathlist, NULL, 0);
696
697 /* We have to manually jam the right tlist into the path; ick */
698 path->pathtarget = create_pathtarget(root, tlist);
699
700 /* Identify the grouping semantics */
701 groupList = generate_setop_grouplist(op, tlist);
702
703 /* punt if nothing to group on (not worth fixing in back branches) */
704 if (groupList == NIL)
705 ereport(ERROR,
706 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
707 /* translator: %s is UNION, INTERSECT, or EXCEPT */
708 errmsg("%s over no columns is not supported",
709 (op->op == SETOP_INTERSECT) ? "INTERSECT" : "EXCEPT")));
710
711 /*
712 * Estimate number of distinct groups that we'll need hashtable entries
713 * for; this is the size of the left-hand input for EXCEPT, or the smaller
714 * input for INTERSECT. Also estimate the number of eventual output rows.
715 * In non-ALL cases, we estimate each group produces one output row; in
716 * ALL cases use the relevant relation size. These are worst-case
717 * estimates, of course, but we need to be conservative.
718 */
719 if (op->op == SETOP_EXCEPT)
720 {
721 dNumGroups = dLeftGroups;
722 dNumOutputRows = op->all ? lpath->rows : dNumGroups;
723 }
724 else
725 {
726 dNumGroups = Min(dLeftGroups, dRightGroups);
727 dNumOutputRows = op->all ? Min(lpath->rows, rpath->rows) : dNumGroups;
728 }
729
730 /*
731 * Decide whether to hash or sort, and add a sort node if needed.
732 */
733 use_hash = choose_hashed_setop(root, groupList, path,
734 dNumGroups, dNumOutputRows,
735 (op->op == SETOP_INTERSECT) ? "INTERSECT" : "EXCEPT");
736
737 if (!use_hash)
738 path = (Path *) create_sort_path(root,
739 result_rel,
740 path,
741 make_pathkeys_for_sortclauses(root,
742 groupList,
743 tlist),
744 -1.0);
745
746 /*
747 * Finally, add a SetOp path node to generate the correct output.
748 */
749 switch (op->op)
750 {
751 case SETOP_INTERSECT:
752 cmd = op->all ? SETOPCMD_INTERSECT_ALL : SETOPCMD_INTERSECT;
753 break;
754 case SETOP_EXCEPT:
755 cmd = op->all ? SETOPCMD_EXCEPT_ALL : SETOPCMD_EXCEPT;
756 break;
757 default:
758 elog(ERROR, "unrecognized set op: %d", (int) op->op);
759 cmd = SETOPCMD_INTERSECT; /* keep compiler quiet */
760 break;
761 }
762 path = (Path *) create_setop_path(root,
763 result_rel,
764 path,
765 cmd,
766 use_hash ? SETOP_HASHED : SETOP_SORTED,
767 groupList,
768 list_length(op->colTypes) + 1,
769 use_hash ? firstFlag : -1,
770 dNumGroups,
771 dNumOutputRows);
772
773 if (pNumGroups)
774 *pNumGroups = dNumGroups;
775
776 return path;
777 }
778
779 /*
780 * Pull up children of a UNION node that are identically-propertied UNIONs.
781 *
782 * NOTE: we can also pull a UNION ALL up into a UNION, since the distinct
783 * output rows will be lost anyway.
784 *
785 * NOTE: currently, we ignore collations while determining if a child has
786 * the same properties. This is semantically sound only so long as all
787 * collations have the same notion of equality. It is valid from an
788 * implementation standpoint because we don't care about the ordering of
789 * a UNION child's result: UNION ALL results are always unordered, and
790 * generate_union_path will force a fresh sort if the top level is a UNION.
791 */
792 static List *
recurse_union_children(Node * setOp,PlannerInfo * root,SetOperationStmt * top_union,List * refnames_tlist,List ** tlist_list)793 recurse_union_children(Node *setOp, PlannerInfo *root,
794 SetOperationStmt *top_union,
795 List *refnames_tlist,
796 List **tlist_list)
797 {
798 List *result;
799 List *child_tlist;
800
801 if (IsA(setOp, SetOperationStmt))
802 {
803 SetOperationStmt *op = (SetOperationStmt *) setOp;
804
805 if (op->op == top_union->op &&
806 (op->all == top_union->all || op->all) &&
807 equal(op->colTypes, top_union->colTypes))
808 {
809 /* Same UNION, so fold children into parent's subpath list */
810 List *child_tlists1;
811 List *child_tlists2;
812
813 result = list_concat(recurse_union_children(op->larg, root,
814 top_union,
815 refnames_tlist,
816 &child_tlists1),
817 recurse_union_children(op->rarg, root,
818 top_union,
819 refnames_tlist,
820 &child_tlists2));
821 *tlist_list = list_concat(child_tlists1, child_tlists2);
822 return result;
823 }
824 }
825
826 /*
827 * Not same, so plan this child separately.
828 *
829 * Note we disallow any resjunk columns in child results. This is
830 * necessary since the Append node that implements the union won't do any
831 * projection, and upper levels will get confused if some of our output
832 * tuples have junk and some don't. This case only arises when we have an
833 * EXCEPT or INTERSECT as child, else there won't be resjunk anyway.
834 */
835 result = list_make1(recurse_set_operations(setOp, root,
836 top_union->colTypes,
837 top_union->colCollations,
838 false, -1,
839 refnames_tlist,
840 &child_tlist,
841 NULL));
842 *tlist_list = list_make1(child_tlist);
843 return result;
844 }
845
846 /*
847 * Add nodes to the given path tree to unique-ify the result of a UNION.
848 */
849 static Path *
make_union_unique(SetOperationStmt * op,Path * path,List * tlist,PlannerInfo * root)850 make_union_unique(SetOperationStmt *op, Path *path, List *tlist,
851 PlannerInfo *root)
852 {
853 RelOptInfo *result_rel = fetch_upper_rel(root, UPPERREL_SETOP, NULL);
854 List *groupList;
855 double dNumGroups;
856
857 /* Identify the grouping semantics */
858 groupList = generate_setop_grouplist(op, tlist);
859
860 /* punt if nothing to group on (not worth fixing in back branches) */
861 if (groupList == NIL)
862 ereport(ERROR,
863 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
864 /* translator: %s is UNION, INTERSECT, or EXCEPT */
865 errmsg("%s over no columns is not supported", "UNION")));
866
867 /*
868 * XXX for the moment, take the number of distinct groups as equal to the
869 * total input size, ie, the worst case. This is too conservative, but we
870 * don't want to risk having the hashtable overrun memory; also, it's not
871 * clear how to get a decent estimate of the true size. One should note
872 * as well the propensity of novices to write UNION rather than UNION ALL
873 * even when they don't expect any duplicates...
874 */
875 dNumGroups = path->rows;
876
877 /* Decide whether to hash or sort */
878 if (choose_hashed_setop(root, groupList, path,
879 dNumGroups, dNumGroups,
880 "UNION"))
881 {
882 /* Hashed aggregate plan --- no sort needed */
883 path = (Path *) create_agg_path(root,
884 result_rel,
885 path,
886 create_pathtarget(root, tlist),
887 AGG_HASHED,
888 AGGSPLIT_SIMPLE,
889 groupList,
890 NIL,
891 NULL,
892 dNumGroups);
893 }
894 else
895 {
896 /* Sort and Unique */
897 path = (Path *) create_sort_path(root,
898 result_rel,
899 path,
900 make_pathkeys_for_sortclauses(root,
901 groupList,
902 tlist),
903 -1.0);
904 /* We have to manually jam the right tlist into the path; ick */
905 path->pathtarget = create_pathtarget(root, tlist);
906 path = (Path *) create_upper_unique_path(root,
907 result_rel,
908 path,
909 list_length(path->pathkeys),
910 dNumGroups);
911 }
912
913 return path;
914 }
915
916 /*
917 * choose_hashed_setop - should we use hashing for a set operation?
918 */
919 static bool
choose_hashed_setop(PlannerInfo * root,List * groupClauses,Path * input_path,double dNumGroups,double dNumOutputRows,const char * construct)920 choose_hashed_setop(PlannerInfo *root, List *groupClauses,
921 Path *input_path,
922 double dNumGroups, double dNumOutputRows,
923 const char *construct)
924 {
925 int numGroupCols = list_length(groupClauses);
926 bool can_sort;
927 bool can_hash;
928 Size hashentrysize;
929 Path hashed_p;
930 Path sorted_p;
931 double tuple_fraction;
932
933 /* Check whether the operators support sorting or hashing */
934 can_sort = grouping_is_sortable(groupClauses);
935 can_hash = grouping_is_hashable(groupClauses);
936 if (can_hash && can_sort)
937 {
938 /* we have a meaningful choice to make, continue ... */
939 }
940 else if (can_hash)
941 return true;
942 else if (can_sort)
943 return false;
944 else
945 ereport(ERROR,
946 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
947 /* translator: %s is UNION, INTERSECT, or EXCEPT */
948 errmsg("could not implement %s", construct),
949 errdetail("Some of the datatypes only support hashing, while others only support sorting.")));
950
951 /* Prefer sorting when enable_hashagg is off */
952 if (!enable_hashagg)
953 return false;
954
955 /*
956 * Don't do it if it doesn't look like the hashtable will fit into
957 * work_mem.
958 */
959 hashentrysize = MAXALIGN(input_path->pathtarget->width) + MAXALIGN(SizeofMinimalTupleHeader);
960
961 if (hashentrysize * dNumGroups > work_mem * 1024L)
962 return false;
963
964 /*
965 * See if the estimated cost is no more than doing it the other way.
966 *
967 * We need to consider input_plan + hashagg versus input_plan + sort +
968 * group. Note that the actual result plan might involve a SetOp or
969 * Unique node, not Agg or Group, but the cost estimates for Agg and Group
970 * should be close enough for our purposes here.
971 *
972 * These path variables are dummies that just hold cost fields; we don't
973 * make actual Paths for these steps.
974 */
975 cost_agg(&hashed_p, root, AGG_HASHED, NULL,
976 numGroupCols, dNumGroups,
977 input_path->startup_cost, input_path->total_cost,
978 input_path->rows);
979
980 /*
981 * Now for the sorted case. Note that the input is *always* unsorted,
982 * since it was made by appending unrelated sub-relations together.
983 */
984 sorted_p.startup_cost = input_path->startup_cost;
985 sorted_p.total_cost = input_path->total_cost;
986 /* XXX cost_sort doesn't actually look at pathkeys, so just pass NIL */
987 cost_sort(&sorted_p, root, NIL, sorted_p.total_cost,
988 input_path->rows, input_path->pathtarget->width,
989 0.0, work_mem, -1.0);
990 cost_group(&sorted_p, root, numGroupCols, dNumGroups,
991 sorted_p.startup_cost, sorted_p.total_cost,
992 input_path->rows);
993
994 /*
995 * Now make the decision using the top-level tuple fraction. First we
996 * have to convert an absolute count (LIMIT) into fractional form.
997 */
998 tuple_fraction = root->tuple_fraction;
999 if (tuple_fraction >= 1.0)
1000 tuple_fraction /= dNumOutputRows;
1001
1002 if (compare_fractional_path_costs(&hashed_p, &sorted_p,
1003 tuple_fraction) < 0)
1004 {
1005 /* Hashed is cheaper, so use it */
1006 return true;
1007 }
1008 return false;
1009 }
1010
1011 /*
1012 * Generate targetlist for a set-operation plan node
1013 *
1014 * colTypes: OID list of set-op's result column datatypes
1015 * colCollations: OID list of set-op's result column collations
1016 * flag: -1 if no flag column needed, 0 or 1 to create a const flag column
1017 * varno: varno to use in generated Vars
1018 * hack_constants: true to copy up constants (see comments in code)
1019 * input_tlist: targetlist of this node's input node
1020 * refnames_tlist: targetlist to take column names from
1021 */
1022 static List *
generate_setop_tlist(List * colTypes,List * colCollations,int flag,Index varno,bool hack_constants,List * input_tlist,List * refnames_tlist)1023 generate_setop_tlist(List *colTypes, List *colCollations,
1024 int flag,
1025 Index varno,
1026 bool hack_constants,
1027 List *input_tlist,
1028 List *refnames_tlist)
1029 {
1030 List *tlist = NIL;
1031 int resno = 1;
1032 ListCell *ctlc,
1033 *cclc,
1034 *itlc,
1035 *rtlc;
1036 TargetEntry *tle;
1037 Node *expr;
1038
1039 /* there's no forfour() so we must chase one list manually */
1040 rtlc = list_head(refnames_tlist);
1041 forthree(ctlc, colTypes, cclc, colCollations, itlc, input_tlist)
1042 {
1043 Oid colType = lfirst_oid(ctlc);
1044 Oid colColl = lfirst_oid(cclc);
1045 TargetEntry *inputtle = (TargetEntry *) lfirst(itlc);
1046 TargetEntry *reftle = (TargetEntry *) lfirst(rtlc);
1047
1048 rtlc = lnext(rtlc);
1049
1050 Assert(inputtle->resno == resno);
1051 Assert(reftle->resno == resno);
1052 Assert(!inputtle->resjunk);
1053 Assert(!reftle->resjunk);
1054
1055 /*
1056 * Generate columns referencing input columns and having appropriate
1057 * data types and column names. Insert datatype coercions where
1058 * necessary.
1059 *
1060 * HACK: constants in the input's targetlist are copied up as-is
1061 * rather than being referenced as subquery outputs. This is mainly
1062 * to ensure that when we try to coerce them to the output column's
1063 * datatype, the right things happen for UNKNOWN constants. But do
1064 * this only at the first level of subquery-scan plans; we don't want
1065 * phony constants appearing in the output tlists of upper-level
1066 * nodes!
1067 */
1068 if (hack_constants && inputtle->expr && IsA(inputtle->expr, Const))
1069 expr = (Node *) inputtle->expr;
1070 else
1071 expr = (Node *) makeVar(varno,
1072 inputtle->resno,
1073 exprType((Node *) inputtle->expr),
1074 exprTypmod((Node *) inputtle->expr),
1075 exprCollation((Node *) inputtle->expr),
1076 0);
1077
1078 if (exprType(expr) != colType)
1079 {
1080 /*
1081 * Note: it's not really cool to be applying coerce_to_common_type
1082 * here; one notable point is that assign_expr_collations never
1083 * gets run on any generated nodes. For the moment that's not a
1084 * problem because we force the correct exposed collation below.
1085 * It would likely be best to make the parser generate the correct
1086 * output tlist for every set-op to begin with, though.
1087 */
1088 expr = coerce_to_common_type(NULL, /* no UNKNOWNs here */
1089 expr,
1090 colType,
1091 "UNION/INTERSECT/EXCEPT");
1092 }
1093
1094 /*
1095 * Ensure the tlist entry's exposed collation matches the set-op. This
1096 * is necessary because plan_set_operations() reports the result
1097 * ordering as a list of SortGroupClauses, which don't carry collation
1098 * themselves but just refer to tlist entries. If we don't show the
1099 * right collation then planner.c might do the wrong thing in
1100 * higher-level queries.
1101 *
1102 * Note we use RelabelType, not CollateExpr, since this expression
1103 * will reach the executor without any further processing.
1104 */
1105 if (exprCollation(expr) != colColl)
1106 {
1107 expr = (Node *) makeRelabelType((Expr *) expr,
1108 exprType(expr),
1109 exprTypmod(expr),
1110 colColl,
1111 COERCE_IMPLICIT_CAST);
1112 }
1113
1114 tle = makeTargetEntry((Expr *) expr,
1115 (AttrNumber) resno++,
1116 pstrdup(reftle->resname),
1117 false);
1118
1119 /*
1120 * By convention, all non-resjunk columns in a setop tree have
1121 * ressortgroupref equal to their resno. In some cases the ref isn't
1122 * needed, but this is a cleaner way than modifying the tlist later.
1123 */
1124 tle->ressortgroupref = tle->resno;
1125
1126 tlist = lappend(tlist, tle);
1127 }
1128
1129 if (flag >= 0)
1130 {
1131 /* Add a resjunk flag column */
1132 /* flag value is the given constant */
1133 expr = (Node *) makeConst(INT4OID,
1134 -1,
1135 InvalidOid,
1136 sizeof(int32),
1137 Int32GetDatum(flag),
1138 false,
1139 true);
1140 tle = makeTargetEntry((Expr *) expr,
1141 (AttrNumber) resno++,
1142 pstrdup("flag"),
1143 true);
1144 tlist = lappend(tlist, tle);
1145 }
1146
1147 return tlist;
1148 }
1149
1150 /*
1151 * Generate targetlist for a set-operation Append node
1152 *
1153 * colTypes: OID list of set-op's result column datatypes
1154 * colCollations: OID list of set-op's result column collations
1155 * flag: true to create a flag column copied up from subplans
1156 * input_tlists: list of tlists for sub-plans of the Append
1157 * refnames_tlist: targetlist to take column names from
1158 *
1159 * The entries in the Append's targetlist should always be simple Vars;
1160 * we just have to make sure they have the right datatypes/typmods/collations.
1161 * The Vars are always generated with varno 0.
1162 *
1163 * XXX a problem with the varno-zero approach is that set_pathtarget_cost_width
1164 * cannot figure out a realistic width for the tlist we make here. But we
1165 * ought to refactor this code to produce a PathTarget directly, anyway.
1166 */
1167 static List *
generate_append_tlist(List * colTypes,List * colCollations,bool flag,List * input_tlists,List * refnames_tlist)1168 generate_append_tlist(List *colTypes, List *colCollations,
1169 bool flag,
1170 List *input_tlists,
1171 List *refnames_tlist)
1172 {
1173 List *tlist = NIL;
1174 int resno = 1;
1175 ListCell *curColType;
1176 ListCell *curColCollation;
1177 ListCell *ref_tl_item;
1178 int colindex;
1179 TargetEntry *tle;
1180 Node *expr;
1181 ListCell *tlistl;
1182 int32 *colTypmods;
1183
1184 /*
1185 * First extract typmods to use.
1186 *
1187 * If the inputs all agree on type and typmod of a particular column, use
1188 * that typmod; else use -1.
1189 */
1190 colTypmods = (int32 *) palloc(list_length(colTypes) * sizeof(int32));
1191
1192 foreach(tlistl, input_tlists)
1193 {
1194 List *subtlist = (List *) lfirst(tlistl);
1195 ListCell *subtlistl;
1196
1197 curColType = list_head(colTypes);
1198 colindex = 0;
1199 foreach(subtlistl, subtlist)
1200 {
1201 TargetEntry *subtle = (TargetEntry *) lfirst(subtlistl);
1202
1203 if (subtle->resjunk)
1204 continue;
1205 Assert(curColType != NULL);
1206 if (exprType((Node *) subtle->expr) == lfirst_oid(curColType))
1207 {
1208 /* If first subplan, copy the typmod; else compare */
1209 int32 subtypmod = exprTypmod((Node *) subtle->expr);
1210
1211 if (tlistl == list_head(input_tlists))
1212 colTypmods[colindex] = subtypmod;
1213 else if (subtypmod != colTypmods[colindex])
1214 colTypmods[colindex] = -1;
1215 }
1216 else
1217 {
1218 /* types disagree, so force typmod to -1 */
1219 colTypmods[colindex] = -1;
1220 }
1221 curColType = lnext(curColType);
1222 colindex++;
1223 }
1224 Assert(curColType == NULL);
1225 }
1226
1227 /*
1228 * Now we can build the tlist for the Append.
1229 */
1230 colindex = 0;
1231 forthree(curColType, colTypes, curColCollation, colCollations,
1232 ref_tl_item, refnames_tlist)
1233 {
1234 Oid colType = lfirst_oid(curColType);
1235 int32 colTypmod = colTypmods[colindex++];
1236 Oid colColl = lfirst_oid(curColCollation);
1237 TargetEntry *reftle = (TargetEntry *) lfirst(ref_tl_item);
1238
1239 Assert(reftle->resno == resno);
1240 Assert(!reftle->resjunk);
1241 expr = (Node *) makeVar(0,
1242 resno,
1243 colType,
1244 colTypmod,
1245 colColl,
1246 0);
1247 tle = makeTargetEntry((Expr *) expr,
1248 (AttrNumber) resno++,
1249 pstrdup(reftle->resname),
1250 false);
1251
1252 /*
1253 * By convention, all non-resjunk columns in a setop tree have
1254 * ressortgroupref equal to their resno. In some cases the ref isn't
1255 * needed, but this is a cleaner way than modifying the tlist later.
1256 */
1257 tle->ressortgroupref = tle->resno;
1258
1259 tlist = lappend(tlist, tle);
1260 }
1261
1262 if (flag)
1263 {
1264 /* Add a resjunk flag column */
1265 /* flag value is shown as copied up from subplan */
1266 expr = (Node *) makeVar(0,
1267 resno,
1268 INT4OID,
1269 -1,
1270 InvalidOid,
1271 0);
1272 tle = makeTargetEntry((Expr *) expr,
1273 (AttrNumber) resno++,
1274 pstrdup("flag"),
1275 true);
1276 tlist = lappend(tlist, tle);
1277 }
1278
1279 pfree(colTypmods);
1280
1281 return tlist;
1282 }
1283
1284 /*
1285 * generate_setop_grouplist
1286 * Build a SortGroupClause list defining the sort/grouping properties
1287 * of the setop's output columns.
1288 *
1289 * Parse analysis already determined the properties and built a suitable
1290 * list, except that the entries do not have sortgrouprefs set because
1291 * the parser output representation doesn't include a tlist for each
1292 * setop. So what we need to do here is copy that list and install
1293 * proper sortgrouprefs into it (copying those from the targetlist).
1294 */
1295 static List *
generate_setop_grouplist(SetOperationStmt * op,List * targetlist)1296 generate_setop_grouplist(SetOperationStmt *op, List *targetlist)
1297 {
1298 List *grouplist = (List *) copyObject(op->groupClauses);
1299 ListCell *lg;
1300 ListCell *lt;
1301
1302 lg = list_head(grouplist);
1303 foreach(lt, targetlist)
1304 {
1305 TargetEntry *tle = (TargetEntry *) lfirst(lt);
1306 SortGroupClause *sgc;
1307
1308 if (tle->resjunk)
1309 {
1310 /* resjunk columns should not have sortgrouprefs */
1311 Assert(tle->ressortgroupref == 0);
1312 continue; /* ignore resjunk columns */
1313 }
1314
1315 /* non-resjunk columns should have sortgroupref = resno */
1316 Assert(tle->ressortgroupref == tle->resno);
1317
1318 /* non-resjunk columns should have grouping clauses */
1319 Assert(lg != NULL);
1320 sgc = (SortGroupClause *) lfirst(lg);
1321 lg = lnext(lg);
1322 Assert(sgc->tleSortGroupRef == 0);
1323
1324 sgc->tleSortGroupRef = tle->ressortgroupref;
1325 }
1326 Assert(lg == NULL);
1327 return grouplist;
1328 }
1329
1330
1331 /*
1332 * expand_inherited_tables
1333 * Expand each rangetable entry that represents an inheritance set
1334 * into an "append relation". At the conclusion of this process,
1335 * the "inh" flag is set in all and only those RTEs that are append
1336 * relation parents.
1337 */
1338 void
expand_inherited_tables(PlannerInfo * root)1339 expand_inherited_tables(PlannerInfo *root)
1340 {
1341 Index nrtes;
1342 Index rti;
1343 ListCell *rl;
1344
1345 /*
1346 * expand_inherited_rtentry may add RTEs to parse->rtable; there is no
1347 * need to scan them since they can't have inh=true. So just scan as far
1348 * as the original end of the rtable list.
1349 */
1350 nrtes = list_length(root->parse->rtable);
1351 rl = list_head(root->parse->rtable);
1352 for (rti = 1; rti <= nrtes; rti++)
1353 {
1354 RangeTblEntry *rte = (RangeTblEntry *) lfirst(rl);
1355
1356 expand_inherited_rtentry(root, rte, rti);
1357 rl = lnext(rl);
1358 }
1359 }
1360
1361 /*
1362 * expand_inherited_rtentry
1363 * Check whether a rangetable entry represents an inheritance set.
1364 * If so, add entries for all the child tables to the query's
1365 * rangetable, and build AppendRelInfo nodes for all the child tables
1366 * and add them to root->append_rel_list. If not, clear the entry's
1367 * "inh" flag to prevent later code from looking for AppendRelInfos.
1368 *
1369 * Note that the original RTE is considered to represent the whole
1370 * inheritance set. The first of the generated RTEs is an RTE for the same
1371 * table, but with inh = false, to represent the parent table in its role
1372 * as a simple member of the inheritance set.
1373 *
1374 * A childless table is never considered to be an inheritance set; therefore
1375 * a parent RTE must always have at least two associated AppendRelInfos.
1376 */
1377 static void
expand_inherited_rtentry(PlannerInfo * root,RangeTblEntry * rte,Index rti)1378 expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
1379 {
1380 Query *parse = root->parse;
1381 Oid parentOID;
1382 PlanRowMark *oldrc;
1383 Relation oldrelation;
1384 LOCKMODE lockmode;
1385 List *inhOIDs;
1386 List *appinfos;
1387 ListCell *l;
1388
1389 /* Does RT entry allow inheritance? */
1390 if (!rte->inh)
1391 return;
1392 /* Ignore any already-expanded UNION ALL nodes */
1393 if (rte->rtekind != RTE_RELATION)
1394 {
1395 Assert(rte->rtekind == RTE_SUBQUERY);
1396 return;
1397 }
1398 /* Fast path for common case of childless table */
1399 parentOID = rte->relid;
1400 if (!has_subclass(parentOID))
1401 {
1402 /* Clear flag before returning */
1403 rte->inh = false;
1404 return;
1405 }
1406
1407 /*
1408 * The rewriter should already have obtained an appropriate lock on each
1409 * relation named in the query. However, for each child relation we add
1410 * to the query, we must obtain an appropriate lock, because this will be
1411 * the first use of those relations in the parse/rewrite/plan pipeline.
1412 *
1413 * If the parent relation is the query's result relation, then we need
1414 * RowExclusiveLock. Otherwise, if it's accessed FOR UPDATE/SHARE, we
1415 * need RowShareLock; otherwise AccessShareLock. We can't just grab
1416 * AccessShareLock because then the executor would be trying to upgrade
1417 * the lock, leading to possible deadlocks. (This code should match the
1418 * parser and rewriter.)
1419 */
1420 oldrc = get_plan_rowmark(root->rowMarks, rti);
1421 if (rti == parse->resultRelation)
1422 lockmode = RowExclusiveLock;
1423 else if (oldrc && RowMarkRequiresRowShareLock(oldrc->markType))
1424 lockmode = RowShareLock;
1425 else
1426 lockmode = AccessShareLock;
1427
1428 /* Scan for all members of inheritance set, acquire needed locks */
1429 inhOIDs = find_all_inheritors(parentOID, lockmode, NULL);
1430
1431 /*
1432 * Check that there's at least one descendant, else treat as no-child
1433 * case. This could happen despite above has_subclass() check, if table
1434 * once had a child but no longer does.
1435 */
1436 if (list_length(inhOIDs) < 2)
1437 {
1438 /* Clear flag before returning */
1439 rte->inh = false;
1440 return;
1441 }
1442
1443 /*
1444 * If parent relation is selected FOR UPDATE/SHARE, we need to mark its
1445 * PlanRowMark as isParent = true, and generate a new PlanRowMark for each
1446 * child.
1447 */
1448 if (oldrc)
1449 oldrc->isParent = true;
1450
1451 /*
1452 * Must open the parent relation to examine its tupdesc. We need not lock
1453 * it; we assume the rewriter already did.
1454 */
1455 oldrelation = heap_open(parentOID, NoLock);
1456
1457 /* Scan the inheritance set and expand it */
1458 appinfos = NIL;
1459 foreach(l, inhOIDs)
1460 {
1461 Oid childOID = lfirst_oid(l);
1462 Relation newrelation;
1463 RangeTblEntry *childrte;
1464 Index childRTindex;
1465 AppendRelInfo *appinfo;
1466
1467 /* Open rel if needed; we already have required locks */
1468 if (childOID != parentOID)
1469 newrelation = heap_open(childOID, NoLock);
1470 else
1471 newrelation = oldrelation;
1472
1473 /*
1474 * It is possible that the parent table has children that are temp
1475 * tables of other backends. We cannot safely access such tables
1476 * (because of buffering issues), and the best thing to do seems to be
1477 * to silently ignore them.
1478 */
1479 if (childOID != parentOID && RELATION_IS_OTHER_TEMP(newrelation))
1480 {
1481 heap_close(newrelation, lockmode);
1482 continue;
1483 }
1484
1485 /*
1486 * Build an RTE for the child, and attach to query's rangetable list.
1487 * We copy most fields of the parent's RTE, but replace relation OID
1488 * and relkind, and set inh = false. Also, set requiredPerms to zero
1489 * since all required permissions checks are done on the original RTE.
1490 */
1491 childrte = copyObject(rte);
1492 childrte->relid = childOID;
1493 childrte->relkind = newrelation->rd_rel->relkind;
1494 childrte->inh = false;
1495 childrte->requiredPerms = 0;
1496 parse->rtable = lappend(parse->rtable, childrte);
1497 childRTindex = list_length(parse->rtable);
1498
1499 /*
1500 * Build an AppendRelInfo for this parent and child.
1501 */
1502 appinfo = makeNode(AppendRelInfo);
1503 appinfo->parent_relid = rti;
1504 appinfo->child_relid = childRTindex;
1505 appinfo->parent_reltype = oldrelation->rd_rel->reltype;
1506 appinfo->child_reltype = newrelation->rd_rel->reltype;
1507 make_inh_translation_list(oldrelation, newrelation, childRTindex,
1508 &appinfo->translated_vars);
1509 appinfo->parent_reloid = parentOID;
1510 appinfos = lappend(appinfos, appinfo);
1511
1512 /*
1513 * Translate the column permissions bitmaps to the child's attnums (we
1514 * have to build the translated_vars list before we can do this). But
1515 * if this is the parent table, leave copyObject's result alone.
1516 *
1517 * Note: we need to do this even though the executor won't run any
1518 * permissions checks on the child RTE. The insertedCols/updatedCols
1519 * bitmaps may be examined for trigger-firing purposes.
1520 */
1521 if (childOID != parentOID)
1522 {
1523 childrte->selectedCols = translate_col_privs(rte->selectedCols,
1524 appinfo->translated_vars);
1525 childrte->insertedCols = translate_col_privs(rte->insertedCols,
1526 appinfo->translated_vars);
1527 childrte->updatedCols = translate_col_privs(rte->updatedCols,
1528 appinfo->translated_vars);
1529 }
1530
1531 /*
1532 * Build a PlanRowMark if parent is marked FOR UPDATE/SHARE.
1533 */
1534 if (oldrc)
1535 {
1536 PlanRowMark *newrc = makeNode(PlanRowMark);
1537
1538 newrc->rti = childRTindex;
1539 newrc->prti = rti;
1540 newrc->rowmarkId = oldrc->rowmarkId;
1541 /* Reselect rowmark type, because relkind might not match parent */
1542 newrc->markType = select_rowmark_type(childrte, oldrc->strength);
1543 newrc->allMarkTypes = (1 << newrc->markType);
1544 newrc->strength = oldrc->strength;
1545 newrc->waitPolicy = oldrc->waitPolicy;
1546 newrc->isParent = false;
1547
1548 /* Include child's rowmark type in parent's allMarkTypes */
1549 oldrc->allMarkTypes |= newrc->allMarkTypes;
1550
1551 root->rowMarks = lappend(root->rowMarks, newrc);
1552 }
1553
1554 /* Close child relations, but keep locks */
1555 if (childOID != parentOID)
1556 heap_close(newrelation, NoLock);
1557 }
1558
1559 heap_close(oldrelation, NoLock);
1560
1561 /*
1562 * If all the children were temp tables, pretend it's a non-inheritance
1563 * situation. The duplicate RTE we added for the parent table is
1564 * harmless, so we don't bother to get rid of it.
1565 */
1566 if (list_length(appinfos) < 2)
1567 {
1568 /* Clear flag before returning */
1569 rte->inh = false;
1570 return;
1571 }
1572
1573 /* Otherwise, OK to add to root->append_rel_list */
1574 root->append_rel_list = list_concat(root->append_rel_list, appinfos);
1575 }
1576
1577 /*
1578 * make_inh_translation_list
1579 * Build the list of translations from parent Vars to child Vars for
1580 * an inheritance child.
1581 *
1582 * For paranoia's sake, we match type/collation as well as attribute name.
1583 */
1584 static void
make_inh_translation_list(Relation oldrelation,Relation newrelation,Index newvarno,List ** translated_vars)1585 make_inh_translation_list(Relation oldrelation, Relation newrelation,
1586 Index newvarno,
1587 List **translated_vars)
1588 {
1589 List *vars = NIL;
1590 TupleDesc old_tupdesc = RelationGetDescr(oldrelation);
1591 TupleDesc new_tupdesc = RelationGetDescr(newrelation);
1592 int oldnatts = old_tupdesc->natts;
1593 int newnatts = new_tupdesc->natts;
1594 int old_attno;
1595
1596 for (old_attno = 0; old_attno < oldnatts; old_attno++)
1597 {
1598 Form_pg_attribute att;
1599 char *attname;
1600 Oid atttypid;
1601 int32 atttypmod;
1602 Oid attcollation;
1603 int new_attno;
1604
1605 att = old_tupdesc->attrs[old_attno];
1606 if (att->attisdropped)
1607 {
1608 /* Just put NULL into this list entry */
1609 vars = lappend(vars, NULL);
1610 continue;
1611 }
1612 attname = NameStr(att->attname);
1613 atttypid = att->atttypid;
1614 atttypmod = att->atttypmod;
1615 attcollation = att->attcollation;
1616
1617 /*
1618 * When we are generating the "translation list" for the parent table
1619 * of an inheritance set, no need to search for matches.
1620 */
1621 if (oldrelation == newrelation)
1622 {
1623 vars = lappend(vars, makeVar(newvarno,
1624 (AttrNumber) (old_attno + 1),
1625 atttypid,
1626 atttypmod,
1627 attcollation,
1628 0));
1629 continue;
1630 }
1631
1632 /*
1633 * Otherwise we have to search for the matching column by name.
1634 * There's no guarantee it'll have the same column position, because
1635 * of cases like ALTER TABLE ADD COLUMN and multiple inheritance.
1636 * However, in simple cases it will be the same column number, so try
1637 * that before we go groveling through all the columns.
1638 *
1639 * Note: the test for (att = ...) != NULL cannot fail, it's just a
1640 * notational device to include the assignment into the if-clause.
1641 */
1642 if (old_attno < newnatts &&
1643 (att = new_tupdesc->attrs[old_attno]) != NULL &&
1644 !att->attisdropped &&
1645 strcmp(attname, NameStr(att->attname)) == 0)
1646 new_attno = old_attno;
1647 else
1648 {
1649 for (new_attno = 0; new_attno < newnatts; new_attno++)
1650 {
1651 att = new_tupdesc->attrs[new_attno];
1652 if (!att->attisdropped &&
1653 strcmp(attname, NameStr(att->attname)) == 0)
1654 break;
1655 }
1656 if (new_attno >= newnatts)
1657 elog(ERROR, "could not find inherited attribute \"%s\" of relation \"%s\"",
1658 attname, RelationGetRelationName(newrelation));
1659 }
1660
1661 /* Found it, check type and collation match */
1662 if (atttypid != att->atttypid || atttypmod != att->atttypmod)
1663 elog(ERROR, "attribute \"%s\" of relation \"%s\" does not match parent's type",
1664 attname, RelationGetRelationName(newrelation));
1665 if (attcollation != att->attcollation)
1666 elog(ERROR, "attribute \"%s\" of relation \"%s\" does not match parent's collation",
1667 attname, RelationGetRelationName(newrelation));
1668
1669 vars = lappend(vars, makeVar(newvarno,
1670 (AttrNumber) (new_attno + 1),
1671 atttypid,
1672 atttypmod,
1673 attcollation,
1674 0));
1675 }
1676
1677 *translated_vars = vars;
1678 }
1679
1680 /*
1681 * translate_col_privs
1682 * Translate a bitmapset representing per-column privileges from the
1683 * parent rel's attribute numbering to the child's.
1684 *
1685 * The only surprise here is that we don't translate a parent whole-row
1686 * reference into a child whole-row reference. That would mean requiring
1687 * permissions on all child columns, which is overly strict, since the
1688 * query is really only going to reference the inherited columns. Instead
1689 * we set the per-column bits for all inherited columns.
1690 */
1691 static Bitmapset *
translate_col_privs(const Bitmapset * parent_privs,List * translated_vars)1692 translate_col_privs(const Bitmapset *parent_privs,
1693 List *translated_vars)
1694 {
1695 Bitmapset *child_privs = NULL;
1696 bool whole_row;
1697 int attno;
1698 ListCell *lc;
1699
1700 /* System attributes have the same numbers in all tables */
1701 for (attno = FirstLowInvalidHeapAttributeNumber + 1; attno < 0; attno++)
1702 {
1703 if (bms_is_member(attno - FirstLowInvalidHeapAttributeNumber,
1704 parent_privs))
1705 child_privs = bms_add_member(child_privs,
1706 attno - FirstLowInvalidHeapAttributeNumber);
1707 }
1708
1709 /* Check if parent has whole-row reference */
1710 whole_row = bms_is_member(InvalidAttrNumber - FirstLowInvalidHeapAttributeNumber,
1711 parent_privs);
1712
1713 /* And now translate the regular user attributes, using the vars list */
1714 attno = InvalidAttrNumber;
1715 foreach(lc, translated_vars)
1716 {
1717 Var *var = (Var *) lfirst(lc);
1718
1719 attno++;
1720 if (var == NULL) /* ignore dropped columns */
1721 continue;
1722 Assert(IsA(var, Var));
1723 if (whole_row ||
1724 bms_is_member(attno - FirstLowInvalidHeapAttributeNumber,
1725 parent_privs))
1726 child_privs = bms_add_member(child_privs,
1727 var->varattno - FirstLowInvalidHeapAttributeNumber);
1728 }
1729
1730 return child_privs;
1731 }
1732
1733 /*
1734 * adjust_appendrel_attrs
1735 * Copy the specified query or expression and translate Vars referring
1736 * to the parent rel of the specified AppendRelInfo to refer to the
1737 * child rel instead. We also update rtindexes appearing outside Vars,
1738 * such as resultRelation and jointree relids.
1739 *
1740 * Note: this is applied after conversion of sublinks to subplans in the
1741 * query jointree, but there may still be sublinks in the security barrier
1742 * quals of RTEs, so we do need to cope with recursion into sub-queries.
1743 *
1744 * Note: this is not hugely different from what pullup_replace_vars() does;
1745 * maybe we should try to fold the two routines together.
1746 */
1747 Node *
adjust_appendrel_attrs(PlannerInfo * root,Node * node,AppendRelInfo * appinfo)1748 adjust_appendrel_attrs(PlannerInfo *root, Node *node, AppendRelInfo *appinfo)
1749 {
1750 Node *result;
1751 adjust_appendrel_attrs_context context;
1752
1753 context.root = root;
1754 context.appinfo = appinfo;
1755 context.sublevels_up = 0;
1756
1757 /*
1758 * Must be prepared to start with a Query or a bare expression tree; if
1759 * it's a Query, go straight to query_tree_walker to make sure that
1760 * sublevels_up doesn't get incremented prematurely.
1761 */
1762 if (node && IsA(node, Query))
1763 {
1764 Query *newnode;
1765
1766 newnode = query_tree_mutator((Query *) node,
1767 adjust_appendrel_attrs_mutator,
1768 (void *) &context,
1769 QTW_IGNORE_RC_SUBQUERIES);
1770 if (newnode->resultRelation == appinfo->parent_relid)
1771 {
1772 newnode->resultRelation = appinfo->child_relid;
1773 /* Fix tlist resnos too, if it's inherited UPDATE */
1774 if (newnode->commandType == CMD_UPDATE)
1775 newnode->targetList =
1776 adjust_inherited_tlist(newnode->targetList,
1777 appinfo);
1778 }
1779 result = (Node *) newnode;
1780 }
1781 else
1782 result = adjust_appendrel_attrs_mutator(node, &context);
1783
1784 return result;
1785 }
1786
1787 static Node *
adjust_appendrel_attrs_mutator(Node * node,adjust_appendrel_attrs_context * context)1788 adjust_appendrel_attrs_mutator(Node *node,
1789 adjust_appendrel_attrs_context *context)
1790 {
1791 AppendRelInfo *appinfo = context->appinfo;
1792
1793 if (node == NULL)
1794 return NULL;
1795 if (IsA(node, Var))
1796 {
1797 Var *var = (Var *) copyObject(node);
1798
1799 if (var->varlevelsup == context->sublevels_up &&
1800 var->varno == appinfo->parent_relid)
1801 {
1802 var->varno = appinfo->child_relid;
1803 var->varnoold = appinfo->child_relid;
1804 if (var->varattno > 0)
1805 {
1806 Node *newnode;
1807
1808 if (var->varattno > list_length(appinfo->translated_vars))
1809 elog(ERROR, "attribute %d of relation \"%s\" does not exist",
1810 var->varattno, get_rel_name(appinfo->parent_reloid));
1811 newnode = copyObject(list_nth(appinfo->translated_vars,
1812 var->varattno - 1));
1813 if (newnode == NULL)
1814 elog(ERROR, "attribute %d of relation \"%s\" does not exist",
1815 var->varattno, get_rel_name(appinfo->parent_reloid));
1816 ((Var *) newnode)->varlevelsup += context->sublevels_up;
1817 return newnode;
1818 }
1819 else if (var->varattno == 0)
1820 {
1821 /*
1822 * Whole-row Var: if we are dealing with named rowtypes, we
1823 * can use a whole-row Var for the child table plus a coercion
1824 * step to convert the tuple layout to the parent's rowtype.
1825 * Otherwise we have to generate a RowExpr.
1826 */
1827 if (OidIsValid(appinfo->child_reltype))
1828 {
1829 Assert(var->vartype == appinfo->parent_reltype);
1830 if (appinfo->parent_reltype != appinfo->child_reltype)
1831 {
1832 ConvertRowtypeExpr *r = makeNode(ConvertRowtypeExpr);
1833
1834 r->arg = (Expr *) var;
1835 r->resulttype = appinfo->parent_reltype;
1836 r->convertformat = COERCE_IMPLICIT_CAST;
1837 r->location = -1;
1838 /* Make sure the Var node has the right type ID, too */
1839 var->vartype = appinfo->child_reltype;
1840 return (Node *) r;
1841 }
1842 }
1843 else
1844 {
1845 /*
1846 * Build a RowExpr containing the translated variables.
1847 *
1848 * In practice var->vartype will always be RECORDOID here,
1849 * so we need to come up with some suitable column names.
1850 * We use the parent RTE's column names.
1851 *
1852 * Note: we can't get here for inheritance cases, so there
1853 * is no need to worry that translated_vars might contain
1854 * some dummy NULLs.
1855 */
1856 RowExpr *rowexpr;
1857 List *fields;
1858 RangeTblEntry *rte;
1859 ListCell *lc;
1860
1861 rte = rt_fetch(appinfo->parent_relid,
1862 context->root->parse->rtable);
1863 fields = (List *) copyObject(appinfo->translated_vars);
1864 foreach(lc, fields)
1865 {
1866 Var *field = (Var *) lfirst(lc);
1867
1868 field->varlevelsup += context->sublevels_up;
1869 }
1870 rowexpr = makeNode(RowExpr);
1871 rowexpr->args = fields;
1872 rowexpr->row_typeid = var->vartype;
1873 rowexpr->row_format = COERCE_IMPLICIT_CAST;
1874 rowexpr->colnames = copyObject(rte->eref->colnames);
1875 rowexpr->location = -1;
1876
1877 return (Node *) rowexpr;
1878 }
1879 }
1880 /* system attributes don't need any other translation */
1881 }
1882 return (Node *) var;
1883 }
1884 if (IsA(node, CurrentOfExpr))
1885 {
1886 CurrentOfExpr *cexpr = (CurrentOfExpr *) copyObject(node);
1887
1888 if (context->sublevels_up == 0 &&
1889 cexpr->cvarno == appinfo->parent_relid)
1890 cexpr->cvarno = appinfo->child_relid;
1891 return (Node *) cexpr;
1892 }
1893 if (IsA(node, RangeTblRef))
1894 {
1895 RangeTblRef *rtr = (RangeTblRef *) copyObject(node);
1896
1897 if (context->sublevels_up == 0 &&
1898 rtr->rtindex == appinfo->parent_relid)
1899 rtr->rtindex = appinfo->child_relid;
1900 return (Node *) rtr;
1901 }
1902 if (IsA(node, JoinExpr))
1903 {
1904 /* Copy the JoinExpr node with correct mutation of subnodes */
1905 JoinExpr *j;
1906
1907 j = (JoinExpr *) expression_tree_mutator(node,
1908 adjust_appendrel_attrs_mutator,
1909 (void *) context);
1910 /* now fix JoinExpr's rtindex (probably never happens) */
1911 if (context->sublevels_up == 0 &&
1912 j->rtindex == appinfo->parent_relid)
1913 j->rtindex = appinfo->child_relid;
1914 return (Node *) j;
1915 }
1916 if (IsA(node, PlaceHolderVar))
1917 {
1918 /* Copy the PlaceHolderVar node with correct mutation of subnodes */
1919 PlaceHolderVar *phv;
1920
1921 phv = (PlaceHolderVar *) expression_tree_mutator(node,
1922 adjust_appendrel_attrs_mutator,
1923 (void *) context);
1924 /* now fix PlaceHolderVar's relid sets */
1925 if (phv->phlevelsup == context->sublevels_up)
1926 phv->phrels = adjust_relid_set(phv->phrels,
1927 appinfo->parent_relid,
1928 appinfo->child_relid);
1929 return (Node *) phv;
1930 }
1931 /* Shouldn't need to handle planner auxiliary nodes here */
1932 Assert(!IsA(node, SpecialJoinInfo));
1933 Assert(!IsA(node, AppendRelInfo));
1934 Assert(!IsA(node, PlaceHolderInfo));
1935 Assert(!IsA(node, MinMaxAggInfo));
1936
1937 /*
1938 * We have to process RestrictInfo nodes specially. (Note: although
1939 * set_append_rel_pathlist will hide RestrictInfos in the parent's
1940 * baserestrictinfo list from us, it doesn't hide those in joininfo.)
1941 */
1942 if (IsA(node, RestrictInfo))
1943 {
1944 RestrictInfo *oldinfo = (RestrictInfo *) node;
1945 RestrictInfo *newinfo = makeNode(RestrictInfo);
1946
1947 /* Copy all flat-copiable fields */
1948 memcpy(newinfo, oldinfo, sizeof(RestrictInfo));
1949
1950 /* Recursively fix the clause itself */
1951 newinfo->clause = (Expr *)
1952 adjust_appendrel_attrs_mutator((Node *) oldinfo->clause, context);
1953
1954 /* and the modified version, if an OR clause */
1955 newinfo->orclause = (Expr *)
1956 adjust_appendrel_attrs_mutator((Node *) oldinfo->orclause, context);
1957
1958 /* adjust relid sets too */
1959 newinfo->clause_relids = adjust_relid_set(oldinfo->clause_relids,
1960 appinfo->parent_relid,
1961 appinfo->child_relid);
1962 newinfo->required_relids = adjust_relid_set(oldinfo->required_relids,
1963 appinfo->parent_relid,
1964 appinfo->child_relid);
1965 newinfo->outer_relids = adjust_relid_set(oldinfo->outer_relids,
1966 appinfo->parent_relid,
1967 appinfo->child_relid);
1968 newinfo->nullable_relids = adjust_relid_set(oldinfo->nullable_relids,
1969 appinfo->parent_relid,
1970 appinfo->child_relid);
1971 newinfo->left_relids = adjust_relid_set(oldinfo->left_relids,
1972 appinfo->parent_relid,
1973 appinfo->child_relid);
1974 newinfo->right_relids = adjust_relid_set(oldinfo->right_relids,
1975 appinfo->parent_relid,
1976 appinfo->child_relid);
1977
1978 /*
1979 * Reset cached derivative fields, since these might need to have
1980 * different values when considering the child relation. Note we
1981 * don't reset left_ec/right_ec: each child variable is implicitly
1982 * equivalent to its parent, so still a member of the same EC if any.
1983 */
1984 newinfo->eval_cost.startup = -1;
1985 newinfo->norm_selec = -1;
1986 newinfo->outer_selec = -1;
1987 newinfo->left_em = NULL;
1988 newinfo->right_em = NULL;
1989 newinfo->scansel_cache = NIL;
1990 newinfo->left_bucketsize = -1;
1991 newinfo->right_bucketsize = -1;
1992
1993 return (Node *) newinfo;
1994 }
1995
1996 if (IsA(node, Query))
1997 {
1998 /*
1999 * Recurse into sublink subqueries. This should only be possible in
2000 * security barrier quals of top-level RTEs. All other sublinks should
2001 * have already been converted to subplans during expression
2002 * preprocessing, but this doesn't happen for security barrier quals,
2003 * since they are destined to become quals of a subquery RTE, which
2004 * will be recursively planned, and so should not be preprocessed at
2005 * this stage.
2006 *
2007 * We don't explicitly Assert() for securityQuals here simply because
2008 * it's not trivial to do so.
2009 */
2010 Query *newnode;
2011
2012 context->sublevels_up++;
2013 newnode = query_tree_mutator((Query *) node,
2014 adjust_appendrel_attrs_mutator,
2015 (void *) context, 0);
2016 context->sublevels_up--;
2017 return (Node *) newnode;
2018 }
2019
2020 return expression_tree_mutator(node, adjust_appendrel_attrs_mutator,
2021 (void *) context);
2022 }
2023
2024 /*
2025 * Substitute newrelid for oldrelid in a Relid set
2026 */
2027 static Relids
adjust_relid_set(Relids relids,Index oldrelid,Index newrelid)2028 adjust_relid_set(Relids relids, Index oldrelid, Index newrelid)
2029 {
2030 if (bms_is_member(oldrelid, relids))
2031 {
2032 /* Ensure we have a modifiable copy */
2033 relids = bms_copy(relids);
2034 /* Remove old, add new */
2035 relids = bms_del_member(relids, oldrelid);
2036 relids = bms_add_member(relids, newrelid);
2037 }
2038 return relids;
2039 }
2040
2041 /*
2042 * Adjust the targetlist entries of an inherited UPDATE operation
2043 *
2044 * The expressions have already been fixed, but we have to make sure that
2045 * the target resnos match the child table (they may not, in the case of
2046 * a column that was added after-the-fact by ALTER TABLE). In some cases
2047 * this can force us to re-order the tlist to preserve resno ordering.
2048 * (We do all this work in special cases so that preptlist.c is fast for
2049 * the typical case.)
2050 *
2051 * The given tlist has already been through expression_tree_mutator;
2052 * therefore the TargetEntry nodes are fresh copies that it's okay to
2053 * scribble on.
2054 *
2055 * Note that this is not needed for INSERT because INSERT isn't inheritable.
2056 */
2057 static List *
adjust_inherited_tlist(List * tlist,AppendRelInfo * context)2058 adjust_inherited_tlist(List *tlist, AppendRelInfo *context)
2059 {
2060 bool changed_it = false;
2061 ListCell *tl;
2062 List *new_tlist;
2063 bool more;
2064 int attrno;
2065
2066 /* This should only happen for an inheritance case, not UNION ALL */
2067 Assert(OidIsValid(context->parent_reloid));
2068
2069 /* Scan tlist and update resnos to match attnums of child rel */
2070 foreach(tl, tlist)
2071 {
2072 TargetEntry *tle = (TargetEntry *) lfirst(tl);
2073 Var *childvar;
2074
2075 if (tle->resjunk)
2076 continue; /* ignore junk items */
2077
2078 /* Look up the translation of this column: it must be a Var */
2079 if (tle->resno <= 0 ||
2080 tle->resno > list_length(context->translated_vars))
2081 elog(ERROR, "attribute %d of relation \"%s\" does not exist",
2082 tle->resno, get_rel_name(context->parent_reloid));
2083 childvar = (Var *) list_nth(context->translated_vars, tle->resno - 1);
2084 if (childvar == NULL || !IsA(childvar, Var))
2085 elog(ERROR, "attribute %d of relation \"%s\" does not exist",
2086 tle->resno, get_rel_name(context->parent_reloid));
2087
2088 if (tle->resno != childvar->varattno)
2089 {
2090 tle->resno = childvar->varattno;
2091 changed_it = true;
2092 }
2093 }
2094
2095 /*
2096 * If we changed anything, re-sort the tlist by resno, and make sure
2097 * resjunk entries have resnos above the last real resno. The sort
2098 * algorithm is a bit stupid, but for such a seldom-taken path, small is
2099 * probably better than fast.
2100 */
2101 if (!changed_it)
2102 return tlist;
2103
2104 new_tlist = NIL;
2105 more = true;
2106 for (attrno = 1; more; attrno++)
2107 {
2108 more = false;
2109 foreach(tl, tlist)
2110 {
2111 TargetEntry *tle = (TargetEntry *) lfirst(tl);
2112
2113 if (tle->resjunk)
2114 continue; /* ignore junk items */
2115
2116 if (tle->resno == attrno)
2117 new_tlist = lappend(new_tlist, tle);
2118 else if (tle->resno > attrno)
2119 more = true;
2120 }
2121 }
2122
2123 foreach(tl, tlist)
2124 {
2125 TargetEntry *tle = (TargetEntry *) lfirst(tl);
2126
2127 if (!tle->resjunk)
2128 continue; /* here, ignore non-junk items */
2129
2130 tle->resno = attrno;
2131 new_tlist = lappend(new_tlist, tle);
2132 attrno++;
2133 }
2134
2135 return new_tlist;
2136 }
2137
2138 /*
2139 * adjust_appendrel_attrs_multilevel
2140 * Apply Var translations from a toplevel appendrel parent down to a child.
2141 *
2142 * In some cases we need to translate expressions referencing a baserel
2143 * to reference an appendrel child that's multiple levels removed from it.
2144 */
2145 Node *
adjust_appendrel_attrs_multilevel(PlannerInfo * root,Node * node,RelOptInfo * child_rel)2146 adjust_appendrel_attrs_multilevel(PlannerInfo *root, Node *node,
2147 RelOptInfo *child_rel)
2148 {
2149 AppendRelInfo *appinfo = find_childrel_appendrelinfo(root, child_rel);
2150 RelOptInfo *parent_rel = find_base_rel(root, appinfo->parent_relid);
2151
2152 /* If parent is also a child, first recurse to apply its translations */
2153 if (parent_rel->reloptkind == RELOPT_OTHER_MEMBER_REL)
2154 node = adjust_appendrel_attrs_multilevel(root, node, parent_rel);
2155 else
2156 Assert(parent_rel->reloptkind == RELOPT_BASEREL);
2157 /* Now translate for this child */
2158 return adjust_appendrel_attrs(root, node, appinfo);
2159 }
2160