Commit 2c8f4836 authored by Andres Freund's avatar Andres Freund

Represent columns requiring insert and update privileges indentently.

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.  As statements so far required
either insert or update privileges but never both, that was
sufficient. The required permission could be inferred from the top level
statement run.

The upcoming INSERT ... ON CONFLICT UPDATE feature needs to
independently check for both privileges in one statement though, so that
is not sufficient anymore.

Bumps catversion as stored rules change.

Author: Peter Geoghegan
Reviewed-By: Andres Freund
parent db5f98ab
...@@ -1205,7 +1205,7 @@ postgresPlanForeignModify(PlannerInfo *root, ...@@ -1205,7 +1205,7 @@ postgresPlanForeignModify(PlannerInfo *root,
int col; int col;
col = -1; 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 */ /* bit numbers are offset by FirstLowInvalidHeapAttributeNumber */
AttrNumber attno = col + FirstLowInvalidHeapAttributeNumber; AttrNumber attno = col + FirstLowInvalidHeapAttributeNumber;
......
...@@ -145,7 +145,8 @@ fixup_inherited_columns(Oid parentId, Oid childId, Bitmapset *columns) ...@@ -145,7 +145,8 @@ fixup_inherited_columns(Oid parentId, Oid childId, Bitmapset *columns)
static bool static bool
check_relation_privileges(Oid relOid, check_relation_privileges(Oid relOid,
Bitmapset *selected, Bitmapset *selected,
Bitmapset *modified, Bitmapset *inserted,
Bitmapset *updated,
uint32 required, uint32 required,
bool abort_on_violation) bool abort_on_violation)
{ {
...@@ -231,8 +232,9 @@ check_relation_privileges(Oid relOid, ...@@ -231,8 +232,9 @@ check_relation_privileges(Oid relOid,
* Check permissions on the columns * Check permissions on the columns
*/ */
selected = fixup_whole_row_references(relOid, selected); selected = fixup_whole_row_references(relOid, selected);
modified = fixup_whole_row_references(relOid, modified); inserted = fixup_whole_row_references(relOid, inserted);
columns = bms_union(selected, modified); updated = fixup_whole_row_references(relOid, updated);
columns = bms_union(selected, bms_union(inserted, updated));
while ((index = bms_first_member(columns)) >= 0) while ((index = bms_first_member(columns)) >= 0)
{ {
...@@ -241,13 +243,16 @@ check_relation_privileges(Oid relOid, ...@@ -241,13 +243,16 @@ check_relation_privileges(Oid relOid,
if (bms_is_member(index, selected)) if (bms_is_member(index, selected))
column_perms |= SEPG_DB_COLUMN__SELECT; 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) if (required & SEPG_DB_TABLE__INSERT)
column_perms |= SEPG_DB_COLUMN__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) if (column_perms == 0)
continue; continue;
...@@ -304,7 +309,7 @@ sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) ...@@ -304,7 +309,7 @@ sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation)
required |= SEPG_DB_TABLE__INSERT; required |= SEPG_DB_TABLE__INSERT;
if (rte->requiredPerms & ACL_UPDATE) if (rte->requiredPerms & ACL_UPDATE)
{ {
if (!bms_is_empty(rte->modifiedCols)) if (!bms_is_empty(rte->updatedCols))
required |= SEPG_DB_TABLE__UPDATE; required |= SEPG_DB_TABLE__UPDATE;
else else
required |= SEPG_DB_TABLE__LOCK; required |= SEPG_DB_TABLE__LOCK;
...@@ -333,7 +338,8 @@ sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) ...@@ -333,7 +338,8 @@ sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation)
{ {
Oid tableOid = lfirst_oid(li); Oid tableOid = lfirst_oid(li);
Bitmapset *selectedCols; Bitmapset *selectedCols;
Bitmapset *modifiedCols; Bitmapset *insertedCols;
Bitmapset *updatedCols;
/* /*
* child table has different attribute numbers, so we need to fix * child table has different attribute numbers, so we need to fix
...@@ -341,15 +347,18 @@ sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) ...@@ -341,15 +347,18 @@ sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation)
*/ */
selectedCols = fixup_inherited_columns(rte->relid, tableOid, selectedCols = fixup_inherited_columns(rte->relid, tableOid,
rte->selectedCols); rte->selectedCols);
modifiedCols = fixup_inherited_columns(rte->relid, tableOid, insertedCols = fixup_inherited_columns(rte->relid, tableOid,
rte->modifiedCols); rte->insertedCols);
updatedCols = fixup_inherited_columns(rte->relid, tableOid,
rte->updatedCols);
/* /*
* check permissions on individual tables * check permissions on individual tables
*/ */
if (!check_relation_privileges(tableOid, if (!check_relation_privileges(tableOid,
selectedCols, selectedCols,
modifiedCols, insertedCols,
updatedCols,
required, abort_on_violation)) required, abort_on_violation))
return false; return false;
} }
......
...@@ -847,7 +847,7 @@ DoCopy(const CopyStmt *stmt, const char *queryString, uint64 *processed) ...@@ -847,7 +847,7 @@ DoCopy(const CopyStmt *stmt, const char *queryString, uint64 *processed)
FirstLowInvalidHeapAttributeNumber; FirstLowInvalidHeapAttributeNumber;
if (is_from) if (is_from)
rte->modifiedCols = bms_add_member(rte->modifiedCols, attno); rte->insertedCols = bms_add_member(rte->insertedCols, attno);
else else
rte->selectedCols = bms_add_member(rte->selectedCols, attno); rte->selectedCols = bms_add_member(rte->selectedCols, attno);
} }
......
...@@ -433,7 +433,7 @@ intorel_startup(DestReceiver *self, int operation, TupleDesc typeinfo) ...@@ -433,7 +433,7 @@ intorel_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
rte->requiredPerms = ACL_INSERT; rte->requiredPerms = ACL_INSERT;
for (attnum = 1; attnum <= intoRelationDesc->rd_att->natts; attnum++) 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); attnum - FirstLowInvalidHeapAttributeNumber);
ExecCheckRTPerms(list_make1(rte), true); ExecCheckRTPerms(list_make1(rte), true);
......
...@@ -66,13 +66,13 @@ int SessionReplicationRole = SESSION_REPLICATION_ROLE_ORIGIN; ...@@ -66,13 +66,13 @@ int SessionReplicationRole = SESSION_REPLICATION_ROLE_ORIGIN;
static int MyTriggerDepth = 0; static int MyTriggerDepth = 0;
/* /*
* Note that this macro also exists in executor/execMain.c. There does not * 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 * appear to be any good header to put it into, given the structures that it
* it uses, so we let them be duplicated. Be sure to update both if one needs * uses, so we let them be duplicated. Be sure to update both if one needs to
* to be changed, however. * be changed, however.
*/ */
#define GetModifiedColumns(relinfo, estate) \ #define GetUpdatedColumns(relinfo, estate) \
(rt_fetch((relinfo)->ri_RangeTableIndex, (estate)->es_range_table)->modifiedCols) (rt_fetch((relinfo)->ri_RangeTableIndex, (estate)->es_range_table)->updatedCols)
/* Local function prototypes */ /* Local function prototypes */
static void ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid); static void ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid);
...@@ -2347,7 +2347,7 @@ ExecBSUpdateTriggers(EState *estate, ResultRelInfo *relinfo) ...@@ -2347,7 +2347,7 @@ ExecBSUpdateTriggers(EState *estate, ResultRelInfo *relinfo)
TriggerDesc *trigdesc; TriggerDesc *trigdesc;
int i; int i;
TriggerData LocTriggerData; TriggerData LocTriggerData;
Bitmapset *modifiedCols; Bitmapset *updatedCols;
trigdesc = relinfo->ri_TrigDesc; trigdesc = relinfo->ri_TrigDesc;
...@@ -2356,7 +2356,7 @@ ExecBSUpdateTriggers(EState *estate, ResultRelInfo *relinfo) ...@@ -2356,7 +2356,7 @@ ExecBSUpdateTriggers(EState *estate, ResultRelInfo *relinfo)
if (!trigdesc->trig_update_before_statement) if (!trigdesc->trig_update_before_statement)
return; return;
modifiedCols = GetModifiedColumns(relinfo, estate); updatedCols = GetUpdatedColumns(relinfo, estate);
LocTriggerData.type = T_TriggerData; LocTriggerData.type = T_TriggerData;
LocTriggerData.tg_event = TRIGGER_EVENT_UPDATE | LocTriggerData.tg_event = TRIGGER_EVENT_UPDATE |
...@@ -2377,7 +2377,7 @@ ExecBSUpdateTriggers(EState *estate, ResultRelInfo *relinfo) ...@@ -2377,7 +2377,7 @@ ExecBSUpdateTriggers(EState *estate, ResultRelInfo *relinfo)
TRIGGER_TYPE_UPDATE)) TRIGGER_TYPE_UPDATE))
continue; continue;
if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event, if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
modifiedCols, NULL, NULL)) updatedCols, NULL, NULL))
continue; continue;
LocTriggerData.tg_trigger = trigger; LocTriggerData.tg_trigger = trigger;
...@@ -2402,7 +2402,7 @@ ExecASUpdateTriggers(EState *estate, ResultRelInfo *relinfo) ...@@ -2402,7 +2402,7 @@ ExecASUpdateTriggers(EState *estate, ResultRelInfo *relinfo)
if (trigdesc && trigdesc->trig_update_after_statement) if (trigdesc && trigdesc->trig_update_after_statement)
AfterTriggerSaveEvent(estate, relinfo, TRIGGER_EVENT_UPDATE, AfterTriggerSaveEvent(estate, relinfo, TRIGGER_EVENT_UPDATE,
false, NULL, NULL, NIL, false, NULL, NULL, NIL,
GetModifiedColumns(relinfo, estate)); GetUpdatedColumns(relinfo, estate));
} }
TupleTableSlot * TupleTableSlot *
...@@ -2420,7 +2420,7 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate, ...@@ -2420,7 +2420,7 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
HeapTuple oldtuple; HeapTuple oldtuple;
TupleTableSlot *newSlot; TupleTableSlot *newSlot;
int i; int i;
Bitmapset *modifiedCols; Bitmapset *updatedCols;
Bitmapset *keyCols; Bitmapset *keyCols;
LockTupleMode lockmode; LockTupleMode lockmode;
...@@ -2429,10 +2429,10 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate, ...@@ -2429,10 +2429,10 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
* been modified, then we can use a weaker lock, allowing for better * been modified, then we can use a weaker lock, allowing for better
* concurrency. * concurrency.
*/ */
modifiedCols = GetModifiedColumns(relinfo, estate); updatedCols = GetUpdatedColumns(relinfo, estate);
keyCols = RelationGetIndexAttrBitmap(relinfo->ri_RelationDesc, keyCols = RelationGetIndexAttrBitmap(relinfo->ri_RelationDesc,
INDEX_ATTR_BITMAP_KEY); INDEX_ATTR_BITMAP_KEY);
if (bms_overlap(keyCols, modifiedCols)) if (bms_overlap(keyCols, updatedCols))
lockmode = LockTupleExclusive; lockmode = LockTupleExclusive;
else else
lockmode = LockTupleNoKeyExclusive; lockmode = LockTupleNoKeyExclusive;
...@@ -2486,7 +2486,7 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate, ...@@ -2486,7 +2486,7 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
TRIGGER_TYPE_UPDATE)) TRIGGER_TYPE_UPDATE))
continue; continue;
if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event, if (!TriggerEnabled(estate, relinfo, trigger, LocTriggerData.tg_event,
modifiedCols, trigtuple, newtuple)) updatedCols, trigtuple, newtuple))
continue; continue;
LocTriggerData.tg_trigtuple = trigtuple; LocTriggerData.tg_trigtuple = trigtuple;
...@@ -2556,7 +2556,7 @@ ExecARUpdateTriggers(EState *estate, ResultRelInfo *relinfo, ...@@ -2556,7 +2556,7 @@ ExecARUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
AfterTriggerSaveEvent(estate, relinfo, TRIGGER_EVENT_UPDATE, AfterTriggerSaveEvent(estate, relinfo, TRIGGER_EVENT_UPDATE,
true, trigtuple, newtuple, recheckIndexes, true, trigtuple, newtuple, recheckIndexes,
GetModifiedColumns(relinfo, estate)); GetUpdatedColumns(relinfo, estate));
if (trigtuple != fdw_trigtuple) if (trigtuple != fdw_trigtuple)
heap_freetuple(trigtuple); heap_freetuple(trigtuple);
} }
......
...@@ -82,6 +82,9 @@ static void ExecutePlan(EState *estate, PlanState *planstate, ...@@ -82,6 +82,9 @@ static void ExecutePlan(EState *estate, PlanState *planstate,
ScanDirection direction, ScanDirection direction,
DestReceiver *dest); DestReceiver *dest);
static bool ExecCheckRTEPerms(RangeTblEntry *rte); static bool ExecCheckRTEPerms(RangeTblEntry *rte);
static bool ExecCheckRTEPermsModified(Oid relOid, Oid userid,
Bitmapset *modifiedCols,
AclMode requiredPerms);
static void ExecCheckXactReadOnly(PlannedStmt *plannedstmt); static void ExecCheckXactReadOnly(PlannedStmt *plannedstmt);
static char *ExecBuildSlotValueDescription(Oid reloid, static char *ExecBuildSlotValueDescription(Oid reloid,
TupleTableSlot *slot, TupleTableSlot *slot,
...@@ -92,13 +95,15 @@ static void EvalPlanQualStart(EPQState *epqstate, EState *parentestate, ...@@ -92,13 +95,15 @@ static void EvalPlanQualStart(EPQState *epqstate, EState *parentestate,
Plan *planTree); Plan *planTree);
/* /*
* Note that this macro also exists in commands/trigger.c. There does not * Note that GetUpdatedColumns() also exists in commands/trigger.c. There does
* appear to be any good header to put it into, given the structures that * 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 * it uses, so we let them be duplicated. Be sure to update both if one needs
* to be changed, however. * to be changed, however.
*/ */
#define GetModifiedColumns(relinfo, estate) \ #define GetInsertedColumns(relinfo, estate) \
(rt_fetch((relinfo)->ri_RangeTableIndex, (estate)->es_range_table)->modifiedCols) (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 */ /* end of local decls */
...@@ -571,7 +576,6 @@ ExecCheckRTEPerms(RangeTblEntry *rte) ...@@ -571,7 +576,6 @@ ExecCheckRTEPerms(RangeTblEntry *rte)
AclMode remainingPerms; AclMode remainingPerms;
Oid relOid; Oid relOid;
Oid userid; Oid userid;
int col;
/* /*
* Only plain-relation RTEs need to be checked here. Function RTEs are * Only plain-relation RTEs need to be checked here. Function RTEs are
...@@ -609,6 +613,8 @@ ExecCheckRTEPerms(RangeTblEntry *rte) ...@@ -609,6 +613,8 @@ ExecCheckRTEPerms(RangeTblEntry *rte)
remainingPerms = requiredPerms & ~relPerms; remainingPerms = requiredPerms & ~relPerms;
if (remainingPerms != 0) if (remainingPerms != 0)
{ {
int col = -1;
/* /*
* If we lack any permissions that exist only as relation permissions, * If we lack any permissions that exist only as relation permissions,
* we can fail straight away. * we can fail straight away.
...@@ -637,7 +643,6 @@ ExecCheckRTEPerms(RangeTblEntry *rte) ...@@ -637,7 +643,6 @@ ExecCheckRTEPerms(RangeTblEntry *rte)
return false; return false;
} }
col = -1;
while ((col = bms_next_member(rte->selectedCols, col)) >= 0) while ((col = bms_next_member(rte->selectedCols, col)) >= 0)
{ {
/* bit #s are offset by FirstLowInvalidHeapAttributeNumber */ /* bit #s are offset by FirstLowInvalidHeapAttributeNumber */
...@@ -660,27 +665,49 @@ ExecCheckRTEPerms(RangeTblEntry *rte) ...@@ -660,27 +665,49 @@ ExecCheckRTEPerms(RangeTblEntry *rte)
} }
/* /*
* Basically the same for the mod columns, with either INSERT or * Basically the same for the mod columns, for both INSERT and UPDATE
* UPDATE privilege as specified by remainingPerms. * privilege as specified by remainingPerms.
*/ */
remainingPerms &= ~ACL_SELECT; if (remainingPerms & ACL_INSERT && !ExecCheckRTEPermsModified(relOid,
if (remainingPerms != 0) userid,
{ rte->insertedCols,
ACL_INSERT))
return false;
if (remainingPerms & ACL_UPDATE && !ExecCheckRTEPermsModified(relOid,
userid,
rte->updatedCols,
ACL_UPDATE))
return false;
}
return true;
}
/*
* 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 change any columns, allow the * When the query doesn't explicitly update any columns, allow the
* query if we have permission on any column of the rel. This is * 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 * to handle SELECT FOR UPDATE as well as possible corner cases in
* INSERT and UPDATE. * UPDATE.
*/ */
if (bms_is_empty(rte->modifiedCols)) if (bms_is_empty(modifiedCols))
{ {
if (pg_attribute_aclcheck_all(relOid, userid, remainingPerms, if (pg_attribute_aclcheck_all(relOid, userid, requiredPerms,
ACLMASK_ANY) != ACLCHECK_OK) ACLMASK_ANY) != ACLCHECK_OK)
return false; return false;
} }
col = -1; while ((col = bms_next_member(modifiedCols, col)) >= 0)
while ((col = bms_next_member(rte->modifiedCols, col)) >= 0)
{ {
/* bit #s are offset by FirstLowInvalidHeapAttributeNumber */ /* bit #s are offset by FirstLowInvalidHeapAttributeNumber */
AttrNumber attno = col + FirstLowInvalidHeapAttributeNumber; AttrNumber attno = col + FirstLowInvalidHeapAttributeNumber;
...@@ -693,12 +720,10 @@ ExecCheckRTEPerms(RangeTblEntry *rte) ...@@ -693,12 +720,10 @@ ExecCheckRTEPerms(RangeTblEntry *rte)
else else
{ {
if (pg_attribute_aclcheck(relOid, attno, userid, if (pg_attribute_aclcheck(relOid, attno, userid,
remainingPerms) != ACLCHECK_OK) requiredPerms) != ACLCHECK_OK)
return false; return false;
} }
} }
}
}
return true; return true;
} }
...@@ -1633,6 +1658,9 @@ ExecConstraints(ResultRelInfo *resultRelInfo, ...@@ -1633,6 +1658,9 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
Relation rel = resultRelInfo->ri_RelationDesc; Relation rel = resultRelInfo->ri_RelationDesc;
TupleDesc tupdesc = RelationGetDescr(rel); TupleDesc tupdesc = RelationGetDescr(rel);
TupleConstr *constr = tupdesc->constr; TupleConstr *constr = tupdesc->constr;
Bitmapset *modifiedCols;
Bitmapset *insertedCols;
Bitmapset *updatedCols;
Assert(constr); Assert(constr);
...@@ -1647,9 +1675,10 @@ ExecConstraints(ResultRelInfo *resultRelInfo, ...@@ -1647,9 +1675,10 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
slot_attisnull(slot, attrChk)) slot_attisnull(slot, attrChk))
{ {
char *val_desc; 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), val_desc = ExecBuildSlotValueDescription(RelationGetRelid(rel),
slot, slot,
tupdesc, tupdesc,
...@@ -1673,9 +1702,10 @@ ExecConstraints(ResultRelInfo *resultRelInfo, ...@@ -1673,9 +1702,10 @@ ExecConstraints(ResultRelInfo *resultRelInfo,
if ((failed = ExecRelCheck(resultRelInfo, slot, estate)) != NULL) if ((failed = ExecRelCheck(resultRelInfo, slot, estate)) != NULL)
{ {
char *val_desc; 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), val_desc = ExecBuildSlotValueDescription(RelationGetRelid(rel),
slot, slot,
tupdesc, tupdesc,
...@@ -1746,6 +1776,8 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, ...@@ -1746,6 +1776,8 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo,
{ {
char *val_desc; char *val_desc;
Bitmapset *modifiedCols; Bitmapset *modifiedCols;
Bitmapset *insertedCols;
Bitmapset *updatedCols;
switch (wco->kind) switch (wco->kind)
{ {
...@@ -1758,7 +1790,9 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, ...@@ -1758,7 +1790,9 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo,
* as that depends on the USING policy. * as that depends on the USING policy.
*/ */
case WCO_VIEW_CHECK: 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), val_desc = ExecBuildSlotValueDescription(RelationGetRelid(rel),
slot, slot,
tupdesc, tupdesc,
......
...@@ -2042,7 +2042,8 @@ _copyRangeTblEntry(const RangeTblEntry *from) ...@@ -2042,7 +2042,8 @@ _copyRangeTblEntry(const RangeTblEntry *from)
COPY_SCALAR_FIELD(requiredPerms); COPY_SCALAR_FIELD(requiredPerms);
COPY_SCALAR_FIELD(checkAsUser); COPY_SCALAR_FIELD(checkAsUser);
COPY_BITMAPSET_FIELD(selectedCols); COPY_BITMAPSET_FIELD(selectedCols);
COPY_BITMAPSET_FIELD(modifiedCols); COPY_BITMAPSET_FIELD(insertedCols);
COPY_BITMAPSET_FIELD(updatedCols);
COPY_NODE_FIELD(securityQuals); COPY_NODE_FIELD(securityQuals);
return newnode; return newnode;
......
...@@ -2352,7 +2352,8 @@ _equalRangeTblEntry(const RangeTblEntry *a, const RangeTblEntry *b) ...@@ -2352,7 +2352,8 @@ _equalRangeTblEntry(const RangeTblEntry *a, const RangeTblEntry *b)
COMPARE_SCALAR_FIELD(requiredPerms); COMPARE_SCALAR_FIELD(requiredPerms);
COMPARE_SCALAR_FIELD(checkAsUser); COMPARE_SCALAR_FIELD(checkAsUser);
COMPARE_BITMAPSET_FIELD(selectedCols); COMPARE_BITMAPSET_FIELD(selectedCols);
COMPARE_BITMAPSET_FIELD(modifiedCols); COMPARE_BITMAPSET_FIELD(insertedCols);
COMPARE_BITMAPSET_FIELD(updatedCols);
COMPARE_NODE_FIELD(securityQuals); COMPARE_NODE_FIELD(securityQuals);
return true; return true;
......
...@@ -2475,7 +2475,8 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node) ...@@ -2475,7 +2475,8 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node)
WRITE_UINT_FIELD(requiredPerms); WRITE_UINT_FIELD(requiredPerms);
WRITE_OID_FIELD(checkAsUser); WRITE_OID_FIELD(checkAsUser);
WRITE_BITMAPSET_FIELD(selectedCols); WRITE_BITMAPSET_FIELD(selectedCols);
WRITE_BITMAPSET_FIELD(modifiedCols); WRITE_BITMAPSET_FIELD(insertedCols);
WRITE_BITMAPSET_FIELD(updatedCols);
WRITE_NODE_FIELD(securityQuals); WRITE_NODE_FIELD(securityQuals);
} }
......
...@@ -1256,7 +1256,8 @@ _readRangeTblEntry(void) ...@@ -1256,7 +1256,8 @@ _readRangeTblEntry(void)
READ_UINT_FIELD(requiredPerms); READ_UINT_FIELD(requiredPerms);
READ_OID_FIELD(checkAsUser); READ_OID_FIELD(checkAsUser);
READ_BITMAPSET_FIELD(selectedCols); READ_BITMAPSET_FIELD(selectedCols);
READ_BITMAPSET_FIELD(modifiedCols); READ_BITMAPSET_FIELD(insertedCols);
READ_BITMAPSET_FIELD(updatedCols);
READ_NODE_FIELD(securityQuals); READ_NODE_FIELD(securityQuals);
READ_DONE(); READ_DONE();
......
...@@ -373,9 +373,9 @@ flatten_rtes_walker(Node *node, PlannerGlobal *glob) ...@@ -373,9 +373,9 @@ flatten_rtes_walker(Node *node, PlannerGlobal *glob)
* *
* In the flat rangetable, we zero out substructure pointers that are not * In the flat rangetable, we zero out substructure pointers that are not
* needed by the executor; this reduces the storage space and copying cost * 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 * for cached plans. We keep only the alias and eref Alias fields, which are
* are needed by EXPLAIN, and the selectedCols and modifiedCols bitmaps, * needed by EXPLAIN, and the selectedCols, insertedCols and updatedCols
* which are needed for executor-startup permissions checking and for * bitmaps, which are needed for executor-startup permissions checking and for
* trigger event checking. * trigger event checking.
*/ */
static void static void
......
...@@ -125,7 +125,8 @@ expand_security_quals(PlannerInfo *root, List *tlist) ...@@ -125,7 +125,8 @@ expand_security_quals(PlannerInfo *root, List *tlist)
rte->requiredPerms = 0; rte->requiredPerms = 0;
rte->checkAsUser = InvalidOid; rte->checkAsUser = InvalidOid;
rte->selectedCols = NULL; rte->selectedCols = NULL;
rte->modifiedCols = NULL; rte->insertedCols = NULL;
rte->updatedCols = NULL;
/* /*
* For the most part, Vars referencing the original relation * For the most part, Vars referencing the original relation
...@@ -224,7 +225,8 @@ expand_security_qual(PlannerInfo *root, List *tlist, int rt_index, ...@@ -224,7 +225,8 @@ expand_security_qual(PlannerInfo *root, List *tlist, int rt_index,
rte->requiredPerms = 0; rte->requiredPerms = 0;
rte->checkAsUser = InvalidOid; rte->checkAsUser = InvalidOid;
rte->selectedCols = NULL; rte->selectedCols = NULL;
rte->modifiedCols = NULL; rte->insertedCols = NULL;
rte->updatedCols = NULL;
/* /*
* Now deal with any PlanRowMark on this RTE by requesting a lock * Now deal with any PlanRowMark on this RTE by requesting a lock
......
...@@ -1368,14 +1368,16 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti) ...@@ -1368,14 +1368,16 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
* if this is the parent table, leave copyObject's result alone. * 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 * Note: we need to do this even though the executor won't run any
* permissions checks on the child RTE. The modifiedCols bitmap may * permissions checks on the child RTE. The insertedCols/updatedCols
* be examined for trigger-firing purposes. * bitmaps may be examined for trigger-firing purposes.
*/ */
if (childOID != parentOID) if (childOID != parentOID)
{ {
childrte->selectedCols = translate_col_privs(rte->selectedCols, childrte->selectedCols = translate_col_privs(rte->selectedCols,
appinfo->translated_vars); 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); appinfo->translated_vars);
} }
......
...@@ -733,7 +733,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) ...@@ -733,7 +733,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
false); false);
qry->targetList = lappend(qry->targetList, tle); qry->targetList = lappend(qry->targetList, tle);
rte->modifiedCols = bms_add_member(rte->modifiedCols, rte->insertedCols = bms_add_member(rte->insertedCols,
attr_num - FirstLowInvalidHeapAttributeNumber); attr_num - FirstLowInvalidHeapAttributeNumber);
icols = lnext(icols); icols = lnext(icols);
...@@ -2002,7 +2002,7 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt) ...@@ -2002,7 +2002,7 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
origTarget->location); origTarget->location);
/* Mark the target column as requiring update permissions */ /* 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); attrno - FirstLowInvalidHeapAttributeNumber);
origTargetList = lnext(origTargetList); origTargetList = lnext(origTargetList);
......
...@@ -1218,7 +1218,8 @@ addRangeTableEntry(ParseState *pstate, ...@@ -1218,7 +1218,8 @@ addRangeTableEntry(ParseState *pstate,
rte->requiredPerms = ACL_SELECT; rte->requiredPerms = ACL_SELECT;
rte->checkAsUser = InvalidOid; /* not set-uid by default, either */ rte->checkAsUser = InvalidOid; /* not set-uid by default, either */
rte->selectedCols = NULL; 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 * Add completed RTE to pstate's range table list, but not to join list
...@@ -1272,7 +1273,8 @@ addRangeTableEntryForRelation(ParseState *pstate, ...@@ -1272,7 +1273,8 @@ addRangeTableEntryForRelation(ParseState *pstate,
rte->requiredPerms = ACL_SELECT; rte->requiredPerms = ACL_SELECT;
rte->checkAsUser = InvalidOid; /* not set-uid by default, either */ rte->checkAsUser = InvalidOid; /* not set-uid by default, either */
rte->selectedCols = NULL; 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 * Add completed RTE to pstate's range table list, but not to join list
...@@ -1351,7 +1353,8 @@ addRangeTableEntryForSubquery(ParseState *pstate, ...@@ -1351,7 +1353,8 @@ addRangeTableEntryForSubquery(ParseState *pstate,
rte->requiredPerms = 0; rte->requiredPerms = 0;
rte->checkAsUser = InvalidOid; rte->checkAsUser = InvalidOid;
rte->selectedCols = NULL; 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 * Add completed RTE to pstate's range table list, but not to join list
...@@ -1606,7 +1609,8 @@ addRangeTableEntryForFunction(ParseState *pstate, ...@@ -1606,7 +1609,8 @@ addRangeTableEntryForFunction(ParseState *pstate,
rte->requiredPerms = 0; rte->requiredPerms = 0;
rte->checkAsUser = InvalidOid; rte->checkAsUser = InvalidOid;
rte->selectedCols = NULL; 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 * Add completed RTE to pstate's range table list, but not to join list
...@@ -1679,7 +1683,8 @@ addRangeTableEntryForValues(ParseState *pstate, ...@@ -1679,7 +1683,8 @@ addRangeTableEntryForValues(ParseState *pstate,
rte->requiredPerms = 0; rte->requiredPerms = 0;
rte->checkAsUser = InvalidOid; rte->checkAsUser = InvalidOid;
rte->selectedCols = NULL; 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 * Add completed RTE to pstate's range table list, but not to join list
...@@ -1748,7 +1753,8 @@ addRangeTableEntryForJoin(ParseState *pstate, ...@@ -1748,7 +1753,8 @@ addRangeTableEntryForJoin(ParseState *pstate,
rte->requiredPerms = 0; rte->requiredPerms = 0;
rte->checkAsUser = InvalidOid; rte->checkAsUser = InvalidOid;
rte->selectedCols = NULL; 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 * Add completed RTE to pstate's range table list, but not to join list
...@@ -1849,7 +1855,8 @@ addRangeTableEntryForCTE(ParseState *pstate, ...@@ -1849,7 +1855,8 @@ addRangeTableEntryForCTE(ParseState *pstate,
rte->requiredPerms = 0; rte->requiredPerms = 0;
rte->checkAsUser = InvalidOid; rte->checkAsUser = InvalidOid;
rte->selectedCols = NULL; 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 * Add completed RTE to pstate's range table list, but not to join list
......
...@@ -1403,7 +1403,8 @@ ApplyRetrieveRule(Query *parsetree, ...@@ -1403,7 +1403,8 @@ ApplyRetrieveRule(Query *parsetree,
rte->requiredPerms = 0; rte->requiredPerms = 0;
rte->checkAsUser = InvalidOid; rte->checkAsUser = InvalidOid;
rte->selectedCols = NULL; rte->selectedCols = NULL;
rte->modifiedCols = NULL; rte->insertedCols = NULL;
rte->updatedCols = NULL;
/* /*
* For the most part, Vars referencing the view should remain as * For the most part, Vars referencing the view should remain as
...@@ -1466,12 +1467,14 @@ ApplyRetrieveRule(Query *parsetree, ...@@ -1466,12 +1467,14 @@ ApplyRetrieveRule(Query *parsetree,
subrte->requiredPerms = rte->requiredPerms; subrte->requiredPerms = rte->requiredPerms;
subrte->checkAsUser = rte->checkAsUser; subrte->checkAsUser = rte->checkAsUser;
subrte->selectedCols = rte->selectedCols; 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->requiredPerms = 0; /* no permission check on subquery itself */
rte->checkAsUser = InvalidOid; rte->checkAsUser = InvalidOid;
rte->selectedCols = NULL; 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 * If FOR [KEY] UPDATE/SHARE of view, mark all the contained tables as
...@@ -2621,9 +2624,9 @@ rewriteTargetView(Query *parsetree, Relation view) ...@@ -2621,9 +2624,9 @@ rewriteTargetView(Query *parsetree, Relation view)
/* /*
* For INSERT/UPDATE the modified columns must all be updatable. Note that * 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 * we get the modified columns from the query's targetlist, not from the
* result RTE's modifiedCols set, since rewriteTargetListIU may have added * result RTE's insertedCols and/or updatedCols set, since
* additional targetlist entries for view defaults, and these must also be * rewriteTargetListIU may have added additional targetlist entries for
* updatable. * view defaults, and these must also be updatable.
*/ */
if (parsetree->commandType != CMD_DELETE) if (parsetree->commandType != CMD_DELETE)
{ {
...@@ -2760,26 +2763,31 @@ rewriteTargetView(Query *parsetree, Relation view) ...@@ -2760,26 +2763,31 @@ rewriteTargetView(Query *parsetree, Relation view)
* *
* Initially, new_rte contains selectedCols permission check bits for all * Initially, new_rte contains selectedCols permission check bits for all
* base-rel columns referenced by the view, but since the view is a SELECT * 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 * query its insertedCols/updatedCols is empty. We set insertedCols and
* the columns the outer query is trying to modify, adjusting the column * updatedCols to include all the columns the outer query is trying to
* numbers as needed. But we leave selectedCols as-is, so the view owner * modify, adjusting the column numbers as needed. But we leave
* must have read permission for all columns used in the view definition, * selectedCols as-is, so the view owner must have read permission for all
* even if some of them are not read by the outer query. We could try to * columns used in the view definition, even if some of them are not read
* limit selectedCols to only columns used in the transformed query, but * by the outer query. We could try to limit selectedCols to only columns
* that does not correspond to what happens in ordinary SELECT usage of a * used in the transformed query, but that does not correspond to what
* view: all referenced columns must have read permission, even if * happens in ordinary SELECT usage of a view: all referenced columns must
* optimization finds that some of them can be discarded during query * have read permission, even if optimization finds that some of them can
* transformation. The flattening we're doing here is an optional * be discarded during query transformation. The flattening we're doing
* optimization, too. (If you are unpersuaded and want to change this, * here is an optional optimization, too. (If you are unpersuaded and want
* note that applying adjust_view_column_set to view_rte->selectedCols is * to change this, note that applying adjust_view_column_set to
* clearly *not* the right answer, since that neglects base-rel columns * view_rte->selectedCols is clearly *not* the right answer, since that
* used in the view's WHERE quals.) * neglects base-rel columns used in the view's WHERE quals.)
* *
* This step needs the modified view targetlist, so we have to do things * This step needs the modified view targetlist, so we have to do things
* in this order. * in this order.
*/ */
Assert(bms_is_empty(new_rte->modifiedCols)); Assert(bms_is_empty(new_rte->insertedCols) &&
new_rte->modifiedCols = adjust_view_column_set(view_rte->modifiedCols, 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); view_targetlist);
/* /*
......
...@@ -53,6 +53,6 @@ ...@@ -53,6 +53,6 @@
*/ */
/* yyyymmddN */ /* yyyymmddN */
#define CATALOG_VERSION_NO 201505071 #define CATALOG_VERSION_NO 201505081
#endif #endif
...@@ -735,11 +735,12 @@ typedef struct XmlSerialize ...@@ -735,11 +735,12 @@ typedef struct XmlSerialize
* For SELECT/INSERT/UPDATE permissions, if the user doesn't have * For SELECT/INSERT/UPDATE permissions, if the user doesn't have
* table-wide permissions then it is sufficient to have the permissions * table-wide permissions then it is sufficient to have the permissions
* on all columns identified in selectedCols (for SELECT) and/or * on all columns identified in selectedCols (for SELECT) and/or
* modifiedCols (for INSERT/UPDATE; we can tell which from the query type). * insertedCols and/or updatedCols (INSERT with ON CONFLICT UPDATE may
* selectedCols and modifiedCols are bitmapsets, which cannot have negative * have all 3). selectedCols, insertedCols and updatedCols are
* integer members, so we subtract FirstLowInvalidHeapAttributeNumber from * bitmapsets, which cannot have negative integer members, so we subtract
* column numbers before storing them in these fields. A whole-row Var * FirstLowInvalidHeapAttributeNumber from column numbers before storing
* reference is represented by setting the bit for InvalidAttrNumber. * them in these fields. A whole-row Var reference is represented by
* setting the bit for InvalidAttrNumber.
*-------------------- *--------------------
*/ */
typedef enum RTEKind typedef enum RTEKind
...@@ -834,7 +835,8 @@ typedef struct RangeTblEntry ...@@ -834,7 +835,8 @@ typedef struct RangeTblEntry
AclMode requiredPerms; /* bitmask of required access permissions */ AclMode requiredPerms; /* bitmask of required access permissions */
Oid checkAsUser; /* if valid, check access as this role */ Oid checkAsUser; /* if valid, check access as this role */
Bitmapset *selectedCols; /* columns needing SELECT permission */ 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 */ List *securityQuals; /* any security barrier quals to apply */
} RangeTblEntry; } RangeTblEntry;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment