Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Lane2021-02-06 20:17:02 +0000
committerTom Lane2021-02-06 20:17:02 +0000
commit7736ab05fe839ee09386701ca1c992834f9af56a (patch)
tree6cc40ad8493d5c8dca87e1dcee42306a5e81afd8 /src/backend
parente152ccc7fd5932cfa9371a7c4cb7ca9a7733c5ab (diff)
Disallow converting an inheritance child table to a view.
Generally, members of inheritance trees must be plain tables (or, in more recent versions, foreign tables). ALTER TABLE INHERIT rejects creating an inheritance relationship that has a view at either end. When DefineQueryRewrite attempts to convert a relation to a view, it already had checks prohibiting doing so for partitioning parents or children as well as traditional-inheritance parents ... but it neglected to check that a traditional-inheritance child wasn't being converted. Since the planner assumes that any inheritance child is a table, this led to making plans that tried to do a physical scan on a view, causing failures (or even crashes, in recent versions). One could imagine trying to support such a case by expanding the view normally, but since the rewriter runs before the planner does inheritance expansion, it would take some very fundamental refactoring to make that possible. There are probably a lot of other parts of the system that don't cope well with such a situation, too. For now, just forbid it. Per bug #16856 from Yang Lin. Back-patch to all supported branches. (In versions before v10, this includes back-patching the portion of commit 501ed02cf that added has_superclass(). Perhaps the lack of that infrastructure partially explains the missing check.) Discussion: https://postgr.es/m/16856-0363e05c6e1612fd@postgresql.org
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/catalog/pg_inherits.c26
-rw-r--r--src/backend/rewrite/rewriteDefine.c19
2 files changed, 39 insertions, 6 deletions
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),