diff options
author | Alvaro Herrera <alvherre@alvh.no-ip.org> | 2022-12-06 16:09:24 +0100 |
---|---|---|
committer | Alvaro Herrera <alvherre@alvh.no-ip.org> | 2022-12-06 16:09:24 +0100 |
commit | a61b1f74823c9c4f79c95226a461f1e7a367764b (patch) | |
tree | b6436e5cbf82dfed6a0e27d715c22867ce17c768 /src/backend/optimizer/util/inherit.c | |
parent | b5bbaf08ed8bbc45d396c3383fc89331c914b857 (diff) |
Rework query relation permission checking
Currently, information about the permissions to be checked on relations
mentioned in a query is stored in their range table entries. So the
executor must scan the entire range table looking for relations that
need to have permissions checked. This can make the permission checking
part of the executor initialization needlessly expensive when many
inheritance children are present in the range range. While the
permissions need not be checked on the individual child relations, the
executor still must visit every range table entry to filter them out.
This commit moves the permission checking information out of the range
table entries into a new plan node called RTEPermissionInfo. Every
top-level (inheritance "root") RTE_RELATION entry in the range table
gets one and a list of those is maintained alongside the range table.
This new list is initialized by the parser when initializing the range
table. The rewriter can add more entries to it as rules/views are
expanded. Finally, the planner combines the lists of the individual
subqueries into one flat list that is passed to the executor for
checking.
To make it quick to find the RTEPermissionInfo entry belonging to a
given relation, RangeTblEntry gets a new Index field 'perminfoindex'
that stores the corresponding RTEPermissionInfo's index in the query's
list of the latter.
ExecutorCheckPerms_hook has gained another List * argument; the
signature is now:
typedef bool (*ExecutorCheckPerms_hook_type) (List *rangeTable,
List *rtePermInfos,
bool ereport_on_violation);
The first argument is no longer used by any in-core uses of the hook,
but we leave it in place because there may be other implementations that
do. Implementations should likely scan the rtePermInfos list to
determine which operations to allow or deny.
Author: Amit Langote <amitlangote09@gmail.com>
Discussion: https://postgr.es/m/CA+HiwqGjJDmUhDSfv-U2qhKJjt9ST7Xh9JXC_irsAQ1TAUsJYg@mail.gmail.com
Diffstat (limited to 'src/backend/optimizer/util/inherit.c')
-rw-r--r-- | src/backend/optimizer/util/inherit.c | 173 |
1 files changed, 132 insertions, 41 deletions
diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c index 3d270e91d66..f51ce45cd3b 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; +} |