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
38static void expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo,
39 RangeTblEntry *parentrte,
40 Index parentRTindex, Relation parentrel,
41 PlanRowMark *top_parentrc, LOCKMODE lockmode);
42static 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);
48static Bitmapset *translate_col_privs(const Bitmapset *parent_privs,
49 List *translated_vars);
50static 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 */
78void
79expand_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 */
283static void
284expand_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 */
411static void
412expand_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 */
535static Bitmapset *
536translate_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 */
585static void
586expand_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 */
626bool
627apply_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