diff options
Diffstat (limited to 'src/backend/executor')
-rw-r--r-- | src/backend/executor/execMain.c | 117 | ||||
-rw-r--r-- | src/backend/executor/execParallel.c | 1 | ||||
-rw-r--r-- | src/backend/executor/execUtils.c | 134 |
3 files changed, 146 insertions, 106 deletions
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 12ff4f3de58..872b879387b 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -54,6 +54,7 @@ #include "jit/jit.h" #include "mb/pg_wchar.h" #include "miscadmin.h" +#include "parser/parse_relation.h" #include "parser/parsetree.h" #include "storage/bufmgr.h" #include "storage/lmgr.h" @@ -74,7 +75,7 @@ ExecutorRun_hook_type ExecutorRun_hook = NULL; ExecutorFinish_hook_type ExecutorFinish_hook = NULL; ExecutorEnd_hook_type ExecutorEnd_hook = NULL; -/* Hook for plugin to get control in ExecCheckRTPerms() */ +/* Hook for plugin to get control in ExecCheckPermissions() */ ExecutorCheckPerms_hook_type ExecutorCheckPerms_hook = NULL; /* decls for local routines only used within this module */ @@ -90,10 +91,10 @@ static void ExecutePlan(EState *estate, PlanState *planstate, ScanDirection direction, DestReceiver *dest, bool execute_once); -static bool ExecCheckRTEPerms(RangeTblEntry *rte); -static bool ExecCheckRTEPermsModified(Oid relOid, Oid userid, - Bitmapset *modifiedCols, - AclMode requiredPerms); +static bool ExecCheckOneRelPerms(RTEPermissionInfo *perminfo); +static bool ExecCheckPermissionsModified(Oid relOid, Oid userid, + Bitmapset *modifiedCols, + AclMode requiredPerms); static void ExecCheckXactReadOnly(PlannedStmt *plannedstmt); static char *ExecBuildSlotValueDescription(Oid reloid, TupleTableSlot *slot, @@ -554,8 +555,8 @@ ExecutorRewind(QueryDesc *queryDesc) /* - * ExecCheckRTPerms - * Check access permissions for all relations listed in a range table. + * ExecCheckPermissions + * Check access permissions of relations mentioned in a query * * Returns true if permissions are adequate. Otherwise, throws an appropriate * error if ereport_on_violation is true, or simply returns false otherwise. @@ -565,73 +566,65 @@ ExecutorRewind(QueryDesc *queryDesc) * passing, then RLS also needs to be consulted (and check_enable_rls()). * * See rewrite/rowsecurity.c. + * + * NB: rangeTable is no longer used by us, but kept around for the hooks that + * might still want to look at the RTEs. */ bool -ExecCheckRTPerms(List *rangeTable, bool ereport_on_violation) +ExecCheckPermissions(List *rangeTable, List *rteperminfos, + bool ereport_on_violation) { ListCell *l; bool result = true; - foreach(l, rangeTable) + foreach(l, rteperminfos) { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); + RTEPermissionInfo *perminfo = lfirst_node(RTEPermissionInfo, l); - result = ExecCheckRTEPerms(rte); + Assert(OidIsValid(perminfo->relid)); + result = ExecCheckOneRelPerms(perminfo); if (!result) { - Assert(rte->rtekind == RTE_RELATION); if (ereport_on_violation) - aclcheck_error(ACLCHECK_NO_PRIV, get_relkind_objtype(get_rel_relkind(rte->relid)), - get_rel_name(rte->relid)); + aclcheck_error(ACLCHECK_NO_PRIV, + get_relkind_objtype(get_rel_relkind(perminfo->relid)), + get_rel_name(perminfo->relid)); return false; } } if (ExecutorCheckPerms_hook) - result = (*ExecutorCheckPerms_hook) (rangeTable, + result = (*ExecutorCheckPerms_hook) (rangeTable, rteperminfos, ereport_on_violation); return result; } /* - * ExecCheckRTEPerms - * Check access permissions for a single RTE. + * ExecCheckOneRelPerms + * Check access permissions for a single relation. */ static bool -ExecCheckRTEPerms(RangeTblEntry *rte) +ExecCheckOneRelPerms(RTEPermissionInfo *perminfo) { AclMode requiredPerms; AclMode relPerms; AclMode remainingPerms; - Oid relOid; Oid userid; + Oid relOid = perminfo->relid; - /* - * Only plain-relation RTEs need to be checked here. Function RTEs are - * checked when the function is prepared for execution. Join, subquery, - * and special RTEs need no checks. - */ - if (rte->rtekind != RTE_RELATION) - return true; - - /* - * No work if requiredPerms is empty. - */ - requiredPerms = rte->requiredPerms; - if (requiredPerms == 0) - return true; - - relOid = rte->relid; + requiredPerms = perminfo->requiredPerms; + Assert(requiredPerms != 0); /* * userid to check as: current user unless we have a setuid indication. * * Note: GetUserId() is presently fast enough that there's no harm in - * calling it separately for each RTE. If that stops being true, we could - * call it once in ExecCheckRTPerms and pass the userid down from there. - * But for now, no need for the extra clutter. + * calling it separately for each relation. If that stops being true, we + * could call it once in ExecCheckPermissions and pass the userid down + * from there. But for now, no need for the extra clutter. */ - userid = OidIsValid(rte->checkAsUser) ? rte->checkAsUser : GetUserId(); + userid = OidIsValid(perminfo->checkAsUser) ? + perminfo->checkAsUser : GetUserId(); /* * We must have *all* the requiredPerms bits, but some of the bits can be @@ -665,14 +658,14 @@ ExecCheckRTEPerms(RangeTblEntry *rte) * example, SELECT COUNT(*) FROM table), allow the query if we * have SELECT on any column of the rel, as per SQL spec. */ - if (bms_is_empty(rte->selectedCols)) + if (bms_is_empty(perminfo->selectedCols)) { if (pg_attribute_aclcheck_all(relOid, userid, ACL_SELECT, ACLMASK_ANY) != ACLCHECK_OK) return false; } - while ((col = bms_next_member(rte->selectedCols, col)) >= 0) + while ((col = bms_next_member(perminfo->selectedCols, col)) >= 0) { /* bit #s are offset by FirstLowInvalidHeapAttributeNumber */ AttrNumber attno = col + FirstLowInvalidHeapAttributeNumber; @@ -697,29 +690,31 @@ ExecCheckRTEPerms(RangeTblEntry *rte) * Basically the same for the mod columns, for both INSERT and UPDATE * privilege as specified by remainingPerms. */ - if (remainingPerms & ACL_INSERT && !ExecCheckRTEPermsModified(relOid, - userid, - rte->insertedCols, - ACL_INSERT)) + if (remainingPerms & ACL_INSERT && + !ExecCheckPermissionsModified(relOid, + userid, + perminfo->insertedCols, + ACL_INSERT)) return false; - if (remainingPerms & ACL_UPDATE && !ExecCheckRTEPermsModified(relOid, - userid, - rte->updatedCols, - ACL_UPDATE)) + if (remainingPerms & ACL_UPDATE && + !ExecCheckPermissionsModified(relOid, + userid, + perminfo->updatedCols, + ACL_UPDATE)) return false; } return true; } /* - * ExecCheckRTEPermsModified - * Check INSERT or UPDATE access permissions for a single RTE (these + * ExecCheckPermissionsModified + * Check INSERT or UPDATE access permissions for a single relation (these * are processed uniformly). */ static bool -ExecCheckRTEPermsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, - AclMode requiredPerms) +ExecCheckPermissionsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, + AclMode requiredPerms) { int col = -1; @@ -773,17 +768,14 @@ ExecCheckXactReadOnly(PlannedStmt *plannedstmt) * Fail if write permissions are requested in parallel mode for table * (temp or non-temp), otherwise fail for any non-temp table. */ - foreach(l, plannedstmt->rtable) + foreach(l, plannedstmt->permInfos) { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); - - if (rte->rtekind != RTE_RELATION) - continue; + RTEPermissionInfo *perminfo = lfirst_node(RTEPermissionInfo, l); - if ((rte->requiredPerms & (~ACL_SELECT)) == 0) + if ((perminfo->requiredPerms & (~ACL_SELECT)) == 0) continue; - if (isTempNamespace(get_rel_namespace(rte->relid))) + if (isTempNamespace(get_rel_namespace(perminfo->relid))) continue; PreventCommandIfReadOnly(CreateCommandName((Node *) plannedstmt)); @@ -815,9 +807,10 @@ InitPlan(QueryDesc *queryDesc, int eflags) int i; /* - * Do permissions checks + * Do permissions checks and save the list for later use. */ - ExecCheckRTPerms(rangeTable, true); + ExecCheckPermissions(rangeTable, plannedstmt->permInfos, true); + estate->es_rteperminfos = plannedstmt->permInfos; /* * initialize the node's execution state diff --git a/src/backend/executor/execParallel.c b/src/backend/executor/execParallel.c index aca0c6f323f..a5b8e43ec51 100644 --- a/src/backend/executor/execParallel.c +++ b/src/backend/executor/execParallel.c @@ -185,6 +185,7 @@ ExecSerializePlan(Plan *plan, EState *estate) pstmt->planTree = plan; pstmt->partPruneInfos = estate->es_part_prune_infos; pstmt->rtable = estate->es_range_table; + pstmt->permInfos = estate->es_rteperminfos; pstmt->resultRelations = NIL; pstmt->appendRelations = NIL; diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index 572c87e4536..e296f44ebd9 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -57,6 +57,7 @@ #include "miscadmin.h" #include "nodes/nodeFuncs.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "partitioning/partdesc.h" #include "storage/lmgr.h" #include "utils/builtins.h" @@ -67,6 +68,7 @@ static bool tlist_matches_tupdesc(PlanState *ps, List *tlist, int varno, TupleDesc tupdesc); static void ShutdownExprContext(ExprContext *econtext, bool isCommit); +static RTEPermissionInfo *GetResultRTEPermissionInfo(ResultRelInfo *relinfo, EState *estate); /* ---------------------------------------------------------------- @@ -1296,72 +1298,48 @@ ExecGetRootToChildMap(ResultRelInfo *resultRelInfo, EState *estate) Bitmapset * ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate) { - /* - * The columns are stored in the range table entry. If this ResultRelInfo - * represents a partition routing target, and doesn't have an entry of its - * own in the range table, fetch the parent's RTE and map the columns to - * the order they are in the partition. - */ - if (relinfo->ri_RangeTableIndex != 0) - { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); + RTEPermissionInfo *perminfo = GetResultRTEPermissionInfo(relinfo, estate); - return rte->insertedCols; - } - else if (relinfo->ri_RootResultRelInfo) + if (perminfo == NULL) + return NULL; + + /* Map the columns to child's attribute numbers if needed. */ + if (relinfo->ri_RootResultRelInfo) { - ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); TupleConversionMap *map = ExecGetRootToChildMap(relinfo, estate); - if (map != NULL) - return execute_attr_map_cols(map->attrMap, rte->insertedCols); - else - return rte->insertedCols; - } - else - { - /* - * The relation isn't in the range table and it isn't a partition - * routing target. This ResultRelInfo must've been created only for - * firing triggers and the relation is not being inserted into. (See - * ExecGetTriggerResultRel.) - */ - return NULL; + if (map) + return execute_attr_map_cols(map->attrMap, perminfo->insertedCols); } + + return perminfo->insertedCols; } /* Return a bitmap representing columns being updated */ Bitmapset * ExecGetUpdatedCols(ResultRelInfo *relinfo, EState *estate) { - /* see ExecGetInsertedCols() */ - if (relinfo->ri_RangeTableIndex != 0) - { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); + RTEPermissionInfo *perminfo = GetResultRTEPermissionInfo(relinfo, estate); - return rte->updatedCols; - } - else if (relinfo->ri_RootResultRelInfo) + if (perminfo == NULL) + return NULL; + + /* Map the columns to child's attribute numbers if needed. */ + if (relinfo->ri_RootResultRelInfo) { - ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); TupleConversionMap *map = ExecGetRootToChildMap(relinfo, estate); - if (map != NULL) - return execute_attr_map_cols(map->attrMap, rte->updatedCols); - else - return rte->updatedCols; + if (map) + return execute_attr_map_cols(map->attrMap, perminfo->updatedCols); } - else - return NULL; + + return perminfo->updatedCols; } /* Return a bitmap representing generated columns being updated */ Bitmapset * ExecGetExtraUpdatedCols(ResultRelInfo *relinfo, EState *estate) { - /* see ExecGetInsertedCols() */ if (relinfo->ri_RangeTableIndex != 0) { RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); @@ -1390,3 +1368,71 @@ ExecGetAllUpdatedCols(ResultRelInfo *relinfo, EState *estate) return bms_union(ExecGetUpdatedCols(relinfo, estate), ExecGetExtraUpdatedCols(relinfo, estate)); } + +/* + * GetResultRTEPermissionInfo + * Looks up RTEPermissionInfo for ExecGet*Cols() routines + */ +static RTEPermissionInfo * +GetResultRTEPermissionInfo(ResultRelInfo *relinfo, EState *estate) +{ + Index rti; + RangeTblEntry *rte; + RTEPermissionInfo *perminfo = NULL; + + if (relinfo->ri_RootResultRelInfo) + { + /* + * For inheritance child result relations (a partition routing target + * of an INSERT or a child UPDATE target), this returns the root + * parent's RTE to fetch the RTEPermissionInfo because that's the only + * one that has one assigned. + */ + rti = relinfo->ri_RootResultRelInfo->ri_RangeTableIndex; + } + else if (relinfo->ri_RangeTableIndex != 0) + { + /* + * Non-child result relation should have their own RTEPermissionInfo. + */ + rti = relinfo->ri_RangeTableIndex; + } + else + { + /* + * The relation isn't in the range table and it isn't a partition + * routing target. This ResultRelInfo must've been created only for + * firing triggers and the relation is not being inserted into. (See + * ExecGetTriggerResultRel.) + */ + rti = 0; + } + + if (rti > 0) + { + rte = exec_rt_fetch(rti, estate); + perminfo = getRTEPermissionInfo(estate->es_rteperminfos, rte); + } + + return perminfo; +} + +/* + * GetResultRelCheckAsUser + * Returns the user to modify passed-in result relation as + * + * The user is chosen by looking up the relation's or, if a child table, its + * root parent's RTEPermissionInfo. + */ +Oid +ExecGetResultRelCheckAsUser(ResultRelInfo *relInfo, EState *estate) +{ + RTEPermissionInfo *perminfo = GetResultRTEPermissionInfo(relInfo, estate); + + /* XXX - maybe ok to return GetUserId() in this case? */ + if (perminfo == NULL) + elog(ERROR, "no RTEPermissionInfo found for result relation with OID %u", + RelationGetRelid(relInfo->ri_RelationDesc)); + + return perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); +} |