diff options
Diffstat (limited to 'src/backend/optimizer')
| -rw-r--r-- | src/backend/optimizer/plan/planner.c | 6 | ||||
| -rw-r--r-- | src/backend/optimizer/plan/setrefs.c | 73 | ||||
| -rw-r--r-- | src/backend/optimizer/plan/subselect.c | 8 | ||||
| -rw-r--r-- | src/backend/optimizer/prep/prepjointree.c | 21 | ||||
| -rw-r--r-- | src/backend/optimizer/util/inherit.c | 173 | ||||
| -rw-r--r-- | src/backend/optimizer/util/relnode.c | 21 |
6 files changed, 230 insertions, 72 deletions
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 799602f5ea..15aa9c5087 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -57,6 +57,7 @@ #include "optimizer/tlist.h" #include "parser/analyze.h" #include "parser/parse_agg.h" +#include "parser/parse_relation.h" #include "parser/parsetree.h" #include "partitioning/partdesc.h" #include "rewrite/rewriteManip.h" @@ -306,6 +307,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, glob->subroots = NIL; glob->rewindPlanIDs = NULL; glob->finalrtable = NIL; + glob->finalrteperminfos = NIL; glob->finalrowmarks = NIL; glob->resultRelations = NIL; glob->appendRelations = NIL; @@ -493,6 +495,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, /* final cleanup of the plan */ Assert(glob->finalrtable == NIL); + Assert(glob->finalrteperminfos == NIL); Assert(glob->finalrowmarks == NIL); Assert(glob->resultRelations == NIL); Assert(glob->appendRelations == NIL); @@ -521,6 +524,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, result->planTree = top_plan; result->partPruneInfos = glob->partPruneInfos; result->rtable = glob->finalrtable; + result->permInfos = glob->finalrteperminfos; result->resultRelations = glob->resultRelations; result->appendRelations = glob->appendRelations; result->subplans = glob->subplans; @@ -6266,6 +6270,7 @@ plan_cluster_use_sort(Oid tableOid, Oid indexOid) rte->inh = false; rte->inFromCl = true; query->rtable = list_make1(rte); + addRTEPermissionInfo(&query->rteperminfos, rte); /* Set up RTE/RelOptInfo arrays */ setup_simple_rel_arrays(root); @@ -6393,6 +6398,7 @@ plan_create_index_workers(Oid tableOid, Oid indexOid) rte->inh = true; rte->inFromCl = true; query->rtable = list_make1(rte); + addRTEPermissionInfo(&query->rteperminfos, rte); /* Set up RTE/RelOptInfo arrays */ setup_simple_rel_arrays(root); diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index 399c1812d4..596f1fbc8e 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -24,6 +24,7 @@ #include "optimizer/planmain.h" #include "optimizer/planner.h" #include "optimizer/tlist.h" +#include "parser/parse_relation.h" #include "tcop/utility.h" #include "utils/lsyscache.h" #include "utils/syscache.h" @@ -78,6 +79,13 @@ typedef struct int newvarno; } fix_windowagg_cond_context; +/* Context info for flatten_rtes_walker() */ +typedef struct +{ + PlannerGlobal *glob; + Query *query; +} flatten_rtes_walker_context; + /* * Selecting the best alternative in an AlternativeSubPlan expression requires * estimating how many times that expression will be evaluated. For an @@ -113,8 +121,9 @@ typedef struct static void add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing); static void flatten_unplanned_rtes(PlannerGlobal *glob, RangeTblEntry *rte); -static bool flatten_rtes_walker(Node *node, PlannerGlobal *glob); -static void add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte); +static bool flatten_rtes_walker(Node *node, flatten_rtes_walker_context *cxt); +static void add_rte_to_flat_rtable(PlannerGlobal *glob, List *rteperminfos, + RangeTblEntry *rte); static Plan *set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset); static Plan *set_indexonlyscan_references(PlannerInfo *root, IndexOnlyScan *plan, @@ -380,6 +389,9 @@ set_plan_references(PlannerInfo *root, Plan *plan) * Extract RangeTblEntries from the plan's rangetable, and add to flat rtable * * This can recurse into subquery plans; "recursing" is true if so. + * + * This also seems like a good place to add the query's RTEPermissionInfos to + * the flat rteperminfos. */ static void add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing) @@ -400,7 +412,7 @@ add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing) RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc); if (!recursing || rte->rtekind == RTE_RELATION) - add_rte_to_flat_rtable(glob, rte); + add_rte_to_flat_rtable(glob, root->parse->rteperminfos, rte); } /* @@ -467,18 +479,21 @@ add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing) /* * Extract RangeTblEntries from a subquery that was never planned at all */ + static void flatten_unplanned_rtes(PlannerGlobal *glob, RangeTblEntry *rte) { + flatten_rtes_walker_context cxt = {glob, rte->subquery}; + /* Use query_tree_walker to find all RTEs in the parse tree */ (void) query_tree_walker(rte->subquery, flatten_rtes_walker, - (void *) glob, + (void *) &cxt, QTW_EXAMINE_RTES_BEFORE); } static bool -flatten_rtes_walker(Node *node, PlannerGlobal *glob) +flatten_rtes_walker(Node *node, flatten_rtes_walker_context *cxt) { if (node == NULL) return false; @@ -488,33 +503,38 @@ flatten_rtes_walker(Node *node, PlannerGlobal *glob) /* As above, we need only save relation RTEs */ if (rte->rtekind == RTE_RELATION) - add_rte_to_flat_rtable(glob, rte); + add_rte_to_flat_rtable(cxt->glob, cxt->query->rteperminfos, rte); return false; } if (IsA(node, Query)) { - /* Recurse into subselects */ + /* + * Recurse into subselects. Must update cxt->query to this query so + * that the rtable and rteperminfos correspond with each other. + */ + cxt->query = (Query *) node; return query_tree_walker((Query *) node, flatten_rtes_walker, - (void *) glob, + (void *) cxt, QTW_EXAMINE_RTES_BEFORE); } return expression_tree_walker(node, flatten_rtes_walker, - (void *) glob); + (void *) cxt); } /* - * Add (a copy of) the given RTE to the final rangetable + * Add (a copy of) the given RTE to the final rangetable and also the + * corresponding RTEPermissionInfo, if any, to final rteperminfos. * * In the flat rangetable, we zero out substructure pointers that are not * needed by the executor; this reduces the storage space and copying cost - * for cached plans. We keep only the ctename, alias and eref Alias fields, - * which are needed by EXPLAIN, and the selectedCols, insertedCols, - * updatedCols, and extraUpdatedCols bitmaps, which are needed for - * executor-startup permissions checking and for trigger event checking. + * for cached plans. We keep only the ctename, alias, eref Alias fields, + * which are needed by EXPLAIN, and perminfoindex which is needed by the + * executor to fetch the RTE's RTEPermissionInfo. */ static void -add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte) +add_rte_to_flat_rtable(PlannerGlobal *glob, List *rteperminfos, + RangeTblEntry *rte) { RangeTblEntry *newrte; @@ -552,6 +572,29 @@ add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte) */ if (newrte->rtekind == RTE_RELATION) glob->relationOids = lappend_oid(glob->relationOids, newrte->relid); + + /* + * Add a copy of the RTEPermissionInfo, if any, corresponding to this RTE + * to the flattened global list. + */ + if (rte->perminfoindex > 0) + { + RTEPermissionInfo *perminfo; + RTEPermissionInfo *newperminfo; + + /* Get the existing one from this query's rteperminfos. */ + perminfo = getRTEPermissionInfo(rteperminfos, newrte); + + /* + * Add a new one to finalrteperminfos and copy the contents of the + * existing one into it. Note that addRTEPermissionInfo() also + * updates newrte->perminfoindex to point to newperminfo in + * finalrteperminfos. + */ + newrte->perminfoindex = 0; /* expected by addRTEPermissionInfo() */ + newperminfo = addRTEPermissionInfo(&glob->finalrteperminfos, newrte); + memcpy(newperminfo, perminfo, sizeof(RTEPermissionInfo)); + } } /* diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index 92e3338584..abd407825b 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -1496,8 +1496,12 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink, if (!bms_is_subset(upper_varnos, available_rels)) return NULL; - /* Now we can attach the modified subquery rtable to the parent */ - parse->rtable = list_concat(parse->rtable, subselect->rtable); + /* + * Now we can attach the modified subquery rtable to the parent. This also + * adds subquery's RTEPermissionInfos into the upper query. + */ + CombineRangeTables(&parse->rtable, &parse->rteperminfos, + subselect->rtable, subselect->rteperminfos); /* * And finally, build the JoinExpr node. diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c index 2ea3ca734e..57fea35e44 100644 --- a/src/backend/optimizer/prep/prepjointree.c +++ b/src/backend/optimizer/prep/prepjointree.c @@ -176,13 +176,6 @@ transform_MERGE_to_join(Query *parse) joinrte->lateral = false; joinrte->inh = false; joinrte->inFromCl = true; - joinrte->requiredPerms = 0; - joinrte->checkAsUser = InvalidOid; - joinrte->selectedCols = NULL; - joinrte->insertedCols = NULL; - joinrte->updatedCols = NULL; - joinrte->extraUpdatedCols = NULL; - joinrte->securityQuals = NIL; /* * Add completed RTE to pstate's range table list, so that we know its @@ -1206,11 +1199,12 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte, } /* - * Now append the adjusted rtable entries to upper query. (We hold off - * until after fixing the upper rtable entries; no point in running that - * code on the subquery ones too.) + * Now append the adjusted rtable entries and their perminfos to upper + * query. (We hold off until after fixing the upper rtable entries; no + * point in running that code on the subquery ones too.) */ - parse->rtable = list_concat(parse->rtable, subquery->rtable); + CombineRangeTables(&parse->rtable, &parse->rteperminfos, + subquery->rtable, subquery->rteperminfos); /* * Pull up any FOR UPDATE/SHARE markers, too. (OffsetVarNodes already @@ -1346,9 +1340,10 @@ pull_up_simple_union_all(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte) } /* - * Append child RTEs to parent rtable. + * Append child RTEs (and their perminfos) to parent rtable. */ - root->parse->rtable = list_concat(root->parse->rtable, rtable); + CombineRangeTables(&root->parse->rtable, &root->parse->rteperminfos, + rtable, subquery->rteperminfos); /* * Recursively scan the subquery's setOperations tree and add diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c index 3d270e91d6..f51ce45cd3 100644 --- a/src/backend/optimizer/util/inherit.c +++ b/src/backend/optimizer/util/inherit.c @@ -30,6 +30,7 @@ #include "optimizer/prep.h" #include "optimizer/restrictinfo.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "partitioning/partdesc.h" #include "partitioning/partprune.h" #include "utils/rel.h" @@ -38,6 +39,7 @@ static void expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, + Bitmapset *parent_updatedCols, PlanRowMark *top_parentrc, LOCKMODE lockmode); static void expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, @@ -47,6 +49,10 @@ static void expand_single_inheritance_child(PlannerInfo *root, Index *childRTindex_p); static Bitmapset *translate_col_privs(const Bitmapset *parent_privs, List *translated_vars); +static Bitmapset *translate_col_privs_multilevel(PlannerInfo *root, + RelOptInfo *rel, + RelOptInfo *top_parent_rel, + Bitmapset *top_parent_cols); static void expand_appendrel_subquery(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte, Index rti); @@ -131,6 +137,10 @@ expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, /* Scan the inheritance set and expand it */ if (oldrelation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) { + RTEPermissionInfo *perminfo; + + perminfo = getRTEPermissionInfo(root->parse->rteperminfos, rte); + /* * Partitioned table, so set up for partitioning. */ @@ -141,7 +151,9 @@ expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, * extract the partition key columns of all the partitioned tables. */ expand_partitioned_rtentry(root, rel, rte, rti, - oldrelation, oldrc, lockmode); + oldrelation, + perminfo->updatedCols, + oldrc, lockmode); } else { @@ -305,6 +317,7 @@ static void expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, + Bitmapset *parent_updatedCols, PlanRowMark *top_parentrc, LOCKMODE lockmode) { PartitionDesc partdesc; @@ -324,14 +337,13 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, /* * 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. + * the root partitioned table's updatedCols we are interested in, + * parent_updatedCols provided by the caller contains 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); + has_partition_attrs(parentrel, parent_updatedCols, NULL); /* * There shouldn't be any generated columns in the partition key. @@ -402,9 +414,19 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, /* If this child is itself partitioned, recurse */ if (childrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) + { + AppendRelInfo *appinfo = root->append_rel_array[childRTindex]; + Bitmapset *child_updatedCols; + + child_updatedCols = translate_col_privs(parent_updatedCols, + appinfo->translated_vars); + expand_partitioned_rtentry(root, childrelinfo, childrte, childRTindex, - childrel, top_parentrc, lockmode); + childrel, + child_updatedCols, + top_parentrc, lockmode); + } /* Close child relation, but keep locks */ table_close(childrel, NoLock); @@ -451,17 +473,15 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, /* * 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. + * relkind, and inh for the child. 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)); @@ -476,9 +496,16 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, } else childrte->inh = false; - childrte->requiredPerms = 0; childrte->securityQuals = NIL; + /* + * No permission checking for the child RTE unless it's the parent + * relation in its child role, which only applies to traditional + * inheritance. + */ + if (childOID != parentOID) + childrte->perminfoindex = 0; + /* Link not-yet-fully-filled child RTE into data structures */ parse->rtable = lappend(parse->rtable, childrte); childRTindex = list_length(parse->rtable); @@ -539,33 +566,12 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, 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. - */ + /* Translate the bitmapset of generated columns being updated. */ 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 @@ -649,6 +655,54 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, } /* + * get_rel_all_updated_cols + * Returns the set of columns of a given "simple" relation that are + * updated by this query. + */ +Bitmapset * +get_rel_all_updated_cols(PlannerInfo *root, RelOptInfo *rel) +{ + Index relid; + RangeTblEntry *rte; + RTEPermissionInfo *perminfo; + Bitmapset *updatedCols, + *extraUpdatedCols; + + Assert(root->parse->commandType == CMD_UPDATE); + Assert(IS_SIMPLE_REL(rel)); + + /* + * We obtain updatedCols and extraUpdatedCols for the query's result + * relation. Then, if necessary, we map it to the column numbers of the + * relation for which they were requested. + */ + relid = root->parse->resultRelation; + rte = planner_rt_fetch(relid, root); + perminfo = getRTEPermissionInfo(root->parse->rteperminfos, rte); + + updatedCols = perminfo->updatedCols; + extraUpdatedCols = rte->extraUpdatedCols; + + /* + * For "other" rels, we must look up the root parent relation mentioned in + * the query, and translate the column numbers. + */ + if (rel->relid != relid) + { + RelOptInfo *top_parent_rel = find_base_rel(root, relid); + + Assert(IS_OTHER_REL(rel)); + + updatedCols = translate_col_privs_multilevel(root, rel, top_parent_rel, + updatedCols); + extraUpdatedCols = translate_col_privs_multilevel(root, rel, top_parent_rel, + extraUpdatedCols); + } + + return bms_union(updatedCols, extraUpdatedCols); +} + +/* * translate_col_privs * Translate a bitmapset representing per-column privileges from the * parent rel's attribute numbering to the child's. @@ -866,3 +920,40 @@ apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel, return true; } + +/* + * translate_col_privs_multilevel + * Recursively translates the column numbers contained in + * 'top_parent_cols' to the columns numbers of a descendent relation + * given by 'relid' + */ +static Bitmapset * +translate_col_privs_multilevel(PlannerInfo *root, RelOptInfo *rel, + RelOptInfo *top_parent_rel, + Bitmapset *top_parent_cols) +{ + Bitmapset *result; + AppendRelInfo *appinfo; + + if (top_parent_cols == NULL) + return NULL; + + /* Recurse if immediate parent is not the top parent. */ + if (rel->parent != top_parent_rel) + { + if (rel->parent) + result = translate_col_privs_multilevel(root, rel->parent, + top_parent_rel, + top_parent_cols); + else + elog(ERROR, "rel with relid %u is not a child rel", rel->relid); + } + + Assert(root->append_rel_array != NULL); + appinfo = root->append_rel_array[rel->relid]; + Assert(appinfo != NULL); + + result = translate_col_privs(top_parent_cols, appinfo->translated_vars); + + return result; +} diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index d7b4434e7f..7085cf3c41 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -28,6 +28,7 @@ #include "optimizer/plancat.h" #include "optimizer/restrictinfo.h" #include "optimizer/tlist.h" +#include "parser/parse_relation.h" #include "utils/hsearch.h" #include "utils/lsyscache.h" @@ -223,7 +224,25 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent) rel->rel_parallel_workers = -1; /* set up in get_relation_info */ rel->amflags = 0; rel->serverid = InvalidOid; - rel->userid = rte->checkAsUser; + if (rte->rtekind == RTE_RELATION) + { + /* + * Get the userid from the relation's RTEPermissionInfo, though only + * the tables mentioned in query are assigned RTEPermissionInfos. + * Child relations (otherrels) simply use the parent's value. + */ + if (parent == NULL) + { + RTEPermissionInfo *perminfo; + + perminfo = getRTEPermissionInfo(root->parse->rteperminfos, rte); + rel->userid = perminfo->checkAsUser; + } + else + rel->userid = parent->userid; + } + else + rel->userid = InvalidOid; rel->useridiscurrent = false; rel->fdwroutine = NULL; rel->fdw_private = NULL; |
