Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Disallow converting a table to a view if row security is present.
authorJoe Conway <mail@joeconway.com>
Tue, 28 Jul 2015 23:24:09 +0000 (16:24 -0700)
committerJoe Conway <mail@joeconway.com>
Tue, 28 Jul 2015 23:24:09 +0000 (16:24 -0700)
When DefineQueryRewrite() is about to convert a table to a view, it checks
the table for features unavailable to views.  For example, it rejects tables
having triggers.  It omits to reject tables having relrowsecurity or a
pg_policy record. Fix that. To faciliate the repair, invent
relation_has_policies() which indicates the presence of policies on a
relation even when row security is disabled for that relation.

Reported by Noah Misch. Patch by me, review by Stephen Frost. Back-patch
to 9.5 where RLS was introduced.

src/backend/commands/policy.c
src/backend/rewrite/rewriteDefine.c
src/include/commands/policy.h
src/test/regress/expected/rowsecurity.out
src/test/regress/sql/rowsecurity.sql

index 9544f75032b4b1bf614686b0aaf8ad3064cbde73..0d4e557d5abfe4092180e32e56e2f364dec1a681 100644 (file)
@@ -1037,3 +1037,32 @@ get_relation_policy_oid(Oid relid, const char *policy_name, bool missing_ok)
 
    return policy_oid;
 }
+
+/*
+ * relation_has_policies - Determine if relation has any policies
+ */
+bool
+relation_has_policies(Relation rel)
+{
+   Relation    catalog;
+   ScanKeyData skey;
+   SysScanDesc sscan;
+   HeapTuple   policy_tuple;
+   bool        ret = false;
+
+   catalog = heap_open(PolicyRelationId, AccessShareLock);
+   ScanKeyInit(&skey,
+               Anum_pg_policy_polrelid,
+               BTEqualStrategyNumber, F_OIDEQ,
+               ObjectIdGetDatum(RelationGetRelid(rel)));
+   sscan = systable_beginscan(catalog, PolicyPolrelidPolnameIndexId, true,
+                              NULL, 1, &skey);
+   policy_tuple = systable_getnext(sscan);
+   if (HeapTupleIsValid(policy_tuple))
+       ret = true;
+
+   systable_endscan(sscan);
+   heap_close(catalog, AccessShareLock);
+
+   return ret;
+}
index a88d73e15f2a2c7465e99bf58e2d050f65f5419a..39c83a605ca10d2bcafbfb4d58b22be36241833a 100644 (file)
@@ -27,6 +27,7 @@
 #include "catalog/objectaccess.h"
 #include "catalog/pg_rewrite.h"
 #include "catalog/storage.h"
+#include "commands/policy.h"
 #include "miscadmin.h"
 #include "nodes/nodeFuncs.h"
 #include "parser/parse_utilcmd.h"
@@ -410,11 +411,12 @@ DefineQueryRewrite(char *rulename,
         *
         * If so, check that the relation is empty because the storage for the
         * relation is going to be deleted.  Also insist that the rel not have
-        * any triggers, indexes, or child tables.  (Note: these tests are too
-        * strict, because they will reject relations that once had such but
-        * don't anymore.  But we don't really care, because this whole
-        * business of converting relations to views is just a kluge to allow
-        * dump/reload of views that participate in circular dependencies.)
+        * any triggers, indexes, child tables, policies, or RLS enabled.
+        * (Note: these tests are too strict, because they will reject
+        * relations that once had such but don't anymore.  But we don't
+        * really care, because this whole business of converting relations
+        * to views is just a kluge to allow dump/reload of views that
+        * participate in circular dependencies.)
         */
        if (event_relation->rd_rel->relkind != RELKIND_VIEW &&
            event_relation->rd_rel->relkind != RELKIND_MATVIEW)
@@ -451,6 +453,18 @@ DefineQueryRewrite(char *rulename,
                         errmsg("could not convert table \"%s\" to a view because it has child tables",
                                RelationGetRelationName(event_relation))));
 
+           if (event_relation->rd_rel->relrowsecurity)
+               ereport(ERROR,
+                       (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+                        errmsg("could not convert table \"%s\" to a view because it has row security enabled",
+                               RelationGetRelationName(event_relation))));
+
+           if (relation_has_policies(event_relation))
+               ereport(ERROR,
+                       (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+                        errmsg("could not convert table \"%s\" to a view because it has row security policies",
+                               RelationGetRelationName(event_relation))));
+
            RelisBecomingView = true;
        }
    }
index ac322e0db9263519a8e8edac64b7885460a1c31f..be000432312ff2e2b6e23a2800b1d5ccdb62dff6 100644 (file)
@@ -31,5 +31,6 @@ extern Oid get_relation_policy_oid(Oid relid, const char *policy_name,
 
 extern ObjectAddress rename_policy(RenameStmt *stmt);
 
+extern bool relation_has_policies(Relation rel);
 
 #endif   /* POLICY_H */
index 4749efc5679d9cf31cd50ea2badb16c9c077c456..b0f2565b60ae497b400dcd11b09a51c2029d8a33 100644 (file)
@@ -2997,6 +2997,29 @@ DROP ROLE bob; -- succeeds
 ROLLBACK TO q;
 ROLLBACK; -- cleanup
 --
+-- Converting table to view
+--
+BEGIN;
+SET ROW_SECURITY = FORCE;
+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
+ERROR:  could not convert table "t" to a view because it has row 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
+ERROR:  could not convert table "t" to a view because it has row security policies
+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;
+--
 -- Clean up objects
 --
 RESET SESSION AUTHORIZATION;
index 529edd01c7f0b95072d61518547dd01714d061aa..03f82987c47533e161b0aa8126baaeb2f0d7ee08 100644 (file)
@@ -1260,6 +1260,31 @@ ROLLBACK TO q;
 
 ROLLBACK; -- cleanup
 
+--
+-- Converting table to view
+--
+BEGIN;
+SET ROW_SECURITY = FORCE;
+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;
+
 --
 -- Clean up objects
 --