Coverage Report

Created: 2025-10-09 06:07

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