From 624d523d975c2306a88dd03a73540b9ba313805d Mon Sep 17 00:00:00 2001 From: amitlan Date: Wed, 21 Jul 2021 21:33:19 +0900 Subject: [PATCH v4 1/2] Rework query relation permission checking Currently, any information about the permissions to be checked is stored in query's range table entries. Only the permissions of RTE_RELATION entries need be checked, that too only for the relations that are directly mentioned in the query, not those added afterwards, say, due to expanding inheritance. This arrangement means that the executor must wade through the range table to find those entries that need their permissions checked, which can be severely wasteful when there are many entries that belong to inheritance child tables whose permissions need not be checked. This commit moves the permission checking information out of the range table entries into a new node type called RelPermissionInfo. 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, keyed on relation OIDs. The 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 down to the executor. --- contrib/postgres_fdw/postgres_fdw.c | 75 +++++--- contrib/sepgsql/dml.c | 42 ++-- contrib/sepgsql/hooks.c | 6 +- src/backend/access/common/attmap.c | 13 +- src/backend/access/common/tupconvert.c | 2 +- src/backend/catalog/partition.c | 3 +- src/backend/commands/copy.c | 18 +- src/backend/commands/copyfrom.c | 9 + src/backend/commands/indexcmds.c | 3 +- src/backend/commands/tablecmds.c | 24 ++- src/backend/commands/view.c | 4 - src/backend/executor/execMain.c | 105 +++++----- src/backend/executor/execParallel.c | 1 + src/backend/executor/execPartition.c | 12 +- src/backend/executor/execUtils.c | 137 +++++++++---- src/backend/nodes/copyfuncs.c | 31 ++- src/backend/nodes/equalfuncs.c | 16 +- src/backend/nodes/outfuncs.c | 28 ++- src/backend/nodes/readfuncs.c | 22 ++- src/backend/optimizer/plan/createplan.c | 6 +- src/backend/optimizer/plan/planner.c | 6 + src/backend/optimizer/plan/setrefs.c | 8 +- src/backend/optimizer/plan/subselect.c | 2 + src/backend/optimizer/prep/prepjointree.c | 26 +++ src/backend/optimizer/util/inherit.c | 172 ++++++++++++----- src/backend/optimizer/util/relnode.c | 9 +- src/backend/parser/analyze.c | 61 ++++-- src/backend/parser/parse_clause.c | 9 +- src/backend/parser/parse_relation.c | 223 ++++++++++++++-------- src/backend/parser/parse_target.c | 19 +- src/backend/parser/parse_utilcmd.c | 9 +- src/backend/replication/logical/worker.c | 13 +- src/backend/rewrite/rewriteDefine.c | 15 +- src/backend/rewrite/rewriteHandler.c | 178 ++++++++--------- src/backend/rewrite/rowsecurity.c | 24 ++- src/backend/statistics/extended_stats.c | 7 +- src/backend/utils/adt/ri_triggers.c | 34 ++-- src/backend/utils/adt/selfuncs.c | 19 +- src/include/access/attmap.h | 6 +- src/include/commands/copyfrom_internal.h | 3 +- src/include/executor/executor.h | 7 +- src/include/nodes/execnodes.h | 9 + src/include/nodes/nodes.h | 1 + src/include/nodes/parsenodes.h | 81 ++++---- src/include/nodes/pathnodes.h | 5 +- src/include/nodes/plannodes.h | 5 + src/include/optimizer/inherit.h | 1 + src/include/optimizer/planner.h | 1 + src/include/parser/parse_node.h | 6 +- src/include/parser/parse_relation.h | 3 + src/include/rewrite/rewriteHandler.h | 2 +- 51 files changed, 971 insertions(+), 550 deletions(-) diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c index 9d443baf02..39d28df3b6 100644 --- a/contrib/postgres_fdw/postgres_fdw.c +++ b/contrib/postgres_fdw/postgres_fdw.c @@ -30,6 +30,7 @@ #include "optimizer/appendinfo.h" #include "optimizer/clauses.h" #include "optimizer/cost.h" +#include "optimizer/inherit.h" #include "optimizer/optimizer.h" #include "optimizer/pathnode.h" #include "optimizer/paths.h" @@ -38,6 +39,7 @@ #include "optimizer/restrictinfo.h" #include "optimizer/tlist.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "postgres_fdw.h" #include "storage/latch.h" #include "utils/builtins.h" @@ -457,7 +459,8 @@ static PgFdwModifyState *create_foreign_modify(EState *estate, List *target_attrs, int len, bool has_returning, - List *retrieved_attrs); + List *retrieved_attrs, + Oid userid); static TupleTableSlot **execute_foreign_modify(EState *estate, ResultRelInfo *resultRelInfo, CmdType operation, @@ -622,7 +625,6 @@ postgresGetForeignRelSize(PlannerInfo *root, { PgFdwRelationInfo *fpinfo; ListCell *lc; - RangeTblEntry *rte = planner_rt_fetch(baserel->relid, root); /* * We use PgFdwRelationInfo to pass various information to subsequent @@ -656,12 +658,12 @@ postgresGetForeignRelSize(PlannerInfo *root, /* * If the table or the server is configured to use remote estimates, * identify which user to do remote access as during planning. This - * should match what ExecCheckRTEPerms() does. If we fail due to lack of - * permissions, the query would have failed at runtime anyway. + * should match what ExecCheckPermisssions() does. If we fail due to + * lack of permissions, the query would have failed at runtime anyway. */ if (fpinfo->use_remote_estimate) { - Oid userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + Oid userid = baserel->userid ? baserel->userid : GetUserId(); fpinfo->user = GetUserMapping(userid, fpinfo->server->serverid); } @@ -1514,16 +1516,15 @@ postgresBeginForeignScan(ForeignScanState *node, int eflags) /* * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. In case of a join or aggregate, use the - * lowest-numbered member RTE as a representative; we would get the same - * result from any. + * ExecCheckPermisssions() does. */ + userid = fsplan->checkAsUser ? fsplan->checkAsUser : GetUserId(); + if (fsplan->scan.scanrelid > 0) rtindex = fsplan->scan.scanrelid; else rtindex = bms_next_member(fsplan->fs_relids, -1); rte = exec_rt_fetch(rtindex, estate); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); /* Get info about foreign table. */ table = GetForeignTable(rte->relid); @@ -1803,7 +1804,8 @@ postgresPlanForeignModify(PlannerInfo *root, else if (operation == CMD_UPDATE) { int col; - Bitmapset *allUpdatedCols = bms_union(rte->updatedCols, rte->extraUpdatedCols); + RelOptInfo *rel = find_base_rel(root, resultRelation); + Bitmapset *allUpdatedCols = GetRelAllUpdatedCols(root, rel); col = -1; while ((col = bms_next_member(allUpdatedCols, col)) >= 0) @@ -1882,6 +1884,29 @@ postgresPlanForeignModify(PlannerInfo *root, retrieved_attrs); } +/* + * GetResultRelCheckAsUser + * Returns the user to modify passed-in foreign table result relation as + * + * The way the user is chosen matches what ExecCheckPermissions() does. + */ +static Oid +GetResultRelCheckAsUser(ResultRelInfo *relInfo, EState *estate) +{ + RelPermissionInfo *perminfo; + Oid relid; + Oid result; + + if (relInfo->ri_RootResultRelInfo) + relid = RelationGetRelid(relInfo->ri_RootResultRelInfo->ri_RelationDesc); + else + relid = RelationGetRelid(relInfo->ri_RelationDesc); + + perminfo = GetRelPermissionInfo(estate->es_relpermlist, relid, false); + + return perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); +} + /* * postgresBeginForeignModify * Begin an insert/update/delete operation on a foreign table @@ -1893,6 +1918,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, int subplan_index, int eflags) { + EState *estate = mtstate->ps.state; PgFdwModifyState *fmstate; char *query; List *target_attrs; @@ -1900,6 +1926,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, int values_end_len; List *retrieved_attrs; RangeTblEntry *rte; + Oid userid; /* * Do nothing in EXPLAIN (no ANALYZE) case. resultRelInfo->ri_FdwState @@ -1923,6 +1950,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, /* Find RTE. */ rte = exec_rt_fetch(resultRelInfo->ri_RangeTableIndex, mtstate->ps.state); + userid = GetResultRelCheckAsUser(resultRelInfo, estate); /* Construct an execution state. */ fmstate = create_foreign_modify(mtstate->ps.state, @@ -1934,7 +1962,8 @@ postgresBeginForeignModify(ModifyTableState *mtstate, target_attrs, values_end_len, has_returning, - retrieved_attrs); + retrieved_attrs, + userid); resultRelInfo->ri_FdwState = fmstate; } @@ -2126,6 +2155,7 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, List *targetAttrs = NIL; List *retrieved_attrs = NIL; bool doNothing = false; + Oid userid; /* * If the foreign table we are about to insert routed rows into is also an @@ -2203,6 +2233,8 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, rte = exec_rt_fetch(resultRelation, estate); } + userid = GetResultRelCheckAsUser(resultRelInfo, estate); + /* Construct the SQL command string. */ deparseInsertSql(&sql, rte, resultRelation, rel, targetAttrs, doNothing, resultRelInfo->ri_WithCheckOptions, @@ -2219,7 +2251,8 @@ postgresBeginForeignInsert(ModifyTableState *mtstate, targetAttrs, values_end_len, retrieved_attrs != NIL, - retrieved_attrs); + retrieved_attrs, + userid); /* * If the given resultRelInfo already has PgFdwModifyState set, it means @@ -2605,7 +2638,6 @@ postgresBeginDirectModify(ForeignScanState *node, int eflags) EState *estate = node->ss.ps.state; PgFdwDirectModifyState *dmstate; Index rtindex; - RangeTblEntry *rte; Oid userid; ForeignTable *table; UserMapping *user; @@ -2625,13 +2657,12 @@ postgresBeginDirectModify(ForeignScanState *node, int eflags) /* * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. + * ExecCheckPermissions() does. */ - rtindex = node->resultRelInfo->ri_RangeTableIndex; - rte = exec_rt_fetch(rtindex, estate); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = fsplan->checkAsUser ? fsplan->checkAsUser : GetUserId(); /* Get info about foreign table. */ + rtindex = node->resultRelInfo->ri_RangeTableIndex; if (fsplan->scan.scanrelid == 0) dmstate->rel = ExecOpenScanRelation(estate, rtindex, eflags); else @@ -3929,12 +3960,12 @@ create_foreign_modify(EState *estate, List *target_attrs, int values_end, bool has_returning, - List *retrieved_attrs) + List *retrieved_attrs, + Oid userid) { PgFdwModifyState *fmstate; Relation rel = resultRelInfo->ri_RelationDesc; TupleDesc tupdesc = RelationGetDescr(rel); - Oid userid; ForeignTable *table; UserMapping *user; AttrNumber n_params; @@ -3946,12 +3977,6 @@ create_foreign_modify(EState *estate, fmstate = (PgFdwModifyState *) palloc0(sizeof(PgFdwModifyState)); fmstate->rel = rel; - /* - * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. - */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); - /* Get info about foreign table. */ table = GetForeignTable(RelationGetRelid(rel)); user = GetUserMapping(userid, table->serverid); diff --git a/contrib/sepgsql/dml.c b/contrib/sepgsql/dml.c index 1f96e8b507..44ec89840f 100644 --- a/contrib/sepgsql/dml.c +++ b/contrib/sepgsql/dml.c @@ -277,38 +277,32 @@ check_relation_privileges(Oid relOid, * Entrypoint of the DML permission checks */ bool -sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) +sepgsql_dml_privileges(List *relpermlist, bool abort_on_violation) { ListCell *lr; - foreach(lr, rangeTabls) + foreach(lr, relpermlist) { - RangeTblEntry *rte = lfirst(lr); + RelPermissionInfo *perminfo = lfirst(lr); uint32 required = 0; List *tableIds; ListCell *li; - /* - * Only regular relations shall be checked - */ - if (rte->rtekind != RTE_RELATION) - continue; - /* * Find out required permissions */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) required |= SEPG_DB_TABLE__SELECT; - if (rte->requiredPerms & ACL_INSERT) + if (perminfo->requiredPerms & ACL_INSERT) required |= SEPG_DB_TABLE__INSERT; - if (rte->requiredPerms & ACL_UPDATE) + if (perminfo->requiredPerms & ACL_UPDATE) { - if (!bms_is_empty(rte->updatedCols)) + if (!bms_is_empty(perminfo->updatedCols)) required |= SEPG_DB_TABLE__UPDATE; else required |= SEPG_DB_TABLE__LOCK; } - if (rte->requiredPerms & ACL_DELETE) + if (perminfo->requiredPerms & ACL_DELETE) required |= SEPG_DB_TABLE__DELETE; /* @@ -320,13 +314,13 @@ sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) /* * If this RangeTblEntry is also supposed to reference inherited * tables, we need to check security label of the child tables. So, we - * expand rte->relid into list of OIDs of inheritance hierarchy, then + * expand perminfo->relid into list of OIDs of inheritance hierarchy, then * checker routine will be invoked for each relations. */ - if (!rte->inh) - tableIds = list_make1_oid(rte->relid); + if (!perminfo->inh) + tableIds = list_make1_oid(perminfo->relid); else - tableIds = find_all_inheritors(rte->relid, NoLock, NULL); + tableIds = find_all_inheritors(perminfo->relid, NoLock, NULL); foreach(li, tableIds) { @@ -339,12 +333,12 @@ sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) * child table has different attribute numbers, so we need to fix * up them. */ - selectedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->selectedCols); - insertedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->insertedCols); - updatedCols = fixup_inherited_columns(rte->relid, tableOid, - rte->updatedCols); + selectedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->selectedCols); + insertedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->insertedCols); + updatedCols = fixup_inherited_columns(perminfo->relid, tableOid, + perminfo->updatedCols); /* * check permissions on individual tables diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c index 19a3ffb7ff..036a4c97f2 100644 --- a/contrib/sepgsql/hooks.c +++ b/contrib/sepgsql/hooks.c @@ -288,17 +288,17 @@ sepgsql_object_access(ObjectAccessType access, * Entrypoint of DML permissions */ static bool -sepgsql_exec_check_perms(List *rangeTabls, bool abort) +sepgsql_exec_check_perms(List *relpermlist, bool abort) { /* * If security provider is stacking and one of them replied 'false' at * least, we don't need to check any more. */ if (next_exec_check_perms_hook && - !(*next_exec_check_perms_hook) (rangeTabls, abort)) + !(*next_exec_check_perms_hook) (relpermlist, abort)) return false; - if (!sepgsql_dml_privileges(rangeTabls, abort)) + if (!sepgsql_dml_privileges(relpermlist, abort)) return false; return true; diff --git a/src/backend/access/common/attmap.c b/src/backend/access/common/attmap.c index 32405f8610..85221ada52 100644 --- a/src/backend/access/common/attmap.c +++ b/src/backend/access/common/attmap.c @@ -169,10 +169,14 @@ build_attrmap_by_position(TupleDesc indesc, * and output columns by name. (Dropped columns are ignored in both input and * output.) This is normally a subroutine for convert_tuples_by_name in * tupconvert.c, but can be used standalone. + * + * If 'missing_ok' is true, a column from 'outdesc' not being present in + * 'indesc' is not flagged as an error. */ AttrMap * build_attrmap_by_name(TupleDesc indesc, - TupleDesc outdesc) + TupleDesc outdesc, + bool missing_ok) { AttrMap *attrMap; int outnatts; @@ -235,7 +239,7 @@ build_attrmap_by_name(TupleDesc indesc, break; } } - if (attrMap->attnums[i] == 0) + if (attrMap->attnums[i] == 0 && !missing_ok) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("could not convert row type"), @@ -257,12 +261,13 @@ build_attrmap_by_name(TupleDesc indesc, */ AttrMap * build_attrmap_by_name_if_req(TupleDesc indesc, - TupleDesc outdesc) + TupleDesc outdesc, + bool missing_ok) { AttrMap *attrMap; /* Verify compatibility and prepare attribute-number map */ - attrMap = build_attrmap_by_name(indesc, outdesc); + attrMap = build_attrmap_by_name(indesc, outdesc, missing_ok); /* Check if the map has a one-to-one match */ if (check_attrmap_match(indesc, outdesc, attrMap)) diff --git a/src/backend/access/common/tupconvert.c b/src/backend/access/common/tupconvert.c index 64f54393f3..f5624eeab9 100644 --- a/src/backend/access/common/tupconvert.c +++ b/src/backend/access/common/tupconvert.c @@ -107,7 +107,7 @@ convert_tuples_by_name(TupleDesc indesc, int n = outdesc->natts; /* Verify compatibility and prepare attribute-number map */ - attrMap = build_attrmap_by_name_if_req(indesc, outdesc); + attrMap = build_attrmap_by_name_if_req(indesc, outdesc, false); if (attrMap == NULL) { diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c index 790f4ccb92..ffc45efb32 100644 --- a/src/backend/catalog/partition.c +++ b/src/backend/catalog/partition.c @@ -227,7 +227,8 @@ map_partition_varattnos(List *expr, int fromrel_varno, bool found_whole_row; part_attmap = build_attrmap_by_name(RelationGetDescr(to_rel), - RelationGetDescr(from_rel)); + RelationGetDescr(from_rel), + false); expr = (List *) map_variable_attnos((Node *) expr, fromrel_varno, 0, part_attmap, diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index 6b33951e0c..5118343f02 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -109,7 +109,7 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, { LOCKMODE lockmode = is_from ? RowExclusiveLock : AccessShareLock; ParseNamespaceItem *nsitem; - RangeTblEntry *rte; + RelPermissionInfo *perminfo; TupleDesc tupDesc; List *attnums; ListCell *cur; @@ -123,8 +123,10 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, nsitem = addRangeTableEntryForRelation(pstate, rel, lockmode, NULL, false, false); - rte = nsitem->p_rte; - rte->requiredPerms = (is_from ? ACL_INSERT : ACL_SELECT); + + perminfo = nsitem->p_perminfo; + perminfo->relid = relid; + perminfo->requiredPerms = (is_from ? ACL_INSERT : ACL_SELECT); if (stmt->whereClause) { @@ -154,11 +156,13 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, FirstLowInvalidHeapAttributeNumber; if (is_from) - rte->insertedCols = bms_add_member(rte->insertedCols, attno); + perminfo->insertedCols = bms_add_member(perminfo->insertedCols, + attno); else - rte->selectedCols = bms_add_member(rte->selectedCols, attno); + perminfo->selectedCols = bms_add_member(perminfo->selectedCols, + attno); } - ExecCheckRTPerms(pstate->p_rtable, true); + ExecCheckPermissions(list_make1(perminfo), true); /* * Permission check for row security policies. @@ -174,7 +178,7 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt, * If RLS is not enabled for this, then just fall through to the * normal non-filtering relation handling. */ - if (check_enable_rls(rte->relid, InvalidOid, false) == RLS_ENABLED) + if (check_enable_rls(relid, InvalidOid, false) == RLS_ENABLED) { SelectStmt *select; ColumnRef *cr; diff --git a/src/backend/commands/copyfrom.c b/src/backend/commands/copyfrom.c index 40a54ad0bd..4e9e94eee0 100644 --- a/src/backend/commands/copyfrom.c +++ b/src/backend/commands/copyfrom.c @@ -654,6 +654,12 @@ CopyFrom(CopyFromState cstate) resultRelInfo = target_resultRelInfo = makeNode(ResultRelInfo); ExecInitResultRelation(estate, resultRelInfo, 1); + /* + * Copy the relation permissions into estate as well, so that + * ExecGetInsertedCols() et al will work correctly. + */ + estate->es_relpermlist = cstate->relpermlist; + /* Verify the named relation is a valid target for INSERT */ CheckValidResultRel(resultRelInfo, CMD_INSERT); @@ -1384,7 +1390,10 @@ BeginCopyFrom(ParseState *pstate, /* Assign range table, we'll need it in CopyFrom. */ if (pstate) + { cstate->range_table = pstate->p_rtable; + cstate->relpermlist = pstate->p_relpermlist; + } tupDesc = RelationGetDescr(cstate->rel); num_phys_attrs = tupDesc->natts; diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index c14ca27c5e..75e4b0cbf3 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -1230,7 +1230,8 @@ DefineIndex(Oid relationId, childidxs = RelationGetIndexList(childrel); attmap = build_attrmap_by_name(RelationGetDescr(childrel), - parentDesc); + parentDesc, + false); foreach(cell, childidxs) { diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index dbee6ae199..7f71f5cfb3 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -1172,7 +1172,8 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, } attmap = build_attrmap_by_name(RelationGetDescr(rel), - RelationGetDescr(parent)); + RelationGetDescr(parent), + false); idxstmt = generateClonedIndexStmt(NULL, idxRel, attmap, &constraintOid); @@ -9494,7 +9495,8 @@ addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel, * definition to match the partition's column layout. */ map = build_attrmap_by_name_if_req(RelationGetDescr(partRel), - RelationGetDescr(pkrel)); + RelationGetDescr(pkrel), + false); if (map) { mapped_pkattnum = palloc(sizeof(AttrNumber) * numfks); @@ -9636,7 +9638,8 @@ addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel, CheckTableNotInUse(partition, "ALTER TABLE"); attmap = build_attrmap_by_name(RelationGetDescr(partition), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); for (int j = 0; j < numfks; j++) mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1]; @@ -9822,7 +9825,8 @@ CloneFkReferenced(Relation parentRel, Relation partitionRel) table_close(pg_constraint, RowShareLock); attmap = build_attrmap_by_name(RelationGetDescr(partitionRel), - RelationGetDescr(parentRel)); + RelationGetDescr(parentRel), + false); foreach(cell, clone) { Oid constrOid = lfirst_oid(cell); @@ -9969,7 +9973,8 @@ CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel) * different. This map is used to convert them. */ attmap = build_attrmap_by_name(RelationGetDescr(partRel), - RelationGetDescr(parentRel)); + RelationGetDescr(parentRel), + false); partFKs = copyObject(RelationGetFKeyList(partRel)); @@ -11878,7 +11883,8 @@ ATPrepAlterColumnType(List **wqueue, cmd = copyObject(cmd); attmap = build_attrmap_by_name(RelationGetDescr(childrel), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); ((ColumnDef *) cmd->def)->cooked_default = map_variable_attnos(def->cooked_default, 1, 0, @@ -17561,7 +17567,8 @@ AttachPartitionEnsureIndexes(Relation rel, Relation attachrel) /* construct an indexinfo to compare existing indexes against */ info = BuildIndexInfo(idxRel); attmap = build_attrmap_by_name(RelationGetDescr(attachrel), - RelationGetDescr(rel)); + RelationGetDescr(rel), + false); constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(rel), idx); /* @@ -18447,7 +18454,8 @@ ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name) childInfo = BuildIndexInfo(partIdx); parentInfo = BuildIndexInfo(parentIdx); attmap = build_attrmap_by_name(RelationGetDescr(partTbl), - RelationGetDescr(parentTbl)); + RelationGetDescr(parentTbl), + false); if (!CompareIndexInfo(childInfo, parentInfo, partIdx->rd_indcollation, parentIdx->rd_indcollation, diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c index 4df05a0b33..5bfa730e8a 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -381,10 +381,6 @@ UpdateRangeTableOfViewParse(Oid viewOid, Query *viewParse) false, false); rt_entry2 = nsitem->p_rte; - /* Must override addRangeTableEntry's default access-check flags */ - rt_entry1->requiredPerms = 0; - rt_entry2->requiredPerms = 0; - new_rt = lcons(rt_entry1, lcons(rt_entry2, viewParse->rtable)); viewParse->rtable = new_rt; diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index b3ce4bae53..5bde876b78 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -73,7 +73,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 */ @@ -89,8 +89,8 @@ 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, +static bool ExecCheckOneRelPerms(RelPermissionInfo *perminfo); +static bool ExecCheckPermissionsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, AclMode requiredPerms); static void ExecCheckXactReadOnly(PlannedStmt *plannedstmt); @@ -552,8 +552,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,38 +565,39 @@ ExecutorRewind(QueryDesc *queryDesc) * See rewrite/rowsecurity.c. */ bool -ExecCheckRTPerms(List *rangeTable, bool ereport_on_violation) +ExecCheckPermissions(List *relpermlist, + bool ereport_on_violation) { ListCell *l; bool result = true; - foreach(l, rangeTable) + foreach(l, relpermlist) { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); + RelPermissionInfo *perminfo = (RelPermissionInfo *) lfirst(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) (relpermlist, 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(RelPermissionInfo *perminfo) { AclMode requiredPerms; AclMode relPerms; @@ -604,32 +605,21 @@ ExecCheckRTEPerms(RangeTblEntry *rte) Oid relOid; Oid userid; - /* - * 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; + requiredPerms = perminfo->requiredPerms; + Assert(requiredPerms != 0); - /* - * No work if requiredPerms is empty. - */ - requiredPerms = rte->requiredPerms; - if (requiredPerms == 0) - return true; - - relOid = rte->relid; + relOid = perminfo->relid; + Assert(OidIsValid(relOid)); /* * 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 ExecCheckPermisssions and pass the userid down from + * there. But for now, no need for the extra clutter. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); /* * We must have *all* the requiredPerms bits, but some of the bits can be @@ -663,14 +653,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; @@ -695,15 +685,15 @@ 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, + if (remainingPerms & ACL_INSERT && !ExecCheckPermissionsModified(relOid, userid, - rte->insertedCols, + perminfo->insertedCols, ACL_INSERT)) return false; - if (remainingPerms & ACL_UPDATE && !ExecCheckRTEPermsModified(relOid, + if (remainingPerms & ACL_UPDATE && !ExecCheckPermissionsModified(relOid, userid, - rte->updatedCols, + perminfo->updatedCols, ACL_UPDATE)) return false; } @@ -711,12 +701,12 @@ ExecCheckRTEPerms(RangeTblEntry *rte) } /* - * 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, +ExecCheckPermissionsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols, AclMode requiredPerms) { int col = -1; @@ -771,17 +761,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->relpermlist) { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); - - if (rte->rtekind != RTE_RELATION) - continue; + RelPermissionInfo *perminfo = (RelPermissionInfo *) lfirst(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)); @@ -813,9 +800,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(plannedstmt->relpermlist, true); + estate->es_relpermlist = plannedstmt->relpermlist; /* * initialize the node's execution state @@ -1773,7 +1761,7 @@ ExecPartitionCheckEmitError(ResultRelInfo *resultRelInfo, old_tupdesc = RelationGetDescr(resultRelInfo->ri_RelationDesc); /* a reverse map */ - map = build_attrmap_by_name_if_req(old_tupdesc, tupdesc); + map = build_attrmap_by_name_if_req(old_tupdesc, tupdesc, false); /* * Partition-specific slot's tupdesc can't be changed, so allocate a @@ -1858,7 +1846,8 @@ ExecConstraints(ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(orig_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, so @@ -1910,7 +1899,8 @@ ExecConstraints(ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(old_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, so @@ -2017,7 +2007,8 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, tupdesc = RelationGetDescr(rootrel->ri_RelationDesc); /* a reverse map */ map = build_attrmap_by_name_if_req(old_tupdesc, - tupdesc); + tupdesc, + false); /* * Partition-specific slot's tupdesc can't be changed, diff --git a/src/backend/executor/execParallel.c b/src/backend/executor/execParallel.c index f8a4a40e7b..6c932b8261 100644 --- a/src/backend/executor/execParallel.c +++ b/src/backend/executor/execParallel.c @@ -184,6 +184,7 @@ ExecSerializePlan(Plan *plan, EState *estate) pstmt->parallelModeNeeded = false; pstmt->planTree = plan; pstmt->rtable = estate->es_range_table; + pstmt->relpermlist = NIL; pstmt->resultRelations = NIL; pstmt->appendRelations = NIL; diff --git a/src/backend/executor/execPartition.c b/src/backend/executor/execPartition.c index 5c723bc54e..f4456a1aca 100644 --- a/src/backend/executor/execPartition.c +++ b/src/backend/executor/execPartition.c @@ -574,7 +574,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, */ part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); wcoList = (List *) map_variable_attnos((Node *) wcoList, firstVarno, 0, @@ -631,7 +632,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, if (part_attmap == NULL) part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); returningList = (List *) map_variable_attnos((Node *) returningList, firstVarno, 0, @@ -773,7 +775,8 @@ ExecInitPartitionInfo(ModifyTableState *mtstate, EState *estate, if (part_attmap == NULL) part_attmap = build_attrmap_by_name(RelationGetDescr(partrel), - RelationGetDescr(firstResultRel)); + RelationGetDescr(firstResultRel), + false); onconflset = (List *) map_variable_attnos((Node *) onconflset, INNER_VAR, 0, @@ -1040,7 +1043,8 @@ ExecInitPartitionDispatchInfo(EState *estate, * routing. */ pd->tupmap = build_attrmap_by_name_if_req(RelationGetDescr(parent_pd->reldesc), - tupdesc); + tupdesc, + false); pd->tupslot = pd->tupmap ? MakeSingleTupleTableSlot(tupdesc, &TTSOpsVirtual) : NULL; } diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index 6ef37c0886..7e649be151 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" @@ -1251,32 +1252,76 @@ ExecGetChildToRootMap(ResultRelInfo *resultRelInfo) return resultRelInfo->ri_ChildToRootMap; } +/* + * Return the map needed to convert "root" table column bitmapsets to the + * rowtype of an individual child table. Note that a NULL result is valid and + * means that no conversion is needed. + */ +AttrMap * +ExecGetRootToChildMap(ResultRelInfo *resultRelInfo, + EState *estate) +{ + /* If we didn't already do so, compute the map for this child. */ + if (!resultRelInfo->ri_RootToChildMapValid) + { + ResultRelInfo *rootRelInfo = resultRelInfo->ri_RootResultRelInfo; + MemoryContext oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); + + if (rootRelInfo) + { + /* + * Passing 'true' below means any columns present in the child + * table but not in the root parent are ignored. + */ + resultRelInfo->ri_RootToChildMap = + build_attrmap_by_name_if_req(RelationGetDescr(rootRelInfo->ri_RelationDesc), + RelationGetDescr(resultRelInfo->ri_RelationDesc), + true); + } + else /* this isn't a child result rel */ + resultRelInfo->ri_RootToChildMap = NULL; + + resultRelInfo->ri_RootToChildMapValid = true; + + MemoryContextSwitchTo(oldcontext); + } + + return resultRelInfo->ri_RootToChildMap; +} + /* Return a bitmap representing columns being inserted */ 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. + * The columns are stored in estate->relpermlist. If this ResultRelInfo + * represents a child relation (a partition routing target of an INSERT or + * a child UPDATE target), it doesn't have an entry of its own, so fetch + * the parent's entry and map the columns to the order they are in the + * partition. */ - if (relinfo->ri_RangeTableIndex != 0) + if (relinfo->ri_RootResultRelInfo) { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); - - return rte->insertedCols; + ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; + AttrMap *map = ExecGetRootToChildMap(relinfo, estate); + RelPermissionInfo *perminfo = + GetRelPermissionInfo(estate->es_relpermlist, + RelationGetRelid(rootRelInfo->ri_RelationDesc), + false); + + if (map) + return execute_attr_map_cols(map, perminfo->insertedCols); + else + return perminfo->insertedCols; } - else if (relinfo->ri_RootResultRelInfo) + else if (relinfo->ri_RangeTableIndex != 0) { - ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); + RelPermissionInfo *perminfo = + GetRelPermissionInfo(estate->es_relpermlist, + RelationGetRelid(relinfo->ri_RelationDesc), + false); - if (relinfo->ri_RootToPartitionMap != NULL) - return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->insertedCols); - else - return rte->insertedCols; + return perminfo->insertedCols; } else { @@ -1295,22 +1340,28 @@ Bitmapset * ExecGetUpdatedCols(ResultRelInfo *relinfo, EState *estate) { /* see ExecGetInsertedCols() */ - if (relinfo->ri_RangeTableIndex != 0) + if (relinfo->ri_RootResultRelInfo) { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); - - return rte->updatedCols; + ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; + AttrMap *map = ExecGetRootToChildMap(relinfo, estate); + RelPermissionInfo *perminfo = + GetRelPermissionInfo(estate->es_relpermlist, + RelationGetRelid(rootRelInfo->ri_RelationDesc), + false); + + if (map) + return execute_attr_map_cols(map, perminfo->updatedCols); + else + return perminfo->updatedCols; } - else if (relinfo->ri_RootResultRelInfo) + else if (relinfo->ri_RangeTableIndex != 0) { - ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); + RelPermissionInfo *perminfo = + GetRelPermissionInfo(estate->es_relpermlist, + RelationGetRelid(relinfo->ri_RelationDesc), + false); - if (relinfo->ri_RootToPartitionMap != NULL) - return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->updatedCols); - else - return rte->updatedCols; + return perminfo->updatedCols; } else return NULL; @@ -1321,22 +1372,28 @@ Bitmapset * ExecGetExtraUpdatedCols(ResultRelInfo *relinfo, EState *estate) { /* see ExecGetInsertedCols() */ - if (relinfo->ri_RangeTableIndex != 0) + if (relinfo->ri_RootResultRelInfo) { - RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate); - - return rte->extraUpdatedCols; + ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; + AttrMap *map = ExecGetRootToChildMap(relinfo, estate); + RelPermissionInfo *perminfo = + GetRelPermissionInfo(estate->es_relpermlist, + RelationGetRelid(rootRelInfo->ri_RelationDesc), + false); + + if (map) + return execute_attr_map_cols(map, perminfo->extraUpdatedCols); + else + return perminfo->extraUpdatedCols; } - else if (relinfo->ri_RootResultRelInfo) + else if (relinfo->ri_RangeTableIndex != 0) { - ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo; - RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate); + RelPermissionInfo *perminfo = + GetRelPermissionInfo(estate->es_relpermlist, + RelationGetRelid(relinfo->ri_RelationDesc), + false); - if (relinfo->ri_RootToPartitionMap != NULL) - return execute_attr_map_cols(relinfo->ri_RootToPartitionMap->attrMap, - rte->extraUpdatedCols); - else - return rte->extraUpdatedCols; + return perminfo->extraUpdatedCols; } else return NULL; diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 38251c2b8e..db56906d17 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -93,6 +93,7 @@ _copyPlannedStmt(const PlannedStmt *from) COPY_SCALAR_FIELD(jitFlags); COPY_NODE_FIELD(planTree); COPY_NODE_FIELD(rtable); + COPY_NODE_FIELD(relpermlist); COPY_NODE_FIELD(resultRelations); COPY_NODE_FIELD(appendRelations); COPY_NODE_FIELD(subplans); @@ -776,6 +777,7 @@ _copyForeignScan(const ForeignScan *from) */ COPY_SCALAR_FIELD(operation); COPY_SCALAR_FIELD(resultRelation); + COPY_SCALAR_FIELD(checkAsUser); COPY_SCALAR_FIELD(fs_server); COPY_NODE_FIELD(fdw_exprs); COPY_NODE_FIELD(fdw_private); @@ -1264,6 +1266,25 @@ _copyPlanRowMark(const PlanRowMark *from) return newnode; } +/* + * _copyRelPermissionInfo + */ +static RelPermissionInfo * +_copyRelPermissionInfo(const RelPermissionInfo *from) +{ + RelPermissionInfo *newnode = makeNode(RelPermissionInfo); + + COPY_SCALAR_FIELD(relid); + COPY_SCALAR_FIELD(inh); + COPY_SCALAR_FIELD(requiredPerms); + COPY_SCALAR_FIELD(checkAsUser); + COPY_BITMAPSET_FIELD(selectedCols); + COPY_BITMAPSET_FIELD(insertedCols); + COPY_BITMAPSET_FIELD(updatedCols); + COPY_BITMAPSET_FIELD(extraUpdatedCols); + + return newnode; +} static PartitionPruneInfo * _copyPartitionPruneInfo(const PartitionPruneInfo *from) @@ -2480,12 +2501,6 @@ _copyRangeTblEntry(const RangeTblEntry *from) COPY_SCALAR_FIELD(lateral); COPY_SCALAR_FIELD(inh); COPY_SCALAR_FIELD(inFromCl); - COPY_SCALAR_FIELD(requiredPerms); - COPY_SCALAR_FIELD(checkAsUser); - COPY_BITMAPSET_FIELD(selectedCols); - COPY_BITMAPSET_FIELD(insertedCols); - COPY_BITMAPSET_FIELD(updatedCols); - COPY_BITMAPSET_FIELD(extraUpdatedCols); COPY_NODE_FIELD(securityQuals); return newnode; @@ -3165,6 +3180,7 @@ _copyQuery(const Query *from) COPY_SCALAR_FIELD(isReturn); COPY_NODE_FIELD(cteList); COPY_NODE_FIELD(rtable); + COPY_NODE_FIELD(relpermlist); COPY_NODE_FIELD(jointree); COPY_NODE_FIELD(targetList); COPY_SCALAR_FIELD(override); @@ -5106,6 +5122,9 @@ copyObjectImpl(const void *from) case T_PlanRowMark: retval = _copyPlanRowMark(from); break; + case T_RelPermissionInfo: + retval = _copyRelPermissionInfo(from); + break; case T_PartitionPruneInfo: retval = _copyPartitionPruneInfo(from); break; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 8a1762000c..7603c04784 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -985,6 +985,7 @@ _equalQuery(const Query *a, const Query *b) COMPARE_SCALAR_FIELD(isReturn); COMPARE_NODE_FIELD(cteList); COMPARE_NODE_FIELD(rtable); + COMPARE_NODE_FIELD(relpermlist); COMPARE_NODE_FIELD(jointree); COMPARE_NODE_FIELD(targetList); COMPARE_SCALAR_FIELD(override); @@ -2755,17 +2756,25 @@ _equalRangeTblEntry(const RangeTblEntry *a, const RangeTblEntry *b) COMPARE_SCALAR_FIELD(lateral); COMPARE_SCALAR_FIELD(inh); COMPARE_SCALAR_FIELD(inFromCl); + COMPARE_NODE_FIELD(securityQuals); + + return true; +} + +static bool +_equalRelPermissionInfo(const RelPermissionInfo *a, const RelPermissionInfo *b) +{ + COMPARE_SCALAR_FIELD(relid); + COMPARE_SCALAR_FIELD(inh); COMPARE_SCALAR_FIELD(requiredPerms); COMPARE_SCALAR_FIELD(checkAsUser); COMPARE_BITMAPSET_FIELD(selectedCols); COMPARE_BITMAPSET_FIELD(insertedCols); COMPARE_BITMAPSET_FIELD(updatedCols); COMPARE_BITMAPSET_FIELD(extraUpdatedCols); - COMPARE_NODE_FIELD(securityQuals); return true; } - static bool _equalRangeTblFunction(const RangeTblFunction *a, const RangeTblFunction *b) { @@ -3793,6 +3802,9 @@ equal(const void *a, const void *b) case T_RangeTblEntry: retval = _equalRangeTblEntry(a, b); break; + case T_RelPermissionInfo: + retval = _equalRelPermissionInfo(a, b); + break; case T_RangeTblFunction: retval = _equalRangeTblFunction(a, b); break; diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 87561cbb6f..2d3c239e13 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -308,6 +308,7 @@ _outPlannedStmt(StringInfo str, const PlannedStmt *node) WRITE_INT_FIELD(jitFlags); WRITE_NODE_FIELD(planTree); WRITE_NODE_FIELD(rtable); + WRITE_NODE_FIELD(relpermlist); WRITE_NODE_FIELD(resultRelations); WRITE_NODE_FIELD(appendRelations); WRITE_NODE_FIELD(subplans); @@ -702,6 +703,7 @@ _outForeignScan(StringInfo str, const ForeignScan *node) WRITE_ENUM_FIELD(operation, CmdType); WRITE_UINT_FIELD(resultRelation); + WRITE_OID_FIELD(checkAsUser); WRITE_OID_FIELD(fs_server); WRITE_NODE_FIELD(fdw_exprs); WRITE_NODE_FIELD(fdw_private); @@ -988,6 +990,21 @@ _outPlanRowMark(StringInfo str, const PlanRowMark *node) WRITE_BOOL_FIELD(isParent); } +static void +_outRelPermissionInfo(StringInfo str, const RelPermissionInfo *node) +{ + WRITE_NODE_TYPE("RELPERMISSIONINFO"); + + WRITE_UINT_FIELD(relid); + WRITE_BOOL_FIELD(inh); + WRITE_UINT_FIELD(requiredPerms); + WRITE_UINT_FIELD(checkAsUser); + WRITE_BITMAPSET_FIELD(selectedCols); + WRITE_BITMAPSET_FIELD(insertedCols); + WRITE_BITMAPSET_FIELD(updatedCols); + WRITE_BITMAPSET_FIELD(extraUpdatedCols); +} + static void _outPartitionPruneInfo(StringInfo str, const PartitionPruneInfo *node) { @@ -2263,6 +2280,7 @@ _outPlannerGlobal(StringInfo str, const PlannerGlobal *node) WRITE_NODE_FIELD(subplans); WRITE_BITMAPSET_FIELD(rewindPlanIDs); WRITE_NODE_FIELD(finalrtable); + WRITE_NODE_FIELD(finalrelpermlist); WRITE_NODE_FIELD(finalrowmarks); WRITE_NODE_FIELD(resultRelations); WRITE_NODE_FIELD(appendRelations); @@ -3073,6 +3091,7 @@ _outQuery(StringInfo str, const Query *node) WRITE_BOOL_FIELD(isReturn); WRITE_NODE_FIELD(cteList); WRITE_NODE_FIELD(rtable); + WRITE_NODE_FIELD(relpermlist); WRITE_NODE_FIELD(jointree); WRITE_NODE_FIELD(targetList); WRITE_ENUM_FIELD(override, OverridingKind); @@ -3305,12 +3324,6 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node) WRITE_BOOL_FIELD(lateral); WRITE_BOOL_FIELD(inh); WRITE_BOOL_FIELD(inFromCl); - WRITE_UINT_FIELD(requiredPerms); - WRITE_OID_FIELD(checkAsUser); - WRITE_BITMAPSET_FIELD(selectedCols); - WRITE_BITMAPSET_FIELD(insertedCols); - WRITE_BITMAPSET_FIELD(updatedCols); - WRITE_BITMAPSET_FIELD(extraUpdatedCols); WRITE_NODE_FIELD(securityQuals); } @@ -3992,6 +4005,9 @@ outNode(StringInfo str, const void *obj) case T_PlanRowMark: _outPlanRowMark(str, obj); break; + case T_RelPermissionInfo: + _outRelPermissionInfo(str, obj); + break; case T_PartitionPruneInfo: _outPartitionPruneInfo(str, obj); break; diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index 0dd1ad7dfc..d8e30b8dbb 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -266,6 +266,7 @@ _readQuery(void) READ_BOOL_FIELD(isReturn); READ_NODE_FIELD(cteList); READ_NODE_FIELD(rtable); + READ_NODE_FIELD(relpermlist); READ_NODE_FIELD(jointree); READ_NODE_FIELD(targetList); READ_ENUM_FIELD(override, OverridingKind); @@ -1505,13 +1506,24 @@ _readRangeTblEntry(void) READ_BOOL_FIELD(lateral); READ_BOOL_FIELD(inh); READ_BOOL_FIELD(inFromCl); - READ_UINT_FIELD(requiredPerms); - READ_OID_FIELD(checkAsUser); + READ_NODE_FIELD(securityQuals); + + READ_DONE(); +} + +static RelPermissionInfo * +_readRelPermissionInfo(void) +{ + READ_LOCALS(RelPermissionInfo); + + READ_INT_FIELD(relid); + READ_BOOL_FIELD(inh); + READ_INT_FIELD(requiredPerms); + READ_INT_FIELD(checkAsUser); READ_BITMAPSET_FIELD(selectedCols); READ_BITMAPSET_FIELD(insertedCols); READ_BITMAPSET_FIELD(updatedCols); READ_BITMAPSET_FIELD(extraUpdatedCols); - READ_NODE_FIELD(securityQuals); READ_DONE(); } @@ -1590,6 +1602,7 @@ _readPlannedStmt(void) READ_INT_FIELD(jitFlags); READ_NODE_FIELD(planTree); READ_NODE_FIELD(rtable); + READ_NODE_FIELD(relpermlist); READ_NODE_FIELD(resultRelations); READ_NODE_FIELD(appendRelations); READ_NODE_FIELD(subplans); @@ -2075,6 +2088,7 @@ _readForeignScan(void) READ_ENUM_FIELD(operation, CmdType); READ_UINT_FIELD(resultRelation); + READ_OID_FIELD(checkAsUser); READ_OID_FIELD(fs_server); READ_NODE_FIELD(fdw_exprs); READ_NODE_FIELD(fdw_private); @@ -2847,6 +2861,8 @@ parseNodeString(void) return_value = _readAppendRelInfo(); else if (MATCH("RTE", 3)) return_value = _readRangeTblEntry(); + else if (MATCH("RELPERMISSIONINFO", 17)) + return_value = _readRelPermissionInfo(); else if (MATCH("RANGETBLFUNCTION", 16)) return_value = _readRangeTblFunction(); else if (MATCH("TABLESAMPLECLAUSE", 17)) diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index a5f6d678cc..700643d2c4 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -4051,6 +4051,9 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path, /* Copy cost data from Path to Plan; no need to make FDW do this */ copy_generic_path_info(&scan_plan->scan.plan, &best_path->path); + /* Copy user OID to access as; likewise no need to make FDW do this */ + scan_plan->checkAsUser = rel->userid; + /* Copy foreign server OID; likewise, no need to make FDW do this */ scan_plan->fs_server = rel->serverid; @@ -5698,7 +5701,8 @@ make_foreignscan(List *qptlist, node->operation = CMD_SELECT; node->resultRelation = 0; - /* fs_server will be filled in by create_foreignscan_plan */ + /* checkAsUser, fs_server will be filled in by create_foreignscan_plan */ + node->checkAsUser = InvalidOid; node->fs_server = InvalidOid; node->fdw_exprs = fdw_exprs; node->fdw_private = fdw_private; diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 1e42d75465..55505bfee3 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -56,6 +56,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" @@ -305,6 +306,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, glob->subroots = NIL; glob->rewindPlanIDs = NULL; glob->finalrtable = NIL; + glob->finalrelpermlist = NIL; glob->finalrowmarks = NIL; glob->resultRelations = NIL; glob->appendRelations = NIL; @@ -492,6 +494,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, /* final cleanup of the plan */ Assert(glob->finalrtable == NIL); + Assert(glob->finalrelpermlist == NIL); Assert(glob->finalrowmarks == NIL); Assert(glob->resultRelations == NIL); Assert(glob->appendRelations == NIL); @@ -519,6 +522,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions, result->parallelModeNeeded = glob->parallelModeNeeded; result->planTree = top_plan; result->rtable = glob->finalrtable; + result->relpermlist = glob->finalrelpermlist; result->resultRelations = glob->resultRelations; result->appendRelations = glob->appendRelations; result->subplans = glob->subplans; @@ -5925,6 +5929,7 @@ plan_cluster_use_sort(Oid tableOid, Oid indexOid) rte->inh = false; rte->inFromCl = true; query->rtable = list_make1(rte); + AddRelPermissionInfo(&query->relpermlist, tableOid); /* Set up RTE/RelOptInfo arrays */ setup_simple_rel_arrays(root); @@ -6052,6 +6057,7 @@ plan_create_index_workers(Oid tableOid, Oid indexOid) rte->inh = true; rte->inFromCl = true; query->rtable = list_make1(rte); + AddRelPermissionInfo(&query->relpermlist, tableOid); /* 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 e50624c465..b519c53f70 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -260,6 +260,10 @@ set_plan_references(PlannerInfo *root, Plan *plan) */ add_rtes_to_flat_rtable(root, false); + /* Also add the query's RelPermissionInfos to the global list. */ + glob->finalrelpermlist = list_concat(glob->finalrelpermlist, + root->parse->relpermlist); + /* * Adjust RT indexes of PlanRowMarks and add to final rowmarks list */ @@ -438,9 +442,7 @@ flatten_rtes_walker(Node *node, PlannerGlobal *glob) * 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. + * which are needed by EXPLAIN. */ static void add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte) diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index c9f7a09d10..5a622cabe5 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -1504,6 +1504,8 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink, /* Now we can attach the modified subquery rtable to the parent */ parse->rtable = list_concat(parse->rtable, subselect->rtable); + parse->relpermlist = MergeRelPermissionInfos(parse->relpermlist, + subselect->relpermlist); /* * And finally, build the JoinExpr node. diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c index 224c5153b1..b1a1eec17f 100644 --- a/src/backend/optimizer/prep/prepjointree.c +++ b/src/backend/optimizer/prep/prepjointree.c @@ -1131,6 +1131,9 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte, */ parse->rtable = list_concat(parse->rtable, subquery->rtable); + parse->relpermlist = MergeRelPermissionInfos(parse->relpermlist, + subquery->relpermlist); + /* * Pull up any FOR UPDATE/SHARE markers, too. (OffsetVarNodes already * adjusted the marker rtindexes, so just concat the lists.) @@ -1268,6 +1271,9 @@ pull_up_simple_union_all(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte) * Append child RTEs to parent rtable. */ root->parse->rtable = list_concat(root->parse->rtable, rtable); + root->parse->relpermlist = + MergeRelPermissionInfos(root->parse->relpermlist, + subquery->relpermlist); /* * Recursively scan the subquery's setOperations tree and add @@ -1311,6 +1317,7 @@ pull_up_union_leaf_queries(Node *setOp, PlannerInfo *root, int parentRTindex, if (IsA(setOp, RangeTblRef)) { RangeTblRef *rtr = (RangeTblRef *) setOp; + RangeTblEntry *rte = rt_fetch(rtr->rtindex, root->parse->rtable); int childRTindex; AppendRelInfo *appinfo; @@ -1345,6 +1352,25 @@ pull_up_union_leaf_queries(Node *setOp, PlannerInfo *root, int parentRTindex, rtr->rtindex = childRTindex; (void) pull_up_subqueries_recurse(root, (Node *) rtr, NULL, NULL, appinfo); + + /* + * If the subquery was not pulled up, need to merge its relpermlist + * entries into the parent query by ourselves. It's a no-op if the + * subquery is not a simple one, because its relpermlist would be + * empty in that case. + * + * HACK: this relies on rte->subquery still being non-NULL to know + * that the subquery pull-up failed; it's set to NULL by + * pull_up_simple_subquery() when it succeeds. + */ + if (rte->rtekind == RTE_SUBQUERY && rte->subquery) + { + Query *subquery = rte->subquery; + + root->parse->relpermlist = + MergeRelPermissionInfos(root->parse->relpermlist, + subquery->relpermlist); + } } else if (IsA(setOp, SetOperationStmt)) { diff --git a/src/backend/optimizer/util/inherit.c b/src/backend/optimizer/util/inherit.c index c758172efa..49d707da5e 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,8 @@ static void expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, + Bitmapset *parent_updatedCols, + Bitmapset *parent_extraUpdatedCols, PlanRowMark *top_parentrc, LOCKMODE lockmode); static void expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, @@ -131,6 +134,9 @@ expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, /* Scan the inheritance set and expand it */ if (oldrelation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) { + RelPermissionInfo *root_perminfo = + GetRelPermissionInfo(root->parse->relpermlist, rte->relid, false); + /* * Partitioned table, so set up for partitioning. */ @@ -141,7 +147,10 @@ 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, + root_perminfo->updatedCols, + root_perminfo->extraUpdatedCols, + oldrc, lockmode); } else { @@ -305,6 +314,8 @@ static void expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, RangeTblEntry *parentrte, Index parentRTindex, Relation parentrel, + Bitmapset *parent_updatedCols, + Bitmapset *parent_extraUpdatedCols, PlanRowMark *top_parentrc, LOCKMODE lockmode) { PartitionDesc partdesc; @@ -323,20 +334,16 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo, 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. + * Note down whether any partition key cols are being updated. */ 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. */ - Assert(!has_partition_attrs(parentrel, parentrte->extraUpdatedCols, NULL)); + Assert(!has_partition_attrs(parentrel, parent_extraUpdatedCols, NULL)); /* Nothing further to do here if there are no partitions. */ if (partdesc->nparts == 0) @@ -402,9 +409,23 @@ 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; + Bitmapset *child_extraUpdatedCols; + + child_updatedCols = translate_col_privs(parent_updatedCols, + appinfo->translated_vars); + child_extraUpdatedCols = translate_col_privs(parent_extraUpdatedCols, + appinfo->translated_vars); + expand_partitioned_rtentry(root, childrelinfo, childrte, childRTindex, - childrel, top_parentrc, lockmode); + childrel, + child_updatedCols, + child_extraUpdatedCols, + top_parentrc, lockmode); + } /* Close child relation, but keep locks */ table_close(childrel, NoLock); @@ -439,7 +460,6 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, Index *childRTindex_p) { Query *parse = root->parse; - Oid parentOID = RelationGetRelid(parentrel); Oid childOID = RelationGetRelid(childrel); RangeTblEntry *childrte; Index childRTindex; @@ -451,17 +471,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)); @@ -471,12 +489,11 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte, /* A partitioned child will need to be expanded further. */ if (childrte->relkind == RELKIND_PARTITIONED_TABLE) { - Assert(childOID != parentOID); + Assert(childOID != RelationGetRelid(parentrel)); childrte->inh = true; } else childrte->inh = false; - childrte->requiredPerms = 0; childrte->securityQuals = NIL; /* Link not-yet-fully-filled child RTE into data structures */ @@ -539,34 +556,6 @@ 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. - */ - 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. @@ -866,3 +855,84 @@ apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel, return true; } + +/* + * translate_col_privs_recurse + * 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_recurse(PlannerInfo *root, Index relid, + Bitmapset *top_parent_cols, + Relids top_parent_relids) +{ + AppendRelInfo *appinfo; + + Assert(root->append_rel_array != NULL); + appinfo = root->append_rel_array[relid]; + Assert(appinfo != NULL); + + /* + * Must recurse if 'relid' doesn't appear to the parent's direct child, + * because appinfo->translated_vars maps between directly related parent + * and child relation pairs. + */ + if (bms_singleton_member(top_parent_relids) != appinfo->parent_relid) + translate_col_privs_recurse(root, appinfo->parent_relid, + top_parent_cols, + top_parent_relids); + + return translate_col_privs(top_parent_cols, appinfo->translated_vars); +} + +/* + * GetRelAllUpdatedCols + * Returns the set of columns of a given "simple" relation that are updated + * by this query + */ +Bitmapset * +GetRelAllUpdatedCols(PlannerInfo *root, RelOptInfo *rel) +{ + RangeTblEntry *rte; + RelPermissionInfo *perminfo; + Bitmapset *updatedCols, + *extraUpdatedCols; + + if (!IS_SIMPLE_REL(rel)) + return NULL; + + /* + * If it's a simple "base" rel, can just fetch its RelPermissionInfo and + * get the needed columns from there. For "other" rels, must look up the + * root parent relation mentioned in the query, because only that one + * gets assigned a RelPermissionInfo, and translate the columns found + * there to match the input relation. + */ + if (rel->top_parent_relids != NULL) + rte = planner_rt_fetch(bms_singleton_member(rel->top_parent_relids), + root); + else + rte = planner_rt_fetch(rel->relid, root); + + perminfo = GetRelPermissionInfo(root->parse->relpermlist, + rte->relid, + false); + + if (rel->top_parent_relids != NULL) + { + updatedCols = translate_col_privs_recurse(root, rel->relid, + perminfo->updatedCols, + rel->top_parent_relids); + extraUpdatedCols = translate_col_privs_recurse(root, rel->relid, + perminfo->extraUpdatedCols, + rel->top_parent_relids); + } + else + { + updatedCols = perminfo->updatedCols; + extraUpdatedCols = perminfo->extraUpdatedCols; + } + + return bms_union(updatedCols, extraUpdatedCols); +} diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index 47769cea45..6c7eeeb419 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" @@ -236,7 +237,13 @@ 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) + { + /* otherrels use the root parent's value. */ + rel->userid = parent ? parent->userid : + GetRelPermissionInfo(root->parse->relpermlist, + rte->relid, false)->checkAsUser; + } rel->useridiscurrent = false; rel->fdwroutine = NULL; rel->fdw_private = NULL; diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 15669c8238..2d929f097b 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -475,6 +475,7 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt) /* done building the range table and jointree */ qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -507,7 +508,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) List *icolumns; List *attrnos; ParseNamespaceItem *nsitem; - RangeTblEntry *rte; + RelPermissionInfo *perminfo; ListCell *icols; ListCell *attnos; ListCell *lc; @@ -626,6 +627,12 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) * the target column's type, which we handle below. */ sub_pstate->p_rtable = sub_rtable; + + /* + * Using the value of pstate->p_relpermlist after setTargetTable() has + * been performed. + */ + sub_pstate->p_relpermlist = pstate->p_relpermlist; sub_pstate->p_joinexprs = NIL; /* sub_rtable has no joins */ sub_pstate->p_namespace = sub_namespace; sub_pstate->p_resolve_unknowns = false; @@ -851,7 +858,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) * Generate query's target list using the computed list of expressions. * Also, mark all the target columns as needing insert permissions. */ - rte = pstate->p_target_nsitem->p_rte; + perminfo = pstate->p_target_nsitem->p_perminfo; qry->targetList = NIL; Assert(list_length(exprList) <= list_length(icolumns)); forthree(lc, exprList, icols, icolumns, attnos, attrnos) @@ -867,8 +874,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) false); qry->targetList = lappend(qry->targetList, tle); - rte->insertedCols = bms_add_member(rte->insertedCols, - attr_num - FirstLowInvalidHeapAttributeNumber); + perminfo->insertedCols = bms_add_member(perminfo->insertedCols, + attr_num - FirstLowInvalidHeapAttributeNumber); } /* @@ -895,6 +902,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) /* done building the range table and jointree */ qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasTargetSRFs = pstate->p_hasTargetSRFs; @@ -1053,8 +1061,6 @@ transformOnConflictClause(ParseState *pstate, * (We'll check the actual target relation, instead.) */ exclRte->relkind = RELKIND_COMPOSITE_TYPE; - exclRte->requiredPerms = 0; - /* other permissions fields in exclRte are already empty */ /* Create EXCLUDED rel's targetlist for use by EXPLAIN */ exclRelTlist = BuildOnConflictExcludedTargetlist(targetrel, @@ -1348,6 +1354,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt) resolveTargetListUnknowns(pstate, qry->targetList); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -1582,6 +1589,7 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt) linitial(stmt->lockingClause))->strength)))); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -1828,6 +1836,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) qry->limitOption = stmt->limitOption; qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -2286,6 +2295,7 @@ transformReturnStmt(ParseState *pstate, ReturnStmt *stmt) if (pstate->p_resolve_unknowns) resolveTargetListUnknowns(pstate, qry->targetList); qry->rtable = pstate->p_rtable; + Assert(pstate->p_relpermlist == NIL); qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; qry->hasWindowFuncs = pstate->p_hasWindowFuncs; @@ -2352,6 +2362,7 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt) qry->targetList = transformUpdateTargetList(pstate, stmt->targetList); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasTargetSRFs = pstate->p_hasTargetSRFs; @@ -2370,7 +2381,7 @@ static List * transformUpdateTargetList(ParseState *pstate, List *origTlist) { List *tlist = NIL; - RangeTblEntry *target_rte; + RelPermissionInfo *target_perminfo; ListCell *orig_tl; ListCell *tl; @@ -2382,7 +2393,7 @@ transformUpdateTargetList(ParseState *pstate, List *origTlist) pstate->p_next_resno = RelationGetNumberOfAttributes(pstate->p_target_relation) + 1; /* Prepare non-junk columns for assignment to target table */ - target_rte = pstate->p_target_nsitem->p_rte; + target_perminfo = pstate->p_target_nsitem->p_perminfo; orig_tl = list_head(origTlist); foreach(tl, tlist) @@ -2423,8 +2434,8 @@ transformUpdateTargetList(ParseState *pstate, List *origTlist) origTarget->location); /* Mark the target column as requiring update permissions */ - target_rte->updatedCols = bms_add_member(target_rte->updatedCols, - attrno - FirstLowInvalidHeapAttributeNumber); + target_perminfo->updatedCols = bms_add_member(target_perminfo->updatedCols, + attrno - FirstLowInvalidHeapAttributeNumber); orig_tl = lnext(origTlist, orig_tl); } @@ -2711,6 +2722,7 @@ transformPLAssignStmt(ParseState *pstate, PLAssignStmt *stmt) &qry->targetList); qry->rtable = pstate->p_rtable; + qry->relpermlist = pstate->p_relpermlist; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; @@ -3189,9 +3201,17 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, switch (rte->rtekind) { case RTE_RELATION: - applyLockingClause(qry, i, lc->strength, lc->waitPolicy, - pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + { + RelPermissionInfo *perminfo; + + applyLockingClause(qry, i, + lc->strength, + lc->waitPolicy, + pushedDown); + perminfo = GetRelPermissionInfo(qry->relpermlist, + rte->relid, false); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; + } break; case RTE_SUBQUERY: applyLockingClause(qry, i, lc->strength, lc->waitPolicy, @@ -3247,9 +3267,18 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, switch (rte->rtekind) { case RTE_RELATION: - applyLockingClause(qry, i, lc->strength, - lc->waitPolicy, pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + { + RelPermissionInfo *perminfo; + + applyLockingClause(qry, i, + lc->strength, + lc->waitPolicy, + pushedDown); + perminfo = + GetRelPermissionInfo(qry->relpermlist, + rte->relid, false); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; + } break; case RTE_SUBQUERY: applyLockingClause(qry, i, lc->strength, diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index b3f151d33b..d5537c424a 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -223,7 +223,7 @@ setTargetTable(ParseState *pstate, RangeVar *relation, * analysis, we will add the ACL_SELECT bit back again; see * markVarForSelectPriv and its callers. */ - nsitem->p_rte->requiredPerms = requiredPerms; + nsitem->p_perminfo->requiredPerms = requiredPerms; /* * If UPDATE/DELETE, add table to joinlist and namespace. @@ -3242,16 +3242,17 @@ transformOnConflictArbiter(ParseState *pstate, if (infer->conname) { Oid relid = RelationGetRelid(pstate->p_target_relation); - RangeTblEntry *rte = pstate->p_target_nsitem->p_rte; + RelPermissionInfo *perminfo = pstate->p_target_nsitem->p_perminfo; Bitmapset *conattnos; conattnos = get_relation_constraint_attnos(relid, infer->conname, false, constraint); /* Make sure the rel as a whole is marked for SELECT access */ - rte->requiredPerms |= ACL_SELECT; + perminfo->requiredPerms |= ACL_SELECT; /* Mark the constrained columns as requiring SELECT access */ - rte->selectedCols = bms_add_members(rte->selectedCols, conattnos); + perminfo->selectedCols = bms_add_members(perminfo->selectedCols, + conattnos); } } diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index 7465919044..9ecbbb09f7 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -1010,10 +1010,13 @@ markRTEForSelectPriv(ParseState *pstate, int rtindex, AttrNumber col) if (rte->rtekind == RTE_RELATION) { + RelPermissionInfo *perminfo = + GetRelPermissionInfo(pstate->p_relpermlist, rte->relid, false); + /* Make sure the rel as a whole is marked for SELECT access */ - rte->requiredPerms |= ACL_SELECT; + perminfo->requiredPerms |= ACL_SELECT; /* Must offset the attnum to fit in a bitmapset */ - rte->selectedCols = bms_add_member(rte->selectedCols, + perminfo->selectedCols = bms_add_member(perminfo->selectedCols, col - FirstLowInvalidHeapAttributeNumber); } else if (rte->rtekind == RTE_JOIN) @@ -1224,10 +1227,13 @@ chooseScalarFunctionAlias(Node *funcexpr, char *funcname, * * rte: the new RangeTblEntry for the rel * rtindex: its index in the rangetable list + * perminfo: permission list entry for the rel * tupdesc: the physical column information */ static ParseNamespaceItem * -buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, TupleDesc tupdesc) +buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, + RelPermissionInfo *perminfo, + TupleDesc tupdesc) { ParseNamespaceItem *nsitem; ParseNamespaceColumn *nscolumns; @@ -1263,6 +1269,7 @@ buildNSItemFromTupleDesc(RangeTblEntry *rte, Index rtindex, TupleDesc tupdesc) nsitem->p_names = rte->eref; nsitem->p_rte = rte; nsitem->p_rtindex = rtindex; + nsitem->p_perminfo = perminfo; nsitem->p_nscolumns = nscolumns; /* set default visibility flags; might get changed later */ nsitem->p_rel_visible = true; @@ -1406,6 +1413,7 @@ addRangeTableEntry(ParseState *pstate, bool inFromCl) { RangeTblEntry *rte = makeNode(RangeTblEntry); + RelPermissionInfo *perminfo; char *refname = alias ? alias->aliasname : relation->relname; LOCKMODE lockmode; Relation rel; @@ -1442,7 +1450,7 @@ addRangeTableEntry(ParseState *pstate, buildRelationAliases(rel->rd_att, alias, rte->eref); /* - * Set flags and access permissions. + * Set flags and initialize acesss permissions. * * The initial default on access checks is always check-for-READ-access, * which is the right thing for all except target tables. @@ -1451,12 +1459,14 @@ addRangeTableEntry(ParseState *pstate, rte->inh = inh; rte->inFromCl = inFromCl; - rte->requiredPerms = ACL_SELECT; - rte->checkAsUser = InvalidOid; /* not set-uid by default, either */ - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; + perminfo = AddRelPermissionInfo(&pstate->p_relpermlist, rte->relid); + perminfo->inh = inh; + + /* + * Using |=, not = just in case the permissions entry is shared with + * another RT entry for the same table. + */ + perminfo->requiredPerms |= ACL_SELECT; /* * Add completed RTE to pstate's range table list, so that we know its @@ -1470,7 +1480,7 @@ addRangeTableEntry(ParseState *pstate, * list --- caller must do that if appropriate. */ nsitem = buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), - rel->rd_att); + perminfo, rel->rd_att); /* * Drop the rel refcount, but keep the access lock till end of transaction @@ -1507,6 +1517,7 @@ addRangeTableEntryForRelation(ParseState *pstate, bool inFromCl) { RangeTblEntry *rte = makeNode(RangeTblEntry); + RelPermissionInfo *perminfo = NULL; char *refname = alias ? alias->aliasname : RelationGetRelationName(rel); Assert(pstate != NULL); @@ -1530,7 +1541,7 @@ addRangeTableEntryForRelation(ParseState *pstate, buildRelationAliases(rel->rd_att, alias, rte->eref); /* - * Set flags and access permissions. + * Set flags and initialize access permissions. * * The initial default on access checks is always check-for-READ-access, * which is the right thing for all except target tables. @@ -1539,12 +1550,14 @@ addRangeTableEntryForRelation(ParseState *pstate, rte->inh = inh; rte->inFromCl = inFromCl; - rte->requiredPerms = ACL_SELECT; - rte->checkAsUser = InvalidOid; /* not set-uid by default, either */ - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; + perminfo = AddRelPermissionInfo(&pstate->p_relpermlist, rte->relid); + perminfo->inh = inh; + + /* + * Using |=, not = just in case the permissions entry is shared with + * another RT entry for the same table. + */ + perminfo->requiredPerms |= ACL_SELECT; /* * Add completed RTE to pstate's range table list, so that we know its @@ -1558,7 +1571,7 @@ addRangeTableEntryForRelation(ParseState *pstate, * list --- caller must do that if appropriate. */ return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), - rel->rd_att); + perminfo, rel->rd_att); } /* @@ -1628,21 +1641,15 @@ addRangeTableEntryForSubquery(ParseState *pstate, rte->eref = eref; /* - * Set flags and access permissions. + * Set flags. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for subqueries */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -1935,20 +1942,13 @@ addRangeTableEntryForFunction(ParseState *pstate, /* * Set flags and access permissions. * - * Functions are never checked for access rights (at least, not by the RTE - * permissions mechanism). + * Functions are never checked for access rights (at least, not by + * ExecCheckPermissions()), so no need to perform AddRelPermissionsInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for functions */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -1960,7 +1960,7 @@ addRangeTableEntryForFunction(ParseState *pstate, * Build a ParseNamespaceItem, but don't add it to the pstate's namespace * list --- caller must do that if appropriate. */ - return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), + return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), NULL, tupdesc); } @@ -2006,20 +2006,13 @@ addRangeTableEntryForTableFunc(ParseState *pstate, /* * Set flags and access permissions. * - * Tablefuncs are never checked for access rights (at least, not by the - * RTE permissions mechanism). + * Tablefuncs are never checked for access rights (at least, not by + * ExecCheckPermissions()), so no need to perform AddRelPermissionsInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for tablefunc RTEs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2094,19 +2087,13 @@ addRangeTableEntryForValues(ParseState *pstate, /* * Set flags and access permissions. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = lateral; rte->inh = false; /* never true for values RTEs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2185,19 +2172,13 @@ addRangeTableEntryForJoin(ParseState *pstate, /* * Set flags and access permissions. * - * Joins are never checked for access rights. + * Joins are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for joins */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2212,6 +2193,7 @@ addRangeTableEntryForJoin(ParseState *pstate, nsitem = (ParseNamespaceItem *) palloc(sizeof(ParseNamespaceItem)); nsitem->p_names = rte->eref; nsitem->p_rte = rte; + nsitem->p_perminfo = NULL; nsitem->p_rtindex = list_length(pstate->p_rtable); nsitem->p_nscolumns = nscolumns; /* set default visibility flags; might get changed later */ @@ -2335,19 +2317,13 @@ addRangeTableEntryForCTE(ParseState *pstate, /* * Set flags and access permissions. * - * Subqueries are never checked for access rights. + * Subqueries are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for subqueries */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2461,16 +2437,13 @@ addRangeTableEntryForENR(ParseState *pstate, /* * Set flags and access permissions. * - * ENRs are never checked for access rights. + * ENRs are never checked for access rights, so no need to perform + * AddRelPermissionInfo(). */ rte->lateral = false; rte->inh = false; /* never true for ENRs */ rte->inFromCl = inFromCl; - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - /* * Add completed RTE to pstate's range table list, so that we know its * index. But we don't add it to the join list --- caller must do that if @@ -2482,7 +2455,7 @@ addRangeTableEntryForENR(ParseState *pstate, * Build a ParseNamespaceItem, but don't add it to the pstate's namespace * list --- caller must do that if appropriate. */ - return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), + return buildNSItemFromTupleDesc(rte, list_length(pstate->p_rtable), NULL, tupdesc); } @@ -3102,6 +3075,7 @@ expandNSItemAttrs(ParseState *pstate, ParseNamespaceItem *nsitem, int sublevels_up, int location) { RangeTblEntry *rte = nsitem->p_rte; + RelPermissionInfo *perminfo = nsitem->p_perminfo; List *names, *vars; ListCell *name, @@ -3119,7 +3093,10 @@ expandNSItemAttrs(ParseState *pstate, ParseNamespaceItem *nsitem, * relation of UPDATE/DELETE, which cannot be under a join.) */ if (rte->rtekind == RTE_RELATION) - rte->requiredPerms |= ACL_SELECT; + { + Assert(perminfo != NULL); + perminfo->requiredPerms |= ACL_SELECT; + } forboth(name, names, var, vars) { @@ -3665,3 +3642,93 @@ isQueryUsingTempRelation_walker(Node *node, void *context) isQueryUsingTempRelation_walker, context); } + +/* + * AddRelPermissionInfo + * Creates RelPermissionInfo for a given relation OID and adds it into + * the provided list unless added already + * + * Returns the RelPermssionInfo. + */ +RelPermissionInfo * +AddRelPermissionInfo(List **relpermlist, Oid relid) +{ + RelPermissionInfo *perminfo; + + /* Don't allow duplicate entries for a given relation. */ + perminfo = GetRelPermissionInfo(*relpermlist, relid, true); + if (perminfo) + return perminfo; + + perminfo = makeNode(RelPermissionInfo); + perminfo->relid = relid; + /* Other information is set by fetching the node as and where needed. */ + + *relpermlist = lappend(*relpermlist, perminfo); + + return perminfo; +} + +/* + * GetRelPermissionInfo + * Tries to find a RelPermissionInfo for given relation OID in the + * provided list, erroring out or returning NULL (depending on + * missing_ok) if not found + */ +RelPermissionInfo * +GetRelPermissionInfo(List *relpermlist, Oid relid, bool missing_ok) +{ + ListCell *lc; + + Assert(OidIsValid(relid)); + + foreach(lc, relpermlist) + { + RelPermissionInfo *perminfo = lfirst(lc); + + if (perminfo->relid == relid) + return perminfo; + } + + if (!missing_ok) + elog(ERROR, "permission info of relation %u not found", relid); + + return NULL; +} + +/* + * MergeRelPermissionInfos + * Adds the RelPermissionInfo in the 'from' list to the 'into' list, + * "merging" the contents of any that are present in both. + * + * Returns the destination list. + */ +List * +MergeRelPermissionInfos(List *into, List *from) +{ + ListCell *l; + + foreach(l, from) + { + RelPermissionInfo *perminfo = (RelPermissionInfo *) lfirst(l); + RelPermissionInfo *target; + + target = AddRelPermissionInfo(&into, perminfo->relid); + + /* "merge" proprties. */ + target->inh = perminfo->inh; + target->requiredPerms |= perminfo->requiredPerms; + if (!OidIsValid(target->checkAsUser)) + target->checkAsUser = perminfo->checkAsUser; + target->selectedCols = bms_union(target->selectedCols, + perminfo->selectedCols); + target->insertedCols = bms_union(target->insertedCols, + perminfo->insertedCols); + target->updatedCols = bms_union(target->updatedCols, + perminfo->updatedCols); + target->extraUpdatedCols = bms_union(target->extraUpdatedCols, + perminfo->extraUpdatedCols); + } + + return into; +} diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index 6e8fbc4780..6d308b2cd8 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -1142,7 +1142,7 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref, * * Note: this code is a lot like transformColumnRef; it's tempting to * call that instead and then replace the resulting whole-row Var with - * a list of Vars. However, that would leave us with the RTE's + * a list of Vars. However, that would leave us with the relation's * selectedCols bitmap showing the whole row as needing select * permission, as well as the individual columns. That would be * incorrect (since columns added later shouldn't need select @@ -1376,6 +1376,7 @@ ExpandSingleTable(ParseState *pstate, ParseNamespaceItem *nsitem, else { RangeTblEntry *rte = nsitem->p_rte; + RelPermissionInfo *perminfo = nsitem->p_perminfo; List *vars; ListCell *l; @@ -1390,7 +1391,10 @@ ExpandSingleTable(ParseState *pstate, ParseNamespaceItem *nsitem, * target relation of UPDATE/DELETE, which cannot be under a join.) */ if (rte->rtekind == RTE_RELATION) - rte->requiredPerms |= ACL_SELECT; + { + Assert(perminfo != NULL); + perminfo->requiredPerms |= ACL_SELECT; + } /* Require read access to each column */ foreach(l, vars) @@ -1422,12 +1426,11 @@ ExpandRowReference(ParseState *pstate, Node *expr, /* * If the rowtype expression is a whole-row Var, we can expand the fields - * as simple Vars. Note: if the RTE is a relation, this case leaves us - * with the RTE's selectedCols bitmap showing the whole row as needing - * select permission, as well as the individual columns. However, we can - * only get here for weird notations like (table.*).*, so it's not worth - * trying to clean up --- arguably, the permissions marking is correct - * anyway for such cases. + * as simple Vars. Note: this case leaves us with the relation's + * selectedCols bitmap showing the whole row as needing select permission, + * as well as the individual columns. However, we can only get here for + * weird notations like (table.*).*, so it's not worth trying to clean up + * --- arguably, the permissions marking is correct anyway for such cases. */ if (IsA(expr, Var) && ((Var *) expr)->varattno == InvalidAttrNumber) diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index e5eefdbd43..a0842bf3a5 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -1221,7 +1221,8 @@ expandTableLikeClause(RangeVar *heapRel, TableLikeClause *table_like_clause) * have a failure since both tables are locked. */ attmap = build_attrmap_by_name(RelationGetDescr(childrel), - tupleDesc); + tupleDesc, + false); /* * Process defaults, if required. @@ -3010,9 +3011,6 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, AccessShareLock, makeAlias("new", NIL), false, false); - /* Must override addRangeTableEntry's default access-check flags */ - oldnsitem->p_rte->requiredPerms = 0; - newnsitem->p_rte->requiredPerms = 0; /* * They must be in the namespace too for lookup purposes, but only add the @@ -3068,6 +3066,7 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, nothing_qry->commandType = CMD_NOTHING; nothing_qry->rtable = pstate->p_rtable; + nothing_qry->relpermlist = pstate->p_relpermlist; nothing_qry->jointree = makeFromExpr(NIL, NULL); /* no join wanted */ *actions = list_make1(nothing_qry); @@ -3110,8 +3109,6 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, AccessShareLock, makeAlias("new", NIL), false, false); - oldnsitem->p_rte->requiredPerms = 0; - newnsitem->p_rte->requiredPerms = 0; addNSItemToQuery(sub_pstate, oldnsitem, false, true, false); addNSItemToQuery(sub_pstate, newnsitem, false, true, false); diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c index 38b493e4f5..db1b1f1002 100644 --- a/src/backend/replication/logical/worker.c +++ b/src/backend/replication/logical/worker.c @@ -155,6 +155,7 @@ #include "miscadmin.h" #include "nodes/makefuncs.h" #include "optimizer/optimizer.h" +#include "parser/parse_relation.h" #include "pgstat.h" #include "postmaster/bgworker.h" #include "postmaster/interrupt.h" @@ -463,6 +464,8 @@ create_edata_for_relation(LogicalRepRelMapEntry *rel) rte->rellockmode = AccessShareLock; ExecInitRangeTable(estate, list_make1(rte)); + AddRelPermissionInfo(&estate->es_relpermlist, rte->relid); + edata->targetRelInfo = resultRelInfo = makeNode(ResultRelInfo); /* @@ -1691,7 +1694,7 @@ apply_handle_update(StringInfo s) LogicalRepTupleData newtup; bool has_oldtup; TupleTableSlot *remoteslot; - RangeTblEntry *target_rte; + RelPermissionInfo *target_perminfo; MemoryContext oldctx; if (handle_streamed_transaction(LOGICAL_REP_MSG_UPDATE, s)) @@ -1731,7 +1734,7 @@ apply_handle_update(StringInfo s) * information. But it would for example exclude columns that only exist * on the subscriber, since we are not touching those. */ - target_rte = list_nth(estate->es_range_table, 0); + target_perminfo = list_nth(estate->es_relpermlist, 0); for (int i = 0; i < remoteslot->tts_tupleDescriptor->natts; i++) { Form_pg_attribute att = TupleDescAttr(remoteslot->tts_tupleDescriptor, i); @@ -1741,14 +1744,14 @@ apply_handle_update(StringInfo s) { Assert(remoteattnum < newtup.ncols); if (newtup.colstatus[remoteattnum] != LOGICALREP_COLUMN_UNCHANGED) - target_rte->updatedCols = - bms_add_member(target_rte->updatedCols, + target_perminfo->updatedCols = + bms_add_member(target_perminfo->updatedCols, i + 1 - FirstLowInvalidHeapAttributeNumber); } } /* Also populate extraUpdatedCols, in case we have generated columns */ - fill_extraUpdatedCols(target_rte, rel->localrel); + fill_extraUpdatedCols(target_perminfo, rel->localrel); /* Build the search tuple. */ oldctx = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate)); diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c index 6589345ac4..5ffcb58306 100644 --- a/src/backend/rewrite/rewriteDefine.c +++ b/src/backend/rewrite/rewriteDefine.c @@ -31,6 +31,7 @@ #include "commands/policy.h" #include "miscadmin.h" #include "nodes/nodeFuncs.h" +#include "parser/parse_relation.h" #include "parser/parse_utilcmd.h" #include "rewrite/rewriteDefine.h" #include "rewrite/rewriteManip.h" @@ -821,18 +822,20 @@ setRuleCheckAsUser_Query(Query *qry, Oid userid) { ListCell *l; - /* Set all the RTEs in this query node */ + foreach(l, qry->relpermlist) + { + RelPermissionInfo *perminfo = (RelPermissionInfo *) lfirst(l); + + perminfo->checkAsUser = userid; + } + + /* Now recurse to any subquery RTEs */ foreach(l, qry->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); if (rte->rtekind == RTE_SUBQUERY) - { - /* Recurse into subquery in FROM */ setRuleCheckAsUser_Query(rte->subquery, userid); - } - else - rte->checkAsUser = userid; } /* Recurse into subquery-in-WITH */ diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 54fd6d6fb2..92462daa61 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -394,25 +394,9 @@ rewriteRuleAction(Query *parsetree, * Generate expanded rtable consisting of main parsetree's rtable plus * rule action's rtable; this becomes the complete rtable for the rule * action. Some of the entries may be unused after we finish rewriting, - * but we leave them all in place for two reasons: - * - * We'd have a much harder job to adjust the query's varnos if we - * selectively removed RT entries. - * - * If the rule is INSTEAD, then the original query won't be executed at - * all, and so its rtable must be preserved so that the executor will do - * the correct permissions checks on it. - * - * RT entries that are not referenced in the completed jointree will be - * ignored by the planner, so they do not affect query semantics. But any - * permissions checks specified in them will be applied during executor - * startup (see ExecCheckRTEPerms()). This allows us to check that the - * caller has, say, insert-permission on a view, when the view is not - * semantically referenced at all in the resulting query. - * - * When a rule is not INSTEAD, the permissions checks done on its copied - * RT entries will be redundant with those done during execution of the - * original query, but we don't bother to treat that case differently. + * but we leave them all in place to avoid having to adjust the query's + * varnos. RT entries that are not referenced in the completed jointree + * will be ignored by the planner, so they do not affect query semantics. * * NOTE: because planner will destructively alter rtable, we must ensure * that rule action's rtable is separate and shares no substructure with @@ -421,6 +405,25 @@ rewriteRuleAction(Query *parsetree, sub_action->rtable = list_concat(copyObject(parsetree->rtable), sub_action->rtable); + /* + * Merge permission info lists to ensure that all permissions are checked + * correctly. + * + * If the rule is INSTEAD, then the original query won't be executed at + * all, and so its permissions list must be preserved so that the + * executor will do the correct permissions checks on the relations + * referenced in it. This allows us to check that the caller has, say, + * insert-permission on a view, when the view is not semantically + * referenced at all in the resulting query. + * + * When a rule is not INSTEAD, the permissions checks done using the + * copied entries will be redundant with those done during execution of + * the original query, but we don't bother to treat that case differently. + */ + sub_action->relpermlist = + MergeRelPermissionInfos(copyObject(parsetree->relpermlist), + sub_action->relpermlist); + /* * There could have been some SubLinks in parsetree's rtable, in which * case we'd better mark the sub_action correctly. @@ -1566,16 +1569,18 @@ rewriteValuesRTE(Query *parsetree, RangeTblEntry *rte, int rti, /* - * Record in target_rte->extraUpdatedCols the indexes of any generated columns - * that depend on any columns mentioned in target_rte->updatedCols. + * Record in target_perminfo->extraUpdatedCols the indexes of any generated + * columns that depend on any columns mentioned in + * target_perminfo->updatedCols. */ void -fill_extraUpdatedCols(RangeTblEntry *target_rte, Relation target_relation) +fill_extraUpdatedCols(RelPermissionInfo *target_perminfo, + Relation target_relation) { TupleDesc tupdesc = RelationGetDescr(target_relation); TupleConstr *constr = tupdesc->constr; - target_rte->extraUpdatedCols = NULL; + target_perminfo->extraUpdatedCols = NULL; if (constr && constr->has_generated_stored) { @@ -1593,9 +1598,9 @@ fill_extraUpdatedCols(RangeTblEntry *target_rte, Relation target_relation) expr = stringToNode(defval->adbin); pull_varattnos(expr, 1, &attrs_used); - if (bms_overlap(target_rte->updatedCols, attrs_used)) - target_rte->extraUpdatedCols = - bms_add_member(target_rte->extraUpdatedCols, + if (bms_overlap(target_perminfo->updatedCols, attrs_used)) + target_perminfo->extraUpdatedCols = + bms_add_member(target_perminfo->extraUpdatedCols, defval->adnum - FirstLowInvalidHeapAttributeNumber); } } @@ -1680,8 +1685,7 @@ ApplyRetrieveRule(Query *parsetree, List *activeRIRs) { Query *rule_action; - RangeTblEntry *rte, - *subrte; + RangeTblEntry *rte; RowMarkClause *rc; if (list_length(rule->actions) != 1) @@ -1722,18 +1726,6 @@ ApplyRetrieveRule(Query *parsetree, parsetree->rtable = lappend(parsetree->rtable, newrte); parsetree->resultRelation = list_length(parsetree->rtable); - /* - * There's no need to do permissions checks twice, so wipe out the - * permissions info for the original RTE (we prefer to keep the - * bits set on the result RTE). - */ - rte->requiredPerms = 0; - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - /* * For the most part, Vars referencing the view should remain as * they are, meaning that they implicitly represent OLD values. @@ -1822,26 +1814,6 @@ ApplyRetrieveRule(Query *parsetree, rte->tablesample = NULL; rte->inh = false; /* must not be set for a subquery */ - /* - * We move the view's permission check data down to its rangetable. The - * checks will actually be done against the OLD entry therein. - */ - subrte = rt_fetch(PRS2_OLD_VARNO, rule_action->rtable); - Assert(subrte->relid == relation->rd_id); - subrte->requiredPerms = rte->requiredPerms; - subrte->checkAsUser = rte->checkAsUser; - subrte->selectedCols = rte->selectedCols; - subrte->insertedCols = rte->insertedCols; - subrte->updatedCols = rte->updatedCols; - subrte->extraUpdatedCols = rte->extraUpdatedCols; - - rte->requiredPerms = 0; /* no permission check on subquery itself */ - rte->checkAsUser = InvalidOid; - rte->selectedCols = NULL; - rte->insertedCols = NULL; - rte->updatedCols = NULL; - rte->extraUpdatedCols = NULL; - return parsetree; } @@ -1870,8 +1842,13 @@ markQueryForLocking(Query *qry, Node *jtnode, if (rte->rtekind == RTE_RELATION) { + RelPermissionInfo *perminfo; + applyLockingClause(qry, rti, strength, waitPolicy, pushedDown); - rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; + + perminfo = GetRelPermissionInfo(qry->relpermlist, + rte->relid, false); + perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE; } else if (rte->rtekind == RTE_SUBQUERY) { @@ -3012,6 +2989,9 @@ rewriteTargetView(Query *parsetree, Relation view) RangeTblEntry *base_rte; RangeTblEntry *view_rte; RangeTblEntry *new_rte; + RelPermissionInfo *view_perminfo; + RelPermissionInfo *base_perminfo; + RelPermissionInfo *new_perminfo; Relation base_rel; List *view_targetlist; ListCell *lc; @@ -3147,6 +3127,9 @@ rewriteTargetView(Query *parsetree, Relation view) base_rt_index = rtr->rtindex; base_rte = rt_fetch(base_rt_index, viewquery->rtable); + base_perminfo = GetRelPermissionInfo(viewquery->relpermlist, + base_rte->relid, + false); Assert(base_rte->rtekind == RTE_RELATION); /* @@ -3219,51 +3202,53 @@ rewriteTargetView(Query *parsetree, Relation view) 0); /* - * Mark the new target RTE for the permissions checks that we want to + * Mark the new target relation for the permissions checks that we want to * enforce against the view owner, as distinct from the query caller. At * the relation level, require the same INSERT/UPDATE/DELETE permissions - * that the query caller needs against the view. We drop the ACL_SELECT - * bit that is presumably in new_rte->requiredPerms initially. + * that the query caller needs against the view. * - * Note: the original view RTE remains in the query's rangetable list. - * Although it will be unused in the query plan, we need it there so that - * the executor still performs appropriate permissions checks for the - * query caller's use of the view. + * Note: the original view's RelPermissionInfo remains in the query's + * permissions list so that the executor still performs appropriate + * permissions checks for the query caller's use of the view. */ - new_rte->checkAsUser = view->rd_rel->relowner; - new_rte->requiredPerms = view_rte->requiredPerms; + view_perminfo = GetRelPermissionInfo(parsetree->relpermlist, + view_rte->relid, false); + new_perminfo = AddRelPermissionInfo(&parsetree->relpermlist, + new_rte->relid); + new_perminfo->checkAsUser = view->rd_rel->relowner; + new_perminfo->requiredPerms = view_perminfo->requiredPerms; /* * Now for the per-column permissions bits. * - * Initially, new_rte contains selectedCols permission check bits for all - * base-rel columns referenced by the view, but since the view is a SELECT - * query its insertedCols/updatedCols is empty. We set insertedCols and - * updatedCols to include all the columns the outer query is trying to - * modify, adjusting the column numbers as needed. But we leave - * selectedCols as-is, so the view owner must have read permission for all - * columns used in the view definition, even if some of them are not read - * by the outer query. We could try to limit selectedCols to only columns - * used in the transformed query, but that does not correspond to what - * happens in ordinary SELECT usage of a view: all referenced columns must - * have read permission, even if optimization finds that some of them can - * be discarded during query transformation. The flattening we're doing - * here is an optional optimization, too. (If you are unpersuaded and - * want to change this, note that applying adjust_view_column_set to - * view_rte->selectedCols is clearly *not* the right answer, since that - * neglects base-rel columns used in the view's WHERE quals.) + * Set new_perminfo->selectedCols to include permission check bits for + * all base-rel columns referenced by the view and insertedCols/updatedCols + * to include all the columns the outer query is trying to modify, adjusting + * the column numbers as needed. We leave selectedCols as-is, so the view + * owner must have read permission for all columns used in the view + * definition, even if some of them are not read by the outer query. We + * could try to limit selectedCols to only columns used in the transformed + * query, but that does not correspond to what happens in ordinary SELECT + * usage of a view: all referenced columns must have read permission, even + * if optimization finds that some of them can be discarded during query + * transformation. The flattening we're doing here is an optional + * optimization, too. (If you are unpersuaded and want to change this, + * note that applying adjust_view_column_set to view_perminfo->selectedCols + * is clearly *not* the right answer, since that neglects base-rel columns + * used in the view's WHERE quals.) * * This step needs the modified view targetlist, so we have to do things * in this order. */ - Assert(bms_is_empty(new_rte->insertedCols) && - bms_is_empty(new_rte->updatedCols)); + new_perminfo->selectedCols = base_perminfo->selectedCols; + Assert(bms_is_empty(new_perminfo->insertedCols) && + bms_is_empty(new_perminfo->updatedCols)); - new_rte->insertedCols = adjust_view_column_set(view_rte->insertedCols, - view_targetlist); + new_perminfo->insertedCols = + adjust_view_column_set(view_perminfo->insertedCols, view_targetlist); - new_rte->updatedCols = adjust_view_column_set(view_rte->updatedCols, - view_targetlist); + new_perminfo->updatedCols = + adjust_view_column_set(view_perminfo->updatedCols, view_targetlist); /* * Move any security barrier quals from the view RTE onto the new target @@ -3367,7 +3352,7 @@ rewriteTargetView(Query *parsetree, Relation view) * from the view, hence we need a new column alias list). This should * match transformOnConflictClause. In particular, note that the * relkind is set to composite to signal that we're not dealing with - * an actual relation, and no permissions checks are wanted. + * an actual relation. */ old_exclRelIndex = parsetree->onConflict->exclRelIndex; @@ -3378,8 +3363,6 @@ rewriteTargetView(Query *parsetree, Relation view) false, false); new_exclRte = new_exclNSItem->p_rte; new_exclRte->relkind = RELKIND_COMPOSITE_TYPE; - new_exclRte->requiredPerms = 0; - /* other permissions fields in new_exclRte are already empty */ parsetree->rtable = lappend(parsetree->rtable, new_exclRte); new_exclRelIndex = parsetree->onConflict->exclRelIndex = @@ -3653,6 +3636,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events) { int result_relation; RangeTblEntry *rt_entry; + RelPermissionInfo *rt_perminfo; Relation rt_entry_relation; List *locks; List *product_queries; @@ -3664,6 +3648,8 @@ RewriteQuery(Query *parsetree, List *rewrite_events) Assert(result_relation != 0); rt_entry = rt_fetch(result_relation, parsetree->rtable); Assert(rt_entry->rtekind == RTE_RELATION); + rt_perminfo = GetRelPermissionInfo(parsetree->relpermlist, + rt_entry->relid, false); /* * We can use NoLock here since either the parser or @@ -3749,7 +3735,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events) NULL, 0, NULL); /* Also populate extraUpdatedCols (for generated columns) */ - fill_extraUpdatedCols(rt_entry, rt_entry_relation); + fill_extraUpdatedCols(rt_perminfo, rt_entry_relation); } else if (event == CMD_DELETE) { diff --git a/src/backend/rewrite/rowsecurity.c b/src/backend/rewrite/rowsecurity.c index e10f94904e..9da976e375 100644 --- a/src/backend/rewrite/rowsecurity.c +++ b/src/backend/rewrite/rowsecurity.c @@ -47,6 +47,7 @@ #include "nodes/pg_list.h" #include "nodes/plannodes.h" #include "parser/parsetree.h" +#include "parser/parse_relation.h" #include "rewrite/rewriteDefine.h" #include "rewrite/rewriteHandler.h" #include "rewrite/rewriteManip.h" @@ -115,6 +116,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, CmdType commandType; List *permissive_policies; List *restrictive_policies; + RelPermissionInfo *perminfo; /* Defaults for the return values */ *securityQuals = NIL; @@ -122,16 +124,20 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, *hasRowSecurity = false; *hasSubLinks = false; + Assert(rte->rtekind == RTE_RELATION); + /* If this is not a normal relation, just return immediately */ if (rte->relkind != RELKIND_RELATION && rte->relkind != RELKIND_PARTITIONED_TABLE) return; + perminfo = GetRelPermissionInfo(root->relpermlist, rte->relid, false); + /* Switch to checkAsUser if it's set */ - user_id = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + user_id = perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId(); /* Determine the state of RLS for this, pass checkAsUser explicitly */ - rls_status = check_enable_rls(rte->relid, rte->checkAsUser, false); + rls_status = check_enable_rls(rte->relid, perminfo->checkAsUser, false); /* If there is no RLS on this table at all, nothing to do */ if (rls_status == RLS_NONE) @@ -196,7 +202,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * which the user does not have access to via the UPDATE USING policies, * similar to how we require normal UPDATE rights for these queries. */ - if (commandType == CMD_SELECT && rte->requiredPerms & ACL_UPDATE) + if (commandType == CMD_SELECT && perminfo->requiredPerms & ACL_UPDATE) { List *update_permissive_policies; List *update_restrictive_policies; @@ -241,7 +247,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * ALL or SELECT USING policy. */ if ((commandType == CMD_UPDATE || commandType == CMD_DELETE) && - rte->requiredPerms & ACL_SELECT) + perminfo->requiredPerms & ACL_SELECT) { List *select_permissive_policies; List *select_restrictive_policies; @@ -284,7 +290,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * raised if a policy is violated; otherwise, we might end up silently * dropping rows to be added. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) { List *select_permissive_policies = NIL; List *select_restrictive_policies = NIL; @@ -340,7 +346,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * for this relation, also as WCO policies, again, to avoid * silently dropping data. See above. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) { get_policies_for_relation(rel, CMD_SELECT, user_id, &conflict_select_permissive_policies, @@ -369,7 +375,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * path of an INSERT .. ON CONFLICT DO UPDATE, if SELECT rights * are required for this relation. */ - if (rte->requiredPerms & ACL_SELECT) + if (perminfo->requiredPerms & ACL_SELECT) add_with_check_options(rel, rt_index, WCO_RLS_UPDATE_CHECK, conflict_select_permissive_policies, @@ -386,8 +392,8 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, * Copy checkAsUser to the row security quals and WithCheckOption checks, * in case they contain any subqueries referring to other relations. */ - setRuleCheckAsUser((Node *) *securityQuals, rte->checkAsUser); - setRuleCheckAsUser((Node *) *withCheckOptions, rte->checkAsUser); + setRuleCheckAsUser((Node *) *securityQuals, perminfo->checkAsUser); + setRuleCheckAsUser((Node *) *withCheckOptions, perminfo->checkAsUser); /* * Mark this query as having row security, so plancache can invalidate it diff --git a/src/backend/statistics/extended_stats.c b/src/backend/statistics/extended_stats.c index 2e55913bc8..f438b71229 100644 --- a/src/backend/statistics/extended_stats.c +++ b/src/backend/statistics/extended_stats.c @@ -30,6 +30,7 @@ #include "nodes/nodeFuncs.h" #include "optimizer/clauses.h" #include "optimizer/optimizer.h" +#include "parser/parse_relation.h" #include "pgstat.h" #include "postmaster/autovacuum.h" #include "statistics/extended_stats_internal.h" @@ -1555,6 +1556,7 @@ statext_is_compatible_clause(PlannerInfo *root, Node *clause, Index relid, Bitmapset **attnums, List **exprs) { RangeTblEntry *rte = root->simple_rte_array[relid]; + RelOptInfo *rel = root->simple_rel_array[relid]; RestrictInfo *rinfo = (RestrictInfo *) clause; int clause_relid; Oid userid; @@ -1602,10 +1604,9 @@ statext_is_compatible_clause(PlannerInfo *root, Node *clause, Index relid, return false; /* - * Check that the user has permission to read all required attributes. Use - * checkAsUser if it's set, in case we're accessing the table via a view. + * Check that the user has permission to read all required attributes. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = rel->userid ? rel->userid : GetUserId(); if (pg_class_aclcheck(rte->relid, userid, ACL_SELECT) != ACLCHECK_OK) { diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c index 96269fc2ad..486fedfd43 100644 --- a/src/backend/utils/adt/ri_triggers.c +++ b/src/backend/utils/adt/ri_triggers.c @@ -1308,8 +1308,8 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) char fkrelname[MAX_QUOTED_REL_NAME_LEN]; char pkattname[MAX_QUOTED_NAME_LEN + 3]; char fkattname[MAX_QUOTED_NAME_LEN + 3]; - RangeTblEntry *pkrte; - RangeTblEntry *fkrte; + RelPermissionInfo *pk_perminfo; + RelPermissionInfo *fk_perminfo; const char *sep; const char *fk_only; const char *pk_only; @@ -1327,32 +1327,26 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) * * XXX are there any other show-stopper conditions to check? */ - pkrte = makeNode(RangeTblEntry); - pkrte->rtekind = RTE_RELATION; - pkrte->relid = RelationGetRelid(pk_rel); - pkrte->relkind = pk_rel->rd_rel->relkind; - pkrte->rellockmode = AccessShareLock; - pkrte->requiredPerms = ACL_SELECT; - - fkrte = makeNode(RangeTblEntry); - fkrte->rtekind = RTE_RELATION; - fkrte->relid = RelationGetRelid(fk_rel); - fkrte->relkind = fk_rel->rd_rel->relkind; - fkrte->rellockmode = AccessShareLock; - fkrte->requiredPerms = ACL_SELECT; + pk_perminfo = makeNode(RelPermissionInfo); + pk_perminfo->relid = RelationGetRelid(pk_rel); + pk_perminfo->requiredPerms = ACL_SELECT; + + fk_perminfo = makeNode(RelPermissionInfo); + fk_perminfo->relid = RelationGetRelid(fk_rel); + fk_perminfo->requiredPerms = ACL_SELECT; for (int i = 0; i < riinfo->nkeys; i++) { int attno; attno = riinfo->pk_attnums[i] - FirstLowInvalidHeapAttributeNumber; - pkrte->selectedCols = bms_add_member(pkrte->selectedCols, attno); + pk_perminfo->selectedCols = bms_add_member(pk_perminfo->selectedCols, attno); attno = riinfo->fk_attnums[i] - FirstLowInvalidHeapAttributeNumber; - fkrte->selectedCols = bms_add_member(fkrte->selectedCols, attno); + fk_perminfo->selectedCols = bms_add_member(fk_perminfo->selectedCols, attno); } - if (!ExecCheckRTPerms(list_make2(fkrte, pkrte), false)) + if (!ExecCheckPermissions(list_make2(fk_perminfo, pk_perminfo), false)) return false; /* @@ -1362,9 +1356,9 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) */ if (!has_bypassrls_privilege(GetUserId()) && ((pk_rel->rd_rel->relrowsecurity && - !pg_class_ownercheck(pkrte->relid, GetUserId())) || + !pg_class_ownercheck(pk_perminfo->relid, GetUserId())) || (fk_rel->rd_rel->relrowsecurity && - !pg_class_ownercheck(fkrte->relid, GetUserId())))) + !pg_class_ownercheck(fk_perminfo->relid, GetUserId())))) return false; /*---------- diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index 0c8c05f6c2..15bb356572 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -5135,7 +5135,8 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, * Use checkAsUser if it's set, in case we're * accessing the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? + onerel->userid : GetUserId(); /* * For simplicity, we insist on the whole @@ -5187,7 +5188,8 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? + onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5271,7 +5273,8 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, * Use checkAsUser if it's set, in case we're accessing * the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? + onerel->userid : GetUserId(); /* * For simplicity, we insist on the whole table being @@ -5319,7 +5322,8 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? + onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5380,6 +5384,7 @@ examine_simple_variable(PlannerInfo *root, Var *var, if (HeapTupleIsValid(vardata->statsTuple)) { + RelOptInfo *onerel = find_base_rel(root, var->varno); Oid userid; /* @@ -5388,7 +5393,8 @@ examine_simple_variable(PlannerInfo *root, Var *var, * from security barrier views or RLS policies. Use checkAsUser * if it's set, in case we're accessing the table via a view. */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? + onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && @@ -5457,7 +5463,8 @@ examine_simple_variable(PlannerInfo *root, Var *var, rte = planner_rt_fetch(varno, root); Assert(rte->rtekind == RTE_RELATION); - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + userid = onerel->userid ? + onerel->userid : GetUserId(); vardata->acl_ok = rte->securityQuals == NIL && diff --git a/src/include/access/attmap.h b/src/include/access/attmap.h index 778fa27fd1..f3ce859395 100644 --- a/src/include/access/attmap.h +++ b/src/include/access/attmap.h @@ -42,9 +42,11 @@ extern void free_attrmap(AttrMap *map); /* Conversion routines to build mappings */ extern AttrMap *build_attrmap_by_name(TupleDesc indesc, - TupleDesc outdesc); + TupleDesc outdesc, + bool missing_ok); extern AttrMap *build_attrmap_by_name_if_req(TupleDesc indesc, - TupleDesc outdesc); + TupleDesc outdesc, + bool missing_ok); extern AttrMap *build_attrmap_by_position(TupleDesc indesc, TupleDesc outdesc, const char *msg); diff --git a/src/include/commands/copyfrom_internal.h b/src/include/commands/copyfrom_internal.h index 4d68d9cceb..752e204ec7 100644 --- a/src/include/commands/copyfrom_internal.h +++ b/src/include/commands/copyfrom_internal.h @@ -93,7 +93,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 *relpermlist; /* single element list of RelPermissionInfo */ ExprState *qualexpr; TransitionCaptureState *transition_capture; diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index 3dc03c913e..19ed90b956 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -80,7 +80,7 @@ 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() */ +/* Hook for plugins to get control in ExecCheckPermissions() */ typedef bool (*ExecutorCheckPerms_hook_type) (List *, bool); extern PGDLLIMPORT ExecutorCheckPerms_hook_type ExecutorCheckPerms_hook; @@ -196,7 +196,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 *relpermlist, + bool ereport_on_violation); extern void CheckValidResultRel(ResultRelInfo *resultRelInfo, CmdType operation); extern void InitResultRelInfo(ResultRelInfo *resultRelInfo, Relation resultRelationDesc, @@ -598,6 +599,8 @@ extern TupleTableSlot *ExecGetTriggerOldSlot(EState *estate, ResultRelInfo *relI extern TupleTableSlot *ExecGetTriggerNewSlot(EState *estate, ResultRelInfo *relInfo); extern TupleTableSlot *ExecGetReturningSlot(EState *estate, ResultRelInfo *relInfo); extern TupleConversionMap *ExecGetChildToRootMap(ResultRelInfo *resultRelInfo); +extern AttrMap *ExecGetRootToChildMap(ResultRelInfo *resultRelInfo, + EState *estate); extern Bitmapset *ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate); extern Bitmapset *ExecGetUpdatedCols(ResultRelInfo *relinfo, EState *estate); diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 37cb4f3d59..5d44c88238 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -522,6 +522,14 @@ typedef struct ResultRelInfo TupleConversionMap *ri_ChildToRootMap; bool ri_ChildToRootMapValid; + /* + * Map used to convert "root" table column bitmapsets into the ones that + * describe a given child table's columns; see ExecGetInsertedCols() et + * al. Like ri_ChildToRootMap, computed only if needed. + */ + AttrMap *ri_RootToChildMap; + bool ri_RootToChildMapValid; + /* for use by copyfrom.c when performing multi-inserts */ struct CopyMultiInsertBuffer *ri_CopyMultiInsertBuffer; } ResultRelInfo; @@ -563,6 +571,7 @@ 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_relpermlist; /* List of RelPermissionInfo */ PlannedStmt *es_plannedstmt; /* link to top of plan tree */ const char *es_sourceText; /* Source text from QueryDesc */ diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 6a4d82f0a8..ee4fcdf32f 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -90,6 +90,7 @@ typedef enum NodeTag /* these aren't subclasses of Plan: */ T_NestLoopParam, T_PlanRowMark, + T_RelPermissionInfo, T_PartitionPruneInfo, T_PartitionedRelPruneInfo, T_PartitionPruneStepOp, diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 7af13dee43..a02ca46ce6 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -145,6 +145,8 @@ typedef struct Query List *cteList; /* WITH list (of CommonTableExpr's) */ List *rtable; /* list of range table entries */ + List *relpermlist; /* list of RTEPermissionInfo nodes for + * the RTE_RELATION entries in rtable */ FromExpr *jointree; /* table join tree (FROM and WHERE clauses) */ List *targetList; /* target list (of TargetEntry) */ @@ -934,37 +936,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 @@ -1142,14 +1113,58 @@ typedef struct RangeTblEntry bool lateral; /* subquery, function, or values is LATERAL? */ bool inh; /* inheritance requested? */ bool inFromCl; /* present in FROM clause? */ + List *securityQuals; /* security barrier quals to apply, if any */ +} RangeTblEntry; + +/* + * RelPermissionInfo + * Per-relation information for permission checking. Added to the query + * by the parser when populating the query range table and subsequently + * editorialized on by the rewriter and the planner. There is an entry + * each for all RTE_RELATION entries present in the range table. + * + * 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 the 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 RelPermissionInfo +{ + NodeTag type; + + Oid relid; /* OID of the relation */ + bool inh; /* true if inheritance children may exist */ 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; +} RelPermissionInfo; /* * RangeTblFunction - diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h index 1abe233db2..244b5c5792 100644 --- a/src/include/nodes/pathnodes.h +++ b/src/include/nodes/pathnodes.h @@ -101,6 +101,8 @@ typedef struct PlannerGlobal List *finalrtable; /* "flat" rangetable for executor */ + List *finalrelpermlist; /* "flat list of RelPermissionInfo "*/ + List *finalrowmarks; /* "flat" list of PlanRowMarks */ List *resultRelations; /* "flat" list of integer RT indexes */ @@ -726,7 +728,8 @@ typedef struct RelOptInfo /* Information about foreign tables and foreign joins */ Oid serverid; /* identifies server for the table or join */ - Oid userid; /* identifies user to check access as */ + Oid userid; /* identifies user to check access as; set + * in non-foreign table relations too! */ bool useridiscurrent; /* join is only valid for current user */ /* use "struct FdwRoutine" to avoid including fdwapi.h here */ struct FdwRoutine *fdwroutine; diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index ec9a8b0c81..7b39ca264a 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -19,6 +19,7 @@ #include "lib/stringinfo.h" #include "nodes/bitmapset.h" #include "nodes/lockoptions.h" +#include "nodes/parsenodes.h" #include "nodes/primnodes.h" @@ -65,6 +66,9 @@ typedef struct PlannedStmt List *rtable; /* list of RangeTblEntry nodes */ + List *relpermlist; /* list of RelPermissionInfo nodes for + * the RTE_RELATION entries in rtable */ + /* rtable indexes of target relations for INSERT/UPDATE/DELETE */ List *resultRelations; /* integer list of RT indexes, or NIL */ @@ -636,6 +640,7 @@ typedef struct ForeignScan Scan scan; CmdType operation; /* SELECT/INSERT/UPDATE/DELETE */ Index resultRelation; /* direct modification target's RT index */ + Oid checkAsUser; /* copy of RelOptInfo.userid */ Oid fs_server; /* OID of foreign server */ List *fdw_exprs; /* expressions that FDW may evaluate */ List *fdw_private; /* private data for FDW */ diff --git a/src/include/optimizer/inherit.h b/src/include/optimizer/inherit.h index e9472f2f73..1ec96d89bd 100644 --- a/src/include/optimizer/inherit.h +++ b/src/include/optimizer/inherit.h @@ -23,5 +23,6 @@ extern void expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel, extern bool apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel, RelOptInfo *childrel, RangeTblEntry *childRTE, AppendRelInfo *appinfo); +extern Bitmapset *GetRelAllUpdatedCols(PlannerInfo *root, RelOptInfo *rel); #endif /* INHERIT_H */ diff --git a/src/include/optimizer/planner.h b/src/include/optimizer/planner.h index 9a15de5025..5b884ad96f 100644 --- a/src/include/optimizer/planner.h +++ b/src/include/optimizer/planner.h @@ -58,4 +58,5 @@ extern Path *get_cheapest_fractional_path(RelOptInfo *rel, extern Expr *preprocess_phv_expression(PlannerInfo *root, Expr *expr); + #endif /* PLANNER_H */ diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h index 1500de2dd0..dd4b751f73 100644 --- a/src/include/parser/parse_node.h +++ b/src/include/parser/parse_node.h @@ -180,6 +180,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_relpermlist; /* list of RelPermissionInfo nodes for + * the RTE_RELATION entries 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) */ @@ -233,7 +235,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 relpermlist. * * The p_nscolumns array contains info showing how to construct Vars * referencing the names appearing in the p_names->colnames list. @@ -267,6 +270,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 */ + RelPermissionInfo *p_perminfo; /* The relation's permissions 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 8336c2c5a2..ae06487670 100644 --- a/src/include/parser/parse_relation.h +++ b/src/include/parser/parse_relation.h @@ -119,5 +119,8 @@ extern const NameData *attnumAttName(Relation rd, int attid); extern Oid attnumTypeId(Relation rd, int attid); extern Oid attnumCollationId(Relation rd, int attid); extern bool isQueryUsingTempRelation(Query *query); +extern RelPermissionInfo *AddRelPermissionInfo(List **relpermlist, Oid relid); +extern List *MergeRelPermissionInfos(List *into, List *from); +extern RelPermissionInfo *GetRelPermissionInfo(List *relpermlist, Oid relid, bool missing_ok); #endif /* PARSE_RELATION_H */ diff --git a/src/include/rewrite/rewriteHandler.h b/src/include/rewrite/rewriteHandler.h index 728a60c0b0..26300cc143 100644 --- a/src/include/rewrite/rewriteHandler.h +++ b/src/include/rewrite/rewriteHandler.h @@ -24,7 +24,7 @@ extern void AcquireRewriteLocks(Query *parsetree, extern Node *build_column_default(Relation rel, int attrno); -extern void fill_extraUpdatedCols(RangeTblEntry *target_rte, +extern void fill_extraUpdatedCols(RelPermissionInfo *target_perminfo, Relation target_relation); extern Query *get_view_query(Relation view); -- 2.24.1