diff options
| author | Alvaro Herrera | 2022-12-06 15:09:24 +0000 |
|---|---|---|
| committer | Alvaro Herrera | 2022-12-06 15:09:24 +0000 |
| commit | a61b1f74823c9c4f79c95226a461f1e7a367764b (patch) | |
| tree | b6436e5cbf82dfed6a0e27d715c22867ce17c768 /src/include | |
| 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 <[email protected]>
Discussion: https://postgr.es/m/CA+HiwqGjJDmUhDSfv-U2qhKJjt9ST7Xh9JXC_irsAQ1TAUsJYg@mail.gmail.com
Diffstat (limited to 'src/include')
| -rw-r--r-- | src/include/catalog/catversion.h | 2 | ||||
| -rw-r--r-- | src/include/commands/copyfrom_internal.h | 3 | ||||
| -rw-r--r-- | src/include/executor/executor.h | 10 | ||||
| -rw-r--r-- | src/include/nodes/execnodes.h | 3 | ||||
| -rw-r--r-- | src/include/nodes/parsenodes.h | 94 | ||||
| -rw-r--r-- | src/include/nodes/pathnodes.h | 3 | ||||
| -rw-r--r-- | src/include/nodes/plannodes.h | 3 | ||||
| -rw-r--r-- | src/include/optimizer/inherit.h | 2 | ||||
| -rw-r--r-- | src/include/parser/parse_node.h | 9 | ||||
| -rw-r--r-- | src/include/parser/parse_relation.h | 4 | ||||
| -rw-r--r-- | src/include/rewrite/rewriteHandler.h | 1 | ||||
| -rw-r--r-- | src/include/rewrite/rewriteManip.h | 2 |
12 files changed, 95 insertions, 41 deletions
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 8e80a075a27..51e5acfe4fb 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -57,6 +57,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 202212011 +#define CATALOG_VERSION_NO 202212061 #endif diff --git a/src/include/commands/copyfrom_internal.h b/src/include/commands/copyfrom_internal.h index 8d9cc5accdb..c5e5875eb87 100644 --- a/src/include/commands/copyfrom_internal.h +++ b/src/include/commands/copyfrom_internal.h @@ -97,7 +97,8 @@ typedef struct CopyFromStateData int *defmap; /* array of default att numbers */ ExprState **defexprs; /* array of default att expressions */ bool volatile_defexprs; /* is any of defexprs volatile? */ - List *range_table; + List *range_table; /* single element list of RangeTblEntry */ + List *rteperminfos; /* single element list of RTEPermissionInfo */ ExprState *qualexpr; TransitionCaptureState *transition_capture; diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index aaf2bc78b9c..a1202a0cf5f 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -80,8 +80,10 @@ extern PGDLLIMPORT ExecutorFinish_hook_type ExecutorFinish_hook; typedef void (*ExecutorEnd_hook_type) (QueryDesc *queryDesc); extern PGDLLIMPORT ExecutorEnd_hook_type ExecutorEnd_hook; -/* Hook for plugins to get control in ExecCheckRTPerms() */ -typedef bool (*ExecutorCheckPerms_hook_type) (List *, bool); +/* Hook for plugins to get control in ExecCheckPermissions() */ +typedef bool (*ExecutorCheckPerms_hook_type) (List *rangeTable, + List *rtePermInfos, + bool ereport_on_violation); extern PGDLLIMPORT ExecutorCheckPerms_hook_type ExecutorCheckPerms_hook; @@ -196,7 +198,8 @@ extern void standard_ExecutorFinish(QueryDesc *queryDesc); extern void ExecutorEnd(QueryDesc *queryDesc); extern void standard_ExecutorEnd(QueryDesc *queryDesc); extern void ExecutorRewind(QueryDesc *queryDesc); -extern bool ExecCheckRTPerms(List *rangeTable, bool ereport_on_violation); +extern bool ExecCheckPermissions(List *rangeTable, + List *rteperminfos, bool ereport_on_violation); extern void CheckValidResultRel(ResultRelInfo *resultRelInfo, CmdType operation); extern void InitResultRelInfo(ResultRelInfo *resultRelInfo, Relation resultRelationDesc, @@ -602,6 +605,7 @@ extern TupleTableSlot *ExecGetReturningSlot(EState *estate, ResultRelInfo *relIn extern TupleConversionMap *ExecGetChildToRootMap(ResultRelInfo *resultRelInfo); extern TupleConversionMap *ExecGetRootToChildMap(ResultRelInfo *resultRelInfo, EState *estate); +extern Oid ExecGetResultRelCheckAsUser(ResultRelInfo *relInfo, EState *estate); extern Bitmapset *ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate); extern Bitmapset *ExecGetUpdatedCols(ResultRelInfo *relinfo, EState *estate); extern Bitmapset *ExecGetExtraUpdatedCols(ResultRelInfo *relinfo, EState *estate); diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 71248a94660..08ad9d6be3f 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -617,8 +617,9 @@ typedef struct EState * pointers, or NULL if not yet opened */ struct ExecRowMark **es_rowmarks; /* Array of per-range-table-entry * ExecRowMarks, or NULL if none */ + List *es_rteperminfos; /* List of RTEPermissionInfo */ PlannedStmt *es_plannedstmt; /* link to top of plan tree */ - List *es_part_prune_infos; /* PlannedStmt.partPruneInfos */ + List *es_part_prune_infos; /* PlannedStmt.partPruneInfos */ const char *es_sourceText; /* Source text from QueryDesc */ JunkFilter *es_junkFilter; /* top-level junk filter, if any */ diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index f17846e30e2..6a6d3293e41 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -154,6 +154,8 @@ typedef struct Query List *cteList; /* WITH list (of CommonTableExpr's) */ List *rtable; /* list of range table entries */ + List *rteperminfos; /* list of RTEPermissionInfo nodes for the + * rtable entries having perminfoindex > 0 */ FromExpr *jointree; /* table join tree (FROM and WHERE clauses); * also USING clause for MERGE */ @@ -968,37 +970,6 @@ typedef struct PartitionCmd * control visibility. But it is needed by ruleutils.c to determine * whether RTEs should be shown in decompiled queries. * - * requiredPerms and checkAsUser specify run-time access permissions - * checks to be performed at query startup. The user must have *all* - * of the permissions that are OR'd together in requiredPerms (zero - * indicates no permissions checking). If checkAsUser is not zero, - * then do the permissions checks using the access rights of that user, - * not the current effective user ID. (This allows rules to act as - * setuid gateways.) Permissions checks only apply to RELATION RTEs. - * - * For SELECT/INSERT/UPDATE permissions, if the user doesn't have - * table-wide permissions then it is sufficient to have the permissions - * on all columns identified in selectedCols (for SELECT) and/or - * insertedCols and/or updatedCols (INSERT with ON CONFLICT DO UPDATE may - * have all 3). selectedCols, insertedCols and updatedCols are bitmapsets, - * which cannot have negative integer members, so we subtract - * FirstLowInvalidHeapAttributeNumber from column numbers before storing - * them in these fields. A whole-row Var reference is represented by - * setting the bit for InvalidAttrNumber. - * - * updatedCols is also used in some other places, for example, to determine - * which triggers to fire and in FDWs to know which changed columns they - * need to ship off. - * - * Generated columns that are caused to be updated by an update to a base - * column are listed in extraUpdatedCols. This is not considered for - * permission checking, but it is useful in those places that want to know - * the full set of columns being updated as opposed to only the ones the - * user explicitly mentioned in the query. (There is currently no need for - * an extraInsertedCols, but it could exist.) Note that extraUpdatedCols - * is populated during query rewrite, NOT in the parser, since generated - * columns could be added after a rule has been parsed and stored. - * * securityQuals is a list of security barrier quals (boolean expressions), * to be tested in the listed order before returning a row from the * relation. It is always NIL in parser output. Entries are added by the @@ -1054,11 +1025,16 @@ typedef struct RangeTblEntry * current query; this happens if a DO ALSO rule simply scans the original * target table. We leave such RTEs with their original lockmode so as to * avoid getting an additional, lesser lock. + * + * perminfoindex is 1-based index of the RTEPermissionInfo belonging to + * this RTE in the containing struct's list of same; 0 if permissions need + * not be checked for this RTE. */ Oid relid; /* OID of the relation */ char relkind; /* relation kind (see pg_class.relkind) */ int rellockmode; /* lock level that query requires on the rel */ struct TableSampleClause *tablesample; /* sampling info, or NULL */ + Index perminfoindex; /* * Fields valid for a subquery RTE (else NULL): @@ -1178,14 +1154,64 @@ typedef struct RangeTblEntry bool lateral; /* subquery, function, or values is LATERAL? */ bool inh; /* inheritance requested? */ bool inFromCl; /* present in FROM clause? */ + Bitmapset *extraUpdatedCols; /* generated columns being updated */ + List *securityQuals; /* security barrier quals to apply, if any */ +} RangeTblEntry; + +/* + * RTEPermissionInfo + * Per-relation information for permission checking. Added to the Query + * node by the parser when adding the corresponding RTE to the query + * range table and subsequently editorialized on by the rewriter if + * needed after rule expansion. + * + * Only the relations directly mentioned in the query are checked for + * accesss permissions by the core executor, so only their RTEPermissionInfos + * are present in the Query. However, extensions may want to check inheritance + * children too, depending on the value of rte->inh, so it's copied in 'inh' + * for their perusal. + * + * requiredPerms and checkAsUser specify run-time access permissions checks + * to be performed at query startup. The user must have *all* of the + * permissions that are OR'd together in requiredPerms (never 0!). If + * checkAsUser is not zero, then do the permissions checks using the access + * rights of that user, not the current effective user ID. (This allows rules + * to act as setuid gateways.) + * + * For SELECT/INSERT/UPDATE permissions, if the user doesn't have table-wide + * permissions then it is sufficient to have the permissions on all columns + * identified in selectedCols (for SELECT) and/or insertedCols and/or + * updatedCols (INSERT with ON CONFLICT DO UPDATE may have all 3). + * selectedCols, insertedCols and updatedCols are bitmapsets, which cannot have + * negative integer members, so we subtract FirstLowInvalidHeapAttributeNumber + * from column numbers before storing them in these fields. A whole-row Var + * reference is represented by setting the bit for InvalidAttrNumber. + * + * updatedCols is also used in some other places, for example, to determine + * which triggers to fire and in FDWs to know which changed columns they need + * to ship off. + * + * Generated columns that are caused to be updated by an update to a base + * column are listed in extraUpdatedCols. This is not considered for + * permission checking, but it is useful in those places that want to know the + * full set of columns being updated as opposed to only the ones the user + * explicitly mentioned in the query. (There is currently no need for an + * extraInsertedCols, but it could exist.) Note that extraUpdatedCols is + * populated during query rewrite, NOT in the parser, since generated columns + * could be added after a rule has been parsed and stored. + */ +typedef struct RTEPermissionInfo +{ + NodeTag type; + + Oid relid; /* relation OID */ + bool inh; /* separately check inheritance children? */ AclMode requiredPerms; /* bitmask of required access permissions */ Oid checkAsUser; /* if valid, check access as this role */ Bitmapset *selectedCols; /* columns needing SELECT permission */ Bitmapset *insertedCols; /* columns needing INSERT permission */ Bitmapset *updatedCols; /* columns needing UPDATE permission */ - Bitmapset *extraUpdatedCols; /* generated columns being updated */ - List *securityQuals; /* security barrier quals to apply, if any */ -} RangeTblEntry; +} RTEPermissionInfo; /* * RangeTblFunction - diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h index 12624e6adb1..654dba61aa0 100644 --- a/src/include/nodes/pathnodes.h +++ b/src/include/nodes/pathnodes.h @@ -113,6 +113,9 @@ typedef struct PlannerGlobal /* "flat" rangetable for executor */ List *finalrtable; + /* "flat" list of RTEPermissionInfos */ + List *finalrteperminfos; + /* "flat" list of PlanRowMarks */ List *finalrowmarks; diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index c36a15bd09d..bddfe861912 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -75,6 +75,9 @@ typedef struct PlannedStmt List *rtable; /* list of RangeTblEntry nodes */ + List *permInfos; /* list of RTEPermissionInfo nodes for rtable + * entries needing one */ + /* rtable indexes of target relations for INSERT/UPDATE/DELETE/MERGE */ List *resultRelations; /* integer list of RT indexes, or NIL */ diff --git a/src/include/optimizer/inherit.h b/src/include/optimizer/inherit.h index adcb1d73722..8ebd42b757a 100644 --- a/src/include/optimizer/inherit.h +++ b/src/include/optimizer/inherit.h @@ -20,6 +20,8 @@ extern void expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte, Index rti); +extern Bitmapset *get_rel_all_updated_cols(PlannerInfo *root, RelOptInfo *rel); + extern bool apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel, RelOptInfo *childrel, RangeTblEntry *childRTE, AppendRelInfo *appinfo); diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h index 962ebf65de1..3fd56ceccdf 100644 --- a/src/include/parser/parse_node.h +++ b/src/include/parser/parse_node.h @@ -111,6 +111,9 @@ typedef Node *(*CoerceParamHook) (ParseState *pstate, Param *param, * Note that neither relname nor refname of these entries are necessarily * unique; searching the rtable by name is a bad idea. * + * p_rteperminfos: list of RTEPermissionInfo containing an entry corresponding + * to each RTE_RELATION entry in p_rtable. + * * p_joinexprs: list of JoinExpr nodes associated with p_rtable entries. * This is one-for-one with p_rtable, but contains NULLs for non-join * RTEs, and may be shorter than p_rtable if the last RTE(s) aren't joins. @@ -181,6 +184,8 @@ struct ParseState ParseState *parentParseState; /* stack link */ const char *p_sourcetext; /* source text, or NULL if not available */ List *p_rtable; /* range table so far */ + List *p_rteperminfos; /* list of RTEPermissionInfo nodes for each + * RTE_RELATION entry in rtable */ List *p_joinexprs; /* JoinExprs for RTE_JOIN p_rtable entries */ List *p_joinlist; /* join items so far (will become FromExpr * node's fromlist) */ @@ -234,7 +239,8 @@ struct ParseState * join's first N columns, the net effect is just that we expose only those * join columns via this nsitem.) * - * p_rte and p_rtindex link to the underlying rangetable entry. + * p_rte and p_rtindex link to the underlying rangetable entry, and + * p_perminfo to the entry in rteperminfos. * * The p_nscolumns array contains info showing how to construct Vars * referencing the names appearing in the p_names->colnames list. @@ -271,6 +277,7 @@ struct ParseNamespaceItem Alias *p_names; /* Table and column names */ RangeTblEntry *p_rte; /* The relation's rangetable entry */ int p_rtindex; /* The relation's index in the rangetable */ + RTEPermissionInfo *p_perminfo; /* The relation's rteperminfos entry */ /* array of same length as p_names->colnames: */ ParseNamespaceColumn *p_nscolumns; /* per-column data */ bool p_rel_visible; /* Relation name is visible? */ diff --git a/src/include/parser/parse_relation.h b/src/include/parser/parse_relation.h index 484db165dbf..2f8d417709f 100644 --- a/src/include/parser/parse_relation.h +++ b/src/include/parser/parse_relation.h @@ -99,6 +99,10 @@ extern ParseNamespaceItem *addRangeTableEntryForCTE(ParseState *pstate, extern ParseNamespaceItem *addRangeTableEntryForENR(ParseState *pstate, RangeVar *rv, bool inFromCl); +extern RTEPermissionInfo *addRTEPermissionInfo(List **rteperminfos, + RangeTblEntry *rte); +extern RTEPermissionInfo *getRTEPermissionInfo(List *rteperminfos, + RangeTblEntry *rte); extern bool isLockedRefname(ParseState *pstate, const char *refname); extern void addNSItemToQuery(ParseState *pstate, ParseNamespaceItem *nsitem, bool addToJoinList, diff --git a/src/include/rewrite/rewriteHandler.h b/src/include/rewrite/rewriteHandler.h index 90ecf109afc..05c3680cd6a 100644 --- a/src/include/rewrite/rewriteHandler.h +++ b/src/include/rewrite/rewriteHandler.h @@ -25,6 +25,7 @@ extern void AcquireRewriteLocks(Query *parsetree, extern Node *build_column_default(Relation rel, int attrno); extern void fill_extraUpdatedCols(RangeTblEntry *target_rte, + RTEPermissionInfo *target_perminfo, Relation target_relation); extern Query *get_view_query(Relation view); diff --git a/src/include/rewrite/rewriteManip.h b/src/include/rewrite/rewriteManip.h index f001ca41bbc..05e6fe1f4b9 100644 --- a/src/include/rewrite/rewriteManip.h +++ b/src/include/rewrite/rewriteManip.h @@ -41,6 +41,8 @@ typedef enum ReplaceVarsNoMatchOption } ReplaceVarsNoMatchOption; +extern void CombineRangeTables(List **dst_rtable, List **dst_perminfos, + List *src_rtable, List *src_perminfos); extern void OffsetVarNodes(Node *node, int offset, int sublevels_up); extern void ChangeVarNodes(Node *node, int rt_index, int new_index, int sublevels_up); |
