diff --git a/src/backend/commands/policy.c b/src/backend/commands/policy.c index 9544f75032b4b1bf614686b0aaf8ad3064cbde73..0d4e557d5abfe4092180e32e56e2f364dec1a681 100644 --- a/src/backend/commands/policy.c +++ b/src/backend/commands/policy.c @@ -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; +} diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c index a88d73e15f2a2c7465e99bf58e2d050f65f5419a..39c83a605ca10d2bcafbfb4d58b22be36241833a 100644 --- a/src/backend/rewrite/rewriteDefine.c +++ b/src/backend/rewrite/rewriteDefine.c @@ -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; } } diff --git a/src/include/commands/policy.h b/src/include/commands/policy.h index ac322e0db9263519a8e8edac64b7885460a1c31f..be000432312ff2e2b6e23a2800b1d5ccdb62dff6 100644 --- a/src/include/commands/policy.h +++ b/src/include/commands/policy.h @@ -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 */ diff --git a/src/test/regress/expected/rowsecurity.out b/src/test/regress/expected/rowsecurity.out index 4749efc5679d9cf31cd50ea2badb16c9c077c456..b0f2565b60ae497b400dcd11b09a51c2029d8a33 100644 --- a/src/test/regress/expected/rowsecurity.out +++ b/src/test/regress/expected/rowsecurity.out @@ -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; diff --git a/src/test/regress/sql/rowsecurity.sql b/src/test/regress/sql/rowsecurity.sql index 529edd01c7f0b95072d61518547dd01714d061aa..03f82987c47533e161b0aa8126baaeb2f0d7ee08 100644 --- a/src/test/regress/sql/rowsecurity.sql +++ b/src/test/regress/sql/rowsecurity.sql @@ -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 --