diff --git a/src/backend/catalog/pg_inherits.c b/src/backend/catalog/pg_inherits.c index 00f2ae0bbbe..b512df2eae1 100644 --- a/src/backend/catalog/pg_inherits.c +++ b/src/backend/catalog/pg_inherits.c @@ -254,6 +254,32 @@ has_subclass(Oid relationId) return result; } +/* + * 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) +{ + Relation catalog; + SysScanDesc scan; + ScanKeyData skey; + bool result; + + catalog = heap_open(InheritsRelationId, AccessShareLock); + ScanKeyInit(&skey, Anum_pg_inherits_inhrelid, BTEqualStrategyNumber, + F_OIDEQ, ObjectIdGetDatum(relationId)); + scan = systable_beginscan(catalog, InheritsRelidSeqnoIndexId, true, + NULL, 1, &skey); + result = HeapTupleIsValid(systable_getnext(scan)); + systable_endscan(scan); + heap_close(catalog, AccessShareLock); + + return result; +} /* * Given two type OIDs, determine whether the first is a complex type diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c index f82d891c347..ca1fab287f8 100644 --- a/src/backend/rewrite/rewriteDefine.c +++ b/src/backend/rewrite/rewriteDefine.c @@ -25,6 +25,7 @@ #include "catalog/indexing.h" #include "catalog/namespace.h" #include "catalog/objectaccess.h" +#include "catalog/pg_inherits_fn.h" #include "catalog/pg_rewrite.h" #include "catalog/storage.h" #include "commands/policy.h" @@ -411,12 +412,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, 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.) + * 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 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) @@ -453,6 +454,12 @@ DefineQueryRewrite(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/include/catalog/pg_inherits_fn.h b/src/include/catalog/pg_inherits_fn.h index a717108481c..868f395c919 100644 --- a/src/include/catalog/pg_inherits_fn.h +++ b/src/include/catalog/pg_inherits_fn.h @@ -21,6 +21,7 @@ extern List *find_inheritance_children(Oid parentrelId, LOCKMODE lockmode); extern List *find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **parents); extern bool has_subclass(Oid relationId); +extern bool has_superclass(Oid relationId); extern bool typeInheritsFrom(Oid subclassTypeId, Oid superclassTypeId); #endif /* PG_INHERITS_FN_H */ diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index 7e8e473d048..56b3e8ddc93 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -2498,6 +2498,17 @@ select reltoastrelid, relkind, relfrozenxid (1 row) drop view fooview; +-- cannot convert an inheritance parent or child to a view, though +create table fooview (x int, y text); +create table fooview_child () inherits (fooview); +create rule "_RETURN" as on select to fooview do instead + select 1 as x, 'aaa'::text as y; +ERROR: could not convert table "fooview" to a view because it has child tables +create rule "_RETURN" as on select to fooview_child do instead + select 1 as x, 'aaa'::text as y; +ERROR: could not convert table "fooview_child" to a view because it has parent tables +drop table fooview cascade; +NOTICE: drop cascades to table fooview_child -- -- 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 efbbb636a2b..945503d9153 100644 --- a/src/test/regress/sql/rules.sql +++ b/src/test/regress/sql/rules.sql @@ -898,6 +898,17 @@ select reltoastrelid, relkind, relfrozenxid drop view fooview; +-- cannot convert an inheritance parent or child to a view, though +create table fooview (x int, y text); +create table fooview_child () inherits (fooview); + +create rule "_RETURN" as on select to fooview do instead + select 1 as x, 'aaa'::text as y; +create rule "_RETURN" as on select to fooview_child do instead + select 1 as x, 'aaa'::text as y; + +drop table fooview cascade; + -- -- check for planner problems with complex inherited UPDATES --