mirror of
https://github.com/postgres/postgres.git
synced 2025-07-02 09:02:37 +03:00
Fix column-privilege leak in error-message paths
While building error messages to return to the user, BuildIndexValueDescription and ri_ReportViolation would happily include the entire key or entire row in the result returned to the user, even if the user didn't have access to view all of the columns being included. Instead, include only those columns which the user is providing or which the user has select rights on. If the user does not have any rights to view the table or any of the columns involved then no detail is provided and a NULL value is returned from BuildIndexValueDescription. Note that, for key cases, the user must have access to all of the columns for the key to be shown; a partial key will not be returned. Back-patch all the way, as column-level privileges are now in all supported versions. This has been assigned CVE-2014-8161, but since the issue and the patch have already been publicized on pgsql-hackers, there's no point in trying to hide this commit.
This commit is contained in:
@ -25,9 +25,11 @@
|
|||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "pgstat.h"
|
#include "pgstat.h"
|
||||||
#include "storage/bufmgr.h"
|
#include "storage/bufmgr.h"
|
||||||
|
#include "utils/acl.h"
|
||||||
#include "utils/builtins.h"
|
#include "utils/builtins.h"
|
||||||
#include "utils/lsyscache.h"
|
#include "utils/lsyscache.h"
|
||||||
#include "utils/rel.h"
|
#include "utils/rel.h"
|
||||||
|
#include "utils/syscache.h"
|
||||||
#include "utils/tqual.h"
|
#include "utils/tqual.h"
|
||||||
|
|
||||||
|
|
||||||
@ -150,6 +152,11 @@ IndexScanEnd(IndexScanDesc scan)
|
|||||||
* form "(key_name, ...)=(key_value, ...)". This is currently used
|
* form "(key_name, ...)=(key_value, ...)". This is currently used
|
||||||
* for building unique-constraint and exclusion-constraint error messages.
|
* for building unique-constraint and exclusion-constraint error messages.
|
||||||
*
|
*
|
||||||
|
* Note that if the user does not have permissions to view all of the
|
||||||
|
* columns involved then a NULL is returned. Returning a partial key seems
|
||||||
|
* unlikely to be useful and we have no way to know which of the columns the
|
||||||
|
* user provided.
|
||||||
|
*
|
||||||
* The passed-in values/nulls arrays are the "raw" input to the index AM,
|
* The passed-in values/nulls arrays are the "raw" input to the index AM,
|
||||||
* e.g. results of FormIndexDatum --- this is not necessarily what is stored
|
* e.g. results of FormIndexDatum --- this is not necessarily what is stored
|
||||||
* in the index, but it's what the user perceives to be stored.
|
* in the index, but it's what the user perceives to be stored.
|
||||||
@ -159,13 +166,62 @@ BuildIndexValueDescription(Relation indexRelation,
|
|||||||
Datum *values, bool *isnull)
|
Datum *values, bool *isnull)
|
||||||
{
|
{
|
||||||
StringInfoData buf;
|
StringInfoData buf;
|
||||||
|
Form_pg_index idxrec;
|
||||||
|
HeapTuple ht_idx;
|
||||||
int natts = indexRelation->rd_rel->relnatts;
|
int natts = indexRelation->rd_rel->relnatts;
|
||||||
int i;
|
int i;
|
||||||
|
int keyno;
|
||||||
|
Oid indexrelid = RelationGetRelid(indexRelation);
|
||||||
|
Oid indrelid;
|
||||||
|
AclResult aclresult;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check permissions- if the user does not have access to view all of the
|
||||||
|
* key columns then return NULL to avoid leaking data.
|
||||||
|
*
|
||||||
|
* First we need to check table-level SELECT access and then, if
|
||||||
|
* there is no access there, check column-level permissions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fetch the pg_index tuple by the Oid of the index
|
||||||
|
*/
|
||||||
|
ht_idx = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexrelid));
|
||||||
|
if (!HeapTupleIsValid(ht_idx))
|
||||||
|
elog(ERROR, "cache lookup failed for index %u", indexrelid);
|
||||||
|
idxrec = (Form_pg_index) GETSTRUCT(ht_idx);
|
||||||
|
|
||||||
|
indrelid = idxrec->indrelid;
|
||||||
|
Assert(indexrelid == idxrec->indexrelid);
|
||||||
|
|
||||||
|
/* Table-level SELECT is enough, if the user has it */
|
||||||
|
aclresult = pg_class_aclcheck(indrelid, GetUserId(), ACL_SELECT);
|
||||||
|
if (aclresult != ACLCHECK_OK)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* No table-level access, so step through the columns in the
|
||||||
|
* index and make sure the user has SELECT rights on all of them.
|
||||||
|
*/
|
||||||
|
for (keyno = 0; keyno < idxrec->indnatts; keyno++)
|
||||||
|
{
|
||||||
|
AttrNumber attnum = idxrec->indkey.values[keyno];
|
||||||
|
|
||||||
|
aclresult = pg_attribute_aclcheck(indrelid, attnum, GetUserId(),
|
||||||
|
ACL_SELECT);
|
||||||
|
|
||||||
|
if (aclresult != ACLCHECK_OK)
|
||||||
|
{
|
||||||
|
/* No access, so clean up and return */
|
||||||
|
ReleaseSysCache(ht_idx);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ReleaseSysCache(ht_idx);
|
||||||
|
|
||||||
initStringInfo(&buf);
|
initStringInfo(&buf);
|
||||||
appendStringInfo(&buf, "(%s)=(",
|
appendStringInfo(&buf, "(%s)=(",
|
||||||
pg_get_indexdef_columns(RelationGetRelid(indexRelation),
|
pg_get_indexdef_columns(indexrelid, true));
|
||||||
true));
|
|
||||||
|
|
||||||
for (i = 0; i < natts; i++)
|
for (i = 0; i < natts; i++)
|
||||||
{
|
{
|
||||||
|
@ -385,16 +385,20 @@ _bt_check_unique(Relation rel, IndexTuple itup, Relation heapRel,
|
|||||||
{
|
{
|
||||||
Datum values[INDEX_MAX_KEYS];
|
Datum values[INDEX_MAX_KEYS];
|
||||||
bool isnull[INDEX_MAX_KEYS];
|
bool isnull[INDEX_MAX_KEYS];
|
||||||
|
char *key_desc;
|
||||||
|
|
||||||
index_deform_tuple(itup, RelationGetDescr(rel),
|
index_deform_tuple(itup, RelationGetDescr(rel),
|
||||||
values, isnull);
|
values, isnull);
|
||||||
|
|
||||||
|
key_desc = BuildIndexValueDescription(rel, values,
|
||||||
|
isnull);
|
||||||
|
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_UNIQUE_VIOLATION),
|
(errcode(ERRCODE_UNIQUE_VIOLATION),
|
||||||
errmsg("duplicate key value violates unique constraint \"%s\"",
|
errmsg("duplicate key value violates unique constraint \"%s\"",
|
||||||
RelationGetRelationName(rel)),
|
RelationGetRelationName(rel)),
|
||||||
errdetail("Key %s already exists.",
|
key_desc ? errdetail("Key %s already exists.",
|
||||||
BuildIndexValueDescription(rel,
|
key_desc) : 0));
|
||||||
values, isnull))));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (all_dead)
|
else if (all_dead)
|
||||||
|
@ -148,6 +148,7 @@ typedef struct CopyStateData
|
|||||||
Oid *typioparams; /* array of element types for in_functions */
|
Oid *typioparams; /* array of element types for in_functions */
|
||||||
int *defmap; /* array of default att numbers */
|
int *defmap; /* array of default att numbers */
|
||||||
ExprState **defexprs; /* array of default att expressions */
|
ExprState **defexprs; /* array of default att expressions */
|
||||||
|
List *range_table;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* These variables are used to reduce overhead in textual COPY FROM.
|
* These variables are used to reduce overhead in textual COPY FROM.
|
||||||
@ -737,6 +738,7 @@ DoCopy(const CopyStmt *stmt, const char *queryString)
|
|||||||
bool pipe = (stmt->filename == NULL);
|
bool pipe = (stmt->filename == NULL);
|
||||||
Relation rel;
|
Relation rel;
|
||||||
uint64 processed;
|
uint64 processed;
|
||||||
|
RangeTblEntry *rte;
|
||||||
|
|
||||||
/* Disallow file COPY except to superusers. */
|
/* Disallow file COPY except to superusers. */
|
||||||
if (!pipe && !superuser())
|
if (!pipe && !superuser())
|
||||||
@ -750,7 +752,6 @@ DoCopy(const CopyStmt *stmt, const char *queryString)
|
|||||||
{
|
{
|
||||||
TupleDesc tupDesc;
|
TupleDesc tupDesc;
|
||||||
AclMode required_access = (is_from ? ACL_INSERT : ACL_SELECT);
|
AclMode required_access = (is_from ? ACL_INSERT : ACL_SELECT);
|
||||||
RangeTblEntry *rte;
|
|
||||||
List *attnums;
|
List *attnums;
|
||||||
ListCell *cur;
|
ListCell *cur;
|
||||||
|
|
||||||
@ -795,6 +796,7 @@ DoCopy(const CopyStmt *stmt, const char *queryString)
|
|||||||
|
|
||||||
cstate = BeginCopyFrom(rel, stmt->filename,
|
cstate = BeginCopyFrom(rel, stmt->filename,
|
||||||
stmt->attlist, stmt->options);
|
stmt->attlist, stmt->options);
|
||||||
|
cstate->range_table = list_make1(rte);
|
||||||
processed = CopyFrom(cstate); /* copy from file to database */
|
processed = CopyFrom(cstate); /* copy from file to database */
|
||||||
EndCopyFrom(cstate);
|
EndCopyFrom(cstate);
|
||||||
}
|
}
|
||||||
@ -802,6 +804,7 @@ DoCopy(const CopyStmt *stmt, const char *queryString)
|
|||||||
{
|
{
|
||||||
cstate = BeginCopyTo(rel, stmt->query, queryString, stmt->filename,
|
cstate = BeginCopyTo(rel, stmt->query, queryString, stmt->filename,
|
||||||
stmt->attlist, stmt->options);
|
stmt->attlist, stmt->options);
|
||||||
|
cstate->range_table = list_make1(rte);
|
||||||
processed = DoCopyTo(cstate); /* copy from database to file */
|
processed = DoCopyTo(cstate); /* copy from database to file */
|
||||||
EndCopyTo(cstate);
|
EndCopyTo(cstate);
|
||||||
}
|
}
|
||||||
@ -1933,6 +1936,7 @@ CopyFrom(CopyState cstate)
|
|||||||
estate->es_result_relations = resultRelInfo;
|
estate->es_result_relations = resultRelInfo;
|
||||||
estate->es_num_result_relations = 1;
|
estate->es_num_result_relations = 1;
|
||||||
estate->es_result_relation_info = resultRelInfo;
|
estate->es_result_relation_info = resultRelInfo;
|
||||||
|
estate->es_range_table = cstate->range_table;
|
||||||
|
|
||||||
/* Set up a tuple slot too */
|
/* Set up a tuple slot too */
|
||||||
myslot = ExecInitExtraTupleSlot(estate);
|
myslot = ExecInitExtraTupleSlot(estate);
|
||||||
|
@ -60,6 +60,12 @@
|
|||||||
int SessionReplicationRole = SESSION_REPLICATION_ROLE_ORIGIN;
|
int SessionReplicationRole = SESSION_REPLICATION_ROLE_ORIGIN;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
#define GetModifiedColumns(relinfo, estate) \
|
#define GetModifiedColumns(relinfo, estate) \
|
||||||
(rt_fetch((relinfo)->ri_RangeTableIndex, (estate)->es_range_table)->modifiedCols)
|
(rt_fetch((relinfo)->ri_RangeTableIndex, (estate)->es_range_table)->modifiedCols)
|
||||||
|
|
||||||
|
@ -95,6 +95,15 @@ static void intorel_receive(TupleTableSlot *slot, DestReceiver *self);
|
|||||||
static void intorel_shutdown(DestReceiver *self);
|
static void intorel_shutdown(DestReceiver *self);
|
||||||
static void intorel_destroy(DestReceiver *self);
|
static void intorel_destroy(DestReceiver *self);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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
|
||||||
|
* 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)
|
||||||
|
|
||||||
/* end of local decls */
|
/* end of local decls */
|
||||||
|
|
||||||
|
|
||||||
|
@ -1307,15 +1307,19 @@ retry:
|
|||||||
(errcode(ERRCODE_EXCLUSION_VIOLATION),
|
(errcode(ERRCODE_EXCLUSION_VIOLATION),
|
||||||
errmsg("could not create exclusion constraint \"%s\"",
|
errmsg("could not create exclusion constraint \"%s\"",
|
||||||
RelationGetRelationName(index)),
|
RelationGetRelationName(index)),
|
||||||
|
error_new && error_existing ?
|
||||||
errdetail("Key %s conflicts with key %s.",
|
errdetail("Key %s conflicts with key %s.",
|
||||||
error_new, error_existing)));
|
error_new, error_existing) :
|
||||||
|
errdetail("Key conflicts exist.")));
|
||||||
else
|
else
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_EXCLUSION_VIOLATION),
|
(errcode(ERRCODE_EXCLUSION_VIOLATION),
|
||||||
errmsg("conflicting key value violates exclusion constraint \"%s\"",
|
errmsg("conflicting key value violates exclusion constraint \"%s\"",
|
||||||
RelationGetRelationName(index)),
|
RelationGetRelationName(index)),
|
||||||
|
error_new && error_existing ?
|
||||||
errdetail("Key %s conflicts with existing key %s.",
|
errdetail("Key %s conflicts with existing key %s.",
|
||||||
error_new, error_existing)));
|
error_new, error_existing) :
|
||||||
|
errdetail("Key conflicts with existing key.")));
|
||||||
}
|
}
|
||||||
|
|
||||||
index_endscan(index_scan);
|
index_endscan(index_scan);
|
||||||
|
@ -3495,6 +3495,9 @@ ri_ReportViolation(RI_QueryKey *qkey, const char *constrname,
|
|||||||
bool onfk;
|
bool onfk;
|
||||||
int idx,
|
int idx,
|
||||||
key_idx;
|
key_idx;
|
||||||
|
Oid rel_oid;
|
||||||
|
AclResult aclresult;
|
||||||
|
bool has_perm = true;
|
||||||
|
|
||||||
if (spi_err)
|
if (spi_err)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
@ -3513,12 +3516,14 @@ ri_ReportViolation(RI_QueryKey *qkey, const char *constrname,
|
|||||||
if (onfk)
|
if (onfk)
|
||||||
{
|
{
|
||||||
key_idx = RI_KEYPAIR_FK_IDX;
|
key_idx = RI_KEYPAIR_FK_IDX;
|
||||||
|
rel_oid = fk_rel->rd_id;
|
||||||
if (tupdesc == NULL)
|
if (tupdesc == NULL)
|
||||||
tupdesc = fk_rel->rd_att;
|
tupdesc = fk_rel->rd_att;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
key_idx = RI_KEYPAIR_PK_IDX;
|
key_idx = RI_KEYPAIR_PK_IDX;
|
||||||
|
rel_oid = pk_rel->rd_id;
|
||||||
if (tupdesc == NULL)
|
if (tupdesc == NULL)
|
||||||
tupdesc = pk_rel->rd_att;
|
tupdesc = pk_rel->rd_att;
|
||||||
}
|
}
|
||||||
@ -3538,6 +3543,33 @@ ri_ReportViolation(RI_QueryKey *qkey, const char *constrname,
|
|||||||
RelationGetRelationName(pk_rel))));
|
RelationGetRelationName(pk_rel))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check permissions- if the user does not have access to view the data in
|
||||||
|
* any of the key columns then we don't include the errdetail() below.
|
||||||
|
*
|
||||||
|
* Check table-level permissions first and, failing that, column-level
|
||||||
|
* privileges.
|
||||||
|
*/
|
||||||
|
aclresult = pg_class_aclcheck(rel_oid, GetUserId(), ACL_SELECT);
|
||||||
|
if (aclresult != ACLCHECK_OK)
|
||||||
|
{
|
||||||
|
/* Try for column-level permissions */
|
||||||
|
for (idx = 0; idx < qkey->nkeypairs; idx++)
|
||||||
|
{
|
||||||
|
aclresult = pg_attribute_aclcheck(rel_oid, qkey->keypair[idx][key_idx],
|
||||||
|
GetUserId(),
|
||||||
|
ACL_SELECT);
|
||||||
|
/* No access to the key */
|
||||||
|
if (aclresult != ACLCHECK_OK)
|
||||||
|
{
|
||||||
|
has_perm = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_perm)
|
||||||
|
{
|
||||||
/* Get printable versions of the keys involved */
|
/* Get printable versions of the keys involved */
|
||||||
initStringInfo(&key_names);
|
initStringInfo(&key_names);
|
||||||
initStringInfo(&key_values);
|
initStringInfo(&key_values);
|
||||||
@ -3560,23 +3592,32 @@ ri_ReportViolation(RI_QueryKey *qkey, const char *constrname,
|
|||||||
appendStringInfoString(&key_names, name);
|
appendStringInfoString(&key_names, name);
|
||||||
appendStringInfoString(&key_values, val);
|
appendStringInfoString(&key_values, val);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (onfk)
|
if (onfk)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
|
(errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
|
||||||
errmsg("insert or update on table \"%s\" violates foreign key constraint \"%s\"",
|
errmsg("insert or update on table \"%s\" violates foreign key constraint \"%s\"",
|
||||||
RelationGetRelationName(fk_rel), constrname),
|
RelationGetRelationName(fk_rel),
|
||||||
|
constrname),
|
||||||
|
has_perm ?
|
||||||
errdetail("Key (%s)=(%s) is not present in table \"%s\".",
|
errdetail("Key (%s)=(%s) is not present in table \"%s\".",
|
||||||
key_names.data, key_values.data,
|
key_names.data, key_values.data,
|
||||||
|
RelationGetRelationName(pk_rel)) :
|
||||||
|
errdetail("Key is not present in table \"%s\".",
|
||||||
RelationGetRelationName(pk_rel))));
|
RelationGetRelationName(pk_rel))));
|
||||||
else
|
else
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
|
(errcode(ERRCODE_FOREIGN_KEY_VIOLATION),
|
||||||
errmsg("update or delete on table \"%s\" violates foreign key constraint \"%s\" on table \"%s\"",
|
errmsg("update or delete on table \"%s\" violates foreign key constraint \"%s\" on table \"%s\"",
|
||||||
RelationGetRelationName(pk_rel),
|
RelationGetRelationName(pk_rel),
|
||||||
constrname, RelationGetRelationName(fk_rel)),
|
constrname,
|
||||||
|
RelationGetRelationName(fk_rel)),
|
||||||
|
has_perm ?
|
||||||
errdetail("Key (%s)=(%s) is still referenced from table \"%s\".",
|
errdetail("Key (%s)=(%s) is still referenced from table \"%s\".",
|
||||||
key_names.data, key_values.data,
|
key_names.data, key_values.data,
|
||||||
|
RelationGetRelationName(fk_rel)) :
|
||||||
|
errdetail("Key is still referenced from table \"%s\".",
|
||||||
RelationGetRelationName(fk_rel))));
|
RelationGetRelationName(fk_rel))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3124,15 +3124,18 @@ comparetup_index_btree(const SortTuple *a, const SortTuple *b,
|
|||||||
{
|
{
|
||||||
Datum values[INDEX_MAX_KEYS];
|
Datum values[INDEX_MAX_KEYS];
|
||||||
bool isnull[INDEX_MAX_KEYS];
|
bool isnull[INDEX_MAX_KEYS];
|
||||||
|
char *key_desc;
|
||||||
|
|
||||||
index_deform_tuple(tuple1, tupDes, values, isnull);
|
index_deform_tuple(tuple1, tupDes, values, isnull);
|
||||||
|
|
||||||
|
key_desc = BuildIndexValueDescription(state->indexRel, values, isnull);
|
||||||
|
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_UNIQUE_VIOLATION),
|
(errcode(ERRCODE_UNIQUE_VIOLATION),
|
||||||
errmsg("could not create unique index \"%s\"",
|
errmsg("could not create unique index \"%s\"",
|
||||||
RelationGetRelationName(state->indexRel)),
|
RelationGetRelationName(state->indexRel)),
|
||||||
errdetail("Key %s is duplicated.",
|
key_desc ? errdetail("Key %s is duplicated.", key_desc) :
|
||||||
BuildIndexValueDescription(state->indexRel,
|
errdetail("Duplicate keys exist.")));
|
||||||
values, isnull))));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -362,6 +362,34 @@ SELECT atest6 FROM atest6; -- ok
|
|||||||
(0 rows)
|
(0 rows)
|
||||||
|
|
||||||
COPY atest6 TO stdout; -- ok
|
COPY atest6 TO stdout; -- ok
|
||||||
|
-- check error reporting with column privs
|
||||||
|
SET SESSION AUTHORIZATION regressuser1;
|
||||||
|
CREATE TABLE t1 (c1 int, c2 int, c3 int check (c3 < 5), primary key (c1, c2));
|
||||||
|
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "t1_pkey" for table "t1"
|
||||||
|
GRANT SELECT (c1) ON t1 TO regressuser2;
|
||||||
|
GRANT INSERT (c1, c2, c3) ON t1 TO regressuser2;
|
||||||
|
GRANT UPDATE (c1, c2, c3) ON t1 TO regressuser2;
|
||||||
|
-- seed data
|
||||||
|
INSERT INTO t1 VALUES (1, 1, 1);
|
||||||
|
INSERT INTO t1 VALUES (1, 2, 1);
|
||||||
|
INSERT INTO t1 VALUES (2, 1, 2);
|
||||||
|
INSERT INTO t1 VALUES (2, 2, 2);
|
||||||
|
INSERT INTO t1 VALUES (3, 1, 3);
|
||||||
|
SET SESSION AUTHORIZATION regressuser2;
|
||||||
|
INSERT INTO t1 (c1, c2) VALUES (1, 1); -- fail, but row not shown
|
||||||
|
ERROR: duplicate key value violates unique constraint "t1_pkey"
|
||||||
|
UPDATE t1 SET c2 = 1; -- fail, but row not shown
|
||||||
|
ERROR: duplicate key value violates unique constraint "t1_pkey"
|
||||||
|
INSERT INTO t1 (c1, c2) VALUES (null, null); -- fail, but see columns being inserted
|
||||||
|
ERROR: null value in column "c1" violates not-null constraint
|
||||||
|
INSERT INTO t1 (c3) VALUES (null); -- fail, but see columns being inserted or have SELECT
|
||||||
|
ERROR: null value in column "c1" violates not-null constraint
|
||||||
|
INSERT INTO t1 (c1) VALUES (5); -- fail, but see columns being inserted or have SELECT
|
||||||
|
ERROR: null value in column "c2" violates not-null constraint
|
||||||
|
UPDATE t1 SET c3 = 10; -- fail, but see columns with SELECT rights, or being modified
|
||||||
|
ERROR: new row for relation "t1" violates check constraint "t1_c3_check"
|
||||||
|
SET SESSION AUTHORIZATION regressuser1;
|
||||||
|
DROP TABLE t1;
|
||||||
-- test column-level privileges when involved with DELETE
|
-- test column-level privileges when involved with DELETE
|
||||||
SET SESSION AUTHORIZATION regressuser1;
|
SET SESSION AUTHORIZATION regressuser1;
|
||||||
ALTER TABLE atest6 ADD COLUMN three integer;
|
ALTER TABLE atest6 ADD COLUMN three integer;
|
||||||
|
@ -238,6 +238,31 @@ UPDATE atest5 SET one = 1; -- fail
|
|||||||
SELECT atest6 FROM atest6; -- ok
|
SELECT atest6 FROM atest6; -- ok
|
||||||
COPY atest6 TO stdout; -- ok
|
COPY atest6 TO stdout; -- ok
|
||||||
|
|
||||||
|
-- check error reporting with column privs
|
||||||
|
SET SESSION AUTHORIZATION regressuser1;
|
||||||
|
CREATE TABLE t1 (c1 int, c2 int, c3 int check (c3 < 5), primary key (c1, c2));
|
||||||
|
GRANT SELECT (c1) ON t1 TO regressuser2;
|
||||||
|
GRANT INSERT (c1, c2, c3) ON t1 TO regressuser2;
|
||||||
|
GRANT UPDATE (c1, c2, c3) ON t1 TO regressuser2;
|
||||||
|
|
||||||
|
-- seed data
|
||||||
|
INSERT INTO t1 VALUES (1, 1, 1);
|
||||||
|
INSERT INTO t1 VALUES (1, 2, 1);
|
||||||
|
INSERT INTO t1 VALUES (2, 1, 2);
|
||||||
|
INSERT INTO t1 VALUES (2, 2, 2);
|
||||||
|
INSERT INTO t1 VALUES (3, 1, 3);
|
||||||
|
|
||||||
|
SET SESSION AUTHORIZATION regressuser2;
|
||||||
|
INSERT INTO t1 (c1, c2) VALUES (1, 1); -- fail, but row not shown
|
||||||
|
UPDATE t1 SET c2 = 1; -- fail, but row not shown
|
||||||
|
INSERT INTO t1 (c1, c2) VALUES (null, null); -- fail, but see columns being inserted
|
||||||
|
INSERT INTO t1 (c3) VALUES (null); -- fail, but see columns being inserted or have SELECT
|
||||||
|
INSERT INTO t1 (c1) VALUES (5); -- fail, but see columns being inserted or have SELECT
|
||||||
|
UPDATE t1 SET c3 = 10; -- fail, but see columns with SELECT rights, or being modified
|
||||||
|
|
||||||
|
SET SESSION AUTHORIZATION regressuser1;
|
||||||
|
DROP TABLE t1;
|
||||||
|
|
||||||
-- test column-level privileges when involved with DELETE
|
-- test column-level privileges when involved with DELETE
|
||||||
SET SESSION AUTHORIZATION regressuser1;
|
SET SESSION AUTHORIZATION regressuser1;
|
||||||
ALTER TABLE atest6 ADD COLUMN three integer;
|
ALTER TABLE atest6 ADD COLUMN three integer;
|
||||||
|
Reference in New Issue
Block a user