Make UPDATE privileges distinct from INSERT privileges in RTEs
authorPeter Geoghegan <pg@heroku.com>
Wed, 27 Aug 2014 04:28:40 +0000 (21:28 -0700)
committerPeter Geoghegan <peter.geoghegan86@gmail.com>
Sun, 26 Apr 2015 01:41:56 +0000 (18:41 -0700)
Previously, relation range table entries used a single Bitmapset field
representing which columns required either UPDATE or INSERT privileges,
despite the fact that INSERT and UPDATE privileges are separately
cataloged, and may be independently held.  This worked because
ExecCheckRTEPerms() was called with a ACL_INSERT or ACL_UPDATE
requiredPerms, and based on that it was evident which type of
optimizable statement was under consideration.  Since historically no
type of optimizable statement could directly INSERT and UPDATE at the
same time, there was no ambiguity as to which privileges were required.

This largely mechanical commit is required infrastructure for the
INSERT...ON CONFLICT UPDATE feature, which introduces an optimizable
statement that may be subject to both INSERT and UPDATE permissions
enforcement.  Tests follow in a later commit.

sepgsql is also affected by this commit.  Note that this commit
necessitates an initdb, since stored ACLs are broken.

Peter Geoghegan, reviewed by Andres Freund.

17 files changed:
contrib/postgres_fdw/postgres_fdw.c
contrib/sepgsql/dml.c
src/backend/commands/copy.c
src/backend/commands/createas.c
src/backend/commands/trigger.c
src/backend/executor/execMain.c
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/nodes/outfuncs.c
src/backend/nodes/readfuncs.c
src/backend/optimizer/plan/setrefs.c
src/backend/optimizer/prep/prepsecurity.c
src/backend/optimizer/prep/prepunion.c
src/backend/parser/analyze.c
src/backend/parser/parse_relation.c
src/backend/rewrite/rewriteHandler.c
src/include/nodes/parsenodes.h

index b4a58d5cfcdbaac89b5a157e07a3cc238c688b65..d9466522183421d149c2a19fa9bfd7a0670459a1 100644 (file)
@@ -1206,7 +1206,7 @@ postgresPlanForeignModify(PlannerInfo *root,
        int         col;
 
        col = -1;
-       while ((col = bms_next_member(rte->modifiedCols, col)) >= 0)
+       while ((col = bms_next_member(rte->updatedCols, col)) >= 0)
        {
            /* bit numbers are offset by FirstLowInvalidHeapAttributeNumber */
            AttrNumber  attno = col + FirstLowInvalidHeapAttributeNumber;
index 36c6a37ac13fcd3fd60c408d1f31310dcff7c890..4a71753d3fb55a62bdd300a2e16aa4f3e72ee2f1 100644 (file)
@@ -145,7 +145,8 @@ fixup_inherited_columns(Oid parentId, Oid childId, Bitmapset *columns)
 static bool
 check_relation_privileges(Oid relOid,
                          Bitmapset *selected,
-                         Bitmapset *modified,
+                         Bitmapset *inserted,
+                         Bitmapset *updated,
                          uint32 required,
                          bool abort_on_violation)
 {
@@ -231,8 +232,9 @@ check_relation_privileges(Oid relOid,
     * Check permissions on the columns
     */
    selected = fixup_whole_row_references(relOid, selected);
-   modified = fixup_whole_row_references(relOid, modified);
-   columns = bms_union(selected, modified);
+   inserted = fixup_whole_row_references(relOid, inserted);
+   updated = fixup_whole_row_references(relOid, updated);
+   columns = bms_union(selected, bms_union(inserted, updated));
 
    while ((index = bms_first_member(columns)) >= 0)
    {
@@ -241,13 +243,16 @@ check_relation_privileges(Oid relOid,
 
        if (bms_is_member(index, selected))
            column_perms |= SEPG_DB_COLUMN__SELECT;
-       if (bms_is_member(index, modified))
+       if (bms_is_member(index, inserted))
        {
-           if (required & SEPG_DB_TABLE__UPDATE)
-               column_perms |= SEPG_DB_COLUMN__UPDATE;
            if (required & SEPG_DB_TABLE__INSERT)
                column_perms |= SEPG_DB_COLUMN__INSERT;
        }
+       if (bms_is_member(index, updated))
+       {
+           if (required & SEPG_DB_TABLE__UPDATE)
+               column_perms |= SEPG_DB_COLUMN__UPDATE;
+       }
        if (column_perms == 0)
            continue;
 
@@ -304,7 +309,7 @@ sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation)
            required |= SEPG_DB_TABLE__INSERT;
        if (rte->requiredPerms & ACL_UPDATE)
        {
-           if (!bms_is_empty(rte->modifiedCols))
+           if (!bms_is_empty(rte->updatedCols))
                required |= SEPG_DB_TABLE__UPDATE;
            else
                required |= SEPG_DB_TABLE__LOCK;
@@ -333,7 +338,8 @@ sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation)
        {
            Oid         tableOid = lfirst_oid(li);
            Bitmapset  *selectedCols;
-           Bitmapset  *modifiedCols;
+           Bitmapset  *insertedCols;
+           Bitmapset  *updatedCols;
 
            /*
             * child table has different attribute numbers, so we need to fix
@@ -341,15 +347,18 @@ sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation)
             */
            selectedCols = fixup_inherited_columns(rte->relid, tableOid,
                                                   rte->selectedCols);
-           modifiedCols = fixup_inherited_columns(rte->relid, tableOid,
-                                                  rte->modifiedCols);
+           insertedCols = fixup_inherited_columns(rte->relid, tableOid,
+                                                  rte->insertedCols);
+           updatedCols = fixup_inherited_columns(rte->relid, tableOid,
+                                                 rte->updatedCols);
 
            /*
             * check permissions on individual tables
             */
            if (!check_relation_privileges(tableOid,
                                           selectedCols,
-                                          modifiedCols,
+                                          insertedCols,
+                                          updatedCols,
                                           required, abort_on_violation))
                return false;
        }
index 669bca406e5a322ed6f2e92a13fd003a2f1d3754..ec25e13b44c7309b2c04c34898e1bbedc1252993 100644 (file)
@@ -847,7 +847,7 @@ DoCopy(const CopyStmt *stmt, const char *queryString, uint64 *processed)
            FirstLowInvalidHeapAttributeNumber;
 
            if (is_from)
-               rte->modifiedCols = bms_add_member(rte->modifiedCols, attno);
+               rte->insertedCols = bms_add_member(rte->insertedCols, attno);
            else
                rte->selectedCols = bms_add_member(rte->selectedCols, attno);
        }
index 54b2f382ea0e03f5c872711643ee4cbb8e1ccca9..e8f0d793b678ff620efe8a1efe403cb92b4f1ab8 100644 (file)
@@ -433,7 +433,7 @@ intorel_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
    rte->requiredPerms = ACL_INSERT;
 
    for (attnum = 1; attnum <= intoRelationDesc->rd_att->natts; attnum++)
-       rte->modifiedCols = bms_add_member(rte->modifiedCols,
+       rte->insertedCols = bms_add_member(rte->insertedCols,
                                attnum - FirstLowInvalidHeapAttributeNumber);
 
    ExecCheckRTPerms(list_make1(rte), true);
index 098893f4944f487d61dbefa8e358126ab8249f6f..222e7fce854491b1d504c5042a37c732abb3f87c 100644 (file)
@@ -66,13 +66,13 @@ int         SessionReplicationRole = SESSION_REPLICATION_ROLE_ORIGIN;
 static int MyTriggerDepth = 0;
 
 /*
- * Note that this macro also exists in executor/execMain.c.  There does not
- * appear to be any good header to put it into, given the structures that
- * it uses, so we let them be duplicated.  Be sure to update both if one needs
- * to be changed, however.
+ * Note that similar macros also exists in executor/execMain.c.  There does not
+ * appear to be any good header to put it into, given the structures that it
+ * uses, so we let them be duplicated.  Be sure to update both if one needs to
+ * be changed, however.
  */
-#define GetModifiedColumns(relinfo, estate) \
-   (rt_fetch((relinfo)->ri_RangeTableIndex, (estate)->es_range_table)->modifiedCols)
+#define GetUpdatedColumns(relinfo, estate) \
+   (rt_fetch((relinfo)->ri_RangeTableIndex, (estate)->es_range_table)->updatedCols)
 
 /* Local function prototypes */
 static void ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid);
@@ -2347,7 +2347,7 @@ ExecBSUpdateTriggers(EState *estate, ResultRelInfo *relinfo)
    TriggerDesc *trigdesc;
    int         i;
    TriggerData LocTriggerData;
-   Bitmapset  *modifiedCols;
+   Bitmapset  *updatedCols;
 
    trigdesc = relinfo->ri_TrigDesc;
 
@@ -2356,7 +2356,7 @@ ExecBSUpdateTriggers(EState *estate, ResultRelInfo *relinfo)
    if (!trigdesc->trig_update_before_statement)
        return;
 
-   modifiedCols = GetModifiedColumns(relinfo, estate);
+   updatedCols = GetUpdatedColumns(relinfo, estate);
 
    LocTriggerData.type = T_TriggerData;
    LocTriggerData.tg_event = TRIGGER_EVENT_UPDATE |
@@ -2377,7 +2377,7 @@ ExecBSUpdateTriggers(EState *estate, ResultRelInfo *relinfo)
                                  TRIGGER_TYPE_UPDATE))
            continue;
        if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
-                           modifiedCols, NULL, NULL))
+                           updatedCols, NULL, NULL))
            continue;
 
        LocTriggerData.tg_trigger = trigger;
@@ -2402,7 +2402,7 @@ ExecASUpdateTriggers(EState *estate, ResultRelInfo *relinfo)
    if (trigdesc && trigdesc->trig_update_after_statement)
        AfterTriggerSaveEvent(estate, relinfo, TRIGGER_EVENT_UPDATE,
                              false, NULL, NULL, NIL,
-                             GetModifiedColumns(relinfo, estate));
+                             GetUpdatedColumns(relinfo, estate));
 }
 
 TupleTableSlot *
@@ -2420,7 +2420,7 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
    HeapTuple   oldtuple;
    TupleTableSlot *newSlot;
    int         i;
-   Bitmapset  *modifiedCols;
+   Bitmapset  *updatedCols;
    Bitmapset  *keyCols;
    LockTupleMode lockmode;
 
@@ -2429,10 +2429,10 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
     * been modified, then we can use a weaker lock, allowing for better
     * concurrency.
     */
-   modifiedCols = GetModifiedColumns(relinfo, estate);
+   updatedCols = GetUpdatedColumns(relinfo, estate);
    keyCols = RelationGetIndexAttrBitmap(relinfo->ri_RelationDesc,
                                         INDEX_ATTR_BITMAP_KEY);
-   if (bms_overlap(keyCols, modifiedCols))
+   if (bms_overlap(keyCols, updatedCols))
        lockmode = LockTupleExclusive;
    else
        lockmode = LockTupleNoKeyExclusive;
@@ -2486,7 +2486,7 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
                                  TRIGGER_TYPE_UPDATE))
            continue;
        if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
-                           modifiedCols, trigtuple, newtuple))
+                           updatedCols, trigtuple, newtuple))
            continue;
 
        LocTriggerData.tg_trigtuple = trigtuple;
@@ -2556,7 +2556,7 @@ ExecARUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
 
        AfterTriggerSaveEvent(estate, relinfo, TRIGGER_EVENT_UPDATE,
                              true, trigtuple, newtuple, recheckIndexes,
-                             GetModifiedColumns(relinfo, estate));
+                             GetUpdatedColumns(relinfo, estate));
        if (trigtuple != fdw_trigtuple)
            heap_freetuple(trigtuple);
    }
index 6f592585bb8b7be21658b960b0876bcfc404e212..80719017eacd9140f79656cb428ce68aa953dff9 100644 (file)
@@ -82,6 +82,9 @@ static void ExecutePlan(EState *estate, PlanState *planstate,
            ScanDirection direction,
            DestReceiver *dest);
 static bool ExecCheckRTEPerms(RangeTblEntry *rte);
+static bool ExecCheckRTEPermsModified(Oid relOid, Oid userid,
+                         Bitmapset *modifiedCols,
+                         AclMode requiredPerms);
 static void ExecCheckXactReadOnly(PlannedStmt *plannedstmt);
 static char *ExecBuildSlotValueDescription(Oid reloid,
                              TupleTableSlot *slot,
@@ -92,13 +95,15 @@ static void EvalPlanQualStart(EPQState *epqstate, EState *parentestate,
                  Plan *planTree);
 
 /*
- * Note that this macro also exists in commands/trigger.c.  There does not
- * appear to be any good header to put it into, given the structures that
+ * Note that GetUpdatedColumns() also exists in commands/trigger.c.  There does
+ * not appear to be any good header to put it into, given the structures that
  * it uses, so we let them be duplicated.  Be sure to update both if one needs
  * to be changed, however.
  */
-#define GetModifiedColumns(relinfo, estate) \
-   (rt_fetch((relinfo)->ri_RangeTableIndex, (estate)->es_range_table)->modifiedCols)
+#define GetInsertedColumns(relinfo, estate) \
+   (rt_fetch((relinfo)->ri_RangeTableIndex, (estate)->es_range_table)->insertedCols)
+#define GetUpdatedColumns(relinfo, estate) \
+   (rt_fetch((relinfo)->ri_RangeTableIndex, (estate)->es_range_table)->updatedCols)
 
 /* end of local decls */
 
@@ -559,7 +564,6 @@ ExecCheckRTEPerms(RangeTblEntry *rte)
    AclMode     remainingPerms;
    Oid         relOid;
    Oid         userid;
-   int         col;
 
    /*
     * Only plain-relation RTEs need to be checked here.  Function RTEs are
@@ -597,6 +601,8 @@ ExecCheckRTEPerms(RangeTblEntry *rte)
    remainingPerms = requiredPerms & ~relPerms;
    if (remainingPerms != 0)
    {
+       int         col = -1;
+
        /*
         * If we lack any permissions that exist only as relation permissions,
         * we can fail straight away.
@@ -625,7 +631,6 @@ ExecCheckRTEPerms(RangeTblEntry *rte)
                    return false;
            }
 
-           col = -1;
            while ((col = bms_next_member(rte->selectedCols, col)) >= 0)
            {
                /* bit #s are offset by FirstLowInvalidHeapAttributeNumber */
@@ -648,43 +653,63 @@ ExecCheckRTEPerms(RangeTblEntry *rte)
        }
 
        /*
-        * Basically the same for the mod columns, with either INSERT or
-        * UPDATE privilege as specified by remainingPerms.
+        * Basically the same for the mod columns, for both INSERT and UPDATE
+        * privilege as specified by remainingPerms.
         */
-       remainingPerms &= ~ACL_SELECT;
-       if (remainingPerms != 0)
-       {
-           /*
-            * When the query doesn't explicitly change any columns, allow the
-            * query if we have permission on any column of the rel.  This is
-            * to handle SELECT FOR UPDATE as well as possible corner cases in
-            * INSERT and UPDATE.
-            */
-           if (bms_is_empty(rte->modifiedCols))
-           {
-               if (pg_attribute_aclcheck_all(relOid, userid, remainingPerms,
-                                             ACLMASK_ANY) != ACLCHECK_OK)
-                   return false;
-           }
+       if (remainingPerms & ACL_INSERT && !ExecCheckRTEPermsModified(relOid,
+                                                                     userid,
+                                                                     rte->insertedCols,
+                                                                     ACL_INSERT))
+           return false;
 
-           col = -1;
-           while ((col = bms_next_member(rte->modifiedCols, col)) >= 0)
-           {
-               /* bit #s are offset by FirstLowInvalidHeapAttributeNumber */
-               AttrNumber  attno = col + FirstLowInvalidHeapAttributeNumber;
+       if (remainingPerms & ACL_UPDATE && !ExecCheckRTEPermsModified(relOid,
+                                                                     userid,
+                                                                     rte->updatedCols,
+                                                                     ACL_UPDATE))
+           return false;
+   }
+   return true;
+}
 
-               if (attno == InvalidAttrNumber)
-               {
-                   /* whole-row reference can't happen here */
-                   elog(ERROR, "whole-row update is not implemented");
-               }
-               else
-               {
-                   if (pg_attribute_aclcheck(relOid, attno, userid,
-                                             remainingPerms) != ACLCHECK_OK)
-                       return false;
-               }
-           }
+/*
+ * ExecCheckRTEPermsModified
+ *     Check INSERT or UPDATE access permissions for a single RTE (these
+ *     are processed uniformly).
+ */
+static bool
+ExecCheckRTEPermsModified(Oid relOid, Oid userid, Bitmapset *modifiedCols,
+                         AclMode requiredPerms)
+{
+   int         col = -1;
+
+   /*
+    * When the query doesn't explicitly update any columns, allow the
+    * query if we have permission on any column of the rel.  This is
+    * to handle SELECT FOR UPDATE as well as possible corner cases in
+    * UPDATE.
+    */
+   if (bms_is_empty(modifiedCols))
+   {
+       if (pg_attribute_aclcheck_all(relOid, userid, requiredPerms,
+                                     ACLMASK_ANY) != ACLCHECK_OK)
+           return false;
+   }
+
+   while ((col = bms_next_member(modifiedCols, col)) >= 0)
+   {
+       /* bit #s are offset by FirstLowInvalidHeapAttributeNumber */
+       AttrNumber  attno = col + FirstLowInvalidHeapAttributeNumber;
+
+       if (attno == InvalidAttrNumber)
+       {
+           /* whole-row reference can't happen here */
+           elog(ERROR, "whole-row update is not implemented");
+       }
+       else
+       {
+           if (pg_attribute_aclcheck(relOid, attno, userid,
+                                     requiredPerms) != ACLCHECK_OK)
+               return false;
        }
    }
    return true;
@@ -1613,6 +1638,9 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
    Relation    rel = resultRelInfo->ri_RelationDesc;
    TupleDesc   tupdesc = RelationGetDescr(rel);
    TupleConstr *constr = tupdesc->constr;
+   Bitmapset   *modifiedCols;
+   Bitmapset   *insertedCols;
+   Bitmapset   *updatedCols;
 
    Assert(constr);
 
@@ -1627,9 +1655,10 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
                slot_attisnull(slot, attrChk))
            {
                char       *val_desc;
-               Bitmapset  *modifiedCols;
 
-               modifiedCols = GetModifiedColumns(resultRelInfo, estate);
+               insertedCols = GetInsertedColumns(resultRelInfo, estate);
+               updatedCols = GetUpdatedColumns(resultRelInfo, estate);
+               modifiedCols = bms_union(insertedCols, updatedCols);
                val_desc = ExecBuildSlotValueDescription(RelationGetRelid(rel),
                                                         slot,
                                                         tupdesc,
@@ -1653,9 +1682,10 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
        if ((failed = ExecRelCheck(resultRelInfo, slot, estate)) != NULL)
        {
            char       *val_desc;
-           Bitmapset  *modifiedCols;
 
-           modifiedCols = GetModifiedColumns(resultRelInfo, estate);
+           insertedCols = GetInsertedColumns(resultRelInfo, estate);
+           updatedCols = GetUpdatedColumns(resultRelInfo, estate);
+           modifiedCols = bms_union(insertedCols, updatedCols);
            val_desc = ExecBuildSlotValueDescription(RelationGetRelid(rel),
                                                     slot,
                                                     tupdesc,
@@ -1726,6 +1756,8 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo,
        {
            char       *val_desc;
            Bitmapset  *modifiedCols;
+           Bitmapset  *insertedCols;
+           Bitmapset  *updatedCols;
 
            switch (wco->kind)
            {
@@ -1738,7 +1770,9 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo,
                 * as that depends on the USING policy.
                 */
                case WCO_VIEW_CHECK:
-                   modifiedCols = GetModifiedColumns(resultRelInfo, estate);
+                   insertedCols = GetInsertedColumns(resultRelInfo, estate);
+                   updatedCols = GetUpdatedColumns(resultRelInfo, estate);
+                   modifiedCols = bms_union(insertedCols, updatedCols);
                    val_desc = ExecBuildSlotValueDescription(RelationGetRelid(rel),
                                                             slot,
                                                             tupdesc,
index 6ca0245aa381657596b21ebbe86dad080c1d7827..251e07eefee4f2a1fa35ce8572b84f62425082fd 100644 (file)
@@ -2055,7 +2055,8 @@ _copyRangeTblEntry(const RangeTblEntry *from)
    COPY_SCALAR_FIELD(requiredPerms);
    COPY_SCALAR_FIELD(checkAsUser);
    COPY_BITMAPSET_FIELD(selectedCols);
-   COPY_BITMAPSET_FIELD(modifiedCols);
+   COPY_BITMAPSET_FIELD(insertedCols);
+   COPY_BITMAPSET_FIELD(updatedCols);
    COPY_NODE_FIELD(securityQuals);
 
    return newnode;
index 8d31ecc5cff765e07769342226c5275bebe859e4..adaa9b115c56c1a720d39d1e57e73b5ea3ace78b 100644 (file)
@@ -2355,7 +2355,8 @@ _equalRangeTblEntry(const RangeTblEntry *a, const RangeTblEntry *b)
    COMPARE_SCALAR_FIELD(requiredPerms);
    COMPARE_SCALAR_FIELD(checkAsUser);
    COMPARE_BITMAPSET_FIELD(selectedCols);
-   COMPARE_BITMAPSET_FIELD(modifiedCols);
+   COMPARE_BITMAPSET_FIELD(insertedCols);
+   COMPARE_BITMAPSET_FIELD(updatedCols);
    COMPARE_NODE_FIELD(securityQuals);
 
    return true;
index c911ea0e54524ada055457911db7a25f1556c9a7..48c67b373a89c26c4980af14113211cb880e009b 100644 (file)
@@ -2486,7 +2486,8 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node)
    WRITE_UINT_FIELD(requiredPerms);
    WRITE_OID_FIELD(checkAsUser);
    WRITE_BITMAPSET_FIELD(selectedCols);
-   WRITE_BITMAPSET_FIELD(modifiedCols);
+   WRITE_BITMAPSET_FIELD(insertedCols);
+   WRITE_BITMAPSET_FIELD(updatedCols);
    WRITE_NODE_FIELD(securityQuals);
 }
 
index 5a70176be1b52bfb278cd2e28de00ae65c0da4d0..b3fabd020977fe2229ba9d21fa7b19d41a1a4e17 100644 (file)
@@ -1275,7 +1275,8 @@ _readRangeTblEntry(void)
    READ_UINT_FIELD(requiredPerms);
    READ_OID_FIELD(checkAsUser);
    READ_BITMAPSET_FIELD(selectedCols);
-   READ_BITMAPSET_FIELD(modifiedCols);
+   READ_BITMAPSET_FIELD(insertedCols);
+   READ_BITMAPSET_FIELD(updatedCols);
    READ_NODE_FIELD(securityQuals);
 
    READ_DONE();
index 94b12ab8ca1132867cd3a1b3f830eb52942c2d22..06fc7ba34d39cf9386fbe4839ddf88e7094e44ac 100644 (file)
@@ -367,9 +367,9 @@ flatten_rtes_walker(Node *node, PlannerGlobal *glob)
  *
  * In the flat rangetable, we zero out substructure pointers that are not
  * needed by the executor; this reduces the storage space and copying cost
- * for cached plans.  We keep only the alias and eref Alias fields, which
- * are needed by EXPLAIN, and the selectedCols and modifiedCols bitmaps,
- * which are needed for executor-startup permissions checking and for
+ * for cached plans.  We keep only the alias and eref Alias fields, which are
+ * needed by EXPLAIN, and the selectedCols, insertedCols and updatedCols
+ * bitmaps, which are needed for executor-startup permissions checking and for
  * trigger event checking.
  */
 static void
index 0be44c1c2f7ccca8f8d1e546964939d3591999c4..c4b61df3003128c491f8389fd88c8f63ba940e61 100644 (file)
@@ -125,7 +125,8 @@ expand_security_quals(PlannerInfo *root, List *tlist)
            rte->requiredPerms = 0;
            rte->checkAsUser = InvalidOid;
            rte->selectedCols = NULL;
-           rte->modifiedCols = NULL;
+           rte->insertedCols = NULL;
+           rte->updatedCols = NULL;
 
            /*
             * For the most part, Vars referencing the original relation
@@ -224,7 +225,8 @@ expand_security_qual(PlannerInfo *root, List *tlist, int rt_index,
            rte->requiredPerms = 0;
            rte->checkAsUser = InvalidOid;
            rte->selectedCols = NULL;
-           rte->modifiedCols = NULL;
+           rte->insertedCols = NULL;
+           rte->updatedCols = NULL;
 
            /*
             * Now deal with any PlanRowMark on this RTE by requesting a lock
index 51b3da21b30c9696bbe73bd0d26732c3d9ce9102..5859748df8eeda5812ad9674a3146cca13d08846 100644 (file)
@@ -1368,14 +1368,16 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
         * if this is the parent table, leave copyObject's result alone.
         *
         * Note: we need to do this even though the executor won't run any
-        * permissions checks on the child RTE.  The modifiedCols bitmap may
-        * be examined for trigger-firing purposes.
+        * permissions checks on the child RTE.  The insertedCols/updatedCols
+        * bitmaps may be examined for trigger-firing purposes.
         */
        if (childOID != parentOID)
        {
            childrte->selectedCols = translate_col_privs(rte->selectedCols,
                                                   appinfo->translated_vars);
-           childrte->modifiedCols = translate_col_privs(rte->modifiedCols,
+           childrte->insertedCols = translate_col_privs(rte->insertedCols,
+                                                  appinfo->translated_vars);
+           childrte->updatedCols = translate_col_privs(rte->updatedCols,
                                                   appinfo->translated_vars);
        }
 
index 642e4e0e2d1b9f03f5f3837ef6a22aa7fd45360c..f30b9c301ae333273208dcc499d38a2f5ef49ee5 100644 (file)
@@ -735,7 +735,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
                              false);
        qry->targetList = lappend(qry->targetList, tle);
 
-       rte->modifiedCols = bms_add_member(rte->modifiedCols,
+       rte->insertedCols = bms_add_member(rte->insertedCols,
                              attr_num - FirstLowInvalidHeapAttributeNumber);
 
        icols = lnext(icols);
@@ -2020,7 +2020,7 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
                              origTarget->location);
 
        /* Mark the target column as requiring update permissions */
-       target_rte->modifiedCols = bms_add_member(target_rte->modifiedCols,
+       target_rte->updatedCols = bms_add_member(target_rte->updatedCols,
                                attrno - FirstLowInvalidHeapAttributeNumber);
 
        origTargetList = lnext(origTargetList);
index ca560ccee1b5bd5272139ddb5766111c8e29c6c0..562c2f54f92385a82de7ea1336a0fd175d8e1fac 100644 (file)
@@ -1218,7 +1218,8 @@ addRangeTableEntry(ParseState *pstate,
    rte->requiredPerms = ACL_SELECT;
    rte->checkAsUser = InvalidOid;      /* not set-uid by default, either */
    rte->selectedCols = NULL;
-   rte->modifiedCols = NULL;
+   rte->insertedCols = NULL;
+   rte->updatedCols = NULL;
 
    /*
     * Add completed RTE to pstate's range table list, but not to join list
@@ -1272,7 +1273,8 @@ addRangeTableEntryForRelation(ParseState *pstate,
    rte->requiredPerms = ACL_SELECT;
    rte->checkAsUser = InvalidOid;      /* not set-uid by default, either */
    rte->selectedCols = NULL;
-   rte->modifiedCols = NULL;
+   rte->insertedCols = NULL;
+   rte->updatedCols = NULL;
 
    /*
     * Add completed RTE to pstate's range table list, but not to join list
@@ -1351,7 +1353,8 @@ addRangeTableEntryForSubquery(ParseState *pstate,
    rte->requiredPerms = 0;
    rte->checkAsUser = InvalidOid;
    rte->selectedCols = NULL;
-   rte->modifiedCols = NULL;
+   rte->insertedCols = NULL;
+   rte->updatedCols = NULL;
 
    /*
     * Add completed RTE to pstate's range table list, but not to join list
@@ -1606,7 +1609,8 @@ addRangeTableEntryForFunction(ParseState *pstate,
    rte->requiredPerms = 0;
    rte->checkAsUser = InvalidOid;
    rte->selectedCols = NULL;
-   rte->modifiedCols = NULL;
+   rte->insertedCols = NULL;
+   rte->updatedCols = NULL;
 
    /*
     * Add completed RTE to pstate's range table list, but not to join list
@@ -1679,7 +1683,8 @@ addRangeTableEntryForValues(ParseState *pstate,
    rte->requiredPerms = 0;
    rte->checkAsUser = InvalidOid;
    rte->selectedCols = NULL;
-   rte->modifiedCols = NULL;
+   rte->insertedCols = NULL;
+   rte->updatedCols = NULL;
 
    /*
     * Add completed RTE to pstate's range table list, but not to join list
@@ -1748,7 +1753,8 @@ addRangeTableEntryForJoin(ParseState *pstate,
    rte->requiredPerms = 0;
    rte->checkAsUser = InvalidOid;
    rte->selectedCols = NULL;
-   rte->modifiedCols = NULL;
+   rte->insertedCols = NULL;
+   rte->updatedCols = NULL;
 
    /*
     * Add completed RTE to pstate's range table list, but not to join list
@@ -1849,7 +1855,8 @@ addRangeTableEntryForCTE(ParseState *pstate,
    rte->requiredPerms = 0;
    rte->checkAsUser = InvalidOid;
    rte->selectedCols = NULL;
-   rte->modifiedCols = NULL;
+   rte->insertedCols = NULL;
+   rte->updatedCols = NULL;
 
    /*
     * Add completed RTE to pstate's range table list, but not to join list
index 88ba29b05db28a5773b7034b061abc0697ccfd2a..08709ba5757f56f43cd5bfeaf1c4b675d4868330 100644 (file)
@@ -1407,7 +1407,8 @@ ApplyRetrieveRule(Query *parsetree,
            rte->requiredPerms = 0;
            rte->checkAsUser = InvalidOid;
            rte->selectedCols = NULL;
-           rte->modifiedCols = NULL;
+           rte->insertedCols = NULL;
+           rte->updatedCols = NULL;
 
            /*
             * For the most part, Vars referencing the view should remain as
@@ -1470,12 +1471,14 @@ ApplyRetrieveRule(Query *parsetree,
    subrte->requiredPerms = rte->requiredPerms;
    subrte->checkAsUser = rte->checkAsUser;
    subrte->selectedCols = rte->selectedCols;
-   subrte->modifiedCols = rte->modifiedCols;
+   subrte->insertedCols = rte->insertedCols;
+   subrte->updatedCols = rte->updatedCols;
 
    rte->requiredPerms = 0;     /* no permission check on subquery itself */
    rte->checkAsUser = InvalidOid;
    rte->selectedCols = NULL;
-   rte->modifiedCols = NULL;
+   rte->insertedCols = NULL;
+   rte->updatedCols = NULL;
 
    /*
     * If FOR [KEY] UPDATE/SHARE of view, mark all the contained tables as
@@ -2625,9 +2628,9 @@ rewriteTargetView(Query *parsetree, Relation view)
    /*
     * For INSERT/UPDATE the modified columns must all be updatable. Note that
     * we get the modified columns from the query's targetlist, not from the
-    * result RTE's modifiedCols set, since rewriteTargetListIU may have added
-    * additional targetlist entries for view defaults, and these must also be
-    * updatable.
+    * result RTE's insertedCols and/or updatedCols set, since
+    * rewriteTargetListIU may have added additional targetlist entries for
+    * view defaults, and these must also be updatable.
     */
    if (parsetree->commandType != CMD_DELETE)
    {
@@ -2764,28 +2767,33 @@ rewriteTargetView(Query *parsetree, Relation view)
     *
     * 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 modifiedCols is empty.  We set modifiedCols 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.)
+    * 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.)
     *
     * This step needs the modified view targetlist, so we have to do things
     * in this order.
     */
-   Assert(bms_is_empty(new_rte->modifiedCols));
-   new_rte->modifiedCols = adjust_view_column_set(view_rte->modifiedCols,
+   Assert(bms_is_empty(new_rte->insertedCols) &&
+          bms_is_empty(new_rte->updatedCols));
+
+   new_rte->insertedCols = adjust_view_column_set(view_rte->insertedCols,
                                                   view_targetlist);
 
+   new_rte->updatedCols = adjust_view_column_set(view_rte->updatedCols,
+                                                 view_targetlist);
+
    /*
     * Move any security barrier quals from the view RTE onto the new target
     * RTE.  Any such quals should now apply to the new target RTE and will
index c4cb157e857a868699521926e27f3b7048a85d6d..e0c8c42f0fde42fead16e25d17db14d0d2c65678 100644 (file)
@@ -739,11 +739,12 @@ typedef struct XmlSerialize
  *   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
- *   modifiedCols (for INSERT/UPDATE; we can tell which from the query type).
- *   selectedCols and modifiedCols 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.
+ *   insertedCols and/or updatedCols (INSERT with ON CONFLICT 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.
  *--------------------
  */
 typedef enum RTEKind
@@ -838,7 +839,8 @@ typedef struct RangeTblEntry
    AclMode     requiredPerms;  /* bitmask of required access permissions */
    Oid         checkAsUser;    /* if valid, check access as this role */
    Bitmapset  *selectedCols;   /* columns needing SELECT permission */
-   Bitmapset  *modifiedCols;   /* columns needing INSERT/UPDATE permission */
+   Bitmapset  *insertedCols;   /* columns needing INSERT permission */
+   Bitmapset  *updatedCols;    /* columns needing UPDATE permission */
    List       *securityQuals;  /* any security barrier quals to apply */
 } RangeTblEntry;