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