From a38aa4024f39580c0c7a06e12174c47257e17733 Mon Sep 17 00:00:00 2001 From: "duong.doan1" Date: Tue, 7 Apr 2026 11:51:08 +0700 Subject: [PATCH] feat: PostgreSQL support infrastructure - Add DatabaseType enum and database abstraction layer - Update DataSourcesConfiguration to support PostgreSQL DataSource creation - Create DatabaseUdfProvider interface with SQLite and PostgreSQL implementations - Add JooqUdfHelper for database-agnostic UDF/collation functions - Update KomgaJooqConfiguration to use dynamic SQLDialect based on database type - Add PostgreSQL migration directory with initial migration - Update build.gradle.kts with PostgreSQL dependencies - Fix qualifier name mismatch in KomgaJooqConfiguration - Begin migration of DAO classes to use JooqUdfHelper instead of SqliteUdfDataSource Note: Compilation errors remain due to incomplete migration from old API to new abstraction layer. --- .kilo/plans/1775535760568-brave-panda.md | 96 +++++++++- gradle/libs.versions.toml | 1 + komga/build.gradle.kts | 2 + .../V20200706141854__initial_migration.sql | 164 ++++++++++++++++++ .../configuration/KomgaProperties.kt | 6 + .../datasource/DataSourcesConfiguration.kt | 69 ++++++-- .../datasource/DatabaseCompatibility.kt | 12 ++ .../infrastructure/datasource/DatabaseType.kt | 6 + .../datasource/DatabaseUdfProvider.kt | 14 ++ .../DatabaseUdfProviderConfiguration.kt | 26 +++ .../datasource/PostgresUdfProvider.kt | 47 +++++ .../datasource/SqliteUdfDataSource.kt | 90 ++-------- .../datasource/SqliteUdfProvider.kt | 82 +++++++++ .../infrastructure/jooq/JooqUdfHelper.kt | 14 ++ .../jooq/KomgaJooqConfiguration.kt | 22 ++- .../gotson/komga/infrastructure/jooq/Utils.kt | 5 +- .../infrastructure/jooq/main/BookDtoDao.kt | 9 +- .../jooq/main/ReferentialDao.kt | 4 +- .../infrastructure/jooq/main/SeriesDtoDao.kt | 1 + komga/src/main/resources/application.yml | 6 +- .../DataSourcesConfigurationTest.kt | 4 +- 21 files changed, 568 insertions(+), 112 deletions(-) create mode 100644 komga/src/flyway/resources/db/migration/postgresql/V20200706141854__initial_migration.sql create mode 100644 komga/src/main/kotlin/org/gotson/komga/infrastructure/datasource/DatabaseCompatibility.kt create mode 100644 komga/src/main/kotlin/org/gotson/komga/infrastructure/datasource/DatabaseType.kt create mode 100644 komga/src/main/kotlin/org/gotson/komga/infrastructure/datasource/DatabaseUdfProvider.kt create mode 100644 komga/src/main/kotlin/org/gotson/komga/infrastructure/datasource/DatabaseUdfProviderConfiguration.kt create mode 100644 komga/src/main/kotlin/org/gotson/komga/infrastructure/datasource/PostgresUdfProvider.kt create mode 100644 komga/src/main/kotlin/org/gotson/komga/infrastructure/datasource/SqliteUdfProvider.kt create mode 100644 komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/JooqUdfHelper.kt diff --git a/.kilo/plans/1775535760568-brave-panda.md b/.kilo/plans/1775535760568-brave-panda.md index 7e4fb48a..4f8f9556 100644 --- a/.kilo/plans/1775535760568-brave-panda.md +++ b/.kilo/plans/1775535760568-brave-panda.md @@ -27,6 +27,7 @@ Thêm hỗ trợ PostgreSQL cho Komga, cho phép người dùng lựa chọn dat ### 1.1. Thêm dependency PostgreSQL - Thêm runtime dependency `org.postgresql:postgresql` vào `build.gradle.kts`. - Thêm `jooqGenerator` dependency tương ứng. +- Thêm Testcontainers PostgreSQL dependency cho testing. - Giữ SQLite làm mặc định. ### 1.2. Mở rộng KomgaProperties.Database @@ -42,29 +43,54 @@ Thêm hỗ trợ PostgreSQL cho Komga, cho phép người dùng lựa chọn dat - Với PostgreSQL: sử dụng `DataSourceBuilder` với driver PostgreSQL, không có pragmas/journal mode. - Với SQLite: giữ nguyên logic hiện tại. - Tạo bean DataSource tương ứng. +- **Unit tests**: Test tạo DataSource cho cả SQLite và PostgreSQL. ### 1.4. Tạo abstract UDF/Collation provider - Tạo interface `DatabaseDialectProvider` với các method: `collationUnicode3()`, `regexpFunction()`, `stripAccentsFunction()`. - Tạo implementation cho SQLite (`SqliteDialectProvider`) và PostgreSQL (`PostgresDialectProvider`). - PostgreSQL: sử dụng extension `unaccent` cho strip accents, collation ICU cho unicode collation, toán tử `~*` cho regexp. - Đăng ký bean tương ứng với database type. +- **Unit tests**: Test từng implementation. ### 1.5. Cập nhật KomgaJooqConfiguration - Inject `KomgaProperties` để lấy `database.type`. - Set dialect động: `SQLDialect.SQLITE` hoặc `SQLDialect.POSTGRES`. - Cập nhật `createDslContext` để sử dụng dialect phù hợp. +- **Unit tests**: Test DSLContext creation với cả hai dialect. ### 1.6. Cập nhật cấu hình Flyway - `spring.flyway.locations` hiện dùng `{vendor}` sẽ tự động chọn thư mục `postgresql` khi dialect là PostgreSQL. - Đảm bảo Flyway detect đúng vendor. +### 1.7. Integration testing setup +- Tạo test configuration cho PostgreSQL sử dụng Testcontainers. +- Tạo integration test cơ bản khởi động ứng dụng với PostgreSQL. +- Đảm bảo Flyway migration chạy được trên PostgreSQL (chưa có migration thật, chỉ test connection). + +### 1.8. Python API test script +- Tạo script Python test các API endpoints chính: + - Health check (`/actuator/health`) + - Authentication endpoints + - Library endpoints + - Series endpoints + - Book endpoints +- Script chạy với backend SQLite (mặc định) để đảm bảo không bị break. +- Có thể mở rộng sau để test với PostgreSQL. + +### 1.9. Backward compatibility verification +- Đảm bảo ứng dụng vẫn chạy được với cấu hình SQLite hiện tại. +- Test với `dev,noclaim` profile. +- Verify các chức năng cơ bản. + ### Deliverables Sprint 1 - Dependency PostgreSQL được thêm. - Cấu hình database type hoạt động. - DataSources tạo đúng với PostgreSQL. -- Abstract dialect provider. -- JOOQ dialect động. -- Các unit test cơ bản cho cấu hình. +- Abstract dialect provider với unit tests. +- JOOQ dialect động với unit tests. +- Integration test setup với Testcontainers PostgreSQL. +- Python API test script. +- Ứng dụng vẫn hoạt động với SQLite (backward compatibility). ## Sprint 2: Migration database và JOOQ generation (Tuần 2) @@ -144,9 +170,58 @@ Thêm hỗ trợ PostgreSQL cho Komga, cho phép người dùng lựa chọn dat ## Rủi ro và phụ thuộc 1. **Hiệu năng**: PostgreSQL có thể khác SQLite, cần tuning. -2. **UDF/collation**: Đảm bảo chức năng tương đương. +2. **UDF/collation**: Đảm bảo chức năng tương đương, đặc biệt collation unicode và strip accents. 3. **Migration phức tạp**: Số lượng migration lớn (91 files), cần chuyển đổi cẩn thận. 4. **Testing**: Cần thiết lập CI với cả hai database. +5. **PostgreSQL extensions**: Cần extension `unaccent` và ICU collation support. +6. **Backward compatibility**: Phải giữ nguyên hành vi với SQLite. + +## Chi tiết Sprint 1 tasks + +### Task 1.1: Thêm dependencies +- File: `komga/build.gradle.kts` +- Thêm: `runtimeOnly("org.postgresql:postgresql")` +- Thêm: `testImplementation("org.testcontainers:postgresql")` +- Cập nhật jooq generator dependency nếu cần. + +### Task 1.2: Mở rộng KomgaProperties +- File: `komga/src/main/kotlin/org/gotson/komga/infrastructure/configuration/KomgaProperties.kt` +- Thêm enum `DatabaseType`. +- Thêm thuộc tính mới, giữ `file` cho backward compatibility. +- Xử lý logic tự động detect type từ URL/file. + +### Task 1.3: DataSourcesConfiguration +- File: `komga/src/main/kotlin/org/gotson/komga/infrastructure/datasource/DataSourcesConfiguration.kt` +- Refactor `buildDataSource` thành hai method. +- Unit tests: `DataSourcesConfigurationTest` mở rộng. + +### Task 1.4: DatabaseDialectProvider +- File mới: `komga/src/main/kotlin/org/gotson/komga/infrastructure/dialect/DatabaseDialectProvider.kt` +- Interface và hai implementations. +- Configuration để đăng ký bean đúng. +- Unit tests cho từng implementation. + +### Task 1.5: KomgaJooqConfiguration +- File: `komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/KomgaJooqConfiguration.kt` +- Inject `KomgaProperties` và set dialect động. +- Unit tests. + +### Task 1.6: Integration tests +- File: `komga/src/test/kotlin/org/gotson/komga/infrastructure/PostgresIntegrationTest.kt` +- Sử dụng Testcontainers PostgreSQL. +- Test khởi động ứng dụng với PostgreSQL. +- Test Flyway connection. + +### Task 1.7: Python API test script +- File: `test_api_baseline.py` +- Test các endpoints chính với requests library. +- Chạy với backend SQLite dev profile. +- Output: pass/fail report. + +### Task 1.8: Verification +- Chạy ứng dụng với `dev,noclaim` profile. +- Test manual các chức năng cơ bản. +- Đảm bảo không regression. ## Các file chính cần sửa đổi 1. `komga/build.gradle.kts` @@ -161,10 +236,19 @@ Thêm hỗ trợ PostgreSQL cho Komga, cho phép người dùng lựa chọn dat 10. Tạo `src/flyway/kotlin/db/migration/postgresql/` ## Ước lượng công sức -- Sprint 1: 40 giờ +- Sprint 1: 50 giờ (bao gồm unit tests, integration tests, Python script) - Sprint 2: 60 giờ (do migration nhiều) - Sprint 3: 50 giờ -- Tổng: ~150 giờ +- Tổng: ~160 giờ + +## Success criteria Sprint 1 +1. Ứng dụng khởi động được với cấu hình SQLite hiện tại (không regression). +2. Cấu hình PostgreSQL được parse và validate đúng. +3. DataSources tạo thành công cho PostgreSQL (test với Testcontainers). +4. DatabaseDialectProvider implementations hoạt động. +5. JOOQ dialect được set động. +6. Python API test script pass với SQLite backend. +7. Tất cả unit tests và integration tests pass. ## Kết quả mong đợi Komga hỗ trợ cả SQLite và PostgreSQL, người dùng có thể lựa chọn database phù hợp với nhu cầu scale. \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 6bd5699c..7382aec8 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,6 @@ [versions] sqliteJdbc = "3.50.2.0" +postgresql = "42.7.5" nightmonkeys = "1.0.0" twelvemonkeys = "3.12.0" springboot = "3.5.4" diff --git a/komga/build.gradle.kts b/komga/build.gradle.kts index c5e15cdf..a5dd4270 100644 --- a/komga/build.gradle.kts +++ b/komga/build.gradle.kts @@ -105,7 +105,9 @@ dependencies { implementation("com.github.ben-manes.caffeine:caffeine") implementation("org.xerial:sqlite-jdbc:${libs.versions.sqliteJdbc.get()}") + implementation("org.postgresql:postgresql:${libs.versions.postgresql.get()}") jooqGenerator("org.xerial:sqlite-jdbc:${libs.versions.sqliteJdbc.get()}") + jooqGenerator("org.postgresql:postgresql:${libs.versions.postgresql.get()}") if (version.toString().endsWith(".0.0")) { ksp("com.github.gotson.bestbefore:bestbefore-processor-kotlin:0.2.0") diff --git a/komga/src/flyway/resources/db/migration/postgresql/V20200706141854__initial_migration.sql b/komga/src/flyway/resources/db/migration/postgresql/V20200706141854__initial_migration.sql new file mode 100644 index 00000000..a998e301 --- /dev/null +++ b/komga/src/flyway/resources/db/migration/postgresql/V20200706141854__initial_migration.sql @@ -0,0 +1,164 @@ +CREATE TABLE LIBRARY +( + ID varchar NOT NULL PRIMARY KEY, + CREATED_DATE timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + LAST_MODIFIED_DATE timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + NAME varchar NOT NULL, + ROOT varchar NOT NULL, + IMPORT_COMICINFO_BOOK boolean NOT NULL DEFAULT true, + IMPORT_COMICINFO_SERIES boolean NOT NULL DEFAULT true, + IMPORT_COMICINFO_COLLECTION boolean NOT NULL DEFAULT true, + IMPORT_EPUB_BOOK boolean NOT NULL DEFAULT true, + IMPORT_EPUB_SERIES boolean NOT NULL DEFAULT true +); + +CREATE TABLE "USER" +( + ID varchar NOT NULL PRIMARY KEY, + CREATED_DATE timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + LAST_MODIFIED_DATE timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + EMAIL varchar NOT NULL UNIQUE, + PASSWORD varchar NOT NULL, + SHARED_ALL_LIBRARIES boolean NOT NULL DEFAULT true, + ROLE_ADMIN boolean NOT NULL DEFAULT false, + ROLE_FILE_DOWNLOAD boolean NOT NULL DEFAULT true, + ROLE_PAGE_STREAMING boolean NOT NULL DEFAULT true +); + +CREATE TABLE USER_LIBRARY_SHARING +( + USER_ID varchar NOT NULL, + LIBRARY_ID varchar NOT NULL, + PRIMARY KEY (USER_ID, LIBRARY_ID), + FOREIGN KEY (USER_ID) REFERENCES "USER" (ID), + FOREIGN KEY (LIBRARY_ID) REFERENCES LIBRARY (ID) +); + +CREATE TABLE SERIES +( + ID varchar NOT NULL PRIMARY KEY, + CREATED_DATE timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + LAST_MODIFIED_DATE timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + FILE_LAST_MODIFIED timestamp NOT NULL, + NAME varchar NOT NULL, + URL varchar NOT NULL, + LIBRARY_ID varchar NOT NULL, + FOREIGN KEY (LIBRARY_ID) REFERENCES LIBRARY (ID) +); + +CREATE TABLE SERIES_METADATA +( + CREATED_DATE timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + LAST_MODIFIED_DATE timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + STATUS varchar NOT NULL, + STATUS_LOCK boolean NOT NULL DEFAULT false, + TITLE varchar NOT NULL, + TITLE_LOCK boolean NOT NULL DEFAULT false, + TITLE_SORT varchar NOT NULL, + TITLE_SORT_LOCK boolean NOT NULL DEFAULT false, + SUMMARY text, + SUMMARY_LOCK boolean NOT NULL DEFAULT false, + PUBLISHER varchar, + PUBLISHER_LOCK boolean NOT NULL DEFAULT false, + READING_DIRECTION varchar, + READING_DIRECTION_LOCK boolean NOT NULL DEFAULT false, + AGE_RATING integer, + AGE_RATING_LOCK boolean NOT NULL DEFAULT false, + LANGUAGE varchar, + LANGUAGE_LOCK boolean NOT NULL DEFAULT false, + SERIES_ID varchar NOT NULL PRIMARY KEY, + FOREIGN KEY (SERIES_ID) REFERENCES SERIES (ID) +); + +CREATE TABLE BOOK +( + ID varchar NOT NULL PRIMARY KEY, + CREATED_DATE timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + LAST_MODIFIED_DATE timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + FILE_LAST_MODIFIED timestamp NOT NULL, + NAME varchar NOT NULL, + URL varchar NOT NULL, + FILE_SIZE bigint NOT NULL, + NUMBER integer, + SERIES_ID varchar NOT NULL, + FOREIGN KEY (SERIES_ID) REFERENCES SERIES (ID) +); + +CREATE TABLE BOOK_METADATA +( + CREATED_DATE timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + LAST_MODIFIED_DATE timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + TITLE varchar NOT NULL, + TITLE_LOCK boolean NOT NULL DEFAULT false, + TITLE_SORT varchar NOT NULL, + TITLE_SORT_LOCK boolean NOT NULL DEFAULT false, + SUMMARY text, + SUMMARY_LOCK boolean NOT NULL DEFAULT false, + NUMBER varchar, + NUMBER_LOCK boolean NOT NULL DEFAULT false, + NUMBER_SORT double precision, + NUMBER_SORT_LOCK boolean NOT NULL DEFAULT false, + RELEASE_DATE date, + RELEASE_DATE_LOCK boolean NOT NULL DEFAULT false, + AUTHORS text, + AUTHORS_LOCK boolean NOT NULL DEFAULT false, + BOOK_ID varchar NOT NULL PRIMARY KEY, + FOREIGN KEY (BOOK_ID) REFERENCES BOOK (ID) +); + +CREATE TABLE READ_PROGRESS +( + CREATED_DATE timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + LAST_MODIFIED_DATE timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PAGE integer NOT NULL, + COMPLETED boolean NOT NULL DEFAULT false, + USER_ID varchar NOT NULL, + BOOK_ID varchar NOT NULL, + PRIMARY KEY (USER_ID, BOOK_ID), + FOREIGN KEY (USER_ID) REFERENCES "USER" (ID), + FOREIGN KEY (BOOK_ID) REFERENCES BOOK (ID) +); + +CREATE TABLE READLIST +( + ID varchar NOT NULL PRIMARY KEY, + CREATED_DATE timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + LAST_MODIFIED_DATE timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + NAME varchar NOT NULL, + SUMMARY text, + FILTERED boolean NOT NULL DEFAULT false, + USER_ID varchar NOT NULL, + FOREIGN KEY (USER_ID) REFERENCES "USER" (ID) +); + +CREATE TABLE READLIST_BOOKS +( + READLIST_ID varchar NOT NULL, + BOOK_ID varchar NOT NULL, + NUMBER integer NOT NULL, + PRIMARY KEY (READLIST_ID, BOOK_ID), + FOREIGN KEY (READLIST_ID) REFERENCES READLIST (ID), + FOREIGN KEY (BOOK_ID) REFERENCES BOOK (ID) +); + +CREATE TABLE COLLECTION +( + ID varchar NOT NULL PRIMARY KEY, + CREATED_DATE timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + LAST_MODIFIED_DATE timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + NAME varchar NOT NULL, + SUMMARY text, + FILTERED boolean NOT NULL DEFAULT false, + USER_ID varchar NOT NULL, + FOREIGN KEY (USER_ID) REFERENCES "USER" (ID) +); + +CREATE TABLE COLLECTION_SERIES +( + COLLECTION_ID varchar NOT NULL, + SERIES_ID varchar NOT NULL, + NUMBER integer NOT NULL, + PRIMARY KEY (COLLECTION_ID, SERIES_ID), + FOREIGN KEY (COLLECTION_ID) REFERENCES COLLECTION (ID), + FOREIGN KEY (SERIES_ID) REFERENCES SERIES (ID) +); \ No newline at end of file diff --git a/komga/src/main/kotlin/org/gotson/komga/infrastructure/configuration/KomgaProperties.kt b/komga/src/main/kotlin/org/gotson/komga/infrastructure/configuration/KomgaProperties.kt index 40af5fbc..2006b5cd 100644 --- a/komga/src/main/kotlin/org/gotson/komga/infrastructure/configuration/KomgaProperties.kt +++ b/komga/src/main/kotlin/org/gotson/komga/infrastructure/configuration/KomgaProperties.kt @@ -75,6 +75,12 @@ class KomgaProperties { var pragmas: Map = emptyMap() var checkLocalFilesystem: Boolean = true + + var type: org.gotson.komga.infrastructure.datasource.DatabaseType = org.gotson.komga.infrastructure.datasource.DatabaseType.SQLITE + + var url: String? = null + var username: String? = null + var password: String? = null } class Fonts { diff --git a/komga/src/main/kotlin/org/gotson/komga/infrastructure/datasource/DataSourcesConfiguration.kt b/komga/src/main/kotlin/org/gotson/komga/infrastructure/datasource/DataSourcesConfiguration.kt index 3f491eee..e194ec5a 100644 --- a/komga/src/main/kotlin/org/gotson/komga/infrastructure/datasource/DataSourcesConfiguration.kt +++ b/komga/src/main/kotlin/org/gotson/komga/infrastructure/datasource/DataSourcesConfiguration.kt @@ -3,6 +3,7 @@ package org.gotson.komga.infrastructure.datasource import com.zaxxer.hikari.HikariConfig import com.zaxxer.hikari.HikariDataSource import org.gotson.komga.infrastructure.configuration.KomgaProperties +import org.postgresql.ds.PGSimpleDataSource import org.springframework.boot.jdbc.DataSourceBuilder import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration @@ -15,25 +16,25 @@ import javax.sql.DataSource class DataSourcesConfiguration( private val komgaProperties: KomgaProperties, ) { - @Bean("sqliteDataSourceRW") + @Bean("mainDataSourceRW") @Primary - fun sqliteDataSourceRW(): DataSource = - buildDataSource("SqliteMainPoolRW", SqliteUdfDataSource::class.java, komgaProperties.database) + fun mainDataSourceRW(): DataSource = + buildDataSource("MainPoolRW", komgaProperties.database) .apply { // force pool size to 1 if the pool is only used for writes if (komgaProperties.database.shouldSeparateReadFromWrites()) this.maximumPoolSize = 1 } - @Bean("sqliteDataSourceRO") - fun sqliteDataSourceRO(): DataSource = + @Bean("mainDataSourceRO") + fun mainDataSourceRO(): DataSource = if (komgaProperties.database.shouldSeparateReadFromWrites()) - buildDataSource("SqliteMainPoolRO", SqliteUdfDataSource::class.java, komgaProperties.database) + buildDataSource("MainPoolRO", komgaProperties.database) else - sqliteDataSourceRW() + mainDataSourceRW() @Bean("tasksDataSourceRW") fun tasksDataSourceRW(): DataSource = - buildDataSource("SqliteTasksPoolRW", SQLiteDataSource::class.java, komgaProperties.tasksDb) + buildDataSource("TasksPoolRW", komgaProperties.tasksDb) .apply { // pool size is always 1: // - if there's only 1 pool for read and writes, size should be 1 @@ -44,13 +45,22 @@ class DataSourcesConfiguration( @Bean("tasksDataSourceRO") fun tasksDataSourceRO(): DataSource = if (komgaProperties.tasksDb.shouldSeparateReadFromWrites()) - buildDataSource("SqliteTasksPoolRO", SQLiteDataSource::class.java, komgaProperties.tasksDb) + buildDataSource("TasksPoolRO", komgaProperties.tasksDb) else tasksDataSourceRW() private fun buildDataSource( poolName: String, - dataSourceClass: Class, + databaseProps: KomgaProperties.Database, + ): HikariDataSource { + return when (databaseProps.type) { + DatabaseType.SQLITE -> buildSqliteDataSource(poolName, databaseProps) + DatabaseType.POSTGRESQL -> buildPostgresDataSource(poolName, databaseProps) + } + } + + private fun buildSqliteDataSource( + poolName: String, databaseProps: KomgaProperties.Database, ): HikariDataSource { val extraPragmas = @@ -66,10 +76,10 @@ class DataSourcesConfiguration( .create() .driverClassName("org.sqlite.JDBC") .url("jdbc:sqlite:${databaseProps.file}$extraPragmas") - .type(dataSourceClass) + .type(SqliteUdfDataSource::class.java) .build() - with(dataSource) { + with(dataSource as SqliteUdfDataSource) { setEnforceForeignKeys(true) setGetGeneratedKeys(false) } @@ -95,7 +105,40 @@ class DataSourcesConfiguration( ) } + private fun buildPostgresDataSource( + poolName: String, + databaseProps: KomgaProperties.Database, + ): HikariDataSource { + val dataSource = PGSimpleDataSource().apply { + databaseProps.url?.let { setURL(it) } + databaseProps.username?.let { user = it } + databaseProps.password?.let { password = it } + } + + val poolSize = + if (databaseProps.poolSize != null) + databaseProps.poolSize!! + else + Runtime.getRuntime().availableProcessors().coerceAtMost(databaseProps.maxPoolSize) + + return HikariDataSource( + HikariConfig().apply { + this.dataSource = dataSource + this.poolName = poolName + this.maximumPoolSize = poolSize + this.minimumIdle = 1 + this.connectionTimeout = 30000 + this.idleTimeout = 600000 + this.maxLifetime = 1800000 + }, + ) + } + fun KomgaProperties.Database.isMemory() = file.contains(":memory:") || file.contains("mode=memory") - fun KomgaProperties.Database.shouldSeparateReadFromWrites(): Boolean = !isMemory() && journalMode == SQLiteConfig.JournalMode.WAL + fun KomgaProperties.Database.shouldSeparateReadFromWrites(): Boolean = + when (type) { + DatabaseType.SQLITE -> !isMemory() && journalMode == SQLiteConfig.JournalMode.WAL + DatabaseType.POSTGRESQL -> false // PostgreSQL doesn't need separate read/write pools + } } diff --git a/komga/src/main/kotlin/org/gotson/komga/infrastructure/datasource/DatabaseCompatibility.kt b/komga/src/main/kotlin/org/gotson/komga/infrastructure/datasource/DatabaseCompatibility.kt new file mode 100644 index 00000000..18b5c911 --- /dev/null +++ b/komga/src/main/kotlin/org/gotson/komga/infrastructure/datasource/DatabaseCompatibility.kt @@ -0,0 +1,12 @@ +package org.gotson.komga.infrastructure.datasource + +object DatabaseCompatibility { + // These constants are maintained for backward compatibility + // They will be dynamically resolved based on the active database type + + @Deprecated("Use DatabaseUdfProvider instead", ReplaceWith("databaseUdfProvider.udfStripAccentsName")) + const val UDF_STRIP_ACCENTS = "UDF_STRIP_ACCENTS" + + @Deprecated("Use DatabaseUdfProvider instead", ReplaceWith("databaseUdfProvider.collationUnicode3Name")) + const val COLLATION_UNICODE_3 = "COLLATION_UNICODE_3" +} \ No newline at end of file diff --git a/komga/src/main/kotlin/org/gotson/komga/infrastructure/datasource/DatabaseType.kt b/komga/src/main/kotlin/org/gotson/komga/infrastructure/datasource/DatabaseType.kt new file mode 100644 index 00000000..99ea22f5 --- /dev/null +++ b/komga/src/main/kotlin/org/gotson/komga/infrastructure/datasource/DatabaseType.kt @@ -0,0 +1,6 @@ +package org.gotson.komga.infrastructure.datasource + +enum class DatabaseType { + SQLITE, + POSTGRESQL +} \ No newline at end of file diff --git a/komga/src/main/kotlin/org/gotson/komga/infrastructure/datasource/DatabaseUdfProvider.kt b/komga/src/main/kotlin/org/gotson/komga/infrastructure/datasource/DatabaseUdfProvider.kt new file mode 100644 index 00000000..8ac63320 --- /dev/null +++ b/komga/src/main/kotlin/org/gotson/komga/infrastructure/datasource/DatabaseUdfProvider.kt @@ -0,0 +1,14 @@ +package org.gotson.komga.infrastructure.datasource + +import org.jooq.Field +import org.jooq.impl.DSL + +interface DatabaseUdfProvider { + val udfStripAccentsName: String + val collationUnicode3Name: String + + fun Field.udfStripAccents(): Field + fun Field.collateUnicode3(): Field + + fun initializeConnection(connection: Any) +} \ No newline at end of file diff --git a/komga/src/main/kotlin/org/gotson/komga/infrastructure/datasource/DatabaseUdfProviderConfiguration.kt b/komga/src/main/kotlin/org/gotson/komga/infrastructure/datasource/DatabaseUdfProviderConfiguration.kt new file mode 100644 index 00000000..8fbc58e6 --- /dev/null +++ b/komga/src/main/kotlin/org/gotson/komga/infrastructure/datasource/DatabaseUdfProviderConfiguration.kt @@ -0,0 +1,26 @@ +package org.gotson.komga.infrastructure.datasource + +import org.gotson.komga.infrastructure.configuration.KomgaProperties +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration + +@Configuration +class DatabaseUdfProviderConfiguration( + private val komgaProperties: KomgaProperties +) { + @Bean + fun databaseUdfProvider(): DatabaseUdfProvider { + return when (komgaProperties.database.type) { + DatabaseType.SQLITE -> SqliteUdfProvider() + DatabaseType.POSTGRESQL -> PostgresUdfProvider() + } + } + + @Bean + fun tasksDatabaseUdfProvider(): DatabaseUdfProvider { + return when (komgaProperties.tasksDb.type) { + DatabaseType.SQLITE -> SqliteUdfProvider() + DatabaseType.POSTGRESQL -> PostgresUdfProvider() + } + } +} \ No newline at end of file diff --git a/komga/src/main/kotlin/org/gotson/komga/infrastructure/datasource/PostgresUdfProvider.kt b/komga/src/main/kotlin/org/gotson/komga/infrastructure/datasource/PostgresUdfProvider.kt new file mode 100644 index 00000000..f996753f --- /dev/null +++ b/komga/src/main/kotlin/org/gotson/komga/infrastructure/datasource/PostgresUdfProvider.kt @@ -0,0 +1,47 @@ +package org.gotson.komga.infrastructure.datasource + +import io.github.oshai.kotlinlogging.KotlinLogging +import org.jooq.Field +import org.jooq.impl.DSL +import java.sql.Connection + +private val log = KotlinLogging.logger {} + +class PostgresUdfProvider : DatabaseUdfProvider { + override val udfStripAccentsName = "UDF_STRIP_ACCENTS" + override val collationUnicode3Name = "COLLATION_UNICODE_3" + + override fun Field.udfStripAccents(): Field = + // PostgreSQL has unaccent extension, but we'll implement it in application layer + // For now, we'll create a placeholder function + DSL.function(udfStripAccentsName, String::class.java, this) + + override fun Field.collateUnicode3(): Field = + // PostgreSQL uses ICU collations, we'll use "und-u-ks-level2" for Unicode collation + this.collate("und-u-ks-level2") + + override fun initializeConnection(connection: Any) { + val pgConnection = connection as Connection + log.debug { "Initializing PostgreSQL connection with custom functions" } + + // Create the strip accents function if it doesn't exist + val createFunctionSQL = """ + CREATE OR REPLACE FUNCTION $udfStripAccentsName(text TEXT) + RETURNS TEXT AS $$ + BEGIN + -- This is a placeholder. In production, you might want to: + -- 1. Use the unaccent extension: SELECT unaccent(text) + -- 2. Or implement custom logic in application layer + RETURN text; + END; + $$ LANGUAGE plpgsql IMMUTABLE; + """.trimIndent() + + try { + pgConnection.createStatement().execute(createFunctionSQL) + log.debug { "Created PostgreSQL function $udfStripAccentsName" } + } catch (e: Exception) { + log.error(e) { "Failed to create PostgreSQL function $udfStripAccentsName" } + } + } +} \ No newline at end of file diff --git a/komga/src/main/kotlin/org/gotson/komga/infrastructure/datasource/SqliteUdfDataSource.kt b/komga/src/main/kotlin/org/gotson/komga/infrastructure/datasource/SqliteUdfDataSource.kt index e69fc795..3de5029a 100644 --- a/komga/src/main/kotlin/org/gotson/komga/infrastructure/datasource/SqliteUdfDataSource.kt +++ b/komga/src/main/kotlin/org/gotson/komga/infrastructure/datasource/SqliteUdfDataSource.kt @@ -1,83 +1,27 @@ package org.gotson.komga.infrastructure.datasource -import com.ibm.icu.text.Collator -import io.github.oshai.kotlinlogging.KotlinLogging -import org.gotson.komga.language.stripAccents -import org.sqlite.Collation -import org.sqlite.Function import org.sqlite.SQLiteConnection import org.sqlite.SQLiteDataSource import java.sql.Connection -private val log = KotlinLogging.logger {} - class SqliteUdfDataSource : SQLiteDataSource() { - companion object { - const val UDF_STRIP_ACCENTS = "UDF_STRIP_ACCENTS" - const val COLLATION_UNICODE_3 = "COLLATION_UNICODE_3" - } + companion object { + // These constants are maintained for backward compatibility + // In a future version, they should be replaced with DatabaseUdfProvider + const val UDF_STRIP_ACCENTS = "UDF_STRIP_ACCENTS" + const val COLLATION_UNICODE_3 = "COLLATION_UNICODE_3" + } + + private val udfProvider = SqliteUdfProvider() - override fun getConnection(): Connection = super.getConnection().also { addAllUdf(it as SQLiteConnection) } + override fun getConnection(): Connection = super.getConnection().also { + udfProvider.initializeConnection(it as SQLiteConnection) + } - override fun getConnection( - username: String?, - password: String?, - ): SQLiteConnection = super.getConnection(username, password).also { addAllUdf(it) } - - private fun addAllUdf(connection: SQLiteConnection) { - createUdfRegexp(connection) - createUdfStripAccents(connection) - createUnicode3Collation(connection) - } - - private fun createUdfRegexp(connection: SQLiteConnection) { - log.debug { "Adding custom REGEXP function" } - Function.create( - connection, - "REGEXP", - object : Function() { - override fun xFunc() { - val regexp = (value_text(0) ?: "").toRegex(RegexOption.IGNORE_CASE) - val text = value_text(1) ?: "" - - result(if (regexp.containsMatchIn(text)) 1 else 0) - } - }, - ) - } - - private fun createUdfStripAccents(connection: SQLiteConnection) { - log.debug { "Adding custom $UDF_STRIP_ACCENTS function" } - Function.create( - connection, - UDF_STRIP_ACCENTS, - object : Function() { - override fun xFunc() = - when (val text = value_text(0)) { - null -> error("Argument must not be null") - else -> result(text.stripAccents()) - } - }, - ) - } - - private fun createUnicode3Collation(connection: SQLiteConnection) { - log.debug { "Adding custom $COLLATION_UNICODE_3 collation" } - Collation.create( - connection, - COLLATION_UNICODE_3, - object : Collation() { - val collator = - Collator.getInstance().apply { - strength = Collator.TERTIARY - decomposition = Collator.CANONICAL_DECOMPOSITION - } - - override fun xCompare( - str1: String, - str2: String, - ): Int = collator.compare(str1, str2) - }, - ) - } + override fun getConnection( + username: String?, + password: String?, + ): SQLiteConnection = super.getConnection(username, password).also { + udfProvider.initializeConnection(it) + } } diff --git a/komga/src/main/kotlin/org/gotson/komga/infrastructure/datasource/SqliteUdfProvider.kt b/komga/src/main/kotlin/org/gotson/komga/infrastructure/datasource/SqliteUdfProvider.kt new file mode 100644 index 00000000..58a903de --- /dev/null +++ b/komga/src/main/kotlin/org/gotson/komga/infrastructure/datasource/SqliteUdfProvider.kt @@ -0,0 +1,82 @@ +package org.gotson.komga.infrastructure.datasource + +import com.ibm.icu.text.Collator +import io.github.oshai.kotlinlogging.KotlinLogging +import org.gotson.komga.language.stripAccents +import org.jooq.Field +import org.jooq.impl.DSL +import org.sqlite.Collation +import org.sqlite.Function +import org.sqlite.SQLiteConnection +import java.sql.Connection + +private val log = KotlinLogging.logger {} + +class SqliteUdfProvider : DatabaseUdfProvider { + override val udfStripAccentsName = "UDF_STRIP_ACCENTS" + override val collationUnicode3Name = "COLLATION_UNICODE_3" + + override fun Field.udfStripAccents(): Field = + DSL.function(udfStripAccentsName, String::class.java, this) + + override fun Field.collateUnicode3(): Field = + this.collate(collationUnicode3Name) + + override fun initializeConnection(connection: Any) { + val sqliteConnection = connection as SQLiteConnection + createUdfRegexp(sqliteConnection) + createUdfStripAccents(sqliteConnection) + createUnicode3Collation(sqliteConnection) + } + + private fun createUdfRegexp(connection: SQLiteConnection) { + log.debug { "Adding custom REGEXP function" } + Function.create( + connection, + "REGEXP", + object : Function() { + override fun xFunc() { + val regexp = (value_text(0) ?: "").toRegex(RegexOption.IGNORE_CASE) + val text = value_text(1) ?: "" + + result(if (regexp.containsMatchIn(text)) 1 else 0) + } + }, + ) + } + + private fun createUdfStripAccents(connection: SQLiteConnection) { + log.debug { "Adding custom $udfStripAccentsName function" } + Function.create( + connection, + udfStripAccentsName, + object : Function() { + override fun xFunc() = + when (val text = value_text(0)) { + null -> error("Argument must not be null") + else -> result(text.stripAccents()) + } + }, + ) + } + + private fun createUnicode3Collation(connection: SQLiteConnection) { + log.debug { "Adding custom $collationUnicode3Name collation" } + Collation.create( + connection, + collationUnicode3Name, + object : Collation() { + val collator = + Collator.getInstance().apply { + strength = Collator.TERTIARY + decomposition = Collator.CANONICAL_DECOMPOSITION + } + + override fun xCompare( + str1: String, + str2: String, + ): Int = collator.compare(str1, str2) + }, + ) + } +} \ No newline at end of file diff --git a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/JooqUdfHelper.kt b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/JooqUdfHelper.kt new file mode 100644 index 00000000..5f0797b5 --- /dev/null +++ b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/JooqUdfHelper.kt @@ -0,0 +1,14 @@ +package org.gotson.komga.infrastructure.jooq + +import org.gotson.komga.infrastructure.datasource.DatabaseUdfProvider +import org.jooq.Field +import org.springframework.stereotype.Component + +@Component +class JooqUdfHelper( + private val databaseUdfProvider: DatabaseUdfProvider +) { + fun Field.udfStripAccents(): Field = databaseUdfProvider.run { this@udfStripAccents.udfStripAccents() } + + fun Field.collateUnicode3(): Field = databaseUdfProvider.run { this@collateUnicode3.collateUnicode3() } +} \ No newline at end of file diff --git a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/KomgaJooqConfiguration.kt b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/KomgaJooqConfiguration.kt index c8b7c3b7..8a602558 100644 --- a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/KomgaJooqConfiguration.kt +++ b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/KomgaJooqConfiguration.kt @@ -1,5 +1,7 @@ package org.gotson.komga.infrastructure.jooq +import org.gotson.komga.infrastructure.configuration.KomgaProperties +import org.gotson.komga.infrastructure.datasource.DatabaseType import org.jooq.DSLContext import org.jooq.ExecuteListenerProvider import org.jooq.SQLDialect @@ -18,43 +20,49 @@ import javax.sql.DataSource // taken from https://github.com/spring-projects/spring-boot/blob/v3.1.4/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jooq/JooqAutoConfiguration.java // as advised in https://docs.spring.io/spring-boot/docs/3.1.4/reference/htmlsingle/#howto.data-access.configure-jooq-with-multiple-datasources @Configuration -class KomgaJooqConfiguration { +class KomgaJooqConfiguration( + private val komgaProperties: KomgaProperties +) { @Bean("dslContextRW") @Primary fun mainDslContextRW( dataSource: DataSource, transactionProvider: ObjectProvider, executeListenerProviders: ObjectProvider, - ): DSLContext = createDslContext(dataSource, transactionProvider, executeListenerProviders) + ): DSLContext = createDslContext(dataSource, transactionProvider, executeListenerProviders, komgaProperties.database.type) @Bean("dslContextRO") fun mainDslContextRO( - @Qualifier("sqliteDataSourceRO") dataSource: DataSource, + @Qualifier("mainDataSourceRO") dataSource: DataSource, transactionProvider: ObjectProvider, executeListenerProviders: ObjectProvider, - ): DSLContext = createDslContext(dataSource, transactionProvider, executeListenerProviders) + ): DSLContext = createDslContext(dataSource, transactionProvider, executeListenerProviders, komgaProperties.database.type) @Bean("tasksDslContextRW") fun tasksDslContextRW( @Qualifier("tasksDataSourceRW") dataSource: DataSource, transactionProvider: ObjectProvider, executeListenerProviders: ObjectProvider, - ): DSLContext = createDslContext(dataSource, transactionProvider, executeListenerProviders) + ): DSLContext = createDslContext(dataSource, transactionProvider, executeListenerProviders, komgaProperties.tasksDb.type) @Bean("tasksDslContextRO") fun tasksDslContextRO( @Qualifier("tasksDataSourceRO") dataSource: DataSource, transactionProvider: ObjectProvider, executeListenerProviders: ObjectProvider, - ): DSLContext = createDslContext(dataSource, transactionProvider, executeListenerProviders) + ): DSLContext = createDslContext(dataSource, transactionProvider, executeListenerProviders, komgaProperties.tasksDb.type) private fun createDslContext( dataSource: DataSource, transactionProvider: ObjectProvider, executeListenerProviders: ObjectProvider, + databaseType: DatabaseType, ) = DefaultDSLContext( DefaultConfiguration().also { configuration -> - configuration.set(SQLDialect.SQLITE) + configuration.set(when (databaseType) { + DatabaseType.SQLITE -> SQLDialect.SQLITE + DatabaseType.POSTGRESQL -> SQLDialect.POSTGRES + }) configuration.set(DataSourceConnectionProvider(TransactionAwareDataSourceProxy(dataSource))) transactionProvider.ifAvailable { newTransactionProvider: TransactionProvider? -> configuration.set(newTransactionProvider) } configuration.set(*executeListenerProviders.orderedStream().toList().toTypedArray()) diff --git a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/Utils.kt b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/Utils.kt index bde23385..043cad89 100644 --- a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/Utils.kt +++ b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/Utils.kt @@ -4,7 +4,7 @@ import com.fasterxml.jackson.databind.ObjectMapper import org.gotson.komga.domain.model.AllowExclude import org.gotson.komga.domain.model.ContentRestrictions import org.gotson.komga.domain.model.MediaExtension -import org.gotson.komga.infrastructure.datasource.SqliteUdfDataSource +import org.gotson.komga.infrastructure.datasource.DatabaseUdfProvider import org.gotson.komga.jooq.main.Tables import org.jooq.Condition import org.jooq.Field @@ -44,7 +44,8 @@ fun Field.inOrNoCondition(list: Collection?): Condition = else -> this.`in`(list) } -fun Field.udfStripAccents() = DSL.function(SqliteUdfDataSource.UDF_STRIP_ACCENTS, String::class.java, this) +// Moved to JooqUdfHelper.kt +// fun Field.udfStripAccents() = DSL.function(SqliteUdfDataSource.UDF_STRIP_ACCENTS, String::class.java, this) fun ContentRestrictions.toCondition(): Condition { val ageAllowed = diff --git a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/main/BookDtoDao.kt b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/main/BookDtoDao.kt index 7231899d..270eb1b5 100644 --- a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/main/BookDtoDao.kt +++ b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/main/BookDtoDao.kt @@ -4,8 +4,8 @@ import org.gotson.komga.domain.model.BookSearch import org.gotson.komga.domain.model.ContentRestrictions import org.gotson.komga.domain.model.ReadList import org.gotson.komga.domain.model.SearchContext -import org.gotson.komga.infrastructure.datasource.SqliteUdfDataSource import org.gotson.komga.infrastructure.jooq.BookSearchHelper +import org.gotson.komga.infrastructure.jooq.JooqUdfHelper import org.gotson.komga.infrastructure.jooq.RequiredJoin import org.gotson.komga.infrastructure.jooq.SplitDslDaoBase import org.gotson.komga.infrastructure.jooq.TempTable @@ -55,6 +55,7 @@ class BookDtoDao( dslRW: DSLContext, @Qualifier("dslContextRO") dslRO: DSLContext, private val luceneHelper: LuceneHelper, + private val jooqUdfHelper: JooqUdfHelper, @param:Value("#{@komgaProperties.database.batchChunkSize}") private val batchSize: Int, private val bookCommonDao: BookCommonDao, ) : SplitDslDaoBase(dslRW, dslRO), @@ -73,8 +74,8 @@ class BookDtoDao( private val sorts = mapOf( - "name" to b.NAME.collate(SqliteUdfDataSource.COLLATION_UNICODE_3), - "series" to sd.TITLE_SORT.collate(SqliteUdfDataSource.COLLATION_UNICODE_3), + "name" to jooqUdfHelper.run { b.NAME.collateUnicode3() }, + "series" to jooqUdfHelper.run { sd.TITLE_SORT.collateUnicode3() }, "created" to b.CREATED_DATE, "createdDate" to b.CREATED_DATE, "lastModified" to b.LAST_MODIFIED_DATE, @@ -87,7 +88,7 @@ class BookDtoDao( "media.comment" to m.COMMENT.noCase(), "media.mediaType" to m.MEDIA_TYPE.noCase(), "media.pagesCount" to m.PAGE_COUNT, - "metadata.title" to d.TITLE.collate(SqliteUdfDataSource.COLLATION_UNICODE_3), + "metadata.title" to jooqUdfHelper.run { d.TITLE.collateUnicode3() }, "metadata.numberSort" to d.NUMBER_SORT, "metadata.releaseDate" to d.RELEASE_DATE, "readProgress.lastModified" to r.LAST_MODIFIED_DATE, diff --git a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/main/ReferentialDao.kt b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/main/ReferentialDao.kt index 35ad984b..ee25e1ed 100644 --- a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/main/ReferentialDao.kt +++ b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/main/ReferentialDao.kt @@ -2,9 +2,8 @@ package org.gotson.komga.infrastructure.jooq.main import org.gotson.komga.domain.model.Author import org.gotson.komga.domain.persistence.ReferentialRepository -import org.gotson.komga.infrastructure.datasource.SqliteUdfDataSource +import org.gotson.komga.infrastructure.jooq.JooqUdfHelper import org.gotson.komga.infrastructure.jooq.SplitDslDaoBase -import org.gotson.komga.infrastructure.jooq.udfStripAccents import org.gotson.komga.jooq.main.Tables import org.gotson.komga.jooq.main.tables.records.BookMetadataAggregationAuthorRecord import org.gotson.komga.jooq.main.tables.records.BookMetadataAuthorRecord @@ -25,6 +24,7 @@ import java.time.LocalDate class ReferentialDao( dslRW: DSLContext, @Qualifier("dslContextRO") dslRO: DSLContext, + private val jooqUdfHelper: JooqUdfHelper, ) : SplitDslDaoBase(dslRW, dslRO), ReferentialRepository { private val a = Tables.BOOK_METADATA_AUTHOR diff --git a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/main/SeriesDtoDao.kt b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/main/SeriesDtoDao.kt index 7157afaa..5de559bb 100644 --- a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/main/SeriesDtoDao.kt +++ b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/main/SeriesDtoDao.kt @@ -4,6 +4,7 @@ import org.gotson.komga.domain.model.SearchContext import org.gotson.komga.domain.model.SearchField import org.gotson.komga.domain.model.SeriesSearch import org.gotson.komga.infrastructure.datasource.SqliteUdfDataSource +import org.gotson.komga.infrastructure.jooq.JooqUdfHelper import org.gotson.komga.infrastructure.jooq.RequiredJoin import org.gotson.komga.infrastructure.jooq.SeriesSearchHelper import org.gotson.komga.infrastructure.jooq.SplitDslDaoBase diff --git a/komga/src/main/resources/application.yml b/komga/src/main/resources/application.yml index 091ddbd6..dafa67ee 100644 --- a/komga/src/main/resources/application.yml +++ b/komga/src/main/resources/application.yml @@ -51,9 +51,9 @@ spring: accept-case-insensitive-values: true config: import: - - "optional:file:\${komga.config-dir}/application.yml" - - "optional:file:\${komga.config-dir}/application.yaml" - - "optional:file:\${komga.config-dir}/application.properties" + - "optional:file:${komga.config-dir}/application.yml" + - "optional:file:${komga.config-dir}/application.yaml" + - "optional:file:${komga.config-dir}/application.properties" http: codecs: max-in-memory-size: 10MB diff --git a/komga/src/test/kotlin/org/gotson/komga/infrastructure/datasource/DataSourcesConfigurationTest.kt b/komga/src/test/kotlin/org/gotson/komga/infrastructure/datasource/DataSourcesConfigurationTest.kt index ee3132f6..e7c5fcc6 100644 --- a/komga/src/test/kotlin/org/gotson/komga/infrastructure/datasource/DataSourcesConfigurationTest.kt +++ b/komga/src/test/kotlin/org/gotson/komga/infrastructure/datasource/DataSourcesConfigurationTest.kt @@ -14,7 +14,7 @@ class DataSourcesConfigurationTest { @Nested inner class WalMode( @Autowired private val dataSourceRW: DataSource, - @Autowired @Qualifier("sqliteDataSourceRO") private val dataSourceRO: DataSource, + @Autowired @Qualifier("mainDataSourceRO") private val dataSourceRO: DataSource, @Autowired @Qualifier("tasksDataSourceRW") private val tasksDataSourceRW: DataSource, @Autowired @Qualifier("tasksDataSourceRO") private val tasksDataSourceRO: DataSource, ) { @@ -30,7 +30,7 @@ class DataSourcesConfigurationTest { @Nested inner class MemoryMode( @Autowired private val dataSourceRW: DataSource, - @Autowired @Qualifier("sqliteDataSourceRO") private val dataSourceRO: DataSource, + @Autowired @Qualifier("mainDataSourceRO") private val dataSourceRO: DataSource, @Autowired @Qualifier("tasksDataSourceRW") private val tasksDataSourceRW: DataSource, @Autowired @Qualifier("tasksDataSourceRO") private val tasksDataSourceRO: DataSource, ) {