diff --git a/src/EFCore.PG/Scaffolding/Internal/NpgsqlDatabaseModelFactory.cs b/src/EFCore.PG/Scaffolding/Internal/NpgsqlDatabaseModelFactory.cs index b305a8626..7d09dcd0d 100644 --- a/src/EFCore.PG/Scaffolding/Internal/NpgsqlDatabaseModelFactory.cs +++ b/src/EFCore.PG/Scaffolding/Internal/NpgsqlDatabaseModelFactory.cs @@ -201,9 +201,11 @@ FROM pg_class AS cls JOIN pg_namespace AS ns ON ns.oid = cls.relnamespace LEFT OUTER JOIN pg_description AS des ON des.objoid = cls.oid AND des.objsubid=0 WHERE - cls.relkind IN ('r', 'v', 'm', 'f') AND + cls.relkind IN ('r', 'v', 'm', 'f', 'p') AND ns.nspname NOT IN ({internalSchemas}) AND cls.relname <> '{HistoryRepository.DefaultTableName}' AND + -- Exclude child partitions + cls.relispartition <> true AND -- Exclude tables which are members of PG extensions NOT EXISTS ( SELECT 1 FROM pg_depend WHERE @@ -236,6 +238,7 @@ deptype IN ('e', 'x') { 'r' => new DatabaseTable(), 'f' => new DatabaseTable(), + 'p' => new DatabaseTable(), 'v' => new DatabaseView(), 'm' => new DatabaseView(), _ => throw new ArgumentOutOfRangeException($"Unknown relkind '{type}' when scaffolding {DisplayName(schema, name)}") @@ -318,10 +321,12 @@ LEFT JOIN pg_type AS basetyp ON (basetyp.oid = typ.typbasetype) LEFT JOIN pg_depend AS dep ON dep.refobjid = cls.oid AND dep.refobjsubid = attr.attnum AND dep.deptype = 'i' {(connection.PostgreSqlVersion >= new Version(10, 0) ? "LEFT JOIN pg_sequence AS seq ON seq.seqrelid = dep.objid" : "")} WHERE - cls.relkind IN ('r', 'v', 'm', 'f') AND + cls.relkind IN ('r', 'v', 'm', 'f', 'p') AND nspname NOT IN ({internalSchemas}) AND attnum > 0 AND cls.relname <> '{HistoryRepository.DefaultTableName}' AND + -- Exclude child partitions + cls.relispartition <> true AND -- Exclude tables which are members of PG extensions NOT EXISTS ( SELECT 1 FROM pg_depend WHERE @@ -619,10 +624,12 @@ FROM pg_class AS cls JOIN pg_class AS idxcls ON idxcls.oid = indexrelid JOIN pg_am AS am ON am.oid = idxcls.relam WHERE - cls.relkind = 'r' AND + cls.relkind IN ('r','p') AND nspname NOT IN ({internalSchemas}) AND NOT indisprimary AND cls.relname <> '{HistoryRepository.DefaultTableName}' AND + -- Exclude child partitions + cls.relispartition <> true AND -- Exclude tables which are members of PG extensions NOT EXISTS ( SELECT 1 FROM pg_depend WHERE @@ -828,10 +835,12 @@ FROM pg_class AS cls LEFT OUTER JOIN pg_class AS frncls ON frncls.oid = con.confrelid LEFT OUTER JOIN pg_namespace as frnns ON frnns.oid = frncls.relnamespace WHERE - cls.relkind = 'r' AND + cls.relkind IN ('r','p') AND ns.nspname NOT IN ({internalSchemas}) AND con.contype IN ('p', 'f', 'u') AND cls.relname <> '{HistoryRepository.DefaultTableName}' AND + -- Exclude child partitions + cls.relispartition <> true AND -- Exclude tables which are members of PG extensions NOT EXISTS ( SELECT 1 FROM pg_depend WHERE diff --git a/test/EFCore.PG.FunctionalTests/Scaffolding/NpgsqlDatabaseModelFactoryTest.cs b/test/EFCore.PG.FunctionalTests/Scaffolding/NpgsqlDatabaseModelFactoryTest.cs index 7cdfe3d88..e8d9255eb 100644 --- a/test/EFCore.PG.FunctionalTests/Scaffolding/NpgsqlDatabaseModelFactoryTest.cs +++ b/test/EFCore.PG.FunctionalTests/Scaffolding/NpgsqlDatabaseModelFactoryTest.cs @@ -2155,6 +2155,66 @@ line line }, "DROP TABLE column_types"); + [Fact] + public void Partition_tables() + => Test( + """ +DROP TABLE IF EXISTS test_table; +DROP TABLE IF EXISTS foo; +CREATE TABLE foo (some_num int UNIQUE); +CREATE TABLE test_table ( + partition_id smallint, + partition_key integer + ) PARTITION BY LIST (partition_key); +""", + [], + [], + dbModel => + { + Assert.Equal(2, dbModel.Tables.Count); + Assert.Equal(3, dbModel.Tables.SelectMany(x => x.Columns).Count()); + }, + "DROP TABLE test_table;DROP TABLE foo;"); + + + [Fact] + public void Child_partition_tables_ignored() +=> Test( +""" + DROP TABLE IF EXISTS test_table; + DROP TABLE IF EXISTS foo; + CREATE TABLE foo (some_num int UNIQUE); + + CREATE TABLE test_table ( + partition_id smallint NOT NULL, + p_key smallint NOT NULL, + test_foreign_key int references foo(some_num), + partition_key integer NOT NULL, + PRIMARY KEY (p_key, partition_key), + CONSTRAINT test_constraint UNIQUE (partition_id, partition_key) + ) PARTITION BY LIST (partition_key); + + CREATE INDEX IF NOT EXISTS test_index + ON public.test_table USING btree + (partition_key ASC NULLS LAST, p_key ASC NULLS LAST, partition_id ASC NULLS LAST) + WITH (deduplicate_items=True); + + CREATE TABLE test_table_1 PARTITION OF test_table FOR VALUES IN (1); +""", + [], + [], + dbModel => + { + Assert.Equal(2, dbModel.Tables.Count); + Assert.Equal(5, dbModel.Tables.SelectMany(x => x.Columns).Count()); + Assert.Single(dbModel.Tables.Where(x => x.PrimaryKey != null).Select(x => x.PrimaryKey)); + Assert.Equal(2, dbModel.Tables.SelectMany(x => x.UniqueConstraints).Count()); + Assert.Single(dbModel.Tables.SelectMany(x => x.ForeignKeys)); + Assert.Single(dbModel.Tables.SelectMany(x => x.Indexes)); + }, + "DROP TABLE test_table_1;DROP TABLE test_table;DROP TABLE foo;"); + + [ConditionalFact] [RequiresPostgis] public void System_tables_are_ignored()