diff --git a/src/main/java/at/procon/dip/domain/time/entity/TimeEntrySearchProjection.java b/src/main/java/at/procon/dip/domain/time/entity/TimeEntrySearchProjection.java index ece081b..cd723e7 100644 --- a/src/main/java/at/procon/dip/domain/time/entity/TimeEntrySearchProjection.java +++ b/src/main/java/at/procon/dip/domain/time/entity/TimeEntrySearchProjection.java @@ -80,10 +80,10 @@ public class TimeEntrySearchProjection { @Column(name = "time_recording_mcl_id", length = 255) private String timeRecordingMclId; - @Column(name = "time_recording_desc", length = 255) + @Column(name = "time_recording_desc", columnDefinition = "TEXT") private String timeRecordingDesc; - @Column(name = "time_recording_remark", length = 255) + @Column(name = "time_recording_remark", columnDefinition = "TEXT") private String timeRecordingRemark; @Column(name = "time_recording_url", length = 1000) diff --git a/src/main/resources/db/migration/V33__time_t1_foundation.sql b/src/main/resources/db/migration/V33__time_t1_foundation.sql index 7a1d339..b4d6817 100644 --- a/src/main/resources/db/migration/V33__time_t1_foundation.sql +++ b/src/main/resources/db/migration/V33__time_t1_foundation.sql @@ -44,34 +44,6 @@ BEGIN END $$; -DO $$ -BEGIN - IF NOT EXISTS ( - SELECT 1 - FROM pg_enum e - JOIN pg_type t ON t.oid = e.enumtypid - JOIN pg_namespace n ON n.oid = t.typnamespace - WHERE n.nspname = 'doc' AND t.typname = 'doc_document_type' AND e.enumlabel = 'TIME_ENTRY' - ) THEN - ALTER TYPE doc.doc_document_type ADD VALUE 'TIME_ENTRY'; - END IF; -END -$$; - -DO $$ -BEGIN - IF NOT EXISTS ( - SELECT 1 - FROM pg_enum e - JOIN pg_type t ON t.oid = e.enumtypid - JOIN pg_namespace n ON n.oid = t.typnamespace - WHERE n.nspname = 'doc' AND t.typname = 'doc_document_family' AND e.enumlabel = 'TIME' - ) THEN - ALTER TYPE doc.doc_document_family ADD VALUE 'TIME'; - END IF; -END -$$; - CREATE TABLE IF NOT EXISTS "time".time_entry ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), document_id UUID NOT NULL UNIQUE REFERENCES doc.doc_document(id) ON DELETE CASCADE, diff --git a/src/main/resources/db/migration/V42__time_projection_text_column_alignment.sql b/src/main/resources/db/migration/V42__time_projection_text_column_alignment.sql new file mode 100644 index 0000000..26c472d --- /dev/null +++ b/src/main/resources/db/migration/V42__time_projection_text_column_alignment.sql @@ -0,0 +1,7 @@ +-- Align TIME projection source text fields with real Leitstand payload lengths. + +ALTER TABLE "time".time_entry_search_projection + ALTER COLUMN time_recording_desc TYPE TEXT; + +ALTER TABLE "time".time_entry_search_projection + ALTER COLUMN time_recording_remark TYPE TEXT; diff --git a/src/main/resources/db/migration/V43__doc_document_time_enum_constraint_repair.sql b/src/main/resources/db/migration/V43__doc_document_time_enum_constraint_repair.sql new file mode 100644 index 0000000..7f35b9d --- /dev/null +++ b/src/main/resources/db/migration/V43__doc_document_time_enum_constraint_repair.sql @@ -0,0 +1,80 @@ +-- Repair DOC document enum/check alignment for TIME documents on databases +-- that still carry the pre-TIME family/type constraints. + +DO $$ +BEGIN + IF EXISTS ( + SELECT 1 + FROM pg_type t + JOIN pg_namespace n ON n.oid = t.typnamespace + WHERE n.nspname = 'doc' + AND t.typname = 'doc_document_type' + ) THEN + ALTER TYPE DOC.doc_document_type ADD VALUE IF NOT EXISTS 'TED_PACKAGE'; + ALTER TYPE DOC.doc_document_type ADD VALUE IF NOT EXISTS 'TED_NOTICE_LOT'; + ALTER TYPE DOC.doc_document_type ADD VALUE IF NOT EXISTS 'TIME_ENTRY'; + END IF; +END +$$; + +DO $$ +BEGIN + IF EXISTS ( + SELECT 1 + FROM pg_type t + JOIN pg_namespace n ON n.oid = t.typnamespace + WHERE n.nspname = 'doc' + AND t.typname = 'doc_document_family' + ) THEN + ALTER TYPE DOC.doc_document_family ADD VALUE IF NOT EXISTS 'TIME'; + END IF; +END +$$; + +DO $$ +BEGIN + IF EXISTS ( + SELECT 1 + FROM information_schema.tables + WHERE table_schema = 'doc' + AND table_name = 'doc_document' + ) THEN + ALTER TABLE DOC.doc_document DROP CONSTRAINT IF EXISTS doc_document_document_type_check; + ALTER TABLE DOC.doc_document + ADD CONSTRAINT doc_document_document_type_check + CHECK ( + document_type IN ( + 'TED_PACKAGE', + 'TED_NOTICE', + 'TED_NOTICE_LOT', + 'TIME_ENTRY', + 'EMAIL', + 'MIME_MESSAGE', + 'PDF', + 'DOCX', + 'HTML', + 'XML_GENERIC', + 'TEXT', + 'MARKDOWN', + 'ZIP_ARCHIVE', + 'GENERIC_BINARY', + 'UNKNOWN' + ) + ); + + ALTER TABLE DOC.doc_document DROP CONSTRAINT IF EXISTS doc_document_document_family_check; + ALTER TABLE DOC.doc_document + ADD CONSTRAINT doc_document_document_family_check + CHECK ( + document_family IN ( + 'PROCUREMENT', + 'TIME', + 'MAIL', + 'ATTACHMENT', + 'KNOWLEDGE', + 'GENERIC' + ) + ); + END IF; +END +$$; diff --git a/src/test/java/at/procon/dip/migration/DocDocumentTimeEnumConstraintRepairMigrationTest.java b/src/test/java/at/procon/dip/migration/DocDocumentTimeEnumConstraintRepairMigrationTest.java new file mode 100644 index 0000000..845a79c --- /dev/null +++ b/src/test/java/at/procon/dip/migration/DocDocumentTimeEnumConstraintRepairMigrationTest.java @@ -0,0 +1,135 @@ +package at.procon.dip.migration; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.sql.Statement; +import org.flywaydb.core.Flyway; +import org.flywaydb.core.api.MigrationVersion; +import org.junit.jupiter.api.Test; +import org.testcontainers.containers.PostgreSQLContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +@Testcontainers +class DocDocumentTimeEnumConstraintRepairMigrationTest { + + @Container + static PostgreSQLContainer postgres = new PostgreSQLContainer<>("postgres:16-alpine") + .withDatabaseName("dip_migration_test") + .withUsername("test") + .withPassword("test"); + + @Test + void repairMigrationExpandsLegacyDocDocumentChecksForTimeDocuments() throws SQLException { + createLegacyDocDocumentState(); + + Flyway.configure() + .dataSource(postgres.getJdbcUrl(), postgres.getUsername(), postgres.getPassword()) + .locations("filesystem:src/main/resources/db/migration") + .schemas("doc") + .defaultSchema("doc") + .baselineOnMigrate(true) + .baselineVersion(MigrationVersion.fromVersion("42")) + .load() + .migrate(); + + try (Connection connection = openConnection(); + Statement statement = connection.createStatement()) { + statement.executeUpdate(""" + INSERT INTO doc.doc_document (id, document_type, document_family) + VALUES ('709e388b-19d9-4c21-8d06-82b295b33505', 'TIME_ENTRY', 'TIME') + """); + } + + try (Connection connection = openConnection(); + var preparedStatement = connection.prepareStatement(""" + SELECT pg_get_constraintdef(oid) + FROM pg_constraint + WHERE conrelid = 'doc.doc_document'::regclass + AND conname = ? + """)) { + preparedStatement.setString(1, "doc_document_document_family_check"); + try (var resultSet = preparedStatement.executeQuery()) { + assertThat(resultSet.next()).isTrue(); + assertThat(resultSet.getString(1)).contains("TIME"); + } + } + } + + private void createLegacyDocDocumentState() throws SQLException { + try (Connection connection = openConnection(); + Statement statement = connection.createStatement()) { + statement.execute("CREATE SCHEMA doc"); + statement.execute(""" + CREATE TYPE doc.doc_document_type AS ENUM ( + 'TED_NOTICE', + 'EMAIL', + 'MIME_MESSAGE', + 'PDF', + 'DOCX', + 'HTML', + 'XML_GENERIC', + 'TEXT', + 'MARKDOWN', + 'ZIP_ARCHIVE', + 'GENERIC_BINARY', + 'UNKNOWN' + ) + """); + statement.execute(""" + CREATE TYPE doc.doc_document_family AS ENUM ( + 'PROCUREMENT', + 'MAIL', + 'ATTACHMENT', + 'KNOWLEDGE', + 'GENERIC' + ) + """); + statement.execute(""" + CREATE TABLE doc.doc_document ( + id UUID PRIMARY KEY, + document_type doc.doc_document_type NOT NULL, + document_family doc.doc_document_family NOT NULL, + CONSTRAINT doc_document_document_type_check + CHECK ( + document_type IN ( + 'TED_NOTICE', + 'EMAIL', + 'MIME_MESSAGE', + 'PDF', + 'DOCX', + 'HTML', + 'XML_GENERIC', + 'TEXT', + 'MARKDOWN', + 'ZIP_ARCHIVE', + 'GENERIC_BINARY', + 'UNKNOWN' + ) + ), + CONSTRAINT doc_document_document_family_check + CHECK ( + document_family IN ( + 'PROCUREMENT', + 'MAIL', + 'ATTACHMENT', + 'KNOWLEDGE', + 'GENERIC' + ) + ) + ) + """); + } + } + + private Connection openConnection() throws SQLException { + return DriverManager.getConnection( + postgres.getJdbcUrl(), + postgres.getUsername(), + postgres.getPassword() + ); + } +}