mirror of
https://github.com/postgres/postgres.git
synced 2025-07-03 20:02:46 +03:00
ALTER TABLE .. FORCE ROW LEVEL SECURITY
To allow users to force RLS to always be applied, even for table owners, add ALTER TABLE .. FORCE ROW LEVEL SECURITY. row_security=off overrides FORCE ROW LEVEL SECURITY, to ensure pg_dump output is complete (by default). Also add SECURITY_NOFORCE_RLS context to avoid data corruption when ALTER TABLE .. FORCE ROW SECURITY is being used. The SECURITY_NOFORCE_RLS security context is used only during referential integrity checks and is only considered in check_enable_rls() after we have already checked that the current user is the owner of the relation (which should always be the case during referential integrity checks). Back-patch to 9.5 where RLS was added.
This commit is contained in:
@ -1971,6 +1971,16 @@
|
|||||||
</entry>
|
</entry>
|
||||||
</row>
|
</row>
|
||||||
|
|
||||||
|
<row>
|
||||||
|
<entry><structfield>relforcerowsecurity</structfield></entry>
|
||||||
|
<entry><type>bool</type></entry>
|
||||||
|
<entry></entry>
|
||||||
|
<entry>
|
||||||
|
True if row level security (when enabled) will also apply to table owner; see
|
||||||
|
<link linkend="catalog-pg-policy"><structname>pg_policy</structname></link> catalog
|
||||||
|
</entry>
|
||||||
|
</row>
|
||||||
|
|
||||||
<row>
|
<row>
|
||||||
<entry><structfield>relispopulated</structfield></entry>
|
<entry><structfield>relispopulated</structfield></entry>
|
||||||
<entry><type>bool</type></entry>
|
<entry><type>bool</type></entry>
|
||||||
|
@ -61,6 +61,8 @@ ALTER TABLE ALL IN TABLESPACE <replaceable class="PARAMETER">name</replaceable>
|
|||||||
ENABLE ALWAYS RULE <replaceable class="PARAMETER">rewrite_rule_name</replaceable>
|
ENABLE ALWAYS RULE <replaceable class="PARAMETER">rewrite_rule_name</replaceable>
|
||||||
DISABLE ROW LEVEL SECURITY
|
DISABLE ROW LEVEL SECURITY
|
||||||
ENABLE ROW LEVEL SECURITY
|
ENABLE ROW LEVEL SECURITY
|
||||||
|
FORCE ROW LEVEL SECURITY
|
||||||
|
NO FORCE ROW LEVEL SECURITY
|
||||||
CLUSTER ON <replaceable class="PARAMETER">index_name</replaceable>
|
CLUSTER ON <replaceable class="PARAMETER">index_name</replaceable>
|
||||||
SET WITHOUT CLUSTER
|
SET WITHOUT CLUSTER
|
||||||
SET WITH OIDS
|
SET WITH OIDS
|
||||||
@ -431,6 +433,21 @@ ALTER TABLE ALL IN TABLESPACE <replaceable class="PARAMETER">name</replaceable>
|
|||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><literal>NO FORCE</literal>/<literal>FORCE ROW LEVEL SECURITY</literal></term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
These forms control the application of row security policies belonging
|
||||||
|
to the table when the user is the table owner. If enabled, row level
|
||||||
|
security policies will be applied when the user is the table owner. If
|
||||||
|
disabled (the default) then row level security will not be applied when
|
||||||
|
the user is the table owner.
|
||||||
|
See also
|
||||||
|
<xref linkend="SQL-CREATEPOLICY">.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term><literal>CLUSTER ON</literal></term>
|
<term><literal>CLUSTER ON</literal></term>
|
||||||
<listitem>
|
<listitem>
|
||||||
|
@ -802,6 +802,7 @@ InsertPgClassTuple(Relation pg_class_desc,
|
|||||||
values[Anum_pg_class_relhasrules - 1] = BoolGetDatum(rd_rel->relhasrules);
|
values[Anum_pg_class_relhasrules - 1] = BoolGetDatum(rd_rel->relhasrules);
|
||||||
values[Anum_pg_class_relhastriggers - 1] = BoolGetDatum(rd_rel->relhastriggers);
|
values[Anum_pg_class_relhastriggers - 1] = BoolGetDatum(rd_rel->relhastriggers);
|
||||||
values[Anum_pg_class_relrowsecurity - 1] = BoolGetDatum(rd_rel->relrowsecurity);
|
values[Anum_pg_class_relrowsecurity - 1] = BoolGetDatum(rd_rel->relrowsecurity);
|
||||||
|
values[Anum_pg_class_relforcerowsecurity - 1] = BoolGetDatum(rd_rel->relforcerowsecurity);
|
||||||
values[Anum_pg_class_relhassubclass - 1] = BoolGetDatum(rd_rel->relhassubclass);
|
values[Anum_pg_class_relhassubclass - 1] = BoolGetDatum(rd_rel->relhassubclass);
|
||||||
values[Anum_pg_class_relispopulated - 1] = BoolGetDatum(rd_rel->relispopulated);
|
values[Anum_pg_class_relispopulated - 1] = BoolGetDatum(rd_rel->relispopulated);
|
||||||
values[Anum_pg_class_relreplident - 1] = CharGetDatum(rd_rel->relreplident);
|
values[Anum_pg_class_relreplident - 1] = CharGetDatum(rd_rel->relreplident);
|
||||||
|
@ -418,6 +418,7 @@ static void ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKM
|
|||||||
static void ATExecGenericOptions(Relation rel, List *options);
|
static void ATExecGenericOptions(Relation rel, List *options);
|
||||||
static void ATExecEnableRowSecurity(Relation rel);
|
static void ATExecEnableRowSecurity(Relation rel);
|
||||||
static void ATExecDisableRowSecurity(Relation rel);
|
static void ATExecDisableRowSecurity(Relation rel);
|
||||||
|
static void ATExecForceNoForceRowSecurity(Relation rel, bool force_rls);
|
||||||
|
|
||||||
static void copy_relation_data(SMgrRelation rel, SMgrRelation dst,
|
static void copy_relation_data(SMgrRelation rel, SMgrRelation dst,
|
||||||
ForkNumber forkNum, char relpersistence);
|
ForkNumber forkNum, char relpersistence);
|
||||||
@ -2929,6 +2930,8 @@ AlterTableGetLockLevel(List *cmds)
|
|||||||
case AT_SetNotNull:
|
case AT_SetNotNull:
|
||||||
case AT_EnableRowSecurity:
|
case AT_EnableRowSecurity:
|
||||||
case AT_DisableRowSecurity:
|
case AT_DisableRowSecurity:
|
||||||
|
case AT_ForceRowSecurity:
|
||||||
|
case AT_NoForceRowSecurity:
|
||||||
cmd_lockmode = AccessExclusiveLock;
|
cmd_lockmode = AccessExclusiveLock;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -3354,6 +3357,8 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
|
|||||||
case AT_DropOf: /* NOT OF */
|
case AT_DropOf: /* NOT OF */
|
||||||
case AT_EnableRowSecurity:
|
case AT_EnableRowSecurity:
|
||||||
case AT_DisableRowSecurity:
|
case AT_DisableRowSecurity:
|
||||||
|
case AT_ForceRowSecurity:
|
||||||
|
case AT_NoForceRowSecurity:
|
||||||
ATSimplePermissions(rel, ATT_TABLE);
|
ATSimplePermissions(rel, ATT_TABLE);
|
||||||
/* These commands never recurse */
|
/* These commands never recurse */
|
||||||
/* No command-specific prep needed */
|
/* No command-specific prep needed */
|
||||||
@ -3670,6 +3675,12 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
|
|||||||
case AT_DisableRowSecurity:
|
case AT_DisableRowSecurity:
|
||||||
ATExecDisableRowSecurity(rel);
|
ATExecDisableRowSecurity(rel);
|
||||||
break;
|
break;
|
||||||
|
case AT_ForceRowSecurity:
|
||||||
|
ATExecForceNoForceRowSecurity(rel, true);
|
||||||
|
break;
|
||||||
|
case AT_NoForceRowSecurity:
|
||||||
|
ATExecForceNoForceRowSecurity(rel, false);
|
||||||
|
break;
|
||||||
case AT_GenericOptions:
|
case AT_GenericOptions:
|
||||||
ATExecGenericOptions(rel, (List *) cmd->def);
|
ATExecGenericOptions(rel, (List *) cmd->def);
|
||||||
break;
|
break;
|
||||||
@ -11048,6 +11059,35 @@ ATExecDisableRowSecurity(Relation rel)
|
|||||||
heap_freetuple(tuple);
|
heap_freetuple(tuple);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ALTER TABLE FORCE/NO FORCE ROW LEVEL SECURITY
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
ATExecForceNoForceRowSecurity(Relation rel, bool force_rls)
|
||||||
|
{
|
||||||
|
Relation pg_class;
|
||||||
|
Oid relid;
|
||||||
|
HeapTuple tuple;
|
||||||
|
|
||||||
|
relid = RelationGetRelid(rel);
|
||||||
|
|
||||||
|
pg_class = heap_open(RelationRelationId, RowExclusiveLock);
|
||||||
|
|
||||||
|
tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
|
||||||
|
|
||||||
|
if (!HeapTupleIsValid(tuple))
|
||||||
|
elog(ERROR, "cache lookup failed for relation %u", relid);
|
||||||
|
|
||||||
|
((Form_pg_class) GETSTRUCT(tuple))->relforcerowsecurity = force_rls;
|
||||||
|
simple_heap_update(pg_class, &tuple->t_self, tuple);
|
||||||
|
|
||||||
|
/* keep catalog indexes current */
|
||||||
|
CatalogUpdateIndexes(pg_class, tuple);
|
||||||
|
|
||||||
|
heap_close(pg_class, RowExclusiveLock);
|
||||||
|
heap_freetuple(tuple);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ALTER FOREIGN TABLE <name> OPTIONS (...)
|
* ALTER FOREIGN TABLE <name> OPTIONS (...)
|
||||||
*/
|
*/
|
||||||
|
@ -2332,6 +2332,20 @@ alter_table_cmd:
|
|||||||
n->subtype = AT_DisableRowSecurity;
|
n->subtype = AT_DisableRowSecurity;
|
||||||
$$ = (Node *)n;
|
$$ = (Node *)n;
|
||||||
}
|
}
|
||||||
|
/* ALTER TABLE <name> FORCE ROW LEVEL SECURITY */
|
||||||
|
| FORCE ROW LEVEL SECURITY
|
||||||
|
{
|
||||||
|
AlterTableCmd *n = makeNode(AlterTableCmd);
|
||||||
|
n->subtype = AT_ForceRowSecurity;
|
||||||
|
$$ = (Node *)n;
|
||||||
|
}
|
||||||
|
/* ALTER TABLE <name> NO FORCE ROW LEVEL SECURITY */
|
||||||
|
| NO FORCE ROW LEVEL SECURITY
|
||||||
|
{
|
||||||
|
AlterTableCmd *n = makeNode(AlterTableCmd);
|
||||||
|
n->subtype = AT_NoForceRowSecurity;
|
||||||
|
$$ = (Node *)n;
|
||||||
|
}
|
||||||
| alter_generic_options
|
| alter_generic_options
|
||||||
{
|
{
|
||||||
AlterTableCmd *n = makeNode(AlterTableCmd);
|
AlterTableCmd *n = makeNode(AlterTableCmd);
|
||||||
|
@ -3014,7 +3014,8 @@ ri_PlanCheck(const char *querystr, int nargs, Oid *argtypes,
|
|||||||
/* Switch to proper UID to perform check as */
|
/* Switch to proper UID to perform check as */
|
||||||
GetUserIdAndSecContext(&save_userid, &save_sec_context);
|
GetUserIdAndSecContext(&save_userid, &save_sec_context);
|
||||||
SetUserIdAndSecContext(RelationGetForm(query_rel)->relowner,
|
SetUserIdAndSecContext(RelationGetForm(query_rel)->relowner,
|
||||||
save_sec_context | SECURITY_LOCAL_USERID_CHANGE);
|
save_sec_context | SECURITY_LOCAL_USERID_CHANGE |
|
||||||
|
SECURITY_NOFORCE_RLS);
|
||||||
|
|
||||||
/* Create the plan */
|
/* Create the plan */
|
||||||
qplan = SPI_prepare(querystr, nargs, argtypes);
|
qplan = SPI_prepare(querystr, nargs, argtypes);
|
||||||
@ -3134,7 +3135,8 @@ ri_PerformCheck(const RI_ConstraintInfo *riinfo,
|
|||||||
/* Switch to proper UID to perform check as */
|
/* Switch to proper UID to perform check as */
|
||||||
GetUserIdAndSecContext(&save_userid, &save_sec_context);
|
GetUserIdAndSecContext(&save_userid, &save_sec_context);
|
||||||
SetUserIdAndSecContext(RelationGetForm(query_rel)->relowner,
|
SetUserIdAndSecContext(RelationGetForm(query_rel)->relowner,
|
||||||
save_sec_context | SECURITY_LOCAL_USERID_CHANGE);
|
save_sec_context | SECURITY_LOCAL_USERID_CHANGE |
|
||||||
|
SECURITY_NOFORCE_RLS);
|
||||||
|
|
||||||
/* Finally we can run the query. */
|
/* Finally we can run the query. */
|
||||||
spi_result = SPI_execute_snapshot(qplan,
|
spi_result = SPI_execute_snapshot(qplan,
|
||||||
|
@ -341,7 +341,7 @@ GetAuthenticatedUserId(void)
|
|||||||
* GetUserIdAndSecContext/SetUserIdAndSecContext - get/set the current user ID
|
* GetUserIdAndSecContext/SetUserIdAndSecContext - get/set the current user ID
|
||||||
* and the SecurityRestrictionContext flags.
|
* and the SecurityRestrictionContext flags.
|
||||||
*
|
*
|
||||||
* Currently there are two valid bits in SecurityRestrictionContext:
|
* Currently there are three valid bits in SecurityRestrictionContext:
|
||||||
*
|
*
|
||||||
* SECURITY_LOCAL_USERID_CHANGE indicates that we are inside an operation
|
* SECURITY_LOCAL_USERID_CHANGE indicates that we are inside an operation
|
||||||
* that is temporarily changing CurrentUserId via these functions. This is
|
* that is temporarily changing CurrentUserId via these functions. This is
|
||||||
@ -359,6 +359,13 @@ GetAuthenticatedUserId(void)
|
|||||||
* where the called functions are really supposed to be side-effect-free
|
* where the called functions are really supposed to be side-effect-free
|
||||||
* anyway, such as VACUUM/ANALYZE/REINDEX.
|
* anyway, such as VACUUM/ANALYZE/REINDEX.
|
||||||
*
|
*
|
||||||
|
* SECURITY_NOFORCE_RLS indicates that we are inside an operation which should
|
||||||
|
* ignore the FORCE ROW LEVEL SECURITY per-table indication. This is used to
|
||||||
|
* ensure that FORCE RLS does not mistakenly break referential integrity
|
||||||
|
* checks. Note that this is intentionally only checked when running as the
|
||||||
|
* owner of the table (which should always be the case for referential
|
||||||
|
* integrity checks).
|
||||||
|
*
|
||||||
* Unlike GetUserId, GetUserIdAndSecContext does *not* Assert that the current
|
* Unlike GetUserId, GetUserIdAndSecContext does *not* Assert that the current
|
||||||
* value of CurrentUserId is valid; nor does SetUserIdAndSecContext require
|
* value of CurrentUserId is valid; nor does SetUserIdAndSecContext require
|
||||||
* the new value to be valid. In fact, these routines had better not
|
* the new value to be valid. In fact, these routines had better not
|
||||||
@ -401,6 +408,15 @@ InSecurityRestrictedOperation(void)
|
|||||||
return (SecurityRestrictionContext & SECURITY_RESTRICTED_OPERATION) != 0;
|
return (SecurityRestrictionContext & SECURITY_RESTRICTED_OPERATION) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* InNoForceRLSOperation - are we ignoring FORCE ROW LEVEL SECURITY ?
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
InNoForceRLSOperation(void)
|
||||||
|
{
|
||||||
|
return (SecurityRestrictionContext & SECURITY_NOFORCE_RLS) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* These are obsolete versions of Get/SetUserIdAndSecContext that are
|
* These are obsolete versions of Get/SetUserIdAndSecContext that are
|
||||||
|
@ -55,6 +55,7 @@ check_enable_rls(Oid relid, Oid checkAsUser, bool noError)
|
|||||||
HeapTuple tuple;
|
HeapTuple tuple;
|
||||||
Form_pg_class classform;
|
Form_pg_class classform;
|
||||||
bool relrowsecurity;
|
bool relrowsecurity;
|
||||||
|
bool relforcerowsecurity;
|
||||||
Oid user_id = checkAsUser ? checkAsUser : GetUserId();
|
Oid user_id = checkAsUser ? checkAsUser : GetUserId();
|
||||||
|
|
||||||
/* Nothing to do for built-in relations */
|
/* Nothing to do for built-in relations */
|
||||||
@ -68,6 +69,7 @@ check_enable_rls(Oid relid, Oid checkAsUser, bool noError)
|
|||||||
classform = (Form_pg_class) GETSTRUCT(tuple);
|
classform = (Form_pg_class) GETSTRUCT(tuple);
|
||||||
|
|
||||||
relrowsecurity = classform->relrowsecurity;
|
relrowsecurity = classform->relrowsecurity;
|
||||||
|
relforcerowsecurity = classform->relforcerowsecurity;
|
||||||
|
|
||||||
ReleaseSysCache(tuple);
|
ReleaseSysCache(tuple);
|
||||||
|
|
||||||
@ -76,14 +78,46 @@ check_enable_rls(Oid relid, Oid checkAsUser, bool noError)
|
|||||||
return RLS_NONE;
|
return RLS_NONE;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Table owners and BYPASSRLS users bypass RLS. Note that a superuser
|
* BYPASSRLS users always bypass RLS. Note that superusers are always
|
||||||
* qualifies as both. Return RLS_NONE_ENV to indicate that this decision
|
* considered to have BYPASSRLS.
|
||||||
* depends on the environment (in this case, the user_id).
|
*
|
||||||
|
* Return RLS_NONE_ENV to indicate that this decision depends on the
|
||||||
|
* environment (in this case, the user_id).
|
||||||
*/
|
*/
|
||||||
if (pg_class_ownercheck(relid, user_id) ||
|
if (has_bypassrls_privilege(user_id))
|
||||||
has_bypassrls_privilege(user_id))
|
|
||||||
return RLS_NONE_ENV;
|
return RLS_NONE_ENV;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Table owners generally bypass RLS, except if row_security=true and the
|
||||||
|
* table has been set (by an owner) to FORCE ROW SECURITY, and this is not
|
||||||
|
* a referential integrity check.
|
||||||
|
*
|
||||||
|
* Return RLS_NONE_ENV to indicate that this decision depends on the
|
||||||
|
* environment (in this case, the user_id).
|
||||||
|
*/
|
||||||
|
if (pg_class_ownercheck(relid, user_id))
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* If row_security=true and FORCE ROW LEVEL SECURITY has been set on
|
||||||
|
* the relation then we return RLS_ENABLED to indicate that RLS should
|
||||||
|
* still be applied. If we are in a SECURITY_NOFORCE_RLS context or if
|
||||||
|
* row_security=false then we return RLS_NONE_ENV.
|
||||||
|
*
|
||||||
|
* The SECURITY_NOFORCE_RLS indicates that we should not apply RLS even
|
||||||
|
* if the table has FORCE RLS set- IF the current user is the owner.
|
||||||
|
* This is specifically to ensure that referential integrity checks are
|
||||||
|
* able to still run correctly.
|
||||||
|
*
|
||||||
|
* This is intentionally only done after we have checked that the user
|
||||||
|
* is the table owner, which should always be the case for referential
|
||||||
|
* integrity checks.
|
||||||
|
*/
|
||||||
|
if (row_security && relforcerowsecurity && !InNoForceRLSOperation())
|
||||||
|
return RLS_ENABLED;
|
||||||
|
else
|
||||||
|
return RLS_NONE_ENV;
|
||||||
|
}
|
||||||
|
|
||||||
/* row_security GUC says to bypass RLS, but user lacks permission */
|
/* row_security GUC says to bypass RLS, but user lacks permission */
|
||||||
if (!row_security && !noError)
|
if (!row_security && !noError)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
|
@ -4521,6 +4521,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
|
|||||||
int i_relhasindex;
|
int i_relhasindex;
|
||||||
int i_relhasrules;
|
int i_relhasrules;
|
||||||
int i_relrowsec;
|
int i_relrowsec;
|
||||||
|
int i_relforcerowsec;
|
||||||
int i_relhasoids;
|
int i_relhasoids;
|
||||||
int i_relfrozenxid;
|
int i_relfrozenxid;
|
||||||
int i_relminmxid;
|
int i_relminmxid;
|
||||||
@ -4574,7 +4575,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
|
|||||||
"(%s c.relowner) AS rolname, "
|
"(%s c.relowner) AS rolname, "
|
||||||
"c.relchecks, c.relhastriggers, "
|
"c.relchecks, c.relhastriggers, "
|
||||||
"c.relhasindex, c.relhasrules, c.relhasoids, "
|
"c.relhasindex, c.relhasrules, c.relhasoids, "
|
||||||
"c.relrowsecurity, "
|
"c.relrowsecurity, c.relforcerowsecurity, "
|
||||||
"c.relfrozenxid, c.relminmxid, tc.oid AS toid, "
|
"c.relfrozenxid, c.relminmxid, tc.oid AS toid, "
|
||||||
"tc.relfrozenxid AS tfrozenxid, "
|
"tc.relfrozenxid AS tfrozenxid, "
|
||||||
"tc.relminmxid AS tminmxid, "
|
"tc.relminmxid AS tminmxid, "
|
||||||
@ -4616,6 +4617,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
|
|||||||
"c.relchecks, c.relhastriggers, "
|
"c.relchecks, c.relhastriggers, "
|
||||||
"c.relhasindex, c.relhasrules, c.relhasoids, "
|
"c.relhasindex, c.relhasrules, c.relhasoids, "
|
||||||
"'f'::bool AS relrowsecurity, "
|
"'f'::bool AS relrowsecurity, "
|
||||||
|
"'f'::bool AS relforcerowsecurity, "
|
||||||
"c.relfrozenxid, c.relminmxid, tc.oid AS toid, "
|
"c.relfrozenxid, c.relminmxid, tc.oid AS toid, "
|
||||||
"tc.relfrozenxid AS tfrozenxid, "
|
"tc.relfrozenxid AS tfrozenxid, "
|
||||||
"tc.relminmxid AS tminmxid, "
|
"tc.relminmxid AS tminmxid, "
|
||||||
@ -4657,6 +4659,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
|
|||||||
"c.relchecks, c.relhastriggers, "
|
"c.relchecks, c.relhastriggers, "
|
||||||
"c.relhasindex, c.relhasrules, c.relhasoids, "
|
"c.relhasindex, c.relhasrules, c.relhasoids, "
|
||||||
"'f'::bool AS relrowsecurity, "
|
"'f'::bool AS relrowsecurity, "
|
||||||
|
"'f'::bool AS relforcerowsecurity, "
|
||||||
"c.relfrozenxid, c.relminmxid, tc.oid AS toid, "
|
"c.relfrozenxid, c.relminmxid, tc.oid AS toid, "
|
||||||
"tc.relfrozenxid AS tfrozenxid, "
|
"tc.relfrozenxid AS tfrozenxid, "
|
||||||
"tc.relminmxid AS tminmxid, "
|
"tc.relminmxid AS tminmxid, "
|
||||||
@ -4698,6 +4701,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
|
|||||||
"c.relchecks, c.relhastriggers, "
|
"c.relchecks, c.relhastriggers, "
|
||||||
"c.relhasindex, c.relhasrules, c.relhasoids, "
|
"c.relhasindex, c.relhasrules, c.relhasoids, "
|
||||||
"'f'::bool AS relrowsecurity, "
|
"'f'::bool AS relrowsecurity, "
|
||||||
|
"'f'::bool AS relforcerowsecurity, "
|
||||||
"c.relfrozenxid, 0 AS relminmxid, tc.oid AS toid, "
|
"c.relfrozenxid, 0 AS relminmxid, tc.oid AS toid, "
|
||||||
"tc.relfrozenxid AS tfrozenxid, "
|
"tc.relfrozenxid AS tfrozenxid, "
|
||||||
"0 AS tminmxid, "
|
"0 AS tminmxid, "
|
||||||
@ -4737,6 +4741,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
|
|||||||
"c.relchecks, c.relhastriggers, "
|
"c.relchecks, c.relhastriggers, "
|
||||||
"c.relhasindex, c.relhasrules, c.relhasoids, "
|
"c.relhasindex, c.relhasrules, c.relhasoids, "
|
||||||
"'f'::bool AS relrowsecurity, "
|
"'f'::bool AS relrowsecurity, "
|
||||||
|
"'f'::bool AS relforcerowsecurity, "
|
||||||
"c.relfrozenxid, 0 AS relminmxid, tc.oid AS toid, "
|
"c.relfrozenxid, 0 AS relminmxid, tc.oid AS toid, "
|
||||||
"tc.relfrozenxid AS tfrozenxid, "
|
"tc.relfrozenxid AS tfrozenxid, "
|
||||||
"0 AS tminmxid, "
|
"0 AS tminmxid, "
|
||||||
@ -4775,6 +4780,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
|
|||||||
"c.relchecks, c.relhastriggers, "
|
"c.relchecks, c.relhastriggers, "
|
||||||
"c.relhasindex, c.relhasrules, c.relhasoids, "
|
"c.relhasindex, c.relhasrules, c.relhasoids, "
|
||||||
"'f'::bool AS relrowsecurity, "
|
"'f'::bool AS relrowsecurity, "
|
||||||
|
"'f'::bool AS relforcerowsecurity, "
|
||||||
"c.relfrozenxid, 0 AS relminmxid, tc.oid AS toid, "
|
"c.relfrozenxid, 0 AS relminmxid, tc.oid AS toid, "
|
||||||
"tc.relfrozenxid AS tfrozenxid, "
|
"tc.relfrozenxid AS tfrozenxid, "
|
||||||
"0 AS tminmxid, "
|
"0 AS tminmxid, "
|
||||||
@ -4813,6 +4819,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
|
|||||||
"c.relchecks, (c.reltriggers <> 0) AS relhastriggers, "
|
"c.relchecks, (c.reltriggers <> 0) AS relhastriggers, "
|
||||||
"c.relhasindex, c.relhasrules, c.relhasoids, "
|
"c.relhasindex, c.relhasrules, c.relhasoids, "
|
||||||
"'f'::bool AS relrowsecurity, "
|
"'f'::bool AS relrowsecurity, "
|
||||||
|
"'f'::bool AS relforcerowsecurity, "
|
||||||
"c.relfrozenxid, 0 AS relminmxid, tc.oid AS toid, "
|
"c.relfrozenxid, 0 AS relminmxid, tc.oid AS toid, "
|
||||||
"tc.relfrozenxid AS tfrozenxid, "
|
"tc.relfrozenxid AS tfrozenxid, "
|
||||||
"0 AS tminmxid, "
|
"0 AS tminmxid, "
|
||||||
@ -4851,6 +4858,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
|
|||||||
"relchecks, (reltriggers <> 0) AS relhastriggers, "
|
"relchecks, (reltriggers <> 0) AS relhastriggers, "
|
||||||
"relhasindex, relhasrules, relhasoids, "
|
"relhasindex, relhasrules, relhasoids, "
|
||||||
"'f'::bool AS relrowsecurity, "
|
"'f'::bool AS relrowsecurity, "
|
||||||
|
"'f'::bool AS relforcerowsecurity, "
|
||||||
"0 AS relfrozenxid, 0 AS relminmxid,"
|
"0 AS relfrozenxid, 0 AS relminmxid,"
|
||||||
"0 AS toid, "
|
"0 AS toid, "
|
||||||
"0 AS tfrozenxid, 0 AS tminmxid,"
|
"0 AS tfrozenxid, 0 AS tminmxid,"
|
||||||
@ -4888,6 +4896,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
|
|||||||
"relchecks, (reltriggers <> 0) AS relhastriggers, "
|
"relchecks, (reltriggers <> 0) AS relhastriggers, "
|
||||||
"relhasindex, relhasrules, relhasoids, "
|
"relhasindex, relhasrules, relhasoids, "
|
||||||
"'f'::bool AS relrowsecurity, "
|
"'f'::bool AS relrowsecurity, "
|
||||||
|
"'f'::bool AS relforcerowsecurity, "
|
||||||
"0 AS relfrozenxid, 0 AS relminmxid,"
|
"0 AS relfrozenxid, 0 AS relminmxid,"
|
||||||
"0 AS toid, "
|
"0 AS toid, "
|
||||||
"0 AS tfrozenxid, 0 AS tminmxid,"
|
"0 AS tfrozenxid, 0 AS tminmxid,"
|
||||||
@ -4921,6 +4930,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
|
|||||||
"relchecks, (reltriggers <> 0) AS relhastriggers, "
|
"relchecks, (reltriggers <> 0) AS relhastriggers, "
|
||||||
"relhasindex, relhasrules, relhasoids, "
|
"relhasindex, relhasrules, relhasoids, "
|
||||||
"'f'::bool AS relrowsecurity, "
|
"'f'::bool AS relrowsecurity, "
|
||||||
|
"'f'::bool AS relforcerowsecurity, "
|
||||||
"0 AS relfrozenxid, 0 AS relminmxid,"
|
"0 AS relfrozenxid, 0 AS relminmxid,"
|
||||||
"0 AS toid, "
|
"0 AS toid, "
|
||||||
"0 AS tfrozenxid, 0 AS tminmxid,"
|
"0 AS tfrozenxid, 0 AS tminmxid,"
|
||||||
@ -4949,6 +4959,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
|
|||||||
"relhasindex, relhasrules, "
|
"relhasindex, relhasrules, "
|
||||||
"'t'::bool AS relhasoids, "
|
"'t'::bool AS relhasoids, "
|
||||||
"'f'::bool AS relrowsecurity, "
|
"'f'::bool AS relrowsecurity, "
|
||||||
|
"'f'::bool AS relforcerowsecurity, "
|
||||||
"0 AS relfrozenxid, 0 AS relminmxid,"
|
"0 AS relfrozenxid, 0 AS relminmxid,"
|
||||||
"0 AS toid, "
|
"0 AS toid, "
|
||||||
"0 AS tfrozenxid, 0 AS tminmxid,"
|
"0 AS tfrozenxid, 0 AS tminmxid,"
|
||||||
@ -4987,6 +4998,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
|
|||||||
"relhasindex, relhasrules, "
|
"relhasindex, relhasrules, "
|
||||||
"'t'::bool AS relhasoids, "
|
"'t'::bool AS relhasoids, "
|
||||||
"'f'::bool AS relrowsecurity, "
|
"'f'::bool AS relrowsecurity, "
|
||||||
|
"'f'::bool AS relforcerowsecurity, "
|
||||||
"0 AS relfrozenxid, 0 AS relminmxid,"
|
"0 AS relfrozenxid, 0 AS relminmxid,"
|
||||||
"0 AS toid, "
|
"0 AS toid, "
|
||||||
"0 AS tfrozenxid, 0 AS tminmxid,"
|
"0 AS tfrozenxid, 0 AS tminmxid,"
|
||||||
@ -5035,6 +5047,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
|
|||||||
i_relhasindex = PQfnumber(res, "relhasindex");
|
i_relhasindex = PQfnumber(res, "relhasindex");
|
||||||
i_relhasrules = PQfnumber(res, "relhasrules");
|
i_relhasrules = PQfnumber(res, "relhasrules");
|
||||||
i_relrowsec = PQfnumber(res, "relrowsecurity");
|
i_relrowsec = PQfnumber(res, "relrowsecurity");
|
||||||
|
i_relforcerowsec = PQfnumber(res, "relforcerowsecurity");
|
||||||
i_relhasoids = PQfnumber(res, "relhasoids");
|
i_relhasoids = PQfnumber(res, "relhasoids");
|
||||||
i_relfrozenxid = PQfnumber(res, "relfrozenxid");
|
i_relfrozenxid = PQfnumber(res, "relfrozenxid");
|
||||||
i_relminmxid = PQfnumber(res, "relminmxid");
|
i_relminmxid = PQfnumber(res, "relminmxid");
|
||||||
@ -5087,6 +5100,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
|
|||||||
tblinfo[i].hasrules = (strcmp(PQgetvalue(res, i, i_relhasrules), "t") == 0);
|
tblinfo[i].hasrules = (strcmp(PQgetvalue(res, i, i_relhasrules), "t") == 0);
|
||||||
tblinfo[i].hastriggers = (strcmp(PQgetvalue(res, i, i_relhastriggers), "t") == 0);
|
tblinfo[i].hastriggers = (strcmp(PQgetvalue(res, i, i_relhastriggers), "t") == 0);
|
||||||
tblinfo[i].rowsec = (strcmp(PQgetvalue(res, i, i_relrowsec), "t") == 0);
|
tblinfo[i].rowsec = (strcmp(PQgetvalue(res, i, i_relrowsec), "t") == 0);
|
||||||
|
tblinfo[i].forcerowsec = (strcmp(PQgetvalue(res, i, i_relforcerowsec), "t") == 0);
|
||||||
tblinfo[i].hasoids = (strcmp(PQgetvalue(res, i, i_relhasoids), "t") == 0);
|
tblinfo[i].hasoids = (strcmp(PQgetvalue(res, i, i_relhasoids), "t") == 0);
|
||||||
tblinfo[i].relispopulated = (strcmp(PQgetvalue(res, i, i_relispopulated), "t") == 0);
|
tblinfo[i].relispopulated = (strcmp(PQgetvalue(res, i, i_relispopulated), "t") == 0);
|
||||||
tblinfo[i].relreplident = *(PQgetvalue(res, i, i_relreplident));
|
tblinfo[i].relreplident = *(PQgetvalue(res, i, i_relreplident));
|
||||||
@ -14353,6 +14367,10 @@ dumpTableSchema(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
|
|||||||
appendPQExpBuffer(q, "\nALTER TABLE ONLY %s SET WITH OIDS;\n",
|
appendPQExpBuffer(q, "\nALTER TABLE ONLY %s SET WITH OIDS;\n",
|
||||||
fmtId(tbinfo->dobj.name));
|
fmtId(tbinfo->dobj.name));
|
||||||
|
|
||||||
|
if (tbinfo->forcerowsec)
|
||||||
|
appendPQExpBuffer(q, "\nALTER TABLE ONLY %s FORCE ROW LEVEL SECURITY;\n",
|
||||||
|
fmtId(tbinfo->dobj.name));
|
||||||
|
|
||||||
if (dopt->binary_upgrade)
|
if (dopt->binary_upgrade)
|
||||||
binary_upgrade_extension_member(q, &tbinfo->dobj, labelq->data);
|
binary_upgrade_extension_member(q, &tbinfo->dobj, labelq->data);
|
||||||
|
|
||||||
|
@ -211,6 +211,7 @@ typedef struct _tableInfo
|
|||||||
bool hasrules; /* does it have any rules? */
|
bool hasrules; /* does it have any rules? */
|
||||||
bool hastriggers; /* does it have any triggers? */
|
bool hastriggers; /* does it have any triggers? */
|
||||||
bool rowsec; /* is row security enabled? */
|
bool rowsec; /* is row security enabled? */
|
||||||
|
bool forcerowsec; /* is row security forced? */
|
||||||
bool hasoids; /* does it have OIDs? */
|
bool hasoids; /* does it have OIDs? */
|
||||||
uint32 frozenxid; /* for restore frozen xid */
|
uint32 frozenxid; /* for restore frozen xid */
|
||||||
uint32 minmxid; /* for restore min multi xid */
|
uint32 minmxid; /* for restore min multi xid */
|
||||||
|
@ -1234,6 +1234,7 @@ describeOneTableDetails(const char *schemaname,
|
|||||||
bool hasrules;
|
bool hasrules;
|
||||||
bool hastriggers;
|
bool hastriggers;
|
||||||
bool rowsecurity;
|
bool rowsecurity;
|
||||||
|
bool forcerowsecurity;
|
||||||
bool hasoids;
|
bool hasoids;
|
||||||
Oid tablespace;
|
Oid tablespace;
|
||||||
char *reloptions;
|
char *reloptions;
|
||||||
@ -1259,8 +1260,8 @@ describeOneTableDetails(const char *schemaname,
|
|||||||
{
|
{
|
||||||
printfPQExpBuffer(&buf,
|
printfPQExpBuffer(&buf,
|
||||||
"SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, "
|
"SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, "
|
||||||
"c.relhastriggers, c.relrowsecurity, c.relhasoids, "
|
"c.relhastriggers, c.relrowsecurity, c.relforcerowsecurity, "
|
||||||
"%s, c.reltablespace, "
|
"c.relhasoids, %s, c.reltablespace, "
|
||||||
"CASE WHEN c.reloftype = 0 THEN '' ELSE c.reloftype::pg_catalog.regtype::pg_catalog.text END, "
|
"CASE WHEN c.reloftype = 0 THEN '' ELSE c.reloftype::pg_catalog.regtype::pg_catalog.text END, "
|
||||||
"c.relpersistence, c.relreplident\n"
|
"c.relpersistence, c.relreplident\n"
|
||||||
"FROM pg_catalog.pg_class c\n "
|
"FROM pg_catalog.pg_class c\n "
|
||||||
@ -1276,7 +1277,7 @@ describeOneTableDetails(const char *schemaname,
|
|||||||
{
|
{
|
||||||
printfPQExpBuffer(&buf,
|
printfPQExpBuffer(&buf,
|
||||||
"SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, "
|
"SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, "
|
||||||
"c.relhastriggers, false, c.relhasoids, "
|
"c.relhastriggers, false, false, c.relhasoids, "
|
||||||
"%s, c.reltablespace, "
|
"%s, c.reltablespace, "
|
||||||
"CASE WHEN c.reloftype = 0 THEN '' ELSE c.reloftype::pg_catalog.regtype::pg_catalog.text END, "
|
"CASE WHEN c.reloftype = 0 THEN '' ELSE c.reloftype::pg_catalog.regtype::pg_catalog.text END, "
|
||||||
"c.relpersistence, c.relreplident\n"
|
"c.relpersistence, c.relreplident\n"
|
||||||
@ -1293,7 +1294,7 @@ describeOneTableDetails(const char *schemaname,
|
|||||||
{
|
{
|
||||||
printfPQExpBuffer(&buf,
|
printfPQExpBuffer(&buf,
|
||||||
"SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, "
|
"SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, "
|
||||||
"c.relhastriggers, false, c.relhasoids, "
|
"c.relhastriggers, false, false, c.relhasoids, "
|
||||||
"%s, c.reltablespace, "
|
"%s, c.reltablespace, "
|
||||||
"CASE WHEN c.reloftype = 0 THEN '' ELSE c.reloftype::pg_catalog.regtype::pg_catalog.text END, "
|
"CASE WHEN c.reloftype = 0 THEN '' ELSE c.reloftype::pg_catalog.regtype::pg_catalog.text END, "
|
||||||
"c.relpersistence\n"
|
"c.relpersistence\n"
|
||||||
@ -1310,7 +1311,7 @@ describeOneTableDetails(const char *schemaname,
|
|||||||
{
|
{
|
||||||
printfPQExpBuffer(&buf,
|
printfPQExpBuffer(&buf,
|
||||||
"SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, "
|
"SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, "
|
||||||
"c.relhastriggers, false, c.relhasoids, "
|
"c.relhastriggers, false, false, c.relhasoids, "
|
||||||
"%s, c.reltablespace, "
|
"%s, c.reltablespace, "
|
||||||
"CASE WHEN c.reloftype = 0 THEN '' ELSE c.reloftype::pg_catalog.regtype::pg_catalog.text END\n"
|
"CASE WHEN c.reloftype = 0 THEN '' ELSE c.reloftype::pg_catalog.regtype::pg_catalog.text END\n"
|
||||||
"FROM pg_catalog.pg_class c\n "
|
"FROM pg_catalog.pg_class c\n "
|
||||||
@ -1326,7 +1327,7 @@ describeOneTableDetails(const char *schemaname,
|
|||||||
{
|
{
|
||||||
printfPQExpBuffer(&buf,
|
printfPQExpBuffer(&buf,
|
||||||
"SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, "
|
"SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, "
|
||||||
"c.relhastriggers, false, c.relhasoids, "
|
"c.relhastriggers, false, false, c.relhasoids, "
|
||||||
"%s, c.reltablespace\n"
|
"%s, c.reltablespace\n"
|
||||||
"FROM pg_catalog.pg_class c\n "
|
"FROM pg_catalog.pg_class c\n "
|
||||||
"LEFT JOIN pg_catalog.pg_class tc ON (c.reltoastrelid = tc.oid)\n"
|
"LEFT JOIN pg_catalog.pg_class tc ON (c.reltoastrelid = tc.oid)\n"
|
||||||
@ -1341,7 +1342,7 @@ describeOneTableDetails(const char *schemaname,
|
|||||||
{
|
{
|
||||||
printfPQExpBuffer(&buf,
|
printfPQExpBuffer(&buf,
|
||||||
"SELECT relchecks, relkind, relhasindex, relhasrules, "
|
"SELECT relchecks, relkind, relhasindex, relhasrules, "
|
||||||
"reltriggers <> 0, false, relhasoids, "
|
"reltriggers <> 0, false, false, relhasoids, "
|
||||||
"%s, reltablespace\n"
|
"%s, reltablespace\n"
|
||||||
"FROM pg_catalog.pg_class WHERE oid = '%s';",
|
"FROM pg_catalog.pg_class WHERE oid = '%s';",
|
||||||
(verbose ?
|
(verbose ?
|
||||||
@ -1352,7 +1353,7 @@ describeOneTableDetails(const char *schemaname,
|
|||||||
{
|
{
|
||||||
printfPQExpBuffer(&buf,
|
printfPQExpBuffer(&buf,
|
||||||
"SELECT relchecks, relkind, relhasindex, relhasrules, "
|
"SELECT relchecks, relkind, relhasindex, relhasrules, "
|
||||||
"reltriggers <> 0, false, relhasoids, "
|
"reltriggers <> 0, false, false, relhasoids, "
|
||||||
"'', reltablespace\n"
|
"'', reltablespace\n"
|
||||||
"FROM pg_catalog.pg_class WHERE oid = '%s';",
|
"FROM pg_catalog.pg_class WHERE oid = '%s';",
|
||||||
oid);
|
oid);
|
||||||
@ -1361,7 +1362,7 @@ describeOneTableDetails(const char *schemaname,
|
|||||||
{
|
{
|
||||||
printfPQExpBuffer(&buf,
|
printfPQExpBuffer(&buf,
|
||||||
"SELECT relchecks, relkind, relhasindex, relhasrules, "
|
"SELECT relchecks, relkind, relhasindex, relhasrules, "
|
||||||
"reltriggers <> 0, false, relhasoids, "
|
"reltriggers <> 0, false, false, relhasoids, "
|
||||||
"'', ''\n"
|
"'', ''\n"
|
||||||
"FROM pg_catalog.pg_class WHERE oid = '%s';",
|
"FROM pg_catalog.pg_class WHERE oid = '%s';",
|
||||||
oid);
|
oid);
|
||||||
@ -1385,18 +1386,19 @@ describeOneTableDetails(const char *schemaname,
|
|||||||
tableinfo.hasrules = strcmp(PQgetvalue(res, 0, 3), "t") == 0;
|
tableinfo.hasrules = strcmp(PQgetvalue(res, 0, 3), "t") == 0;
|
||||||
tableinfo.hastriggers = strcmp(PQgetvalue(res, 0, 4), "t") == 0;
|
tableinfo.hastriggers = strcmp(PQgetvalue(res, 0, 4), "t") == 0;
|
||||||
tableinfo.rowsecurity = strcmp(PQgetvalue(res, 0, 5), "t") == 0;
|
tableinfo.rowsecurity = strcmp(PQgetvalue(res, 0, 5), "t") == 0;
|
||||||
tableinfo.hasoids = strcmp(PQgetvalue(res, 0, 6), "t") == 0;
|
tableinfo.forcerowsecurity = strcmp(PQgetvalue(res, 0, 6), "t") == 0;
|
||||||
|
tableinfo.hasoids = strcmp(PQgetvalue(res, 0, 7), "t") == 0;
|
||||||
tableinfo.reloptions = (pset.sversion >= 80200) ?
|
tableinfo.reloptions = (pset.sversion >= 80200) ?
|
||||||
pg_strdup(PQgetvalue(res, 0, 7)) : NULL;
|
pg_strdup(PQgetvalue(res, 0, 8)) : NULL;
|
||||||
tableinfo.tablespace = (pset.sversion >= 80000) ?
|
tableinfo.tablespace = (pset.sversion >= 80000) ?
|
||||||
atooid(PQgetvalue(res, 0, 8)) : 0;
|
atooid(PQgetvalue(res, 0, 9)) : 0;
|
||||||
tableinfo.reloftype = (pset.sversion >= 90000 &&
|
tableinfo.reloftype = (pset.sversion >= 90000 &&
|
||||||
strcmp(PQgetvalue(res, 0, 9), "") != 0) ?
|
strcmp(PQgetvalue(res, 0, 10), "") != 0) ?
|
||||||
pg_strdup(PQgetvalue(res, 0, 9)) : NULL;
|
pg_strdup(PQgetvalue(res, 0, 10)) : NULL;
|
||||||
tableinfo.relpersistence = (pset.sversion >= 90100) ?
|
tableinfo.relpersistence = (pset.sversion >= 90100) ?
|
||||||
*(PQgetvalue(res, 0, 10)) : 0;
|
*(PQgetvalue(res, 0, 11)) : 0;
|
||||||
tableinfo.relreplident = (pset.sversion >= 90400) ?
|
tableinfo.relreplident = (pset.sversion >= 90400) ?
|
||||||
*(PQgetvalue(res, 0, 11)) : 'd';
|
*(PQgetvalue(res, 0, 12)) : 'd';
|
||||||
PQclear(res);
|
PQclear(res);
|
||||||
res = NULL;
|
res = NULL;
|
||||||
|
|
||||||
@ -2057,12 +2059,18 @@ describeOneTableDetails(const char *schemaname,
|
|||||||
* there aren't policies, or RLS isn't enabled but there are
|
* there aren't policies, or RLS isn't enabled but there are
|
||||||
* policies
|
* policies
|
||||||
*/
|
*/
|
||||||
if (tableinfo.rowsecurity && tuples > 0)
|
if (tableinfo.rowsecurity && !tableinfo.forcerowsecurity && tuples > 0)
|
||||||
printTableAddFooter(&cont, _("Policies:"));
|
printTableAddFooter(&cont, _("Policies:"));
|
||||||
|
|
||||||
if (tableinfo.rowsecurity && tuples == 0)
|
if (tableinfo.rowsecurity && tableinfo.forcerowsecurity && tuples > 0)
|
||||||
|
printTableAddFooter(&cont, _("Policies (Forced Row Security Enabled):"));
|
||||||
|
|
||||||
|
if (tableinfo.rowsecurity && !tableinfo.forcerowsecurity && tuples == 0)
|
||||||
printTableAddFooter(&cont, _("Policies (Row Security Enabled): (None)"));
|
printTableAddFooter(&cont, _("Policies (Row Security Enabled): (None)"));
|
||||||
|
|
||||||
|
if (tableinfo.rowsecurity && tableinfo.forcerowsecurity && tuples == 0)
|
||||||
|
printTableAddFooter(&cont, _("Policies (Forced Row Security Enabled): (None)"));
|
||||||
|
|
||||||
if (!tableinfo.rowsecurity && tuples > 0)
|
if (!tableinfo.rowsecurity && tuples > 0)
|
||||||
printTableAddFooter(&cont, _("Policies (Row Security Disabled):"));
|
printTableAddFooter(&cont, _("Policies (Row Security Disabled):"));
|
||||||
|
|
||||||
|
@ -53,6 +53,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/* yyyymmddN */
|
/* yyyymmddN */
|
||||||
#define CATALOG_VERSION_NO 201508061
|
#define CATALOG_VERSION_NO 201510041
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -66,6 +66,7 @@ CATALOG(pg_class,1259) BKI_BOOTSTRAP BKI_ROWTYPE_OID(83) BKI_SCHEMA_MACRO
|
|||||||
bool relhastriggers; /* has (or has had) any TRIGGERs */
|
bool relhastriggers; /* has (or has had) any TRIGGERs */
|
||||||
bool relhassubclass; /* has (or has had) derived classes */
|
bool relhassubclass; /* has (or has had) derived classes */
|
||||||
bool relrowsecurity; /* row security is enabled or not */
|
bool relrowsecurity; /* row security is enabled or not */
|
||||||
|
bool relforcerowsecurity; /* row security forced for owners or not */
|
||||||
bool relispopulated; /* matview currently holds query results */
|
bool relispopulated; /* matview currently holds query results */
|
||||||
char relreplident; /* see REPLICA_IDENTITY_xxx constants */
|
char relreplident; /* see REPLICA_IDENTITY_xxx constants */
|
||||||
TransactionId relfrozenxid; /* all Xids < this are frozen in this rel */
|
TransactionId relfrozenxid; /* all Xids < this are frozen in this rel */
|
||||||
@ -95,37 +96,38 @@ typedef FormData_pg_class *Form_pg_class;
|
|||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define Natts_pg_class 30
|
#define Natts_pg_class 31
|
||||||
#define Anum_pg_class_relname 1
|
#define Anum_pg_class_relname 1
|
||||||
#define Anum_pg_class_relnamespace 2
|
#define Anum_pg_class_relnamespace 2
|
||||||
#define Anum_pg_class_reltype 3
|
#define Anum_pg_class_reltype 3
|
||||||
#define Anum_pg_class_reloftype 4
|
#define Anum_pg_class_reloftype 4
|
||||||
#define Anum_pg_class_relowner 5
|
#define Anum_pg_class_relowner 5
|
||||||
#define Anum_pg_class_relam 6
|
#define Anum_pg_class_relam 6
|
||||||
#define Anum_pg_class_relfilenode 7
|
#define Anum_pg_class_relfilenode 7
|
||||||
#define Anum_pg_class_reltablespace 8
|
#define Anum_pg_class_reltablespace 8
|
||||||
#define Anum_pg_class_relpages 9
|
#define Anum_pg_class_relpages 9
|
||||||
#define Anum_pg_class_reltuples 10
|
#define Anum_pg_class_reltuples 10
|
||||||
#define Anum_pg_class_relallvisible 11
|
#define Anum_pg_class_relallvisible 11
|
||||||
#define Anum_pg_class_reltoastrelid 12
|
#define Anum_pg_class_reltoastrelid 12
|
||||||
#define Anum_pg_class_relhasindex 13
|
#define Anum_pg_class_relhasindex 13
|
||||||
#define Anum_pg_class_relisshared 14
|
#define Anum_pg_class_relisshared 14
|
||||||
#define Anum_pg_class_relpersistence 15
|
#define Anum_pg_class_relpersistence 15
|
||||||
#define Anum_pg_class_relkind 16
|
#define Anum_pg_class_relkind 16
|
||||||
#define Anum_pg_class_relnatts 17
|
#define Anum_pg_class_relnatts 17
|
||||||
#define Anum_pg_class_relchecks 18
|
#define Anum_pg_class_relchecks 18
|
||||||
#define Anum_pg_class_relhasoids 19
|
#define Anum_pg_class_relhasoids 19
|
||||||
#define Anum_pg_class_relhaspkey 20
|
#define Anum_pg_class_relhaspkey 20
|
||||||
#define Anum_pg_class_relhasrules 21
|
#define Anum_pg_class_relhasrules 21
|
||||||
#define Anum_pg_class_relhastriggers 22
|
#define Anum_pg_class_relhastriggers 22
|
||||||
#define Anum_pg_class_relhassubclass 23
|
#define Anum_pg_class_relhassubclass 23
|
||||||
#define Anum_pg_class_relrowsecurity 24
|
#define Anum_pg_class_relrowsecurity 24
|
||||||
#define Anum_pg_class_relispopulated 25
|
#define Anum_pg_class_relforcerowsecurity 25
|
||||||
#define Anum_pg_class_relreplident 26
|
#define Anum_pg_class_relispopulated 26
|
||||||
#define Anum_pg_class_relfrozenxid 27
|
#define Anum_pg_class_relreplident 27
|
||||||
#define Anum_pg_class_relminmxid 28
|
#define Anum_pg_class_relfrozenxid 28
|
||||||
#define Anum_pg_class_relacl 29
|
#define Anum_pg_class_relminmxid 29
|
||||||
#define Anum_pg_class_reloptions 30
|
#define Anum_pg_class_relacl 30
|
||||||
|
#define Anum_pg_class_reloptions 31
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* initial contents of pg_class
|
* initial contents of pg_class
|
||||||
@ -140,13 +142,13 @@ typedef FormData_pg_class *Form_pg_class;
|
|||||||
* Note: "3" in the relfrozenxid column stands for FirstNormalTransactionId;
|
* Note: "3" in the relfrozenxid column stands for FirstNormalTransactionId;
|
||||||
* similarly, "1" in relminmxid stands for FirstMultiXactId
|
* similarly, "1" in relminmxid stands for FirstMultiXactId
|
||||||
*/
|
*/
|
||||||
DATA(insert OID = 1247 ( pg_type PGNSP 71 0 PGUID 0 0 0 0 0 0 0 f f p r 30 0 t f f f f f t n 3 1 _null_ _null_ ));
|
DATA(insert OID = 1247 ( pg_type PGNSP 71 0 PGUID 0 0 0 0 0 0 0 f f p r 30 0 t f f f f f f t n 3 1 _null_ _null_ ));
|
||||||
DESCR("");
|
DESCR("");
|
||||||
DATA(insert OID = 1249 ( pg_attribute PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f p r 21 0 f f f f f f t n 3 1 _null_ _null_ ));
|
DATA(insert OID = 1249 ( pg_attribute PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f p r 21 0 f f f f f f f t n 3 1 _null_ _null_ ));
|
||||||
DESCR("");
|
DESCR("");
|
||||||
DATA(insert OID = 1255 ( pg_proc PGNSP 81 0 PGUID 0 0 0 0 0 0 0 f f p r 28 0 t f f f f f t n 3 1 _null_ _null_ ));
|
DATA(insert OID = 1255 ( pg_proc PGNSP 81 0 PGUID 0 0 0 0 0 0 0 f f p r 28 0 t f f f f f f t n 3 1 _null_ _null_ ));
|
||||||
DESCR("");
|
DESCR("");
|
||||||
DATA(insert OID = 1259 ( pg_class PGNSP 83 0 PGUID 0 0 0 0 0 0 0 f f p r 30 0 t f f f f f t n 3 1 _null_ _null_ ));
|
DATA(insert OID = 1259 ( pg_class PGNSP 83 0 PGUID 0 0 0 0 0 0 0 f f p r 31 0 t f f f f f f t n 3 1 _null_ _null_ ));
|
||||||
DESCR("");
|
DESCR("");
|
||||||
|
|
||||||
|
|
||||||
|
@ -287,6 +287,7 @@ extern int trace_recovery(int trace_level);
|
|||||||
/* flags to be OR'd to form sec_context */
|
/* flags to be OR'd to form sec_context */
|
||||||
#define SECURITY_LOCAL_USERID_CHANGE 0x0001
|
#define SECURITY_LOCAL_USERID_CHANGE 0x0001
|
||||||
#define SECURITY_RESTRICTED_OPERATION 0x0002
|
#define SECURITY_RESTRICTED_OPERATION 0x0002
|
||||||
|
#define SECURITY_NOFORCE_RLS 0x0004
|
||||||
|
|
||||||
extern char *DatabasePath;
|
extern char *DatabasePath;
|
||||||
|
|
||||||
@ -305,6 +306,7 @@ extern void GetUserIdAndSecContext(Oid *userid, int *sec_context);
|
|||||||
extern void SetUserIdAndSecContext(Oid userid, int sec_context);
|
extern void SetUserIdAndSecContext(Oid userid, int sec_context);
|
||||||
extern bool InLocalUserIdChange(void);
|
extern bool InLocalUserIdChange(void);
|
||||||
extern bool InSecurityRestrictedOperation(void);
|
extern bool InSecurityRestrictedOperation(void);
|
||||||
|
extern bool InNoForceRLSOperation(void);
|
||||||
extern void GetUserIdAndContext(Oid *userid, bool *sec_def_context);
|
extern void GetUserIdAndContext(Oid *userid, bool *sec_def_context);
|
||||||
extern void SetUserIdAndContext(Oid userid, bool sec_def_context);
|
extern void SetUserIdAndContext(Oid userid, bool sec_def_context);
|
||||||
extern void InitializeSessionUserId(const char *rolename, Oid useroid);
|
extern void InitializeSessionUserId(const char *rolename, Oid useroid);
|
||||||
|
@ -1514,6 +1514,8 @@ typedef enum AlterTableType
|
|||||||
AT_ReplicaIdentity, /* REPLICA IDENTITY */
|
AT_ReplicaIdentity, /* REPLICA IDENTITY */
|
||||||
AT_EnableRowSecurity, /* ENABLE ROW SECURITY */
|
AT_EnableRowSecurity, /* ENABLE ROW SECURITY */
|
||||||
AT_DisableRowSecurity, /* DISABLE ROW SECURITY */
|
AT_DisableRowSecurity, /* DISABLE ROW SECURITY */
|
||||||
|
AT_ForceRowSecurity, /* FORCE ROW SECURITY */
|
||||||
|
AT_NoForceRowSecurity, /* NO FORCE ROW SECURITY */
|
||||||
AT_GenericOptions /* OPTIONS (...) */
|
AT_GenericOptions /* OPTIONS (...) */
|
||||||
} AlterTableType;
|
} AlterTableType;
|
||||||
|
|
||||||
|
@ -275,6 +275,12 @@ get_altertable_subcmdtypes(PG_FUNCTION_ARGS)
|
|||||||
case AT_DisableRowSecurity:
|
case AT_DisableRowSecurity:
|
||||||
strtype = "DISABLE ROW SECURITY";
|
strtype = "DISABLE ROW SECURITY";
|
||||||
break;
|
break;
|
||||||
|
case AT_ForceRowSecurity:
|
||||||
|
strtype = "FORCE ROW SECURITY";
|
||||||
|
break;
|
||||||
|
case AT_NoForceRowSecurity:
|
||||||
|
strtype = "NO FORCE ROW SECURITY";
|
||||||
|
break;
|
||||||
case AT_GenericOptions:
|
case AT_GenericOptions:
|
||||||
strtype = "SET OPTIONS";
|
strtype = "SET OPTIONS";
|
||||||
break;
|
break;
|
||||||
|
@ -3009,6 +3009,155 @@ SET SESSION AUTHORIZATION rls_regress_user0;
|
|||||||
DROP TABLE r1;
|
DROP TABLE r1;
|
||||||
DROP TABLE r2;
|
DROP TABLE r2;
|
||||||
--
|
--
|
||||||
|
-- FORCE ROW LEVEL SECURITY applies RLS to owners but
|
||||||
|
-- only when row_security = on
|
||||||
|
--
|
||||||
|
SET SESSION AUTHORIZATION rls_regress_user0;
|
||||||
|
SET row_security = on;
|
||||||
|
CREATE TABLE r1 (a int);
|
||||||
|
INSERT INTO r1 VALUES (10), (20);
|
||||||
|
CREATE POLICY p1 ON r1 USING (false);
|
||||||
|
ALTER TABLE r1 ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE r1 FORCE ROW LEVEL SECURITY;
|
||||||
|
-- No error, but no rows
|
||||||
|
TABLE r1;
|
||||||
|
a
|
||||||
|
---
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
|
-- RLS error
|
||||||
|
INSERT INTO r1 VALUES (1);
|
||||||
|
ERROR: new row violates row level security policy for "r1"
|
||||||
|
-- No error (unable to see any rows to update)
|
||||||
|
UPDATE r1 SET a = 1;
|
||||||
|
TABLE r1;
|
||||||
|
a
|
||||||
|
---
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
|
-- No error (unable to see any rows to delete)
|
||||||
|
DELETE FROM r1;
|
||||||
|
TABLE r1;
|
||||||
|
a
|
||||||
|
---
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
|
SET row_security = off;
|
||||||
|
-- Shows all rows
|
||||||
|
TABLE r1;
|
||||||
|
a
|
||||||
|
----
|
||||||
|
10
|
||||||
|
20
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
-- Update all rows
|
||||||
|
UPDATE r1 SET a = 1;
|
||||||
|
TABLE r1;
|
||||||
|
a
|
||||||
|
---
|
||||||
|
1
|
||||||
|
1
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
-- Delete all rows
|
||||||
|
DELETE FROM r1;
|
||||||
|
TABLE r1;
|
||||||
|
a
|
||||||
|
---
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
|
DROP TABLE r1;
|
||||||
|
--
|
||||||
|
-- FORCE ROW LEVEL SECURITY does not break RI
|
||||||
|
--
|
||||||
|
SET SESSION AUTHORIZATION rls_regress_user0;
|
||||||
|
SET row_security = on;
|
||||||
|
CREATE TABLE r1 (a int PRIMARY KEY);
|
||||||
|
CREATE TABLE r2 (a int REFERENCES r1);
|
||||||
|
INSERT INTO r1 VALUES (10), (20);
|
||||||
|
INSERT INTO r2 VALUES (10), (20);
|
||||||
|
-- Create policies on r2 which prevent the
|
||||||
|
-- owner from seeing any rows, but RI should
|
||||||
|
-- still see them.
|
||||||
|
CREATE POLICY p1 ON r2 USING (false);
|
||||||
|
ALTER TABLE r2 ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE r2 FORCE ROW LEVEL SECURITY;
|
||||||
|
-- Errors due to rows in r2
|
||||||
|
DELETE FROM r1;
|
||||||
|
ERROR: update or delete on table "r1" violates foreign key constraint "r2_a_fkey" on table "r2"
|
||||||
|
DETAIL: Key (a)=(10) is still referenced from table "r2".
|
||||||
|
-- Reset r2 to no-RLS
|
||||||
|
DROP POLICY p1 ON r2;
|
||||||
|
ALTER TABLE r2 NO FORCE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE r2 DISABLE ROW LEVEL SECURITY;
|
||||||
|
-- clean out r2 for INSERT test below
|
||||||
|
DELETE FROM r2;
|
||||||
|
-- Change r1 to not allow rows to be seen
|
||||||
|
CREATE POLICY p1 ON r1 USING (false);
|
||||||
|
ALTER TABLE r1 ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE r1 FORCE ROW LEVEL SECURITY;
|
||||||
|
-- No rows seen
|
||||||
|
TABLE r1;
|
||||||
|
a
|
||||||
|
---
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
|
-- No error, RI still sees that row exists in r1
|
||||||
|
INSERT INTO r2 VALUES (10);
|
||||||
|
DROP TABLE r2;
|
||||||
|
DROP TABLE r1;
|
||||||
|
-- Ensure cascaded DELETE works
|
||||||
|
CREATE TABLE r1 (a int PRIMARY KEY);
|
||||||
|
CREATE TABLE r2 (a int REFERENCES r1 ON DELETE CASCADE);
|
||||||
|
INSERT INTO r1 VALUES (10), (20);
|
||||||
|
INSERT INTO r2 VALUES (10), (20);
|
||||||
|
-- Create policies on r2 which prevent the
|
||||||
|
-- owner from seeing any rows, but RI should
|
||||||
|
-- still see them.
|
||||||
|
CREATE POLICY p1 ON r2 USING (false);
|
||||||
|
ALTER TABLE r2 ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE r2 FORCE ROW LEVEL SECURITY;
|
||||||
|
-- Deletes all records from both
|
||||||
|
DELETE FROM r1;
|
||||||
|
-- Remove FORCE from r2
|
||||||
|
ALTER TABLE r2 NO FORCE ROW LEVEL SECURITY;
|
||||||
|
-- As owner, we now bypass RLS
|
||||||
|
-- verify no rows in r2 now
|
||||||
|
TABLE r2;
|
||||||
|
a
|
||||||
|
---
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
|
DROP TABLE r2;
|
||||||
|
DROP TABLE r1;
|
||||||
|
-- Ensure cascaded UPDATE works
|
||||||
|
CREATE TABLE r1 (a int PRIMARY KEY);
|
||||||
|
CREATE TABLE r2 (a int REFERENCES r1 ON UPDATE CASCADE);
|
||||||
|
INSERT INTO r1 VALUES (10), (20);
|
||||||
|
INSERT INTO r2 VALUES (10), (20);
|
||||||
|
-- Create policies on r2 which prevent the
|
||||||
|
-- owner from seeing any rows, but RI should
|
||||||
|
-- still see them.
|
||||||
|
CREATE POLICY p1 ON r2 USING (false);
|
||||||
|
ALTER TABLE r2 ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE r2 FORCE ROW LEVEL SECURITY;
|
||||||
|
-- Updates records in both
|
||||||
|
UPDATE r1 SET a = a+5;
|
||||||
|
-- Remove FORCE from r2
|
||||||
|
ALTER TABLE r2 NO FORCE ROW LEVEL SECURITY;
|
||||||
|
-- As owner, we now bypass RLS
|
||||||
|
-- verify records in r2 updated
|
||||||
|
TABLE r2;
|
||||||
|
a
|
||||||
|
----
|
||||||
|
15
|
||||||
|
25
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
DROP TABLE r2;
|
||||||
|
DROP TABLE r1;
|
||||||
|
--
|
||||||
-- Clean up objects
|
-- Clean up objects
|
||||||
--
|
--
|
||||||
RESET SESSION AUTHORIZATION;
|
RESET SESSION AUTHORIZATION;
|
||||||
@ -3031,3 +3180,10 @@ CREATE POLICY p1 ON rls_tbl USING (c1 > 5);
|
|||||||
CREATE POLICY p2 ON rls_tbl FOR SELECT USING (c1 <= 3);
|
CREATE POLICY p2 ON rls_tbl FOR SELECT USING (c1 <= 3);
|
||||||
CREATE POLICY p3 ON rls_tbl FOR UPDATE USING (c1 <= 3) WITH CHECK (c1 > 5);
|
CREATE POLICY p3 ON rls_tbl FOR UPDATE USING (c1 <= 3) WITH CHECK (c1 > 5);
|
||||||
CREATE POLICY p4 ON rls_tbl FOR DELETE USING (c1 <= 3);
|
CREATE POLICY p4 ON rls_tbl FOR DELETE USING (c1 <= 3);
|
||||||
|
CREATE TABLE rls_tbl_force (c1 int);
|
||||||
|
ALTER TABLE rls_tbl_force ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE rls_tbl_force FORCE ROW LEVEL SECURITY;
|
||||||
|
CREATE POLICY p1 ON rls_tbl_force USING (c1 = 5) WITH CHECK (c1 < 5);
|
||||||
|
CREATE POLICY p2 ON rls_tbl_force FOR SELECT USING (c1 = 8);
|
||||||
|
CREATE POLICY p3 ON rls_tbl_force FOR UPDATE USING (c1 = 8) WITH CHECK (c1 >= 5);
|
||||||
|
CREATE POLICY p4 ON rls_tbl_force FOR DELETE USING (c1 = 8);
|
||||||
|
@ -672,6 +672,7 @@ SELECT user_relns() AS user_relns
|
|||||||
real_city
|
real_city
|
||||||
reltime_tbl
|
reltime_tbl
|
||||||
rls_tbl
|
rls_tbl
|
||||||
|
rls_tbl_force
|
||||||
road
|
road
|
||||||
shighway
|
shighway
|
||||||
slow_emp4000
|
slow_emp4000
|
||||||
@ -709,7 +710,7 @@ SELECT user_relns() AS user_relns
|
|||||||
tvvmv
|
tvvmv
|
||||||
varchar_tbl
|
varchar_tbl
|
||||||
xacttest
|
xacttest
|
||||||
(131 rows)
|
(132 rows)
|
||||||
|
|
||||||
SELECT name(equipment(hobby_construct(text 'skywalking', text 'mer')));
|
SELECT name(equipment(hobby_construct(text 'skywalking', text 'mer')));
|
||||||
name
|
name
|
||||||
|
@ -1288,6 +1288,141 @@ SET SESSION AUTHORIZATION rls_regress_user0;
|
|||||||
DROP TABLE r1;
|
DROP TABLE r1;
|
||||||
DROP TABLE r2;
|
DROP TABLE r2;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- FORCE ROW LEVEL SECURITY applies RLS to owners but
|
||||||
|
-- only when row_security = on
|
||||||
|
--
|
||||||
|
SET SESSION AUTHORIZATION rls_regress_user0;
|
||||||
|
SET row_security = on;
|
||||||
|
CREATE TABLE r1 (a int);
|
||||||
|
INSERT INTO r1 VALUES (10), (20);
|
||||||
|
|
||||||
|
CREATE POLICY p1 ON r1 USING (false);
|
||||||
|
ALTER TABLE r1 ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE r1 FORCE ROW LEVEL SECURITY;
|
||||||
|
|
||||||
|
-- No error, but no rows
|
||||||
|
TABLE r1;
|
||||||
|
|
||||||
|
-- RLS error
|
||||||
|
INSERT INTO r1 VALUES (1);
|
||||||
|
|
||||||
|
-- No error (unable to see any rows to update)
|
||||||
|
UPDATE r1 SET a = 1;
|
||||||
|
TABLE r1;
|
||||||
|
|
||||||
|
-- No error (unable to see any rows to delete)
|
||||||
|
DELETE FROM r1;
|
||||||
|
TABLE r1;
|
||||||
|
|
||||||
|
SET row_security = off;
|
||||||
|
-- Shows all rows
|
||||||
|
TABLE r1;
|
||||||
|
|
||||||
|
-- Update all rows
|
||||||
|
UPDATE r1 SET a = 1;
|
||||||
|
TABLE r1;
|
||||||
|
|
||||||
|
-- Delete all rows
|
||||||
|
DELETE FROM r1;
|
||||||
|
TABLE r1;
|
||||||
|
|
||||||
|
DROP TABLE r1;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- FORCE ROW LEVEL SECURITY does not break RI
|
||||||
|
--
|
||||||
|
SET SESSION AUTHORIZATION rls_regress_user0;
|
||||||
|
SET row_security = on;
|
||||||
|
CREATE TABLE r1 (a int PRIMARY KEY);
|
||||||
|
CREATE TABLE r2 (a int REFERENCES r1);
|
||||||
|
INSERT INTO r1 VALUES (10), (20);
|
||||||
|
INSERT INTO r2 VALUES (10), (20);
|
||||||
|
|
||||||
|
-- Create policies on r2 which prevent the
|
||||||
|
-- owner from seeing any rows, but RI should
|
||||||
|
-- still see them.
|
||||||
|
CREATE POLICY p1 ON r2 USING (false);
|
||||||
|
ALTER TABLE r2 ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE r2 FORCE ROW LEVEL SECURITY;
|
||||||
|
|
||||||
|
-- Errors due to rows in r2
|
||||||
|
DELETE FROM r1;
|
||||||
|
|
||||||
|
-- Reset r2 to no-RLS
|
||||||
|
DROP POLICY p1 ON r2;
|
||||||
|
ALTER TABLE r2 NO FORCE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE r2 DISABLE ROW LEVEL SECURITY;
|
||||||
|
|
||||||
|
-- clean out r2 for INSERT test below
|
||||||
|
DELETE FROM r2;
|
||||||
|
|
||||||
|
-- Change r1 to not allow rows to be seen
|
||||||
|
CREATE POLICY p1 ON r1 USING (false);
|
||||||
|
ALTER TABLE r1 ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE r1 FORCE ROW LEVEL SECURITY;
|
||||||
|
|
||||||
|
-- No rows seen
|
||||||
|
TABLE r1;
|
||||||
|
|
||||||
|
-- No error, RI still sees that row exists in r1
|
||||||
|
INSERT INTO r2 VALUES (10);
|
||||||
|
|
||||||
|
DROP TABLE r2;
|
||||||
|
DROP TABLE r1;
|
||||||
|
|
||||||
|
-- Ensure cascaded DELETE works
|
||||||
|
CREATE TABLE r1 (a int PRIMARY KEY);
|
||||||
|
CREATE TABLE r2 (a int REFERENCES r1 ON DELETE CASCADE);
|
||||||
|
INSERT INTO r1 VALUES (10), (20);
|
||||||
|
INSERT INTO r2 VALUES (10), (20);
|
||||||
|
|
||||||
|
-- Create policies on r2 which prevent the
|
||||||
|
-- owner from seeing any rows, but RI should
|
||||||
|
-- still see them.
|
||||||
|
CREATE POLICY p1 ON r2 USING (false);
|
||||||
|
ALTER TABLE r2 ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE r2 FORCE ROW LEVEL SECURITY;
|
||||||
|
|
||||||
|
-- Deletes all records from both
|
||||||
|
DELETE FROM r1;
|
||||||
|
|
||||||
|
-- Remove FORCE from r2
|
||||||
|
ALTER TABLE r2 NO FORCE ROW LEVEL SECURITY;
|
||||||
|
|
||||||
|
-- As owner, we now bypass RLS
|
||||||
|
-- verify no rows in r2 now
|
||||||
|
TABLE r2;
|
||||||
|
|
||||||
|
DROP TABLE r2;
|
||||||
|
DROP TABLE r1;
|
||||||
|
|
||||||
|
-- Ensure cascaded UPDATE works
|
||||||
|
CREATE TABLE r1 (a int PRIMARY KEY);
|
||||||
|
CREATE TABLE r2 (a int REFERENCES r1 ON UPDATE CASCADE);
|
||||||
|
INSERT INTO r1 VALUES (10), (20);
|
||||||
|
INSERT INTO r2 VALUES (10), (20);
|
||||||
|
|
||||||
|
-- Create policies on r2 which prevent the
|
||||||
|
-- owner from seeing any rows, but RI should
|
||||||
|
-- still see them.
|
||||||
|
CREATE POLICY p1 ON r2 USING (false);
|
||||||
|
ALTER TABLE r2 ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE r2 FORCE ROW LEVEL SECURITY;
|
||||||
|
|
||||||
|
-- Updates records in both
|
||||||
|
UPDATE r1 SET a = a+5;
|
||||||
|
|
||||||
|
-- Remove FORCE from r2
|
||||||
|
ALTER TABLE r2 NO FORCE ROW LEVEL SECURITY;
|
||||||
|
|
||||||
|
-- As owner, we now bypass RLS
|
||||||
|
-- verify records in r2 updated
|
||||||
|
TABLE r2;
|
||||||
|
|
||||||
|
DROP TABLE r2;
|
||||||
|
DROP TABLE r1;
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Clean up objects
|
-- Clean up objects
|
||||||
--
|
--
|
||||||
@ -1315,3 +1450,11 @@ CREATE POLICY p1 ON rls_tbl USING (c1 > 5);
|
|||||||
CREATE POLICY p2 ON rls_tbl FOR SELECT USING (c1 <= 3);
|
CREATE POLICY p2 ON rls_tbl FOR SELECT USING (c1 <= 3);
|
||||||
CREATE POLICY p3 ON rls_tbl FOR UPDATE USING (c1 <= 3) WITH CHECK (c1 > 5);
|
CREATE POLICY p3 ON rls_tbl FOR UPDATE USING (c1 <= 3) WITH CHECK (c1 > 5);
|
||||||
CREATE POLICY p4 ON rls_tbl FOR DELETE USING (c1 <= 3);
|
CREATE POLICY p4 ON rls_tbl FOR DELETE USING (c1 <= 3);
|
||||||
|
|
||||||
|
CREATE TABLE rls_tbl_force (c1 int);
|
||||||
|
ALTER TABLE rls_tbl_force ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE rls_tbl_force FORCE ROW LEVEL SECURITY;
|
||||||
|
CREATE POLICY p1 ON rls_tbl_force USING (c1 = 5) WITH CHECK (c1 < 5);
|
||||||
|
CREATE POLICY p2 ON rls_tbl_force FOR SELECT USING (c1 = 8);
|
||||||
|
CREATE POLICY p3 ON rls_tbl_force FOR UPDATE USING (c1 = 8) WITH CHECK (c1 >= 5);
|
||||||
|
CREATE POLICY p4 ON rls_tbl_force FOR DELETE USING (c1 = 8);
|
||||||
|
Reference in New Issue
Block a user