diff --git a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/config/ClickHouseConfig.java b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/config/ClickHouseConfig.java index d4df75ae..ee5d8db5 100644 --- a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/config/ClickHouseConfig.java +++ b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/config/ClickHouseConfig.java @@ -4,12 +4,15 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.Resource; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.jdbc.core.JdbcTemplate; import jakarta.annotation.PostConstruct; import javax.sql.DataSource; import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Comparator; import java.util.stream.Collectors; /** @@ -20,16 +23,15 @@ import java.util.stream.Collectors; *
* The ClickHouse container's {@code CLICKHOUSE_DB} env var creates the database; * this class creates the tables within it. + *
+ * Migration files are discovered automatically from {@code classpath:clickhouse/*.sql} + * and executed in filename order (numeric prefix sort). */ @Configuration public class ClickHouseConfig { private static final Logger log = LoggerFactory.getLogger(ClickHouseConfig.class); - private static final String[] SCHEMA_FILES = { - "clickhouse/01-schema.sql", "clickhouse/02-search-columns.sql", - "clickhouse/03-users.sql", "clickhouse/04-oidc-config.sql", - "clickhouse/05-oidc-auto-signup.sql", "clickhouse/06-oidc-display-name-claim.sql" - }; + private static final String MIGRATION_PATTERN = "classpath:clickhouse/*.sql"; private final DataSource dataSource; @@ -45,25 +47,34 @@ public class ClickHouseConfig { @PostConstruct void initSchema() { var jdbc = new JdbcTemplate(dataSource); - for (String schemaFile : SCHEMA_FILES) { - try { - String sql = new ClassPathResource(schemaFile).getContentAsString(StandardCharsets.UTF_8); - // Strip comment lines before splitting — a statement preceded by - // comment lines would otherwise be skipped entirely. - String stripped = sql.lines() - .filter(line -> !line.trim().startsWith("--")) - .collect(Collectors.joining("\n")); - for (String statement : stripped.split(";")) { - String trimmed = statement.trim(); - if (!trimmed.isEmpty()) { - jdbc.execute(trimmed); + try { + Resource[] resources = new PathMatchingResourcePatternResolver() + .getResources(MIGRATION_PATTERN); + Arrays.sort(resources, Comparator.comparing(Resource::getFilename)); + + for (Resource resource : resources) { + String filename = resource.getFilename(); + try { + String sql = resource.getContentAsString(StandardCharsets.UTF_8); + String stripped = sql.lines() + .filter(line -> !line.trim().startsWith("--")) + .collect(Collectors.joining("\n")); + for (String statement : stripped.split(";")) { + String trimmed = statement.trim(); + if (!trimmed.isEmpty()) { + jdbc.execute(trimmed); + } } + log.info("Applied schema: {}", filename); + } catch (Exception e) { + log.error("Failed to apply schema: {}", filename, e); + throw new RuntimeException("Schema initialization failed: " + filename, e); } - log.info("Applied schema: {}", schemaFile); - } catch (Exception e) { - log.error("Failed to apply schema: {}", schemaFile, e); - throw new RuntimeException("Schema initialization failed: " + schemaFile, e); } + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { + throw new RuntimeException("Failed to discover migration files", e); } } }