Age Owner Branch data TLA 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
2403 tgl@sss.pgh.pa.us 86 :CBC 11112 : 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 : 11112 : bool old_isParent = false;
94 : 11112 : int old_allMarkTypes = 0;
95 : :
96 [ - + ]: 11112 : Assert(rte->inh); /* else caller error */
97 : :
98 [ + + ]: 11112 : if (rte->rtekind == RTE_SUBQUERY)
99 : : {
100 : 2698 : expand_appendrel_subquery(root, rel, rte, rti);
2482 alvherre@alvh.no-ip. 101 : 2698 : return;
102 : : }
103 : :
2403 tgl@sss.pgh.pa.us 104 [ - + ]: 8414 : Assert(rte->rtekind == RTE_RELATION);
105 : :
2482 alvherre@alvh.no-ip. 106 : 8414 : 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 : : */
2435 rhaas@postgresql.org 121 : 8414 : oldrelation = table_open(parentOID, NoLock);
2482 alvherre@alvh.no-ip. 122 : 8414 : 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 : 8414 : oldrc = get_plan_rowmark(root->rowMarks, rti);
130 [ + + ]: 8414 : if (oldrc)
131 : : {
2403 tgl@sss.pgh.pa.us 132 : 808 : old_isParent = oldrc->isParent;
2482 alvherre@alvh.no-ip. 133 : 808 : oldrc->isParent = true;
134 : : /* Save initial value of allMarkTypes before children add to it */
2403 tgl@sss.pgh.pa.us 135 : 808 : old_allMarkTypes = oldrc->allMarkTypes;
136 : : }
137 : :
138 : : /* Scan the inheritance set and expand it */
2435 rhaas@postgresql.org 139 [ + + ]: 8414 : if (oldrelation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
140 : : {
141 : : RTEPermissionInfo *perminfo;
142 : :
1056 alvherre@alvh.no-ip. 143 : 7009 : perminfo = getRTEPermissionInfo(root->parse->rteperminfos, rte);
144 : :
145 : : /*
146 : : * Partitioned table, so set up for partitioning.
147 : : */
2482 148 [ - + ]: 7009 : 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 : : */
2403 tgl@sss.pgh.pa.us 154 : 7009 : 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 */
2435 rhaas@postgresql.org 170 : 1405 : 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 : : */
2403 tgl@sss.pgh.pa.us 180 [ - + ]: 1405 : Assert(inhOIDs != NIL);
181 [ - + ]: 1405 : Assert(linitial_oid(inhOIDs) == parentOID);
182 : :
183 : : /* Expand simple_rel_array and friends to hold child objects. */
184 : 1405 : 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 : : */
2482 alvherre@alvh.no-ip. 190 [ + - + + : 5073 : foreach(l, inhOIDs)
+ + ]
191 : : {
192 : 3669 : 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 [ + + ]: 3669 : if (childOID != parentOID)
2471 andres@anarazel.de 199 : 2264 : newrelation = table_open(childOID, NoLock);
200 : : else
2482 alvherre@alvh.no-ip. 201 : 1405 : 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 [ + + + + : 3669 : if (childOID != parentOID && RELATION_IS_OTHER_TEMP(newrelation))
+ + ]
210 : : {
2471 andres@anarazel.de 211 : 21 : table_close(newrelation, lockmode);
2482 alvherre@alvh.no-ip. 212 : 21 : continue;
213 : : }
214 : :
215 : : /* Create RTE and AppendRelInfo, plus PlanRowMark if needed. */
2403 tgl@sss.pgh.pa.us 216 : 3648 : expand_single_inheritance_child(root, rte, rti, oldrelation,
217 : : oldrc, newrelation,
218 : : &childrte, &childRTindex);
219 : :
220 : : /* Create the otherrel RelOptInfo too. */
221 : 3647 : (void) build_simple_rel(root, childRTindex, rel);
222 : :
223 : : /* Close child relations, but keep locks */
2482 alvherre@alvh.no-ip. 224 [ + + ]: 3647 : if (childOID != parentOID)
2471 andres@anarazel.de 225 : 2242 : 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 : : */
2403 tgl@sss.pgh.pa.us 240 [ + + ]: 8413 : if (oldrc)
241 : : {
242 : 808 : int new_allMarkTypes = oldrc->allMarkTypes;
243 : : Var *var;
244 : : TargetEntry *tle;
245 : : char resname[32];
246 : 808 : List *newvars = NIL;
247 : :
248 : : /* Add TID junk Var if needed, unless we had it already */
1608 249 [ + + ]: 808 : if (new_allMarkTypes & ~(1 << ROW_MARK_COPY) &&
250 [ + + ]: 806 : !(old_allMarkTypes & ~(1 << ROW_MARK_COPY)))
251 : : {
252 : : /* Need to fetch TID */
253 : 2 : var = makeVar(oldrc->rti,
254 : : SelfItemPointerAttributeNumber,
255 : : TIDOID,
256 : : -1,
257 : : InvalidOid,
258 : : 0);
259 : 2 : snprintf(resname, sizeof(resname), "ctid%u", oldrc->rowmarkId);
260 : 2 : tle = makeTargetEntry((Expr *) var,
261 : 2 : list_length(root->processed_tlist) + 1,
262 : : pstrdup(resname),
263 : : true);
264 : 2 : root->processed_tlist = lappend(root->processed_tlist, tle);
265 : 2 : newvars = lappend(newvars, var);
266 : : }
267 : :
268 : : /* Add whole-row junk Var if needed, unless we had it already */
2403 269 [ + + ]: 808 : if ((new_allMarkTypes & (1 << ROW_MARK_COPY)) &&
270 [ + + ]: 23 : !(old_allMarkTypes & (1 << ROW_MARK_COPY)))
271 : : {
272 : 19 : var = makeWholeRowVar(planner_rt_fetch(oldrc->rti, root),
273 [ + - ]: 19 : oldrc->rti,
274 : : 0,
275 : : false);
276 : 19 : snprintf(resname, sizeof(resname), "wholerow%u", oldrc->rowmarkId);
277 : 19 : tle = makeTargetEntry((Expr *) var,
278 : 19 : list_length(root->processed_tlist) + 1,
279 : : pstrdup(resname),
280 : : true);
281 : 19 : root->processed_tlist = lappend(root->processed_tlist, tle);
282 : 19 : newvars = lappend(newvars, var);
283 : : }
284 : :
285 : : /* Add tableoid junk Var, unless we had it already */
286 [ + - ]: 808 : if (!old_isParent)
287 : : {
288 : 808 : var = makeVar(oldrc->rti,
289 : : TableOidAttributeNumber,
290 : : OIDOID,
291 : : -1,
292 : : InvalidOid,
293 : : 0);
294 : 808 : snprintf(resname, sizeof(resname), "tableoid%u", oldrc->rowmarkId);
295 : 808 : tle = makeTargetEntry((Expr *) var,
296 : 808 : list_length(root->processed_tlist) + 1,
297 : : pstrdup(resname),
298 : : true);
299 : 808 : root->processed_tlist = lappend(root->processed_tlist, tle);
300 : 808 : 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 : : */
1167 307 : 808 : add_vars_to_targetlist(root, newvars, bms_make_singleton(0));
308 : : }
309 : :
2471 andres@anarazel.de 310 : 8413 : table_close(oldrelation, NoLock);
311 : : }
312 : :
313 : : /*
314 : : * expand_partitioned_rtentry
315 : : * Recursively expand an RTE for a partitioned table.
316 : : */
317 : : static void
2403 tgl@sss.pgh.pa.us 318 : 8806 : 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 : 8806 : check_stack_depth();
329 : :
330 [ - + ]: 8806 : Assert(parentrte->inh);
331 : :
2426 rhaas@postgresql.org 332 : 8806 : partdesc = PartitionDirectoryLookup(root->glob->partition_directory,
333 : : parentrel);
334 : :
335 : : /* A partitioned table should always have a partition descriptor. */
2482 alvherre@alvh.no-ip. 336 [ - + ]: 8806 : Assert(partdesc);
337 : :
338 : : /* Nothing further to do here if there are no partitions. */
339 [ + + ]: 8806 : if (partdesc->nparts == 0)
340 : 21 : return;
341 : :
342 : : /*
343 : : * Perform partition pruning using restriction clauses assigned to parent
344 : : * relation. live_parts will contain PartitionDesc indexes of partitions
345 : : * that survive pruning. Below, we will initialize child objects for the
346 : : * surviving partitions.
347 : : */
58 drowley@postgresql.o 348 : 8785 : relinfo->live_parts = prune_append_rel_partitions(relinfo);
349 : :
350 : : /* Expand simple_rel_array and friends to hold child objects. */
351 : 8785 : num_live_parts = bms_num_members(relinfo->live_parts);
2403 tgl@sss.pgh.pa.us 352 [ + + ]: 8785 : if (num_live_parts > 0)
353 : 8641 : expand_planner_arrays(root, num_live_parts);
354 : :
355 : : /*
356 : : * We also store partition RelOptInfo pointers in the parent relation.
357 : : * Since we're palloc0'ing, slots corresponding to pruned partitions will
358 : : * contain NULL.
359 : : */
360 [ - + ]: 8785 : Assert(relinfo->part_rels == NULL);
361 : 8785 : relinfo->part_rels = (RelOptInfo **)
362 : 8785 : palloc0(relinfo->nparts * sizeof(RelOptInfo *));
363 : :
364 : : /*
365 : : * Create a child RTE for each live partition. Note that unlike
366 : : * traditional inheritance, we don't need a child RTE for the partitioned
367 : : * table itself, because it's not going to be scanned.
368 : : */
369 : 8785 : i = -1;
58 drowley@postgresql.o 370 [ + + ]: 26298 : while ((i = bms_next_member(relinfo->live_parts, i)) >= 0)
371 : : {
2482 alvherre@alvh.no-ip. 372 : 17513 : Oid childOID = partdesc->oids[i];
373 : : Relation childrel;
374 : : RangeTblEntry *childrte;
375 : : Index childRTindex;
376 : : RelOptInfo *childrelinfo;
377 : :
378 : : /*
379 : : * Open rel, acquiring required locks. If a partition was recently
380 : : * detached and subsequently dropped, then opening it will fail. In
381 : : * this case, behave as though the partition had been pruned.
382 : : */
434 383 : 17513 : childrel = try_table_open(childOID, lockmode);
384 [ - + ]: 17513 : if (childrel == NULL)
385 : : {
434 alvherre@alvh.no-ip. 386 :UBC 0 : relinfo->live_parts = bms_del_member(relinfo->live_parts, i);
387 : 0 : continue;
388 : : }
389 : :
390 : : /*
391 : : * Temporary partitions belonging to other sessions should have been
392 : : * disallowed at definition, but for paranoia's sake, let's double
393 : : * check.
394 : : */
2482 alvherre@alvh.no-ip. 395 [ + + - + ]:CBC 17513 : if (RELATION_IS_OTHER_TEMP(childrel))
2482 alvherre@alvh.no-ip. 396 [ # # ]:UBC 0 : elog(ERROR, "temporary relation from another session found as partition");
397 : :
398 : : /* Create RTE and AppendRelInfo, plus PlanRowMark if needed. */
2482 alvherre@alvh.no-ip. 399 :CBC 17513 : expand_single_inheritance_child(root, parentrte, parentRTindex,
400 : : parentrel, top_parentrc, childrel,
401 : : &childrte, &childRTindex);
402 : :
403 : : /* Create the otherrel RelOptInfo too. */
2403 tgl@sss.pgh.pa.us 404 : 17513 : childrelinfo = build_simple_rel(root, childRTindex, relinfo);
405 : 17513 : relinfo->part_rels[i] = childrelinfo;
2028 efujita@postgresql.o 406 : 35026 : relinfo->all_partrels = bms_add_members(relinfo->all_partrels,
407 : 17513 : childrelinfo->relids);
408 : :
409 : : /* If this child is itself partitioned, recurse */
2482 alvherre@alvh.no-ip. 410 [ + + ]: 17513 : if (childrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
411 : : {
1056 412 : 1797 : AppendRelInfo *appinfo = root->append_rel_array[childRTindex];
413 : : Bitmapset *child_updatedCols;
414 : :
415 : 1797 : child_updatedCols = translate_col_privs(parent_updatedCols,
416 : : appinfo->translated_vars);
417 : :
2403 tgl@sss.pgh.pa.us 418 : 1797 : expand_partitioned_rtentry(root, childrelinfo,
419 : : childrte, childRTindex,
420 : : childrel,
421 : : child_updatedCols,
422 : : top_parentrc, lockmode);
423 : : }
424 : :
425 : : /* Close child relation, but keep locks */
2471 andres@anarazel.de 426 : 17513 : table_close(childrel, NoLock);
427 : : }
428 : : }
429 : :
430 : : /*
431 : : * expand_single_inheritance_child
432 : : * Build a RangeTblEntry and an AppendRelInfo, plus maybe a PlanRowMark.
433 : : *
434 : : * We now expand the partition hierarchy level by level, creating a
435 : : * corresponding hierarchy of AppendRelInfos and RelOptInfos, where each
436 : : * partitioned descendant acts as a parent of its immediate partitions.
437 : : * (This is a difference from what older versions of PostgreSQL did and what
438 : : * is still done in the case of table inheritance for unpartitioned tables,
439 : : * where the hierarchy is flattened during RTE expansion.)
440 : : *
441 : : * PlanRowMarks still carry the top-parent's RTI, and the top-parent's
442 : : * allMarkTypes field still accumulates values from all descendents.
443 : : *
444 : : * "parentrte" and "parentRTindex" are immediate parent's RTE and
445 : : * RTI. "top_parentrc" is top parent's PlanRowMark.
446 : : *
447 : : * The child RangeTblEntry and its RTI are returned in "childrte_p" and
448 : : * "childRTindex_p" resp.
449 : : */
450 : : static void
2482 alvherre@alvh.no-ip. 451 : 21161 : expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
452 : : Index parentRTindex, Relation parentrel,
453 : : PlanRowMark *top_parentrc, Relation childrel,
454 : : RangeTblEntry **childrte_p,
455 : : Index *childRTindex_p)
456 : : {
457 : 21161 : Query *parse = root->parse;
97 rguo@postgresql.org 458 :GNC 21161 : Oid parentOID = RelationGetRelid(parentrel);
2482 alvherre@alvh.no-ip. 459 :CBC 21161 : Oid childOID = RelationGetRelid(childrel);
460 : : RangeTblEntry *childrte;
461 : : Index childRTindex;
462 : : AppendRelInfo *appinfo;
463 : : TupleDesc child_tupdesc;
464 : : List *parent_colnames;
465 : : List *child_colnames;
466 : :
467 : : /*
468 : : * Build an RTE for the child, and attach to query's rangetable list. We
469 : : * copy most scalar fields of the parent's RTE, but replace relation OID,
470 : : * relkind, and inh for the child. Set the child's securityQuals to
471 : : * empty, because we only want to apply the parent's RLS conditions
472 : : * regardless of what RLS properties individual children may have. (This
473 : : * is an intentional choice to make inherited RLS work like regular
474 : : * permissions checks.) The parent securityQuals will be propagated to
475 : : * children along with other base restriction clauses, so we don't need to
476 : : * do it here. Other infrastructure of the parent RTE has to be
477 : : * translated to match the child table's column ordering, which we do
478 : : * below, so a "flat" copy is sufficient to start with.
479 : : */
2156 tgl@sss.pgh.pa.us 480 : 21161 : childrte = makeNode(RangeTblEntry);
481 : 21161 : memcpy(childrte, parentrte, sizeof(RangeTblEntry));
482 [ - + ]: 21161 : Assert(parentrte->rtekind == RTE_RELATION); /* else this is dubious */
2482 alvherre@alvh.no-ip. 483 : 21161 : childrte->relid = childOID;
484 : 21161 : childrte->relkind = childrel->rd_rel->relkind;
485 : : /* A partitioned child will need to be expanded further. */
2407 tgl@sss.pgh.pa.us 486 [ + + ]: 21161 : if (childrte->relkind == RELKIND_PARTITIONED_TABLE)
487 : : {
488 [ - + ]: 1797 : Assert(childOID != parentOID);
2482 alvherre@alvh.no-ip. 489 : 1797 : childrte->inh = true;
490 : : }
491 : : else
492 : 19364 : childrte->inh = false;
493 : 21161 : childrte->securityQuals = NIL;
494 : :
495 : : /* No permission checking for child RTEs. */
732 amitlan@postgresql.o 496 : 21161 : childrte->perminfoindex = 0;
497 : :
498 : : /* Link not-yet-fully-filled child RTE into data structures */
2482 alvherre@alvh.no-ip. 499 : 21161 : parse->rtable = lappend(parse->rtable, childrte);
500 : 21161 : childRTindex = list_length(parse->rtable);
2156 tgl@sss.pgh.pa.us 501 : 21161 : *childrte_p = childrte;
2482 alvherre@alvh.no-ip. 502 : 21161 : *childRTindex_p = childRTindex;
503 : :
504 : : /*
505 : : * Retrieve column not-null constraint information for the child relation
506 : : * if its relation OID is different from the parent's.
507 : : */
97 rguo@postgresql.org 508 [ + + ]:GNC 21161 : if (childOID != parentOID)
509 : 19756 : get_relation_notnullatts(root, childrel);
510 : :
511 : : /*
512 : : * Build an AppendRelInfo struct for each parent/child pair.
513 : : */
2407 tgl@sss.pgh.pa.us 514 :CBC 21161 : appinfo = make_append_rel_info(parentrel, childrel,
515 : : parentRTindex, childRTindex);
2403 516 : 21160 : root->append_rel_list = lappend(root->append_rel_list, appinfo);
517 : :
518 : : /* tablesample is probably null, but copy it */
2156 519 : 21160 : childrte->tablesample = copyObject(parentrte->tablesample);
520 : :
521 : : /*
522 : : * Construct an alias clause for the child, which we can also use as eref.
523 : : * This is important so that EXPLAIN will print the right column aliases
524 : : * for child-table columns. (Since ruleutils.c doesn't have any easy way
525 : : * to reassociate parent and child columns, we must get the child column
526 : : * aliases right to start with. Note that setting childrte->alias forces
527 : : * ruleutils.c to use these column names, which it otherwise would not.)
528 : : */
529 : 21160 : child_tupdesc = RelationGetDescr(childrel);
530 : 21160 : parent_colnames = parentrte->eref->colnames;
531 : 21160 : child_colnames = NIL;
532 [ + + ]: 79720 : for (int cattno = 0; cattno < child_tupdesc->natts; cattno++)
533 : : {
534 : 58560 : Form_pg_attribute att = TupleDescAttr(child_tupdesc, cattno);
535 : : const char *attname;
536 : :
537 [ + + ]: 58560 : if (att->attisdropped)
538 : : {
539 : : /* Always insert an empty string for a dropped column */
540 : 1187 : attname = "";
541 : : }
542 [ + + + - ]: 112793 : else if (appinfo->parent_colnos[cattno] > 0 &&
543 : 55420 : appinfo->parent_colnos[cattno] <= list_length(parent_colnames))
544 : : {
545 : : /* Duplicate the query-assigned name for the parent column */
546 : 55420 : attname = strVal(list_nth(parent_colnames,
547 : : appinfo->parent_colnos[cattno] - 1));
548 : : }
549 : : else
550 : : {
551 : : /* New column, just use its real name */
552 : 1953 : attname = NameStr(att->attname);
553 : : }
554 : 58560 : child_colnames = lappend(child_colnames, makeString(pstrdup(attname)));
555 : : }
556 : :
557 : : /*
558 : : * We just duplicate the parent's table alias name for each child. If the
559 : : * plan gets printed, ruleutils.c has to sort out unique table aliases to
560 : : * use, which it can handle.
561 : : */
562 : 21160 : childrte->alias = childrte->eref = makeAlias(parentrte->eref->aliasname,
563 : : child_colnames);
564 : :
565 : : /*
566 : : * Store the RTE and appinfo in the respective PlannerInfo arrays, which
567 : : * the caller must already have allocated space for.
568 : : */
2403 569 [ - + ]: 21160 : Assert(childRTindex < root->simple_rel_array_size);
570 [ - + ]: 21160 : Assert(root->simple_rte_array[childRTindex] == NULL);
571 : 21160 : root->simple_rte_array[childRTindex] = childrte;
572 [ - + ]: 21160 : Assert(root->append_rel_array[childRTindex] == NULL);
573 : 21160 : root->append_rel_array[childRTindex] = appinfo;
574 : :
575 : : /*
576 : : * Build a PlanRowMark if parent is marked FOR UPDATE/SHARE.
577 : : */
2482 alvherre@alvh.no-ip. 578 [ + + ]: 21160 : if (top_parentrc)
579 : : {
580 : 1172 : PlanRowMark *childrc = makeNode(PlanRowMark);
581 : :
582 : 1172 : childrc->rti = childRTindex;
583 : 1172 : childrc->prti = top_parentrc->rti;
584 : 1172 : childrc->rowmarkId = top_parentrc->rowmarkId;
585 : : /* Reselect rowmark type, because relkind might not match parent */
586 : 1172 : childrc->markType = select_rowmark_type(childrte,
587 : : top_parentrc->strength);
588 : 1172 : childrc->allMarkTypes = (1 << childrc->markType);
589 : 1172 : childrc->strength = top_parentrc->strength;
590 : 1172 : childrc->waitPolicy = top_parentrc->waitPolicy;
591 : :
592 : : /*
593 : : * We mark RowMarks for partitioned child tables as parent RowMarks so
594 : : * that the executor ignores them (except their existence means that
595 : : * the child tables will be locked using the appropriate mode).
596 : : */
597 : 1172 : childrc->isParent = (childrte->relkind == RELKIND_PARTITIONED_TABLE);
598 : :
599 : : /* Include child's rowmark type in top parent's allMarkTypes */
600 : 1172 : top_parentrc->allMarkTypes |= childrc->allMarkTypes;
601 : :
602 : 1172 : root->rowMarks = lappend(root->rowMarks, childrc);
603 : : }
604 : :
605 : : /*
606 : : * If we are creating a child of the query target relation (only possible
607 : : * in UPDATE/DELETE/MERGE), add it to all_result_relids, as well as
608 : : * leaf_result_relids if appropriate, and make sure that we generate
609 : : * required row-identity data.
610 : : */
1671 tgl@sss.pgh.pa.us 611 [ + + ]: 21160 : if (bms_is_member(parentRTindex, root->all_result_relids))
612 : : {
613 : : /* OK, record the child as a result rel too. */
614 : 3041 : root->all_result_relids = bms_add_member(root->all_result_relids,
615 : : childRTindex);
616 : :
617 : : /* Non-leaf partitions don't need any row identity info. */
618 [ + + ]: 3041 : if (childrte->relkind != RELKIND_PARTITIONED_TABLE)
619 : : {
620 : : Var *rrvar;
621 : :
622 : 2756 : root->leaf_result_relids = bms_add_member(root->leaf_result_relids,
623 : : childRTindex);
624 : :
625 : : /*
626 : : * If we have any child target relations, assume they all need to
627 : : * generate a junk "tableoid" column. (If only one child survives
628 : : * pruning, we wouldn't really need this, but it's not worth
629 : : * thrashing about to avoid it.)
630 : : */
631 : 2756 : rrvar = makeVar(childRTindex,
632 : : TableOidAttributeNumber,
633 : : OIDOID,
634 : : -1,
635 : : InvalidOid,
636 : : 0);
637 : 2756 : add_row_identity_var(root, rrvar, childRTindex, "tableoid");
638 : :
639 : : /* Register any row-identity columns needed by this child. */
640 : 2756 : add_row_identity_columns(root, childRTindex,
641 : : childrte, childrel);
642 : : }
643 : : }
2482 alvherre@alvh.no-ip. 644 : 21160 : }
645 : :
646 : : /*
647 : : * get_rel_all_updated_cols
648 : : * Returns the set of columns of a given "simple" relation that are
649 : : * updated by this query.
650 : : */
651 : : Bitmapset *
1056 652 : 45 : get_rel_all_updated_cols(PlannerInfo *root, RelOptInfo *rel)
653 : : {
654 : : Index relid;
655 : : RangeTblEntry *rte;
656 : : RTEPermissionInfo *perminfo;
657 : : Bitmapset *updatedCols,
658 : : *extraUpdatedCols;
659 : :
660 [ - + ]: 45 : Assert(root->parse->commandType == CMD_UPDATE);
661 [ + + - + ]: 45 : Assert(IS_SIMPLE_REL(rel));
662 : :
663 : : /*
664 : : * We obtain updatedCols for the query's result relation. Then, if
665 : : * necessary, we map it to the column numbers of the relation for which
666 : : * they were requested.
667 : : */
668 : 45 : relid = root->parse->resultRelation;
669 [ + - ]: 45 : rte = planner_rt_fetch(relid, root);
670 : 45 : perminfo = getRTEPermissionInfo(root->parse->rteperminfos, rte);
671 : :
672 : 45 : updatedCols = perminfo->updatedCols;
673 : :
674 [ + + ]: 45 : if (rel->relid != relid)
675 : : {
676 : 21 : RelOptInfo *top_parent_rel = find_base_rel(root, relid);
677 : :
678 [ - + - - : 21 : Assert(IS_OTHER_REL(rel));
- - ]
679 : :
680 : 21 : updatedCols = translate_col_privs_multilevel(root, rel, top_parent_rel,
681 : : updatedCols);
682 : : }
683 : :
684 : : /*
685 : : * Now we must check to see if there are any generated columns that depend
686 : : * on the updatedCols, and add them to the result.
687 : : */
1026 tgl@sss.pgh.pa.us 688 : 45 : extraUpdatedCols = get_dependent_generated_columns(root, rel->relid,
689 : : updatedCols);
690 : :
1056 alvherre@alvh.no-ip. 691 : 45 : return bms_union(updatedCols, extraUpdatedCols);
692 : : }
693 : :
694 : : /*
695 : : * translate_col_privs
696 : : * Translate a bitmapset representing per-column privileges from the
697 : : * parent rel's attribute numbering to the child's.
698 : : *
699 : : * The only surprise here is that we don't translate a parent whole-row
700 : : * reference into a child whole-row reference. That would mean requiring
701 : : * permissions on all child columns, which is overly strict, since the
702 : : * query is really only going to reference the inherited columns. Instead
703 : : * we set the per-column bits for all inherited columns.
704 : : */
705 : : static Bitmapset *
2476 706 : 1820 : translate_col_privs(const Bitmapset *parent_privs,
707 : : List *translated_vars)
708 : : {
709 : 1820 : Bitmapset *child_privs = NULL;
710 : : bool whole_row;
711 : : int attno;
712 : : ListCell *lc;
713 : :
714 : : /* System attributes have the same numbers in all tables */
715 [ + + ]: 12740 : for (attno = FirstLowInvalidHeapAttributeNumber + 1; attno < 0; attno++)
716 : : {
717 [ - + ]: 10920 : if (bms_is_member(attno - FirstLowInvalidHeapAttributeNumber,
718 : : parent_privs))
2476 alvherre@alvh.no-ip. 719 :UBC 0 : child_privs = bms_add_member(child_privs,
720 : : attno - FirstLowInvalidHeapAttributeNumber);
721 : : }
722 : :
723 : : /* Check if parent has whole-row reference */
2476 alvherre@alvh.no-ip. 724 :CBC 1820 : whole_row = bms_is_member(InvalidAttrNumber - FirstLowInvalidHeapAttributeNumber,
725 : : parent_privs);
726 : :
727 : : /* And now translate the regular user attributes, using the vars list */
728 : 1820 : attno = InvalidAttrNumber;
729 [ + - + + : 6468 : foreach(lc, translated_vars)
+ + ]
730 : : {
731 : 4648 : Var *var = lfirst_node(Var, lc);
732 : :
733 : 4648 : attno++;
734 [ + + ]: 4648 : if (var == NULL) /* ignore dropped columns */
735 : 63 : continue;
736 [ + - + + ]: 9170 : if (whole_row ||
737 : 4585 : bms_is_member(attno - FirstLowInvalidHeapAttributeNumber,
738 : : parent_privs))
739 : 274 : child_privs = bms_add_member(child_privs,
740 : 274 : var->varattno - FirstLowInvalidHeapAttributeNumber);
741 : : }
742 : :
743 : 1820 : return child_privs;
744 : : }
745 : :
746 : : /*
747 : : * translate_col_privs_multilevel
748 : : * Recursively translates the column numbers contained in 'parent_cols'
749 : : * to the column numbers of a descendant relation given by 'rel'
750 : : *
751 : : * Note that because this is based on translate_col_privs, it will expand
752 : : * a whole-row reference into all inherited columns. This is not an issue
753 : : * for current usages, but beware.
754 : : */
755 : : static Bitmapset *
1026 tgl@sss.pgh.pa.us 756 : 23 : translate_col_privs_multilevel(PlannerInfo *root, RelOptInfo *rel,
757 : : RelOptInfo *parent_rel,
758 : : Bitmapset *parent_cols)
759 : : {
760 : : AppendRelInfo *appinfo;
761 : :
762 : : /* Fast path for easy case. */
763 [ - + ]: 23 : if (parent_cols == NULL)
1026 tgl@sss.pgh.pa.us 764 :UBC 0 : return NULL;
765 : :
766 : : /* Recurse if immediate parent is not the top parent. */
1026 tgl@sss.pgh.pa.us 767 [ + + ]:CBC 23 : if (rel->parent != parent_rel)
768 : : {
769 [ + - ]: 2 : if (rel->parent)
770 : 2 : parent_cols = translate_col_privs_multilevel(root, rel->parent,
771 : : parent_rel,
772 : : parent_cols);
773 : : else
1026 tgl@sss.pgh.pa.us 774 [ # # ]:UBC 0 : elog(ERROR, "rel with relid %u is not a child rel", rel->relid);
775 : : }
776 : :
777 : : /* Now translate for this child. */
1026 tgl@sss.pgh.pa.us 778 [ - + ]:CBC 23 : Assert(root->append_rel_array != NULL);
779 : 23 : appinfo = root->append_rel_array[rel->relid];
780 [ - + ]: 23 : Assert(appinfo != NULL);
781 : :
782 : 23 : return translate_col_privs(parent_cols, appinfo->translated_vars);
783 : : }
784 : :
785 : : /*
786 : : * expand_appendrel_subquery
787 : : * Add "other rel" RelOptInfos for the children of an appendrel baserel
788 : : *
789 : : * "rel" is a subquery relation that has the rte->inh flag set, meaning it
790 : : * is a UNION ALL subquery that's been flattened into an appendrel, with
791 : : * child subqueries listed in root->append_rel_list. We need to build
792 : : * a RelOptInfo for each child relation so that we can plan scans on them.
793 : : */
794 : : static void
2403 795 : 2698 : expand_appendrel_subquery(PlannerInfo *root, RelOptInfo *rel,
796 : : RangeTblEntry *rte, Index rti)
797 : : {
798 : : ListCell *l;
799 : :
800 [ + - + + : 11219 : foreach(l, root->append_rel_list)
+ + ]
801 : : {
802 : 8521 : AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l);
803 : 8521 : Index childRTindex = appinfo->child_relid;
804 : : RangeTblEntry *childrte;
805 : : RelOptInfo *childrel;
806 : :
807 : : /* append_rel_list contains all append rels; ignore others */
808 [ + + ]: 8521 : if (appinfo->parent_relid != rti)
809 : 597 : continue;
810 : :
811 : : /* find the child RTE, which should already exist */
812 [ - + ]: 7924 : Assert(childRTindex < root->simple_rel_array_size);
813 : 7924 : childrte = root->simple_rte_array[childRTindex];
814 [ - + ]: 7924 : Assert(childrte != NULL);
815 : :
816 : : /* Build the child RelOptInfo. */
817 : 7924 : childrel = build_simple_rel(root, childRTindex, rel);
818 : :
819 : : /* Child may itself be an inherited rel, either table or subquery. */
820 [ + + ]: 7924 : if (childrte->inh)
821 : 118 : expand_inherited_rtentry(root, childrel, childrte, childRTindex);
822 : : }
823 : 2698 : }
824 : :
825 : :
826 : : /*
827 : : * apply_child_basequals
828 : : * Populate childrel's base restriction quals from parent rel's quals,
829 : : * translating Vars using appinfo and re-checking for quals which are
830 : : * constant-TRUE or constant-FALSE when applied to this child relation.
831 : : *
832 : : * If any of the resulting clauses evaluate to constant false or NULL, we
833 : : * return false and don't apply any quals. Caller should mark the relation as
834 : : * a dummy rel in this case, since it doesn't need to be scanned. Constant
835 : : * true quals are ignored.
836 : : */
837 : : bool
838 : 29084 : apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel,
839 : : RelOptInfo *childrel, RangeTblEntry *childRTE,
840 : : AppendRelInfo *appinfo)
841 : : {
842 : : List *childquals;
843 : : Index cq_min_security;
844 : : ListCell *lc;
845 : :
846 : : /*
847 : : * The child rel's targetlist might contain non-Var expressions, which
848 : : * means that substitution into the quals could produce opportunities for
849 : : * const-simplification, and perhaps even pseudoconstant quals. Therefore,
850 : : * transform each RestrictInfo separately to see if it reduces to a
851 : : * constant or pseudoconstant. (We must process them separately to keep
852 : : * track of the security level of each qual.)
853 : : */
854 : 29084 : childquals = NIL;
855 : 29084 : cq_min_security = UINT_MAX;
856 [ + + + + : 43870 : foreach(lc, parentrel->baserestrictinfo)
+ + ]
857 : : {
858 : 14831 : RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
859 : : Node *childqual;
860 : : ListCell *lc2;
861 : :
862 [ - + ]: 14831 : Assert(IsA(rinfo, RestrictInfo));
863 : 14831 : childqual = adjust_appendrel_attrs(root,
864 : 14831 : (Node *) rinfo->clause,
865 : : 1, &appinfo);
866 : 14831 : childqual = eval_const_expressions(root, childqual);
867 : : /* check for flat-out constant */
868 [ + - + + ]: 14831 : if (childqual && IsA(childqual, Const))
869 : : {
870 [ + - ]: 96 : if (((Const *) childqual)->constisnull ||
871 [ + + ]: 96 : !DatumGetBool(((Const *) childqual)->constvalue))
872 : : {
873 : : /* Restriction reduces to constant FALSE or NULL */
874 : 45 : return false;
875 : : }
876 : : /* Restriction reduces to constant TRUE, so drop it */
877 : 51 : continue;
878 : : }
879 : : /* might have gotten an AND clause, if so flatten it */
880 [ + - + + : 29476 : foreach(lc2, make_ands_implicit((Expr *) childqual))
+ + ]
881 : : {
882 : 14741 : Node *onecq = (Node *) lfirst(lc2);
883 : : bool pseudoconstant;
884 : : RestrictInfo *childrinfo;
885 : :
886 : : /* check for pseudoconstant (no Vars or volatile functions) */
887 : 14741 : pseudoconstant =
888 [ + + ]: 14764 : !contain_vars_of_level(onecq, 0) &&
889 [ + - ]: 23 : !contain_volatile_functions(onecq);
890 [ + + ]: 14741 : if (pseudoconstant)
891 : : {
892 : : /* tell createplan.c to check for gating quals */
893 : 23 : root->hasPseudoConstantQuals = true;
894 : : }
895 : : /* reconstitute RestrictInfo with appropriate properties */
563 drowley@postgresql.o 896 : 14741 : childrinfo = make_restrictinfo(root,
897 : : (Expr *) onecq,
898 : 14741 : rinfo->is_pushed_down,
899 : 14741 : rinfo->has_clone,
900 : 14741 : rinfo->is_clone,
901 : : pseudoconstant,
902 : : rinfo->security_level,
903 : : NULL, NULL, NULL);
904 : :
905 : : /* Restriction is proven always false */
906 [ - + ]: 14741 : if (restriction_is_always_false(root, childrinfo))
563 drowley@postgresql.o 907 :LBC (6) : return false;
908 : : /* Restriction is proven always true, so drop it */
563 drowley@postgresql.o 909 [ - + ]:CBC 14741 : if (restriction_is_always_true(root, childrinfo))
563 drowley@postgresql.o 910 :LBC (9) : continue;
911 : :
563 drowley@postgresql.o 912 :CBC 14741 : childquals = lappend(childquals, childrinfo);
913 : : /* track minimum security level among child quals */
2403 tgl@sss.pgh.pa.us 914 : 14741 : cq_min_security = Min(cq_min_security, rinfo->security_level);
915 : : }
916 : : }
917 : :
918 : : /*
919 : : * In addition to the quals inherited from the parent, we might have
920 : : * securityQuals associated with this particular child node. (Currently
921 : : * this can only happen in appendrels originating from UNION ALL;
922 : : * inheritance child tables don't have their own securityQuals, see
923 : : * expand_single_inheritance_child().) Pull any such securityQuals up
924 : : * into the baserestrictinfo for the child. This is similar to
925 : : * process_security_barrier_quals() for the parent rel, except that we
926 : : * can't make any general deductions from such quals, since they don't
927 : : * hold for the whole appendrel.
928 : : */
929 [ + + ]: 29039 : if (childRTE->securityQuals)
930 : : {
931 : 24 : Index security_level = 0;
932 : :
933 [ + - + + : 48 : foreach(lc, childRTE->securityQuals)
+ + ]
934 : : {
935 : 24 : List *qualset = (List *) lfirst(lc);
936 : : ListCell *lc2;
937 : :
938 [ + - + + : 48 : foreach(lc2, qualset)
+ + ]
939 : : {
940 : 24 : Expr *qual = (Expr *) lfirst(lc2);
941 : :
942 : : /* not likely that we'd see constants here, so no check */
943 : 24 : childquals = lappend(childquals,
1740 944 : 24 : make_restrictinfo(root, qual,
945 : : true,
946 : : false, false,
947 : : false,
948 : : security_level,
949 : : NULL, NULL, NULL));
2403 950 : 24 : cq_min_security = Min(cq_min_security, security_level);
951 : : }
952 : 24 : security_level++;
953 : : }
954 [ - + ]: 24 : Assert(security_level <= root->qual_security_level);
955 : : }
956 : :
957 : : /*
958 : : * OK, we've got all the baserestrictinfo quals for this child.
959 : : */
960 : 29039 : childrel->baserestrictinfo = childquals;
961 : 29039 : childrel->baserestrict_min_security = cq_min_security;
962 : :
963 : 29039 : return true;
964 : : }
|