Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * inherit.c
4 : * Routines to process child relations in inheritance trees
5 : *
6 : * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
7 : * Portions Copyright (c) 1994, Regents of the University of California
8 : *
9 : *
10 : * IDENTIFICATION
11 : * src/backend/optimizer/util/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/plancat.h"
29 : #include "optimizer/planmain.h"
30 : #include "optimizer/planner.h"
31 : #include "optimizer/prep.h"
32 : #include "optimizer/restrictinfo.h"
33 : #include "parser/parsetree.h"
34 : #include "parser/parse_relation.h"
35 : #include "partitioning/partdesc.h"
36 : #include "partitioning/partprune.h"
37 : #include "utils/rel.h"
38 :
39 :
40 : static void expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo,
41 : RangeTblEntry *parentrte,
42 : Index parentRTindex, Relation parentrel,
43 : Bitmapset *parent_updatedCols,
44 : PlanRowMark *top_parentrc, LOCKMODE lockmode);
45 : static void expand_single_inheritance_child(PlannerInfo *root,
46 : RangeTblEntry *parentrte,
47 : Index parentRTindex, Relation parentrel,
48 : PlanRowMark *top_parentrc, Relation childrel,
49 : RangeTblEntry **childrte_p,
50 : Index *childRTindex_p);
51 : static Bitmapset *translate_col_privs(const Bitmapset *parent_privs,
52 : List *translated_vars);
53 : static Bitmapset *translate_col_privs_multilevel(PlannerInfo *root,
54 : RelOptInfo *rel,
55 : RelOptInfo *parent_rel,
56 : Bitmapset *parent_cols);
57 : static void expand_appendrel_subquery(PlannerInfo *root, RelOptInfo *rel,
58 : RangeTblEntry *rte, Index rti);
59 :
60 :
61 : /*
62 : * expand_inherited_rtentry
63 : * Expand a rangetable entry that has the "inh" bit set.
64 : *
65 : * "inh" is only allowed in two cases: RELATION and SUBQUERY RTEs.
66 : *
67 : * "inh" on a plain RELATION RTE means that it is a partitioned table or the
68 : * parent of a traditional-inheritance set. In this case we must add entries
69 : * for all the interesting child tables to the query's rangetable, and build
70 : * additional planner data structures for them, including RelOptInfos,
71 : * AppendRelInfos, and possibly PlanRowMarks.
72 : *
73 : * Note that the original RTE is considered to represent the whole inheritance
74 : * set. In the case of traditional inheritance, the first of the generated
75 : * RTEs is an RTE for the same table, but with inh = false, to represent the
76 : * parent table in its role as a simple member of the inheritance set. For
77 : * partitioning, we don't need a second RTE because the partitioned table
78 : * itself has no data and need not be scanned.
79 : *
80 : * "inh" on a SUBQUERY RTE means that it's the parent of a UNION ALL group,
81 : * which is treated as an appendrel similarly to inheritance cases; however,
82 : * we already made RTEs and AppendRelInfos for the subqueries. We only need
83 : * to build RelOptInfos for them, which is done by expand_appendrel_subquery.
84 : */
85 : void
86 21192 : expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel,
87 : RangeTblEntry *rte, Index rti)
88 : {
89 : Oid parentOID;
90 : Relation oldrelation;
91 : LOCKMODE lockmode;
92 : PlanRowMark *oldrc;
93 21192 : bool old_isParent = false;
94 21192 : int old_allMarkTypes = 0;
95 :
96 : Assert(rte->inh); /* else caller error */
97 :
98 21192 : if (rte->rtekind == RTE_SUBQUERY)
99 : {
100 4726 : expand_appendrel_subquery(root, rel, rte, rti);
101 4726 : return;
102 : }
103 :
104 : Assert(rte->rtekind == RTE_RELATION);
105 :
106 16466 : parentOID = rte->relid;
107 :
108 : /*
109 : * We used to check has_subclass() here, but there's no longer any need
110 : * to, because subquery_planner already did.
111 : */
112 :
113 : /*
114 : * The rewriter should already have obtained an appropriate lock on each
115 : * relation named in the query, so we can open the parent relation without
116 : * locking it. However, for each child relation we add to the query, we
117 : * must obtain an appropriate lock, because this will be the first use of
118 : * those relations in the parse/rewrite/plan pipeline. Child rels should
119 : * use the same lockmode as their parent.
120 : */
121 16466 : oldrelation = table_open(parentOID, NoLock);
122 16466 : lockmode = rte->rellockmode;
123 :
124 : /*
125 : * If parent relation is selected FOR UPDATE/SHARE, we need to mark its
126 : * PlanRowMark as isParent = true, and generate a new PlanRowMark for each
127 : * child.
128 : */
129 16466 : oldrc = get_plan_rowmark(root->rowMarks, rti);
130 16466 : if (oldrc)
131 : {
132 1604 : old_isParent = oldrc->isParent;
133 1604 : oldrc->isParent = true;
134 : /* Save initial value of allMarkTypes before children add to it */
135 1604 : old_allMarkTypes = oldrc->allMarkTypes;
136 : }
137 :
138 : /* Scan the inheritance set and expand it */
139 16466 : if (oldrelation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
140 : {
141 : RTEPermissionInfo *perminfo;
142 :
143 13656 : perminfo = getRTEPermissionInfo(root->parse->rteperminfos, rte);
144 :
145 : /*
146 : * Partitioned table, so set up for partitioning.
147 : */
148 : Assert(rte->relkind == RELKIND_PARTITIONED_TABLE);
149 :
150 : /*
151 : * Recursively expand and lock the partitions. While at it, also
152 : * extract the partition key columns of all the partitioned tables.
153 : */
154 13656 : expand_partitioned_rtentry(root, rel, rte, rti,
155 : oldrelation,
156 : perminfo->updatedCols,
157 : oldrc, lockmode);
158 : }
159 : else
160 : {
161 : /*
162 : * Ordinary table, so process traditional-inheritance children. (Note
163 : * that partitioned tables are not allowed to have inheritance
164 : * children, so it's not possible for both cases to apply.)
165 : */
166 : List *inhOIDs;
167 : ListCell *l;
168 :
169 : /* Scan for all members of inheritance set, acquire needed locks */
170 2810 : inhOIDs = find_all_inheritors(parentOID, lockmode, NULL);
171 :
172 : /*
173 : * We used to special-case the situation where the table no longer has
174 : * any children, by clearing rte->inh and exiting. That no longer
175 : * works, because this function doesn't get run until after decisions
176 : * have been made that depend on rte->inh. We have to treat such
177 : * situations as normal inheritance. The table itself should always
178 : * have been found, though.
179 : */
180 : Assert(inhOIDs != NIL);
181 : Assert(linitial_oid(inhOIDs) == parentOID);
182 :
183 : /* Expand simple_rel_array and friends to hold child objects. */
184 2810 : expand_planner_arrays(root, list_length(inhOIDs));
185 :
186 : /*
187 : * Expand inheritance children in the order the OIDs were returned by
188 : * find_all_inheritors.
189 : */
190 10146 : foreach(l, inhOIDs)
191 : {
192 7338 : Oid childOID = lfirst_oid(l);
193 : Relation newrelation;
194 : RangeTblEntry *childrte;
195 : Index childRTindex;
196 :
197 : /* Open rel if needed; we already have required locks */
198 7338 : if (childOID != parentOID)
199 4528 : newrelation = table_open(childOID, NoLock);
200 : else
201 2810 : newrelation = oldrelation;
202 :
203 : /*
204 : * It is possible that the parent table has children that are temp
205 : * tables of other backends. We cannot safely access such tables
206 : * (because of buffering issues), and the best thing to do seems
207 : * to be to silently ignore them.
208 : */
209 7338 : if (childOID != parentOID && RELATION_IS_OTHER_TEMP(newrelation))
210 : {
211 42 : table_close(newrelation, lockmode);
212 42 : continue;
213 : }
214 :
215 : /* Create RTE and AppendRelInfo, plus PlanRowMark if needed. */
216 7296 : expand_single_inheritance_child(root, rte, rti, oldrelation,
217 : oldrc, newrelation,
218 : &childrte, &childRTindex);
219 :
220 : /* Create the otherrel RelOptInfo too. */
221 7294 : (void) build_simple_rel(root, childRTindex, rel);
222 :
223 : /* Close child relations, but keep locks */
224 7294 : if (childOID != parentOID)
225 4484 : table_close(newrelation, NoLock);
226 : }
227 : }
228 :
229 : /*
230 : * Some children might require different mark types, which would've been
231 : * reported into oldrc. If so, add relevant entries to the top-level
232 : * targetlist and update parent rel's reltarget. This should match what
233 : * preprocess_targetlist() would have added if the mark types had been
234 : * requested originally.
235 : *
236 : * (Someday it might be useful to fold these resjunk columns into the
237 : * row-identity-column management used for UPDATE/DELETE. Today is not
238 : * that day, however.)
239 : */
240 16464 : if (oldrc)
241 : {
242 1604 : int new_allMarkTypes = oldrc->allMarkTypes;
243 : Var *var;
244 : TargetEntry *tle;
245 : char resname[32];
246 1604 : List *newvars = NIL;
247 :
248 : /* Add TID junk Var if needed, unless we had it already */
249 1604 : if (new_allMarkTypes & ~(1 << ROW_MARK_COPY) &&
250 1600 : !(old_allMarkTypes & ~(1 << ROW_MARK_COPY)))
251 : {
252 : /* Need to fetch TID */
253 4 : var = makeVar(oldrc->rti,
254 : SelfItemPointerAttributeNumber,
255 : TIDOID,
256 : -1,
257 : InvalidOid,
258 : 0);
259 4 : snprintf(resname, sizeof(resname), "ctid%u", oldrc->rowmarkId);
260 4 : tle = makeTargetEntry((Expr *) var,
261 4 : list_length(root->processed_tlist) + 1,
262 : pstrdup(resname),
263 : true);
264 4 : root->processed_tlist = lappend(root->processed_tlist, tle);
265 4 : newvars = lappend(newvars, var);
266 : }
267 :
268 : /* Add whole-row junk Var if needed, unless we had it already */
269 1604 : if ((new_allMarkTypes & (1 << ROW_MARK_COPY)) &&
270 46 : !(old_allMarkTypes & (1 << ROW_MARK_COPY)))
271 : {
272 38 : var = makeWholeRowVar(planner_rt_fetch(oldrc->rti, root),
273 38 : oldrc->rti,
274 : 0,
275 : false);
276 38 : snprintf(resname, sizeof(resname), "wholerow%u", oldrc->rowmarkId);
277 38 : tle = makeTargetEntry((Expr *) var,
278 38 : list_length(root->processed_tlist) + 1,
279 : pstrdup(resname),
280 : true);
281 38 : root->processed_tlist = lappend(root->processed_tlist, tle);
282 38 : newvars = lappend(newvars, var);
283 : }
284 :
285 : /* Add tableoid junk Var, unless we had it already */
286 1604 : if (!old_isParent)
287 : {
288 1604 : var = makeVar(oldrc->rti,
289 : TableOidAttributeNumber,
290 : OIDOID,
291 : -1,
292 : InvalidOid,
293 : 0);
294 1604 : snprintf(resname, sizeof(resname), "tableoid%u", oldrc->rowmarkId);
295 1604 : tle = makeTargetEntry((Expr *) var,
296 1604 : list_length(root->processed_tlist) + 1,
297 : pstrdup(resname),
298 : true);
299 1604 : root->processed_tlist = lappend(root->processed_tlist, tle);
300 1604 : newvars = lappend(newvars, var);
301 : }
302 :
303 : /*
304 : * Add the newly added Vars to parent's reltarget. We needn't worry
305 : * about the children's reltargets, they'll be made later.
306 : */
307 1604 : add_vars_to_targetlist(root, newvars, bms_make_singleton(0));
308 : }
309 :
310 16464 : table_close(oldrelation, NoLock);
311 : }
312 :
313 : /*
314 : * expand_partitioned_rtentry
315 : * Recursively expand an RTE for a partitioned table.
316 : */
317 : static void
318 16962 : expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo,
319 : RangeTblEntry *parentrte,
320 : Index parentRTindex, Relation parentrel,
321 : Bitmapset *parent_updatedCols,
322 : PlanRowMark *top_parentrc, LOCKMODE lockmode)
323 : {
324 : PartitionDesc partdesc;
325 : int num_live_parts;
326 : int i;
327 :
328 16962 : check_stack_depth();
329 :
330 : Assert(parentrte->inh);
331 :
332 16962 : partdesc = PartitionDirectoryLookup(root->glob->partition_directory,
333 : parentrel);
334 :
335 : /* A partitioned table should always have a partition descriptor. */
336 : Assert(partdesc);
337 :
338 : /*
339 : * Note down whether any partition key cols are being updated. Though it's
340 : * the root partitioned table's updatedCols we are interested in,
341 : * parent_updatedCols provided by the caller contains the root partrel's
342 : * updatedCols translated to match the attribute ordering of parentrel.
343 : */
344 16962 : if (!root->partColsUpdated)
345 16656 : root->partColsUpdated =
346 16656 : has_partition_attrs(parentrel, parent_updatedCols, NULL);
347 :
348 : /* Nothing further to do here if there are no partitions. */
349 16962 : if (partdesc->nparts == 0)
350 42 : return;
351 :
352 : /*
353 : * Perform partition pruning using restriction clauses assigned to parent
354 : * relation. live_parts will contain PartitionDesc indexes of partitions
355 : * that survive pruning. Below, we will initialize child objects for the
356 : * surviving partitions.
357 : */
358 16920 : relinfo->live_parts = prune_append_rel_partitions(relinfo);
359 :
360 : /* Expand simple_rel_array and friends to hold child objects. */
361 16920 : num_live_parts = bms_num_members(relinfo->live_parts);
362 16920 : if (num_live_parts > 0)
363 16632 : expand_planner_arrays(root, num_live_parts);
364 :
365 : /*
366 : * We also store partition RelOptInfo pointers in the parent relation.
367 : * Since we're palloc0'ing, slots corresponding to pruned partitions will
368 : * contain NULL.
369 : */
370 : Assert(relinfo->part_rels == NULL);
371 16920 : relinfo->part_rels = (RelOptInfo **)
372 16920 : palloc0(relinfo->nparts * sizeof(RelOptInfo *));
373 :
374 : /*
375 : * Create a child RTE for each live partition. Note that unlike
376 : * traditional inheritance, we don't need a child RTE for the partitioned
377 : * table itself, because it's not going to be scanned.
378 : */
379 16920 : i = -1;
380 50350 : while ((i = bms_next_member(relinfo->live_parts, i)) >= 0)
381 : {
382 33430 : Oid childOID = partdesc->oids[i];
383 : Relation childrel;
384 : RangeTblEntry *childrte;
385 : Index childRTindex;
386 : RelOptInfo *childrelinfo;
387 :
388 : /*
389 : * Open rel, acquiring required locks. If a partition was recently
390 : * detached and subsequently dropped, then opening it will fail. In
391 : * this case, behave as though the partition had been pruned.
392 : */
393 33430 : childrel = try_table_open(childOID, lockmode);
394 33430 : if (childrel == NULL)
395 : {
396 0 : relinfo->live_parts = bms_del_member(relinfo->live_parts, i);
397 0 : continue;
398 : }
399 :
400 : /*
401 : * Temporary partitions belonging to other sessions should have been
402 : * disallowed at definition, but for paranoia's sake, let's double
403 : * check.
404 : */
405 33430 : if (RELATION_IS_OTHER_TEMP(childrel))
406 0 : elog(ERROR, "temporary relation from another session found as partition");
407 :
408 : /* Create RTE and AppendRelInfo, plus PlanRowMark if needed. */
409 33430 : expand_single_inheritance_child(root, parentrte, parentRTindex,
410 : parentrel, top_parentrc, childrel,
411 : &childrte, &childRTindex);
412 :
413 : /* Create the otherrel RelOptInfo too. */
414 33430 : childrelinfo = build_simple_rel(root, childRTindex, relinfo);
415 33430 : relinfo->part_rels[i] = childrelinfo;
416 66860 : relinfo->all_partrels = bms_add_members(relinfo->all_partrels,
417 33430 : childrelinfo->relids);
418 :
419 : /* If this child is itself partitioned, recurse */
420 33430 : if (childrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
421 : {
422 3306 : AppendRelInfo *appinfo = root->append_rel_array[childRTindex];
423 : Bitmapset *child_updatedCols;
424 :
425 3306 : child_updatedCols = translate_col_privs(parent_updatedCols,
426 : appinfo->translated_vars);
427 :
428 3306 : expand_partitioned_rtentry(root, childrelinfo,
429 : childrte, childRTindex,
430 : childrel,
431 : child_updatedCols,
432 : top_parentrc, lockmode);
433 : }
434 :
435 : /* Close child relation, but keep locks */
436 33430 : table_close(childrel, NoLock);
437 : }
438 : }
439 :
440 : /*
441 : * expand_single_inheritance_child
442 : * Build a RangeTblEntry and an AppendRelInfo, plus maybe a PlanRowMark.
443 : *
444 : * We now expand the partition hierarchy level by level, creating a
445 : * corresponding hierarchy of AppendRelInfos and RelOptInfos, where each
446 : * partitioned descendant acts as a parent of its immediate partitions.
447 : * (This is a difference from what older versions of PostgreSQL did and what
448 : * is still done in the case of table inheritance for unpartitioned tables,
449 : * where the hierarchy is flattened during RTE expansion.)
450 : *
451 : * PlanRowMarks still carry the top-parent's RTI, and the top-parent's
452 : * allMarkTypes field still accumulates values from all descendents.
453 : *
454 : * "parentrte" and "parentRTindex" are immediate parent's RTE and
455 : * RTI. "top_parentrc" is top parent's PlanRowMark.
456 : *
457 : * The child RangeTblEntry and its RTI are returned in "childrte_p" and
458 : * "childRTindex_p" resp.
459 : */
460 : static void
461 40726 : expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
462 : Index parentRTindex, Relation parentrel,
463 : PlanRowMark *top_parentrc, Relation childrel,
464 : RangeTblEntry **childrte_p,
465 : Index *childRTindex_p)
466 : {
467 40726 : Query *parse = root->parse;
468 40726 : Oid parentOID = RelationGetRelid(parentrel);
469 40726 : Oid childOID = RelationGetRelid(childrel);
470 : RangeTblEntry *childrte;
471 : Index childRTindex;
472 : AppendRelInfo *appinfo;
473 : TupleDesc child_tupdesc;
474 : List *parent_colnames;
475 : List *child_colnames;
476 :
477 : /*
478 : * Build an RTE for the child, and attach to query's rangetable list. We
479 : * copy most scalar fields of the parent's RTE, but replace relation OID,
480 : * relkind, and inh for the child. Set the child's securityQuals to
481 : * empty, because we only want to apply the parent's RLS conditions
482 : * regardless of what RLS properties individual children may have. (This
483 : * is an intentional choice to make inherited RLS work like regular
484 : * permissions checks.) The parent securityQuals will be propagated to
485 : * children along with other base restriction clauses, so we don't need to
486 : * do it here. Other infrastructure of the parent RTE has to be
487 : * translated to match the child table's column ordering, which we do
488 : * below, so a "flat" copy is sufficient to start with.
489 : */
490 40726 : childrte = makeNode(RangeTblEntry);
491 40726 : memcpy(childrte, parentrte, sizeof(RangeTblEntry));
492 : Assert(parentrte->rtekind == RTE_RELATION); /* else this is dubious */
493 40726 : childrte->relid = childOID;
494 40726 : childrte->relkind = childrel->rd_rel->relkind;
495 : /* A partitioned child will need to be expanded further. */
496 40726 : if (childrte->relkind == RELKIND_PARTITIONED_TABLE)
497 : {
498 : Assert(childOID != parentOID);
499 3306 : childrte->inh = true;
500 : }
501 : else
502 37420 : childrte->inh = false;
503 40726 : childrte->securityQuals = NIL;
504 :
505 : /* No permission checking for child RTEs. */
506 40726 : childrte->perminfoindex = 0;
507 :
508 : /* Link not-yet-fully-filled child RTE into data structures */
509 40726 : parse->rtable = lappend(parse->rtable, childrte);
510 40726 : childRTindex = list_length(parse->rtable);
511 40726 : *childrte_p = childrte;
512 40726 : *childRTindex_p = childRTindex;
513 :
514 : /*
515 : * Retrieve column not-null constraint information for the child relation
516 : * if its relation OID is different from the parent's.
517 : */
518 40726 : if (childOID != parentOID)
519 37916 : get_relation_notnullatts(root, childrel);
520 :
521 : /*
522 : * Build an AppendRelInfo struct for each parent/child pair.
523 : */
524 40726 : appinfo = make_append_rel_info(parentrel, childrel,
525 : parentRTindex, childRTindex);
526 40724 : root->append_rel_list = lappend(root->append_rel_list, appinfo);
527 :
528 : /* tablesample is probably null, but copy it */
529 40724 : childrte->tablesample = copyObject(parentrte->tablesample);
530 :
531 : /*
532 : * Construct an alias clause for the child, which we can also use as eref.
533 : * This is important so that EXPLAIN will print the right column aliases
534 : * for child-table columns. (Since ruleutils.c doesn't have any easy way
535 : * to reassociate parent and child columns, we must get the child column
536 : * aliases right to start with. Note that setting childrte->alias forces
537 : * ruleutils.c to use these column names, which it otherwise would not.)
538 : */
539 40724 : child_tupdesc = RelationGetDescr(childrel);
540 40724 : parent_colnames = parentrte->eref->colnames;
541 40724 : child_colnames = NIL;
542 154526 : for (int cattno = 0; cattno < child_tupdesc->natts; cattno++)
543 : {
544 113802 : Form_pg_attribute att = TupleDescAttr(child_tupdesc, cattno);
545 : const char *attname;
546 :
547 113802 : if (att->attisdropped)
548 : {
549 : /* Always insert an empty string for a dropped column */
550 2356 : attname = "";
551 : }
552 218986 : else if (appinfo->parent_colnos[cattno] > 0 &&
553 107540 : appinfo->parent_colnos[cattno] <= list_length(parent_colnames))
554 : {
555 : /* Duplicate the query-assigned name for the parent column */
556 107540 : attname = strVal(list_nth(parent_colnames,
557 : appinfo->parent_colnos[cattno] - 1));
558 : }
559 : else
560 : {
561 : /* New column, just use its real name */
562 3906 : attname = NameStr(att->attname);
563 : }
564 113802 : child_colnames = lappend(child_colnames, makeString(pstrdup(attname)));
565 : }
566 :
567 : /*
568 : * We just duplicate the parent's table alias name for each child. If the
569 : * plan gets printed, ruleutils.c has to sort out unique table aliases to
570 : * use, which it can handle.
571 : */
572 40724 : childrte->alias = childrte->eref = makeAlias(parentrte->eref->aliasname,
573 : child_colnames);
574 :
575 : /*
576 : * Store the RTE and appinfo in the respective PlannerInfo arrays, which
577 : * the caller must already have allocated space for.
578 : */
579 : Assert(childRTindex < root->simple_rel_array_size);
580 : Assert(root->simple_rte_array[childRTindex] == NULL);
581 40724 : root->simple_rte_array[childRTindex] = childrte;
582 : Assert(root->append_rel_array[childRTindex] == NULL);
583 40724 : root->append_rel_array[childRTindex] = appinfo;
584 :
585 : /*
586 : * Build a PlanRowMark if parent is marked FOR UPDATE/SHARE.
587 : */
588 40724 : if (top_parentrc)
589 : {
590 2332 : PlanRowMark *childrc = makeNode(PlanRowMark);
591 :
592 2332 : childrc->rti = childRTindex;
593 2332 : childrc->prti = top_parentrc->rti;
594 2332 : childrc->rowmarkId = top_parentrc->rowmarkId;
595 : /* Reselect rowmark type, because relkind might not match parent */
596 2332 : childrc->markType = select_rowmark_type(childrte,
597 : top_parentrc->strength);
598 2332 : childrc->allMarkTypes = (1 << childrc->markType);
599 2332 : childrc->strength = top_parentrc->strength;
600 2332 : childrc->waitPolicy = top_parentrc->waitPolicy;
601 :
602 : /*
603 : * We mark RowMarks for partitioned child tables as parent RowMarks so
604 : * that the executor ignores them (except their existence means that
605 : * the child tables will be locked using the appropriate mode).
606 : */
607 2332 : childrc->isParent = (childrte->relkind == RELKIND_PARTITIONED_TABLE);
608 :
609 : /* Include child's rowmark type in top parent's allMarkTypes */
610 2332 : top_parentrc->allMarkTypes |= childrc->allMarkTypes;
611 :
612 2332 : root->rowMarks = lappend(root->rowMarks, childrc);
613 : }
614 :
615 : /*
616 : * If we are creating a child of the query target relation (only possible
617 : * in UPDATE/DELETE/MERGE), add it to all_result_relids, as well as
618 : * leaf_result_relids if appropriate, and make sure that we generate
619 : * required row-identity data.
620 : */
621 40724 : if (bms_is_member(parentRTindex, root->all_result_relids))
622 : {
623 : /* OK, record the child as a result rel too. */
624 6064 : root->all_result_relids = bms_add_member(root->all_result_relids,
625 : childRTindex);
626 :
627 : /* Non-leaf partitions don't need any row identity info. */
628 6064 : if (childrte->relkind != RELKIND_PARTITIONED_TABLE)
629 : {
630 : Var *rrvar;
631 :
632 5494 : root->leaf_result_relids = bms_add_member(root->leaf_result_relids,
633 : childRTindex);
634 :
635 : /*
636 : * If we have any child target relations, assume they all need to
637 : * generate a junk "tableoid" column. (If only one child survives
638 : * pruning, we wouldn't really need this, but it's not worth
639 : * thrashing about to avoid it.)
640 : */
641 5494 : rrvar = makeVar(childRTindex,
642 : TableOidAttributeNumber,
643 : OIDOID,
644 : -1,
645 : InvalidOid,
646 : 0);
647 5494 : add_row_identity_var(root, rrvar, childRTindex, "tableoid");
648 :
649 : /* Register any row-identity columns needed by this child. */
650 5494 : add_row_identity_columns(root, childRTindex,
651 : childrte, childrel);
652 : }
653 : }
654 40724 : }
655 :
656 : /*
657 : * get_rel_all_updated_cols
658 : * Returns the set of columns of a given "simple" relation that are
659 : * updated by this query.
660 : */
661 : Bitmapset *
662 90 : get_rel_all_updated_cols(PlannerInfo *root, RelOptInfo *rel)
663 : {
664 : Index relid;
665 : RangeTblEntry *rte;
666 : RTEPermissionInfo *perminfo;
667 : Bitmapset *updatedCols,
668 : *extraUpdatedCols;
669 :
670 : Assert(root->parse->commandType == CMD_UPDATE);
671 : Assert(IS_SIMPLE_REL(rel));
672 :
673 : /*
674 : * We obtain updatedCols for the query's result relation. Then, if
675 : * necessary, we map it to the column numbers of the relation for which
676 : * they were requested.
677 : */
678 90 : relid = root->parse->resultRelation;
679 90 : rte = planner_rt_fetch(relid, root);
680 90 : perminfo = getRTEPermissionInfo(root->parse->rteperminfos, rte);
681 :
682 90 : updatedCols = perminfo->updatedCols;
683 :
684 90 : if (rel->relid != relid)
685 : {
686 42 : RelOptInfo *top_parent_rel = find_base_rel(root, relid);
687 :
688 : Assert(IS_OTHER_REL(rel));
689 :
690 42 : updatedCols = translate_col_privs_multilevel(root, rel, top_parent_rel,
691 : updatedCols);
692 : }
693 :
694 : /*
695 : * Now we must check to see if there are any generated columns that depend
696 : * on the updatedCols, and add them to the result.
697 : */
698 90 : extraUpdatedCols = get_dependent_generated_columns(root, rel->relid,
699 : updatedCols);
700 :
701 90 : return bms_union(updatedCols, extraUpdatedCols);
702 : }
703 :
704 : /*
705 : * translate_col_privs
706 : * Translate a bitmapset representing per-column privileges from the
707 : * parent rel's attribute numbering to the child's.
708 : *
709 : * The only surprise here is that we don't translate a parent whole-row
710 : * reference into a child whole-row reference. That would mean requiring
711 : * permissions on all child columns, which is overly strict, since the
712 : * query is really only going to reference the inherited columns. Instead
713 : * we set the per-column bits for all inherited columns.
714 : */
715 : static Bitmapset *
716 3352 : translate_col_privs(const Bitmapset *parent_privs,
717 : List *translated_vars)
718 : {
719 3352 : Bitmapset *child_privs = NULL;
720 : bool whole_row;
721 : int attno;
722 : ListCell *lc;
723 :
724 : /* System attributes have the same numbers in all tables */
725 23464 : for (attno = FirstLowInvalidHeapAttributeNumber + 1; attno < 0; attno++)
726 : {
727 20112 : if (bms_is_member(attno - FirstLowInvalidHeapAttributeNumber,
728 : parent_privs))
729 0 : child_privs = bms_add_member(child_privs,
730 : attno - FirstLowInvalidHeapAttributeNumber);
731 : }
732 :
733 : /* Check if parent has whole-row reference */
734 3352 : whole_row = bms_is_member(InvalidAttrNumber - FirstLowInvalidHeapAttributeNumber,
735 : parent_privs);
736 :
737 : /* And now translate the regular user attributes, using the vars list */
738 3352 : attno = InvalidAttrNumber;
739 12072 : foreach(lc, translated_vars)
740 : {
741 8720 : Var *var = lfirst_node(Var, lc);
742 :
743 8720 : attno++;
744 8720 : if (var == NULL) /* ignore dropped columns */
745 126 : continue;
746 17188 : if (whole_row ||
747 8594 : bms_is_member(attno - FirstLowInvalidHeapAttributeNumber,
748 : parent_privs))
749 548 : child_privs = bms_add_member(child_privs,
750 548 : var->varattno - FirstLowInvalidHeapAttributeNumber);
751 : }
752 :
753 3352 : return child_privs;
754 : }
755 :
756 : /*
757 : * translate_col_privs_multilevel
758 : * Recursively translates the column numbers contained in 'parent_cols'
759 : * to the column numbers of a descendant relation given by 'rel'
760 : *
761 : * Note that because this is based on translate_col_privs, it will expand
762 : * a whole-row reference into all inherited columns. This is not an issue
763 : * for current usages, but beware.
764 : */
765 : static Bitmapset *
766 46 : translate_col_privs_multilevel(PlannerInfo *root, RelOptInfo *rel,
767 : RelOptInfo *parent_rel,
768 : Bitmapset *parent_cols)
769 : {
770 : AppendRelInfo *appinfo;
771 :
772 : /* Fast path for easy case. */
773 46 : if (parent_cols == NULL)
774 0 : return NULL;
775 :
776 : /* Recurse if immediate parent is not the top parent. */
777 46 : if (rel->parent != parent_rel)
778 : {
779 4 : if (rel->parent)
780 4 : parent_cols = translate_col_privs_multilevel(root, rel->parent,
781 : parent_rel,
782 : parent_cols);
783 : else
784 0 : elog(ERROR, "rel with relid %u is not a child rel", rel->relid);
785 : }
786 :
787 : /* Now translate for this child. */
788 : Assert(root->append_rel_array != NULL);
789 46 : appinfo = root->append_rel_array[rel->relid];
790 : Assert(appinfo != NULL);
791 :
792 46 : return translate_col_privs(parent_cols, appinfo->translated_vars);
793 : }
794 :
795 : /*
796 : * expand_appendrel_subquery
797 : * Add "other rel" RelOptInfos for the children of an appendrel baserel
798 : *
799 : * "rel" is a subquery relation that has the rte->inh flag set, meaning it
800 : * is a UNION ALL subquery that's been flattened into an appendrel, with
801 : * child subqueries listed in root->append_rel_list. We need to build
802 : * a RelOptInfo for each child relation so that we can plan scans on them.
803 : */
804 : static void
805 4726 : expand_appendrel_subquery(PlannerInfo *root, RelOptInfo *rel,
806 : RangeTblEntry *rte, Index rti)
807 : {
808 : ListCell *l;
809 :
810 16342 : foreach(l, root->append_rel_list)
811 : {
812 11616 : AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l);
813 11616 : Index childRTindex = appinfo->child_relid;
814 : RangeTblEntry *childrte;
815 : RelOptInfo *childrel;
816 :
817 : /* append_rel_list contains all append rels; ignore others */
818 11616 : if (appinfo->parent_relid != rti)
819 1242 : continue;
820 :
821 : /* find the child RTE, which should already exist */
822 : Assert(childRTindex < root->simple_rel_array_size);
823 10374 : childrte = root->simple_rte_array[childRTindex];
824 : Assert(childrte != NULL);
825 :
826 : /* Build the child RelOptInfo. */
827 10374 : childrel = build_simple_rel(root, childRTindex, rel);
828 :
829 : /* Child may itself be an inherited rel, either table or subquery. */
830 10374 : if (childrte->inh)
831 240 : expand_inherited_rtentry(root, childrel, childrte, childRTindex);
832 : }
833 4726 : }
834 :
835 :
836 : /*
837 : * apply_child_basequals
838 : * Populate childrel's base restriction quals from parent rel's quals,
839 : * translating Vars using appinfo and re-checking for quals which are
840 : * constant-TRUE or constant-FALSE when applied to this child relation.
841 : *
842 : * If any of the resulting clauses evaluate to constant false or NULL, we
843 : * return false and don't apply any quals. Caller should mark the relation as
844 : * a dummy rel in this case, since it doesn't need to be scanned. Constant
845 : * true quals are ignored.
846 : */
847 : bool
848 51098 : apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel,
849 : RelOptInfo *childrel, RangeTblEntry *childRTE,
850 : AppendRelInfo *appinfo)
851 : {
852 : List *childquals;
853 : Index cq_min_security;
854 : ListCell *lc;
855 :
856 : /*
857 : * The child rel's targetlist might contain non-Var expressions, which
858 : * means that substitution into the quals could produce opportunities for
859 : * const-simplification, and perhaps even pseudoconstant quals. Therefore,
860 : * transform each RestrictInfo separately to see if it reduces to a
861 : * constant or pseudoconstant. (We must process them separately to keep
862 : * track of the security level of each qual.)
863 : */
864 51098 : childquals = NIL;
865 51098 : cq_min_security = UINT_MAX;
866 80604 : foreach(lc, parentrel->baserestrictinfo)
867 : {
868 29596 : RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
869 : Node *childqual;
870 : ListCell *lc2;
871 :
872 : Assert(IsA(rinfo, RestrictInfo));
873 29596 : childqual = adjust_appendrel_attrs(root,
874 29596 : (Node *) rinfo->clause,
875 : 1, &appinfo);
876 29596 : childqual = eval_const_expressions(root, childqual);
877 : /* check for flat-out constant */
878 29596 : if (childqual && IsA(childqual, Const))
879 : {
880 192 : if (((Const *) childqual)->constisnull ||
881 192 : !DatumGetBool(((Const *) childqual)->constvalue))
882 : {
883 : /* Restriction reduces to constant FALSE or NULL */
884 90 : return false;
885 : }
886 : /* Restriction reduces to constant TRUE, so drop it */
887 102 : continue;
888 : }
889 : /* might have gotten an AND clause, if so flatten it */
890 58820 : foreach(lc2, make_ands_implicit((Expr *) childqual))
891 : {
892 29416 : Node *onecq = (Node *) lfirst(lc2);
893 : bool pseudoconstant;
894 : RestrictInfo *childrinfo;
895 :
896 : /* check for pseudoconstant (no Vars or volatile functions) */
897 29416 : pseudoconstant =
898 29462 : !contain_vars_of_level(onecq, 0) &&
899 46 : !contain_volatile_functions(onecq);
900 29416 : if (pseudoconstant)
901 : {
902 : /* tell createplan.c to check for gating quals */
903 46 : root->hasPseudoConstantQuals = true;
904 : }
905 : /* reconstitute RestrictInfo with appropriate properties */
906 29416 : childrinfo = make_restrictinfo(root,
907 : (Expr *) onecq,
908 29416 : rinfo->is_pushed_down,
909 29416 : rinfo->has_clone,
910 29416 : rinfo->is_clone,
911 : pseudoconstant,
912 : rinfo->security_level,
913 : NULL, NULL, NULL);
914 :
915 : /* Restriction is proven always false */
916 29416 : if (restriction_is_always_false(root, childrinfo))
917 0 : return false;
918 : /* Restriction is proven always true, so drop it */
919 29416 : if (restriction_is_always_true(root, childrinfo))
920 0 : continue;
921 :
922 29416 : childquals = lappend(childquals, childrinfo);
923 : /* track minimum security level among child quals */
924 29416 : cq_min_security = Min(cq_min_security, rinfo->security_level);
925 : }
926 : }
927 :
928 : /*
929 : * In addition to the quals inherited from the parent, we might have
930 : * securityQuals associated with this particular child node. (Currently
931 : * this can only happen in appendrels originating from UNION ALL;
932 : * inheritance child tables don't have their own securityQuals, see
933 : * expand_single_inheritance_child().) Pull any such securityQuals up
934 : * into the baserestrictinfo for the child. This is similar to
935 : * process_security_barrier_quals() for the parent rel, except that we
936 : * can't make any general deductions from such quals, since they don't
937 : * hold for the whole appendrel.
938 : */
939 51008 : if (childRTE->securityQuals)
940 : {
941 48 : Index security_level = 0;
942 :
943 96 : foreach(lc, childRTE->securityQuals)
944 : {
945 48 : List *qualset = (List *) lfirst(lc);
946 : ListCell *lc2;
947 :
948 96 : foreach(lc2, qualset)
949 : {
950 48 : Expr *qual = (Expr *) lfirst(lc2);
951 :
952 : /* not likely that we'd see constants here, so no check */
953 48 : childquals = lappend(childquals,
954 48 : make_restrictinfo(root, qual,
955 : true,
956 : false, false,
957 : false,
958 : security_level,
959 : NULL, NULL, NULL));
960 48 : cq_min_security = Min(cq_min_security, security_level);
961 : }
962 48 : security_level++;
963 : }
964 : Assert(security_level <= root->qual_security_level);
965 : }
966 :
967 : /*
968 : * OK, we've got all the baserestrictinfo quals for this child.
969 : */
970 51008 : childrel->baserestrictinfo = childquals;
971 51008 : childrel->baserestrict_min_security = cq_min_security;
972 :
973 51008 : return true;
974 : }
|