From e56d73c05e55a8567f231a5d95f396bfc007a860 Mon Sep 17 00:00:00 2001 From: Fouz Date: Tue, 29 Jul 2025 00:14:46 +0500 Subject: [PATCH 1/2] Listing partitioned tables during scaffolding --- .../Internal/NpgsqlDatabaseModelFactory.cs | 5 +++-- .../NpgsqlDatabaseModelFactoryTest.cs | 22 +++++++++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/EFCore.PG/Scaffolding/Internal/NpgsqlDatabaseModelFactory.cs b/src/EFCore.PG/Scaffolding/Internal/NpgsqlDatabaseModelFactory.cs index b305a8626..f199a2b0d 100644 --- a/src/EFCore.PG/Scaffolding/Internal/NpgsqlDatabaseModelFactory.cs +++ b/src/EFCore.PG/Scaffolding/Internal/NpgsqlDatabaseModelFactory.cs @@ -201,7 +201,7 @@ 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 tables which are members of PG extensions @@ -236,6 +236,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,7 +319,7 @@ 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 diff --git a/test/EFCore.PG.FunctionalTests/Scaffolding/NpgsqlDatabaseModelFactoryTest.cs b/test/EFCore.PG.FunctionalTests/Scaffolding/NpgsqlDatabaseModelFactoryTest.cs index 7cdfe3d88..191f9f781 100644 --- a/test/EFCore.PG.FunctionalTests/Scaffolding/NpgsqlDatabaseModelFactoryTest.cs +++ b/test/EFCore.PG.FunctionalTests/Scaffolding/NpgsqlDatabaseModelFactoryTest.cs @@ -2155,6 +2155,28 @@ 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;"); + + [ConditionalFact] [RequiresPostgis] public void System_tables_are_ignored() From 3010210a3a84c3777cc13eee68505326991de7d0 Mon Sep 17 00:00:00 2001 From: Fouz Date: Wed, 30 Jul 2025 01:37:14 +0500 Subject: [PATCH 2/2] Children partitions ignore along with columns, constraints and indexes --- .../Internal/NpgsqlDatabaseModelFactory.cs | 12 +++++- .../NpgsqlDatabaseModelFactoryTest.cs | 38 +++++++++++++++++++ 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/src/EFCore.PG/Scaffolding/Internal/NpgsqlDatabaseModelFactory.cs b/src/EFCore.PG/Scaffolding/Internal/NpgsqlDatabaseModelFactory.cs index f199a2b0d..7d09dcd0d 100644 --- a/src/EFCore.PG/Scaffolding/Internal/NpgsqlDatabaseModelFactory.cs +++ b/src/EFCore.PG/Scaffolding/Internal/NpgsqlDatabaseModelFactory.cs @@ -204,6 +204,8 @@ FROM pg_class AS cls 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 @@ -323,6 +325,8 @@ 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 @@ -620,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 @@ -829,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 191f9f781..e8d9255eb 100644 --- a/test/EFCore.PG.FunctionalTests/Scaffolding/NpgsqlDatabaseModelFactoryTest.cs +++ b/test/EFCore.PG.FunctionalTests/Scaffolding/NpgsqlDatabaseModelFactoryTest.cs @@ -2177,6 +2177,44 @@ partition_key integer "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()