diff --git a/src/backend/catalog/pg_inherits.c b/src/backend/catalog/pg_inherits.c index f3783961b7..5ab7902827 100644 --- a/src/backend/catalog/pg_inherits.c +++ b/src/backend/catalog/pg_inherits.c @@ -3,8 +3,8 @@ * pg_inherits.c * routines to support manipulation of the pg_inherits relation * - * Note: currently, this module only contains inquiry functions; the actual - * creation and deletion of pg_inherits entries is done in tablecmds.c. + * Note: currently, this module mostly contains inquiry functions; actual + * creation and deletion of pg_inherits entries is mostly done in tablecmds.c. * Perhaps someday that code should be moved here, but it'd have to be * disentangled from other stuff such as pg_depend updates. * @@ -277,9 +277,11 @@ has_subclass(Oid relationId) } /* - * has_superclass - does this relation inherit from another? The caller - * should hold a lock on the given relation so that it can't be concurrently - * added to or removed from an inheritance hierarchy. + * has_superclass - does this relation inherit from another? + * + * Unlike has_subclass, this can be relied on to give an accurate answer. + * However, the caller must hold a lock on the given relation so that it + * can't be concurrently added to or removed from an inheritance hierarchy. */ bool has_superclass(Oid relationId) diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c index ee4ccbbd38..27e4ef911c 100644 --- a/src/backend/rewrite/rewriteDefine.c +++ b/src/backend/rewrite/rewriteDefine.c @@ -25,6 +25,7 @@ #include "catalog/heap.h" #include "catalog/namespace.h" #include "catalog/objectaccess.h" +#include "catalog/pg_inherits.h" #include "catalog/pg_rewrite.h" #include "catalog/storage.h" #include "commands/policy.h" @@ -412,13 +413,14 @@ DefineQueryRewrite(const char *rulename, * Are we converting a relation to a view? * * 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, 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.) + * relation is going to be deleted. Also insist that the rel not be + * involved in partitioning, nor have any triggers, indexes, child or + * parent tables, RLS policies, or RLS enabled. (Note: some of 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 an obsolete + * 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) @@ -433,6 +435,9 @@ DefineQueryRewrite(const char *rulename, errmsg("cannot convert partitioned table \"%s\" to a view", RelationGetRelationName(event_relation)))); + /* only case left: */ + Assert(event_relation->rd_rel->relkind == RELKIND_RELATION); + if (event_relation->rd_rel->relispartition) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), @@ -470,6 +475,12 @@ DefineQueryRewrite(const char *rulename, errmsg("could not convert table \"%s\" to a view because it has child tables", RelationGetRelationName(event_relation)))); + if (has_superclass(RelationGetRelid(event_relation))) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("could not convert table \"%s\" to a view because it has parent tables", + RelationGetRelationName(event_relation)))); + if (event_relation->rd_rel->relrowsecurity) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index 6173473de9..b632d9f2ea 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -2801,16 +2801,27 @@ select reltoastrelid, relkind, relfrozenxid (1 row) drop view rules_fooview; --- trying to convert a partitioned table to view is not allowed +-- cannot convert an inheritance parent or child to a view, though +create table rules_fooview (x int, y text); +create table rules_fooview_child () inherits (rules_fooview); +create rule "_RETURN" as on select to rules_fooview do instead + select 1 as x, 'aaa'::text as y; +ERROR: could not convert table "rules_fooview" to a view because it has child tables +create rule "_RETURN" as on select to rules_fooview_child do instead + select 1 as x, 'aaa'::text as y; +ERROR: could not convert table "rules_fooview_child" to a view because it has parent tables +drop table rules_fooview cascade; +NOTICE: drop cascades to table rules_fooview_child +-- likewise, converting a partitioned table or partition to view is not allowed create table rules_fooview (x int, y text) partition by list (x); create rule "_RETURN" as on select to rules_fooview do instead select 1 as x, 'aaa'::text as y; ERROR: cannot convert partitioned table "rules_fooview" to a view --- nor can one convert a partition to view create table rules_fooview_part partition of rules_fooview for values in (1); create rule "_RETURN" as on select to rules_fooview_part do instead select 1 as x, 'aaa'::text as y; ERROR: cannot convert partition "rules_fooview_part" to a view +drop table rules_fooview; -- -- check for planner problems with complex inherited UPDATES -- diff --git a/src/test/regress/sql/rules.sql b/src/test/regress/sql/rules.sql index 744cf7ab54..6ec37c4381 100644 --- a/src/test/regress/sql/rules.sql +++ b/src/test/regress/sql/rules.sql @@ -901,16 +901,28 @@ select reltoastrelid, relkind, relfrozenxid drop view rules_fooview; --- trying to convert a partitioned table to view is not allowed +-- cannot convert an inheritance parent or child to a view, though +create table rules_fooview (x int, y text); +create table rules_fooview_child () inherits (rules_fooview); + +create rule "_RETURN" as on select to rules_fooview do instead + select 1 as x, 'aaa'::text as y; +create rule "_RETURN" as on select to rules_fooview_child do instead + select 1 as x, 'aaa'::text as y; + +drop table rules_fooview cascade; + +-- likewise, converting a partitioned table or partition to view is not allowed create table rules_fooview (x int, y text) partition by list (x); create rule "_RETURN" as on select to rules_fooview do instead select 1 as x, 'aaa'::text as y; --- nor can one convert a partition to view create table rules_fooview_part partition of rules_fooview for values in (1); create rule "_RETURN" as on select to rules_fooview_part do instead select 1 as x, 'aaa'::text as y; +drop table rules_fooview; + -- -- check for planner problems with complex inherited UPDATES --