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