]> granicus.if.org Git - postgresql/commitdiff
Code review for row security.
authorStephen Frost <sfrost@snowman.net>
Wed, 24 Sep 2014 20:32:22 +0000 (16:32 -0400)
committerStephen Frost <sfrost@snowman.net>
Wed, 24 Sep 2014 20:32:22 +0000 (16:32 -0400)
Buildfarm member tick identified an issue where the policies in the
relcache for a relation were were being replaced underneath a running
query, leading to segfaults while processing the policies to be added
to a query.  Similar to how TupleDesc RuleLocks are handled, add in a
equalRSDesc() function to check if the policies have actually changed
and, if not, swap back the rsdesc field (using the original instead of
the temporairly built one; the whole structure is swapped and then
specific fields swapped back).  This now passes a CLOBBER_CACHE_ALWAYS
for me and should resolve the buildfarm error.

In addition to addressing this, add a new chapter in Data Definition
under Privileges which explains row security and provides examples of
its usage, change \d to always list policies (even if row security is
disabled- but note that it is disabled, or enabled with no policies),
rework check_role_for_policy (it really didn't need the entire policy,
but it did need to be using has_privs_of_role()), and change the field
in pg_class to relrowsecurity from relhasrowsecurity, based on
Heikki's suggestion.  Also from Heikki, only issue SET ROW_SECURITY in
pg_restore when talking to a 9.5+ server, list Bypass RLS in \du, and
document --enable-row-security options for pg_dump and pg_restore.

Lastly, fix a number of minor whitespace and typo issues from Heikki,
Dimitri, add a missing #include, per Peter E, fix a few minor
variable-assigned-but-not-used and resource leak issues from Coverity
and add tab completion for role attribute bypassrls as well.

24 files changed:
doc/src/sgml/catalogs.sgml
doc/src/sgml/config.sgml
doc/src/sgml/ddl.sgml
doc/src/sgml/ref/alter_table.sgml
doc/src/sgml/ref/create_policy.sgml
doc/src/sgml/ref/pg_dump.sgml
doc/src/sgml/ref/pg_restore.sgml
src/backend/catalog/heap.c
src/backend/catalog/system_views.sql
src/backend/commands/policy.c
src/backend/commands/tablecmds.c
src/backend/rewrite/rowsecurity.c
src/backend/utils/adt/ri_triggers.c
src/backend/utils/cache/relcache.c
src/bin/pg_dump/pg_backup_archiver.c
src/bin/pg_dump/pg_dump.c
src/bin/pg_dump/pg_dump.h
src/bin/pg_dump/pg_restore.c
src/bin/psql/describe.c
src/bin/psql/tab-complete.c
src/include/catalog/catversion.h
src/include/catalog/pg_class.h
src/include/commands/policy.h
src/test/regress/expected/rules.out

index 76d64050618dc0d2f8d52c696f42b59606be5956..a6ca290cb3d5da28100f94e82c52bcc7fc5f157f 100644 (file)
      </row>
 
      <row>
-      <entry><structfield>relhasrowsecurity</structfield></entry>
+      <entry><structfield>relrowsecurity</structfield></entry>
       <entry><type>bool</type></entry>
+      <entry></entry>
       <entry>
        True if table has row-security enabled; see
        <link linkend="catalog-pg-rowsecurity"><structname>pg_rowsecurity</structname></link> catalog
 
   <note>
    <para>
-    <literal>pg_class.relhasrowsecurity</literal>
+    <literal>pg_class.relrowsecurity</literal>
     True if the table has row-security enabled.
     Must be true if the table has a row-security policy in this catalog.
    </para>
@@ -9228,10 +9229,10 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx
       <entry>True if table has (or once had) triggers</entry>
      </row>
      <row>
-      <entry><structfield>hasrowsecurity</structfield></entry>
+      <entry><structfield>rowsecurity</structfield></entry>
       <entry><type>boolean</type></entry>
-      <entry><literal><link linkend="catalog-pg-class"><structname>pg_class</structname></link>.relhasrowsecurity</literal></entry>
-      <entry>True if table has row security enabled</entry>
+      <entry><literal><link linkend="catalog-pg-class"><structname>pg_class</structname></link>.relrowsecurity</literal></entry>
+      <entry>True if row security is enabled on the table</entry>
      </row>
     </tbody>
    </tgroup>
index 70e47aaa3a1be9e9b953ea9c9c417fffc5788644..949443931cd713b48f6c5b6493d731bf8b0c8d36 100644 (file)
@@ -5457,9 +5457,9 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
 
        <para>
         The allowed values of <varname>row_security</> are
-        <literal>on</> (apply normally- not to superuser or table owner),
+        <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).
+        <literal>force</> (apply always - even to superuser and table owner).
        </para>
 
        <para>
index c07f5a203dde0b2161b1912d260a44e398a2ce79..e5ee591051374046efb9424da8c9e0a8e47e5938 100644 (file)
@@ -1508,6 +1508,174 @@ REVOKE ALL ON accounts FROM PUBLIC;
   </para>
  </sect1>
 
+ <sect1 id="ddl-rowsecurity">
+  <title>Row Security Policies</title>
+
+  <indexterm zone="ddl-rowsecurity">
+   <primary>rowsecurity</primary>
+  </indexterm>
+
+  <indexterm zone="ddl-rowsecurity">
+   <primary>rls</primary>
+  </indexterm>
+
+  <indexterm>
+   <primary>policies</primary>
+   <see>policy</see>
+  </indexterm>
+
+  <indexterm zone="ddl-rowsecurity">
+   <primary>POLICY</primary>
+  </indexterm>
+
+  <para>
+   In addition to the <xref linkend="ddl-priv"> system available through
+   <xref linkend="sql-grant">, tables can have row security policies
+   which limit the rows returned for normal queries and rows which can
+   be added through data modification commands.  By default, tables do
+   not have any policies and all rows are visible and able to be added,
+   subject to the regular <xref linkend="ddl-priv"> system.  This is
+   also known to as Row Level Security.
+  </para>
+
+  <para>
+   When row security is enabled on a table with
+   <xref linkend="sql-altertable">, all normal access to the table
+   (excluding the owner) for selecting rows or adding rows must be through
+   a policy.  If no policy exists for the table, a default-deny policy is
+   used and no rows are visible or can be added.  Privileges which operate
+   at the whole table level such as <literal>TRUNCATE</>, and
+   <literal>REFERENCES</> are not subject to row security.
+  </para>
+
+  <para>
+   Row security policies can be specific to commands, or to roles, or to
+   both.  The commands available are <literal>SELECT</>, <literal>INSERT</>,
+   <literal>UPDATE</>, and <literal>DELETE</>.  Multiple roles can be
+   assigned to a given policy and normal role membership and inheiritance
+   rules apply.
+  </para>
+
+  <para>
+   To specify which rows are visible and what rows can be added to the
+   table with row security, an expression is required which returns a
+   boolean result.  This expression will be evaluated for each row prior
+   to other conditionals or functions which are part of the query.  The
+   one exception to this rule are <literal>leakproof</literal> functions,
+   which are guaranteed to not leak information.  Two expressions may be
+   specified to provide independent control over the rows which are
+   visible and the rows which are allowed to be added.  The expression
+   is run as part of the query and with the privileges of the user
+   running the query, however, security definer functions can be used in
+   the expression.
+  </para>
+
+  <para>
+   Enabling and disabling row security, as well as adding policies to a
+   table, is always the privilege of the owner only.
+  </para>
+
+  <para>
+   Policies are created using the <xref linkend="sql-createpolicy">
+   command, altered using the <xref linkend="sql-alterpolicy"> command,
+   and dropped using the <xref linkend="sql-droppolicy"> command.  To
+   enable and disable row security for a given table, use the
+   <xref linkend="sql-altertable"> command.
+  </para>
+
+  <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.
+  </para>
+
+  <para>
+   Each policy has a name and multiple policies can be defined for a
+   table.  As policies are table-specific, each policy for a table must
+   have a unique name.  Different tables may have policies with the
+   same name.
+  </para>
+
+  <para>
+   When multiple policies apply to a given query, they are combined using
+   <literal>OR</literal>, similar to how a given role has the privileges
+   of all roles which they are a member of.
+  </para>
+
+  <para>
+   Referential integrity checks, such as unique or primary key constraints
+   and foreign key references, will bypass row security to ensure that
+   data integrity is maintained.  Care must be taken when developing
+   schemas and row level policies to avoid a "covert channel" leak of
+   information through these referntial integrity checks.
+  </para>
+
+  <para>
+   To enable row security for a table,
+   the <command>ALTER TABLE</command> is used.  For example, to enable
+   row level security for the table accounts, use:
+  </para>
+
+<programlisting>
+-- Create the table first
+CREATE TABLE accounts (manager text, company text, contact_email text);
+ALTER TABLE accounts ENABLE ROW LEVEL SECURITY;
+</programlisting>
+
+  <para>
+   To create a policy on the account relation to allow the managers role
+   to view the rows of their accounts, the <command>CREATE POLICY</command>
+   command can be used:
+  </para>
+
+<programlisting>
+CREATE POLICY account_managers ON accounts TO managers
+    USING (manager = current_user);
+</programlisting>
+
+  <para>
+   If no role is specified, or the special <quote>user</quote> name
+   <literal>PUBLIC</literal> is used, then the policy applies to all
+   users on the system.  To allow all users to view their own row in
+   a user table, a simple policy can be used:
+  </para>
+
+<programlisting>
+CREATE POLICY user_policy ON users
+    USING (user = current_user);
+</programlisting>
+
+  <para>
+   To use a different policy for rows which are being added to the
+   table from those rows which are visible, the WITH CHECK clause
+   can be used.  This would allow all users to view all rows in the
+   users table, but only modify their own:
+  </para>
+
+<programlisting>
+CREATE POLICY user_policy ON users
+    USING (true)
+    WITH CHECK (user = current_user);
+</programlisting>
+
+  <para>
+   Row security can be disabled with the <command>ALTER TABLE</command>
+   also.  Note that disabling row security does not remove the
+   policies which are defined on the table, they are simply ignored
+   and all rows are visible and able to be added, subject to the
+   normal privileges system.
+  </para>
+
+ </sect1>
+
  <sect1 id="ddl-schemas">
   <title>Schemas</title>
 
index 1b35756c2958b1023b76e6a9224e2e8a5939adc5..b5ef09e6a4a31acdbcbf54d5dac71fe72bf05f86 100644 (file)
@@ -429,7 +429,7 @@ ALTER TABLE ALL IN TABLESPACE <replaceable class="PARAMETER">name</replaceable>
       These forms control the application of row security policies belonging
       to the table.  If enabled and no policies exist for the table, then a
       default-deny policy is applied.  Note that policies can exist for a table
-      even if row level security is disabled- in this case, the policies will
+      even if row level security is disabled - in this case, the policies will
       NOT be applied and the policies will be ignored.
       See also
       <xref linkend="SQL-CREATEPOLICY">.
index c6599eda1c0013a07106a43f690583d5cf7dd6a5..3c5bdc69cdcbea138355df8c10a1e394312e1163 100644 (file)
@@ -240,7 +240,7 @@ CREATE POLICY <replaceable class="parameter">name</replaceable> ON <replaceable
      </varlistentry>
 
      <varlistentry id="SQL-CREATEPOLICY-UPDATE">
-      <term><literal>DELETE</></term>
+      <term><literal>UPDATE</></term>
       <listitem>
        <para>
          Using <literal>UPDATE</literal> for a policy means that it will apply
index eabdc62f82035c92d47ea05bc9c2c85900797ec0..c92c6eef5d3b324b5067a62b57b829c4d57d27dd 100644 (file)
@@ -687,6 +687,23 @@ PostgreSQL documentation
       </listitem>
      </varlistentry>
 
+     <varlistentry>
+      <term><option>--enable-row-security</></term>
+      <listitem>
+       <para>
+        This option is relevant only when dumping the contents of a table
+        which has row security.  By default, pg_dump will set
+        <literal>ROW_SECURITY</literal> to <literal>OFF</literal>, to ensure
+        that all data is dumped from the table.  If the user does not have
+        sufficient privileges to bypass row security, then an error is thrown.
+        This parameter instructs <application>pg_dump</application> to set
+        row_security to 'ON' instead, allowing the user to dump the contents
+        of the table which they have access to.
+       </para>
+
+      </listitem>
+     </varlistentry>
+
      <varlistentry>
       <term><option>--exclude-table-data=<replaceable class="parameter">table</replaceable></option></term>
       <listitem>
index 4bc30ce679b9c87d05c3872def83773f7b3c8416..9f8dc00480cbe6681d29325c669d65ad8d1fa44b 100644 (file)
       </listitem>
      </varlistentry>
 
+     <varlistentry>
+      <term><option>--enable-row-security</></term>                                                          <listitem>
+       <para>
+        This option is relevant only when restoring the contents of a table
+        which has row security.  By default, pg_restore will set
+        <literal>ROW_SECURITY</literal> to <literal>OFF</literal>, to ensure
+        that all data is restored in to the table.  If the user does not have
+        sufficient privileges to bypass row security, then an error is thrown.
+        This parameter instructs <application>pg_restore</application> to set
+        row_security to 'ON' instead, allowing the user to attempt to restore
+        the contents of the table with row security enabled.  This may still
+        fail if the user does not have the right to insert the rows from the
+        dump into the table.
+       </para>
+      
+       <para>
+        Note that this option currently also requires the dump be in INSERT
+        format as COPY TO does not support row security.
+       </para>
+
+      </listitem>
+     </varlistentry>
+
      <varlistentry>
       <term><option>--if-exists</option></term>
       <listitem>
index 8d9eeb9dd7f9eeaf5e385c8f05062dbb5e096913..55c1e79563b3e36d65c5777066e0eba2b2910be2 100644 (file)
@@ -799,7 +799,7 @@ InsertPgClassTuple(Relation pg_class_desc,
        values[Anum_pg_class_relhaspkey - 1] = BoolGetDatum(rd_rel->relhaspkey);
        values[Anum_pg_class_relhasrules - 1] = BoolGetDatum(rd_rel->relhasrules);
        values[Anum_pg_class_relhastriggers - 1] = BoolGetDatum(rd_rel->relhastriggers);
-       values[Anum_pg_class_relhasrowsecurity - 1] = BoolGetDatum(rd_rel->relhasrowsecurity);
+       values[Anum_pg_class_relrowsecurity - 1] = BoolGetDatum(rd_rel->relrowsecurity);
        values[Anum_pg_class_relhassubclass - 1] = BoolGetDatum(rd_rel->relhassubclass);
        values[Anum_pg_class_relispopulated - 1] = BoolGetDatum(rd_rel->relispopulated);
        values[Anum_pg_class_relreplident - 1] = CharGetDatum(rd_rel->relreplident);
index f62ed2e17d8aa9aa2cccbbb8bcf0259c2633b7a0..9d9d2394848d56dcaf4626dfbdfabff4582352ad 100644 (file)
@@ -119,7 +119,7 @@ CREATE VIEW pg_tables AS
         C.relhasindex AS hasindexes,
         C.relhasrules AS hasrules,
         C.relhastriggers AS hastriggers,
-        C.relhasrowsecurity AS hasrowsecurity
+        C.relrowsecurity AS rowsecurity
     FROM pg_class C LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
          LEFT JOIN pg_tablespace T ON (T.oid = C.reltablespace)
     WHERE C.relkind = 'r';
index 0cfba566d09674bc2f82defc97a99e63634a3a35..6bff9500c6b9b5bbb199f17efbc2288c20c57e50 100644 (file)
@@ -108,7 +108,7 @@ parse_row_security_command(const char *cmd_name)
        char cmd;
 
        if (!cmd_name)
-               elog(ERROR, "Unregonized command.");
+               elog(ERROR, "unregonized command");
 
        if (strcmp(cmd_name, "all") == 0)
                cmd = 0;
@@ -121,8 +121,7 @@ parse_row_security_command(const char *cmd_name)
        else if (strcmp(cmd_name, "delete") == 0)
                cmd = ACL_DELETE_CHR;
        else
-               elog(ERROR, "Unregonized command.");
-               /* error unrecognized command */
+               elog(ERROR, "unregonized command");
 
        return cmd;
 }
@@ -422,8 +421,8 @@ RemovePolicyById(Oid policy_id)
        heap_close(rel, AccessExclusiveLock);
 
        /*
-        * Note that, unlike some of the other flags in pg_class, relhasrowsecurity
-        * is not just an indication of if policies exist.  When relhasrowsecurity
+        * Note that, unlike some of the other flags in pg_class, relrowsecurity
+        * is not just an indication of if policies exist.  When relrowsecurity
         * is set (which can be done directly by the user or indirectly by creating
         * a policy on the table), then all access to the relation must be through
         * a policy.  If no policy is defined for the relation then a default-deny
@@ -484,7 +483,7 @@ CreatePolicy(CreatePolicyStmt *stmt)
        if (rseccmd == ACL_INSERT_CHR && stmt->qual != NULL)
                ereport(ERROR,
                                (errcode(ERRCODE_SYNTAX_ERROR),
-                                errmsg("Only WITH CHECK expression allowed for INSERT")));
+                                errmsg("only WITH CHECK expression allowed for INSERT")));
 
 
        /* Collect role ids */
@@ -731,7 +730,7 @@ AlterPolicy(AlterPolicyStmt *stmt)
        if (!HeapTupleIsValid(rsec_tuple))
                ereport(ERROR,
                                (errcode(ERRCODE_UNDEFINED_OBJECT),
-                                errmsg("policy '%s' for does not exist on table %s",
+                                errmsg("policy \"%s\" on table \"%s\" does not exist",
                                                stmt->policy_name,
                                                RelationGetRelationName(target_table))));
 
@@ -850,7 +849,7 @@ rename_policy(RenameStmt *stmt)
 
        pg_rowsecurity_rel = heap_open(RowSecurityRelationId, RowExclusiveLock);
 
-       /* First pass- check for conflict */
+       /* First pass -- check for conflict */
 
        /* Add key - row security relation id. */
        ScanKeyInit(&skey[0],
@@ -868,7 +867,7 @@ rename_policy(RenameStmt *stmt)
                                                           RowSecurityRelidPolnameIndexId, true, NULL, 2,
                                                           skey);
 
-       if (HeapTupleIsValid(rsec_tuple = systable_getnext(sscan)))
+       if (HeapTupleIsValid(systable_getnext(sscan)))
                ereport(ERROR,
                                (errcode(ERRCODE_DUPLICATE_OBJECT),
                                 errmsg("row-policy \"%s\" for table \"%s\" already exists",
index 0385404c57804c035f0f98321530c52cadc9c64f..cb16c53a607a18712280c94d81df0df4aab52e05 100644 (file)
@@ -10647,7 +10647,7 @@ ATExecEnableRowSecurity(Relation rel)
        if (!HeapTupleIsValid(tuple))
                elog(ERROR, "cache lookup failed for relation %u", relid);
 
-       ((Form_pg_class) GETSTRUCT(tuple))->relhasrowsecurity = true;
+       ((Form_pg_class) GETSTRUCT(tuple))->relrowsecurity = true;
        simple_heap_update(pg_class, &tuple->t_self, tuple);
 
        /* keep catalog indexes current */
@@ -10674,7 +10674,7 @@ ATExecDisableRowSecurity(Relation rel)
        if (!HeapTupleIsValid(tuple))
                elog(ERROR, "cache lookup failed for relation %u", relid);
 
-       ((Form_pg_class) GETSTRUCT(tuple))->relhasrowsecurity = false;
+       ((Form_pg_class) GETSTRUCT(tuple))->relrowsecurity = false;
        simple_heap_update(pg_class, &tuple->t_self, tuple);
 
        /* keep catalog indexes current */
index e1ccd1295e6951e38a7d1b0998fca00042e0a6a9..bb95b367198273996655ebfc5bcbf5a1a0005693 100644 (file)
@@ -61,7 +61,7 @@ static void process_policies(List *policies, int rt_index,
                                                         Expr **final_qual,
                                                         Expr **final_with_check_qual,
                                                         bool *hassublinks);
-static bool check_role_for_policy(RowSecurityPolicy *policy, Oid user_id);
+static bool check_role_for_policy(ArrayType *policy_roles, Oid user_id);
 
 /*
  * hook to allow extensions to apply their own security policy
@@ -177,7 +177,7 @@ prepend_row_security_policies(Query* root, RangeTblEntry* rte, int rt_index)
         * all of them OR'd together.  However, to avoid the situation of an
         * extension granting more access to a table than the internal policies
         * would allow, the extension's policies are AND'd with the internal
-        * policies.  In other words- extensions can only provide further
+        * policies.  In other words - extensions can only provide further
         * filtering of the result set (or further reduce the set of records
         * allowed to be added).
         *
@@ -305,7 +305,8 @@ pull_row_security_policies(CmdType cmd, Relation relation, Oid user_id)
                policy = (RowSecurityPolicy *) lfirst(item);
 
                /* Always add ALL policies, if they exist. */
-               if (policy->cmd == '\0' && check_role_for_policy(policy, user_id))
+               if (policy->cmd == '\0' &&
+                               check_role_for_policy(policy->roles, user_id))
                        policies = lcons(policy, policies);
 
                /* Build the list of policies to return. */
@@ -313,23 +314,23 @@ pull_row_security_policies(CmdType cmd, Relation relation, Oid user_id)
                {
                        case CMD_SELECT:
                                if (policy->cmd == ACL_SELECT_CHR
-                                       && check_role_for_policy(policy, user_id))
+                                       && check_role_for_policy(policy->roles, user_id))
                                        policies = lcons(policy, policies);
                                break;
                        case CMD_INSERT:
                                /* If INSERT then only need to add the WITH CHECK qual */
                                if (policy->cmd == ACL_INSERT_CHR
-                                       && check_role_for_policy(policy, user_id))
+                                       && check_role_for_policy(policy->roles, user_id))
                                        policies = lcons(policy, policies);
                                break;
                        case CMD_UPDATE:
                                if (policy->cmd == ACL_UPDATE_CHR
-                                       && check_role_for_policy(policy, user_id))
+                                       && check_role_for_policy(policy->roles, user_id))
                                        policies = lcons(policy, policies);
                                break;
                        case CMD_DELETE:
                                if (policy->cmd == ACL_DELETE_CHR
-                                       && check_role_for_policy(policy, user_id))
+                                       && check_role_for_policy(policy->roles, user_id))
                                        policies = lcons(policy, policies);
                                break;
                        default:
@@ -473,7 +474,7 @@ check_enable_rls(Oid relid, Oid checkAsUser)
 {
        HeapTuple               tuple;
        Form_pg_class   classform;
-       bool                    relhasrowsecurity;
+       bool                    relrowsecurity;
        Oid                             user_id = checkAsUser ? checkAsUser : GetUserId();
 
        tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
@@ -482,12 +483,12 @@ check_enable_rls(Oid relid, Oid checkAsUser)
 
        classform = (Form_pg_class) GETSTRUCT(tuple);
 
-       relhasrowsecurity = classform->relhasrowsecurity;
+       relrowsecurity = classform->relrowsecurity;
 
        ReleaseSysCache(tuple);
 
        /* Nothing to do if the relation does not have RLS */
-       if (!relhasrowsecurity)
+       if (!relrowsecurity)
                return RLS_NONE;
 
        /*
@@ -537,19 +538,19 @@ check_enable_rls(Oid relid, Oid checkAsUser)
  * check_role_for_policy -
  *   determines if the policy should be applied for the current role
  */
-bool
-check_role_for_policy(RowSecurityPolicy *policy, Oid user_id)
+static bool
+check_role_for_policy(ArrayType *policy_roles, Oid user_id)
 {
        int                     i;
-       Oid                *roles = (Oid *) ARR_DATA_PTR(policy->roles);
+       Oid                *roles = (Oid *) ARR_DATA_PTR(policy_roles);
 
        /* Quick fall-thru for policies applied to all roles */
        if (roles[0] == ACL_ID_PUBLIC)
                return true;
 
-       for (i = 0; i < ARR_DIMS(policy->roles)[0]; i++)
+       for (i = 0; i < ARR_DIMS(policy_roles)[0]; i++)
        {
-               if (is_member_of_role(user_id, roles[i]))
+               if (has_privs_of_role(user_id, roles[i]))
                        return true;
        }
 
index ed4a3769e45cfdfdb55cd9f57fdab84a89c93312..c0156fab1f9e7092320c5b7286dac37469f9ee60 100644 (file)
@@ -2309,9 +2309,9 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
         * have RLS enabled.
         */
        if (!has_bypassrls_privilege(GetUserId()) &&
-               ((pk_rel->rd_rel->relhasrowsecurity &&
+               ((pk_rel->rd_rel->relrowsecurity &&
                  !pg_class_ownercheck(pkrte->relid, GetUserId())) ||
-                (fk_rel->rd_rel->relhasrowsecurity &&
+                (fk_rel->rd_rel->relrowsecurity &&
                  !pg_class_ownercheck(fkrte->relid, GetUserId()))))
                return false;
 
index e7f7129bd9dada7a62aa266e4bac2b1edc788ae2..c98e31328836822b738891ea9e1b55fbadee4aa2 100644 (file)
@@ -847,6 +847,87 @@ equalRuleLocks(RuleLock *rlock1, RuleLock *rlock2)
        return true;
 }
 
+/*
+ *             equalPolicy
+ *
+ *             Determine whether two policies are equivalent
+ */
+static bool
+equalPolicy(RowSecurityPolicy *policy1, RowSecurityPolicy *policy2)
+{
+       int                     i;
+       Oid        *r1,
+                  *r2;
+
+       if (policy1 != NULL)
+       {
+               if (policy2 == NULL)
+                       return false;
+
+               if (policy1->rsecid != policy2->rsecid)
+                       return false;
+               if (policy1->cmd != policy2->cmd)
+                       return false;
+               if (policy1->hassublinks != policy2->hassublinks);
+                       return false;
+               if (strcmp(policy1->policy_name,policy2->policy_name) != 0)
+                       return false;
+               if (ARR_DIMS(policy1->roles)[0] != ARR_DIMS(policy2->roles)[0])
+                       return false;
+
+               r1 = (Oid *) ARR_DATA_PTR(policy1->roles);
+               r2 = (Oid *) ARR_DATA_PTR(policy2->roles);
+
+               for (i = 0; i < ARR_DIMS(policy1->roles)[0]; i++)
+               {
+                       if (r1[i] != r2[i])
+                               return false;
+               }
+
+               if (!equal(policy1->qual, policy1->qual))
+                       return false;
+               if (!equal(policy1->with_check_qual, policy2->with_check_qual))
+                       return false;
+       }
+       else if (policy2 != NULL)
+               return false;
+
+       return true;
+}
+
+/*
+ *             equalRSDesc
+ *
+ *             Determine whether two RowSecurityDesc's are equivalent
+ */
+static bool
+equalRSDesc(RowSecurityDesc *rsdesc1, RowSecurityDesc *rsdesc2)
+{
+       ListCell        *lc,
+                               *rc;
+
+       if (rsdesc1 == NULL && rsdesc2 == NULL)
+               return true;
+
+       if ((rsdesc1 != NULL && rsdesc2 == NULL) ||
+               (rsdesc1 == NULL && rsdesc2 != NULL))
+               return false;
+
+       if (list_length(rsdesc1->policies) != list_length(rsdesc2->policies))
+               return false;
+
+       /* RelationBuildRowSecurity should build policies in order */
+       forboth(lc, rsdesc1->policies, rc, rsdesc2->policies)
+       {
+               RowSecurityPolicy          *l = (RowSecurityPolicy *) lfirst(lc);
+               RowSecurityPolicy          *r = (RowSecurityPolicy *) lfirst(rc);
+
+               if (!equalPolicy(l,r))
+                       return false;
+       }
+
+       return false;
+}
 
 /*
  *             RelationBuildDesc
@@ -967,7 +1048,7 @@ RelationBuildDesc(Oid targetRelId, bool insertIt)
        else
                relation->trigdesc = NULL;
 
-       if (relation->rd_rel->relhasrowsecurity)
+       if (relation->rd_rel->relrowsecurity)
                RelationBuildRowSecurity(relation);
        else
                relation->rsdesc = NULL;
@@ -2104,6 +2185,7 @@ RelationClearRelation(Relation relation, bool rebuild)
                Oid                     save_relid = RelationGetRelid(relation);
                bool            keep_tupdesc;
                bool            keep_rules;
+               bool            keep_policies;
 
                /* Build temporary entry, but don't link it into hashtable */
                newrel = RelationBuildDesc(save_relid, false);
@@ -2117,6 +2199,7 @@ RelationClearRelation(Relation relation, bool rebuild)
 
                keep_tupdesc = equalTupleDescs(relation->rd_att, newrel->rd_att);
                keep_rules = equalRuleLocks(relation->rd_rules, newrel->rd_rules);
+               keep_policies = equalRSDesc(relation->rsdesc, newrel->rsdesc);
 
                /*
                 * Perform swapping of the relcache entry contents.  Within this
@@ -2165,6 +2248,8 @@ RelationClearRelation(Relation relation, bool rebuild)
                        SWAPFIELD(RuleLock *, rd_rules);
                        SWAPFIELD(MemoryContext, rd_rulescxt);
                }
+               if (keep_policies)
+                       SWAPFIELD(RowSecurityDesc *, rsdesc);
                /* toast OID override must be preserved */
                SWAPFIELD(Oid, rd_toastoid);
                /* pgstat_info must be preserved */
@@ -3345,11 +3430,11 @@ RelationCacheInitializePhase3(void)
                /*
                 * Re-load the row security policies if the relation has them, since
                 * they are not preserved in the cache.  Note that we can never NOT
-                * have a policy while relhasrowsecurity is true-
+                * have a policy while relrowsecurity is true,
                 * RelationBuildRowSecurity will create a single default-deny policy
                 * if there is no policy defined in pg_rowsecurity.
                 */
-               if (relation->rd_rel->relhasrowsecurity && relation->rsdesc == NULL)
+               if (relation->rd_rel->relrowsecurity && relation->rsdesc == NULL)
                {
                        RelationBuildRowSecurity(relation);
 
index 5476a1e7e2b5a811d5ce7b5e5c4a4551a3770002..3b101d44835a8f62491446265d515d834d2bb876 100644 (file)
@@ -376,10 +376,13 @@ RestoreArchive(Archive *AHX)
        /*
         * Enable row-security if necessary.
         */
-       if (!ropt->enable_row_security)
-               ahprintf(AH, "SET row_security = off;\n");
-       else
-               ahprintf(AH, "SET row_security = on;\n");
+       if (PQserverVersion(AH->connection) >= 90500)
+       {
+               if (!ropt->enable_row_security)
+                       ahprintf(AH, "SET row_security = off;\n");
+               else
+                       ahprintf(AH, "SET row_security = on;\n");
+       }
 
        /*
         * Establish important parameter values right away.
index 29153294e262fcb9ca83b7e49d4ec62a2a6135f5..12811a801a34e5b2d929890705a58468a8bd4b15 100644 (file)
@@ -2777,7 +2777,7 @@ dumpBlobs(Archive *fout, void *arg)
 void
 getRowSecurity(Archive *fout, TableInfo tblinfo[], int numTables)
 {
-       PQExpBuffer             query = createPQExpBuffer();
+       PQExpBuffer             query;
        PGresult           *res;
        RowSecurityInfo *rsinfo;
        int                             i_oid;
@@ -2792,6 +2792,8 @@ getRowSecurity(Archive *fout, TableInfo tblinfo[], int numTables)
        if (fout->remoteVersion < 90500)
                return;
 
+       query = createPQExpBuffer();
+
        for (i = 0; i < numTables; i++)
        {
                TableInfo *tbinfo = &tblinfo[i];
@@ -2809,7 +2811,7 @@ getRowSecurity(Archive *fout, TableInfo tblinfo[], int numTables)
                 * We represent RLS enabled on a table by creating RowSecurityInfo
                 * object with an empty policy.
                 */
-               if (tbinfo->hasrowsec)
+               if (tbinfo->rowsec)
                {
                        /*
                         * Note: use tableoid 0 so that this object won't be mistaken for
@@ -4534,7 +4536,7 @@ getTables(Archive *fout, int *numTables)
        int                     i_relhastriggers;
        int                     i_relhasindex;
        int                     i_relhasrules;
-       int                     i_relhasrowsec;
+       int                     i_relrowsec;
        int                     i_relhasoids;
        int                     i_relfrozenxid;
        int                     i_relminmxid;
@@ -4588,7 +4590,7 @@ getTables(Archive *fout, int *numTables)
                                                  "(%s c.relowner) AS rolname, "
                                                  "c.relchecks, c.relhastriggers, "
                                                  "c.relhasindex, c.relhasrules, c.relhasoids, "
-                                                 "c.relhasrowsecurity, "
+                                                 "c.relrowsecurity, "
                                                  "c.relfrozenxid, c.relminmxid, tc.oid AS toid, "
                                                  "tc.relfrozenxid AS tfrozenxid, "
                                                  "tc.relminmxid AS tminmxid, "
@@ -4629,7 +4631,7 @@ getTables(Archive *fout, int *numTables)
                                                  "(%s c.relowner) AS rolname, "
                                                  "c.relchecks, c.relhastriggers, "
                                                  "c.relhasindex, c.relhasrules, c.relhasoids, "
-                                                 "'f'::bool AS relhasrowsecurity, "
+                                                 "'f'::bool AS relrowsecurity, "
                                                  "c.relfrozenxid, c.relminmxid, tc.oid AS toid, "
                                                  "tc.relfrozenxid AS tfrozenxid, "
                                                  "tc.relminmxid AS tminmxid, "
@@ -4670,7 +4672,7 @@ getTables(Archive *fout, int *numTables)
                                                  "(%s c.relowner) AS rolname, "
                                                  "c.relchecks, c.relhastriggers, "
                                                  "c.relhasindex, c.relhasrules, c.relhasoids, "
-                                                 "'f'::bool AS relhasrowsecurity, "
+                                                 "'f'::bool AS relrowsecurity, "
                                                  "c.relfrozenxid, c.relminmxid, tc.oid AS toid, "
                                                  "tc.relfrozenxid AS tfrozenxid, "
                                                  "tc.relminmxid AS tminmxid, "
@@ -4711,7 +4713,7 @@ getTables(Archive *fout, int *numTables)
                                                  "(%s c.relowner) AS rolname, "
                                                  "c.relchecks, c.relhastriggers, "
                                                  "c.relhasindex, c.relhasrules, c.relhasoids, "
-                                                 "'f'::bool AS relhasrowsecurity, "
+                                                 "'f'::bool AS relrowsecurity, "
                                                  "c.relfrozenxid, 0 AS relminmxid, tc.oid AS toid, "
                                                  "tc.relfrozenxid AS tfrozenxid, "
                                                  "0 AS tminmxid, "
@@ -4750,7 +4752,7 @@ getTables(Archive *fout, int *numTables)
                                                  "(%s c.relowner) AS rolname, "
                                                  "c.relchecks, c.relhastriggers, "
                                                  "c.relhasindex, c.relhasrules, c.relhasoids, "
-                                                 "'f'::bool AS relhasrowsecurity, "
+                                                 "'f'::bool AS relrowsecurity, "
                                                  "c.relfrozenxid, 0 AS relminmxid, tc.oid AS toid, "
                                                  "tc.relfrozenxid AS tfrozenxid, "
                                                  "0 AS tminmxid, "
@@ -4788,7 +4790,7 @@ getTables(Archive *fout, int *numTables)
                                                  "(%s c.relowner) AS rolname, "
                                                  "c.relchecks, c.relhastriggers, "
                                                  "c.relhasindex, c.relhasrules, c.relhasoids, "
-                                                 "'f'::bool AS relhasrowsecurity, "
+                                                 "'f'::bool AS relrowsecurity, "
                                                  "c.relfrozenxid, 0 AS relminmxid, tc.oid AS toid, "
                                                  "tc.relfrozenxid AS tfrozenxid, "
                                                  "0 AS tminmxid, "
@@ -4826,7 +4828,7 @@ getTables(Archive *fout, int *numTables)
                                                  "(%s c.relowner) AS rolname, "
                                          "c.relchecks, (c.reltriggers <> 0) AS relhastriggers, "
                                                  "c.relhasindex, c.relhasrules, c.relhasoids, "
-                                                 "'f'::bool AS relhasrowsecurity, "
+                                                 "'f'::bool AS relrowsecurity, "
                                                  "c.relfrozenxid, 0 AS relminmxid, tc.oid AS toid, "
                                                  "tc.relfrozenxid AS tfrozenxid, "
                                                  "0 AS tminmxid, "
@@ -4864,7 +4866,7 @@ getTables(Archive *fout, int *numTables)
                                                  "(%s relowner) AS rolname, "
                                                  "relchecks, (reltriggers <> 0) AS relhastriggers, "
                                                  "relhasindex, relhasrules, relhasoids, "
-                                                 "'f'::bool AS relhasrowsecurity, "
+                                                 "'f'::bool AS relrowsecurity, "
                                                  "0 AS relfrozenxid, 0 AS relminmxid,"
                                                  "0 AS toid, "
                                                  "0 AS tfrozenxid, 0 AS tminmxid,"
@@ -4901,7 +4903,7 @@ getTables(Archive *fout, int *numTables)
                                                  "(%s relowner) AS rolname, "
                                                  "relchecks, (reltriggers <> 0) AS relhastriggers, "
                                                  "relhasindex, relhasrules, relhasoids, "
-                                                 "'f'::bool AS relhasrowsecurity, "
+                                                 "'f'::bool AS relrowsecurity, "
                                                  "0 AS relfrozenxid, 0 AS relminmxid,"
                                                  "0 AS toid, "
                                                  "0 AS tfrozenxid, 0 AS tminmxid,"
@@ -4934,7 +4936,7 @@ getTables(Archive *fout, int *numTables)
                                                  "(%s relowner) AS rolname, "
                                                  "relchecks, (reltriggers <> 0) AS relhastriggers, "
                                                  "relhasindex, relhasrules, relhasoids, "
-                                                 "'f'::bool AS relhasrowsecurity, "
+                                                 "'f'::bool AS relrowsecurity, "
                                                  "0 AS relfrozenxid, 0 AS relminmxid,"
                                                  "0 AS toid, "
                                                  "0 AS tfrozenxid, 0 AS tminmxid,"
@@ -4962,7 +4964,7 @@ getTables(Archive *fout, int *numTables)
                                                  "relchecks, (reltriggers <> 0) AS relhastriggers, "
                                                  "relhasindex, relhasrules, "
                                                  "'t'::bool AS relhasoids, "
-                                                 "'f'::bool AS relhasrowsecurity, "
+                                                 "'f'::bool AS relrowsecurity, "
                                                  "0 AS relfrozenxid, 0 AS relminmxid,"
                                                  "0 AS toid, "
                                                  "0 AS tfrozenxid, 0 AS tminmxid,"
@@ -5000,7 +5002,7 @@ getTables(Archive *fout, int *numTables)
                                                  "relchecks, (reltriggers <> 0) AS relhastriggers, "
                                                  "relhasindex, relhasrules, "
                                                  "'t'::bool AS relhasoids, "
-                                                 "'f'::bool AS relhasrowsecurity, "
+                                                 "'f'::bool AS relrowsecurity, "
                                                  "0 AS relfrozenxid, 0 AS relminmxid,"
                                                  "0 AS toid, "
                                                  "0 AS tfrozenxid, 0 AS tminmxid,"
@@ -5048,7 +5050,7 @@ getTables(Archive *fout, int *numTables)
        i_relhastriggers = PQfnumber(res, "relhastriggers");
        i_relhasindex = PQfnumber(res, "relhasindex");
        i_relhasrules = PQfnumber(res, "relhasrules");
-       i_relhasrowsec = PQfnumber(res, "relhasrowsecurity");
+       i_relrowsec = PQfnumber(res, "relrowsecurity");
        i_relhasoids = PQfnumber(res, "relhasoids");
        i_relfrozenxid = PQfnumber(res, "relfrozenxid");
        i_relminmxid = PQfnumber(res, "relminmxid");
@@ -5100,7 +5102,7 @@ getTables(Archive *fout, int *numTables)
                tblinfo[i].hasindex = (strcmp(PQgetvalue(res, i, i_relhasindex), "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].hasrowsec = (strcmp(PQgetvalue(res, i, i_relhasrowsec), "t") == 0);
+               tblinfo[i].rowsec = (strcmp(PQgetvalue(res, i, i_relrowsec), "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].relreplident = *(PQgetvalue(res, i, i_relreplident));
index b5d820e7616707e8e66b14b75ac7e0a8d0a45c93..646a2077a6126528423ae83a757307b01024a41d 100644 (file)
@@ -246,7 +246,7 @@ typedef struct _tableInfo
        bool            hasindex;               /* does it have any indexes? */
        bool            hasrules;               /* does it have any rules? */
        bool            hastriggers;    /* does it have any triggers? */
-       bool            hasrowsec;              /* does it have any row-security policy? */
+       bool            rowsec;                 /* does it have any row-security policy? */
        bool            hasoids;                /* does it have OIDs? */
        uint32          frozenxid;              /* for restore frozen xid */
        uint32          minmxid;                /* for restore min multi xid */
index 1c1b80f13744e9b79d23396f97b37a892bbae36c..21715dc944330bf00a46edbfd08103e56bd2a2b8 100644 (file)
@@ -463,7 +463,7 @@ usage(const char *progname)
        printf(_("  -x, --no-privileges          skip restoration of access privileges (grant/revoke)\n"));
        printf(_("  -1, --single-transaction     restore as a single transaction\n"));
        printf(_("  --disable-triggers           disable triggers during data-only restore\n"));
-       printf(_("  --enable-row-security         enable row level security\n"));
+       printf(_("  --enable-row-security        enable row level security\n"));
        printf(_("  --if-exists                  use IF EXISTS when dropping objects\n"));
        printf(_("  --no-data-for-failed-tables  do not restore data of tables that could not be\n"
                         "                               created\n"));
index 97dc2dded2cc5a75cf5117d89a90b5552e021625..074be5769669e0bcc729389b3866746b93424b28 100644 (file)
@@ -1204,7 +1204,7 @@ describeOneTableDetails(const char *schemaname,
                bool            hasindex;
                bool            hasrules;
                bool            hastriggers;
-               bool            hasrowsecurity;
+               bool            rowsecurity;
                bool            hasoids;
                Oid                     tablespace;
                char       *reloptions;
@@ -1230,7 +1230,7 @@ describeOneTableDetails(const char *schemaname,
        {
                printfPQExpBuffer(&buf,
                          "SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, "
-                                                 "c.relhastriggers, c.relhasrowsecurity, c.relhasoids, "
+                                                 "c.relhastriggers, c.relrowsecurity, c.relhasoids, "
                                                  "%s, c.reltablespace, "
                                                  "CASE WHEN c.reloftype = 0 THEN '' ELSE c.reloftype::pg_catalog.regtype::pg_catalog.text END, "
                                                  "c.relpersistence, c.relreplident\n"
@@ -1355,7 +1355,7 @@ describeOneTableDetails(const char *schemaname,
        tableinfo.hasindex = strcmp(PQgetvalue(res, 0, 2), "t") == 0;
        tableinfo.hasrules = strcmp(PQgetvalue(res, 0, 3), "t") == 0;
        tableinfo.hastriggers = strcmp(PQgetvalue(res, 0, 4), "t") == 0;
-       tableinfo.hasrowsecurity = 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.reloptions = (pset.sversion >= 80200) ?
                pg_strdup(PQgetvalue(res, 0, 7)) : NULL;
@@ -1998,18 +1998,17 @@ describeOneTableDetails(const char *schemaname,
                        PQclear(result);
                }
 
-
+               /* print any row-level policies */
                if (pset.sversion >= 90500)
+               {
                        appendPQExpBuffer(&buf,
                                ",\n pg_catalog.pg_get_expr(rs.rsecqual, c.oid) as \"%s\"",
                                gettext_noop("Row-security"));
-       if (verbose && pset.sversion >= 90500)
-               appendPQExpBuffer(&buf,
-                        "\n     LEFT JOIN pg_rowsecurity rs ON rs.rsecrelid = c.oid");
 
-               /* print any row-level policies */
-               if (tableinfo.hasrowsecurity)
-               {
+                       if (verbose)
+                               appendPQExpBuffer(&buf,
+                                       "\n     LEFT JOIN pg_rowsecurity rs ON rs.rsecrelid = c.oid");
+
                        printfPQExpBuffer(&buf,
                                                   "SELECT rs.rsecpolname,\n"
                                                   "CASE WHEN rs.rsecroles = '{0}' THEN NULL ELSE array(select rolname from pg_roles where oid = any (rs.rsecroles) order by 1) END,\n"
@@ -2019,41 +2018,53 @@ describeOneTableDetails(const char *schemaname,
                                                          "FROM pg_catalog.pg_rowsecurity rs\n"
                                  "WHERE rs.rsecrelid = '%s' ORDER BY 1;",
                                                          oid);
+
                        result = PSQLexec(buf.data, false);
                        if (!result)
                                goto error_return;
                        else
                                tuples = PQntuples(result);
 
-                       if (tuples > 0)
-                       {
+                       /*
+                        * Handle cases where RLS is enabled and there are policies,
+                        * or there aren't policies, or RLS isn't enabled but there
+                        * are policies
+                        */
+                       if (tableinfo.rowsecurity && tuples > 0)
                                printTableAddFooter(&cont, _("Policies:"));
-                               for (i = 0; i < tuples; i++)
-                               {
-                                       printfPQExpBuffer(&buf, "    POLICY \"%s\"",
-                                                                                 PQgetvalue(result, i, 0));
 
-                                       if (!PQgetisnull(result, i, 4))
-                                               appendPQExpBuffer(&buf, " (%s)",
-                                                                                 PQgetvalue(result, i, 4));
+                       if (tableinfo.rowsecurity && tuples == 0)
+                               printTableAddFooter(&cont, _("Policies (Row Security Enabled): (None)"));
 
-                                       if (!PQgetisnull(result, i, 2))
-                                               appendPQExpBuffer(&buf, " EXPRESSION %s",
-                                                                                 PQgetvalue(result, i, 2));
+                       if (!tableinfo.rowsecurity && tuples > 0)
+                               printTableAddFooter(&cont, _("Policies (Row Security Disabled):"));
 
-                                       if (!PQgetisnull(result, i, 3))
-                                               appendPQExpBuffer(&buf, " WITH CHECK %s",
-                                                                                 PQgetvalue(result, i, 3));
+                       /* Might be an empty set - that's ok */
+                       for (i = 0; i < tuples; i++)
+                       {
+                               printfPQExpBuffer(&buf, "    POLICY \"%s\"",
+                                                                         PQgetvalue(result, i, 0));
 
-                                       printTableAddFooter(&cont, buf.data);
+                               if (!PQgetisnull(result, i, 4))
+                                       appendPQExpBuffer(&buf, " (%s)",
+                                                                         PQgetvalue(result, i, 4));
 
-                                       if (!PQgetisnull(result, i, 1))
-                                       {
-                                               printfPQExpBuffer(&buf, "          APPLIED TO %s",
-                                                                                 PQgetvalue(result, i, 1));
+                               if (!PQgetisnull(result, i, 2))
+                                       appendPQExpBuffer(&buf, " EXPRESSION %s",
+                                                                         PQgetvalue(result, i, 2));
 
-                                               printTableAddFooter(&cont, buf.data);
-                                       }
+                               if (!PQgetisnull(result, i, 3))
+                                       appendPQExpBuffer(&buf, " WITH CHECK %s",
+                                                                         PQgetvalue(result, i, 3));
+
+                               printTableAddFooter(&cont, buf.data);
+
+                               if (!PQgetisnull(result, i, 1))
+                               {
+                                       printfPQExpBuffer(&buf, "          APPLIED TO %s",
+                                                                         PQgetvalue(result, i, 1));
+
+                                       printTableAddFooter(&cont, buf.data);
                                }
                        }
                        PQclear(result);
@@ -2708,6 +2719,10 @@ describeRoles(const char *pattern, bool verbose)
                        if (strcmp(PQgetvalue(res, i, (verbose ? 10 : 9)), "t") == 0)
                                add_role_attribute(&buf, _("Replication"));
 
+               if (pset.sversion >= 90500)
+                       if (strcmp(PQgetvalue(res, i, (verbose ? 11 : 10)), "t") == 0)
+                               add_role_attribute(&buf, _("Bypass RLS"));
+
                conns = atoi(PQgetvalue(res, i, 6));
                if (conns >= 0)
                {
index a4594b6783c7dc1aae20f0b59db8265a2f55f1cf..886188c036facd6659a9047dce935477726c5a12 100644 (file)
@@ -1214,11 +1214,12 @@ psql_completion(const char *text, int start, int end)
                          pg_strcasecmp(prev2_wd, "ROLE") == 0))
        {
                static const char *const list_ALTERUSER[] =
-               {"CONNECTION LIMIT", "CREATEDB", "CREATEROLE", "CREATEUSER",
-                       "ENCRYPTED", "INHERIT", "LOGIN", "NOCREATEDB", "NOCREATEROLE",
-                       "NOCREATEUSER", "NOINHERIT", "NOLOGIN", "NOREPLICATION",
-                       "NOSUPERUSER", "RENAME TO", "REPLICATION", "RESET", "SET",
-               "SUPERUSER", "UNENCRYPTED", "VALID UNTIL", "WITH", NULL};
+               {"BYPASSRLS", "CONNECTION LIMIT", "CREATEDB", "CREATEROLE",
+                       "CREATEUSER", "ENCRYPTED", "INHERIT", "LOGIN", "NOBYPASSRLS",
+                       "NOCREATEDB", "NOCREATEROLE", "NOCREATEUSER", "NOINHERIT",
+                       "NOLOGIN", "NOREPLICATION", "NOSUPERUSER", "RENAME TO",
+                       "REPLICATION", "RESET", "SET", "SUPERUSER", "UNENCRYPTED",
+               "VALID UNTIL", "WITH", NULL};
 
                COMPLETE_WITH_LIST(list_ALTERUSER);
        }
@@ -1231,11 +1232,12 @@ psql_completion(const char *text, int start, int end)
        {
                /* Similar to the above, but don't complete "WITH" again. */
                static const char *const list_ALTERUSER_WITH[] =
-               {"CONNECTION LIMIT", "CREATEDB", "CREATEROLE", "CREATEUSER",
-                       "ENCRYPTED", "INHERIT", "LOGIN", "NOCREATEDB", "NOCREATEROLE",
-                       "NOCREATEUSER", "NOINHERIT", "NOLOGIN", "NOREPLICATION",
-                       "NOSUPERUSER", "RENAME TO", "REPLICATION", "RESET", "SET",
-               "SUPERUSER", "UNENCRYPTED", "VALID UNTIL", NULL};
+               {"BYPASSRLS", "CONNECTION LIMIT", "CREATEDB", "CREATEROLE",
+                       "CREATEUSER", "ENCRYPTED", "INHERIT", "LOGIN", "NOBYPASSRLS",
+                       "NOCREATEDB", "NOCREATEROLE", "NOCREATEUSER", "NOINHERIT",
+                       "NOLOGIN", "NOREPLICATION", "NOSUPERUSER", "RENAME TO",
+                       "REPLICATION", "RESET", "SET", "SUPERUSER", "UNENCRYPTED",
+               "VALID UNTIL", NULL};
 
                COMPLETE_WITH_LIST(list_ALTERUSER_WITH);
        }
@@ -2565,10 +2567,10 @@ psql_completion(const char *text, int start, int end)
                          pg_strcasecmp(prev2_wd, "GROUP") == 0 || pg_strcasecmp(prev2_wd, "USER") == 0))
        {
                static const char *const list_CREATEROLE[] =
-               {"ADMIN", "CONNECTION LIMIT", "CREATEDB", "CREATEROLE", "CREATEUSER",
-                       "ENCRYPTED", "IN", "INHERIT", "LOGIN", "NOCREATEDB",
-                       "NOCREATEROLE", "NOCREATEUSER", "NOINHERIT", "NOLOGIN",
-                       "NOREPLICATION", "NOSUPERUSER", "REPLICATION", "ROLE",
+               {"ADMIN", "BYPASSRLS", "CONNECTION LIMIT", "CREATEDB", "CREATEROLE",
+                       "CREATEUSER", "ENCRYPTED", "IN", "INHERIT", "LOGIN", "NOBYPASSRLS",
+                       "NOCREATEDB", "NOCREATEROLE", "NOCREATEUSER", "NOINHERIT",
+                       "NOLOGIN", "NOREPLICATION", "NOSUPERUSER", "REPLICATION", "ROLE",
                "SUPERUSER", "SYSID", "UNENCRYPTED", "VALID UNTIL", "WITH", NULL};
 
                COMPLETE_WITH_LIST(list_CREATEROLE);
@@ -2583,10 +2585,10 @@ psql_completion(const char *text, int start, int end)
        {
                /* Similar to the above, but don't complete "WITH" again. */
                static const char *const list_CREATEROLE_WITH[] =
-               {"ADMIN", "CONNECTION LIMIT", "CREATEDB", "CREATEROLE", "CREATEUSER",
-                       "ENCRYPTED", "IN", "INHERIT", "LOGIN", "NOCREATEDB",
-                       "NOCREATEROLE", "NOCREATEUSER", "NOINHERIT", "NOLOGIN",
-                       "NOREPLICATION", "NOSUPERUSER", "REPLICATION", "ROLE",
+               {"ADMIN", "BYPASSRLS", "CONNECTION LIMIT", "CREATEDB", "CREATEROLE",
+                       "CREATEUSER", "ENCRYPTED", "IN", "INHERIT", "LOGIN", "NOBYPASSRLS",
+                       "NOCREATEDB", "NOCREATEROLE", "NOCREATEUSER", "NOINHERIT",
+                       "NOLOGIN", "NOREPLICATION", "NOSUPERUSER", "REPLICATION", "ROLE",
                "SUPERUSER", "SYSID", "UNENCRYPTED", "VALID UNTIL", NULL};
 
                COMPLETE_WITH_LIST(list_CREATEROLE_WITH);
index af0475e8314387e892944e7b22117c4a9faa4b10..bee67ddb373d00ee28dc7f974e48fa259dbd751d 100644 (file)
@@ -53,6 +53,6 @@
  */
 
 /*                                                     yyyymmddN */
-#define CATALOG_VERSION_NO     201409191
+#define CATALOG_VERSION_NO     201409241
 
 #endif
index f6353514cacfbf7f33f1eb37f540c393a3698292..22c55a94903b63cfb30d11e56d2141de9889faa3 100644 (file)
@@ -65,7 +65,7 @@ CATALOG(pg_class,1259) BKI_BOOTSTRAP BKI_ROWTYPE_OID(83) BKI_SCHEMA_MACRO
        bool            relhasrules;    /* has (or has had) any rules */
        bool            relhastriggers; /* has (or has had) any TRIGGERs */
        bool            relhassubclass; /* has (or has had) derived classes */
-       bool            relhasrowsecurity;      /* has (or has had) row-security policy */
+       bool            relrowsecurity; /* row-security is enabled or not */
        bool            relispopulated; /* matview currently holds query results */
        char            relreplident;   /* see REPLICA_IDENTITY_xxx constants  */
        TransactionId relfrozenxid; /* all Xids < this are frozen in this rel */
@@ -119,7 +119,7 @@ typedef FormData_pg_class *Form_pg_class;
 #define Anum_pg_class_relhasrules              21
 #define Anum_pg_class_relhastriggers   22
 #define Anum_pg_class_relhassubclass   23
-#define Anum_pg_class_relhasrowsecurity        24
+#define Anum_pg_class_relrowsecurity   24
 #define Anum_pg_class_relispopulated   25
 #define Anum_pg_class_relreplident             26
 #define Anum_pg_class_relfrozenxid             27
index 95d8a6d11751612b3cbcf17ec79f6ab5308f2762..fcc991173b7c653708166cd6ae72289dfb2538c7 100644 (file)
@@ -16,6 +16,7 @@
 #define POLICY_H
 
 #include "nodes/parsenodes.h"
+#include "utils/relcache.h"
 
 extern void RelationBuildRowSecurity(Relation relation);
 
@@ -24,10 +25,10 @@ extern void RemovePolicyById(Oid policy_id);
 extern Oid CreatePolicy(CreatePolicyStmt *stmt);
 extern Oid AlterPolicy(AlterPolicyStmt *stmt);
 
-Oid get_relation_policy_oid(Oid relid,
-                                                       const char *policy_name, bool missing_ok);
+extern Oid get_relation_policy_oid(Oid relid, const char *policy_name,
+                                               bool missing_ok);
 
-Oid rename_policy(RenameStmt *stmt);
+extern Oid rename_policy(RenameStmt *stmt);
 
 
 #endif   /* POLICY_H */
index 889bcd201fcfd3ea288f893ad8e97391f10bdd94..c53e7851cc9a2b9112501d4fd7d48aa13df977d7 100644 (file)
@@ -2046,7 +2046,7 @@ pg_tables| SELECT n.nspname AS schemaname,
     c.relhasindex AS hasindexes,
     c.relhasrules AS hasrules,
     c.relhastriggers AS hastriggers,
-    c.relhasrowsecurity AS hasrowsecurity
+    c.relrowsecurity AS rowsecurity
    FROM ((pg_class c
      LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace)))
      LEFT JOIN pg_tablespace t ON ((t.oid = c.reltablespace)))