1 | /*------------------------------------------------------------------------- |
2 | * |
3 | * inherit.c |
4 | * Routines to process child relations in inheritance trees |
5 | * |
6 | * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group |
7 | * Portions Copyright (c) 1994, Regents of the University of California |
8 | * |
9 | * |
10 | * IDENTIFICATION |
11 | * src/backend/optimizer/path/inherit.c |
12 | * |
13 | *------------------------------------------------------------------------- |
14 | */ |
15 | #include "postgres.h" |
16 | |
17 | #include "access/sysattr.h" |
18 | #include "access/table.h" |
19 | #include "catalog/partition.h" |
20 | #include "catalog/pg_inherits.h" |
21 | #include "catalog/pg_type.h" |
22 | #include "miscadmin.h" |
23 | #include "nodes/makefuncs.h" |
24 | #include "optimizer/appendinfo.h" |
25 | #include "optimizer/inherit.h" |
26 | #include "optimizer/optimizer.h" |
27 | #include "optimizer/pathnode.h" |
28 | #include "optimizer/planmain.h" |
29 | #include "optimizer/planner.h" |
30 | #include "optimizer/prep.h" |
31 | #include "optimizer/restrictinfo.h" |
32 | #include "parser/parsetree.h" |
33 | #include "partitioning/partdesc.h" |
34 | #include "partitioning/partprune.h" |
35 | #include "utils/rel.h" |
36 | |
37 | |
38 | static void expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, |
39 | RangeTblEntry *parentrte, |
40 | Index parentRTindex, Relation parentrel, |
41 | PlanRowMark *top_parentrc, LOCKMODE lockmode); |
42 | static void expand_single_inheritance_child(PlannerInfo *root, |
43 | RangeTblEntry *parentrte, |
44 | Index parentRTindex, Relation parentrel, |
45 | PlanRowMark *top_parentrc, Relation childrel, |
46 | RangeTblEntry **childrte_p, |
47 | Index *childRTindex_p); |
48 | static Bitmapset *translate_col_privs(const Bitmapset *parent_privs, |
49 | List *translated_vars); |
50 | static void expand_appendrel_subquery(PlannerInfo *root, RelOptInfo *rel, |
51 | RangeTblEntry *rte, Index rti); |
52 | |
53 | |
54 | /* |
55 | * expand_inherited_rtentry |
56 | * Expand a rangetable entry that has the "inh" bit set. |
57 | * |
58 | * "inh" is only allowed in two cases: RELATION and SUBQUERY RTEs. |
59 | * |
60 | * "inh" on a plain RELATION RTE means that it is a partitioned table or the |
61 | * parent of a traditional-inheritance set. In this case we must add entries |
62 | * for all the interesting child tables to the query's rangetable, and build |
63 | * additional planner data structures for them, including RelOptInfos, |
64 | * AppendRelInfos, and possibly PlanRowMarks. |
65 | * |
66 | * Note that the original RTE is considered to represent the whole inheritance |
67 | * set. In the case of traditional inheritance, the first of the generated |
68 | * RTEs is an RTE for the same table, but with inh = false, to represent the |
69 | * parent table in its role as a simple member of the inheritance set. For |
70 | * partitioning, we don't need a second RTE because the partitioned table |
71 | * itself has no data and need not be scanned. |
72 | * |
73 | * "inh" on a SUBQUERY RTE means that it's the parent of a UNION ALL group, |
74 | * which is treated as an appendrel similarly to inheritance cases; however, |
75 | * we already made RTEs and AppendRelInfos for the subqueries. We only need |
76 | * to build RelOptInfos for them, which is done by expand_appendrel_subquery. |
77 | */ |
78 | void |
79 | expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, |
80 | RangeTblEntry *rte, Index rti) |
81 | { |
82 | Oid parentOID; |
83 | Relation oldrelation; |
84 | LOCKMODE lockmode; |
85 | PlanRowMark *oldrc; |
86 | bool old_isParent = false; |
87 | int old_allMarkTypes = 0; |
88 | |
89 | Assert(rte->inh); /* else caller error */ |
90 | |
91 | if (rte->rtekind == RTE_SUBQUERY) |
92 | { |
93 | expand_appendrel_subquery(root, rel, rte, rti); |
94 | return; |
95 | } |
96 | |
97 | Assert(rte->rtekind == RTE_RELATION); |
98 | |
99 | parentOID = rte->relid; |
100 | |
101 | /* |
102 | * We used to check has_subclass() here, but there's no longer any need |
103 | * to, because subquery_planner already did. |
104 | */ |
105 | |
106 | /* |
107 | * The rewriter should already have obtained an appropriate lock on each |
108 | * relation named in the query, so we can open the parent relation without |
109 | * locking it. However, for each child relation we add to the query, we |
110 | * must obtain an appropriate lock, because this will be the first use of |
111 | * those relations in the parse/rewrite/plan pipeline. Child rels should |
112 | * use the same lockmode as their parent. |
113 | */ |
114 | oldrelation = table_open(parentOID, NoLock); |
115 | lockmode = rte->rellockmode; |
116 | |
117 | /* |
118 | * If parent relation is selected FOR UPDATE/SHARE, we need to mark its |
119 | * PlanRowMark as isParent = true, and generate a new PlanRowMark for each |
120 | * child. |
121 | */ |
122 | oldrc = get_plan_rowmark(root->rowMarks, rti); |
123 | if (oldrc) |
124 | { |
125 | old_isParent = oldrc->isParent; |
126 | oldrc->isParent = true; |
127 | /* Save initial value of allMarkTypes before children add to it */ |
128 | old_allMarkTypes = oldrc->allMarkTypes; |
129 | } |
130 | |
131 | /* Scan the inheritance set and expand it */ |
132 | if (oldrelation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) |
133 | { |
134 | /* |
135 | * Partitioned table, so set up for partitioning. |
136 | */ |
137 | Assert(rte->relkind == RELKIND_PARTITIONED_TABLE); |
138 | |
139 | /* |
140 | * Recursively expand and lock the partitions. While at it, also |
141 | * extract the partition key columns of all the partitioned tables. |
142 | */ |
143 | expand_partitioned_rtentry(root, rel, rte, rti, |
144 | oldrelation, oldrc, lockmode); |
145 | } |
146 | else |
147 | { |
148 | /* |
149 | * Ordinary table, so process traditional-inheritance children. (Note |
150 | * that partitioned tables are not allowed to have inheritance |
151 | * children, so it's not possible for both cases to apply.) |
152 | */ |
153 | List *inhOIDs; |
154 | ListCell *l; |
155 | |
156 | /* Scan for all members of inheritance set, acquire needed locks */ |
157 | inhOIDs = find_all_inheritors(parentOID, lockmode, NULL); |
158 | |
159 | /* |
160 | * We used to special-case the situation where the table no longer has |
161 | * any children, by clearing rte->inh and exiting. That no longer |
162 | * works, because this function doesn't get run until after decisions |
163 | * have been made that depend on rte->inh. We have to treat such |
164 | * situations as normal inheritance. The table itself should always |
165 | * have been found, though. |
166 | */ |
167 | Assert(inhOIDs != NIL); |
168 | Assert(linitial_oid(inhOIDs) == parentOID); |
169 | |
170 | /* Expand simple_rel_array and friends to hold child objects. */ |
171 | expand_planner_arrays(root, list_length(inhOIDs)); |
172 | |
173 | /* |
174 | * Expand inheritance children in the order the OIDs were returned by |
175 | * find_all_inheritors. |
176 | */ |
177 | foreach(l, inhOIDs) |
178 | { |
179 | Oid childOID = lfirst_oid(l); |
180 | Relation newrelation; |
181 | RangeTblEntry *childrte; |
182 | Index childRTindex; |
183 | |
184 | /* Open rel if needed; we already have required locks */ |
185 | if (childOID != parentOID) |
186 | newrelation = table_open(childOID, NoLock); |
187 | else |
188 | newrelation = oldrelation; |
189 | |
190 | /* |
191 | * It is possible that the parent table has children that are temp |
192 | * tables of other backends. We cannot safely access such tables |
193 | * (because of buffering issues), and the best thing to do seems |
194 | * to be to silently ignore them. |
195 | */ |
196 | if (childOID != parentOID && RELATION_IS_OTHER_TEMP(newrelation)) |
197 | { |
198 | table_close(newrelation, lockmode); |
199 | continue; |
200 | } |
201 | |
202 | /* Create RTE and AppendRelInfo, plus PlanRowMark if needed. */ |
203 | expand_single_inheritance_child(root, rte, rti, oldrelation, |
204 | oldrc, newrelation, |
205 | &childrte, &childRTindex); |
206 | |
207 | /* Create the otherrel RelOptInfo too. */ |
208 | (void) build_simple_rel(root, childRTindex, rel); |
209 | |
210 | /* Close child relations, but keep locks */ |
211 | if (childOID != parentOID) |
212 | table_close(newrelation, NoLock); |
213 | } |
214 | } |
215 | |
216 | /* |
217 | * Some children might require different mark types, which would've been |
218 | * reported into oldrc. If so, add relevant entries to the top-level |
219 | * targetlist and update parent rel's reltarget. This should match what |
220 | * preprocess_targetlist() would have added if the mark types had been |
221 | * requested originally. |
222 | */ |
223 | if (oldrc) |
224 | { |
225 | int new_allMarkTypes = oldrc->allMarkTypes; |
226 | Var *var; |
227 | TargetEntry *tle; |
228 | char resname[32]; |
229 | List *newvars = NIL; |
230 | |
231 | /* The old PlanRowMark should already have necessitated adding TID */ |
232 | Assert(old_allMarkTypes & ~(1 << ROW_MARK_COPY)); |
233 | |
234 | /* Add whole-row junk Var if needed, unless we had it already */ |
235 | if ((new_allMarkTypes & (1 << ROW_MARK_COPY)) && |
236 | !(old_allMarkTypes & (1 << ROW_MARK_COPY))) |
237 | { |
238 | var = makeWholeRowVar(planner_rt_fetch(oldrc->rti, root), |
239 | oldrc->rti, |
240 | 0, |
241 | false); |
242 | snprintf(resname, sizeof(resname), "wholerow%u" , oldrc->rowmarkId); |
243 | tle = makeTargetEntry((Expr *) var, |
244 | list_length(root->processed_tlist) + 1, |
245 | pstrdup(resname), |
246 | true); |
247 | root->processed_tlist = lappend(root->processed_tlist, tle); |
248 | newvars = lappend(newvars, var); |
249 | } |
250 | |
251 | /* Add tableoid junk Var, unless we had it already */ |
252 | if (!old_isParent) |
253 | { |
254 | var = makeVar(oldrc->rti, |
255 | TableOidAttributeNumber, |
256 | OIDOID, |
257 | -1, |
258 | InvalidOid, |
259 | 0); |
260 | snprintf(resname, sizeof(resname), "tableoid%u" , oldrc->rowmarkId); |
261 | tle = makeTargetEntry((Expr *) var, |
262 | list_length(root->processed_tlist) + 1, |
263 | pstrdup(resname), |
264 | true); |
265 | root->processed_tlist = lappend(root->processed_tlist, tle); |
266 | newvars = lappend(newvars, var); |
267 | } |
268 | |
269 | /* |
270 | * Add the newly added Vars to parent's reltarget. We needn't worry |
271 | * about the children's reltargets, they'll be made later. |
272 | */ |
273 | add_vars_to_targetlist(root, newvars, bms_make_singleton(0), false); |
274 | } |
275 | |
276 | table_close(oldrelation, NoLock); |
277 | } |
278 | |
279 | /* |
280 | * expand_partitioned_rtentry |
281 | * Recursively expand an RTE for a partitioned table. |
282 | */ |
283 | static void |
284 | expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, |
285 | RangeTblEntry *parentrte, |
286 | Index parentRTindex, Relation parentrel, |
287 | PlanRowMark *top_parentrc, LOCKMODE lockmode) |
288 | { |
289 | PartitionDesc partdesc; |
290 | Bitmapset *live_parts; |
291 | int num_live_parts; |
292 | int i; |
293 | |
294 | check_stack_depth(); |
295 | |
296 | Assert(parentrte->inh); |
297 | |
298 | partdesc = PartitionDirectoryLookup(root->glob->partition_directory, |
299 | parentrel); |
300 | |
301 | /* A partitioned table should always have a partition descriptor. */ |
302 | Assert(partdesc); |
303 | |
304 | /* |
305 | * Note down whether any partition key cols are being updated. Though it's |
306 | * the root partitioned table's updatedCols we are interested in, we |
307 | * instead use parentrte to get the updatedCols. This is convenient |
308 | * because parentrte already has the root partrel's updatedCols translated |
309 | * to match the attribute ordering of parentrel. |
310 | */ |
311 | if (!root->partColsUpdated) |
312 | root->partColsUpdated = |
313 | has_partition_attrs(parentrel, parentrte->updatedCols, NULL); |
314 | |
315 | /* |
316 | * There shouldn't be any generated columns in the partition key. |
317 | */ |
318 | Assert(!has_partition_attrs(parentrel, parentrte->extraUpdatedCols, NULL)); |
319 | |
320 | /* Nothing further to do here if there are no partitions. */ |
321 | if (partdesc->nparts == 0) |
322 | return; |
323 | |
324 | /* |
325 | * Perform partition pruning using restriction clauses assigned to parent |
326 | * relation. live_parts will contain PartitionDesc indexes of partitions |
327 | * that survive pruning. Below, we will initialize child objects for the |
328 | * surviving partitions. |
329 | */ |
330 | live_parts = prune_append_rel_partitions(relinfo); |
331 | |
332 | /* Expand simple_rel_array and friends to hold child objects. */ |
333 | num_live_parts = bms_num_members(live_parts); |
334 | if (num_live_parts > 0) |
335 | expand_planner_arrays(root, num_live_parts); |
336 | |
337 | /* |
338 | * We also store partition RelOptInfo pointers in the parent relation. |
339 | * Since we're palloc0'ing, slots corresponding to pruned partitions will |
340 | * contain NULL. |
341 | */ |
342 | Assert(relinfo->part_rels == NULL); |
343 | relinfo->part_rels = (RelOptInfo **) |
344 | palloc0(relinfo->nparts * sizeof(RelOptInfo *)); |
345 | |
346 | /* |
347 | * Create a child RTE for each live partition. Note that unlike |
348 | * traditional inheritance, we don't need a child RTE for the partitioned |
349 | * table itself, because it's not going to be scanned. |
350 | */ |
351 | i = -1; |
352 | while ((i = bms_next_member(live_parts, i)) >= 0) |
353 | { |
354 | Oid childOID = partdesc->oids[i]; |
355 | Relation childrel; |
356 | RangeTblEntry *childrte; |
357 | Index childRTindex; |
358 | RelOptInfo *childrelinfo; |
359 | |
360 | /* Open rel, acquiring required locks */ |
361 | childrel = table_open(childOID, lockmode); |
362 | |
363 | /* |
364 | * Temporary partitions belonging to other sessions should have been |
365 | * disallowed at definition, but for paranoia's sake, let's double |
366 | * check. |
367 | */ |
368 | if (RELATION_IS_OTHER_TEMP(childrel)) |
369 | elog(ERROR, "temporary relation from another session found as partition" ); |
370 | |
371 | /* Create RTE and AppendRelInfo, plus PlanRowMark if needed. */ |
372 | expand_single_inheritance_child(root, parentrte, parentRTindex, |
373 | parentrel, top_parentrc, childrel, |
374 | &childrte, &childRTindex); |
375 | |
376 | /* Create the otherrel RelOptInfo too. */ |
377 | childrelinfo = build_simple_rel(root, childRTindex, relinfo); |
378 | relinfo->part_rels[i] = childrelinfo; |
379 | |
380 | /* If this child is itself partitioned, recurse */ |
381 | if (childrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) |
382 | expand_partitioned_rtentry(root, childrelinfo, |
383 | childrte, childRTindex, |
384 | childrel, top_parentrc, lockmode); |
385 | |
386 | /* Close child relation, but keep locks */ |
387 | table_close(childrel, NoLock); |
388 | } |
389 | } |
390 | |
391 | /* |
392 | * expand_single_inheritance_child |
393 | * Build a RangeTblEntry and an AppendRelInfo, plus maybe a PlanRowMark. |
394 | * |
395 | * We now expand the partition hierarchy level by level, creating a |
396 | * corresponding hierarchy of AppendRelInfos and RelOptInfos, where each |
397 | * partitioned descendant acts as a parent of its immediate partitions. |
398 | * (This is a difference from what older versions of PostgreSQL did and what |
399 | * is still done in the case of table inheritance for unpartitioned tables, |
400 | * where the hierarchy is flattened during RTE expansion.) |
401 | * |
402 | * PlanRowMarks still carry the top-parent's RTI, and the top-parent's |
403 | * allMarkTypes field still accumulates values from all descendents. |
404 | * |
405 | * "parentrte" and "parentRTindex" are immediate parent's RTE and |
406 | * RTI. "top_parentrc" is top parent's PlanRowMark. |
407 | * |
408 | * The child RangeTblEntry and its RTI are returned in "childrte_p" and |
409 | * "childRTindex_p" resp. |
410 | */ |
411 | static void |
412 | expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, |
413 | Index parentRTindex, Relation parentrel, |
414 | PlanRowMark *top_parentrc, Relation childrel, |
415 | RangeTblEntry **childrte_p, |
416 | Index *childRTindex_p) |
417 | { |
418 | Query *parse = root->parse; |
419 | Oid parentOID = RelationGetRelid(parentrel); |
420 | Oid childOID = RelationGetRelid(childrel); |
421 | RangeTblEntry *childrte; |
422 | Index childRTindex; |
423 | AppendRelInfo *appinfo; |
424 | |
425 | /* |
426 | * Build an RTE for the child, and attach to query's rangetable list. We |
427 | * copy most fields of the parent's RTE, but replace relation OID, |
428 | * relkind, and inh for the child. Also, set requiredPerms to zero since |
429 | * all required permissions checks are done on the original RTE. Likewise, |
430 | * set the child's securityQuals to empty, because we only want to apply |
431 | * the parent's RLS conditions regardless of what RLS properties |
432 | * individual children may have. (This is an intentional choice to make |
433 | * inherited RLS work like regular permissions checks.) The parent |
434 | * securityQuals will be propagated to children along with other base |
435 | * restriction clauses, so we don't need to do it here. |
436 | */ |
437 | childrte = copyObject(parentrte); |
438 | *childrte_p = childrte; |
439 | childrte->relid = childOID; |
440 | childrte->relkind = childrel->rd_rel->relkind; |
441 | /* A partitioned child will need to be expanded further. */ |
442 | if (childrte->relkind == RELKIND_PARTITIONED_TABLE) |
443 | { |
444 | Assert(childOID != parentOID); |
445 | childrte->inh = true; |
446 | } |
447 | else |
448 | childrte->inh = false; |
449 | childrte->requiredPerms = 0; |
450 | childrte->securityQuals = NIL; |
451 | parse->rtable = lappend(parse->rtable, childrte); |
452 | childRTindex = list_length(parse->rtable); |
453 | *childRTindex_p = childRTindex; |
454 | |
455 | /* |
456 | * Build an AppendRelInfo struct for each parent/child pair. |
457 | */ |
458 | appinfo = make_append_rel_info(parentrel, childrel, |
459 | parentRTindex, childRTindex); |
460 | root->append_rel_list = lappend(root->append_rel_list, appinfo); |
461 | |
462 | /* |
463 | * Translate the column permissions bitmaps to the child's attnums (we |
464 | * have to build the translated_vars list before we can do this). But if |
465 | * this is the parent table, we can leave copyObject's result alone. |
466 | * |
467 | * Note: we need to do this even though the executor won't run any |
468 | * permissions checks on the child RTE. The insertedCols/updatedCols |
469 | * bitmaps may be examined for trigger-firing purposes. |
470 | */ |
471 | if (childOID != parentOID) |
472 | { |
473 | childrte->selectedCols = translate_col_privs(parentrte->selectedCols, |
474 | appinfo->translated_vars); |
475 | childrte->insertedCols = translate_col_privs(parentrte->insertedCols, |
476 | appinfo->translated_vars); |
477 | childrte->updatedCols = translate_col_privs(parentrte->updatedCols, |
478 | appinfo->translated_vars); |
479 | childrte->extraUpdatedCols = translate_col_privs(parentrte->extraUpdatedCols, |
480 | appinfo->translated_vars); |
481 | } |
482 | |
483 | /* |
484 | * Store the RTE and appinfo in the respective PlannerInfo arrays, which |
485 | * the caller must already have allocated space for. |
486 | */ |
487 | Assert(childRTindex < root->simple_rel_array_size); |
488 | Assert(root->simple_rte_array[childRTindex] == NULL); |
489 | root->simple_rte_array[childRTindex] = childrte; |
490 | Assert(root->append_rel_array[childRTindex] == NULL); |
491 | root->append_rel_array[childRTindex] = appinfo; |
492 | |
493 | /* |
494 | * Build a PlanRowMark if parent is marked FOR UPDATE/SHARE. |
495 | */ |
496 | if (top_parentrc) |
497 | { |
498 | PlanRowMark *childrc = makeNode(PlanRowMark); |
499 | |
500 | childrc->rti = childRTindex; |
501 | childrc->prti = top_parentrc->rti; |
502 | childrc->rowmarkId = top_parentrc->rowmarkId; |
503 | /* Reselect rowmark type, because relkind might not match parent */ |
504 | childrc->markType = select_rowmark_type(childrte, |
505 | top_parentrc->strength); |
506 | childrc->allMarkTypes = (1 << childrc->markType); |
507 | childrc->strength = top_parentrc->strength; |
508 | childrc->waitPolicy = top_parentrc->waitPolicy; |
509 | |
510 | /* |
511 | * We mark RowMarks for partitioned child tables as parent RowMarks so |
512 | * that the executor ignores them (except their existence means that |
513 | * the child tables will be locked using the appropriate mode). |
514 | */ |
515 | childrc->isParent = (childrte->relkind == RELKIND_PARTITIONED_TABLE); |
516 | |
517 | /* Include child's rowmark type in top parent's allMarkTypes */ |
518 | top_parentrc->allMarkTypes |= childrc->allMarkTypes; |
519 | |
520 | root->rowMarks = lappend(root->rowMarks, childrc); |
521 | } |
522 | } |
523 | |
524 | /* |
525 | * translate_col_privs |
526 | * Translate a bitmapset representing per-column privileges from the |
527 | * parent rel's attribute numbering to the child's. |
528 | * |
529 | * The only surprise here is that we don't translate a parent whole-row |
530 | * reference into a child whole-row reference. That would mean requiring |
531 | * permissions on all child columns, which is overly strict, since the |
532 | * query is really only going to reference the inherited columns. Instead |
533 | * we set the per-column bits for all inherited columns. |
534 | */ |
535 | static Bitmapset * |
536 | translate_col_privs(const Bitmapset *parent_privs, |
537 | List *translated_vars) |
538 | { |
539 | Bitmapset *child_privs = NULL; |
540 | bool whole_row; |
541 | int attno; |
542 | ListCell *lc; |
543 | |
544 | /* System attributes have the same numbers in all tables */ |
545 | for (attno = FirstLowInvalidHeapAttributeNumber + 1; attno < 0; attno++) |
546 | { |
547 | if (bms_is_member(attno - FirstLowInvalidHeapAttributeNumber, |
548 | parent_privs)) |
549 | child_privs = bms_add_member(child_privs, |
550 | attno - FirstLowInvalidHeapAttributeNumber); |
551 | } |
552 | |
553 | /* Check if parent has whole-row reference */ |
554 | whole_row = bms_is_member(InvalidAttrNumber - FirstLowInvalidHeapAttributeNumber, |
555 | parent_privs); |
556 | |
557 | /* And now translate the regular user attributes, using the vars list */ |
558 | attno = InvalidAttrNumber; |
559 | foreach(lc, translated_vars) |
560 | { |
561 | Var *var = lfirst_node(Var, lc); |
562 | |
563 | attno++; |
564 | if (var == NULL) /* ignore dropped columns */ |
565 | continue; |
566 | if (whole_row || |
567 | bms_is_member(attno - FirstLowInvalidHeapAttributeNumber, |
568 | parent_privs)) |
569 | child_privs = bms_add_member(child_privs, |
570 | var->varattno - FirstLowInvalidHeapAttributeNumber); |
571 | } |
572 | |
573 | return child_privs; |
574 | } |
575 | |
576 | /* |
577 | * expand_appendrel_subquery |
578 | * Add "other rel" RelOptInfos for the children of an appendrel baserel |
579 | * |
580 | * "rel" is a subquery relation that has the rte->inh flag set, meaning it |
581 | * is a UNION ALL subquery that's been flattened into an appendrel, with |
582 | * child subqueries listed in root->append_rel_list. We need to build |
583 | * a RelOptInfo for each child relation so that we can plan scans on them. |
584 | */ |
585 | static void |
586 | expand_appendrel_subquery(PlannerInfo *root, RelOptInfo *rel, |
587 | RangeTblEntry *rte, Index rti) |
588 | { |
589 | ListCell *l; |
590 | |
591 | foreach(l, root->append_rel_list) |
592 | { |
593 | AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l); |
594 | Index childRTindex = appinfo->child_relid; |
595 | RangeTblEntry *childrte; |
596 | RelOptInfo *childrel; |
597 | |
598 | /* append_rel_list contains all append rels; ignore others */ |
599 | if (appinfo->parent_relid != rti) |
600 | continue; |
601 | |
602 | /* find the child RTE, which should already exist */ |
603 | Assert(childRTindex < root->simple_rel_array_size); |
604 | childrte = root->simple_rte_array[childRTindex]; |
605 | Assert(childrte != NULL); |
606 | |
607 | /* Build the child RelOptInfo. */ |
608 | childrel = build_simple_rel(root, childRTindex, rel); |
609 | |
610 | /* Child may itself be an inherited rel, either table or subquery. */ |
611 | if (childrte->inh) |
612 | expand_inherited_rtentry(root, childrel, childrte, childRTindex); |
613 | } |
614 | } |
615 | |
616 | |
617 | /* |
618 | * apply_child_basequals |
619 | * Populate childrel's base restriction quals from parent rel's quals, |
620 | * translating them using appinfo. |
621 | * |
622 | * If any of the resulting clauses evaluate to constant false or NULL, we |
623 | * return false and don't apply any quals. Caller should mark the relation as |
624 | * a dummy rel in this case, since it doesn't need to be scanned. |
625 | */ |
626 | bool |
627 | apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel, |
628 | RelOptInfo *childrel, RangeTblEntry *childRTE, |
629 | AppendRelInfo *appinfo) |
630 | { |
631 | List *childquals; |
632 | Index cq_min_security; |
633 | ListCell *lc; |
634 | |
635 | /* |
636 | * The child rel's targetlist might contain non-Var expressions, which |
637 | * means that substitution into the quals could produce opportunities for |
638 | * const-simplification, and perhaps even pseudoconstant quals. Therefore, |
639 | * transform each RestrictInfo separately to see if it reduces to a |
640 | * constant or pseudoconstant. (We must process them separately to keep |
641 | * track of the security level of each qual.) |
642 | */ |
643 | childquals = NIL; |
644 | cq_min_security = UINT_MAX; |
645 | foreach(lc, parentrel->baserestrictinfo) |
646 | { |
647 | RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc); |
648 | Node *childqual; |
649 | ListCell *lc2; |
650 | |
651 | Assert(IsA(rinfo, RestrictInfo)); |
652 | childqual = adjust_appendrel_attrs(root, |
653 | (Node *) rinfo->clause, |
654 | 1, &appinfo); |
655 | childqual = eval_const_expressions(root, childqual); |
656 | /* check for flat-out constant */ |
657 | if (childqual && IsA(childqual, Const)) |
658 | { |
659 | if (((Const *) childqual)->constisnull || |
660 | !DatumGetBool(((Const *) childqual)->constvalue)) |
661 | { |
662 | /* Restriction reduces to constant FALSE or NULL */ |
663 | return false; |
664 | } |
665 | /* Restriction reduces to constant TRUE, so drop it */ |
666 | continue; |
667 | } |
668 | /* might have gotten an AND clause, if so flatten it */ |
669 | foreach(lc2, make_ands_implicit((Expr *) childqual)) |
670 | { |
671 | Node *onecq = (Node *) lfirst(lc2); |
672 | bool pseudoconstant; |
673 | |
674 | /* check for pseudoconstant (no Vars or volatile functions) */ |
675 | pseudoconstant = |
676 | !contain_vars_of_level(onecq, 0) && |
677 | !contain_volatile_functions(onecq); |
678 | if (pseudoconstant) |
679 | { |
680 | /* tell createplan.c to check for gating quals */ |
681 | root->hasPseudoConstantQuals = true; |
682 | } |
683 | /* reconstitute RestrictInfo with appropriate properties */ |
684 | childquals = lappend(childquals, |
685 | make_restrictinfo((Expr *) onecq, |
686 | rinfo->is_pushed_down, |
687 | rinfo->outerjoin_delayed, |
688 | pseudoconstant, |
689 | rinfo->security_level, |
690 | NULL, NULL, NULL)); |
691 | /* track minimum security level among child quals */ |
692 | cq_min_security = Min(cq_min_security, rinfo->security_level); |
693 | } |
694 | } |
695 | |
696 | /* |
697 | * In addition to the quals inherited from the parent, we might have |
698 | * securityQuals associated with this particular child node. (Currently |
699 | * this can only happen in appendrels originating from UNION ALL; |
700 | * inheritance child tables don't have their own securityQuals, see |
701 | * expand_single_inheritance_child().) Pull any such securityQuals up |
702 | * into the baserestrictinfo for the child. This is similar to |
703 | * process_security_barrier_quals() for the parent rel, except that we |
704 | * can't make any general deductions from such quals, since they don't |
705 | * hold for the whole appendrel. |
706 | */ |
707 | if (childRTE->securityQuals) |
708 | { |
709 | Index security_level = 0; |
710 | |
711 | foreach(lc, childRTE->securityQuals) |
712 | { |
713 | List *qualset = (List *) lfirst(lc); |
714 | ListCell *lc2; |
715 | |
716 | foreach(lc2, qualset) |
717 | { |
718 | Expr *qual = (Expr *) lfirst(lc2); |
719 | |
720 | /* not likely that we'd see constants here, so no check */ |
721 | childquals = lappend(childquals, |
722 | make_restrictinfo(qual, |
723 | true, false, false, |
724 | security_level, |
725 | NULL, NULL, NULL)); |
726 | cq_min_security = Min(cq_min_security, security_level); |
727 | } |
728 | security_level++; |
729 | } |
730 | Assert(security_level <= root->qual_security_level); |
731 | } |
732 | |
733 | /* |
734 | * OK, we've got all the baserestrictinfo quals for this child. |
735 | */ |
736 | childrel->baserestrictinfo = childquals; |
737 | childrel->baserestrict_min_security = cq_min_security; |
738 | |
739 | return true; |
740 | } |
741 | |