1 /*------------------------------------------------------------------------- 2 * 3 * inherit.c 4 * Routines to process child relations in inheritance trees 5 * 6 * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group 7 * Portions Copyright (c) 1994, Regents of the University of California 8 * 9 * 10 * IDENTIFICATION 11 * src/backend/optimizer/path/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/planmain.h" 29 #include "optimizer/planner.h" 30 #include "optimizer/prep.h" 31 #include "optimizer/restrictinfo.h" 32 #include "parser/parsetree.h" 33 #include "partitioning/partdesc.h" 34 #include "partitioning/partprune.h" 35 #include "utils/rel.h" 36 37 38 /* source-code-compatibility hacks for pull_varnos() API change */ 39 #define make_restrictinfo(a,b,c,d,e,f,g,h,i) make_restrictinfo_new(a,b,c,d,e,f,g,h,i) 40 41 static void expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, 42 RangeTblEntry *parentrte, 43 Index parentRTindex, Relation parentrel, 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 void expand_appendrel_subquery(PlannerInfo *root, RelOptInfo *rel, 54 RangeTblEntry *rte, Index rti); 55 56 57 /* 58 * expand_inherited_rtentry 59 * Expand a rangetable entry that has the "inh" bit set. 60 * 61 * "inh" is only allowed in two cases: RELATION and SUBQUERY RTEs. 62 * 63 * "inh" on a plain RELATION RTE means that it is a partitioned table or the 64 * parent of a traditional-inheritance set. In this case we must add entries 65 * for all the interesting child tables to the query's rangetable, and build 66 * additional planner data structures for them, including RelOptInfos, geqo(PlannerInfo * root,int number_of_rels,List * initial_rels)67 * AppendRelInfos, and possibly PlanRowMarks. 68 * 69 * Note that the original RTE is considered to represent the whole inheritance 70 * set. In the case of traditional inheritance, the first of the generated 71 * RTEs is an RTE for the same table, but with inh = false, to represent the 72 * parent table in its role as a simple member of the inheritance set. For 73 * partitioning, we don't need a second RTE because the partitioned table 74 * itself has no data and need not be scanned. 75 * 76 * "inh" on a SUBQUERY RTE means that it's the parent of a UNION ALL group, 77 * which is treated as an appendrel similarly to inheritance cases; however, 78 * we already made RTEs and AppendRelInfos for the subqueries. We only need 79 * to build RelOptInfos for them, which is done by expand_appendrel_subquery. 80 */ 81 void 82 expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, 83 RangeTblEntry *rte, Index rti) 84 { 85 Oid parentOID; 86 Relation oldrelation; 87 LOCKMODE lockmode; 88 PlanRowMark *oldrc; 89 bool old_isParent = false; 90 int old_allMarkTypes = 0; 91 92 Assert(rte->inh); /* else caller error */ 93 94 if (rte->rtekind == RTE_SUBQUERY) 95 { 96 expand_appendrel_subquery(root, rel, rte, rti); 97 return; 98 } 99 100 Assert(rte->rtekind == RTE_RELATION); 101 102 parentOID = rte->relid; 103 104 /* 105 * We used to check has_subclass() here, but there's no longer any need 106 * to, because subquery_planner already did. 107 */ 108 109 /* 110 * The rewriter should already have obtained an appropriate lock on each 111 * relation named in the query, so we can open the parent relation without 112 * locking it. However, for each child relation we add to the query, we 113 * must obtain an appropriate lock, because this will be the first use of 114 * those relations in the parse/rewrite/plan pipeline. Child rels should 115 * use the same lockmode as their parent. 116 */ 117 oldrelation = table_open(parentOID, NoLock); 118 lockmode = rte->rellockmode; 119 120 /* 121 * If parent relation is selected FOR UPDATE/SHARE, we need to mark its 122 * PlanRowMark as isParent = true, and generate a new PlanRowMark for each 123 * child. 124 */ 125 oldrc = get_plan_rowmark(root->rowMarks, rti); 126 if (oldrc) 127 { 128 old_isParent = oldrc->isParent; 129 oldrc->isParent = true; 130 /* Save initial value of allMarkTypes before children add to it */ 131 old_allMarkTypes = oldrc->allMarkTypes; 132 } 133 134 /* Scan the inheritance set and expand it */ 135 if (oldrelation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) 136 { 137 /* 138 * Partitioned table, so set up for partitioning. 139 */ 140 Assert(rte->relkind == RELKIND_PARTITIONED_TABLE); 141 142 /* 143 * Recursively expand and lock the partitions. While at it, also 144 * extract the partition key columns of all the partitioned tables. 145 */ 146 expand_partitioned_rtentry(root, rel, rte, rti, 147 oldrelation, oldrc, lockmode); 148 } 149 else 150 { 151 /* 152 * Ordinary table, so process traditional-inheritance children. (Note 153 * that partitioned tables are not allowed to have inheritance 154 * children, so it's not possible for both cases to apply.) 155 */ 156 List *inhOIDs; 157 ListCell *l; 158 159 /* Scan for all members of inheritance set, acquire needed locks */ 160 inhOIDs = find_all_inheritors(parentOID, lockmode, NULL); 161 162 /* 163 * We used to special-case the situation where the table no longer has 164 * any children, by clearing rte->inh and exiting. That no longer 165 * works, because this function doesn't get run until after decisions 166 * have been made that depend on rte->inh. We have to treat such 167 * situations as normal inheritance. The table itself should always 168 * have been found, though. 169 */ 170 Assert(inhOIDs != NIL); 171 Assert(linitial_oid(inhOIDs) == parentOID); 172 173 /* Expand simple_rel_array and friends to hold child objects. */ 174 expand_planner_arrays(root, list_length(inhOIDs)); 175 176 /* 177 * Expand inheritance children in the order the OIDs were returned by 178 * find_all_inheritors. 179 */ 180 foreach(l, inhOIDs) 181 { 182 Oid childOID = lfirst_oid(l); 183 Relation newrelation; 184 RangeTblEntry *childrte; 185 Index childRTindex; 186 187 /* Open rel if needed; we already have required locks */ 188 if (childOID != parentOID) 189 newrelation = table_open(childOID, NoLock); 190 else 191 newrelation = oldrelation; 192 193 /* 194 * It is possible that the parent table has children that are temp 195 * tables of other backends. We cannot safely access such tables 196 * (because of buffering issues), and the best thing to do seems 197 * to be to silently ignore them. 198 */ 199 if (childOID != parentOID && RELATION_IS_OTHER_TEMP(newrelation)) 200 { 201 table_close(newrelation, lockmode); 202 continue; 203 } 204 205 /* Create RTE and AppendRelInfo, plus PlanRowMark if needed. */ 206 expand_single_inheritance_child(root, rte, rti, oldrelation, 207 oldrc, newrelation, 208 &childrte, &childRTindex); 209 210 /* Create the otherrel RelOptInfo too. */ 211 (void) build_simple_rel(root, childRTindex, rel); 212 213 /* Close child relations, but keep locks */ 214 if (childOID != parentOID) 215 table_close(newrelation, NoLock); 216 } 217 } 218 219 /* 220 * Some children might require different mark types, which would've been 221 * reported into oldrc. If so, add relevant entries to the top-level 222 * targetlist and update parent rel's reltarget. This should match what 223 * preprocess_targetlist() would have added if the mark types had been 224 * requested originally. 225 */ 226 if (oldrc) 227 { 228 int new_allMarkTypes = oldrc->allMarkTypes; 229 Var *var; 230 TargetEntry *tle; 231 char resname[32]; 232 List *newvars = NIL; 233 234 /* Add TID junk Var if needed, unless we had it already */ 235 if (new_allMarkTypes & ~(1 << ROW_MARK_COPY) && 236 !(old_allMarkTypes & ~(1 << ROW_MARK_COPY))) 237 { 238 /* Need to fetch TID */ 239 var = makeVar(oldrc->rti, 240 SelfItemPointerAttributeNumber, 241 TIDOID, 242 -1, 243 InvalidOid, 244 0); 245 snprintf(resname, sizeof(resname), "ctid%u", oldrc->rowmarkId); 246 tle = makeTargetEntry((Expr *) var, 247 list_length(root->processed_tlist) + 1, 248 pstrdup(resname), 249 true); 250 root->processed_tlist = lappend(root->processed_tlist, tle); 251 newvars = lappend(newvars, var); 252 } 253 254 /* Add whole-row junk Var if needed, unless we had it already */ 255 if ((new_allMarkTypes & (1 << ROW_MARK_COPY)) && 256 !(old_allMarkTypes & (1 << ROW_MARK_COPY))) 257 { 258 var = makeWholeRowVar(planner_rt_fetch(oldrc->rti, root), 259 oldrc->rti, 260 0, 261 false); 262 snprintf(resname, sizeof(resname), "wholerow%u", oldrc->rowmarkId); 263 tle = makeTargetEntry((Expr *) var, 264 list_length(root->processed_tlist) + 1, 265 pstrdup(resname), 266 true); 267 root->processed_tlist = lappend(root->processed_tlist, tle); 268 newvars = lappend(newvars, var); 269 } 270 271 /* Add tableoid junk Var, unless we had it already */ 272 if (!old_isParent) 273 { 274 var = makeVar(oldrc->rti, 275 TableOidAttributeNumber, 276 OIDOID, 277 -1, 278 InvalidOid, 279 0); 280 snprintf(resname, sizeof(resname), "tableoid%u", oldrc->rowmarkId); 281 tle = makeTargetEntry((Expr *) var, 282 list_length(root->processed_tlist) + 1, 283 pstrdup(resname), 284 true); 285 root->processed_tlist = lappend(root->processed_tlist, tle); 286 newvars = lappend(newvars, var); 287 } 288 289 /* 290 * Add the newly added Vars to parent's reltarget. We needn't worry 291 * about the children's reltargets, they'll be made later. 292 */ 293 add_vars_to_targetlist(root, newvars, bms_make_singleton(0), false); 294 } 295 296 table_close(oldrelation, NoLock); 297 } 298 299 /* 300 * expand_partitioned_rtentry 301 * Recursively expand an RTE for a partitioned table. 302 */ 303 static void 304 expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, 305 RangeTblEntry *parentrte, 306 Index parentRTindex, Relation parentrel, 307 PlanRowMark *top_parentrc, LOCKMODE lockmode) 308 { 309 PartitionDesc partdesc; gimme_pool_size(int nr_rel)310 Bitmapset *live_parts; 311 int num_live_parts; 312 int i; 313 314 check_stack_depth(); 315 316 Assert(parentrte->inh); 317 318 partdesc = PartitionDirectoryLookup(root->glob->partition_directory, 319 parentrel); 320 321 /* A partitioned table should always have a partition descriptor. */ 322 Assert(partdesc); 323 324 /* 325 * Note down whether any partition key cols are being updated. Though it's 326 * the root partitioned table's updatedCols we are interested in, we 327 * instead use parentrte to get the updatedCols. This is convenient 328 * because parentrte already has the root partrel's updatedCols translated 329 * to match the attribute ordering of parentrel. 330 */ 331 if (!root->partColsUpdated) 332 root->partColsUpdated = 333 has_partition_attrs(parentrel, parentrte->updatedCols, NULL); 334 335 /* 336 * There shouldn't be any generated columns in the partition key. 337 */ 338 Assert(!has_partition_attrs(parentrel, parentrte->extraUpdatedCols, NULL)); 339 340 /* Nothing further to do here if there are no partitions. */ 341 if (partdesc->nparts == 0) gimme_number_generations(int pool_size)342 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 live_parts = prune_append_rel_partitions(relinfo); 351 352 /* Expand simple_rel_array and friends to hold child objects. */ 353 num_live_parts = bms_num_members(live_parts); 354 if (num_live_parts > 0) 355 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 Assert(relinfo->part_rels == NULL); 363 relinfo->part_rels = (RelOptInfo **) 364 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 i = -1; 372 while ((i = bms_next_member(live_parts, i)) >= 0) 373 { 374 Oid childOID = partdesc->oids[i]; 375 Relation childrel; 376 RangeTblEntry *childrte; 377 Index childRTindex; 378 RelOptInfo *childrelinfo; 379 380 /* Open rel, acquiring required locks */ 381 childrel = table_open(childOID, lockmode); 382 383 /* 384 * Temporary partitions belonging to other sessions should have been 385 * disallowed at definition, but for paranoia's sake, let's double 386 * check. 387 */ 388 if (RELATION_IS_OTHER_TEMP(childrel)) 389 elog(ERROR, "temporary relation from another session found as partition"); 390 391 /* Create RTE and AppendRelInfo, plus PlanRowMark if needed. */ 392 expand_single_inheritance_child(root, parentrte, parentRTindex, 393 parentrel, top_parentrc, childrel, 394 &childrte, &childRTindex); 395 396 /* Create the otherrel RelOptInfo too. */ 397 childrelinfo = build_simple_rel(root, childRTindex, relinfo); 398 relinfo->part_rels[i] = childrelinfo; 399 400 /* If this child is itself partitioned, recurse */ 401 if (childrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) 402 expand_partitioned_rtentry(root, childrelinfo, 403 childrte, childRTindex, 404 childrel, top_parentrc, lockmode); 405 406 /* Close child relation, but keep locks */ 407 table_close(childrel, NoLock); 408 } 409 } 410 411 /* 412 * expand_single_inheritance_child 413 * Build a RangeTblEntry and an AppendRelInfo, plus maybe a PlanRowMark. 414 * 415 * We now expand the partition hierarchy level by level, creating a 416 * corresponding hierarchy of AppendRelInfos and RelOptInfos, where each 417 * partitioned descendant acts as a parent of its immediate partitions. 418 * (This is a difference from what older versions of PostgreSQL did and what 419 * is still done in the case of table inheritance for unpartitioned tables, 420 * where the hierarchy is flattened during RTE expansion.) 421 * 422 * PlanRowMarks still carry the top-parent's RTI, and the top-parent's 423 * allMarkTypes field still accumulates values from all descendents. 424 * 425 * "parentrte" and "parentRTindex" are immediate parent's RTE and 426 * RTI. "top_parentrc" is top parent's PlanRowMark. 427 * 428 * The child RangeTblEntry and its RTI are returned in "childrte_p" and 429 * "childRTindex_p" resp. 430 */ 431 static void 432 expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, 433 Index parentRTindex, Relation parentrel, 434 PlanRowMark *top_parentrc, Relation childrel, 435 RangeTblEntry **childrte_p, 436 Index *childRTindex_p) 437 { 438 Query *parse = root->parse; 439 Oid parentOID = RelationGetRelid(parentrel); 440 Oid childOID = RelationGetRelid(childrel); 441 RangeTblEntry *childrte; 442 Index childRTindex; 443 AppendRelInfo *appinfo; 444 445 /* 446 * Build an RTE for the child, and attach to query's rangetable list. We 447 * copy most fields of the parent's RTE, but replace relation OID, 448 * relkind, and inh for the child. Also, set requiredPerms to zero since 449 * all required permissions checks are done on the original RTE. Likewise, 450 * set the child's securityQuals to empty, because we only want to apply 451 * the parent's RLS conditions regardless of what RLS properties 452 * individual children may have. (This is an intentional choice to make 453 * inherited RLS work like regular permissions checks.) The parent 454 * securityQuals will be propagated to children along with other base 455 * restriction clauses, so we don't need to do it here. 456 */ 457 childrte = copyObject(parentrte); 458 *childrte_p = childrte; 459 childrte->relid = childOID; 460 childrte->relkind = childrel->rd_rel->relkind; 461 /* A partitioned child will need to be expanded further. */ 462 if (childrte->relkind == RELKIND_PARTITIONED_TABLE) 463 { 464 Assert(childOID != parentOID); 465 childrte->inh = true; 466 } 467 else 468 childrte->inh = false; 469 childrte->requiredPerms = 0; 470 childrte->securityQuals = NIL; 471 parse->rtable = lappend(parse->rtable, childrte); 472 childRTindex = list_length(parse->rtable); 473 *childRTindex_p = childRTindex; 474 475 /* 476 * Build an AppendRelInfo struct for each parent/child pair. 477 */ 478 appinfo = make_append_rel_info(parentrel, childrel, 479 parentRTindex, childRTindex); 480 root->append_rel_list = lappend(root->append_rel_list, appinfo); 481 482 /* 483 * Translate the column permissions bitmaps to the child's attnums (we 484 * have to build the translated_vars list before we can do this). But if 485 * this is the parent table, we can leave copyObject's result alone. 486 * 487 * Note: we need to do this even though the executor won't run any 488 * permissions checks on the child RTE. The insertedCols/updatedCols 489 * bitmaps may be examined for trigger-firing purposes. 490 */ 491 if (childOID != parentOID) 492 { 493 childrte->selectedCols = translate_col_privs(parentrte->selectedCols, 494 appinfo->translated_vars); 495 childrte->insertedCols = translate_col_privs(parentrte->insertedCols, 496 appinfo->translated_vars); 497 childrte->updatedCols = translate_col_privs(parentrte->updatedCols, 498 appinfo->translated_vars); 499 childrte->extraUpdatedCols = translate_col_privs(parentrte->extraUpdatedCols, 500 appinfo->translated_vars); 501 } 502 503 /* 504 * Store the RTE and appinfo in the respective PlannerInfo arrays, which 505 * the caller must already have allocated space for. 506 */ 507 Assert(childRTindex < root->simple_rel_array_size); 508 Assert(root->simple_rte_array[childRTindex] == NULL); 509 root->simple_rte_array[childRTindex] = childrte; 510 Assert(root->append_rel_array[childRTindex] == NULL); 511 root->append_rel_array[childRTindex] = appinfo; 512 513 /* 514 * Build a PlanRowMark if parent is marked FOR UPDATE/SHARE. 515 */ 516 if (top_parentrc) 517 { 518 PlanRowMark *childrc = makeNode(PlanRowMark); 519 520 childrc->rti = childRTindex; 521 childrc->prti = top_parentrc->rti; 522 childrc->rowmarkId = top_parentrc->rowmarkId; 523 /* Reselect rowmark type, because relkind might not match parent */ 524 childrc->markType = select_rowmark_type(childrte, 525 top_parentrc->strength); 526 childrc->allMarkTypes = (1 << childrc->markType); 527 childrc->strength = top_parentrc->strength; 528 childrc->waitPolicy = top_parentrc->waitPolicy; 529 530 /* 531 * We mark RowMarks for partitioned child tables as parent RowMarks so 532 * that the executor ignores them (except their existence means that 533 * the child tables will be locked using the appropriate mode). 534 */ 535 childrc->isParent = (childrte->relkind == RELKIND_PARTITIONED_TABLE); 536 537 /* Include child's rowmark type in top parent's allMarkTypes */ 538 top_parentrc->allMarkTypes |= childrc->allMarkTypes; 539 540 root->rowMarks = lappend(root->rowMarks, childrc); 541 } 542 } 543 544 /* 545 * translate_col_privs 546 * Translate a bitmapset representing per-column privileges from the 547 * parent rel's attribute numbering to the child's. 548 * 549 * The only surprise here is that we don't translate a parent whole-row 550 * reference into a child whole-row reference. That would mean requiring 551 * permissions on all child columns, which is overly strict, since the 552 * query is really only going to reference the inherited columns. Instead 553 * we set the per-column bits for all inherited columns. 554 */ 555 static Bitmapset * 556 translate_col_privs(const Bitmapset *parent_privs, 557 List *translated_vars) 558 { 559 Bitmapset *child_privs = NULL; 560 bool whole_row; 561 int attno; 562 ListCell *lc; 563 564 /* System attributes have the same numbers in all tables */ 565 for (attno = FirstLowInvalidHeapAttributeNumber + 1; attno < 0; attno++) 566 { 567 if (bms_is_member(attno - FirstLowInvalidHeapAttributeNumber, 568 parent_privs)) 569 child_privs = bms_add_member(child_privs, 570 attno - FirstLowInvalidHeapAttributeNumber); 571 } 572 573 /* Check if parent has whole-row reference */ 574 whole_row = bms_is_member(InvalidAttrNumber - FirstLowInvalidHeapAttributeNumber, 575 parent_privs); 576 577 /* And now translate the regular user attributes, using the vars list */ 578 attno = InvalidAttrNumber; 579 foreach(lc, translated_vars) 580 { 581 Var *var = lfirst_node(Var, lc); 582 583 attno++; 584 if (var == NULL) /* ignore dropped columns */ 585 continue; 586 if (whole_row || 587 bms_is_member(attno - FirstLowInvalidHeapAttributeNumber, 588 parent_privs)) 589 child_privs = bms_add_member(child_privs, 590 var->varattno - FirstLowInvalidHeapAttributeNumber); 591 } 592 593 return child_privs; 594 } 595 596 /* 597 * expand_appendrel_subquery 598 * Add "other rel" RelOptInfos for the children of an appendrel baserel 599 * 600 * "rel" is a subquery relation that has the rte->inh flag set, meaning it 601 * is a UNION ALL subquery that's been flattened into an appendrel, with 602 * child subqueries listed in root->append_rel_list. We need to build 603 * a RelOptInfo for each child relation so that we can plan scans on them. 604 */ 605 static void 606 expand_appendrel_subquery(PlannerInfo *root, RelOptInfo *rel, 607 RangeTblEntry *rte, Index rti) 608 { 609 ListCell *l; 610 611 foreach(l, root->append_rel_list) 612 { 613 AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l); 614 Index childRTindex = appinfo->child_relid; 615 RangeTblEntry *childrte; 616 RelOptInfo *childrel; 617 618 /* append_rel_list contains all append rels; ignore others */ 619 if (appinfo->parent_relid != rti) 620 continue; 621 622 /* find the child RTE, which should already exist */ 623 Assert(childRTindex < root->simple_rel_array_size); 624 childrte = root->simple_rte_array[childRTindex]; 625 Assert(childrte != NULL); 626 627 /* Build the child RelOptInfo. */ 628 childrel = build_simple_rel(root, childRTindex, rel); 629 630 /* Child may itself be an inherited rel, either table or subquery. */ 631 if (childrte->inh) 632 expand_inherited_rtentry(root, childrel, childrte, childRTindex); 633 } 634 } 635 636 637 /* 638 * apply_child_basequals 639 * Populate childrel's base restriction quals from parent rel's quals, 640 * translating them using appinfo. 641 * 642 * If any of the resulting clauses evaluate to constant false or NULL, we 643 * return false and don't apply any quals. Caller should mark the relation as 644 * a dummy rel in this case, since it doesn't need to be scanned. 645 */ 646 bool 647 apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel, 648 RelOptInfo *childrel, RangeTblEntry *childRTE, 649 AppendRelInfo *appinfo) 650 { 651 List *childquals; 652 Index cq_min_security; 653 ListCell *lc; 654 655 /* 656 * The child rel's targetlist might contain non-Var expressions, which 657 * means that substitution into the quals could produce opportunities for 658 * const-simplification, and perhaps even pseudoconstant quals. Therefore, 659 * transform each RestrictInfo separately to see if it reduces to a 660 * constant or pseudoconstant. (We must process them separately to keep 661 * track of the security level of each qual.) 662 */ 663 childquals = NIL; 664 cq_min_security = UINT_MAX; 665 foreach(lc, parentrel->baserestrictinfo) 666 { 667 RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc); 668 Node *childqual; 669 ListCell *lc2; 670 671 Assert(IsA(rinfo, RestrictInfo)); 672 childqual = adjust_appendrel_attrs(root, 673 (Node *) rinfo->clause, 674 1, &appinfo); 675 childqual = eval_const_expressions(root, childqual); 676 /* check for flat-out constant */ 677 if (childqual && IsA(childqual, Const)) 678 { 679 if (((Const *) childqual)->constisnull || 680 !DatumGetBool(((Const *) childqual)->constvalue)) 681 { 682 /* Restriction reduces to constant FALSE or NULL */ 683 return false; 684 } 685 /* Restriction reduces to constant TRUE, so drop it */ 686 continue; 687 } 688 /* might have gotten an AND clause, if so flatten it */ 689 foreach(lc2, make_ands_implicit((Expr *) childqual)) 690 { 691 Node *onecq = (Node *) lfirst(lc2); 692 bool pseudoconstant; 693 694 /* check for pseudoconstant (no Vars or volatile functions) */ 695 pseudoconstant = 696 !contain_vars_of_level(onecq, 0) && 697 !contain_volatile_functions(onecq); 698 if (pseudoconstant) 699 { 700 /* tell createplan.c to check for gating quals */ 701 root->hasPseudoConstantQuals = true; 702 } 703 /* reconstitute RestrictInfo with appropriate properties */ 704 childquals = lappend(childquals, 705 make_restrictinfo(root, 706 (Expr *) onecq, 707 rinfo->is_pushed_down, 708 rinfo->outerjoin_delayed, 709 pseudoconstant, 710 rinfo->security_level, 711 NULL, NULL, NULL)); 712 /* track minimum security level among child quals */ 713 cq_min_security = Min(cq_min_security, rinfo->security_level); 714 } 715 } 716 717 /* 718 * In addition to the quals inherited from the parent, we might have 719 * securityQuals associated with this particular child node. (Currently 720 * this can only happen in appendrels originating from UNION ALL; 721 * inheritance child tables don't have their own securityQuals, see 722 * expand_single_inheritance_child().) Pull any such securityQuals up 723 * into the baserestrictinfo for the child. This is similar to 724 * process_security_barrier_quals() for the parent rel, except that we 725 * can't make any general deductions from such quals, since they don't 726 * hold for the whole appendrel. 727 */ 728 if (childRTE->securityQuals) 729 { 730 Index security_level = 0; 731 732 foreach(lc, childRTE->securityQuals) 733 { 734 List *qualset = (List *) lfirst(lc); 735 ListCell *lc2; 736 737 foreach(lc2, qualset) 738 { 739 Expr *qual = (Expr *) lfirst(lc2); 740 741 /* not likely that we'd see constants here, so no check */ 742 childquals = lappend(childquals, 743 make_restrictinfo(root, qual, 744 true, false, false, 745 security_level, 746 NULL, NULL, NULL)); 747 cq_min_security = Min(cq_min_security, security_level); 748 } 749 security_level++; 750 } 751 Assert(security_level <= root->qual_security_level); 752 } 753 754 /* 755 * OK, we've got all the baserestrictinfo quals for this child. 756 */ 757 childrel->baserestrictinfo = childquals; 758 childrel->baserestrict_min_security = cq_min_security; 759 760 return true; 761 } 762