package at.procon.ted.startup; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; import org.springframework.core.annotation.Order; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Component; /** * Startup runner that fixes the organization table schema if needed. * This is a workaround for Flyway V2 migration not being applied automatically. * * Extends VARCHAR fields to handle long TED data. * * @author Martin.Schweitzer@procon.co.at and claude.ai */ @Component @Order(1) // Run before other startup runners @RequiredArgsConstructor @Slf4j public class OrganizationSchemaFixRunner implements ApplicationRunner { private final JdbcTemplate jdbcTemplate; @Override public void run(ApplicationArguments args) throws Exception { log.info("Checking organization table schema..."); try { // Check if schema fix is needed by trying to query column types String checkSql = """ SELECT column_name, character_maximum_length, data_type FROM information_schema.columns WHERE table_schema = 'ted' AND table_name = 'organization' AND column_name IN ('postal_code', 'company_id', 'name') ORDER BY column_name """; var columnInfo = jdbcTemplate.queryForList(checkSql); boolean needsFix = false; for (var row : columnInfo) { String columnName = (String) row.get("column_name"); Integer maxLength = (Integer) row.get("character_maximum_length"); String dataType = (String) row.get("data_type"); log.debug("Column {}: type={}, max_length={}", columnName, dataType, maxLength); // Check if any field is still too small if ("postal_code".equals(columnName) && maxLength != null && maxLength < 255) { needsFix = true; log.warn("Column postal_code is too small: {} chars, needs 255", maxLength); } if ("company_id".equals(columnName) && maxLength != null && maxLength < 255) { needsFix = true; log.warn("Column company_id is too small: {} chars, needs 255", maxLength); } } if (needsFix) { log.warn("Organization table schema needs fixing - applying migration..."); applySchemaFix(); log.info("Organization table schema fixed successfully!"); } else { log.info("Organization table schema is up to date"); } } catch (Exception e) { log.error("Failed to check/fix organization table schema: {}", e.getMessage(), e); throw e; } } private void applySchemaFix() { log.info("Applying schema fix to ted.organization table..."); // Apply all column type changes // Use TEXT for fields that can be extremely long String[] alterStatements = { "ALTER TABLE ted.organization ALTER COLUMN postal_code TYPE TEXT", "ALTER TABLE ted.organization ALTER COLUMN street_name TYPE TEXT", "ALTER TABLE ted.organization ALTER COLUMN city TYPE TEXT", // Some cities have very long names "ALTER TABLE ted.organization ALTER COLUMN phone TYPE VARCHAR(100)", "ALTER TABLE ted.organization ALTER COLUMN org_reference TYPE VARCHAR(100)", "ALTER TABLE ted.organization ALTER COLUMN role TYPE VARCHAR(100)", "ALTER TABLE ted.organization ALTER COLUMN company_id TYPE TEXT", // Can be very long "ALTER TABLE ted.organization ALTER COLUMN name TYPE TEXT" }; for (String sql : alterStatements) { try { jdbcTemplate.execute(sql); log.debug("Executed: {}", sql); } catch (Exception e) { log.warn("Failed to execute {}: {} (may already be applied)", sql, e.getMessage()); } } log.info("Schema fix applied successfully"); } }