Remove the row_security=force GUC value.
authorNoah Misch <[email protected]>
Mon, 21 Sep 2015 00:45:41 +0000 (20:45 -0400)
committerPavan Deolasee <[email protected]>
Wed, 26 Oct 2016 07:59:02 +0000 (13:29 +0530)
Every query of a single ENABLE ROW SECURITY table has two meanings, with
the row_security GUC selecting between them.  With row_security=force
available, every function author would have been advised to either set
the GUC locally or test both meanings.  Non-compliance would have
threatened reliability and, for SECURITY DEFINER functions, security.
Authors already face an obligation to account for search_path, and we
should not mimic that example.  With this change, only BYPASSRLS roles
need exercise the aforementioned care.  Back-patch to 9.5, where the
row_security GUC was introduced.

Since this narrows the domain of pg_db_role_setting.setconfig and
pg_proc.proconfig, one might bump catversion.  A row_security=force
setting in one of those columns will elicit a clear message, so don't.

doc/src/sgml/config.sgml
doc/src/sgml/ddl.sgml
src/backend/utils/misc/guc.c
src/backend/utils/misc/rls.c
src/include/utils/rls.h
src/test/regress/expected/rowsecurity.out
src/test/regress/sql/rowsecurity.sql

index ddf81ba0afba33040fcbe9c49bcf3612624897ac..268dc2c08deda563593496b909bd43d81e940f71 100644 (file)
@@ -5593,6 +5593,7 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
        <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
@@ -5609,13 +5610,6 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
         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">.
index 31ad4b83f51cc40bbb913c7e4bfcdf7becb50e25..70af9e066473a46e8354eac07c0150818b8af3b5 100755 (executable)
@@ -1845,16 +1845,13 @@ REVOKE ALL ON accounts FROM PUBLIC;
 
   <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>
index d2022d24e600f8dfe2d7c8a02ea34978a447395f..cd548df8f6811764d4cce9649f29eef5c9a6322f 100644 (file)
@@ -426,22 +426,6 @@ static const struct config_enum_entry huge_pages_options[] = {
        {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
 /*
@@ -485,6 +469,7 @@ bool                log_remotesubplan_stats = false;
 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;
@@ -519,8 +504,6 @@ int                 tcp_keepalives_count;
 #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.
@@ -1514,6 +1497,15 @@ static struct config_bool ConfigureNamesBool[] =
                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."),
@@ -4044,16 +4036,6 @@ static struct config_enum ConfigureNamesEnum[] =
                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,
index 3350f10c9f689e770ff51c0d8fd9a25b903eec43..6f0ab49abc475dcce77a9b6317635bd3161d7127 100644 (file)
@@ -74,32 +74,19 @@ check_enable_rls(Oid relid, Oid checkAsUser, bool noError)
        /*
         * 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 */
index 3770ddc2163ea2fb37acdeb01289332b0441f3e2..3e75f06d6cfc70f3e8dac103a9d599faa9a07187 100644 (file)
 #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.
@@ -30,7 +22,7 @@ typedef enum RowSecurityConfigType
  * 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
index d4402b9cf3ad294fcf94dde6b186f7b64c68154b..f761775af86a30cf2ad8e6841ecc37ea00a9bea9 100644 (file)
@@ -330,19 +330,6 @@ SELECT * FROM category;
   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;
@@ -395,19 +382,6 @@ SELECT * FROM category;
   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;
@@ -2444,35 +2418,6 @@ EXPLAIN (COSTS OFF) SELECT * FROM t1;
    ->  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;
@@ -2544,14 +2489,6 @@ COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ',';
 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;
@@ -2565,14 +2502,6 @@ COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --ok
 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;
@@ -2596,14 +2525,6 @@ COPY (SELECT * FROM copy_t ORDER BY a ASC) TO STDOUT WITH DELIMITER ','; --ok
 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;
@@ -2612,9 +2533,6 @@ ERROR:  insufficient privilege to bypass row security.
 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;
@@ -2631,8 +2549,6 @@ COPY copy_rel_to TO STDOUT WITH DELIMITER ',';
 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;
@@ -2640,8 +2556,6 @@ COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --fail - insufficient to bypass r
 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;
@@ -2649,8 +2563,6 @@ COPY copy_rel_to TO STDOUT WITH DELIMITER ','; --ok
 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;
@@ -2659,19 +2571,12 @@ ERROR:  permission denied for relation copy_rel_to
 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;
@@ -2681,10 +2586,6 @@ SET row_security TO ON;
 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;
@@ -2693,10 +2594,6 @@ SET row_security TO ON;
 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;
@@ -2705,9 +2602,6 @@ ERROR:  permission denied for relation copy_t
 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;
index 39d3922caae0deb33900bbf9140df0c783e88578..880c54b77c8de694a79f5347ca9f6a8f0f5b0fd4 100644 (file)
@@ -168,12 +168,6 @@ SET row_security TO ON;
 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;
@@ -186,12 +180,6 @@ 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;
@@ -983,17 +971,6 @@ SET SESSION AUTHORIZATION rls_regress_user0;
 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;
@@ -1024,8 +1001,6 @@ SET row_security TO OFF;
 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;
@@ -1033,8 +1008,6 @@ SET row_security TO OFF;
 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;
@@ -1042,8 +1015,6 @@ SET row_security TO OFF;
 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;
@@ -1051,8 +1022,6 @@ SET row_security TO OFF;
 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;
@@ -1072,8 +1041,6 @@ SET row_security TO OFF;
 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;
@@ -1081,8 +1048,6 @@ SET row_security TO OFF;
 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;
@@ -1090,8 +1055,6 @@ SET row_security TO OFF;
 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;
@@ -1099,8 +1062,6 @@ SET row_security TO OFF;
 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;
@@ -1118,8 +1079,6 @@ COPY copy_t FROM STDIN; --ok
 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;
@@ -1127,8 +1086,6 @@ SET row_security TO OFF;
 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;
@@ -1141,8 +1098,6 @@ COPY copy_t FROM STDIN; --ok
 \.
 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;
@@ -1150,13 +1105,208 @@ SET row_security TO OFF;
 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
 --