<para>
This variable controls if row security policies are to be applied
to queries which are run against tables that have row security enabled.
+
The default is 'on'. When set to 'on', all users, except superusers
and the owner of the table, will have the row policies for the table
applied to their queries. The table owner and superuser can request
row policies then a permission denied error will be returned.
</para>
- <para>
- The allowed values of <varname>row_security</> are
- <literal>on</> (apply normally - not to superuser or table owner),
- <literal>off</> (fail if row security would be applied), and
- <literal>force</> (apply always - even to superuser and table owner).
- </para>
-
<para>
For more information on row security policies,
see <xref linkend="SQL-CREATEPOLICY">.
<para>
The table owners and superusers bypass the row security system when
- querying a table, by default. Row security can be enabled for
- superusers and table owners by setting
- <xref linkend="guc-row-security"> to <literal>force</literal>. Any
- user can request that row security be bypassed by setting
- <xref linkend="guc-row-security"> to <literal>off</literal>. If
- the user does not have privileges to bypass row security when
- querying a given table then an error will be returned instead. Other
- users can be granted the ability to bypass the row security system
- with the <literal>BYPASSRLS</literal> role attribute. This
- attribute can only be set by a superuser.
+ querying a table. Any user can request that row security be bypassed by
+ setting <xref linkend="guc-row-security"> to <literal>off</literal>. If
+ the user does not have privileges to bypass row security when querying a
+ given table then an error will be returned instead. Other users can be
+ granted the ability to bypass the row security system with
+ the <literal>BYPASSRLS</literal> role attribute. This attribute can only
+ be set by a superuser.
</para>
<para>
{NULL, 0, false}
};
-/*
- * Although only "on", "off", and "force" are documented, we
- * accept all the likely variants of "on" and "off".
- */
-static const struct config_enum_entry row_security_options[] = {
- {"on", ROW_SECURITY_ON, false},
- {"off", ROW_SECURITY_OFF, false},
- {"force", ROW_SECURITY_FORCE, false},
- {"true", ROW_SECURITY_ON, true},
- {"false", ROW_SECURITY_OFF, true},
- {"yes", ROW_SECURITY_ON, true},
- {"no", ROW_SECURITY_OFF, true},
- {"1", ROW_SECURITY_ON, true},
- {"0", ROW_SECURITY_OFF, true},
- {NULL, 0, false}
-};
#ifdef XCP
/*
bool log_btree_build_stats = false;
char *event_source;
+bool row_security;
bool check_function_bodies = true;
bool default_with_oids = false;
bool SQL_inheritance = true;
#ifdef XCP
char *storm_catalog_remap_string;
#endif
-int row_security;
-
/*
* This really belongs in pg_shmem.c, but is defined here so that it doesn't
* need to be duplicated in all the different implementations of pg_shmem.c.
false,
check_transaction_deferrable, NULL, NULL
},
+ {
+ {"row_security", PGC_USERSET, CONN_AUTH_SECURITY,
+ gettext_noop("Enable row security."),
+ gettext_noop("When enabled, row security will be applied to all users.")
+ },
+ &row_security,
+ true,
+ NULL, NULL, NULL
+ },
{
{"check_function_bodies", PGC_USERSET, CLIENT_CONN_STATEMENT,
gettext_noop("Check function bodies during CREATE FUNCTION."),
NULL, NULL, NULL
},
- {
- {"row_security", PGC_USERSET, CONN_AUTH_SECURITY,
- gettext_noop("Enable row security."),
- gettext_noop("When enabled, row security will be applied to all users.")
- },
- &row_security,
- ROW_SECURITY_ON, row_security_options,
- NULL, NULL, NULL
- },
-
#ifdef XCP
{
{"global_snapshot_source", PGC_USERSET, DEVELOPER_OPTIONS,
/*
* Check permissions
*
- * If the relation has row level security enabled and the row_security GUC
- * is off, then check if the user has rights to bypass RLS for this
- * relation. Table owners can always bypass, as can any role with the
- * BYPASSRLS capability.
- *
- * If the role is the table owner, then we bypass RLS unless row_security
- * is set to 'force'. Note that superuser is always considered an owner.
- *
- * Return RLS_NONE_ENV to indicate that this decision depends on the
- * environment (in this case, what the current values of user_id and
- * row_security are).
+ * Table owners always bypass RLS. Note that superuser is always
+ * considered an owner. Return RLS_NONE_ENV to indicate that this
+ * decision depends on the environment (in this case, the user_id).
*/
- if (row_security != ROW_SECURITY_FORCE
- && (pg_class_ownercheck(relid, user_id)))
+ if (pg_class_ownercheck(relid, user_id))
return RLS_NONE_ENV;
/*
- * If the row_security GUC is 'off' then check if the user has permission
- * to bypass it. Note that we have already handled the case where the
- * user is the table owner above.
- *
- * Note that row_security is always considered 'on' when querying through
- * a view or other cases where checkAsUser is true, so skip this if
- * checkAsUser is in use.
+ * If the row_security GUC is 'off', check if the user has permission to
+ * bypass RLS. row_security is always considered 'on' when querying
+ * through a view or other cases where checkAsUser is valid.
*/
- if (!checkAsUser && row_security == ROW_SECURITY_OFF)
+ if (!row_security && !checkAsUser)
{
if (has_bypassrls_privilege(user_id))
/* OK to bypass */
#define RLS_H
/* GUC variable */
-extern int row_security;
-
-/* Possible values for row_security GUC */
-typedef enum RowSecurityConfigType
-{
- ROW_SECURITY_OFF, /* RLS never applied- error thrown if no priv */
- ROW_SECURITY_ON, /* normal case, RLS applied for regular users */
- ROW_SECURITY_FORCE /* RLS applied for superusers and table owners */
-} RowSecurityConfigType;
+extern bool row_security;
/*
* Used by callers of check_enable_rls.
* RLS could be completely disabled on the tables involved in the query,
* which is the simple case, or it may depend on the current environment
* (the role which is running the query or the value of the row_security
- * GUC- on, off, or force), or it might be simply enabled as usual.
+ * GUC), or it might be simply enabled as usual.
*
* If RLS isn't on the table involved then RLS_NONE is returned to indicate
* that we don't need to worry about invalidating the query plan for RLS
44 | manga
(4 rows)
--- database superuser does not bypass RLS policy when FORCE enabled.
-RESET SESSION AUTHORIZATION;
-SET row_security TO FORCE;
-SELECT * FROM document;
- did | cid | dlevel | dauthor | dtitle
------+-----+--------+---------+--------
-(0 rows)
-
-SELECT * FROM category;
- cid | cname
------+-------
-(0 rows)
-
-- database superuser does bypass RLS policy when disabled
RESET SESSION AUTHORIZATION;
SET row_security TO OFF;
44 | manga
(4 rows)
--- RLS policy applies to table owner when FORCE enabled.
-SET SESSION AUTHORIZATION rls_regress_user0;
-SET row_security TO FORCE;
-SELECT * FROM document;
- did | cid | dlevel | dauthor | dtitle
------+-----+--------+---------+--------
-(0 rows)
-
-SELECT * FROM category;
- cid | cname
------+-------
-(0 rows)
-
-- RLS policy does not apply to table owner when RLS enabled.
SET SESSION AUTHORIZATION rls_regress_user0;
SET row_security TO ON;
-> Seq Scan on t1
(3 rows)
--- Check that default deny does apply to superuser when RLS force.
-SET row_security TO FORCE;
-RESET SESSION AUTHORIZATION;
-SELECT * FROM t1;
- a | b
----+---
-(0 rows)
-
-EXPLAIN (COSTS OFF) SELECT * FROM t1;
- QUERY PLAN
---------------------------
- Result
- One-Time Filter: false
-(2 rows)
-
--- Check that default deny does apply to table owner when RLS force.
-SET SESSION AUTHORIZATION rls_regress_user0;
-SELECT * FROM t1;
- a | b
----+---
-(0 rows)
-
-EXPLAIN (COSTS OFF) SELECT * FROM t1;
- QUERY PLAN
---------------------------
- Result
- One-Time Filter: false
-(2 rows)
-
-- Check that default deny applies to non-owner/non-superuser when RLS on.
SET SESSION AUTHORIZATION rls_regress_user1;
SET row_security TO ON;
8,c9f0f895fb98ab9159f51fd0297e236d
9,45c48cce2e2d7fbdea1afc51c7c6ad26
10,d3d9446802a44259755d38e6d163e820
-SET row_security TO FORCE;
-COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ',';
-0,cfcd208495d565ef66e7dff9f98764da
-2,c81e728d9d4c2f636f067f89cc14862c
-4,a87ff679a2f3e71d9181a67b7542122c
-6,1679091c5a880faf6fb5e6087eb1b2dc
-8,c9f0f895fb98ab9159f51fd0297e236d
-10,d3d9446802a44259755d38e6d163e820
-- Check COPY TO as user with permissions.
SET SESSION AUTHORIZATION rls_regress_user1;
SET row_security TO OFF;
6,1679091c5a880faf6fb5e6087eb1b2dc
8,c9f0f895fb98ab9159f51fd0297e236d
10,d3d9446802a44259755d38e6d163e820
-SET row_security TO FORCE;
-COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --ok
-0,cfcd208495d565ef66e7dff9f98764da
-2,c81e728d9d4c2f636f067f89cc14862c
-4,a87ff679a2f3e71d9181a67b7542122c
-6,1679091c5a880faf6fb5e6087eb1b2dc
-8,c9f0f895fb98ab9159f51fd0297e236d
-10,d3d9446802a44259755d38e6d163e820
-- Check COPY TO as user with permissions and BYPASSRLS
SET SESSION AUTHORIZATION rls_regress_exempt_user;
SET row_security TO OFF;
6,1679091c5a880faf6fb5e6087eb1b2dc
8,c9f0f895fb98ab9159f51fd0297e236d
10,d3d9446802a44259755d38e6d163e820
-SET row_security TO FORCE;
-COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --ok
-0,cfcd208495d565ef66e7dff9f98764da
-2,c81e728d9d4c2f636f067f89cc14862c
-4,a87ff679a2f3e71d9181a67b7542122c
-6,1679091c5a880faf6fb5e6087eb1b2dc
-8,c9f0f895fb98ab9159f51fd0297e236d
-10,d3d9446802a44259755d38e6d163e820
-- Check COPY TO as user without permissions. SET row_security TO OFF;
SET SESSION AUTHORIZATION rls_regress_user2;
SET row_security TO OFF;
SET row_security TO ON;
COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --fail - permission denied
ERROR: permission denied for relation copy_t
-SET row_security TO FORCE;
-COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --fail - permission denied
-ERROR: permission denied for relation copy_t
-- Check COPY relation TO; keep it just one row to avoid reordering issues
RESET SESSION AUTHORIZATION;
SET row_security TO ON;
SET row_security TO ON;
COPY copy_rel_to TO STDOUT WITH DELIMITER ',';
1,c4ca4238a0b923820dcc509a6f75849b
-SET row_security TO FORCE;
-COPY copy_rel_to TO STDOUT WITH DELIMITER ',';
-- Check COPY TO as user with permissions.
SET SESSION AUTHORIZATION rls_regress_user1;
SET row_security TO OFF;
ERROR: insufficient privilege to bypass row security.
SET row_security TO ON;
COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --ok
-SET row_security TO FORCE;
-COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --ok
-- Check COPY TO as user with permissions and BYPASSRLS
SET SESSION AUTHORIZATION rls_regress_exempt_user;
SET row_security TO OFF;
1,c4ca4238a0b923820dcc509a6f75849b
SET row_security TO ON;
COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --ok
-SET row_security TO FORCE;
-COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --ok
-- Check COPY TO as user without permissions. SET row_security TO OFF;
SET SESSION AUTHORIZATION rls_regress_user2;
SET row_security TO OFF;
SET row_security TO ON;
COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --fail - permission denied
ERROR: permission denied for relation copy_rel_to
-SET row_security TO FORCE;
-COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --fail - permission denied
-ERROR: permission denied for relation copy_rel_to
-- Check COPY FROM as Superuser/owner.
RESET SESSION AUTHORIZATION;
SET row_security TO OFF;
COPY copy_t FROM STDIN; --ok
SET row_security TO ON;
COPY copy_t FROM STDIN; --ok
-SET row_security TO FORCE;
-COPY copy_t FROM STDIN; --fail - COPY FROM not supported by RLS.
-ERROR: COPY FROM not supported with row level security.
-HINT: Use direct INSERT statements instead.
-- Check COPY FROM as user with permissions.
SET SESSION AUTHORIZATION rls_regress_user1;
SET row_security TO OFF;
COPY copy_t FROM STDIN; --fail - COPY FROM not supported by RLS.
ERROR: COPY FROM not supported with row level security.
HINT: Use direct INSERT statements instead.
-SET row_security TO FORCE;
-COPY copy_t FROM STDIN; --fail - COPY FROM not supported by RLS.
-ERROR: COPY FROM not supported with row level security.
-HINT: Use direct INSERT statements instead.
-- Check COPY TO as user with permissions and BYPASSRLS
SET SESSION AUTHORIZATION rls_regress_exempt_user;
SET row_security TO OFF;
COPY copy_t FROM STDIN; --fail - COPY FROM not supported by RLS.
ERROR: COPY FROM not supported with row level security.
HINT: Use direct INSERT statements instead.
-SET row_security TO FORCE;
-COPY copy_t FROM STDIN; --fail - COPY FROM not supported by RLS.
-ERROR: COPY FROM not supported with row level security.
-HINT: Use direct INSERT statements instead.
-- Check COPY FROM as user without permissions.
SET SESSION AUTHORIZATION rls_regress_user2;
SET row_security TO OFF;
SET row_security TO ON;
COPY copy_t FROM STDIN; --fail - permission denied.
ERROR: permission denied for relation copy_t
-SET row_security TO FORCE;
-COPY copy_t FROM STDIN; --fail - permission denied.
-ERROR: permission denied for relation copy_t
RESET SESSION AUTHORIZATION;
DROP TABLE copy_t;
DROP TABLE copy_rel_to CASCADE;
SELECT * FROM document;
SELECT * FROM category;
--- database superuser does not bypass RLS policy when FORCE enabled.
-RESET SESSION AUTHORIZATION;
-SET row_security TO FORCE;
-SELECT * FROM document;
-SELECT * FROM category;
-
-- database superuser does bypass RLS policy when disabled
RESET SESSION AUTHORIZATION;
SET row_security TO OFF;
SELECT * FROM document;
SELECT * FROM category;
--- RLS policy applies to table owner when FORCE enabled.
-SET SESSION AUTHORIZATION rls_regress_user0;
-SET row_security TO FORCE;
-SELECT * FROM document;
-SELECT * FROM category;
-
-- RLS policy does not apply to table owner when RLS enabled.
SET SESSION AUTHORIZATION rls_regress_user0;
SET row_security TO ON;
SELECT * FROM t1;
EXPLAIN (COSTS OFF) SELECT * FROM t1;
--- Check that default deny does apply to superuser when RLS force.
-SET row_security TO FORCE;
-RESET SESSION AUTHORIZATION;
-SELECT * FROM t1;
-EXPLAIN (COSTS OFF) SELECT * FROM t1;
-
--- Check that default deny does apply to table owner when RLS force.
-SET SESSION AUTHORIZATION rls_regress_user0;
-SELECT * FROM t1;
-EXPLAIN (COSTS OFF) SELECT * FROM t1;
-
-- Check that default deny applies to non-owner/non-superuser when RLS on.
SET SESSION AUTHORIZATION rls_regress_user1;
SET row_security TO ON;
COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ',';
SET row_security TO ON;
COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ',';
-SET row_security TO FORCE;
-COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ',';
-- Check COPY TO as user with permissions.
SET SESSION AUTHORIZATION rls_regress_user1;
COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --fail - insufficient to bypass rls
SET row_security TO ON;
COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --ok
-SET row_security TO FORCE;
-COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --ok
-- Check COPY TO as user with permissions and BYPASSRLS
SET SESSION AUTHORIZATION rls_regress_exempt_user;
COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --ok
SET row_security TO ON;
COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --ok
-SET row_security TO FORCE;
-COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --ok
-- Check COPY TO as user without permissions. SET row_security TO OFF;
SET SESSION AUTHORIZATION rls_regress_user2;
COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --fail - insufficient to bypass rls
SET row_security TO ON;
COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --fail - permission denied
-SET row_security TO FORCE;
-COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --fail - permission denied
-- Check COPY relation TO; keep it just one row to avoid reordering issues
RESET SESSION AUTHORIZATION;
COPY copy_rel_to TO STDOUT WITH DELIMITER ',';
SET row_security TO ON;
COPY copy_rel_to TO STDOUT WITH DELIMITER ',';
-SET row_security TO FORCE;
-COPY copy_rel_to TO STDOUT WITH DELIMITER ',';
-- Check COPY TO as user with permissions.
SET SESSION AUTHORIZATION rls_regress_user1;
COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --fail - insufficient to bypass rls
SET row_security TO ON;
COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --ok
-SET row_security TO FORCE;
-COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --ok
-- Check COPY TO as user with permissions and BYPASSRLS
SET SESSION AUTHORIZATION rls_regress_exempt_user;
COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --ok
SET row_security TO ON;
COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --ok
-SET row_security TO FORCE;
-COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --ok
-- Check COPY TO as user without permissions. SET row_security TO OFF;
SET SESSION AUTHORIZATION rls_regress_user2;
COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --fail - permission denied
SET row_security TO ON;
COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --fail - permission denied
-SET row_security TO FORCE;
-COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --fail - permission denied
-- Check COPY FROM as Superuser/owner.
RESET SESSION AUTHORIZATION;
3 cde
4 def
\.
-SET row_security TO FORCE;
-COPY copy_t FROM STDIN; --fail - COPY FROM not supported by RLS.
-- Check COPY FROM as user with permissions.
SET SESSION AUTHORIZATION rls_regress_user1;
COPY copy_t FROM STDIN; --fail - insufficient privilege to bypass rls.
SET row_security TO ON;
COPY copy_t FROM STDIN; --fail - COPY FROM not supported by RLS.
-SET row_security TO FORCE;
-COPY copy_t FROM STDIN; --fail - COPY FROM not supported by RLS.
-- Check COPY TO as user with permissions and BYPASSRLS
SET SESSION AUTHORIZATION rls_regress_exempt_user;
\.
SET row_security TO ON;
COPY copy_t FROM STDIN; --fail - COPY FROM not supported by RLS.
-SET row_security TO FORCE;
-COPY copy_t FROM STDIN; --fail - COPY FROM not supported by RLS.
-- Check COPY FROM as user without permissions.
SET SESSION AUTHORIZATION rls_regress_user2;
COPY copy_t FROM STDIN; --fail - permission denied.
SET row_security TO ON;
COPY copy_t FROM STDIN; --fail - permission denied.
-SET row_security TO FORCE;
-COPY copy_t FROM STDIN; --fail - permission denied.
RESET SESSION AUTHORIZATION;
DROP TABLE copy_t;
DROP TABLE copy_rel_to CASCADE;
+-- Check WHERE CURRENT OF
+SET SESSION AUTHORIZATION rls_regress_user0;
+
+CREATE TABLE current_check (currentid int, payload text, rlsuser text);
+GRANT ALL ON current_check TO PUBLIC;
+
+INSERT INTO current_check VALUES
+ (1, 'abc', 'rls_regress_user1'),
+ (2, 'bcd', 'rls_regress_user1'),
+ (3, 'cde', 'rls_regress_user1'),
+ (4, 'def', 'rls_regress_user1');
+
+CREATE POLICY p1 ON current_check FOR SELECT USING (currentid % 2 = 0);
+CREATE POLICY p2 ON current_check FOR DELETE USING (currentid = 4 AND rlsuser = current_user);
+CREATE POLICY p3 ON current_check FOR UPDATE USING (currentid = 4) WITH CHECK (rlsuser = current_user);
+
+ALTER TABLE current_check ENABLE ROW LEVEL SECURITY;
+
+SET SESSION AUTHORIZATION rls_regress_user1;
+
+-- Can SELECT even rows
+SELECT * FROM current_check;
+
+-- Cannot UPDATE row 2
+UPDATE current_check SET payload = payload || '_new' WHERE currentid = 2 RETURNING *;
+
+BEGIN;
+
+DECLARE current_check_cursor SCROLL CURSOR FOR SELECT * FROM current_check;
+-- Returns rows that can be seen according to SELECT policy, like plain SELECT
+-- above (even rows)
+FETCH ABSOLUTE 1 FROM current_check_cursor;
+-- Still cannot UPDATE row 2 through cursor
+UPDATE current_check SET payload = payload || '_new' WHERE CURRENT OF current_check_cursor RETURNING *;
+-- Can update row 4 through cursor, which is the next visible row
+FETCH RELATIVE 1 FROM current_check_cursor;
+UPDATE current_check SET payload = payload || '_new' WHERE CURRENT OF current_check_cursor RETURNING *;
+SELECT * FROM current_check;
+-- Plan should be a subquery TID scan
+EXPLAIN (COSTS OFF) UPDATE current_check SET payload = payload WHERE CURRENT OF current_check_cursor;
+-- Similarly can only delete row 4
+FETCH ABSOLUTE 1 FROM current_check_cursor;
+DELETE FROM current_check WHERE CURRENT OF current_check_cursor RETURNING *;
+FETCH RELATIVE 1 FROM current_check_cursor;
+DELETE FROM current_check WHERE CURRENT OF current_check_cursor RETURNING *;
+SELECT * FROM current_check;
+
+COMMIT;
+
+--
+-- check pg_stats view filtering
+--
+SET row_security TO ON;
+SET SESSION AUTHORIZATION rls_regress_user0;
+ANALYZE current_check;
+-- Stats visible
+SELECT row_security_active('current_check');
+SELECT attname, most_common_vals FROM pg_stats
+ WHERE tablename = 'current_check'
+ ORDER BY 1;
+
+SET SESSION AUTHORIZATION rls_regress_user1;
+-- Stats not visible
+SELECT row_security_active('current_check');
+SELECT attname, most_common_vals FROM pg_stats
+ WHERE tablename = 'current_check'
+ ORDER BY 1;
+
+--
+-- Collation support
+--
+BEGIN;
+CREATE TABLE coll_t (c) AS VALUES ('bar'::text);
+CREATE POLICY coll_p ON coll_t USING (c < ('foo'::text COLLATE "C"));
+ALTER TABLE coll_t ENABLE ROW LEVEL SECURITY;
+GRANT SELECT ON coll_t TO rls_regress_user0;
+SELECT (string_to_array(polqual, ':'))[7] AS inputcollid FROM pg_policy WHERE polrelid = 'coll_t'::regclass;
+SET SESSION AUTHORIZATION rls_regress_user0;
+SELECT * FROM coll_t;
+ROLLBACK;
+
+--
+-- Shared Object Dependencies
+--
+RESET SESSION AUTHORIZATION;
+BEGIN;
+CREATE ROLE alice;
+CREATE ROLE bob;
+CREATE TABLE tbl1 (c) AS VALUES ('bar'::text);
+GRANT SELECT ON TABLE tbl1 TO alice;
+CREATE POLICY P ON tbl1 TO alice, bob USING (true);
+SELECT refclassid::regclass, deptype
+ FROM pg_depend
+ WHERE classid = 'pg_policy'::regclass
+ AND refobjid = 'tbl1'::regclass;
+SELECT refclassid::regclass, deptype
+ FROM pg_shdepend
+ WHERE classid = 'pg_policy'::regclass
+ AND refobjid IN ('alice'::regrole, 'bob'::regrole);
+
+SAVEPOINT q;
+DROP ROLE alice; --fails due to dependency on POLICY p
+ROLLBACK TO q;
+
+ALTER POLICY p ON tbl1 TO bob USING (true);
+SAVEPOINT q;
+DROP ROLE alice; --fails due to dependency on GRANT SELECT
+ROLLBACK TO q;
+
+REVOKE ALL ON TABLE tbl1 FROM alice;
+SAVEPOINT q;
+DROP ROLE alice; --succeeds
+ROLLBACK TO q;
+
+SAVEPOINT q;
+DROP ROLE bob; --fails due to dependency on POLICY p
+ROLLBACK TO q;
+
+DROP POLICY p ON tbl1;
+SAVEPOINT q;
+DROP ROLE bob; -- succeeds
+ROLLBACK TO q;
+
+ROLLBACK; -- cleanup
+
+--
+-- Converting table to view
+--
+BEGIN;
+CREATE TABLE t (c int);
+CREATE POLICY p ON t USING (c % 2 = 1);
+ALTER TABLE t ENABLE ROW LEVEL SECURITY;
+
+SAVEPOINT q;
+CREATE RULE "_RETURN" AS ON SELECT TO t DO INSTEAD
+ SELECT * FROM generate_series(1,5) t0(c); -- fails due to row level security enabled
+ROLLBACK TO q;
+
+ALTER TABLE t DISABLE ROW LEVEL SECURITY;
+SAVEPOINT q;
+CREATE RULE "_RETURN" AS ON SELECT TO t DO INSTEAD
+ SELECT * FROM generate_series(1,5) t0(c); -- fails due to policy p on t
+ROLLBACK TO q;
+
+DROP POLICY p ON t;
+CREATE RULE "_RETURN" AS ON SELECT TO t DO INSTEAD
+ SELECT * FROM generate_series(1,5) t0(c); -- succeeds
+ROLLBACK;
+
+--
+-- Policy expression handling
+--
+BEGIN;
+CREATE TABLE t (c) AS VALUES ('bar'::text);
+CREATE POLICY p ON t USING (max(c)); -- fails: aggregate functions are not allowed in policy expressions
+ROLLBACK;
+
+--
+-- Non-target relations are only subject to SELECT policies
+--
+SET SESSION AUTHORIZATION rls_regress_user0;
+CREATE TABLE r1 (a int);
+CREATE TABLE r2 (a int);
+INSERT INTO r1 VALUES (10), (20);
+INSERT INTO r2 VALUES (10), (20);
+
+GRANT ALL ON r1, r2 TO rls_regress_user1;
+
+CREATE POLICY p1 ON r1 USING (true);
+ALTER TABLE r1 ENABLE ROW LEVEL SECURITY;
+
+CREATE POLICY p1 ON r2 FOR SELECT USING (true);
+CREATE POLICY p2 ON r2 FOR INSERT WITH CHECK (false);
+CREATE POLICY p3 ON r2 FOR UPDATE USING (false);
+CREATE POLICY p4 ON r2 FOR DELETE USING (false);
+ALTER TABLE r2 ENABLE ROW LEVEL SECURITY;
+
+SET SESSION AUTHORIZATION rls_regress_user1;
+SELECT * FROM r1;
+SELECT * FROM r2;
+
+-- r2 is read-only
+INSERT INTO r2 VALUES (2); -- Not allowed
+UPDATE r2 SET a = 2 RETURNING *; -- Updates nothing
+DELETE FROM r2 RETURNING *; -- Deletes nothing
+
+-- r2 can be used as a non-target relation in DML
+INSERT INTO r1 SELECT a + 1 FROM r2 RETURNING *; -- OK
+UPDATE r1 SET a = r2.a + 2 FROM r2 WHERE r1.a = r2.a RETURNING *; -- OK
+DELETE FROM r1 USING r2 WHERE r1.a = r2.a + 2 RETURNING *; -- OK
+SELECT * FROM r1;
+SELECT * FROM r2;
+
+SET SESSION AUTHORIZATION rls_regress_user0;
+DROP TABLE r1;
+DROP TABLE r2;
+
--
-- Clean up objects
--