Skip to content

Commit 5800690

Browse files
committed
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 501ed02 that added has_superclass(). Perhaps the lack of that infrastructure partially explains the missing check.) Discussion: https://postgr.es/m/16856-0363e05c6e1612fd@postgresql.org
1 parent d9b4163 commit 5800690

File tree

4 files changed

+52
-16
lines changed

4 files changed

+52
-16
lines changed

src/backend/catalog/pg_inherits.c

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
* pg_inherits.c
44
* routines to support manipulation of the pg_inherits relation
55
*
6-
* Note: currently, this module only contains inquiry functions; the actual
7-
* creation and deletion of pg_inherits entries is done in tablecmds.c.
6+
* Note: currently, this module mostly contains inquiry functions; actual
7+
* creation and deletion of pg_inherits entries is mostly done in tablecmds.c.
88
* Perhaps someday that code should be moved here, but it'd have to be
99
* disentangled from other stuff such as pg_depend updates.
1010
*
@@ -273,9 +273,11 @@ has_subclass(Oid relationId)
273273
}
274274

275275
/*
276-
* has_superclass - does this relation inherit from another? The caller
277-
* should hold a lock on the given relation so that it can't be concurrently
278-
* added to or removed from an inheritance hierarchy.
276+
* has_superclass - does this relation inherit from another?
277+
*
278+
* Unlike has_subclass, this can be relied on to give an accurate answer.
279+
* However, the caller must hold a lock on the given relation so that it
280+
* can't be concurrently added to or removed from an inheritance hierarchy.
279281
*/
280282
bool
281283
has_superclass(Oid relationId)

src/backend/rewrite/rewriteDefine.c

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "catalog/indexing.h"
2626
#include "catalog/namespace.h"
2727
#include "catalog/objectaccess.h"
28+
#include "catalog/pg_inherits.h"
2829
#include "catalog/pg_rewrite.h"
2930
#include "catalog/storage.h"
3031
#include "commands/policy.h"
@@ -408,13 +409,14 @@ DefineQueryRewrite(const char *rulename,
408409
* Are we converting a relation to a view?
409410
*
410411
* If so, check that the relation is empty because the storage for the
411-
* relation is going to be deleted. Also insist that the rel not have
412-
* any triggers, indexes, child tables, policies, or RLS enabled.
413-
* (Note: these tests are too strict, because they will reject
414-
* relations that once had such but don't anymore. But we don't
415-
* really care, because this whole business of converting relations to
416-
* views is just a kluge to allow dump/reload of views that
417-
* participate in circular dependencies.)
412+
* relation is going to be deleted. Also insist that the rel not be
413+
* involved in partitioning, nor have any triggers, indexes, child or
414+
* parent tables, RLS policies, or RLS enabled. (Note: some of these
415+
* tests are too strict, because they will reject relations that once
416+
* had such but don't anymore. But we don't really care, because this
417+
* whole business of converting relations to views is just an obsolete
418+
* kluge to allow dump/reload of views that participate in circular
419+
* dependencies.)
418420
*/
419421
if (event_relation->rd_rel->relkind != RELKIND_VIEW &&
420422
event_relation->rd_rel->relkind != RELKIND_MATVIEW)
@@ -428,6 +430,9 @@ DefineQueryRewrite(const char *rulename,
428430
errmsg("cannot convert partitioned table \"%s\" to a view",
429431
RelationGetRelationName(event_relation))));
430432

433+
/* only case left: */
434+
Assert(event_relation->rd_rel->relkind == RELKIND_RELATION);
435+
431436
if (event_relation->rd_rel->relispartition)
432437
ereport(ERROR,
433438
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
@@ -463,6 +468,12 @@ DefineQueryRewrite(const char *rulename,
463468
errmsg("could not convert table \"%s\" to a view because it has child tables",
464469
RelationGetRelationName(event_relation))));
465470

471+
if (has_superclass(RelationGetRelid(event_relation)))
472+
ereport(ERROR,
473+
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
474+
errmsg("could not convert table \"%s\" to a view because it has parent tables",
475+
RelationGetRelationName(event_relation))));
476+
466477
if (event_relation->rd_rel->relrowsecurity)
467478
ereport(ERROR,
468479
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),

src/test/regress/expected/rules.out

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2579,16 +2579,27 @@ select reltoastrelid, relkind, relfrozenxid
25792579
(1 row)
25802580

25812581
drop view rules_fooview;
2582-
-- trying to convert a partitioned table to view is not allowed
2582+
-- cannot convert an inheritance parent or child to a view, though
2583+
create table rules_fooview (x int, y text);
2584+
create table rules_fooview_child () inherits (rules_fooview);
2585+
create rule "_RETURN" as on select to rules_fooview do instead
2586+
select 1 as x, 'aaa'::text as y;
2587+
ERROR: could not convert table "rules_fooview" to a view because it has child tables
2588+
create rule "_RETURN" as on select to rules_fooview_child do instead
2589+
select 1 as x, 'aaa'::text as y;
2590+
ERROR: could not convert table "rules_fooview_child" to a view because it has parent tables
2591+
drop table rules_fooview cascade;
2592+
NOTICE: drop cascades to table rules_fooview_child
2593+
-- likewise, converting a partitioned table or partition to view is not allowed
25832594
create table rules_fooview (x int, y text) partition by list (x);
25842595
create rule "_RETURN" as on select to rules_fooview do instead
25852596
select 1 as x, 'aaa'::text as y;
25862597
ERROR: cannot convert partitioned table "rules_fooview" to a view
2587-
-- nor can one convert a partition to view
25882598
create table rules_fooview_part partition of rules_fooview for values in (1);
25892599
create rule "_RETURN" as on select to rules_fooview_part do instead
25902600
select 1 as x, 'aaa'::text as y;
25912601
ERROR: cannot convert partition "rules_fooview_part" to a view
2602+
drop table rules_fooview;
25922603
--
25932604
-- check for planner problems with complex inherited UPDATES
25942605
--

src/test/regress/sql/rules.sql

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -898,16 +898,28 @@ select reltoastrelid, relkind, relfrozenxid
898898

899899
drop view rules_fooview;
900900

901-
-- trying to convert a partitioned table to view is not allowed
901+
-- cannot convert an inheritance parent or child to a view, though
902+
create table rules_fooview (x int, y text);
903+
create table rules_fooview_child () inherits (rules_fooview);
904+
905+
create rule "_RETURN" as on select to rules_fooview do instead
906+
select 1 as x, 'aaa'::text as y;
907+
create rule "_RETURN" as on select to rules_fooview_child do instead
908+
select 1 as x, 'aaa'::text as y;
909+
910+
drop table rules_fooview cascade;
911+
912+
-- likewise, converting a partitioned table or partition to view is not allowed
902913
create table rules_fooview (x int, y text) partition by list (x);
903914
create rule "_RETURN" as on select to rules_fooview do instead
904915
select 1 as x, 'aaa'::text as y;
905916

906-
-- nor can one convert a partition to view
907917
create table rules_fooview_part partition of rules_fooview for values in (1);
908918
create rule "_RETURN" as on select to rules_fooview_part do instead
909919
select 1 as x, 'aaa'::text as y;
910920

921+
drop table rules_fooview;
922+
911923
--
912924
-- check for planner problems with complex inherited UPDATES
913925
--

0 commit comments

Comments
 (0)