/src/postgres/src/backend/optimizer/util/inherit.c
Line | Count | Source |
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 |
88 | | expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, |
89 | | RangeTblEntry *rte, Index rti) |
90 | 0 | { |
91 | 0 | Oid parentOID; |
92 | 0 | Relation oldrelation; |
93 | 0 | LOCKMODE lockmode; |
94 | 0 | PlanRowMark *oldrc; |
95 | 0 | bool old_isParent = false; |
96 | 0 | int old_allMarkTypes = 0; |
97 | |
|
98 | 0 | Assert(rte->inh); /* else caller error */ |
99 | |
|
100 | 0 | if (rte->rtekind == RTE_SUBQUERY) |
101 | 0 | { |
102 | 0 | expand_appendrel_subquery(root, rel, rte, rti); |
103 | 0 | return; |
104 | 0 | } |
105 | | |
106 | 0 | Assert(rte->rtekind == RTE_RELATION); |
107 | |
|
108 | 0 | 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 | | */ |
123 | 0 | oldrelation = table_open(parentOID, NoLock); |
124 | 0 | 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 | 0 | oldrc = get_plan_rowmark(root->rowMarks, rti); |
132 | 0 | if (oldrc) |
133 | 0 | { |
134 | 0 | old_isParent = oldrc->isParent; |
135 | 0 | oldrc->isParent = true; |
136 | | /* Save initial value of allMarkTypes before children add to it */ |
137 | 0 | old_allMarkTypes = oldrc->allMarkTypes; |
138 | 0 | } |
139 | | |
140 | | /* Scan the inheritance set and expand it */ |
141 | 0 | if (oldrelation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) |
142 | 0 | { |
143 | 0 | RTEPermissionInfo *perminfo; |
144 | |
|
145 | 0 | perminfo = getRTEPermissionInfo(root->parse->rteperminfos, rte); |
146 | | |
147 | | /* |
148 | | * Partitioned table, so set up for partitioning. |
149 | | */ |
150 | 0 | 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 | | */ |
156 | 0 | expand_partitioned_rtentry(root, rel, rte, rti, |
157 | 0 | oldrelation, |
158 | 0 | perminfo->updatedCols, |
159 | 0 | oldrc, lockmode); |
160 | 0 | } |
161 | 0 | else |
162 | 0 | { |
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 | 0 | List *inhOIDs; |
169 | 0 | ListCell *l; |
170 | | |
171 | | /* Scan for all members of inheritance set, acquire needed locks */ |
172 | 0 | 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 | | */ |
182 | 0 | Assert(inhOIDs != NIL); |
183 | 0 | Assert(linitial_oid(inhOIDs) == parentOID); |
184 | | |
185 | | /* Expand simple_rel_array and friends to hold child objects. */ |
186 | 0 | 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 | | */ |
192 | 0 | foreach(l, inhOIDs) |
193 | 0 | { |
194 | 0 | Oid childOID = lfirst_oid(l); |
195 | 0 | Relation newrelation; |
196 | 0 | RangeTblEntry *childrte; |
197 | 0 | Index childRTindex; |
198 | | |
199 | | /* Open rel if needed; we already have required locks */ |
200 | 0 | if (childOID != parentOID) |
201 | 0 | newrelation = table_open(childOID, NoLock); |
202 | 0 | else |
203 | 0 | 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 | 0 | if (childOID != parentOID && RELATION_IS_OTHER_TEMP(newrelation)) |
212 | 0 | { |
213 | 0 | table_close(newrelation, lockmode); |
214 | 0 | continue; |
215 | 0 | } |
216 | | |
217 | | /* Create RTE and AppendRelInfo, plus PlanRowMark if needed. */ |
218 | 0 | expand_single_inheritance_child(root, rte, rti, oldrelation, |
219 | 0 | oldrc, newrelation, |
220 | 0 | &childrte, &childRTindex); |
221 | | |
222 | | /* Create the otherrel RelOptInfo too. */ |
223 | 0 | (void) build_simple_rel(root, childRTindex, rel); |
224 | | |
225 | | /* Close child relations, but keep locks */ |
226 | 0 | if (childOID != parentOID) |
227 | 0 | table_close(newrelation, NoLock); |
228 | 0 | } |
229 | 0 | } |
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 | | */ |
242 | 0 | if (oldrc) |
243 | 0 | { |
244 | 0 | int new_allMarkTypes = oldrc->allMarkTypes; |
245 | 0 | Var *var; |
246 | 0 | TargetEntry *tle; |
247 | 0 | char resname[32]; |
248 | 0 | List *newvars = NIL; |
249 | | |
250 | | /* Add TID junk Var if needed, unless we had it already */ |
251 | 0 | if (new_allMarkTypes & ~(1 << ROW_MARK_COPY) && |
252 | 0 | !(old_allMarkTypes & ~(1 << ROW_MARK_COPY))) |
253 | 0 | { |
254 | | /* Need to fetch TID */ |
255 | 0 | var = makeVar(oldrc->rti, |
256 | 0 | SelfItemPointerAttributeNumber, |
257 | 0 | TIDOID, |
258 | 0 | -1, |
259 | 0 | InvalidOid, |
260 | 0 | 0); |
261 | 0 | snprintf(resname, sizeof(resname), "ctid%u", oldrc->rowmarkId); |
262 | 0 | tle = makeTargetEntry((Expr *) var, |
263 | 0 | list_length(root->processed_tlist) + 1, |
264 | 0 | pstrdup(resname), |
265 | 0 | true); |
266 | 0 | root->processed_tlist = lappend(root->processed_tlist, tle); |
267 | 0 | newvars = lappend(newvars, var); |
268 | 0 | } |
269 | | |
270 | | /* Add whole-row junk Var if needed, unless we had it already */ |
271 | 0 | if ((new_allMarkTypes & (1 << ROW_MARK_COPY)) && |
272 | 0 | !(old_allMarkTypes & (1 << ROW_MARK_COPY))) |
273 | 0 | { |
274 | 0 | var = makeWholeRowVar(planner_rt_fetch(oldrc->rti, root), |
275 | 0 | oldrc->rti, |
276 | 0 | 0, |
277 | 0 | false); |
278 | 0 | snprintf(resname, sizeof(resname), "wholerow%u", oldrc->rowmarkId); |
279 | 0 | tle = makeTargetEntry((Expr *) var, |
280 | 0 | list_length(root->processed_tlist) + 1, |
281 | 0 | pstrdup(resname), |
282 | 0 | true); |
283 | 0 | root->processed_tlist = lappend(root->processed_tlist, tle); |
284 | 0 | newvars = lappend(newvars, var); |
285 | 0 | } |
286 | | |
287 | | /* Add tableoid junk Var, unless we had it already */ |
288 | 0 | if (!old_isParent) |
289 | 0 | { |
290 | 0 | var = makeVar(oldrc->rti, |
291 | 0 | TableOidAttributeNumber, |
292 | 0 | OIDOID, |
293 | 0 | -1, |
294 | 0 | InvalidOid, |
295 | 0 | 0); |
296 | 0 | snprintf(resname, sizeof(resname), "tableoid%u", oldrc->rowmarkId); |
297 | 0 | tle = makeTargetEntry((Expr *) var, |
298 | 0 | list_length(root->processed_tlist) + 1, |
299 | 0 | pstrdup(resname), |
300 | 0 | true); |
301 | 0 | root->processed_tlist = lappend(root->processed_tlist, tle); |
302 | 0 | newvars = lappend(newvars, var); |
303 | 0 | } |
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 | | */ |
309 | 0 | add_vars_to_targetlist(root, newvars, bms_make_singleton(0)); |
310 | 0 | } |
311 | |
|
312 | 0 | table_close(oldrelation, NoLock); |
313 | 0 | } |
314 | | |
315 | | /* |
316 | | * expand_partitioned_rtentry |
317 | | * Recursively expand an RTE for a partitioned table. |
318 | | */ |
319 | | static void |
320 | | 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 | 0 | { |
326 | 0 | PartitionDesc partdesc; |
327 | 0 | int num_live_parts; |
328 | 0 | int i; |
329 | |
|
330 | 0 | check_stack_depth(); |
331 | |
|
332 | 0 | Assert(parentrte->inh); |
333 | |
|
334 | 0 | partdesc = PartitionDirectoryLookup(root->glob->partition_directory, |
335 | 0 | parentrel); |
336 | | |
337 | | /* A partitioned table should always have a partition descriptor. */ |
338 | 0 | Assert(partdesc); |
339 | | |
340 | | /* Nothing further to do here if there are no partitions. */ |
341 | 0 | if (partdesc->nparts == 0) |
342 | 0 | 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 | | */ |
350 | 0 | relinfo->live_parts = prune_append_rel_partitions(relinfo); |
351 | | |
352 | | /* Expand simple_rel_array and friends to hold child objects. */ |
353 | 0 | num_live_parts = bms_num_members(relinfo->live_parts); |
354 | 0 | if (num_live_parts > 0) |
355 | 0 | 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 | 0 | Assert(relinfo->part_rels == NULL); |
363 | 0 | relinfo->part_rels = (RelOptInfo **) |
364 | 0 | 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 | 0 | i = -1; |
372 | 0 | while ((i = bms_next_member(relinfo->live_parts, i)) >= 0) |
373 | 0 | { |
374 | 0 | Oid childOID = partdesc->oids[i]; |
375 | 0 | Relation childrel; |
376 | 0 | RangeTblEntry *childrte; |
377 | 0 | Index childRTindex; |
378 | 0 | 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 | | */ |
385 | 0 | childrel = try_table_open(childOID, lockmode); |
386 | 0 | if (childrel == NULL) |
387 | 0 | { |
388 | 0 | relinfo->live_parts = bms_del_member(relinfo->live_parts, i); |
389 | 0 | continue; |
390 | 0 | } |
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 | | */ |
397 | 0 | if (RELATION_IS_OTHER_TEMP(childrel)) |
398 | 0 | elog(ERROR, "temporary relation from another session found as partition"); |
399 | | |
400 | | /* Create RTE and AppendRelInfo, plus PlanRowMark if needed. */ |
401 | 0 | expand_single_inheritance_child(root, parentrte, parentRTindex, |
402 | 0 | parentrel, top_parentrc, childrel, |
403 | 0 | &childrte, &childRTindex); |
404 | | |
405 | | /* Create the otherrel RelOptInfo too. */ |
406 | 0 | childrelinfo = build_simple_rel(root, childRTindex, relinfo); |
407 | 0 | relinfo->part_rels[i] = childrelinfo; |
408 | 0 | relinfo->all_partrels = bms_add_members(relinfo->all_partrels, |
409 | 0 | childrelinfo->relids); |
410 | | |
411 | | /* If this child is itself partitioned, recurse */ |
412 | 0 | if (childrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) |
413 | 0 | { |
414 | 0 | AppendRelInfo *appinfo = root->append_rel_array[childRTindex]; |
415 | 0 | Bitmapset *child_updatedCols; |
416 | |
|
417 | 0 | child_updatedCols = translate_col_privs(parent_updatedCols, |
418 | 0 | appinfo->translated_vars); |
419 | |
|
420 | 0 | expand_partitioned_rtentry(root, childrelinfo, |
421 | 0 | childrte, childRTindex, |
422 | 0 | childrel, |
423 | 0 | child_updatedCols, |
424 | 0 | top_parentrc, lockmode); |
425 | 0 | } |
426 | | |
427 | | /* Close child relation, but keep locks */ |
428 | 0 | table_close(childrel, NoLock); |
429 | 0 | } |
430 | 0 | } |
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 |
453 | | 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 | 0 | { |
459 | 0 | Query *parse = root->parse; |
460 | 0 | Oid parentOID = RelationGetRelid(parentrel); |
461 | 0 | Oid childOID = RelationGetRelid(childrel); |
462 | 0 | RangeTblEntry *childrte; |
463 | 0 | Index childRTindex; |
464 | 0 | AppendRelInfo *appinfo; |
465 | 0 | TupleDesc child_tupdesc; |
466 | 0 | List *parent_colnames; |
467 | 0 | 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 | | */ |
482 | 0 | childrte = makeNode(RangeTblEntry); |
483 | 0 | memcpy(childrte, parentrte, sizeof(RangeTblEntry)); |
484 | 0 | Assert(parentrte->rtekind == RTE_RELATION); /* else this is dubious */ |
485 | 0 | childrte->relid = childOID; |
486 | 0 | childrte->relkind = childrel->rd_rel->relkind; |
487 | | /* A partitioned child will need to be expanded further. */ |
488 | 0 | if (childrte->relkind == RELKIND_PARTITIONED_TABLE) |
489 | 0 | { |
490 | 0 | Assert(childOID != parentOID); |
491 | 0 | childrte->inh = true; |
492 | 0 | } |
493 | 0 | else |
494 | 0 | childrte->inh = false; |
495 | 0 | childrte->securityQuals = NIL; |
496 | | |
497 | | /* No permission checking for child RTEs. */ |
498 | 0 | childrte->perminfoindex = 0; |
499 | | |
500 | | /* Link not-yet-fully-filled child RTE into data structures */ |
501 | 0 | parse->rtable = lappend(parse->rtable, childrte); |
502 | 0 | childRTindex = list_length(parse->rtable); |
503 | 0 | *childrte_p = childrte; |
504 | 0 | *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 | | */ |
510 | 0 | if (childOID != parentOID) |
511 | 0 | get_relation_notnullatts(root, childrel); |
512 | | |
513 | | /* |
514 | | * Build an AppendRelInfo struct for each parent/child pair. |
515 | | */ |
516 | 0 | appinfo = make_append_rel_info(parentrel, childrel, |
517 | 0 | parentRTindex, childRTindex); |
518 | 0 | root->append_rel_list = lappend(root->append_rel_list, appinfo); |
519 | | |
520 | | /* tablesample is probably null, but copy it */ |
521 | 0 | 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 | 0 | child_tupdesc = RelationGetDescr(childrel); |
532 | 0 | parent_colnames = parentrte->eref->colnames; |
533 | 0 | child_colnames = NIL; |
534 | 0 | for (int cattno = 0; cattno < child_tupdesc->natts; cattno++) |
535 | 0 | { |
536 | 0 | Form_pg_attribute att = TupleDescAttr(child_tupdesc, cattno); |
537 | 0 | const char *attname; |
538 | |
|
539 | 0 | if (att->attisdropped) |
540 | 0 | { |
541 | | /* Always insert an empty string for a dropped column */ |
542 | 0 | attname = ""; |
543 | 0 | } |
544 | 0 | else if (appinfo->parent_colnos[cattno] > 0 && |
545 | 0 | appinfo->parent_colnos[cattno] <= list_length(parent_colnames)) |
546 | 0 | { |
547 | | /* Duplicate the query-assigned name for the parent column */ |
548 | 0 | attname = strVal(list_nth(parent_colnames, |
549 | 0 | appinfo->parent_colnos[cattno] - 1)); |
550 | 0 | } |
551 | 0 | else |
552 | 0 | { |
553 | | /* New column, just use its real name */ |
554 | 0 | attname = NameStr(att->attname); |
555 | 0 | } |
556 | 0 | child_colnames = lappend(child_colnames, makeString(pstrdup(attname))); |
557 | 0 | } |
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 | 0 | childrte->alias = childrte->eref = makeAlias(parentrte->eref->aliasname, |
565 | 0 | 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 | | */ |
571 | 0 | Assert(childRTindex < root->simple_rel_array_size); |
572 | 0 | Assert(root->simple_rte_array[childRTindex] == NULL); |
573 | 0 | root->simple_rte_array[childRTindex] = childrte; |
574 | 0 | Assert(root->append_rel_array[childRTindex] == NULL); |
575 | 0 | root->append_rel_array[childRTindex] = appinfo; |
576 | | |
577 | | /* |
578 | | * Build a PlanRowMark if parent is marked FOR UPDATE/SHARE. |
579 | | */ |
580 | 0 | if (top_parentrc) |
581 | 0 | { |
582 | 0 | PlanRowMark *childrc = makeNode(PlanRowMark); |
583 | |
|
584 | 0 | childrc->rti = childRTindex; |
585 | 0 | childrc->prti = top_parentrc->rti; |
586 | 0 | childrc->rowmarkId = top_parentrc->rowmarkId; |
587 | | /* Reselect rowmark type, because relkind might not match parent */ |
588 | 0 | childrc->markType = select_rowmark_type(childrte, |
589 | 0 | top_parentrc->strength); |
590 | 0 | childrc->allMarkTypes = (1 << childrc->markType); |
591 | 0 | childrc->strength = top_parentrc->strength; |
592 | 0 | 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 | 0 | childrc->isParent = (childrte->relkind == RELKIND_PARTITIONED_TABLE); |
600 | | |
601 | | /* Include child's rowmark type in top parent's allMarkTypes */ |
602 | 0 | top_parentrc->allMarkTypes |= childrc->allMarkTypes; |
603 | |
|
604 | 0 | root->rowMarks = lappend(root->rowMarks, childrc); |
605 | 0 | } |
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 | | */ |
613 | 0 | if (bms_is_member(parentRTindex, root->all_result_relids)) |
614 | 0 | { |
615 | | /* OK, record the child as a result rel too. */ |
616 | 0 | root->all_result_relids = bms_add_member(root->all_result_relids, |
617 | 0 | childRTindex); |
618 | | |
619 | | /* Non-leaf partitions don't need any row identity info. */ |
620 | 0 | if (childrte->relkind != RELKIND_PARTITIONED_TABLE) |
621 | 0 | { |
622 | 0 | Var *rrvar; |
623 | |
|
624 | 0 | root->leaf_result_relids = bms_add_member(root->leaf_result_relids, |
625 | 0 | 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 | 0 | rrvar = makeVar(childRTindex, |
634 | 0 | TableOidAttributeNumber, |
635 | 0 | OIDOID, |
636 | 0 | -1, |
637 | 0 | InvalidOid, |
638 | 0 | 0); |
639 | 0 | add_row_identity_var(root, rrvar, childRTindex, "tableoid"); |
640 | | |
641 | | /* Register any row-identity columns needed by this child. */ |
642 | 0 | add_row_identity_columns(root, childRTindex, |
643 | 0 | childrte, childrel); |
644 | 0 | } |
645 | 0 | } |
646 | 0 | } |
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 * |
654 | | get_rel_all_updated_cols(PlannerInfo *root, RelOptInfo *rel) |
655 | 0 | { |
656 | 0 | Index relid; |
657 | 0 | RangeTblEntry *rte; |
658 | 0 | RTEPermissionInfo *perminfo; |
659 | 0 | Bitmapset *updatedCols, |
660 | 0 | *extraUpdatedCols; |
661 | |
|
662 | 0 | Assert(root->parse->commandType == CMD_UPDATE); |
663 | 0 | 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 | 0 | relid = root->parse->resultRelation; |
671 | 0 | rte = planner_rt_fetch(relid, root); |
672 | 0 | perminfo = getRTEPermissionInfo(root->parse->rteperminfos, rte); |
673 | |
|
674 | 0 | updatedCols = perminfo->updatedCols; |
675 | |
|
676 | 0 | if (rel->relid != relid) |
677 | 0 | { |
678 | 0 | RelOptInfo *top_parent_rel = find_base_rel(root, relid); |
679 | |
|
680 | 0 | Assert(IS_OTHER_REL(rel)); |
681 | |
|
682 | 0 | updatedCols = translate_col_privs_multilevel(root, rel, top_parent_rel, |
683 | 0 | updatedCols); |
684 | 0 | } |
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 | | */ |
690 | 0 | extraUpdatedCols = get_dependent_generated_columns(root, rel->relid, |
691 | 0 | updatedCols); |
692 | |
|
693 | 0 | return bms_union(updatedCols, extraUpdatedCols); |
694 | 0 | } |
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 * |
708 | | translate_col_privs(const Bitmapset *parent_privs, |
709 | | List *translated_vars) |
710 | 0 | { |
711 | 0 | Bitmapset *child_privs = NULL; |
712 | 0 | bool whole_row; |
713 | 0 | int attno; |
714 | 0 | ListCell *lc; |
715 | | |
716 | | /* System attributes have the same numbers in all tables */ |
717 | 0 | for (attno = FirstLowInvalidHeapAttributeNumber + 1; attno < 0; attno++) |
718 | 0 | { |
719 | 0 | if (bms_is_member(attno - FirstLowInvalidHeapAttributeNumber, |
720 | 0 | parent_privs)) |
721 | 0 | child_privs = bms_add_member(child_privs, |
722 | 0 | attno - FirstLowInvalidHeapAttributeNumber); |
723 | 0 | } |
724 | | |
725 | | /* Check if parent has whole-row reference */ |
726 | 0 | whole_row = bms_is_member(InvalidAttrNumber - FirstLowInvalidHeapAttributeNumber, |
727 | 0 | parent_privs); |
728 | | |
729 | | /* And now translate the regular user attributes, using the vars list */ |
730 | 0 | attno = InvalidAttrNumber; |
731 | 0 | foreach(lc, translated_vars) |
732 | 0 | { |
733 | 0 | Var *var = lfirst_node(Var, lc); |
734 | |
|
735 | 0 | attno++; |
736 | 0 | if (var == NULL) /* ignore dropped columns */ |
737 | 0 | continue; |
738 | 0 | if (whole_row || |
739 | 0 | bms_is_member(attno - FirstLowInvalidHeapAttributeNumber, |
740 | 0 | parent_privs)) |
741 | 0 | child_privs = bms_add_member(child_privs, |
742 | 0 | var->varattno - FirstLowInvalidHeapAttributeNumber); |
743 | 0 | } |
744 | |
|
745 | 0 | return child_privs; |
746 | 0 | } |
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 * |
758 | | translate_col_privs_multilevel(PlannerInfo *root, RelOptInfo *rel, |
759 | | RelOptInfo *parent_rel, |
760 | | Bitmapset *parent_cols) |
761 | 0 | { |
762 | 0 | AppendRelInfo *appinfo; |
763 | | |
764 | | /* Fast path for easy case. */ |
765 | 0 | if (parent_cols == NULL) |
766 | 0 | return NULL; |
767 | | |
768 | | /* Recurse if immediate parent is not the top parent. */ |
769 | 0 | if (rel->parent != parent_rel) |
770 | 0 | { |
771 | 0 | if (rel->parent) |
772 | 0 | parent_cols = translate_col_privs_multilevel(root, rel->parent, |
773 | 0 | parent_rel, |
774 | 0 | parent_cols); |
775 | 0 | else |
776 | 0 | elog(ERROR, "rel with relid %u is not a child rel", rel->relid); |
777 | 0 | } |
778 | | |
779 | | /* Now translate for this child. */ |
780 | 0 | Assert(root->append_rel_array != NULL); |
781 | 0 | appinfo = root->append_rel_array[rel->relid]; |
782 | 0 | Assert(appinfo != NULL); |
783 | |
|
784 | 0 | return translate_col_privs(parent_cols, appinfo->translated_vars); |
785 | 0 | } |
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 |
797 | | expand_appendrel_subquery(PlannerInfo *root, RelOptInfo *rel, |
798 | | RangeTblEntry *rte, Index rti) |
799 | 0 | { |
800 | 0 | ListCell *l; |
801 | |
|
802 | 0 | foreach(l, root->append_rel_list) |
803 | 0 | { |
804 | 0 | AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l); |
805 | 0 | Index childRTindex = appinfo->child_relid; |
806 | 0 | RangeTblEntry *childrte; |
807 | 0 | RelOptInfo *childrel; |
808 | | |
809 | | /* append_rel_list contains all append rels; ignore others */ |
810 | 0 | if (appinfo->parent_relid != rti) |
811 | 0 | continue; |
812 | | |
813 | | /* find the child RTE, which should already exist */ |
814 | 0 | Assert(childRTindex < root->simple_rel_array_size); |
815 | 0 | childrte = root->simple_rte_array[childRTindex]; |
816 | 0 | Assert(childrte != NULL); |
817 | | |
818 | | /* Build the child RelOptInfo. */ |
819 | 0 | childrel = build_simple_rel(root, childRTindex, rel); |
820 | | |
821 | | /* Child may itself be an inherited rel, either table or subquery. */ |
822 | 0 | if (childrte->inh) |
823 | 0 | expand_inherited_rtentry(root, childrel, childrte, childRTindex); |
824 | 0 | } |
825 | 0 | } |
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 | | apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel, |
840 | | RelOptInfo *childrel, RangeTblEntry *childRTE, |
841 | | AppendRelInfo *appinfo) |
842 | 0 | { |
843 | 0 | List *childquals; |
844 | 0 | Index cq_min_security; |
845 | 0 | 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 | 0 | childquals = NIL; |
856 | 0 | cq_min_security = UINT_MAX; |
857 | 0 | foreach(lc, parentrel->baserestrictinfo) |
858 | 0 | { |
859 | 0 | RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc); |
860 | 0 | Node *childqual; |
861 | 0 | ListCell *lc2; |
862 | |
|
863 | 0 | Assert(IsA(rinfo, RestrictInfo)); |
864 | 0 | childqual = adjust_appendrel_attrs(root, |
865 | 0 | (Node *) rinfo->clause, |
866 | 0 | 1, &appinfo); |
867 | 0 | childqual = eval_const_expressions(root, childqual); |
868 | | /* check for flat-out constant */ |
869 | 0 | if (childqual && IsA(childqual, Const)) |
870 | 0 | { |
871 | 0 | if (((Const *) childqual)->constisnull || |
872 | 0 | !DatumGetBool(((Const *) childqual)->constvalue)) |
873 | 0 | { |
874 | | /* Restriction reduces to constant FALSE or NULL */ |
875 | 0 | return false; |
876 | 0 | } |
877 | | /* Restriction reduces to constant TRUE, so drop it */ |
878 | 0 | continue; |
879 | 0 | } |
880 | | /* might have gotten an AND clause, if so flatten it */ |
881 | 0 | foreach(lc2, make_ands_implicit((Expr *) childqual)) |
882 | 0 | { |
883 | 0 | Node *onecq = (Node *) lfirst(lc2); |
884 | 0 | bool pseudoconstant; |
885 | 0 | RestrictInfo *childrinfo; |
886 | | |
887 | | /* check for pseudoconstant (no Vars or volatile functions) */ |
888 | 0 | pseudoconstant = |
889 | 0 | !contain_vars_of_level(onecq, 0) && |
890 | 0 | !contain_volatile_functions(onecq); |
891 | 0 | if (pseudoconstant) |
892 | 0 | { |
893 | | /* tell createplan.c to check for gating quals */ |
894 | 0 | root->hasPseudoConstantQuals = true; |
895 | 0 | } |
896 | | /* reconstitute RestrictInfo with appropriate properties */ |
897 | 0 | childrinfo = make_restrictinfo(root, |
898 | 0 | (Expr *) onecq, |
899 | 0 | rinfo->is_pushed_down, |
900 | 0 | rinfo->has_clone, |
901 | 0 | rinfo->is_clone, |
902 | 0 | pseudoconstant, |
903 | 0 | rinfo->security_level, |
904 | 0 | NULL, NULL, NULL); |
905 | |
|
906 | 0 | childquals = lappend(childquals, childrinfo); |
907 | | /* track minimum security level among child quals */ |
908 | 0 | cq_min_security = Min(cq_min_security, childrinfo->security_level); |
909 | 0 | } |
910 | 0 | } |
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 | | */ |
923 | 0 | if (childRTE->securityQuals) |
924 | 0 | { |
925 | 0 | Index security_level = 0; |
926 | |
|
927 | 0 | foreach(lc, childRTE->securityQuals) |
928 | 0 | { |
929 | 0 | List *qualset = (List *) lfirst(lc); |
930 | 0 | ListCell *lc2; |
931 | |
|
932 | 0 | foreach(lc2, qualset) |
933 | 0 | { |
934 | 0 | Expr *qual = (Expr *) lfirst(lc2); |
935 | | |
936 | | /* not likely that we'd see constants here, so no check */ |
937 | 0 | childquals = lappend(childquals, |
938 | 0 | make_restrictinfo(root, qual, |
939 | 0 | true, |
940 | 0 | false, false, |
941 | 0 | false, |
942 | 0 | security_level, |
943 | 0 | NULL, NULL, NULL)); |
944 | 0 | cq_min_security = Min(cq_min_security, security_level); |
945 | 0 | } |
946 | 0 | security_level++; |
947 | 0 | } |
948 | 0 | Assert(security_level <= root->qual_security_level); |
949 | 0 | } |
950 | | |
951 | | /* |
952 | | * OK, we've got all the baserestrictinfo quals for this child. |
953 | | */ |
954 | 0 | childrel->baserestrictinfo = childquals; |
955 | 0 | childrel->baserestrict_min_security = cq_min_security; |
956 | |
|
957 | | return true; |
958 | 0 | } |