Rework query relation permission checking
authorAlvaro Herrera <[email protected]>
Tue, 6 Dec 2022 15:09:24 +0000 (16:09 +0100)
committerAlvaro Herrera <[email protected]>
Tue, 6 Dec 2022 15:09:24 +0000 (16:09 +0100)
Currently, information about the permissions to be checked on relations
mentioned in a query is stored in their range table entries.  So the
executor must scan the entire range table looking for relations that
need to have permissions checked.  This can make the permission checking
part of the executor initialization needlessly expensive when many
inheritance children are present in the range range.  While the
permissions need not be checked on the individual child relations, the
executor still must visit every range table entry to filter them out.

This commit moves the permission checking information out of the range
table entries into a new plan node called RTEPermissionInfo.  Every
top-level (inheritance "root") RTE_RELATION entry in the range table
gets one and a list of those is maintained alongside the range table.
This new list is initialized by the parser when initializing the range
table.  The rewriter can add more entries to it as rules/views are
expanded.  Finally, the planner combines the lists of the individual
subqueries into one flat list that is passed to the executor for
checking.

To make it quick to find the RTEPermissionInfo entry belonging to a
given relation, RangeTblEntry gets a new Index field 'perminfoindex'
that stores the corresponding RTEPermissionInfo's index in the query's
list of the latter.

ExecutorCheckPerms_hook has gained another List * argument; the
signature is now:
typedef bool (*ExecutorCheckPerms_hook_type) (List *rangeTable,
      List *rtePermInfos,
      bool ereport_on_violation);
The first argument is no longer used by any in-core uses of the hook,
but we leave it in place because there may be other implementations that
do.  Implementations should likely scan the rtePermInfos list to
determine which operations to allow or deny.

Author: Amit Langote <[email protected]>
Discussion: https://postgr.es/m/CA+HiwqGjJDmUhDSfv-U2qhKJjt9ST7Xh9JXC_irsAQ1TAUsJYg@mail.gmail.com

47 files changed:
contrib/postgres_fdw/postgres_fdw.c
contrib/sepgsql/dml.c
contrib/sepgsql/hooks.c
contrib/sepgsql/sepgsql.h
src/backend/commands/copy.c
src/backend/commands/copyfrom.c
src/backend/commands/view.c
src/backend/executor/execMain.c
src/backend/executor/execParallel.c
src/backend/executor/execUtils.c
src/backend/nodes/outfuncs.c
src/backend/nodes/readfuncs.c
src/backend/optimizer/plan/planner.c
src/backend/optimizer/plan/setrefs.c
src/backend/optimizer/plan/subselect.c
src/backend/optimizer/prep/prepjointree.c
src/backend/optimizer/util/inherit.c
src/backend/optimizer/util/relnode.c
src/backend/parser/analyze.c
src/backend/parser/parse_clause.c
src/backend/parser/parse_merge.c
src/backend/parser/parse_relation.c
src/backend/parser/parse_target.c
src/backend/parser/parse_utilcmd.c
src/backend/replication/logical/worker.c
src/backend/rewrite/rewriteDefine.c
src/backend/rewrite/rewriteHandler.c
src/backend/rewrite/rewriteManip.c
src/backend/rewrite/rowsecurity.c
src/backend/utils/adt/ri_triggers.c
src/backend/utils/cache/relcache.c
src/include/catalog/catversion.h
src/include/commands/copyfrom_internal.h
src/include/executor/executor.h
src/include/nodes/execnodes.h
src/include/nodes/parsenodes.h
src/include/nodes/pathnodes.h
src/include/nodes/plannodes.h
src/include/optimizer/inherit.h
src/include/parser/parse_node.h
src/include/parser/parse_relation.h
src/include/rewrite/rewriteHandler.h
src/include/rewrite/rewriteManip.h
src/test/modules/test_oat_hooks/test_oat_hooks.c
src/test/regress/expected/rules.out
src/test/regress/sql/rules.sql
src/tools/pgindent/typedefs.list

index 20c7b1ad05aebc0e70382b861d8e8cb63ead4e7a..1ceac2e0cf9c6168be47a8217843d9758abb31ec 100644 (file)
@@ -31,6 +31,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"
@@ -657,8 +658,8 @@ 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 ExecCheckPermissions() does.  If we fail due to lack
+        * of permissions, the query would have failed at runtime anyway.
         */
        if (fpinfo->use_remote_estimate)
        {
@@ -1809,7 +1810,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 = get_rel_all_updated_cols(root, rel);
 
                col = -1;
                while ((col = bms_next_member(allUpdatedCols, col)) >= 0)
@@ -2650,7 +2652,7 @@ postgresBeginDirectModify(ForeignScanState *node, int eflags)
 
        /*
         * Identify which user to do the remote access as.  This should match what
-        * ExecCheckRTEPerms() does.
+        * ExecCheckPermissions() does.
         */
        userid = OidIsValid(fsplan->checkAsUser) ? fsplan->checkAsUser : GetUserId();
 
@@ -3975,11 +3977,8 @@ 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 = OidIsValid(rte->checkAsUser) ? rte->checkAsUser : GetUserId();
+       /* Identify which user to do the remote access as. */
+       userid = ExecGetResultRelCheckAsUser(resultRelInfo, estate);
 
        /* Get info about foreign table. */
        table = GetForeignTable(RelationGetRelid(rel));
index d75335e327011246cb97a7cd9573f2e1a884a1e2..e01059428366f0b3bbc60012911b1d01e2ba78d9 100644 (file)
@@ -23,6 +23,7 @@
 #include "commands/tablecmds.h"
 #include "executor/executor.h"
 #include "nodes/bitmapset.h"
+#include "parser/parsetree.h"
 #include "sepgsql.h"
 #include "utils/lsyscache.h"
 #include "utils/syscache.h"
@@ -277,38 +278,33 @@ 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 *rangeTbls, List *rteperminfos,
+                                          bool abort_on_violation)
 {
        ListCell   *lr;
 
-       foreach(lr, rangeTabls)
+       foreach(lr, rteperminfos)
        {
-               RangeTblEntry *rte = lfirst(lr);
+               RTEPermissionInfo *perminfo = lfirst_node(RTEPermissionInfo, 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;
 
                /*
@@ -323,10 +319,10 @@ sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation)
                 * expand rte->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 +335,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
index 363ac067003125b8d9247237966ff4a4ab06317a..4e1fe7ee5b6df7f5122d08c2c478ffb45e93fc85 100644 (file)
@@ -287,17 +287,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 *rangeTbls, List *rteperminfos, 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) (rangeTbls, rteperminfos, abort))
                return false;
 
-       if (!sepgsql_dml_privileges(rangeTabls, abort))
+       if (!sepgsql_dml_privileges(rangeTbls, rteperminfos, abort))
                return false;
 
        return true;
index f2a2c795bf5bff19956200ca4fa8dad3f7c5254b..9e292271b7a5e999ee2adcd35b0040955b11f7fc 100644 (file)
@@ -274,7 +274,8 @@ extern void sepgsql_object_relabel(const ObjectAddress *object,
 /*
  * dml.c
  */
-extern bool sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation);
+extern bool sepgsql_dml_privileges(List *rangeTabls, List *rteperminfos,
+                                                                  bool abort_on_violation);
 
 /*
  * database.c
index db4c9dbc2313c861619d572bd88139405a79c29d..b8bd78d358bf7decc8dda58868fd5b1727193625 100644 (file)
@@ -109,7 +109,7 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt,
        {
                LOCKMODE        lockmode = is_from ? RowExclusiveLock : AccessShareLock;
                ParseNamespaceItem *nsitem;
-               RangeTblEntry *rte;
+               RTEPermissionInfo *perminfo;
                TupleDesc       tupDesc;
                List       *attnums;
                ListCell   *cur;
@@ -123,8 +123,9 @@ 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->requiredPerms = (is_from ? ACL_INSERT : ACL_SELECT);
 
                if (stmt->whereClause)
                {
@@ -150,15 +151,15 @@ DoCopy(ParseState *pstate, const CopyStmt *stmt,
                attnums = CopyGetAttnums(tupDesc, rel, stmt->attlist);
                foreach(cur, attnums)
                {
-                       int                     attno = lfirst_int(cur) -
-                       FirstLowInvalidHeapAttributeNumber;
+                       int                     attno;
+                       Bitmapset **bms;
 
-                       if (is_from)
-                               rte->insertedCols = bms_add_member(rte->insertedCols, attno);
-                       else
-                               rte->selectedCols = bms_add_member(rte->selectedCols, attno);
+                       attno = lfirst_int(cur) - FirstLowInvalidHeapAttributeNumber;
+                       bms = is_from ? &perminfo->insertedCols : &perminfo->selectedCols;
+
+                       *bms = bms_add_member(*bms, attno);
                }
-               ExecCheckRTPerms(pstate->p_rtable, true);
+               ExecCheckPermissions(pstate->p_rtable, list_make1(perminfo), true);
 
                /*
                 * Permission check for row security policies.
@@ -174,7 +175,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;
index 504afcb8110cb5d5cb54188e628ae6eca9080158..0371f512208be710b920ca0d4ae6301fee08174f 100644 (file)
@@ -761,6 +761,12 @@ CopyFrom(CopyFromState cstate)
        resultRelInfo = target_resultRelInfo = makeNode(ResultRelInfo);
        ExecInitResultRelation(estate, resultRelInfo, 1);
 
+       /*
+        * Copy the RTEPermissionInfos into estate as well, so that
+        * ExecGetInsertedCols() et al will work correctly.
+        */
+       estate->es_rteperminfos = cstate->rteperminfos;
+
        /* Verify the named relation is a valid target for INSERT */
        CheckValidResultRel(resultRelInfo, CMD_INSERT);
 
@@ -1525,9 +1531,12 @@ BeginCopyFrom(ParseState *pstate,
 
        initStringInfo(&cstate->attribute_buf);
 
-       /* Assign range table, we'll need it in CopyFrom. */
+       /* Assign range table and rteperminfos, we'll need them in CopyFrom. */
        if (pstate)
+       {
                cstate->range_table = pstate->p_rtable;
+               cstate->rteperminfos = pstate->p_rteperminfos;
+       }
 
        tupDesc = RelationGetDescr(cstate->rel);
        num_phys_attrs = tupDesc->natts;
index b5a0fc02e5cc574bde2d3875c6d1d566fdf56d0a..8e3c1efae4090b9af81469a76a06e50151da3271 100644 (file)
@@ -367,7 +367,7 @@ DefineViewRules(Oid viewOid, Query *viewParse, bool replace)
  * by 2...
  *
  * These extra RT entries are not actually used in the query,
- * except for run-time locking and permission checking.
+ * except for run-time locking.
  *---------------------------------------------------------------
  */
 static Query *
@@ -378,7 +378,9 @@ UpdateRangeTableOfViewParse(Oid viewOid, Query *viewParse)
        ParseNamespaceItem *nsitem;
        RangeTblEntry *rt_entry1,
                           *rt_entry2;
+       RTEPermissionInfo *rte_perminfo1;
        ParseState *pstate;
+       ListCell   *lc;
 
        /*
         * Make a copy of the given parsetree.  It's not so much that we don't
@@ -405,15 +407,38 @@ UpdateRangeTableOfViewParse(Oid viewOid, Query *viewParse)
                                                                                   makeAlias("old", NIL),
                                                                                   false, false);
        rt_entry1 = nsitem->p_rte;
+       rte_perminfo1 = nsitem->p_perminfo;
        nsitem = addRangeTableEntryForRelation(pstate, viewRel,
                                                                                   AccessShareLock,
                                                                                   makeAlias("new", NIL),
                                                                                   false, false);
        rt_entry2 = nsitem->p_rte;
 
-       /* Must override addRangeTableEntry's default access-check flags */
-       rt_entry1->requiredPerms = 0;
-       rt_entry2->requiredPerms = 0;
+       /*
+        * Add only the "old" RTEPermissionInfo at the head of view query's list
+        * and update the other RTEs' perminfoindex accordingly.  When rewriting a
+        * query on the view, ApplyRetrieveRule() will transfer the view
+        * relation's permission details into this RTEPermissionInfo.  That's
+        * needed because the view's RTE itself will be transposed into a subquery
+        * RTE that can't carry the permission details; see the code stanza toward
+        * the end of ApplyRetrieveRule() for how that's done.
+        */
+       viewParse->rteperminfos = lcons(rte_perminfo1, viewParse->rteperminfos);
+       foreach(lc, viewParse->rtable)
+       {
+               RangeTblEntry *rte = lfirst(lc);
+
+               if (rte->perminfoindex > 0)
+                       rte->perminfoindex += 1;
+       }
+
+       /*
+        * Also make the "new" RTE's RTEPermissionInfo undiscoverable.  This is a
+        * bit of a hack given that all the non-child RTE_RELATION entries really
+        * should have a RTEPermissionInfo, but this dummy "new" RTE is going to
+        * go away anyway in the very near future.
+        */
+       rt_entry2->perminfoindex = 0;
 
        new_rt = lcons(rt_entry1, lcons(rt_entry2, viewParse->rtable));
 
index 12ff4f3de582a6fa36bbff4cd871144e4ea43c13..872b879387bc3168befc1761992a19ef7e0773be 100644 (file)
@@ -54,6 +54,7 @@
 #include "jit/jit.h"
 #include "mb/pg_wchar.h"
 #include "miscadmin.h"
+#include "parser/parse_relation.h"
 #include "parser/parsetree.h"
 #include "storage/bufmgr.h"
 #include "storage/lmgr.h"
@@ -74,7 +75,7 @@ ExecutorRun_hook_type ExecutorRun_hook = NULL;
 ExecutorFinish_hook_type ExecutorFinish_hook = NULL;
 ExecutorEnd_hook_type ExecutorEnd_hook = NULL;
 
-/* Hook for plugin to get control in ExecCheckRTPerms() */
+/* Hook for plugin to get control in ExecCheckPermissions() */
 ExecutorCheckPerms_hook_type ExecutorCheckPerms_hook = NULL;
 
 /* decls for local routines only used within this module */
@@ -90,10 +91,10 @@ static void ExecutePlan(EState *estate, PlanState *planstate,
                                                ScanDirection direction,
                                                DestReceiver *dest,
                                                bool execute_once);
-static bool ExecCheckRTEPerms(RangeTblEntry *rte);
-static bool ExecCheckRTEPermsModified(Oid relOid, Oid userid,
-                                                                         Bitmapset *modifiedCols,
-                                                                         AclMode requiredPerms);
+static bool ExecCheckOneRelPerms(RTEPermissionInfo *perminfo);
+static bool ExecCheckPermissionsModified(Oid relOid, Oid userid,
+                                                                                Bitmapset *modifiedCols,
+                                                                                AclMode requiredPerms);
 static void ExecCheckXactReadOnly(PlannedStmt *plannedstmt);
 static char *ExecBuildSlotValueDescription(Oid reloid,
                                                                                   TupleTableSlot *slot,
@@ -554,8 +555,8 @@ ExecutorRewind(QueryDesc *queryDesc)
 
 
 /*
- * ExecCheckRTPerms
- *             Check access permissions for all relations listed in a range table.
+ * ExecCheckPermissions
+ *             Check access permissions of relations mentioned in a query
  *
  * Returns true if permissions are adequate.  Otherwise, throws an appropriate
  * error if ereport_on_violation is true, or simply returns false otherwise.
@@ -565,73 +566,65 @@ ExecutorRewind(QueryDesc *queryDesc)
  * passing, then RLS also needs to be consulted (and check_enable_rls()).
  *
  * See rewrite/rowsecurity.c.
+ *
+ * NB: rangeTable is no longer used by us, but kept around for the hooks that
+ * might still want to look at the RTEs.
  */
 bool
-ExecCheckRTPerms(List *rangeTable, bool ereport_on_violation)
+ExecCheckPermissions(List *rangeTable, List *rteperminfos,
+                                        bool ereport_on_violation)
 {
        ListCell   *l;
        bool            result = true;
 
-       foreach(l, rangeTable)
+       foreach(l, rteperminfos)
        {
-               RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);
+               RTEPermissionInfo *perminfo = lfirst_node(RTEPermissionInfo, l);
 
-               result = ExecCheckRTEPerms(rte);
+               Assert(OidIsValid(perminfo->relid));
+               result = ExecCheckOneRelPerms(perminfo);
                if (!result)
                {
-                       Assert(rte->rtekind == RTE_RELATION);
                        if (ereport_on_violation)
-                               aclcheck_error(ACLCHECK_NO_PRIV, get_relkind_objtype(get_rel_relkind(rte->relid)),
-                                                          get_rel_name(rte->relid));
+                               aclcheck_error(ACLCHECK_NO_PRIV,
+                                                          get_relkind_objtype(get_rel_relkind(perminfo->relid)),
+                                                          get_rel_name(perminfo->relid));
                        return false;
                }
        }
 
        if (ExecutorCheckPerms_hook)
-               result = (*ExecutorCheckPerms_hook) (rangeTable,
+               result = (*ExecutorCheckPerms_hook) (rangeTable, rteperminfos,
                                                                                         ereport_on_violation);
        return result;
 }
 
 /*
- * ExecCheckRTEPerms
- *             Check access permissions for a single RTE.
+ * ExecCheckOneRelPerms
+ *             Check access permissions for a single relation.
  */
 static bool
-ExecCheckRTEPerms(RangeTblEntry *rte)
+ExecCheckOneRelPerms(RTEPermissionInfo *perminfo)
 {
        AclMode         requiredPerms;
        AclMode         relPerms;
        AclMode         remainingPerms;
-       Oid                     relOid;
        Oid                     userid;
+       Oid                     relOid = perminfo->relid;
 
-       /*
-        * Only plain-relation RTEs need to be checked here.  Function RTEs are
-        * checked when the function is prepared for execution.  Join, subquery,
-        * and special RTEs need no checks.
-        */
-       if (rte->rtekind != RTE_RELATION)
-               return true;
-
-       /*
-        * No work if requiredPerms is empty.
-        */
-       requiredPerms = rte->requiredPerms;
-       if (requiredPerms == 0)
-               return true;
-
-       relOid = rte->relid;
+       requiredPerms = perminfo->requiredPerms;
+       Assert(requiredPerms != 0);
 
        /*
         * userid to check as: current user unless we have a setuid indication.
         *
         * Note: GetUserId() is presently fast enough that there's no harm in
-        * calling it separately for each RTE.  If that stops being true, we could
-        * call it once in ExecCheckRTPerms and pass the userid down from there.
-        * But for now, no need for the extra clutter.
+        * calling it separately for each relation.  If that stops being true, we
+        * could call it once in ExecCheckPermissions and pass the userid down
+        * from there.  But for now, no need for the extra clutter.
         */
-       userid = OidIsValid(rte->checkAsUser) ? rte->checkAsUser : GetUserId();
+       userid = OidIsValid(perminfo->checkAsUser) ?
+               perminfo->checkAsUser : GetUserId();
 
        /*
         * We must have *all* the requiredPerms bits, but some of the bits can be
@@ -665,14 +658,14 @@ ExecCheckRTEPerms(RangeTblEntry *rte)
                         * example, SELECT COUNT(*) FROM table), allow the query if we
                         * have SELECT on any column of the rel, as per SQL spec.
                         */
-                       if (bms_is_empty(rte->selectedCols))
+                       if (bms_is_empty(perminfo->selectedCols))
                        {
                                if (pg_attribute_aclcheck_all(relOid, userid, ACL_SELECT,
                                                                                          ACLMASK_ANY) != ACLCHECK_OK)
                                        return false;
                        }
 
-                       while ((col = bms_next_member(rte->selectedCols, col)) >= 0)
+                       while ((col = bms_next_member(perminfo->selectedCols, col)) >= 0)
                        {
                                /* bit #s are offset by FirstLowInvalidHeapAttributeNumber */
                                AttrNumber      attno = col + FirstLowInvalidHeapAttributeNumber;
@@ -697,29 +690,31 @@ ExecCheckRTEPerms(RangeTblEntry *rte)
                 * Basically the same for the mod columns, for both INSERT and UPDATE
                 * privilege as specified by remainingPerms.
                 */
-               if (remainingPerms & ACL_INSERT && !ExecCheckRTEPermsModified(relOid,
-                                                                                                                                         userid,
-                                                                                                                                         rte->insertedCols,
-                                                                                                                                         ACL_INSERT))
+               if (remainingPerms & ACL_INSERT &&
+                       !ExecCheckPermissionsModified(relOid,
+                                                                                 userid,
+                                                                                 perminfo->insertedCols,
+                                                                                 ACL_INSERT))
                        return false;
 
-               if (remainingPerms & ACL_UPDATE && !ExecCheckRTEPermsModified(relOid,
-                                                                                                                                         userid,
-                                                                                                                                         rte->updatedCols,
-                                                                                                                                         ACL_UPDATE))
+               if (remainingPerms & ACL_UPDATE &&
+                       !ExecCheckPermissionsModified(relOid,
+                                                                                 userid,
+                                                                                 perminfo->updatedCols,
+                                                                                 ACL_UPDATE))
                        return false;
        }
        return true;
 }
 
 /*
- * ExecCheckRTEPermsModified
- *             Check INSERT or UPDATE access permissions for a single RTE (these
+ * ExecCheckPermissionsModified
+ *             Check INSERT or UPDATE access permissions for a single relation (these
  *             are processed uniformly).
  */
 static bool
-ExecCheckRTEPermsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols,
-                                                 AclMode requiredPerms)
+ExecCheckPermissionsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols,
+                                                        AclMode requiredPerms)
 {
        int                     col = -1;
 
@@ -773,17 +768,14 @@ ExecCheckXactReadOnly(PlannedStmt *plannedstmt)
         * Fail if write permissions are requested in parallel mode for table
         * (temp or non-temp), otherwise fail for any non-temp table.
         */
-       foreach(l, plannedstmt->rtable)
+       foreach(l, plannedstmt->permInfos)
        {
-               RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);
-
-               if (rte->rtekind != RTE_RELATION)
-                       continue;
+               RTEPermissionInfo *perminfo = lfirst_node(RTEPermissionInfo, l);
 
-               if ((rte->requiredPerms & (~ACL_SELECT)) == 0)
+               if ((perminfo->requiredPerms & (~ACL_SELECT)) == 0)
                        continue;
 
-               if (isTempNamespace(get_rel_namespace(rte->relid)))
+               if (isTempNamespace(get_rel_namespace(perminfo->relid)))
                        continue;
 
                PreventCommandIfReadOnly(CreateCommandName((Node *) plannedstmt));
@@ -815,9 +807,10 @@ InitPlan(QueryDesc *queryDesc, int eflags)
        int                     i;
 
        /*
-        * Do permissions checks
+        * Do permissions checks and save the list for later use.
         */
-       ExecCheckRTPerms(rangeTable, true);
+       ExecCheckPermissions(rangeTable, plannedstmt->permInfos, true);
+       estate->es_rteperminfos = plannedstmt->permInfos;
 
        /*
         * initialize the node's execution state
index aca0c6f323f29732eee475837c92057d75ea98d4..a5b8e43ec51d5b6a216d480975fdd12e530e2524 100644 (file)
@@ -185,6 +185,7 @@ ExecSerializePlan(Plan *plan, EState *estate)
        pstmt->planTree = plan;
        pstmt->partPruneInfos = estate->es_part_prune_infos;
        pstmt->rtable = estate->es_range_table;
+       pstmt->permInfos = estate->es_rteperminfos;
        pstmt->resultRelations = NIL;
        pstmt->appendRelations = NIL;
 
index 572c87e4536918a78694a929f1261ffd4ef6abd6..e296f44ebd957d7b87c706a69306bc4d2e67165a 100644 (file)
@@ -57,6 +57,7 @@
 #include "miscadmin.h"
 #include "nodes/nodeFuncs.h"
 #include "parser/parsetree.h"
+#include "parser/parse_relation.h"
 #include "partitioning/partdesc.h"
 #include "storage/lmgr.h"
 #include "utils/builtins.h"
@@ -67,6 +68,7 @@
 
 static bool tlist_matches_tupdesc(PlanState *ps, List *tlist, int varno, TupleDesc tupdesc);
 static void ShutdownExprContext(ExprContext *econtext, bool isCommit);
+static RTEPermissionInfo *GetResultRTEPermissionInfo(ResultRelInfo *relinfo, EState *estate);
 
 
 /* ----------------------------------------------------------------
@@ -1296,72 +1298,48 @@ ExecGetRootToChildMap(ResultRelInfo *resultRelInfo, EState *estate)
 Bitmapset *
 ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate)
 {
-       /*
-        * The columns are stored in the range table entry.  If this ResultRelInfo
-        * represents a partition routing target, and doesn't have an entry of its
-        * own in the range table, fetch the parent's RTE and map the columns to
-        * the order they are in the partition.
-        */
-       if (relinfo->ri_RangeTableIndex != 0)
-       {
-               RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate);
+       RTEPermissionInfo *perminfo = GetResultRTEPermissionInfo(relinfo, estate);
 
-               return rte->insertedCols;
-       }
-       else if (relinfo->ri_RootResultRelInfo)
+       if (perminfo == NULL)
+               return NULL;
+
+       /* Map the columns to child's attribute numbers if needed. */
+       if (relinfo->ri_RootResultRelInfo)
        {
-               ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo;
-               RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate);
                TupleConversionMap *map = ExecGetRootToChildMap(relinfo, estate);
 
-               if (map != NULL)
-                       return execute_attr_map_cols(map->attrMap, rte->insertedCols);
-               else
-                       return rte->insertedCols;
-       }
-       else
-       {
-               /*
-                * The relation isn't in the range table and it isn't a partition
-                * routing target.  This ResultRelInfo must've been created only for
-                * firing triggers and the relation is not being inserted into.  (See
-                * ExecGetTriggerResultRel.)
-                */
-               return NULL;
+               if (map)
+                       return execute_attr_map_cols(map->attrMap, perminfo->insertedCols);
        }
+
+       return perminfo->insertedCols;
 }
 
 /* Return a bitmap representing columns being updated */
 Bitmapset *
 ExecGetUpdatedCols(ResultRelInfo *relinfo, EState *estate)
 {
-       /* see ExecGetInsertedCols() */
-       if (relinfo->ri_RangeTableIndex != 0)
-       {
-               RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate);
+       RTEPermissionInfo *perminfo = GetResultRTEPermissionInfo(relinfo, estate);
 
-               return rte->updatedCols;
-       }
-       else if (relinfo->ri_RootResultRelInfo)
+       if (perminfo == NULL)
+               return NULL;
+
+       /* Map the columns to child's attribute numbers if needed. */
+       if (relinfo->ri_RootResultRelInfo)
        {
-               ResultRelInfo *rootRelInfo = relinfo->ri_RootResultRelInfo;
-               RangeTblEntry *rte = exec_rt_fetch(rootRelInfo->ri_RangeTableIndex, estate);
                TupleConversionMap *map = ExecGetRootToChildMap(relinfo, estate);
 
-               if (map != NULL)
-                       return execute_attr_map_cols(map->attrMap, rte->updatedCols);
-               else
-                       return rte->updatedCols;
+               if (map)
+                       return execute_attr_map_cols(map->attrMap, perminfo->updatedCols);
        }
-       else
-               return NULL;
+
+       return perminfo->updatedCols;
 }
 
 /* Return a bitmap representing generated columns being updated */
 Bitmapset *
 ExecGetExtraUpdatedCols(ResultRelInfo *relinfo, EState *estate)
 {
-       /* see ExecGetInsertedCols() */
        if (relinfo->ri_RangeTableIndex != 0)
        {
                RangeTblEntry *rte = exec_rt_fetch(relinfo->ri_RangeTableIndex, estate);
@@ -1390,3 +1368,71 @@ ExecGetAllUpdatedCols(ResultRelInfo *relinfo, EState *estate)
        return bms_union(ExecGetUpdatedCols(relinfo, estate),
                                         ExecGetExtraUpdatedCols(relinfo, estate));
 }
+
+/*
+ * GetResultRTEPermissionInfo
+ *             Looks up RTEPermissionInfo for ExecGet*Cols() routines
+ */
+static RTEPermissionInfo *
+GetResultRTEPermissionInfo(ResultRelInfo *relinfo, EState *estate)
+{
+       Index           rti;
+       RangeTblEntry *rte;
+       RTEPermissionInfo *perminfo = NULL;
+
+       if (relinfo->ri_RootResultRelInfo)
+       {
+               /*
+                * For inheritance child result relations (a partition routing target
+                * of an INSERT or a child UPDATE target), this returns the root
+                * parent's RTE to fetch the RTEPermissionInfo because that's the only
+                * one that has one assigned.
+                */
+               rti = relinfo->ri_RootResultRelInfo->ri_RangeTableIndex;
+       }
+       else if (relinfo->ri_RangeTableIndex != 0)
+       {
+               /*
+                * Non-child result relation should have their own RTEPermissionInfo.
+                */
+               rti = relinfo->ri_RangeTableIndex;
+       }
+       else
+       {
+               /*
+                * The relation isn't in the range table and it isn't a partition
+                * routing target.  This ResultRelInfo must've been created only for
+                * firing triggers and the relation is not being inserted into.  (See
+                * ExecGetTriggerResultRel.)
+                */
+               rti = 0;
+       }
+
+       if (rti > 0)
+       {
+               rte = exec_rt_fetch(rti, estate);
+               perminfo = getRTEPermissionInfo(estate->es_rteperminfos, rte);
+       }
+
+       return perminfo;
+}
+
+/*
+ * GetResultRelCheckAsUser
+ *             Returns the user to modify passed-in result relation as
+ *
+ * The user is chosen by looking up the relation's or, if a child table, its
+ * root parent's RTEPermissionInfo.
+ */
+Oid
+ExecGetResultRelCheckAsUser(ResultRelInfo *relInfo, EState *estate)
+{
+       RTEPermissionInfo *perminfo = GetResultRTEPermissionInfo(relInfo, estate);
+
+       /* XXX - maybe ok to return GetUserId() in this case? */
+       if (perminfo == NULL)
+               elog(ERROR, "no RTEPermissionInfo found for result relation with OID %u",
+                        RelationGetRelid(relInfo->ri_RelationDesc));
+
+       return perminfo->checkAsUser ? perminfo->checkAsUser : GetUserId();
+}
index 8f150e9a2e89f0132a9f53e001b111fda57a6a04..59b0fdeb62d1daa7f958a15ac4b9c366e7be3eaa 100644 (file)
@@ -507,6 +507,7 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node)
                        WRITE_CHAR_FIELD(relkind);
                        WRITE_INT_FIELD(rellockmode);
                        WRITE_NODE_FIELD(tablesample);
+                       WRITE_UINT_FIELD(perminfoindex);
                        break;
                case RTE_SUBQUERY:
                        WRITE_NODE_FIELD(subquery);
@@ -560,11 +561,6 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node)
        WRITE_BOOL_FIELD(lateral);
        WRITE_BOOL_FIELD(inh);
        WRITE_BOOL_FIELD(inFromCl);
-       WRITE_UINT64_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);
 }
index 23776367c5279dca74d926add80e942ccdcd97ed..966b75f5a68fa0cc80196cca2eacd070aac33142 100644 (file)
@@ -473,6 +473,7 @@ _readRangeTblEntry(void)
                        READ_CHAR_FIELD(relkind);
                        READ_INT_FIELD(rellockmode);
                        READ_NODE_FIELD(tablesample);
+                       READ_UINT_FIELD(perminfoindex);
                        break;
                case RTE_SUBQUERY:
                        READ_NODE_FIELD(subquery);
@@ -536,11 +537,6 @@ _readRangeTblEntry(void)
        READ_BOOL_FIELD(lateral);
        READ_BOOL_FIELD(inh);
        READ_BOOL_FIELD(inFromCl);
-       READ_UINT_FIELD(requiredPerms);
-       READ_OID_FIELD(checkAsUser);
-       READ_BITMAPSET_FIELD(selectedCols);
-       READ_BITMAPSET_FIELD(insertedCols);
-       READ_BITMAPSET_FIELD(updatedCols);
        READ_BITMAPSET_FIELD(extraUpdatedCols);
        READ_NODE_FIELD(securityQuals);
 
index 799602f5ea534250fcc015d6e08c20ae4b2c4629..15aa9c5087668aa5e8c3e8f857e2431e6d1d22e1 100644 (file)
@@ -57,6 +57,7 @@
 #include "optimizer/tlist.h"
 #include "parser/analyze.h"
 #include "parser/parse_agg.h"
+#include "parser/parse_relation.h"
 #include "parser/parsetree.h"
 #include "partitioning/partdesc.h"
 #include "rewrite/rewriteManip.h"
@@ -306,6 +307,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions,
        glob->subroots = NIL;
        glob->rewindPlanIDs = NULL;
        glob->finalrtable = NIL;
+       glob->finalrteperminfos = NIL;
        glob->finalrowmarks = NIL;
        glob->resultRelations = NIL;
        glob->appendRelations = NIL;
@@ -493,6 +495,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions,
 
        /* final cleanup of the plan */
        Assert(glob->finalrtable == NIL);
+       Assert(glob->finalrteperminfos == NIL);
        Assert(glob->finalrowmarks == NIL);
        Assert(glob->resultRelations == NIL);
        Assert(glob->appendRelations == NIL);
@@ -521,6 +524,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions,
        result->planTree = top_plan;
        result->partPruneInfos = glob->partPruneInfos;
        result->rtable = glob->finalrtable;
+       result->permInfos = glob->finalrteperminfos;
        result->resultRelations = glob->resultRelations;
        result->appendRelations = glob->appendRelations;
        result->subplans = glob->subplans;
@@ -6266,6 +6270,7 @@ plan_cluster_use_sort(Oid tableOid, Oid indexOid)
        rte->inh = false;
        rte->inFromCl = true;
        query->rtable = list_make1(rte);
+       addRTEPermissionInfo(&query->rteperminfos, rte);
 
        /* Set up RTE/RelOptInfo arrays */
        setup_simple_rel_arrays(root);
@@ -6393,6 +6398,7 @@ plan_create_index_workers(Oid tableOid, Oid indexOid)
        rte->inh = true;
        rte->inFromCl = true;
        query->rtable = list_make1(rte);
+       addRTEPermissionInfo(&query->rteperminfos, rte);
 
        /* Set up RTE/RelOptInfo arrays */
        setup_simple_rel_arrays(root);
index 399c1812d40354e150cee1e2b80d31086dab22b5..596f1fbc8e5b15c6e7e506b1a6ab3de76b09cb09 100644 (file)
@@ -24,6 +24,7 @@
 #include "optimizer/planmain.h"
 #include "optimizer/planner.h"
 #include "optimizer/tlist.h"
+#include "parser/parse_relation.h"
 #include "tcop/utility.h"
 #include "utils/lsyscache.h"
 #include "utils/syscache.h"
@@ -78,6 +79,13 @@ typedef struct
        int                     newvarno;
 } fix_windowagg_cond_context;
 
+/* Context info for flatten_rtes_walker() */
+typedef struct
+{
+       PlannerGlobal *glob;
+       Query      *query;
+} flatten_rtes_walker_context;
+
 /*
  * Selecting the best alternative in an AlternativeSubPlan expression requires
  * estimating how many times that expression will be evaluated.  For an
@@ -113,8 +121,9 @@ typedef struct
 
 static void add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing);
 static void flatten_unplanned_rtes(PlannerGlobal *glob, RangeTblEntry *rte);
-static bool flatten_rtes_walker(Node *node, PlannerGlobal *glob);
-static void add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte);
+static bool flatten_rtes_walker(Node *node, flatten_rtes_walker_context *cxt);
+static void add_rte_to_flat_rtable(PlannerGlobal *glob, List *rteperminfos,
+                                                                  RangeTblEntry *rte);
 static Plan *set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset);
 static Plan *set_indexonlyscan_references(PlannerInfo *root,
                                                                                  IndexOnlyScan *plan,
@@ -380,6 +389,9 @@ set_plan_references(PlannerInfo *root, Plan *plan)
  * Extract RangeTblEntries from the plan's rangetable, and add to flat rtable
  *
  * This can recurse into subquery plans; "recursing" is true if so.
+ *
+ * This also seems like a good place to add the query's RTEPermissionInfos to
+ * the flat rteperminfos.
  */
 static void
 add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing)
@@ -400,7 +412,7 @@ add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing)
                RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
 
                if (!recursing || rte->rtekind == RTE_RELATION)
-                       add_rte_to_flat_rtable(glob, rte);
+                       add_rte_to_flat_rtable(glob, root->parse->rteperminfos, rte);
        }
 
        /*
@@ -467,18 +479,21 @@ add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing)
 /*
  * Extract RangeTblEntries from a subquery that was never planned at all
  */
+
 static void
 flatten_unplanned_rtes(PlannerGlobal *glob, RangeTblEntry *rte)
 {
+       flatten_rtes_walker_context cxt = {glob, rte->subquery};
+
        /* Use query_tree_walker to find all RTEs in the parse tree */
        (void) query_tree_walker(rte->subquery,
                                                         flatten_rtes_walker,
-                                                        (void *) glob,
+                                                        (void *) &cxt,
                                                         QTW_EXAMINE_RTES_BEFORE);
 }
 
 static bool
-flatten_rtes_walker(Node *node, PlannerGlobal *glob)
+flatten_rtes_walker(Node *node, flatten_rtes_walker_context *cxt)
 {
        if (node == NULL)
                return false;
@@ -488,33 +503,38 @@ flatten_rtes_walker(Node *node, PlannerGlobal *glob)
 
                /* As above, we need only save relation RTEs */
                if (rte->rtekind == RTE_RELATION)
-                       add_rte_to_flat_rtable(glob, rte);
+                       add_rte_to_flat_rtable(cxt->glob, cxt->query->rteperminfos, rte);
                return false;
        }
        if (IsA(node, Query))
        {
-               /* Recurse into subselects */
+               /*
+                * Recurse into subselects.  Must update cxt->query to this query so
+                * that the rtable and rteperminfos correspond with each other.
+                */
+               cxt->query = (Query *) node;
                return query_tree_walker((Query *) node,
                                                                 flatten_rtes_walker,
-                                                                (void *) glob,
+                                                                (void *) cxt,
                                                                 QTW_EXAMINE_RTES_BEFORE);
        }
        return expression_tree_walker(node, flatten_rtes_walker,
-                                                                 (void *) glob);
+                                                                 (void *) cxt);
 }
 
 /*
- * Add (a copy of) the given RTE to the final rangetable
+ * Add (a copy of) the given RTE to the final rangetable and also the
+ * corresponding RTEPermissionInfo, if any, to final rteperminfos.
  *
  * In the flat rangetable, we zero out substructure pointers that are not
  * needed by the executor; this reduces the storage space and copying cost
- * for cached plans.  We keep only the ctename, alias and eref Alias fields,
- * which are needed by EXPLAIN, and the selectedCols, insertedCols,
- * updatedCols, and extraUpdatedCols bitmaps, which are needed for
- * executor-startup permissions checking and for trigger event checking.
+ * for cached plans.  We keep only the ctename, alias, eref Alias fields,
+ * which are needed by EXPLAIN, and perminfoindex which is needed by the
+ * executor to fetch the RTE's RTEPermissionInfo.
  */
 static void
-add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte)
+add_rte_to_flat_rtable(PlannerGlobal *glob, List *rteperminfos,
+                                          RangeTblEntry *rte)
 {
        RangeTblEntry *newrte;
 
@@ -552,6 +572,29 @@ add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte)
         */
        if (newrte->rtekind == RTE_RELATION)
                glob->relationOids = lappend_oid(glob->relationOids, newrte->relid);
+
+       /*
+        * Add a copy of the RTEPermissionInfo, if any, corresponding to this RTE
+        * to the flattened global list.
+        */
+       if (rte->perminfoindex > 0)
+       {
+               RTEPermissionInfo *perminfo;
+               RTEPermissionInfo *newperminfo;
+
+               /* Get the existing one from this query's rteperminfos. */
+               perminfo = getRTEPermissionInfo(rteperminfos, newrte);
+
+               /*
+                * Add a new one to finalrteperminfos and copy the contents of the
+                * existing one into it.  Note that addRTEPermissionInfo() also
+                * updates newrte->perminfoindex to point to newperminfo in
+                * finalrteperminfos.
+                */
+               newrte->perminfoindex = 0;      /* expected by addRTEPermissionInfo() */
+               newperminfo = addRTEPermissionInfo(&glob->finalrteperminfos, newrte);
+               memcpy(newperminfo, perminfo, sizeof(RTEPermissionInfo));
+       }
 }
 
 /*
index 92e33385842ce3d6749e4e79e7d771eeae56e20a..abd407825b5d454f769f48c92543d9cbe596a881 100644 (file)
@@ -1496,8 +1496,12 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink,
        if (!bms_is_subset(upper_varnos, available_rels))
                return NULL;
 
-       /* Now we can attach the modified subquery rtable to the parent */
-       parse->rtable = list_concat(parse->rtable, subselect->rtable);
+       /*
+        * Now we can attach the modified subquery rtable to the parent. This also
+        * adds subquery's RTEPermissionInfos into the upper query.
+        */
+       CombineRangeTables(&parse->rtable, &parse->rteperminfos,
+                                          subselect->rtable, subselect->rteperminfos);
 
        /*
         * And finally, build the JoinExpr node.
index 2ea3ca734ecea5a3463259e53ab071cb5aa84685..57fea35e44b181a48016d09a42624276b9c6ad64 100644 (file)
@@ -176,13 +176,6 @@ transform_MERGE_to_join(Query *parse)
        joinrte->lateral = false;
        joinrte->inh = false;
        joinrte->inFromCl = true;
-       joinrte->requiredPerms = 0;
-       joinrte->checkAsUser = InvalidOid;
-       joinrte->selectedCols = NULL;
-       joinrte->insertedCols = NULL;
-       joinrte->updatedCols = NULL;
-       joinrte->extraUpdatedCols = NULL;
-       joinrte->securityQuals = NIL;
 
        /*
         * Add completed RTE to pstate's range table list, so that we know its
@@ -1206,11 +1199,12 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
        }
 
        /*
-        * Now append the adjusted rtable entries to upper query. (We hold off
-        * until after fixing the upper rtable entries; no point in running that
-        * code on the subquery ones too.)
+        * Now append the adjusted rtable entries and their perminfos to upper
+        * query. (We hold off until after fixing the upper rtable entries; no
+        * point in running that code on the subquery ones too.)
         */
-       parse->rtable = list_concat(parse->rtable, subquery->rtable);
+       CombineRangeTables(&parse->rtable, &parse->rteperminfos,
+                                          subquery->rtable, subquery->rteperminfos);
 
        /*
         * Pull up any FOR UPDATE/SHARE markers, too.  (OffsetVarNodes already
@@ -1346,9 +1340,10 @@ pull_up_simple_union_all(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte)
        }
 
        /*
-        * Append child RTEs to parent rtable.
+        * Append child RTEs (and their perminfos) to parent rtable.
         */
-       root->parse->rtable = list_concat(root->parse->rtable, rtable);
+       CombineRangeTables(&root->parse->rtable, &root->parse->rteperminfos,
+                                          rtable, subquery->rteperminfos);
 
        /*
         * Recursively scan the subquery's setOperations tree and add
index 3d270e91d66313779e37e5c670fb778eceb1f316..f51ce45cd3b39daab8f696604dafb31274573988 100644 (file)
@@ -30,6 +30,7 @@
 #include "optimizer/prep.h"
 #include "optimizer/restrictinfo.h"
 #include "parser/parsetree.h"
+#include "parser/parse_relation.h"
 #include "partitioning/partdesc.h"
 #include "partitioning/partprune.h"
 #include "utils/rel.h"
@@ -38,6 +39,7 @@
 static void expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo,
                                                                           RangeTblEntry *parentrte,
                                                                           Index parentRTindex, Relation parentrel,
+                                                                          Bitmapset *parent_updatedCols,
                                                                           PlanRowMark *top_parentrc, LOCKMODE lockmode);
 static void expand_single_inheritance_child(PlannerInfo *root,
                                                                                        RangeTblEntry *parentrte,
@@ -47,6 +49,10 @@ static void expand_single_inheritance_child(PlannerInfo *root,
                                                                                        Index *childRTindex_p);
 static Bitmapset *translate_col_privs(const Bitmapset *parent_privs,
                                                                          List *translated_vars);
+static Bitmapset *translate_col_privs_multilevel(PlannerInfo *root,
+                                                                                                RelOptInfo *rel,
+                                                                                                RelOptInfo *top_parent_rel,
+                                                                                                Bitmapset *top_parent_cols);
 static void expand_appendrel_subquery(PlannerInfo *root, RelOptInfo *rel,
                                                                          RangeTblEntry *rte, Index rti);
 
@@ -131,6 +137,10 @@ expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel,
        /* Scan the inheritance set and expand it */
        if (oldrelation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
        {
+               RTEPermissionInfo *perminfo;
+
+               perminfo = getRTEPermissionInfo(root->parse->rteperminfos, rte);
+
                /*
                 * Partitioned table, so set up for partitioning.
                 */
@@ -141,7 +151,9 @@ expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel,
                 * extract the partition key columns of all the partitioned tables.
                 */
                expand_partitioned_rtentry(root, rel, rte, rti,
-                                                                  oldrelation, oldrc, lockmode);
+                                                                  oldrelation,
+                                                                  perminfo->updatedCols,
+                                                                  oldrc, lockmode);
        }
        else
        {
@@ -305,6 +317,7 @@ static void
 expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo,
                                                   RangeTblEntry *parentrte,
                                                   Index parentRTindex, Relation parentrel,
+                                                  Bitmapset *parent_updatedCols,
                                                   PlanRowMark *top_parentrc, LOCKMODE lockmode)
 {
        PartitionDesc partdesc;
@@ -324,14 +337,13 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo,
 
        /*
         * Note down whether any partition key cols are being updated. Though it's
-        * the root partitioned table's updatedCols we are interested in, we
-        * instead use parentrte to get the updatedCols. This is convenient
-        * because parentrte already has the root partrel's updatedCols translated
-        * to match the attribute ordering of parentrel.
+        * the root partitioned table's updatedCols we are interested in,
+        * parent_updatedCols provided by the caller contains the root partrel's
+        * updatedCols translated to match the attribute ordering of parentrel.
         */
        if (!root->partColsUpdated)
                root->partColsUpdated =
-                       has_partition_attrs(parentrel, parentrte->updatedCols, NULL);
+                       has_partition_attrs(parentrel, parent_updatedCols, NULL);
 
        /*
         * There shouldn't be any generated columns in the partition key.
@@ -402,9 +414,19 @@ expand_partitioned_rtentry(PlannerInfo *root, RelOptInfo *relinfo,
 
                /* If this child is itself partitioned, recurse */
                if (childrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
+               {
+                       AppendRelInfo *appinfo = root->append_rel_array[childRTindex];
+                       Bitmapset  *child_updatedCols;
+
+                       child_updatedCols = translate_col_privs(parent_updatedCols,
+                                                                                                       appinfo->translated_vars);
+
                        expand_partitioned_rtentry(root, childrelinfo,
                                                                           childrte, childRTindex,
-                                                                          childrel, top_parentrc, lockmode);
+                                                                          childrel,
+                                                                          child_updatedCols,
+                                                                          top_parentrc, lockmode);
+               }
 
                /* Close child relation, but keep locks */
                table_close(childrel, NoLock);
@@ -451,17 +473,15 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
        /*
         * Build an RTE for the child, and attach to query's rangetable list. We
         * copy most scalar fields of the parent's RTE, but replace relation OID,
-        * relkind, and inh for the child.  Also, set requiredPerms to zero since
-        * all required permissions checks are done on the original RTE. Likewise,
-        * set the child's securityQuals to empty, because we only want to apply
-        * the parent's RLS conditions regardless of what RLS properties
-        * individual children may have.  (This is an intentional choice to make
-        * inherited RLS work like regular permissions checks.) The parent
-        * securityQuals will be propagated to children along with other base
-        * restriction clauses, so we don't need to do it here.  Other
-        * infrastructure of the parent RTE has to be translated to match the
-        * child table's column ordering, which we do below, so a "flat" copy is
-        * sufficient to start with.
+        * relkind, and inh for the child.  Set the child's securityQuals to
+        * empty, because we only want to apply the parent's RLS conditions
+        * regardless of what RLS properties individual children may have. (This
+        * is an intentional choice to make inherited RLS work like regular
+        * permissions checks.) The parent securityQuals will be propagated to
+        * children along with other base restriction clauses, so we don't need to
+        * do it here.  Other infrastructure of the parent RTE has to be
+        * translated to match the child table's column ordering, which we do
+        * below, so a "flat" copy is sufficient to start with.
         */
        childrte = makeNode(RangeTblEntry);
        memcpy(childrte, parentrte, sizeof(RangeTblEntry));
@@ -476,9 +496,16 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
        }
        else
                childrte->inh = false;
-       childrte->requiredPerms = 0;
        childrte->securityQuals = NIL;
 
+       /*
+        * No permission checking for the child RTE unless it's the parent
+        * relation in its child role, which only applies to traditional
+        * inheritance.
+        */
+       if (childOID != parentOID)
+               childrte->perminfoindex = 0;
+
        /* Link not-yet-fully-filled child RTE into data structures */
        parse->rtable = lappend(parse->rtable, childrte);
        childRTindex = list_length(parse->rtable);
@@ -539,33 +566,12 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
        childrte->alias = childrte->eref = makeAlias(parentrte->eref->aliasname,
                                                                                                 child_colnames);
 
-       /*
-        * Translate the column permissions bitmaps to the child's attnums (we
-        * have to build the translated_vars list before we can do this).  But if
-        * this is the parent table, we can just duplicate the parent's bitmaps.
-        *
-        * Note: we need to do this even though the executor won't run any
-        * permissions checks on the child RTE.  The insertedCols/updatedCols
-        * bitmaps may be examined for trigger-firing purposes.
-        */
+       /* Translate the bitmapset of generated columns being updated. */
        if (childOID != parentOID)
-       {
-               childrte->selectedCols = translate_col_privs(parentrte->selectedCols,
-                                                                                                        appinfo->translated_vars);
-               childrte->insertedCols = translate_col_privs(parentrte->insertedCols,
-                                                                                                        appinfo->translated_vars);
-               childrte->updatedCols = translate_col_privs(parentrte->updatedCols,
-                                                                                                       appinfo->translated_vars);
                childrte->extraUpdatedCols = translate_col_privs(parentrte->extraUpdatedCols,
                                                                                                                 appinfo->translated_vars);
-       }
        else
-       {
-               childrte->selectedCols = bms_copy(parentrte->selectedCols);
-               childrte->insertedCols = bms_copy(parentrte->insertedCols);
-               childrte->updatedCols = bms_copy(parentrte->updatedCols);
                childrte->extraUpdatedCols = bms_copy(parentrte->extraUpdatedCols);
-       }
 
        /*
         * Store the RTE and appinfo in the respective PlannerInfo arrays, which
@@ -648,6 +654,54 @@ expand_single_inheritance_child(PlannerInfo *root, RangeTblEntry *parentrte,
        }
 }
 
+/*
+ * get_rel_all_updated_cols
+ *             Returns the set of columns of a given "simple" relation that are
+ *             updated by this query.
+ */
+Bitmapset *
+get_rel_all_updated_cols(PlannerInfo *root, RelOptInfo *rel)
+{
+       Index           relid;
+       RangeTblEntry *rte;
+       RTEPermissionInfo *perminfo;
+       Bitmapset  *updatedCols,
+                          *extraUpdatedCols;
+
+       Assert(root->parse->commandType == CMD_UPDATE);
+       Assert(IS_SIMPLE_REL(rel));
+
+       /*
+        * We obtain updatedCols and extraUpdatedCols for the query's result
+        * relation.  Then, if necessary, we map it to the column numbers of the
+        * relation for which they were requested.
+        */
+       relid = root->parse->resultRelation;
+       rte = planner_rt_fetch(relid, root);
+       perminfo = getRTEPermissionInfo(root->parse->rteperminfos, rte);
+
+       updatedCols = perminfo->updatedCols;
+       extraUpdatedCols = rte->extraUpdatedCols;
+
+       /*
+        * For "other" rels, we must look up the root parent relation mentioned in
+        * the query, and translate the column numbers.
+        */
+       if (rel->relid != relid)
+       {
+               RelOptInfo *top_parent_rel = find_base_rel(root, relid);
+
+               Assert(IS_OTHER_REL(rel));
+
+               updatedCols = translate_col_privs_multilevel(root, rel, top_parent_rel,
+                                                                                                        updatedCols);
+               extraUpdatedCols = translate_col_privs_multilevel(root, rel, top_parent_rel,
+                                                                                                                 extraUpdatedCols);
+       }
+
+       return bms_union(updatedCols, extraUpdatedCols);
+}
+
 /*
  * translate_col_privs
  *       Translate a bitmapset representing per-column privileges from the
@@ -866,3 +920,40 @@ apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel,
 
        return true;
 }
+
+/*
+ * translate_col_privs_multilevel
+ *             Recursively translates the column numbers contained in
+ *             'top_parent_cols' to the columns numbers of a descendent relation
+ *             given by 'relid'
+ */
+static Bitmapset *
+translate_col_privs_multilevel(PlannerInfo *root, RelOptInfo *rel,
+                                                          RelOptInfo *top_parent_rel,
+                                                          Bitmapset *top_parent_cols)
+{
+       Bitmapset  *result;
+       AppendRelInfo *appinfo;
+
+       if (top_parent_cols == NULL)
+               return NULL;
+
+       /* Recurse if immediate parent is not the top parent. */
+       if (rel->parent != top_parent_rel)
+       {
+               if (rel->parent)
+                       result = translate_col_privs_multilevel(root, rel->parent,
+                                                                                                       top_parent_rel,
+                                                                                                       top_parent_cols);
+               else
+                       elog(ERROR, "rel with relid %u is not a child rel", rel->relid);
+       }
+
+       Assert(root->append_rel_array != NULL);
+       appinfo = root->append_rel_array[rel->relid];
+       Assert(appinfo != NULL);
+
+       result = translate_col_privs(top_parent_cols, appinfo->translated_vars);
+
+       return result;
+}
index d7b4434e7f4a3acaf40a6e99b52fbc83491f90ec..7085cf3c41695875397c04404339d58ff0558809 100644 (file)
@@ -28,6 +28,7 @@
 #include "optimizer/plancat.h"
 #include "optimizer/restrictinfo.h"
 #include "optimizer/tlist.h"
+#include "parser/parse_relation.h"
 #include "utils/hsearch.h"
 #include "utils/lsyscache.h"
 
@@ -223,7 +224,25 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptInfo *parent)
        rel->rel_parallel_workers = -1; /* set up in get_relation_info */
        rel->amflags = 0;
        rel->serverid = InvalidOid;
-       rel->userid = rte->checkAsUser;
+       if (rte->rtekind == RTE_RELATION)
+       {
+               /*
+                * Get the userid from the relation's RTEPermissionInfo, though only
+                * the tables mentioned in query are assigned RTEPermissionInfos.
+                * Child relations (otherrels) simply use the parent's value.
+                */
+               if (parent == NULL)
+               {
+                       RTEPermissionInfo *perminfo;
+
+                       perminfo = getRTEPermissionInfo(root->parse->rteperminfos, rte);
+                       rel->userid = perminfo->checkAsUser;
+               }
+               else
+                       rel->userid = parent->userid;
+       }
+       else
+               rel->userid = InvalidOid;
        rel->useridiscurrent = false;
        rel->fdwroutine = NULL;
        rel->fdw_private = NULL;
index 6688c2a865b8ed2ad26771ece5a344d2f02767c4..2e593aed2bc999a9f70c6f13d55ac3080a8a524d 100644 (file)
@@ -518,6 +518,7 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
 
        /* done building the range table and jointree */
        qry->rtable = pstate->p_rtable;
+       qry->rteperminfos = pstate->p_rteperminfos;
        qry->jointree = makeFromExpr(pstate->p_joinlist, qual);
 
        qry->hasSubLinks = pstate->p_hasSubLinks;
@@ -546,11 +547,12 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
        List       *exprList = NIL;
        bool            isGeneralSelect;
        List       *sub_rtable;
+       List       *sub_rteperminfos;
        List       *sub_namespace;
        List       *icolumns;
        List       *attrnos;
        ParseNamespaceItem *nsitem;
-       RangeTblEntry *rte;
+       RTEPermissionInfo *perminfo;
        ListCell   *icols;
        ListCell   *attnos;
        ListCell   *lc;
@@ -594,17 +596,19 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
 
        /*
         * If a non-nil rangetable/namespace was passed in, and we are doing
-        * INSERT/SELECT, arrange to pass the rangetable/namespace down to the
-        * SELECT.  This can only happen if we are inside a CREATE RULE, and in
-        * that case we want the rule's OLD and NEW rtable entries to appear as
-        * part of the SELECT's rtable, not as outer references for it.  (Kluge!)
-        * The SELECT's joinlist is not affected however.  We must do this before
-        * adding the target table to the INSERT's rtable.
+        * INSERT/SELECT, arrange to pass the rangetable/rteperminfos/namespace
+        * down to the SELECT.  This can only happen if we are inside a CREATE
+        * RULE, and in that case we want the rule's OLD and NEW rtable entries to
+        * appear as part of the SELECT's rtable, not as outer references for it.
+        * (Kluge!) The SELECT's joinlist is not affected however.  We must do
+        * this before adding the target table to the INSERT's rtable.
         */
        if (isGeneralSelect)
        {
                sub_rtable = pstate->p_rtable;
                pstate->p_rtable = NIL;
+               sub_rteperminfos = pstate->p_rteperminfos;
+               pstate->p_rteperminfos = NIL;
                sub_namespace = pstate->p_namespace;
                pstate->p_namespace = NIL;
        }
@@ -669,6 +673,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
                 * the target column's type, which we handle below.
                 */
                sub_pstate->p_rtable = sub_rtable;
+               sub_pstate->p_rteperminfos = sub_rteperminfos;
                sub_pstate->p_joinexprs = NIL;  /* sub_rtable has no joins */
                sub_pstate->p_namespace = sub_namespace;
                sub_pstate->p_resolve_unknowns = false;
@@ -894,7 +899,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)
@@ -910,8 +915,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);
        }
 
        /*
@@ -938,6 +943,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
 
        /* done building the range table and jointree */
        qry->rtable = pstate->p_rtable;
+       qry->rteperminfos = pstate->p_rteperminfos;
        qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);
 
        qry->hasTargetSRFs = pstate->p_hasTargetSRFs;
@@ -1096,8 +1102,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,
@@ -1391,6 +1395,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
                resolveTargetListUnknowns(pstate, qry->targetList);
 
        qry->rtable = pstate->p_rtable;
+       qry->rteperminfos = pstate->p_rteperminfos;
        qry->jointree = makeFromExpr(pstate->p_joinlist, qual);
 
        qry->hasSubLinks = pstate->p_hasSubLinks;
@@ -1619,6 +1624,7 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt)
                                                                          linitial(stmt->lockingClause))->strength))));
 
        qry->rtable = pstate->p_rtable;
+       qry->rteperminfos = pstate->p_rteperminfos;
        qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);
 
        qry->hasSubLinks = pstate->p_hasSubLinks;
@@ -1865,6 +1871,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
        qry->limitOption = stmt->limitOption;
 
        qry->rtable = pstate->p_rtable;
+       qry->rteperminfos = pstate->p_rteperminfos;
        qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);
 
        qry->hasSubLinks = pstate->p_hasSubLinks;
@@ -2339,6 +2346,7 @@ transformReturnStmt(ParseState *pstate, ReturnStmt *stmt)
        if (pstate->p_resolve_unknowns)
                resolveTargetListUnknowns(pstate, qry->targetList);
        qry->rtable = pstate->p_rtable;
+       qry->rteperminfos = pstate->p_rteperminfos;
        qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);
        qry->hasSubLinks = pstate->p_hasSubLinks;
        qry->hasWindowFuncs = pstate->p_hasWindowFuncs;
@@ -2405,6 +2413,7 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
        qry->targetList = transformUpdateTargetList(pstate, stmt->targetList);
 
        qry->rtable = pstate->p_rtable;
+       qry->rteperminfos = pstate->p_rteperminfos;
        qry->jointree = makeFromExpr(pstate->p_joinlist, qual);
 
        qry->hasTargetSRFs = pstate->p_hasTargetSRFs;
@@ -2423,7 +2432,7 @@ List *
 transformUpdateTargetList(ParseState *pstate, List *origTlist)
 {
        List       *tlist = NIL;
-       RangeTblEntry *target_rte;
+       RTEPermissionInfo *target_perminfo;
        ListCell   *orig_tl;
        ListCell   *tl;
 
@@ -2435,7 +2444,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)
@@ -2476,8 +2485,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);
        }
@@ -2764,6 +2773,7 @@ transformPLAssignStmt(ParseState *pstate, PLAssignStmt *stmt)
                                                                                                   &qry->targetList);
 
        qry->rtable = pstate->p_rtable;
+       qry->rteperminfos = pstate->p_rteperminfos;
        qry->jointree = makeFromExpr(pstate->p_joinlist, qual);
 
        qry->hasSubLinks = pstate->p_hasSubLinks;
@@ -3242,9 +3252,16 @@ 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;
+                                       {
+                                               RTEPermissionInfo *perminfo;
+
+                                               applyLockingClause(qry, i,
+                                                                                  lc->strength,
+                                                                                  lc->waitPolicy,
+                                                                                  pushedDown);
+                                               perminfo = getRTEPermissionInfo(qry->rteperminfos, rte);
+                                               perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE;
+                                       }
                                        break;
                                case RTE_SUBQUERY:
                                        applyLockingClause(qry, i, lc->strength, lc->waitPolicy,
@@ -3324,9 +3341,16 @@ 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;
+                                                       {
+                                                               RTEPermissionInfo *perminfo;
+
+                                                               applyLockingClause(qry, i,
+                                                                                                  lc->strength,
+                                                                                                  lc->waitPolicy,
+                                                                                                  pushedDown);
+                                                               perminfo = getRTEPermissionInfo(qry->rteperminfos, rte);
+                                                               perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE;
+                                                       }
                                                        break;
                                                case RTE_SUBQUERY:
                                                        applyLockingClause(qry, i, lc->strength,
index e01c0734d1734286e288707a0d5ccb31fe0d439b..856839f379d1dc7a03da2c6b311fb30da5ee0376 100644 (file)
@@ -225,7 +225,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.
@@ -3226,16 +3226,17 @@ transformOnConflictArbiter(ParseState *pstate,
                if (infer->conname)
                {
                        Oid                     relid = RelationGetRelid(pstate->p_target_relation);
-                       RangeTblEntry *rte = pstate->p_target_nsitem->p_rte;
+                       RTEPermissionInfo *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);
                }
        }
 
index 62c2ff69f0c4d1825fdde5890d673dab13147d10..3844f2b45f3d3ecef35cca18bf69a9a79a26361d 100644 (file)
@@ -215,6 +215,7 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt)
         */
        qry->targetList = NIL;
        qry->rtable = pstate->p_rtable;
+       qry->rteperminfos = pstate->p_rteperminfos;
 
        /*
         * Transform the join condition.  This includes references to the target
@@ -287,7 +288,7 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt)
                                {
                                        List       *exprList = NIL;
                                        ListCell   *lc;
-                                       RangeTblEntry *rte;
+                                       RTEPermissionInfo *perminfo;
                                        ListCell   *icols;
                                        ListCell   *attnos;
                                        List       *icolumns;
@@ -346,7 +347,7 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt)
                                         * 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;
                                        forthree(lc, exprList, icols, icolumns, attnos, attrnos)
                                        {
                                                Expr       *expr = (Expr *) lfirst(lc);
@@ -360,8 +361,8 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt)
                                                                                          false);
                                                action->targetList = lappend(action->targetList, tle);
 
-                                               rte->insertedCols =
-                                                       bms_add_member(rte->insertedCols,
+                                               perminfo->insertedCols =
+                                                       bms_add_member(perminfo->insertedCols,
                                                                                   attr_num - FirstLowInvalidHeapAttributeNumber);
                                        }
                                }
index 4665f0b2b7cb61436256b8afb0903f50d6418c0f..b4878a92eabde677f87126711a9a9b713c606eb7 100644 (file)
@@ -1037,11 +1037,15 @@ markRTEForSelectPriv(ParseState *pstate, int rtindex, AttrNumber col)
 
        if (rte->rtekind == RTE_RELATION)
        {
+               RTEPermissionInfo *perminfo;
+
                /* Make sure the rel as a whole is marked for SELECT access */
-               rte->requiredPerms |= ACL_SELECT;
+               perminfo = getRTEPermissionInfo(pstate->p_rteperminfos, rte);
+               perminfo->requiredPerms |= ACL_SELECT;
                /* Must offset the attnum to fit in a bitmapset */
-               rte->selectedCols = bms_add_member(rte->selectedCols,
-                                                                                  col - FirstLowInvalidHeapAttributeNumber);
+               perminfo->selectedCols =
+                       bms_add_member(perminfo->selectedCols,
+                                                  col - FirstLowInvalidHeapAttributeNumber);
        }
        else if (rte->rtekind == RTE_JOIN)
        {
@@ -1251,10 +1255,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,
+                                                RTEPermissionInfo *perminfo,
+                                                TupleDesc tupdesc)
 {
        ParseNamespaceItem *nsitem;
        ParseNamespaceColumn *nscolumns;
@@ -1290,6 +1297,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;
@@ -1433,6 +1441,7 @@ addRangeTableEntry(ParseState *pstate,
                                   bool inFromCl)
 {
        RangeTblEntry *rte = makeNode(RangeTblEntry);
+       RTEPermissionInfo *perminfo;
        char       *refname = alias ? alias->aliasname : relation->relname;
        LOCKMODE        lockmode;
        Relation        rel;
@@ -1469,7 +1478,7 @@ addRangeTableEntry(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.
@@ -1478,12 +1487,8 @@ 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 = addRTEPermissionInfo(&pstate->p_rteperminfos, rte);
+       perminfo->requiredPerms = ACL_SELECT;
 
        /*
         * Add completed RTE to pstate's range table list, so that we know its
@@ -1497,7 +1502,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
@@ -1534,6 +1539,7 @@ addRangeTableEntryForRelation(ParseState *pstate,
                                                          bool inFromCl)
 {
        RangeTblEntry *rte = makeNode(RangeTblEntry);
+       RTEPermissionInfo *perminfo;
        char       *refname = alias ? alias->aliasname : RelationGetRelationName(rel);
 
        Assert(pstate != NULL);
@@ -1557,7 +1563,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.
@@ -1566,12 +1572,8 @@ 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 = addRTEPermissionInfo(&pstate->p_rteperminfos, rte);
+       perminfo->requiredPerms = ACL_SELECT;
 
        /*
         * Add completed RTE to pstate's range table list, so that we know its
@@ -1585,7 +1587,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);
 }
 
 /*
@@ -1659,21 +1661,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
+        * addRTEPermissionInfo().
         */
        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
@@ -1990,20 +1986,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 addRTEPermissionInfo().
         */
        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
@@ -2015,7 +2004,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);
 }
 
@@ -2082,20 +2071,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 addRTEPermissionInfo().
         */
        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
@@ -2170,19 +2152,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
+        * addRTEPermissionInfo().
         */
        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
@@ -2267,19 +2243,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
+        * addRTEPermissionInfo().
         */
        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
@@ -2294,6 +2264,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 */
@@ -2417,19 +2388,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
+        * addRTEPermissionInfo().
         */
        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
@@ -2543,16 +2508,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
+        * addRTEPermissionInfo().
         */
        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
@@ -2564,7 +2526,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);
 }
 
@@ -3189,6 +3151,7 @@ expandNSItemAttrs(ParseState *pstate, ParseNamespaceItem *nsitem,
                                  int sublevels_up, bool require_col_privs, int location)
 {
        RangeTblEntry *rte = nsitem->p_rte;
+       RTEPermissionInfo *perminfo = nsitem->p_perminfo;
        List       *names,
                           *vars;
        ListCell   *name,
@@ -3206,7 +3169,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)
        {
@@ -3855,3 +3821,57 @@ isQueryUsingTempRelation_walker(Node *node, void *context)
                                                                  isQueryUsingTempRelation_walker,
                                                                  context);
 }
+
+/*
+ * addRTEPermissionInfo
+ *             Creates RTEPermissionInfo for a given RTE and adds it into the
+ *             provided list.
+ *
+ * Returns the RTEPermissionInfo and sets rte->perminfoindex.
+ */
+RTEPermissionInfo *
+addRTEPermissionInfo(List **rteperminfos, RangeTblEntry *rte)
+{
+       RTEPermissionInfo *perminfo;
+
+       Assert(rte->rtekind == RTE_RELATION);
+       Assert(rte->perminfoindex == 0);
+
+       /* Nope, so make one and add to the list. */
+       perminfo = makeNode(RTEPermissionInfo);
+       perminfo->relid = rte->relid;
+       perminfo->inh = rte->inh;
+       /* Other information is set by fetching the node as and where needed. */
+
+       *rteperminfos = lappend(*rteperminfos, perminfo);
+
+       /* Note its index (1-based!) */
+       rte->perminfoindex = list_length(*rteperminfos);
+
+       return perminfo;
+}
+
+/*
+ * getRTEPermissionInfo
+ *             Find RTEPermissionInfo for a given relation in the provided list.
+ *
+ * This is a simple list_nth() operation, though it's good to have the
+ * function for the various sanity checks.
+ */
+RTEPermissionInfo *
+getRTEPermissionInfo(List *rteperminfos, RangeTblEntry *rte)
+{
+       RTEPermissionInfo *perminfo;
+
+       if (rte->perminfoindex == 0 ||
+               rte->perminfoindex > list_length(rteperminfos))
+               elog(ERROR, "invalid perminfoindex %d in RTE with relid %u",
+                        rte->perminfoindex, rte->relid);
+       perminfo = list_nth_node(RTEPermissionInfo, rteperminfos,
+                                                        rte->perminfoindex - 1);
+       if (perminfo->relid != rte->relid)
+               elog(ERROR, "permission info at index %u (with relid=%u) does not match provided RTE (with relid=%u)",
+                        rte->perminfoindex, perminfo->relid, rte->relid);
+
+       return perminfo;
+}
index 8e0d6fd01f1f0d2bc6a5a383321c3d34c447d357..56d64c8851665b38a04b190dcae6cf8675d90392 100644 (file)
@@ -1132,7 +1132,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
@@ -1367,6 +1367,7 @@ ExpandSingleTable(ParseState *pstate, ParseNamespaceItem *nsitem,
        else
        {
                RangeTblEntry *rte = nsitem->p_rte;
+               RTEPermissionInfo *perminfo = nsitem->p_perminfo;
                List       *vars;
                ListCell   *l;
 
@@ -1381,7 +1382,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)
@@ -1414,11 +1418,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.
+        * with its RTEPermissionInfo'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)
index 36791d881797f83e260873076baba4a7811d049d..342a17913381c224f9b362d2ce1e4025c70473c6 100644 (file)
@@ -3023,9 +3023,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
@@ -3081,6 +3078,7 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString,
 
                nothing_qry->commandType = CMD_NOTHING;
                nothing_qry->rtable = pstate->p_rtable;
+               nothing_qry->rteperminfos = pstate->p_rteperminfos;
                nothing_qry->jointree = makeFromExpr(NIL, NULL);        /* no join wanted */
 
                *actions = list_make1(nothing_qry);
@@ -3123,8 +3121,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);
 
index f9efe6c4c674541d78411459f8dd52dfbae8c040..96772e4d73a189eca1213699737ff4df55aed30c 100644 (file)
 #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"
@@ -516,6 +517,8 @@ create_edata_for_relation(LogicalRepRelMapEntry *rel)
        rte->rellockmode = AccessShareLock;
        ExecInitRangeTable(estate, list_make1(rte));
 
+       addRTEPermissionInfo(&estate->es_rteperminfos, rte);
+
        edata->targetRelInfo = resultRelInfo = makeNode(ResultRelInfo);
 
        /*
@@ -1813,6 +1816,7 @@ apply_handle_update(StringInfo s)
        bool            has_oldtup;
        TupleTableSlot *remoteslot;
        RangeTblEntry *target_rte;
+       RTEPermissionInfo *target_perminfo;
        MemoryContext oldctx;
 
        /*
@@ -1861,6 +1865,7 @@ apply_handle_update(StringInfo s)
         * on the subscriber, since we are not touching those.
         */
        target_rte = list_nth(estate->es_range_table, 0);
+       target_perminfo = list_nth(estate->es_rteperminfos, 0);
        for (int i = 0; i < remoteslot->tts_tupleDescriptor->natts; i++)
        {
                Form_pg_attribute att = TupleDescAttr(remoteslot->tts_tupleDescriptor, i);
@@ -1870,14 +1875,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_rte, target_perminfo, rel->localrel);
 
        /* Build the search tuple. */
        oldctx = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
index 8ac2c81b06be07537eb2e0673c5cdbb9e7141403..9f3afe965a70e847ee73ee2ecbc121ffc5aa8889 100644 (file)
@@ -632,14 +632,14 @@ checkRuleResultList(List *targetList, TupleDesc resultDesc, bool isSelect,
 /*
  * setRuleCheckAsUser
  *             Recursively scan a query or expression tree and set the checkAsUser
- *             field to the given userid in all rtable entries.
+ *             field to the given userid in all RTEPermissionInfos of the query.
  *
  * Note: for a view (ON SELECT rule), the checkAsUser field of the OLD
- * RTE entry will be overridden when the view rule is expanded, and the
- * checkAsUser field of the NEW entry is irrelevant because that entry's
- * requiredPerms bits will always be zero.  However, for other types of rules
- * it's important to set these fields to match the rule owner.  So we just set
- * them always.
+ * RTE entry's RTEPermissionInfo will be overridden when the view rule is
+ * expanded, and the checkAsUser for the NEW RTE entry's RTEPermissionInfo is
+ * irrelevant because its requiredPerms bits will always be zero.  However, for
+ * other types of rules it's important to set these fields to match the rule
+ * owner.  So we just set them always.
  */
 void
 setRuleCheckAsUser(Node *node, Oid userid)
@@ -666,18 +666,21 @@ setRuleCheckAsUser_Query(Query *qry, Oid userid)
 {
        ListCell   *l;
 
-       /* Set all the RTEs in this query node */
+       /* Set in all RTEPermissionInfos for this query. */
+       foreach(l, qry->rteperminfos)
+       {
+               RTEPermissionInfo *perminfo = lfirst_node(RTEPermissionInfo, 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 */
index 1e3efbbd36f84eaac5809074cdb768f7b47377fc..ea56ff79c861bc6a5815a9fca529661480c23145 100644 (file)
@@ -395,36 +395,43 @@ 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:
+        * 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.
         *
-        * We'd have a much harder job to adjust the query's varnos if we
-        * selectively removed RT entries.
+        * Also merge RTEPermissionInfo 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 rtable must be preserved so that the executor will do
-        * the correct permissions checks on it.
+        * all, and so its rteperminfos 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.
         *
-        * 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 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.
         *
-        * 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.
-        *
-        * NOTE: because planner will destructively alter rtable, we must ensure
-        * that rule action's rtable is separate and shares no substructure with
-        * the main rtable.  Hence do a deep copy here.
-        *
-        * Note also that RewriteQuery() relies on the fact that RT entries from
-        * the original query appear at the start of the expanded rtable, so
-        * beware of changing this.
+        * NOTE: because planner will destructively alter rtable and rteperminfos,
+        * we must ensure that rule action's lists are separate and shares no
+        * substructure with the main query's lists.  Hence do a deep copy here
+        * for both.
         */
-       sub_action->rtable = list_concat(copyObject(parsetree->rtable),
-                                                                        sub_action->rtable);
+       {
+               List       *rtable_tail = sub_action->rtable;
+               List       *perminfos_tail = sub_action->rteperminfos;
+
+               /*
+                * RewriteQuery relies on the fact that RT entries from the original
+                * query appear at the start of the expanded rtable, so we put the
+                * action's original table at the end of the list.
+                */
+               sub_action->rtable = copyObject(parsetree->rtable);
+               sub_action->rteperminfos = copyObject(parsetree->rteperminfos);
+               CombineRangeTables(&sub_action->rtable, &sub_action->rteperminfos,
+                                                  rtable_tail, perminfos_tail);
+       }
 
        /*
         * There could have been some SubLinks in parsetree's rtable, in which
@@ -1628,10 +1635,13 @@ rewriteValuesRTEToNulls(Query *parsetree, RangeTblEntry *rte)
 
 /*
  * Record in target_rte->extraUpdatedCols the indexes of any generated columns
- * that depend on any columns mentioned in target_rte->updatedCols.
+ * columns that depend on any columns mentioned in
+ * target_perminfo->updatedCols.
  */
 void
-fill_extraUpdatedCols(RangeTblEntry *target_rte, Relation target_relation)
+fill_extraUpdatedCols(RangeTblEntry *target_rte,
+                                         RTEPermissionInfo *target_perminfo,
+                                         Relation target_relation)
 {
        TupleDesc       tupdesc = RelationGetDescr(target_relation);
        TupleConstr *constr = tupdesc->constr;
@@ -1654,7 +1664,7 @@ 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))
+                       if (bms_overlap(target_perminfo->updatedCols, attrs_used))
                                target_rte->extraUpdatedCols =
                                        bms_add_member(target_rte->extraUpdatedCols,
                                                                   defval->adnum - FirstLowInvalidHeapAttributeNumber);
@@ -1747,6 +1757,8 @@ ApplyRetrieveRule(Query *parsetree,
        Query      *rule_action;
        RangeTblEntry *rte,
                           *subrte;
+       RTEPermissionInfo *perminfo,
+                          *sub_perminfo;
        RowMarkClause *rc;
 
        if (list_length(rule->actions) != 1)
@@ -1787,18 +1799,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.
@@ -1862,12 +1862,6 @@ ApplyRetrieveRule(Query *parsetree,
 
        /*
         * Recursively expand any view references inside the view.
-        *
-        * Note: this must happen after markQueryForLocking.  That way, any UPDATE
-        * permission bits needed for sub-views are initially applied to their
-        * RTE_RELATION RTEs by markQueryForLocking, and then transferred to their
-        * OLD rangetable entries by the action below (in a recursive call of this
-        * routine).
         */
        rule_action = fireRIRrules(rule_action, activeRIRs);
 
@@ -1876,6 +1870,7 @@ ApplyRetrieveRule(Query *parsetree,
         * original RTE to a subquery RTE.
         */
        rte = rt_fetch(rt_index, parsetree->rtable);
+       perminfo = getRTEPermissionInfo(parsetree->rteperminfos, rte);
 
        rte->rtekind = RTE_SUBQUERY;
        rte->subquery = rule_action;
@@ -1885,6 +1880,7 @@ ApplyRetrieveRule(Query *parsetree,
        rte->relkind = 0;
        rte->rellockmode = 0;
        rte->tablesample = NULL;
+       rte->perminfoindex = 0;         /* no permission checking for this RTE */
        rte->inh = false;                       /* must not be set for a subquery */
 
        /*
@@ -1893,19 +1889,12 @@ ApplyRetrieveRule(Query *parsetree,
         */
        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;
+       sub_perminfo = getRTEPermissionInfo(rule_action->rteperminfos, subrte);
+       sub_perminfo->requiredPerms = perminfo->requiredPerms;
+       sub_perminfo->checkAsUser = perminfo->checkAsUser;
+       sub_perminfo->selectedCols = perminfo->selectedCols;
+       sub_perminfo->insertedCols = perminfo->insertedCols;
+       sub_perminfo->updatedCols = perminfo->updatedCols;
 
        return parsetree;
 }
@@ -1935,8 +1924,12 @@ markQueryForLocking(Query *qry, Node *jtnode,
 
                if (rte->rtekind == RTE_RELATION)
                {
+                       RTEPermissionInfo *perminfo;
+
                        applyLockingClause(qry, rti, strength, waitPolicy, pushedDown);
-                       rte->requiredPerms |= ACL_SELECT_FOR_UPDATE;
+
+                       perminfo = getRTEPermissionInfo(qry->rteperminfos, rte);
+                       perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE;
                }
                else if (rte->rtekind == RTE_SUBQUERY)
                {
@@ -3077,6 +3070,9 @@ rewriteTargetView(Query *parsetree, Relation view)
        RangeTblEntry *base_rte;
        RangeTblEntry *view_rte;
        RangeTblEntry *new_rte;
+       RTEPermissionInfo *base_perminfo;
+       RTEPermissionInfo *view_perminfo;
+       RTEPermissionInfo *new_perminfo;
        Relation        base_rel;
        List       *view_targetlist;
        ListCell   *lc;
@@ -3213,6 +3209,7 @@ rewriteTargetView(Query *parsetree, Relation view)
        base_rt_index = rtr->rtindex;
        base_rte = rt_fetch(base_rt_index, viewquery->rtable);
        Assert(base_rte->rtekind == RTE_RELATION);
+       base_perminfo = getRTEPermissionInfo(viewquery->rteperminfos, base_rte);
 
        /*
         * Up to now, the base relation hasn't been touched at all in our query.
@@ -3284,57 +3281,69 @@ rewriteTargetView(Query *parsetree, Relation view)
                                   0);
 
        /*
-        * If the view has "security_invoker" set, mark the new target RTE for the
-        * permissions checks that we want to enforce against the query caller.
-        * Otherwise we want to enforce them against the view owner.
+        * If the view has "security_invoker" set, mark the new target relation
+        * for the permissions checks that we want to enforce against the query
+        * caller. Otherwise we want to enforce them against the view owner.
         *
         * 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.
+        * ACL_SELECT bit that is presumably in new_perminfo->requiredPerms
+        * initially.
         *
-        * 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 RTEPermissionInfo remains in the query's
+        * rteperminfos so that the executor still performs appropriate
+        * permissions checks for the query caller's use of the view.
         */
+       view_perminfo = getRTEPermissionInfo(parsetree->rteperminfos, view_rte);
+
+       /*
+        * Disregard the perminfo in viewquery->rteperminfos that the base_rte
+        * would currently be pointing at, because we'd like it to point now to a
+        * new one that will be filled below.  Must set perminfoindex to 0 to not
+        * trip over the Assert in addRTEPermissionInfo().
+        */
+       new_rte->perminfoindex = 0;
+       new_perminfo = addRTEPermissionInfo(&parsetree->rteperminfos, new_rte);
        if (RelationHasSecurityInvoker(view))
-               new_rte->checkAsUser = InvalidOid;
+               new_perminfo->checkAsUser = InvalidOid;
        else
-               new_rte->checkAsUser = view->rd_rel->relowner;
-
-       new_rte->requiredPerms = view_rte->requiredPerms;
+               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.)
+        * Initially, new_perminfo (base_perminfo) 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_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));
+       Assert(bms_is_empty(new_perminfo->insertedCols) &&
+                  bms_is_empty(new_perminfo->updatedCols));
+
+       new_perminfo->selectedCols = base_perminfo->selectedCols;
 
-       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
@@ -3438,7 +3447,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;
 
@@ -3449,8 +3458,8 @@ 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 */
+               /* Ignore the RTEPermissionInfo that would've been added. */
+               new_exclRte->perminfoindex = 0;
 
                parsetree->rtable = lappend(parsetree->rtable, new_exclRte);
                new_exclRelIndex = parsetree->onConflict->exclRelIndex =
@@ -3728,6 +3737,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events, int orig_rt_length)
        {
                int                     result_relation;
                RangeTblEntry *rt_entry;
+               RTEPermissionInfo *rt_perminfo;
                Relation        rt_entry_relation;
                List       *locks;
                int                     product_orig_rt_length;
@@ -3740,6 +3750,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events, int orig_rt_length)
                Assert(result_relation != 0);
                rt_entry = rt_fetch(result_relation, parsetree->rtable);
                Assert(rt_entry->rtekind == RTE_RELATION);
+               rt_perminfo = getRTEPermissionInfo(parsetree->rteperminfos, rt_entry);
 
                /*
                 * We can use NoLock here since either the parser or
@@ -3833,7 +3844,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events, int orig_rt_length)
                                                                        NULL, 0, NULL);
 
                        /* Also populate extraUpdatedCols (for generated columns) */
-                       fill_extraUpdatedCols(rt_entry, rt_entry_relation);
+                       fill_extraUpdatedCols(rt_entry, rt_perminfo, rt_entry_relation);
                }
                else if (event == CMD_MERGE)
                {
index 101c39553ae55d290ea3f7935a5dddbb87719256..2a07502030be7e0ae5990f79997e695be2ae494f 100644 (file)
@@ -316,6 +316,39 @@ contains_multiexpr_param(Node *node, void *context)
        return expression_tree_walker(node, contains_multiexpr_param, context);
 }
 
+/*
+ * CombineRangeTables
+ *             Adds the RTEs of 'src_rtable' into 'dst_rtable'
+ *
+ * This also adds the RTEPermissionInfos of 'src_perminfos' (belonging to the
+ * RTEs in 'src_rtable') into *dst_perminfos and also updates perminfoindex of
+ * the RTEs in 'src_rtable' to now point to the perminfos' indexes in
+ * *dst_perminfos.
+ *
+ * Note that this changes both 'dst_rtable' and 'dst_perminfo' destructively,
+ * so the caller should have better passed safe-to-modify copies.
+ */
+void
+CombineRangeTables(List **dst_rtable, List **dst_perminfos,
+                                  List *src_rtable, List *src_perminfos)
+{
+       ListCell   *l;
+       int                     offset = list_length(*dst_perminfos);
+
+       if (offset > 0)
+       {
+               foreach(l, src_rtable)
+               {
+                       RangeTblEntry *rte = lfirst_node(RangeTblEntry, l);
+
+                       if (rte->perminfoindex > 0)
+                               rte->perminfoindex += offset;
+               }
+       }
+
+       *dst_perminfos = list_concat(*dst_perminfos, src_perminfos);
+       *dst_rtable = list_concat(*dst_rtable, src_rtable);
+}
 
 /*
  * OffsetVarNodes - adjust Vars when appending one query's RT to another
index f49cfb6cc6678b99b4ac6ffad008c78715752cbb..f03b36a6e4e3a50f6928015fa9a4012b99a1646f 100644 (file)
@@ -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;
+       RTEPermissionInfo *perminfo;
 
        /* Defaults for the return values */
        *securityQuals = NIL;
@@ -122,16 +124,21 @@ 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 = getRTEPermissionInfo(root->rteperminfos, rte);
+
        /* Switch to checkAsUser if it's set */
-       user_id = OidIsValid(rte->checkAsUser) ? rte->checkAsUser : GetUserId();
+       user_id = OidIsValid(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 +203,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;
@@ -243,7 +250,7 @@ get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index,
         */
        if ((commandType == CMD_UPDATE || commandType == CMD_DELETE ||
                 commandType == CMD_MERGE) &&
-               rte->requiredPerms & ACL_SELECT)
+               perminfo->requiredPerms & ACL_SELECT)
        {
                List       *select_permissive_policies;
                List       *select_restrictive_policies;
@@ -286,7 +293,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;
@@ -342,7 +349,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,
@@ -371,7 +378,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,
@@ -474,8 +481,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
index dc07157037d49afba76031f1b233eaa75e550e8b..29f29d664b646c396dc14e87f68eefdc19c9e541 100644 (file)
@@ -1375,6 +1375,8 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
        char            fkattname[MAX_QUOTED_NAME_LEN + 3];
        RangeTblEntry *pkrte;
        RangeTblEntry *fkrte;
+       RTEPermissionInfo *pk_perminfo;
+       RTEPermissionInfo *fk_perminfo;
        const char *sep;
        const char *fk_only;
        const char *pk_only;
@@ -1397,27 +1399,34 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
        pkrte->relid = RelationGetRelid(pk_rel);
        pkrte->relkind = pk_rel->rd_rel->relkind;
        pkrte->rellockmode = AccessShareLock;
-       pkrte->requiredPerms = ACL_SELECT;
+
+       pk_perminfo = makeNode(RTEPermissionInfo);
+       pk_perminfo->relid = RelationGetRelid(pk_rel);
+       pk_perminfo->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;
+
+       fk_perminfo = makeNode(RTEPermissionInfo);
+       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(fkrte, pkrte),
+                                                         list_make2(fk_perminfo, pk_perminfo), false))
                return false;
 
        /*
index a50eecc7c8a4c0bfee51c09fcd2fed2d77e9ed2f..450e5124a5abe36262590a0981cb930c1585893a 100644 (file)
@@ -847,8 +847,8 @@ RelationBuildRuleLock(Relation relation)
 
                /*
                 * Scan through the rule's actions and set the checkAsUser field on
-                * all rtable entries. We have to look at the qual as well, in case it
-                * contains sublinks.
+                * all RTEPermissionInfos. We have to look at the qual as well, in
+                * case it contains sublinks.
                 *
                 * The reason for doing this when the rule is loaded, rather than when
                 * it is stored, is that otherwise ALTER TABLE OWNER would have to
index 8e80a075a2737fa54a506d54783cd602b414b52c..51e5acfe4fb4d22721227e377de40ee849e4584e 100644 (file)
@@ -57,6 +57,6 @@
  */
 
 /*                                                     yyyymmddN */
-#define CATALOG_VERSION_NO     202212011
+#define CATALOG_VERSION_NO     202212061
 
 #endif
index 8d9cc5accdb45a8e38140561589b9b050013951d..c5e5875eb87d2610ddb84569975fd8a3456dc64a 100644 (file)
@@ -97,7 +97,8 @@ typedef struct CopyFromStateData
        int                *defmap;                     /* array of default att numbers */
        ExprState **defexprs;           /* array of default att expressions */
        bool            volatile_defexprs;      /* is any of defexprs volatile? */
-       List       *range_table;
+       List       *range_table;        /* single element list of RangeTblEntry */
+       List       *rteperminfos;       /* single element list of RTEPermissionInfo */
        ExprState  *qualexpr;
 
        TransitionCaptureState *transition_capture;
index aaf2bc78b9c71c4dcdeb8fc2ad09ec3c50bc0070..a1202a0cf5f4ebedd24c70be924a2e3e9934aaf6 100644 (file)
@@ -80,8 +80,10 @@ extern PGDLLIMPORT ExecutorFinish_hook_type ExecutorFinish_hook;
 typedef void (*ExecutorEnd_hook_type) (QueryDesc *queryDesc);
 extern PGDLLIMPORT ExecutorEnd_hook_type ExecutorEnd_hook;
 
-/* Hook for plugins to get control in ExecCheckRTPerms() */
-typedef bool (*ExecutorCheckPerms_hook_type) (List *, bool);
+/* Hook for plugins to get control in ExecCheckPermissions() */
+typedef bool (*ExecutorCheckPerms_hook_type) (List *rangeTable,
+                                                                                         List *rtePermInfos,
+                                                                                         bool ereport_on_violation);
 extern PGDLLIMPORT ExecutorCheckPerms_hook_type ExecutorCheckPerms_hook;
 
 
@@ -196,7 +198,8 @@ extern void standard_ExecutorFinish(QueryDesc *queryDesc);
 extern void ExecutorEnd(QueryDesc *queryDesc);
 extern void standard_ExecutorEnd(QueryDesc *queryDesc);
 extern void ExecutorRewind(QueryDesc *queryDesc);
-extern bool ExecCheckRTPerms(List *rangeTable, bool ereport_on_violation);
+extern bool ExecCheckPermissions(List *rangeTable,
+                                                                List *rteperminfos, bool ereport_on_violation);
 extern void CheckValidResultRel(ResultRelInfo *resultRelInfo, CmdType operation);
 extern void InitResultRelInfo(ResultRelInfo *resultRelInfo,
                                                          Relation resultRelationDesc,
@@ -602,6 +605,7 @@ extern TupleTableSlot *ExecGetReturningSlot(EState *estate, ResultRelInfo *relIn
 extern TupleConversionMap *ExecGetChildToRootMap(ResultRelInfo *resultRelInfo);
 extern TupleConversionMap *ExecGetRootToChildMap(ResultRelInfo *resultRelInfo, EState *estate);
 
+extern Oid     ExecGetResultRelCheckAsUser(ResultRelInfo *relInfo, EState *estate);
 extern Bitmapset *ExecGetInsertedCols(ResultRelInfo *relinfo, EState *estate);
 extern Bitmapset *ExecGetUpdatedCols(ResultRelInfo *relinfo, EState *estate);
 extern Bitmapset *ExecGetExtraUpdatedCols(ResultRelInfo *relinfo, EState *estate);
index 71248a9466071c6e0196f997c2cebf822ca48262..08ad9d6be3f3d5b020b58e928f9d9b8b17cd2586 100644 (file)
@@ -617,8 +617,9 @@ typedef struct EState
                                                                 * pointers, or NULL if not yet opened */
        struct ExecRowMark **es_rowmarks;       /* Array of per-range-table-entry
                                                                                 * ExecRowMarks, or NULL if none */
+       List       *es_rteperminfos;    /* List of RTEPermissionInfo */
        PlannedStmt *es_plannedstmt;    /* link to top of plan tree */
-       List            *es_part_prune_infos;   /* PlannedStmt.partPruneInfos */
+       List       *es_part_prune_infos;        /* PlannedStmt.partPruneInfos */
        const char *es_sourceText;      /* Source text from QueryDesc */
 
        JunkFilter *es_junkFilter;      /* top-level junk filter, if any */
index f17846e30e22bb0c4ed45532b54a7d60acd0dc55..6a6d3293e4145490c19005ea8df677e74a815d5c 100644 (file)
@@ -154,6 +154,8 @@ typedef struct Query
        List       *cteList;            /* WITH list (of CommonTableExpr's) */
 
        List       *rtable;                     /* list of range table entries */
+       List       *rteperminfos;       /* list of RTEPermissionInfo nodes for the
+                                                                * rtable entries having perminfoindex > 0 */
        FromExpr   *jointree;           /* table join tree (FROM and WHERE clauses);
                                                                 * also USING clause for MERGE */
 
@@ -968,37 +970,6 @@ typedef struct PartitionCmd
  *       control visibility.  But it is needed by ruleutils.c to determine
  *       whether RTEs should be shown in decompiled queries.
  *
- *       requiredPerms and checkAsUser specify run-time access permissions
- *       checks to be performed at query startup.  The user must have *all*
- *       of the permissions that are OR'd together in requiredPerms (zero
- *       indicates no permissions checking).  If checkAsUser is not zero,
- *       then do the permissions checks using the access rights of that user,
- *       not the current effective user ID.  (This allows rules to act as
- *       setuid gateways.)  Permissions checks only apply to RELATION RTEs.
- *
- *       For SELECT/INSERT/UPDATE permissions, if the user doesn't have
- *       table-wide permissions then it is sufficient to have the permissions
- *       on all columns identified in selectedCols (for SELECT) and/or
- *       insertedCols and/or updatedCols (INSERT with ON CONFLICT DO UPDATE may
- *       have all 3).  selectedCols, insertedCols and updatedCols are bitmapsets,
- *       which cannot have negative integer members, so we subtract
- *       FirstLowInvalidHeapAttributeNumber from column numbers before storing
- *       them in these fields.  A whole-row Var reference is represented by
- *       setting the bit for InvalidAttrNumber.
- *
- *       updatedCols is also used in some other places, for example, to determine
- *       which triggers to fire and in FDWs to know which changed columns they
- *       need to ship off.
- *
- *       Generated columns that are caused to be updated by an update to a base
- *       column are listed in extraUpdatedCols.  This is not considered for
- *       permission checking, but it is useful in those places that want to know
- *       the full set of columns being updated as opposed to only the ones the
- *       user explicitly mentioned in the query.  (There is currently no need for
- *       an extraInsertedCols, but it could exist.)  Note that extraUpdatedCols
- *       is populated during query rewrite, NOT in the parser, since generated
- *       columns could be added after a rule has been parsed and stored.
- *
  *       securityQuals is a list of security barrier quals (boolean expressions),
  *       to be tested in the listed order before returning a row from the
  *       relation.  It is always NIL in parser output.  Entries are added by the
@@ -1054,11 +1025,16 @@ typedef struct RangeTblEntry
         * current query; this happens if a DO ALSO rule simply scans the original
         * target table.  We leave such RTEs with their original lockmode so as to
         * avoid getting an additional, lesser lock.
+        *
+        * perminfoindex is 1-based index of the RTEPermissionInfo belonging to
+        * this RTE in the containing struct's list of same; 0 if permissions need
+        * not be checked for this RTE.
         */
        Oid                     relid;                  /* OID of the relation */
        char            relkind;                /* relation kind (see pg_class.relkind) */
        int                     rellockmode;    /* lock level that query requires on the rel */
        struct TableSampleClause *tablesample;  /* sampling info, or NULL */
+       Index           perminfoindex;
 
        /*
         * Fields valid for a subquery RTE (else NULL):
@@ -1178,14 +1154,64 @@ typedef struct RangeTblEntry
        bool            lateral;                /* subquery, function, or values is LATERAL? */
        bool            inh;                    /* inheritance requested? */
        bool            inFromCl;               /* present in FROM clause? */
+       Bitmapset  *extraUpdatedCols;   /* generated columns being updated */
+       List       *securityQuals;      /* security barrier quals to apply, if any */
+} RangeTblEntry;
+
+/*
+ * RTEPermissionInfo
+ *             Per-relation information for permission checking. Added to the Query
+ *             node by the parser when adding the corresponding RTE to the query
+ *             range table and subsequently editorialized on by the rewriter if
+ *             needed after rule expansion.
+ *
+ * Only the relations directly mentioned in the query are checked for
+ * accesss permissions by the core executor, so only their RTEPermissionInfos
+ * are present in the Query.  However, extensions may want to check inheritance
+ * children too, depending on the value of rte->inh, so it's copied in 'inh'
+ * for their perusal.
+ *
+ * requiredPerms and checkAsUser specify run-time access permissions checks
+ * to be performed at query startup.  The user must have *all* of the
+ * permissions that are OR'd together in requiredPerms (never 0!).  If
+ * checkAsUser is not zero, then do the permissions checks using the access
+ * rights of that user, not the current effective user ID.  (This allows rules
+ * to act as setuid gateways.)
+ *
+ * For SELECT/INSERT/UPDATE permissions, if the user doesn't have table-wide
+ * permissions then it is sufficient to have the permissions on all columns
+ * identified in selectedCols (for SELECT) and/or insertedCols and/or
+ * updatedCols (INSERT with ON CONFLICT DO UPDATE may have all 3).
+ * selectedCols, insertedCols and updatedCols are bitmapsets, which cannot have
+ * negative integer members, so we subtract FirstLowInvalidHeapAttributeNumber
+ * from column numbers before storing them in these fields.  A whole-row Var
+ * reference is represented by setting the bit for InvalidAttrNumber.
+ *
+ * updatedCols is also used in some other places, for example, to determine
+ * which triggers to fire and in FDWs to know which changed columns they need
+ * to ship off.
+ *
+ * Generated columns that are caused to be updated by an update to a base
+ * column are listed in extraUpdatedCols.  This is not considered for
+ * permission checking, but it is useful in those places that want to know the
+ * full set of columns being updated as opposed to only the ones the user
+ * explicitly mentioned in the query.  (There is currently no need for an
+ * extraInsertedCols, but it could exist.)  Note that extraUpdatedCols is
+ * populated during query rewrite, NOT in the parser, since generated columns
+ * could be added after a rule has been parsed and stored.
+ */
+typedef struct RTEPermissionInfo
+{
+       NodeTag         type;
+
+       Oid                     relid;                  /* relation OID */
+       bool            inh;                    /* separately check inheritance children? */
        AclMode         requiredPerms;  /* bitmask of required access permissions */
        Oid                     checkAsUser;    /* if valid, check access as this role */
        Bitmapset  *selectedCols;       /* columns needing SELECT permission */
        Bitmapset  *insertedCols;       /* columns needing INSERT permission */
        Bitmapset  *updatedCols;        /* columns needing UPDATE permission */
-       Bitmapset  *extraUpdatedCols;   /* generated columns being updated */
-       List       *securityQuals;      /* security barrier quals to apply, if any */
-} RangeTblEntry;
+} RTEPermissionInfo;
 
 /*
  * RangeTblFunction -
index 12624e6adb128a0b0db4cdeb9200c4e454b6187e..654dba61aa0c43d5a9783c25b3e2626ffd23f4e7 100644 (file)
@@ -113,6 +113,9 @@ typedef struct PlannerGlobal
        /* "flat" rangetable for executor */
        List       *finalrtable;
 
+       /* "flat" list of RTEPermissionInfos */
+       List       *finalrteperminfos;
+
        /* "flat" list of PlanRowMarks */
        List       *finalrowmarks;
 
index c36a15bd09d3b69b50fe0e7cbd8512703f7c213f..bddfe861912a9e5dc5caf8ccb754cfaeb05eee3b 100644 (file)
@@ -75,6 +75,9 @@ typedef struct PlannedStmt
 
        List       *rtable;                     /* list of RangeTblEntry nodes */
 
+       List       *permInfos;          /* list of RTEPermissionInfo nodes for rtable
+                                                                * entries needing one */
+
        /* rtable indexes of target relations for INSERT/UPDATE/DELETE/MERGE */
        List       *resultRelations;    /* integer list of RT indexes, or NIL */
 
index adcb1d737220f1e3bc541c85185891fc704ede63..8ebd42b757a31de9fd0a549eb1adaaad78fd9373 100644 (file)
@@ -20,6 +20,8 @@
 extern void expand_inherited_rtentry(PlannerInfo *root, RelOptInfo *rel,
                                                                         RangeTblEntry *rte, Index rti);
 
+extern Bitmapset *get_rel_all_updated_cols(PlannerInfo *root, RelOptInfo *rel);
+
 extern bool apply_child_basequals(PlannerInfo *root, RelOptInfo *parentrel,
                                                                  RelOptInfo *childrel, RangeTblEntry *childRTE,
                                                                  AppendRelInfo *appinfo);
index 962ebf65de185d0f86dc4145219a5bdac7edd113..3fd56ceccdffae55c83e58b596a2056fb54f8529 100644 (file)
@@ -111,6 +111,9 @@ typedef Node *(*CoerceParamHook) (ParseState *pstate, Param *param,
  * Note that neither relname nor refname of these entries are necessarily
  * unique; searching the rtable by name is a bad idea.
  *
+ * p_rteperminfos: list of RTEPermissionInfo containing an entry corresponding
+ * to each RTE_RELATION entry in p_rtable.
+ *
  * p_joinexprs: list of JoinExpr nodes associated with p_rtable entries.
  * This is one-for-one with p_rtable, but contains NULLs for non-join
  * RTEs, and may be shorter than p_rtable if the last RTE(s) aren't joins.
@@ -181,6 +184,8 @@ struct ParseState
        ParseState *parentParseState;   /* stack link */
        const char *p_sourcetext;       /* source text, or NULL if not available */
        List       *p_rtable;           /* range table so far */
+       List       *p_rteperminfos; /* list of RTEPermissionInfo nodes for each
+                                                                * RTE_RELATION entry in rtable */
        List       *p_joinexprs;        /* JoinExprs for RTE_JOIN p_rtable entries */
        List       *p_joinlist;         /* join items so far (will become FromExpr
                                                                 * node's fromlist) */
@@ -234,7 +239,8 @@ struct ParseState
  * join's first N columns, the net effect is just that we expose only those
  * join columns via this nsitem.)
  *
- * p_rte and p_rtindex link to the underlying rangetable entry.
+ * p_rte and p_rtindex link to the underlying rangetable entry, and
+ * p_perminfo to the entry in rteperminfos.
  *
  * The p_nscolumns array contains info showing how to construct Vars
  * referencing the names appearing in the p_names->colnames list.
@@ -271,6 +277,7 @@ struct ParseNamespaceItem
        Alias      *p_names;            /* Table and column names */
        RangeTblEntry *p_rte;           /* The relation's rangetable entry */
        int                     p_rtindex;              /* The relation's index in the rangetable */
+       RTEPermissionInfo *p_perminfo;  /* The relation's rteperminfos entry */
        /* array of same length as p_names->colnames: */
        ParseNamespaceColumn *p_nscolumns;      /* per-column data */
        bool            p_rel_visible;  /* Relation name is visible? */
index 484db165dbf82ef55d0233260d77c4bb207c73a7..2f8d417709f0d582bb56e2b5b700307bc0341405 100644 (file)
@@ -99,6 +99,10 @@ extern ParseNamespaceItem *addRangeTableEntryForCTE(ParseState *pstate,
 extern ParseNamespaceItem *addRangeTableEntryForENR(ParseState *pstate,
                                                                                                        RangeVar *rv,
                                                                                                        bool inFromCl);
+extern RTEPermissionInfo *addRTEPermissionInfo(List **rteperminfos,
+                                                                                          RangeTblEntry *rte);
+extern RTEPermissionInfo *getRTEPermissionInfo(List *rteperminfos,
+                                                                                          RangeTblEntry *rte);
 extern bool isLockedRefname(ParseState *pstate, const char *refname);
 extern void addNSItemToQuery(ParseState *pstate, ParseNamespaceItem *nsitem,
                                                         bool addToJoinList,
index 90ecf109afcffb8884d336c4e629bcf811afcf58..05c3680cd6af1236a0aa0fc829533da9f0167bed 100644 (file)
@@ -25,6 +25,7 @@ extern void AcquireRewriteLocks(Query *parsetree,
 extern Node *build_column_default(Relation rel, int attrno);
 
 extern void fill_extraUpdatedCols(RangeTblEntry *target_rte,
+                                                                 RTEPermissionInfo *target_perminfo,
                                                                  Relation target_relation);
 
 extern Query *get_view_query(Relation view);
index f001ca41bbcfb2e35e4cda5bcf778c9fc2709572..05e6fe1f4b9c1928447d87c6040d24d0af165717 100644 (file)
@@ -41,6 +41,8 @@ typedef enum ReplaceVarsNoMatchOption
 } ReplaceVarsNoMatchOption;
 
 
+extern void CombineRangeTables(List **dst_rtable, List **dst_perminfos,
+                                                          List *src_rtable, List *src_perminfos);
 extern void OffsetVarNodes(Node *node, int offset, int sublevels_up);
 extern void ChangeVarNodes(Node *node, int rt_index, int new_index,
                                                   int sublevels_up);
index 4b4e259cd2377c0c5e236b90ea70ed2e9faf5856..15f9006f2ea2b2962d1aa2424c47b14bc15fc5db 100644 (file)
@@ -55,7 +55,7 @@ static void REGRESS_object_access_hook_str(ObjectAccessType access,
                                                                                   int subId, void *arg);
 static void REGRESS_object_access_hook(ObjectAccessType access, Oid classId,
                                                                           Oid objectId, int subId, void *arg);
-static bool REGRESS_exec_check_perms(List *rangeTabls, bool do_abort);
+static bool REGRESS_exec_check_perms(List *rangeTabls, List *rteperminfos, bool do_abort);
 static void REGRESS_utility_command(PlannedStmt *pstmt,
                                                                        const char *queryString, bool readOnlyTree,
                                                                        ProcessUtilityContext context,
@@ -345,7 +345,7 @@ REGRESS_object_access_hook(ObjectAccessType access, Oid classId, Oid objectId, i
 }
 
 static bool
-REGRESS_exec_check_perms(List *rangeTabls, bool do_abort)
+REGRESS_exec_check_perms(List *rangeTabls, List *rteperminfos, bool do_abort)
 {
        bool            am_super = superuser_arg(GetUserId());
        bool            allow = true;
@@ -361,7 +361,7 @@ REGRESS_exec_check_perms(List *rangeTabls, bool do_abort)
 
        /* Forward to next hook in the chain */
        if (next_exec_check_perms_hook &&
-               !(*next_exec_check_perms_hook) (rangeTabls, do_abort))
+               !(*next_exec_check_perms_hook) (rangeTabls, rteperminfos, do_abort))
                allow = false;
 
        if (allow)
index 532ea369901620e1aae677c197c5c72f216cf3bc..fb9f936d43a56584d47d02e7e8b3e5b81ac38060 100644 (file)
@@ -3569,6 +3569,18 @@ CREATE RULE rule1 AS ON INSERT TO ruletest_v1
 SET SESSION AUTHORIZATION regress_rule_user1;
 INSERT INTO ruletest_v1 VALUES (1);
 RESET SESSION AUTHORIZATION;
+-- Test that main query's relation's permissions are checked before
+-- the rule action's relation's.
+CREATE TABLE ruletest_t3 (x int);
+CREATE RULE rule2 AS ON UPDATE TO ruletest_t1
+    DO INSTEAD INSERT INTO ruletest_t2 VALUES (OLD.*);
+REVOKE ALL ON ruletest_t2 FROM regress_rule_user1;
+REVOKE ALL ON ruletest_t3 FROM regress_rule_user1;
+ALTER TABLE ruletest_t1 OWNER TO regress_rule_user1;
+SET SESSION AUTHORIZATION regress_rule_user1;
+UPDATE ruletest_t1 t1 SET x = 0 FROM ruletest_t3 t3 WHERE t1.x = t3.x;
+ERROR:  permission denied for table ruletest_t3
+RESET SESSION AUTHORIZATION;
 SELECT * FROM ruletest_t1;
  x 
 ---
@@ -3581,6 +3593,8 @@ SELECT * FROM ruletest_t2;
 (1 row)
 
 DROP VIEW ruletest_v1;
+DROP RULE rule2 ON ruletest_t1;
+DROP TABLE ruletest_t3;
 DROP TABLE ruletest_t2;
 DROP TABLE ruletest_t1;
 DROP USER regress_rule_user1;
index e9261da5e06890fab3b2d0b54d78e0eead5f9ec4..1f858129b84d1bace4f4b3cc4a649b11774d5921 100644 (file)
@@ -1293,11 +1293,26 @@ CREATE RULE rule1 AS ON INSERT TO ruletest_v1
 SET SESSION AUTHORIZATION regress_rule_user1;
 INSERT INTO ruletest_v1 VALUES (1);
 
+RESET SESSION AUTHORIZATION;
+
+-- Test that main query's relation's permissions are checked before
+-- the rule action's relation's.
+CREATE TABLE ruletest_t3 (x int);
+CREATE RULE rule2 AS ON UPDATE TO ruletest_t1
+    DO INSTEAD INSERT INTO ruletest_t2 VALUES (OLD.*);
+REVOKE ALL ON ruletest_t2 FROM regress_rule_user1;
+REVOKE ALL ON ruletest_t3 FROM regress_rule_user1;
+ALTER TABLE ruletest_t1 OWNER TO regress_rule_user1;
+SET SESSION AUTHORIZATION regress_rule_user1;
+UPDATE ruletest_t1 t1 SET x = 0 FROM ruletest_t3 t3 WHERE t1.x = t3.x;
+
 RESET SESSION AUTHORIZATION;
 SELECT * FROM ruletest_t1;
 SELECT * FROM ruletest_t2;
 
 DROP VIEW ruletest_v1;
+DROP RULE rule2 ON ruletest_t1;
+DROP TABLE ruletest_t3;
 DROP TABLE ruletest_t2;
 DROP TABLE ruletest_t1;
 
index 58daeca8317423ef9d2d86c962359cbe2a87c5fa..a2dfd5c9da811ca97af63d8a86c50bbbf5dea684 100644 (file)
@@ -2187,6 +2187,7 @@ RI_ConstraintInfo
 RI_QueryHashEntry
 RI_QueryKey
 RTEKind
+RTEPermissionInfo
 RWConflict
 RWConflictPoolHeader
 Range
@@ -3264,6 +3265,7 @@ fix_scan_expr_context
 fix_upper_expr_context
 fix_windowagg_cond_context
 flatten_join_alias_vars_context
+flatten_rtes_walker_context
 float4
 float4KEY
 float8