From 3e9a63cc2a04799f3e97c9bec9b684ababca8db7 Mon Sep 17 00:00:00 2001 From: Ivan Vakhrushev Date: Fri, 20 Dec 2024 20:11:43 +0400 Subject: [PATCH] [POSSIBLE_OBJECT_NAME_OVERFLOW] Add support for partitioned tables. [PRIMARY_KEYS_WITH_SERIAL_TYPES] Add support for partitioned tables. [PRIMARY_KEYS_WITH_SERIAL_TYPES] Fix insufficient permissions error (#544) * Update queries * [POSSIBLE_OBJECT_NAME_OVERFLOW] Add support for partitioned tables * [PRIMARY_KEYS_WITH_SERIAL_TYPES] Add support for partitioned tables * Fix test --- README.md | 4 +-- pg-index-health-core/src/main/resources | 2 +- ...ibleObjectNameOverflowCheckOnHostTest.java | 31 ++++++++++++++--- ...aryKeysWithSerialTypesCheckOnHostTest.java | 16 +++++++++ .../fixtures/support/DatabasePopulator.java | 7 ++++ ...tionedTableWithVeryLongNamesStatement.java | 33 +++++++++++++++++++ .../mfvanek/pg/model/dbobject/AnyObject.java | 22 +++++++++++-- .../pg/model/dbobject/PgObjectType.java | 14 ++++++++ .../SkipDbObjectsByNamePredicate.java | 31 +++++++++++++++++ .../pg/model/dbobject/AnyObjectTest.java | 5 +++ .../SkipDbObjectsByNamePredicateTest.java | 21 ++++++++++-- ...eObjectNameOverflowCheckOnClusterTest.java | 31 ++++++++++++++--- ...KeysWithSerialTypesCheckOnClusterTest.java | 16 +++++++++ ...gresWithCustomUserDemoApplicationTest.java | 9 ++++- 14 files changed, 223 insertions(+), 19 deletions(-) create mode 100644 pg-index-health-core/src/testFixtures/java/io/github/mfvanek/pg/core/fixtures/support/statements/CreatePartitionedTableWithVeryLongNamesStatement.java diff --git a/README.md b/README.md index a0d6c5c5..ab5b41aa 100644 --- a/README.md +++ b/README.md @@ -67,10 +67,10 @@ All checks can be divided into 2 groups: | 17 | Tables with [not valid constraints](https://habr.com/ru/articles/800121/) | **runtime**/static | no | [sql](https://github.com/mfvanek/pg-index-health-sql/blob/master/sql/not_valid_constraints.sql) | | 18 | B-tree indexes [on array columns](https://habr.com/ru/articles/800121/) | static | no | [sql](https://github.com/mfvanek/pg-index-health-sql/blob/master/sql/btree_indexes_on_array_columns.sql) | | 19 | [Sequence overflow](https://habr.com/ru/articles/800121/) | **runtime** | not applicable | [sql](https://github.com/mfvanek/pg-index-health-sql/blob/master/sql/sequence_overflow.sql) | -| 20 | Primary keys with [serial types](https://wiki.postgresql.org/wiki/Don't_Do_This#Don.27t_use_serial) | static | no | [sql](https://github.com/mfvanek/pg-index-health-sql/blob/master/sql/primary_keys_with_serial_types.sql) | +| 20 | Primary keys with [serial types](https://wiki.postgresql.org/wiki/Don't_Do_This#Don.27t_use_serial) | static | yes | [sql](https://github.com/mfvanek/pg-index-health-sql/blob/master/sql/primary_keys_with_serial_types.sql) | | 21 | Duplicated ([completely identical](https://habr.com/ru/articles/803841/)) foreign keys | static | no | [sql](https://github.com/mfvanek/pg-index-health-sql/blob/master/sql/duplicated_foreign_keys.sql) | | 22 | Intersected ([partially identical](https://habr.com/ru/articles/803841/)) foreign keys | static | no | [sql](https://github.com/mfvanek/pg-index-health-sql/blob/master/sql/intersected_foreign_keys.sql) | -| 23 | Possible object name overflow (identifiers with maximum length) | static | no | [sql](https://github.com/mfvanek/pg-index-health-sql/blob/master/sql/possible_object_name_overflow.sql) | +| 23 | Possible object name overflow (identifiers with maximum length) | static | yes | [sql](https://github.com/mfvanek/pg-index-health-sql/blob/master/sql/possible_object_name_overflow.sql) | | 24 | Tables not linked to other tables | static | yes | [sql](https://github.com/mfvanek/pg-index-health-sql/blob/master/sql/tables_not_linked_to_others.sql) | | 25 | Foreign keys [with unmatched column type](https://habr.com/ru/articles/803841/) | static | no | [sql](https://github.com/mfvanek/pg-index-health-sql/blob/master/sql/foreign_keys_with_unmatched_column_type.sql) | diff --git a/pg-index-health-core/src/main/resources b/pg-index-health-core/src/main/resources index eebce6a5..19e50d78 160000 --- a/pg-index-health-core/src/main/resources +++ b/pg-index-health-core/src/main/resources @@ -1 +1 @@ -Subproject commit eebce6a5365f83266db58058c9a6966d2d8c2381 +Subproject commit 19e50d784cdb2b53ac4d334043d8318f568971d4 diff --git a/pg-index-health-core/src/test/java/io/github/mfvanek/pg/core/checks/host/PossibleObjectNameOverflowCheckOnHostTest.java b/pg-index-health-core/src/test/java/io/github/mfvanek/pg/core/checks/host/PossibleObjectNameOverflowCheckOnHostTest.java index 7b50475c..ea033fb4 100644 --- a/pg-index-health-core/src/test/java/io/github/mfvanek/pg/core/checks/host/PossibleObjectNameOverflowCheckOnHostTest.java +++ b/pg-index-health-core/src/test/java/io/github/mfvanek/pg/core/checks/host/PossibleObjectNameOverflowCheckOnHostTest.java @@ -13,6 +13,7 @@ import io.github.mfvanek.pg.core.checks.common.DatabaseCheckOnHost; import io.github.mfvanek.pg.core.checks.common.Diagnostic; import io.github.mfvanek.pg.core.fixtures.support.DatabaseAwareTestBase; +import io.github.mfvanek.pg.core.fixtures.support.DatabasePopulator; import io.github.mfvanek.pg.model.context.PgContext; import io.github.mfvanek.pg.model.dbobject.AnyObject; import io.github.mfvanek.pg.model.dbobject.PgObjectType; @@ -42,18 +43,38 @@ void shouldSatisfyContract() { @ValueSource(strings = {PgContext.DEFAULT_SCHEMA_NAME, "custom"}) void onDatabaseWithThem(final String schemaName) { executeTestOnDatabase(schemaName, dbp -> dbp.withReferences().withMaterializedView().withIdentityPrimaryKey(), ctx -> { - final String matViewName = ctx.enrichWithSchema("accounts_materialized_view_with_length_63_1234567890_1234567890"); - final String constraintName = ctx.enrichWithSchema("num_less_than_million_constraint_with_length_63_1234567890_1234"); + final String matViewName = "accounts_materialized_view_with_length_63_1234567890_1234567890"; + final String constraintName = "num_less_than_million_constraint_with_length_63_1234567890_1234"; assertThat(check) .executing(ctx) .hasSize(2) .containsExactlyInAnyOrder( - AnyObject.ofType(matViewName, PgObjectType.MATERIALIZED_VIEW), - AnyObject.ofType(constraintName, PgObjectType.CONSTRAINT)); + AnyObject.ofType(ctx, matViewName, PgObjectType.MATERIALIZED_VIEW), + AnyObject.ofType(ctx, constraintName, PgObjectType.CONSTRAINT)); assertThat(check) - .executing(ctx, SkipDbObjectsByNamePredicate.of(List.of(matViewName, constraintName))) + .executing(ctx, SkipDbObjectsByNamePredicate.of(ctx, List.of(matViewName, constraintName))) .isEmpty(); }); } + + @ParameterizedTest + @ValueSource(strings = {PgContext.DEFAULT_SCHEMA_NAME, "custom"}) + void shouldWorkWithPartitionedTables(final String schemaName) { + executeTestOnDatabase(schemaName, DatabasePopulator::withVeryLongNamesInPartitionedTable, ctx -> + assertThat(check) + .executing(ctx) + .hasSize(9) + .containsExactly( + AnyObject.ofType(ctx, "entity_default_long_1234567890_1234567890_1234567890_12345_pkey", PgObjectType.CONSTRAINT), + AnyObject.ofType(ctx, "entity_long_1234567890_1234567890_1234567890_1234567890_12_pkey", PgObjectType.CONSTRAINT), + AnyObject.ofType(ctx, "entity_default_long_1234567890_1234567890_1234567890_12345_pkey", PgObjectType.INDEX), + AnyObject.ofType(ctx, "entity_default_long_1234567890_123456789_ref_type_ref_value_idx", PgObjectType.INDEX), + AnyObject.ofType(ctx, "entity_long_1234567890_1234567890_1234567890_1234567890_12_pkey", PgObjectType.PARTITIONED_INDEX), + AnyObject.ofType(ctx, "idx_entity_long_1234567890_1234567890_1234567890_1234567890_123", PgObjectType.PARTITIONED_INDEX), + AnyObject.ofType(ctx, "entity_long_1234567890_1234567890_1234567890_1234567890_1234567", PgObjectType.PARTITIONED_TABLE), + AnyObject.ofType(ctx, "entity_long_1234567890_1234567890_1234567890_1234_entity_id_seq", PgObjectType.SEQUENCE), + AnyObject.ofType(ctx, "entity_default_long_1234567890_1234567890_1234567890_1234567890", PgObjectType.TABLE) + )); + } } diff --git a/pg-index-health-core/src/test/java/io/github/mfvanek/pg/core/checks/host/PrimaryKeysWithSerialTypesCheckOnHostTest.java b/pg-index-health-core/src/test/java/io/github/mfvanek/pg/core/checks/host/PrimaryKeysWithSerialTypesCheckOnHostTest.java index 53255006..9b70dd81 100644 --- a/pg-index-health-core/src/test/java/io/github/mfvanek/pg/core/checks/host/PrimaryKeysWithSerialTypesCheckOnHostTest.java +++ b/pg-index-health-core/src/test/java/io/github/mfvanek/pg/core/checks/host/PrimaryKeysWithSerialTypesCheckOnHostTest.java @@ -13,6 +13,7 @@ import io.github.mfvanek.pg.core.checks.common.DatabaseCheckOnHost; import io.github.mfvanek.pg.core.checks.common.Diagnostic; import io.github.mfvanek.pg.core.fixtures.support.DatabaseAwareTestBase; +import io.github.mfvanek.pg.core.fixtures.support.DatabasePopulator; import io.github.mfvanek.pg.model.column.Column; import io.github.mfvanek.pg.model.column.ColumnWithSerialType; import io.github.mfvanek.pg.model.column.SerialType; @@ -69,4 +70,19 @@ void onDatabaseWithoutThem(final String schemaName) { .isEmpty() ); } + + @ParameterizedTest + @ValueSource(strings = {PgContext.DEFAULT_SCHEMA_NAME, "custom"}) + void shouldWorkWithPartitionedTables(final String schemaName) { + executeTestOnDatabase(schemaName, DatabasePopulator::withVeryLongNamesInPartitionedTable, ctx -> { + final String tableName = "entity_long_1234567890_1234567890_1234567890_1234567890_1234567"; + final String sequenceName = ctx.enrichWithSchema("entity_long_1234567890_1234567890_1234567890_1234_entity_id_seq"); + assertThat(check) + .executing(ctx) + .hasSize(1) + .containsExactly( + ColumnWithSerialType.of(Column.ofNotNull(ctx, tableName, "entity_id"), SerialType.BIG_SERIAL, sequenceName) + ); + }); + } } diff --git a/pg-index-health-core/src/testFixtures/java/io/github/mfvanek/pg/core/fixtures/support/DatabasePopulator.java b/pg-index-health-core/src/testFixtures/java/io/github/mfvanek/pg/core/fixtures/support/DatabasePopulator.java index 157340b8..f932f3d0 100644 --- a/pg-index-health-core/src/testFixtures/java/io/github/mfvanek/pg/core/fixtures/support/DatabasePopulator.java +++ b/pg-index-health-core/src/testFixtures/java/io/github/mfvanek/pg/core/fixtures/support/DatabasePopulator.java @@ -38,6 +38,7 @@ import io.github.mfvanek.pg.core.fixtures.support.statements.CreateMaterializedViewStatement; import io.github.mfvanek.pg.core.fixtures.support.statements.CreateNotSuitableIndexForForeignKeyStatement; import io.github.mfvanek.pg.core.fixtures.support.statements.CreatePartitionedTableWithNullableFieldsStatement; +import io.github.mfvanek.pg.core.fixtures.support.statements.CreatePartitionedTableWithVeryLongNamesStatement; import io.github.mfvanek.pg.core.fixtures.support.statements.CreatePartitionedTableWithoutCommentsStatement; import io.github.mfvanek.pg.core.fixtures.support.statements.CreatePartitionedTableWithoutPrimaryKeyStatement; import io.github.mfvanek.pg.core.fixtures.support.statements.CreateProceduresStatement; @@ -337,6 +338,12 @@ public DatabasePopulator withNullableIndexesInPartitionedTable() { return this; } + @Nonnull + public DatabasePopulator withVeryLongNamesInPartitionedTable() { + statementsToExecuteInSameTransaction.putIfAbsent(114, new CreatePartitionedTableWithVeryLongNamesStatement()); + return this; + } + public void populate() { try (SchemaNameHolder ignored = SchemaNameHolder.with(schemaName)) { ExecuteUtils.executeInTransaction(dataSource, statementsToExecuteInSameTransaction.values()); diff --git a/pg-index-health-core/src/testFixtures/java/io/github/mfvanek/pg/core/fixtures/support/statements/CreatePartitionedTableWithVeryLongNamesStatement.java b/pg-index-health-core/src/testFixtures/java/io/github/mfvanek/pg/core/fixtures/support/statements/CreatePartitionedTableWithVeryLongNamesStatement.java new file mode 100644 index 00000000..dad8ab56 --- /dev/null +++ b/pg-index-health-core/src/testFixtures/java/io/github/mfvanek/pg/core/fixtures/support/statements/CreatePartitionedTableWithVeryLongNamesStatement.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2019-2024. Ivan Vakhrushev and others. + * https://github.com/mfvanek/pg-index-health + * + * This file is a part of "pg-index-health" - a Java library for + * analyzing and maintaining indexes health in PostgreSQL databases. + * + * Licensed under the Apache License 2.0 + */ + +package io.github.mfvanek.pg.core.fixtures.support.statements; + +import java.util.List; +import javax.annotation.Nonnull; + +public class CreatePartitionedTableWithVeryLongNamesStatement extends AbstractDbStatement { + + @Nonnull + @Override + protected List getSqlToExecute() { + return List.of( + "create table if not exists {schemaName}.entity_long_1234567890_1234567890_1234567890_1234567890_1234567(" + + "ref_type varchar(32)," + + "ref_value varchar(64)," + + "entity_id bigserial primary key" + + ") partition by range (entity_id);", + "create index if not exists idx_entity_long_1234567890_1234567890_1234567890_1234567890_123 " + + "on {schemaName}.entity_long_1234567890_1234567890_1234567890_1234567890_1234567 (ref_type, ref_value);", + "create table if not exists {schemaName}.entity_default_long_1234567890_1234567890_1234567890_1234567890 " + + "partition of {schemaName}.entity_long_1234567890_1234567890_1234567890_1234567890_1234567 default;" + ); + } +} diff --git a/pg-index-health-model/src/main/java/io/github/mfvanek/pg/model/dbobject/AnyObject.java b/pg-index-health-model/src/main/java/io/github/mfvanek/pg/model/dbobject/AnyObject.java index 8e8d0859..4811561f 100644 --- a/pg-index-health-model/src/main/java/io/github/mfvanek/pg/model/dbobject/AnyObject.java +++ b/pg-index-health-model/src/main/java/io/github/mfvanek/pg/model/dbobject/AnyObject.java @@ -10,6 +10,7 @@ package io.github.mfvanek.pg.model.dbobject; +import io.github.mfvanek.pg.model.context.PgContext; import io.github.mfvanek.pg.model.validation.Validators; import java.util.Objects; @@ -108,10 +109,26 @@ public int compareTo(@Nonnull final AnyObject other) { * @param objectType type of object in a database; should be non-null. * @return {@code AnyObject} instance */ - public static AnyObject ofType(@Nonnull final String objectName, @Nonnull final PgObjectType objectType) { + public static AnyObject ofType(@Nonnull final String objectName, + @Nonnull final PgObjectType objectType) { return new AnyObject(objectName, objectType); } + /** + * Constructs an {@code AnyObject} instance with given context. + * + * @param pgContext the schema context to enrich object name; must be non-null. + * @param objectName name of object in a database; should be non-blank. + * @param objectType type of object in a database; should be non-null. + * @return {@code AnyObject} instance + * @since 0.14.4 + */ + public static AnyObject ofType(@Nonnull final PgContext pgContext, + @Nonnull final String objectName, + @Nonnull final PgObjectType objectType) { + return ofType(PgContext.enrichWith(objectName, pgContext), objectType); + } + /** * Constructs an {@code AnyObject} instance. * @@ -119,7 +136,8 @@ public static AnyObject ofType(@Nonnull final String objectName, @Nonnull final * @param objectType literal type of object in a database; should be non-null. * @return {@code AnyObject} instance */ - public static AnyObject ofRaw(@Nonnull final String objectName, @Nonnull final String objectType) { + public static AnyObject ofRaw(@Nonnull final String objectName, + @Nonnull final String objectType) { return new AnyObject(objectName, PgObjectType.valueFrom(objectType)); } } diff --git a/pg-index-health-model/src/main/java/io/github/mfvanek/pg/model/dbobject/PgObjectType.java b/pg-index-health-model/src/main/java/io/github/mfvanek/pg/model/dbobject/PgObjectType.java index d0cf1fa4..085111d5 100644 --- a/pg-index-health-model/src/main/java/io/github/mfvanek/pg/model/dbobject/PgObjectType.java +++ b/pg-index-health-model/src/main/java/io/github/mfvanek/pg/model/dbobject/PgObjectType.java @@ -29,12 +29,26 @@ public enum PgObjectType { * @see CREATE TABLE */ TABLE("table"), + /** + * A partitioned table in a database. + * + * @see Table Partitioning + * @since 0.14.4 + */ + PARTITIONED_TABLE("partitioned table"), /** * An index in a database. * * @see Index Types */ INDEX("index"), + /** + * A partitioned index in a database. + * + * @see Table Partitioning + * @since 0.14.4 + */ + PARTITIONED_INDEX("partitioned index"), /** * A sequence in a database. * diff --git a/pg-index-health-model/src/main/java/io/github/mfvanek/pg/model/predicates/SkipDbObjectsByNamePredicate.java b/pg-index-health-model/src/main/java/io/github/mfvanek/pg/model/predicates/SkipDbObjectsByNamePredicate.java index 53cc8f1e..13195a6b 100644 --- a/pg-index-health-model/src/main/java/io/github/mfvanek/pg/model/predicates/SkipDbObjectsByNamePredicate.java +++ b/pg-index-health-model/src/main/java/io/github/mfvanek/pg/model/predicates/SkipDbObjectsByNamePredicate.java @@ -10,6 +10,7 @@ package io.github.mfvanek.pg.model.predicates; +import io.github.mfvanek.pg.model.context.PgContext; import io.github.mfvanek.pg.model.dbobject.DbObject; import java.util.Collection; @@ -22,6 +23,8 @@ import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.ThreadSafe; +import static io.github.mfvanek.pg.model.predicates.AbstractSkipTablesPredicate.prepareFullyQualifiedNamesToSkip; + /** * A predicate for filtering database objects by their fully qualified names. *

@@ -86,6 +89,20 @@ public static Predicate ofName(@Nonnull final String fullyQualifiedObj return new SkipDbObjectsByNamePredicate(fullyQualifiedObjectNameToSkip); } + /** + * Creates a predicate to skip a specific object name in the given context. + * + * @param pgContext the PostgreSQL context to use; must be non-null + * @param objectNameToSkip the object name to skip, must be non-null and non-blank + * @return a predicate that skips the specified object + * @since 0.14.4 + */ + @Nonnull + public static Predicate ofName(@Nonnull final PgContext pgContext, + @Nonnull final String objectNameToSkip) { + return ofName(PgContext.enrichWith(objectNameToSkip, pgContext)); + } + /** * Creates a predicate to skip multiple fully qualified object names. * @@ -97,4 +114,18 @@ public static Predicate ofName(@Nonnull final String fullyQualifiedObj public static Predicate of(@Nonnull final Collection fullyQualifiedObjectNamesToSkip) { return new SkipDbObjectsByNamePredicate(fullyQualifiedObjectNamesToSkip); } + + /** + * Creates a predicate to skip multiple object names in the given context. + * + * @param pgContext the PostgreSQL context to use; must be non-null + * @param objectNamesToSkip a collection of object names to skip, must be non-null + * @return a predicate that skips the specified objects + * @since 0.14.4 + */ + @Nonnull + public static Predicate of(@Nonnull final PgContext pgContext, + @Nonnull final Collection objectNamesToSkip) { + return of(prepareFullyQualifiedNamesToSkip(pgContext, objectNamesToSkip)); + } } diff --git a/pg-index-health-model/src/test/java/io/github/mfvanek/pg/model/dbobject/AnyObjectTest.java b/pg-index-health-model/src/test/java/io/github/mfvanek/pg/model/dbobject/AnyObjectTest.java index 3f25c9a2..56c9943a 100644 --- a/pg-index-health-model/src/test/java/io/github/mfvanek/pg/model/dbobject/AnyObjectTest.java +++ b/pg-index-health-model/src/test/java/io/github/mfvanek/pg/model/dbobject/AnyObjectTest.java @@ -10,6 +10,7 @@ package io.github.mfvanek.pg.model.dbobject; +import io.github.mfvanek.pg.model.context.PgContext; import nl.jqno.equalsverifier.EqualsVerifier; import org.junit.jupiter.api.Test; @@ -58,6 +59,10 @@ void withInvalidValues() { void testToString() { assertThat(AnyObject.ofRaw("mv", "Materialized View")) .hasToString("AnyObject{objectName='mv', objectType=MATERIALIZED_VIEW}"); + assertThat(AnyObject.ofType("pi", PgObjectType.PARTITIONED_INDEX)) + .hasToString("AnyObject{objectName='pi', objectType=PARTITIONED_INDEX}"); + assertThat(AnyObject.ofType(PgContext.of("tst"), "pi", PgObjectType.PARTITIONED_INDEX)) + .hasToString("AnyObject{objectName='tst.pi', objectType=PARTITIONED_INDEX}"); } @SuppressWarnings("ConstantConditions") diff --git a/pg-index-health-model/src/test/java/io/github/mfvanek/pg/model/predicates/SkipDbObjectsByNamePredicateTest.java b/pg-index-health-model/src/test/java/io/github/mfvanek/pg/model/predicates/SkipDbObjectsByNamePredicateTest.java index 14cdbbf6..3f34dc95 100644 --- a/pg-index-health-model/src/test/java/io/github/mfvanek/pg/model/predicates/SkipDbObjectsByNamePredicateTest.java +++ b/pg-index-health-model/src/test/java/io/github/mfvanek/pg/model/predicates/SkipDbObjectsByNamePredicateTest.java @@ -10,6 +10,7 @@ package io.github.mfvanek.pg.model.predicates; +import io.github.mfvanek.pg.model.context.PgContext; import io.github.mfvanek.pg.model.table.Table; import org.junit.jupiter.api.Test; import org.mockito.Mockito; @@ -21,9 +22,10 @@ class SkipDbObjectsByNamePredicateTest { - private static final Table FIRST = Table.of("custom.TABLE1", 1L); - private static final Table SECOND = Table.of("custom.TABLE2", 2L); - private static final Table THIRD = Table.of("custom.TABLE3", 3L); + private static final PgContext CTX = PgContext.of("CUSTOM"); + private static final Table FIRST = Table.of(CTX, "TABLE1", 1L); + private static final Table SECOND = Table.of(CTX, "TABLE2", 2L); + private static final Table THIRD = Table.of(CTX, "TABLE3", 3L); @SuppressWarnings("DataFlowIssue") @Test @@ -43,6 +45,9 @@ void shouldThrowExceptionWhenInvalidDataPassed() { assertThatThrownBy(() -> SkipDbObjectsByNamePredicate.of(null)) .isInstanceOf(NullPointerException.class) .hasMessage("fullyQualifiedObjectNamesToSkip cannot be null"); + assertThatThrownBy(() -> SkipDbObjectsByNamePredicate.of(null, null)) + .isInstanceOf(NullPointerException.class) + .hasMessage("pgContext cannot be null"); } @Test @@ -51,6 +56,11 @@ void caseShouldNotMatter() { .accepts(FIRST) .accepts(SECOND) .rejects(THIRD); + + assertThat(SkipDbObjectsByNamePredicate.ofName(CTX, "table3")) + .accepts(FIRST) + .accepts(SECOND) + .rejects(THIRD); } @Test @@ -59,6 +69,11 @@ void forList() { .rejects(FIRST) .accepts(SECOND) .rejects(THIRD); + + assertThat(SkipDbObjectsByNamePredicate.of(CTX, List.of("table1", "table3"))) + .rejects(FIRST) + .accepts(SECOND) + .rejects(THIRD); } @Test diff --git a/pg-index-health/src/test/java/io/github/mfvanek/pg/health/checks/cluster/PossibleObjectNameOverflowCheckOnClusterTest.java b/pg-index-health/src/test/java/io/github/mfvanek/pg/health/checks/cluster/PossibleObjectNameOverflowCheckOnClusterTest.java index 591c6915..16cbd717 100644 --- a/pg-index-health/src/test/java/io/github/mfvanek/pg/health/checks/cluster/PossibleObjectNameOverflowCheckOnClusterTest.java +++ b/pg-index-health/src/test/java/io/github/mfvanek/pg/health/checks/cluster/PossibleObjectNameOverflowCheckOnClusterTest.java @@ -12,6 +12,7 @@ import io.github.mfvanek.pg.core.checks.common.Diagnostic; import io.github.mfvanek.pg.core.fixtures.support.DatabaseAwareTestBase; +import io.github.mfvanek.pg.core.fixtures.support.DatabasePopulator; import io.github.mfvanek.pg.health.checks.common.DatabaseCheckOnCluster; import io.github.mfvanek.pg.model.context.PgContext; import io.github.mfvanek.pg.model.dbobject.AnyObject; @@ -41,18 +42,38 @@ void shouldSatisfyContract() { @ValueSource(strings = {PgContext.DEFAULT_SCHEMA_NAME, "custom"}) void onDatabaseWithThem(final String schemaName) { executeTestOnDatabase(schemaName, dbp -> dbp.withReferences().withMaterializedView().withIdentityPrimaryKey(), ctx -> { - final String matViewName = ctx.enrichWithSchema("accounts_materialized_view_with_length_63_1234567890_1234567890"); - final String constraintName = ctx.enrichWithSchema("num_less_than_million_constraint_with_length_63_1234567890_1234"); + final String matViewName = "accounts_materialized_view_with_length_63_1234567890_1234567890"; + final String constraintName = "num_less_than_million_constraint_with_length_63_1234567890_1234"; assertThat(check) .executing(ctx) .hasSize(2) .containsExactlyInAnyOrder( - AnyObject.ofType(matViewName, PgObjectType.MATERIALIZED_VIEW), - AnyObject.ofType(constraintName, PgObjectType.CONSTRAINT)); + AnyObject.ofType(ctx, matViewName, PgObjectType.MATERIALIZED_VIEW), + AnyObject.ofType(ctx, constraintName, PgObjectType.CONSTRAINT)); assertThat(check) - .executing(ctx, SkipDbObjectsByNamePredicate.of(List.of(matViewName, constraintName))) + .executing(ctx, SkipDbObjectsByNamePredicate.of(ctx, List.of(matViewName, constraintName))) .isEmpty(); }); } + + @ParameterizedTest + @ValueSource(strings = {PgContext.DEFAULT_SCHEMA_NAME, "custom"}) + void shouldWorkWithPartitionedTables(final String schemaName) { + executeTestOnDatabase(schemaName, DatabasePopulator::withVeryLongNamesInPartitionedTable, ctx -> + assertThat(check) + .executing(ctx) + .hasSize(9) + .containsExactly( + AnyObject.ofType(ctx, "entity_default_long_1234567890_1234567890_1234567890_12345_pkey", PgObjectType.CONSTRAINT), + AnyObject.ofType(ctx, "entity_long_1234567890_1234567890_1234567890_1234567890_12_pkey", PgObjectType.CONSTRAINT), + AnyObject.ofType(ctx, "entity_default_long_1234567890_1234567890_1234567890_12345_pkey", PgObjectType.INDEX), + AnyObject.ofType(ctx, "entity_default_long_1234567890_123456789_ref_type_ref_value_idx", PgObjectType.INDEX), + AnyObject.ofType(ctx, "entity_long_1234567890_1234567890_1234567890_1234567890_12_pkey", PgObjectType.PARTITIONED_INDEX), + AnyObject.ofType(ctx, "idx_entity_long_1234567890_1234567890_1234567890_1234567890_123", PgObjectType.PARTITIONED_INDEX), + AnyObject.ofType(ctx, "entity_long_1234567890_1234567890_1234567890_1234567890_1234567", PgObjectType.PARTITIONED_TABLE), + AnyObject.ofType(ctx, "entity_long_1234567890_1234567890_1234567890_1234_entity_id_seq", PgObjectType.SEQUENCE), + AnyObject.ofType(ctx, "entity_default_long_1234567890_1234567890_1234567890_1234567890", PgObjectType.TABLE) + )); + } } diff --git a/pg-index-health/src/test/java/io/github/mfvanek/pg/health/checks/cluster/PrimaryKeysWithSerialTypesCheckOnClusterTest.java b/pg-index-health/src/test/java/io/github/mfvanek/pg/health/checks/cluster/PrimaryKeysWithSerialTypesCheckOnClusterTest.java index cadc7240..c2de2961 100644 --- a/pg-index-health/src/test/java/io/github/mfvanek/pg/health/checks/cluster/PrimaryKeysWithSerialTypesCheckOnClusterTest.java +++ b/pg-index-health/src/test/java/io/github/mfvanek/pg/health/checks/cluster/PrimaryKeysWithSerialTypesCheckOnClusterTest.java @@ -12,6 +12,7 @@ import io.github.mfvanek.pg.core.checks.common.Diagnostic; import io.github.mfvanek.pg.core.fixtures.support.DatabaseAwareTestBase; +import io.github.mfvanek.pg.core.fixtures.support.DatabasePopulator; import io.github.mfvanek.pg.health.checks.common.DatabaseCheckOnCluster; import io.github.mfvanek.pg.model.column.Column; import io.github.mfvanek.pg.model.column.ColumnWithSerialType; @@ -68,4 +69,19 @@ void onDatabaseWithoutThem(final String schemaName) { .isEmpty() ); } + + @ParameterizedTest + @ValueSource(strings = {PgContext.DEFAULT_SCHEMA_NAME, "custom"}) + void shouldWorkWithPartitionedTables(final String schemaName) { + executeTestOnDatabase(schemaName, DatabasePopulator::withVeryLongNamesInPartitionedTable, ctx -> { + final String tableName = "entity_long_1234567890_1234567890_1234567890_1234567890_1234567"; + final String sequenceName = ctx.enrichWithSchema("entity_long_1234567890_1234567890_1234567890_1234_entity_id_seq"); + assertThat(check) + .executing(ctx) + .hasSize(1) + .containsExactly( + ColumnWithSerialType.of(Column.ofNotNull(ctx, tableName, "entity_id"), SerialType.BIG_SERIAL, sequenceName) + ); + }); + } } diff --git a/spring-boot-integration/postgres-demo-app-with-custom-user/src/test/java/io/github/mfvanek/pg/spring/postgres/with/custom/user/PostgresWithCustomUserDemoApplicationTest.java b/spring-boot-integration/postgres-demo-app-with-custom-user/src/test/java/io/github/mfvanek/pg/spring/postgres/with/custom/user/PostgresWithCustomUserDemoApplicationTest.java index a937e860..6517f1ae 100644 --- a/spring-boot-integration/postgres-demo-app-with-custom-user/src/test/java/io/github/mfvanek/pg/spring/postgres/with/custom/user/PostgresWithCustomUserDemoApplicationTest.java +++ b/spring-boot-integration/postgres-demo-app-with-custom-user/src/test/java/io/github/mfvanek/pg/spring/postgres/with/custom/user/PostgresWithCustomUserDemoApplicationTest.java @@ -131,7 +131,6 @@ void checksShouldWorkForAdditionalSchema() { final ListAssert listAssert = assertThat(c.check(ctx)) .as(c.getDiagnostic().name()); - // PRIMARY_KEYS_WITH_SERIAL_TYPES are present in the schema but cannot be found due to insufficient permissions switch (c.getDiagnostic()) { case TABLES_WITHOUT_DESCRIPTION: case TABLES_NOT_LINKED_TO_OTHERS: @@ -144,6 +143,14 @@ void checksShouldWorkForAdditionalSchema() { listAssert.hasSize(2); break; + case PRIMARY_KEYS_WITH_SERIAL_TYPES: + listAssert.hasSize(1) + .asInstanceOf(list(ColumnWithSerialType.class)) + .containsExactly(ColumnWithSerialType.of( + Column.ofNotNull(ctx, "additional_table", "id"), SerialType.BIG_SERIAL, ctx.enrichWithSchema("additional_table_id_seq")) + ); + break; + default: listAssert.isEmpty(); break;