/*------------------------------------------------------------------------- * * inherit.c * Routines to process child relations in inheritance trees * * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * src/backend/optimizer/path/inherit.c * *------------------------------------------------------------------------- */ #include "postgres.h" #include "access/sysattr.h" #include "access/table.h" #include "catalog/partition.h" #include "catalog/pg_inherits.h" #include "catalog/pg_type.h" #include "miscadmin.h" #include "nodes/makefuncs.h" #include "optimizer/appendinfo.h" #include "optimizer/inherit.h" #include "optimizer/optimizer.h" #include "optimizer/pathnode.h" #include "optimizer/planmain.h" #include "optimizer/planner.h" #include "optimizer/prep.h" #include "optimizer/restrictinfo.h" #include "parser/parsetree.h" #include "partitioning/partdesc.h" #include "partitioning/partprune.h" #include "utils/rel.h" static void expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, PlanRowMark *top_parentrc, LOCKMODE lockmode); static void expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, PlanRowMark *top_parentrc, Relation childrel, RangeTblEntry **childrte_p, Index *childRTindex_p); static Bitmapset *translate_col_privs(const Bitmapset *parent_privs, List *translated_vars); static void expand_appendrel_subquery(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte, Index rti); /* * expand_inherited_rtentry * Expand a rangetable entry that has the "inh" bit set. * * "inh" is only allowed in two cases: RELATION and SUBQUERY RTEs. * * "inh" on a plain RELATION RTE means that it is a partitioned table or the * parent of a traditional-inheritance set. In this case we must add entries * for all the interesting child tables to the query's rangetable, and build * additional planner data structures for them, including RelOptInfos, * AppendRelInfos, and possibly PlanRowMarks. * * Note that the original RTE is considered to represent the whole inheritance * set. In the case of traditional inheritance, the first of the generated * RTEs is an RTE for the same table, but with inh = false, to represent the * parent table in its role as a simple member of the inheritance set. For * partitioning, we don't need a second RTE because the partitioned table * itself has no data and need not be scanned. * * "inh" on a SUBQUERY RTE means that it's the parent of a UNION ALL group, * which is treated as an appendrel similarly to inheritance cases; however, * we already made RTEs and AppendRelInfos for the subqueries. We only need * to build RelOptInfos for them, which is done by expand_appendrel_subquery. */ void expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte, Index rti) { Oid parentOID; Relation oldrelation; LOCKMODE lockmode; PlanRowMark *oldrc; bool old_isParent = false; int old_allMarkTypes = 0; Assert(rte->inh); /* else caller error */ if (rte->rtekind == RTE_SUBQUERY) { expand_appendrel_subquery(root, rel, rte, rti); return; } Assert(rte->rtekind == RTE_RELATION); parentOID = rte->relid; /* * We used to check has_subclass() here, but there's no longer any need * to, because subquery_planner already did. */ /* * The rewriter should already have obtained an appropriate lock on each * relation named in the query, so we can open the parent relation without * locking it. However, for each child relation we add to the query, we * must obtain an appropriate lock, because this will be the first use of * those relations in the parse/rewrite/plan pipeline. Child rels should * use the same lockmode as their parent. */ oldrelation = table_open(parentOID, NoLock); lockmode = rte->rellockmode; /* * If parent relation is selected FOR UPDATE/SHARE, we need to mark its * PlanRowMark as isParent = true, and generate a new PlanRowMark for each * child. */ oldrc = get_plan_rowmark(root->rowMarks, rti); if (oldrc) { old_isParent = oldrc->isParent; oldrc->isParent = true; /* Save initial value of allMarkTypes before children add to it */ old_allMarkTypes = oldrc->allMarkTypes; } /* Scan the inheritance set and expand it */ if (oldrelation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) { /* * Partitioned table, so set up for partitioning. */ Assert(rte->relkind == RELKIND_PARTITIONED_TABLE); /* * Recursively expand and lock the partitions. While at it, also * extract the partition key columns of all the partitioned tables. */ expand_partitioned_rtentry(root, rel, rte, rti, oldrelation, oldrc, lockmode); } else { /* * Ordinary table, so process traditional-inheritance children. (Note * that partitioned tables are not allowed to have inheritance * children, so it's not possible for both cases to apply.) */ List *inhOIDs; ListCell *l; /* Scan for all members of inheritance set, acquire needed locks */ inhOIDs = find_all_inheritors(parentOID, lockmode, NULL); /* * We used to special-case the situation where the table no longer has * any children, by clearing rte->inh and exiting. That no longer * works, because this function doesn't get run until after decisions * have been made that depend on rte->inh. We have to treat such * situations as normal inheritance. The table itself should always * have been found, though. */ Assert(inhOIDs != NIL); Assert(linitial_oid(inhOIDs) == parentOID); /* Expand simple_rel_array and friends to hold child objects. */ expand_planner_arrays(root, list_length(inhOIDs)); /* * Expand inheritance children in the order the OIDs were returned by * find_all_inheritors. */ foreach(l, inhOIDs) { Oid childOID = lfirst_oid(l); Relation newrelation; RangeTblEntry *childrte; Index childRTindex; /* Open rel if needed; we already have required locks */ if (childOID != parentOID) newrelation = table_open(childOID, NoLock); else newrelation = oldrelation; /* * It is possible that the parent table has children that are temp * tables of other backends. We cannot safely access such tables * (because of buffering issues), and the best thing to do seems * to be to silently ignore them. */ if (childOID != parentOID && RELATION_IS_OTHER_TEMP(newrelation)) { table_close(newrelation, lockmode); continue; } /* Create RTE and AppendRelInfo, plus PlanRowMark if needed. */ expand_single_inheritance_child(root, rte, rti, oldrelation, oldrc, newrelation, &childrte, &childRTindex); /* Create the otherrel RelOptInfo too. */ (void) build_simple_rel(root, childRTindex, rel); /* Close child relations, but keep locks */ if (childOID != parentOID) table_close(newrelation, NoLock); } } /* * Some children might require different mark types, which would've been * reported into oldrc. If so, add relevant entries to the top-level * targetlist and update parent rel's reltarget. This should match what * preprocess_targetlist() would have added if the mark types had been * requested originally. * * (Someday it might be useful to fold these resjunk columns into the * row-identity-column management used for UPDATE/DELETE. Today is not * that day, however.) */ if (oldrc) { int new_allMarkTypes = oldrc->allMarkTypes; Var *var; TargetEntry *tle; char resname[32]; List *newvars = NIL; /* Add TID junk Var if needed, unless we had it already */ if (new_allMarkTypes & ~(1 << ROW_MARK_COPY) && !(old_allMarkTypes & ~(1 << ROW_MARK_COPY))) { /* Need to fetch TID */ var = makeVar(oldrc->rti, SelfItemPointerAttributeNumber, TIDOID, -1, InvalidOid, 0); snprintf(resname, sizeof(resname), "ctid%u", oldrc->rowmarkId); tle = makeTargetEntry((Expr *) var, list_length(root->processed_tlist) + 1, pstrdup(resname), true); root->processed_tlist = lappend(root->processed_tlist, tle); newvars = lappend(newvars, var); } /* Add whole-row junk Var if needed, unless we had it already */ if ((new_allMarkTypes & (1 << ROW_MARK_COPY)) && !(old_allMarkTypes & (1 << ROW_MARK_COPY))) { var = makeWholeRowVar(planner_rt_fetch(oldrc->rti, root), oldrc->rti, 0, false); snprintf(resname, sizeof(resname), "wholerow%u", oldrc->rowmarkId); tle = makeTargetEntry((Expr *) var, list_length(root->processed_tlist) + 1, pstrdup(resname), true); root->processed_tlist = lappend(root->processed_tlist, tle); newvars = lappend(newvars, var); } /* Add tableoid junk Var, unless we had it already */ if (!old_isParent) { var = makeVar(oldrc->rti, TableOidAttributeNumber, OIDOID, -1, InvalidOid, 0); snprintf(resname, sizeof(resname), "tableoid%u", oldrc->rowmarkId); tle = makeTargetEntry((Expr *) var, list_length(root->processed_tlist) + 1, pstrdup(resname), true); root->processed_tlist = lappend(root->processed_tlist, tle); newvars = lappend(newvars, var); } /* * Add the newly added Vars to parent's reltarget. We needn't worry * about the children's reltargets, they'll be made later. */ add_vars_to_targetlist(root, newvars, bms_make_singleton(0), false); } table_close(oldrelation, NoLock); } /* * expand_partitioned_rtentry * Recursively expand an RTE for a partitioned table. */ static void expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, PlanRowMark *top_parentrc, LOCKMODE lockmode) { PartitionDesc partdesc; Bitmapset *live_parts; int num_live_parts; int i; check_stack_depth(); Assert(parentrte->inh); partdesc = PartitionDirectoryLookup(root->glob->partition_directory, parentrel); /* A partitioned table should always have a partition descriptor. */ Assert(partdesc); /* * Note down whether any partition key cols are being updated. Though it's * the root partitioned table's updatedCols we are interested in, we * instead use parentrte to get the updatedCols. This is convenient * because parentrte already has the root partrel's updatedCols translated * to match the attribute ordering of parentrel. */ if (!root->partColsUpdated) root->partColsUpdated = has_partition_attrs(parentrel, parentrte->updatedCols, NULL); /* * There shouldn't be any generated columns in the partition key. */ Assert(!has_partition_attrs(parentrel, parentrte->extraUpdatedCols, NULL)); /* Nothing further to do here if there are no partitions. */ if (partdesc->nparts == 0) return; /* * Perform partition pruning using restriction clauses assigned to parent * relation. live_parts will contain PartitionDesc indexes of partitions * that survive pruning. Below, we will initialize child objects for the * surviving partitions. */ live_parts = prune_append_rel_partitions(relinfo); /* Expand simple_rel_array and friends to hold child objects. */ num_live_parts = bms_num_members(live_parts); if (num_live_parts > 0) expand_planner_arrays(root, num_live_parts); /* * We also store partition RelOptInfo pointers in the parent relation. * Since we're palloc0'ing, slots corresponding to pruned partitions will * contain NULL. */ Assert(relinfo->part_rels == NULL); relinfo->part_rels = (RelOptInfo **) palloc0(relinfo->nparts * sizeof(RelOptInfo *)); /* * Create a child RTE for each live partition. Note that unlike * traditional inheritance, we don't need a child RTE for the partitioned * table itself, because it's not going to be scanned. */ i = -1; while ((i = bms_next_member(live_parts, i)) >= 0) { Oid childOID = partdesc->oids[i]; Relation childrel; RangeTblEntry *childrte; Index childRTindex; RelOptInfo *childrelinfo; /* Open rel, acquiring required locks */ childrel = table_open(childOID, lockmode); /* * Temporary partitions belonging to other sessions should have been * disallowed at definition, but for paranoia's sake, let's double * check. */ if (RELATION_IS_OTHER_TEMP(childrel)) elog(ERROR, "temporary relation from another session found as partition"); /* Create RTE and AppendRelInfo, plus PlanRowMark if needed. */ expand_single_inheritance_child(root, parentrte, parentRTindex, parentrel, top_parentrc, childrel, &childrte, &childRTindex); /* Create the otherrel RelOptInfo too. */ childrelinfo = build_simple_rel(root, childRTindex, relinfo); relinfo->part_rels[i] = childrelinfo; relinfo->all_partrels = bms_add_members(relinfo->all_partrels, childrelinfo->relids); /* If this child is itself partitioned, recurse */ if (childrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) expand_partitioned_rtentry(root, childrelinfo, childrte, childRTindex, childrel, top_parentrc, lockmode); /* Close child relation, but keep locks */ table_close(childrel, NoLock); } } /* * expand_single_inheritance_child * Build a RangeTblEntry and an AppendRelInfo, plus maybe a PlanRowMark. * * We now expand the partition hierarchy level by level, creating a * corresponding hierarchy of AppendRelInfos and RelOptInfos, where each * partitioned descendant acts as a parent of its immediate partitions. * (This is a difference from what older versions of PostgreSQL did and what * is still done in the case of table inheritance for unpartitioned tables, * where the hierarchy is flattened during RTE expansion.) * * PlanRowMarks still carry the top-parent's RTI, and the top-parent's * allMarkTypes field still accumulates values from all descendents. * * "parentrte" and "parentRTindex" are immediate parent's RTE and * RTI. "top_parentrc" is top parent's PlanRowMark. * * The child RangeTblEntry and its RTI are returned in "childrte_p" and * "childRTindex_p" resp. */ static void expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, PlanRowMark *top_parentrc, Relation childrel, RangeTblEntry **childrte_p, Index *childRTindex_p) { Query *parse = root->parse; Oid parentOID = RelationGetRelid(parentrel); Oid childOID = RelationGetRelid(childrel); RangeTblEntry *childrte; Index childRTindex; AppendRelInfo *appinfo; TupleDesc child_tupdesc; List *parent_colnames; List *child_colnames; /* * Build an RTE for the child, and attach to query's rangetable list. We * copy most scalar fields of the parent's RTE, but replace relation OID, * relkind, and inh for the child. Also, set requiredPerms to zero since * all required permissions checks are done on the original RTE. Likewise, * set the child's securityQuals to empty, because we only want to apply * the parent's RLS conditions regardless of what RLS properties * individual children may have. (This is an intentional choice to make * inherited RLS work like regular permissions checks.) The parent * securityQuals will be propagated to children along with other base * restriction clauses, so we don't need to do it here. Other * infrastructure of the parent RTE has to be translated to match the * child table's column ordering, which we do below, so a "flat" copy is * sufficient to start with. */ childrte = makeNode(RangeTblEntry); memcpy(childrte, parentrte, sizeof(RangeTblEntry)); Assert(parentrte->rtekind == RTE_RELATION); /* else this is dubious */ childrte->relid = childOID; childrte->relkind = childrel->rd_rel->relkind; /* A partitioned child will need to be expanded further. */ if (childrte->relkind == RELKIND_PARTITIONED_TABLE) { Assert(childOID != parentOID); childrte->inh = true; } else childrte->inh = false; childrte->requiredPerms = 0; childrte->securityQuals = NIL; /* Link not-yet-fully-filled child RTE into data structures */ parse->rtable = lappend(parse->rtable, childrte); childRTindex = list_length(parse->rtable); *childrte_p = childrte; *childRTindex_p = childRTindex; /* * Build an AppendRelInfo struct for each parent/child pair. */ appinfo = make_append_rel_info(parentrel, childrel, parentRTindex, childRTindex); root->append_rel_list = lappend(root->append_rel_list, appinfo); /* tablesample is probably null, but copy it */ childrte->tablesample = copyObject(parentrte->tablesample); /* * Construct an alias clause for the child, which we can also use as eref. * This is important so that EXPLAIN will print the right column aliases * for child-table columns. (Since ruleutils.c doesn't have any easy way * to reassociate parent and child columns, we must get the child column * aliases right to start with. Note that setting childrte->alias forces * ruleutils.c to use these column names, which it otherwise would not.) */ child_tupdesc = RelationGetDescr(childrel); parent_colnames = parentrte->eref->colnames; child_colnames = NIL; for (int cattno = 0; cattno < child_tupdesc->natts; cattno++) { Form_pg_attribute att = TupleDescAttr(child_tupdesc, cattno); const char *attname; if (att->attisdropped) { /* Always insert an empty string for a dropped column */ attname = ""; } else if (appinfo->parent_colnos[cattno] > 0 && appinfo->parent_colnos[cattno] <= list_length(parent_colnames)) { /* Duplicate the query-assigned name for the parent column */ attname = strVal(list_nth(parent_colnames, appinfo->parent_colnos[cattno] - 1)); } else { /* New column, just use its real name */ attname = NameStr(att->attname); } child_colnames = lappend(child_colnames, makeString(pstrdup(attname))); } /* * We just duplicate the parent's table alias name for each child. If the * plan gets printed, ruleutils.c has to sort out unique table aliases to * use, which it can handle. */ childrte->alias = childrte->eref = makeAlias(parentrte->eref->aliasname, child_colnames); /* * Translate the column permissions bitmaps to the child's attnums (we * have to build the translated_vars list before we can do this). But if * this is the parent table, we can just duplicate the parent's bitmaps. * * Note: we need to do this even though the executor won't run any * permissions checks on the child RTE. The insertedCols/updatedCols * bitmaps may be examined for trigger-firing purposes. */ if (childOID != parentOID) { childrte->selectedCols = translate_col_privs(parentrte->selectedCols, appinfo->translated_vars); childrte->insertedCols = translate_col_privs(parentrte->insertedCols, appinfo->translated_vars); childrte->updatedCols = translate_col_privs(parentrte->updatedCols, appinfo->translated_vars); childrte->extraUpdatedCols = translate_col_privs(parentrte->extraUpdatedCols, appinfo->translated_vars); } else { childrte->selectedCols = bms_copy(parentrte->selectedCols); childrte->insertedCols = bms_copy(parentrte->insertedCols); childrte->updatedCols = bms_copy(parentrte->updatedCols); childrte->extraUpdatedCols = bms_copy(parentrte->extraUpdatedCols); } /* * Store the RTE and appinfo in the respective PlannerInfo arrays, which * the caller must already have allocated space for. */ Assert(childRTindex < root->simple_rel_array_size); Assert(root->simple_rte_array[childRTindex] == NULL); root->simple_rte_array[childRTindex] = childrte; Assert(root->append_rel_array[childRTindex] == NULL); root->append_rel_array[childRTindex] = appinfo; /* * Build a PlanRowMark if parent is marked FOR UPDATE/SHARE. */ if (top_parentrc) { PlanRowMark *childrc = makeNode(PlanRowMark); childrc->rti = childRTindex; childrc->prti = top_parentrc->rti; childrc->rowmarkId = top_parentrc->rowmarkId; /* Reselect rowmark type, because relkind might not match parent */ childrc->markType = select_rowmark_type(childrte, top_parentrc->strength); childrc->allMarkTypes = (1 << childrc->markType); childrc->strength = top_parentrc->strength; childrc->waitPolicy = top_parentrc->waitPolicy; /* * We mark RowMarks for partitioned child tables as parent RowMarks so * that the executor ignores them (except their existence means that * the child tables will be locked using the appropriate mode). */ childrc->isParent = (childrte->relkind == RELKIND_PARTITIONED_TABLE); /* Include child's rowmark type in top parent's allMarkTypes */ top_parentrc->allMarkTypes |= childrc->allMarkTypes; root->rowMarks = lappend(root->rowMarks, childrc); } /* * If we are creating a child of the query target relation (only possible * in UPDATE/DELETE), add it to all_result_relids, as well as * leaf_result_relids if appropriate, and make sure that we generate * required row-identity data. */ if (bms_is_member(parentRTindex, root->all_result_relids)) { /* OK, record the child as a result rel too. */ root->all_result_relids = bms_add_member(root->all_result_relids, childRTindex); /* Non-leaf partitions don't need any row identity info. */ if (childrte->relkind != RELKIND_PARTITIONED_TABLE) { Var *rrvar; root->leaf_result_relids = bms_add_member(root->leaf_result_relids, childRTindex); /* * If we have any child target relations, assume they all need to * generate a junk "tableoid" column. (If only one child survives * pruning, we wouldn't really need this, but it's not worth * thrashing about to avoid it.) */ rrvar = makeVar(childRTindex, TableOidAttributeNumber, OIDOID, -1, InvalidOid, 0); add_row_identity_var(root, rrvar, childRTindex, "tableoid"); /* Register any row-identity columns needed by this child. */ add_row_identity_columns(root, childRTindex, childrte, childrel); } } } /* * translate_col_privs * Translate a bitmapset representing per-column privileges from the * parent rel's attribute numbering to the child's. * * The only surprise here is that we don't translate a parent whole-row * reference into a child whole-row reference. That would mean requiring * permissions on all child columns, which is overly strict, since the * query is really only going to reference the inherited columns. Instead * we set the per-column bits for all inherited columns. */ static Bitmapset * translate_col_privs(const Bitmapset *parent_privs, List *translated_vars) { Bitmapset *child_privs = NULL; bool whole_row; int attno; ListCell *lc; /* System attributes have the same numbers in all tables */ for (attno = FirstLowInvalidHeapAttributeNumber + 1; attno < 0; attno++) { if (bms_is_member(attno - FirstLowInvalidHeapAttributeNumber, parent_privs)) child_privs = bms_add_member(child_privs, attno - FirstLowInvalidHeapAttributeNumber); } /* Check if parent has whole-row reference */ whole_row = bms_is_member(InvalidAttrNumber - FirstLowInvalidHeapAttributeNumber, parent_privs); /* And now translate the regular user attributes, using the vars list */ attno = InvalidAttrNumber; foreach(lc, translated_vars) { Var *var = lfirst_node(Var, lc); attno++; if (var == NULL) /* ignore dropped columns */ continue; if (whole_row || bms_is_member(attno - FirstLowInvalidHeapAttributeNumber, parent_privs)) child_privs = bms_add_member(child_privs, var->varattno - FirstLowInvalidHeapAttributeNumber); } return child_privs; } /* * expand_appendrel_subquery * Add "other rel" RelOptInfos for the children of an appendrel baserel * * "rel" is a subquery relation that has the rte->inh flag set, meaning it * is a UNION ALL subquery that's been flattened into an appendrel, with * child subqueries listed in root->append_rel_list. We need to build * a RelOptInfo for each child relation so that we can plan scans on them. */ static void expand_appendrel_subquery(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte, Index rti) { ListCell *l; foreach(l, root->append_rel_list) { AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l); Index childRTindex = appinfo->child_relid; RangeTblEntry *childrte; RelOptInfo *childrel; /* append_rel_list contains all append rels; ignore others */ if (appinfo->parent_relid != rti) continue; /* find the child RTE, which should already exist */ Assert(childRTindex < root->simple_rel_array_size); childrte = root->simple_rte_array[childRTindex]; Assert(childrte != NULL); /* Build the child RelOptInfo. */ childrel = build_simple_rel(root, childRTindex, rel); /* Child may itself be an inherited rel, either table or subquery. */ if (childrte->inh) expand_inherited_rtentry(root, childrel, childrte, childRTindex); } } /* * apply_child_basequals * Populate childrel's base restriction quals from parent rel's quals, * translating them using appinfo. * * If any of the resulting clauses evaluate to constant false or NULL, we * return false and don't apply any quals. Caller should mark the relation as * a dummy rel in this case, since it doesn't need to be scanned. */ bool apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel, RelOptInfo *childrel, RangeTblEntry *childRTE, AppendRelInfo *appinfo) { List *childquals; Index cq_min_security; ListCell *lc; /* * The child rel's targetlist might contain non-Var expressions, which * means that substitution into the quals could produce opportunities for * const-simplification, and perhaps even pseudoconstant quals. Therefore, * transform each RestrictInfo separately to see if it reduces to a * constant or pseudoconstant. (We must process them separately to keep * track of the security level of each qual.) */ childquals = NIL; cq_min_security = UINT_MAX; foreach(lc, parentrel->baserestrictinfo) { RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc); Node *childqual; ListCell *lc2; Assert(IsA(rinfo, RestrictInfo)); childqual = adjust_appendrel_attrs(root, (Node *) rinfo->clause, 1, &appinfo); childqual = eval_const_expressions(root, childqual); /* check for flat-out constant */ if (childqual && IsA(childqual, Const)) { if (((Const *) childqual)->constisnull || !DatumGetBool(((Const *) childqual)->constvalue)) { /* Restriction reduces to constant FALSE or NULL */ return false; } /* Restriction reduces to constant TRUE, so drop it */ continue; } /* might have gotten an AND clause, if so flatten it */ foreach(lc2, make_ands_implicit((Expr *) childqual)) { Node *onecq = (Node *) lfirst(lc2); bool pseudoconstant; /* check for pseudoconstant (no Vars or volatile functions) */ pseudoconstant = !contain_vars_of_level(onecq, 0) && !contain_volatile_functions(onecq); if (pseudoconstant) { /* tell createplan.c to check for gating quals */ root->hasPseudoConstantQuals = true; } /* reconstitute RestrictInfo with appropriate properties */ childquals = lappend(childquals, make_restrictinfo(root, (Expr *) onecq, rinfo->is_pushed_down, rinfo->outerjoin_delayed, pseudoconstant, rinfo->security_level, NULL, NULL, NULL)); /* track minimum security level among child quals */ cq_min_security = Min(cq_min_security, rinfo->security_level); } } /* * In addition to the quals inherited from the parent, we might have * securityQuals associated with this particular child node. (Currently * this can only happen in appendrels originating from UNION ALL; * inheritance child tables don't have their own securityQuals, see * expand_single_inheritance_child().) Pull any such securityQuals up * into the baserestrictinfo for the child. This is similar to * process_security_barrier_quals() for the parent rel, except that we * can't make any general deductions from such quals, since they don't * hold for the whole appendrel. */ if (childRTE->securityQuals) { Index security_level = 0; foreach(lc, childRTE->securityQuals) { List *qualset = (List *) lfirst(lc); ListCell *lc2; foreach(lc2, qualset) { Expr *qual = (Expr *) lfirst(lc2); /* not likely that we'd see constants here, so no check */ childquals = lappend(childquals, make_restrictinfo(root, qual, true, false, false, security_level, NULL, NULL, NULL)); cq_min_security = Min(cq_min_security, security_level); } security_level++; } Assert(security_level <= root->qual_security_level); } /* * OK, we've got all the baserestrictinfo quals for this child. */ childrel->baserestrictinfo = childquals; childrel->baserestrict_min_security = cq_min_security; return true; }