Support PostgreSQL Databases (#1617)

* Support PostgreSQL Databases

* Set the database Schema

* See if we can test postgres

* Another test

* Disable node container

* Update database when changed

* Simplify test workflow

* Only exit on failed migrations

* Run the first databaseUp sync

* Map the port

* Use absolute path for LD_PRELOAD

* Timeout after 1m

* Open the server in both database configurations

* Only exit on migration failed in ci

* Lint

* Use new ServerConfig configuration
This commit is contained in:
Mitchell Syer
2025-09-02 12:29:09 -04:00
committed by GitHub
parent 4cdc7b348d
commit dc79b4c90a
26 changed files with 313 additions and 47 deletions

View File

@@ -23,6 +23,18 @@ jobs:
name: Build pull request
needs: check_wrapper
runs-on: ubuntu-latest
services:
postgres:
image: postgres
env:
POSTGRES_PASSWORD: postgres
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432
steps:
- name: Checkout pull request
@@ -47,7 +59,39 @@ jobs:
mkdir -p ~/.gradle
cp .github/runner-files/ci-gradle.properties ~/.gradle/gradle.properties
- name: Build Jar
- name: Build And Run Jar
working-directory: master
run: ./gradlew ktlintCheck :server:shadowJar --stacktrace
run: |
./gradlew ktlintCheck :server:shadowJar --stacktrace
gcc -fPIC -I$JAVA_HOME/include -I$JAVA_HOME/include/linux -shared scripts/resources/catch_abort.c -lpthread -o scripts/resources/catch_abort.so
export LD_PRELOAD="$(pwd)/scripts/resources/catch_abort.so"
JAR=$(ls ./server/build/*.jar| head -1)
set +e
timeout 30s java -DcrashOnFailedMigration=true \
-Dsuwayomi.tachidesk.config.server.systemTrayEnabled=false \
-Dsuwayomi.tachidesk.config.server.initialOpenInBrowserEnabled=false \
-Dsuwayomi.tachidesk.config.server.databaseType=POSTGRESQL \
-Dsuwayomi.tachidesk.config.server.databaseUrl=postgresql://localhost:5432/postgres \
-Dsuwayomi.tachidesk.config.server.databaseUsername=postgres \
-Dsuwayomi.tachidesk.config.server.databasePassword=postgres \
-jar "$JAR"
ecode="$?"
# if exit code is not 124 or 125, then error
if ! [ "$ecode" -eq 124 -o "$ecode" -eq 125 ]; then
printf "exit code '%s' - exiting.\n" "$ecode"
exit "$ecode"
fi
timeout 30s java -DcrashOnFailedMigration=true \
-Dsuwayomi.tachidesk.config.server.systemTrayEnabled=false \
-Dsuwayomi.tachidesk.config.server.initialOpenInBrowserEnabled=false \
-jar "$JAR"
ecode="$?"
# if exit code is not 124 or 125, then error
if ! [ "$ecode" -eq 124 -o "$ecode" -eq 125 ]; then
printf "exit code '%s' - exiting.\n" "$ecode"
exit "$ecode"
fi
exit 0

View File

@@ -67,6 +67,7 @@ exposed-core = { module = "org.jetbrains.exposed:exposed-core", version.ref = "e
exposed-dao = { module = "org.jetbrains.exposed:exposed-dao", version.ref = "exposed" }
exposed-jdbc = { module = "org.jetbrains.exposed:exposed-jdbc", version.ref = "exposed" }
exposed-javatime = { module = "org.jetbrains.exposed:exposed-java-time", version.ref = "exposed" }
postgres = "org.postgresql:postgresql:42.7.7"
h2 = "com.h2database:h2:1.4.200" # current database driver, can't update to h2 v2 without sql migration
# Exposed Migrations

View File

@@ -52,6 +52,7 @@ dependencies {
// Exposed ORM
implementation(libs.bundles.exposed)
implementation(libs.postgres)
implementation(libs.h2)
// Exposed Migrations

View File

@@ -0,0 +1,6 @@
package suwayomi.tachidesk.graphql.types
enum class DatabaseType {
H2,
POSTGRESQL,
}

View File

@@ -24,6 +24,7 @@ import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.onEach
import org.jetbrains.exposed.sql.SortOrder
import suwayomi.tachidesk.graphql.types.AuthMode
import suwayomi.tachidesk.graphql.types.DatabaseType
import suwayomi.tachidesk.graphql.types.DownloadConversion
import suwayomi.tachidesk.graphql.types.KoreaderSyncChecksumMethod
import suwayomi.tachidesk.graphql.types.KoreaderSyncStrategy
@@ -636,6 +637,32 @@ class ServerConfig(
requiresRestart = true,
)
val databaseType: MutableStateFlow<DatabaseType> by EnumSetting(
protoNumber = 69,
group = SettingGroup.DATABASE,
defaultValue = DatabaseType.H2,
enumClass = DatabaseType::class,
typeInfo = SettingsRegistry.PartialTypeInfo(imports = listOf("suwayomi.tachidesk.graphql.types.DatabaseType")),
)
val databaseUrl: MutableStateFlow<String> by StringSetting(
protoNumber = 70,
group = SettingGroup.DATABASE,
defaultValue = "postgresql://localhost:5432/suwayomi",
)
val databaseUsername: MutableStateFlow<String> by StringSetting(
protoNumber = 71,
group = SettingGroup.DATABASE,
defaultValue = "",
)
val databasePassword: MutableStateFlow<String> by StringSetting(
protoNumber = 72,
group = SettingGroup.DATABASE,
defaultValue = "",
)
/** ****************************************************************** **/
/** **/
/** Renamed settings **/

View File

@@ -4,6 +4,7 @@ enum class SettingGroup(
val value: String,
) {
NETWORK("Network"),
DATABASE("Database"),
PROXY("Proxy"),
WEB_UI("WebUI"),
DOWNLOADER("Downloader"),

View File

@@ -72,7 +72,7 @@ class JavalinGraphQLRequestParser : GraphQLRequestParser<Context> {
)
}
}
} catch (e: IOException) {
} catch (_: IOException) {
null
}
}

View File

@@ -15,7 +15,7 @@ import suwayomi.tachidesk.manga.model.dataclass.IncludeOrExclude
object CategoryTable : IntIdTable() {
val name = varchar("name", 64)
val order = integer("order").default(0)
val order = integer("sort_order").default(0)
val isDefault = bool("is_default").default(false)
val includeInUpdate = integer("include_in_update").default(IncludeOrExclude.UNSET.value)
val includeInDownload = integer("include_in_download").default(IncludeOrExclude.UNSET.value)

View File

@@ -17,16 +17,17 @@ import suwayomi.tachidesk.manga.model.dataclass.MangaDataClass
import suwayomi.tachidesk.manga.model.dataclass.toGenreList
import suwayomi.tachidesk.manga.model.table.MangaStatus.Companion
import suwayomi.tachidesk.manga.model.table.columns.truncatingVarchar
import suwayomi.tachidesk.manga.model.table.columns.unlimitedVarchar
object MangaTable : IntIdTable() {
val url = varchar("url", 2048)
val title = truncatingVarchar("title", 512)
val initialized = bool("initialized").default(false)
val artist = truncatingVarchar("artist", Integer.MAX_VALUE).nullable()
val author = truncatingVarchar("author", Integer.MAX_VALUE).nullable()
val description = truncatingVarchar("description", Integer.MAX_VALUE).nullable()
val genre = truncatingVarchar("genre", Integer.MAX_VALUE).nullable()
val artist = unlimitedVarchar("artist").nullable()
val author = unlimitedVarchar("author").nullable()
val description = unlimitedVarchar("description").nullable()
val genre = unlimitedVarchar("genre").nullable()
val status = integer("status").default(SManga.UNKNOWN)
val thumbnail_url = varchar("thumbnail_url", 2048).nullable()

View File

@@ -9,11 +9,12 @@ package suwayomi.tachidesk.manga.model.table
import org.jetbrains.exposed.dao.id.IntIdTable
import org.jetbrains.exposed.sql.ReferenceOption
import suwayomi.tachidesk.manga.model.table.columns.unlimitedVarchar
object PageTable : IntIdTable() {
val index = integer("index")
val url = varchar("url", 2048)
val imageUrl = varchar("image_url", Integer.MAX_VALUE).nullable()
val imageUrl = unlimitedVarchar("image_url").nullable()
val chapter = reference("chapter", ChapterTable, ReferenceOption.CASCADE)
}

View File

@@ -4,6 +4,8 @@ import io.github.oshai.kotlinlogging.KotlinLogging
import org.jetbrains.exposed.sql.Column
import org.jetbrains.exposed.sql.Table
import org.jetbrains.exposed.sql.VarCharColumnType
import suwayomi.tachidesk.graphql.types.DatabaseType
import suwayomi.tachidesk.server.serverConfig
class TruncatingVarCharColumn(
private val table: String,
@@ -34,3 +36,12 @@ fun Table.truncatingVarchar(
length: Int,
collate: String? = null,
): Column<String> = registerColumn(name, TruncatingVarCharColumn(this.tableName, name, length, collate))
fun Table.unlimitedVarchar(
name: String,
collate: String? = null,
): Column<String> =
when (serverConfig.databaseType.value) {
DatabaseType.H2 -> truncatingVarchar(name, Int.MAX_VALUE, collate)
DatabaseType.POSTGRESQL -> text(name, collate)
}

View File

@@ -38,6 +38,7 @@ import org.koin.core.module.Module
import org.koin.dsl.module
import suwayomi.tachidesk.global.impl.KcefWebView.Companion.toCefCookie
import suwayomi.tachidesk.graphql.types.AuthMode
import suwayomi.tachidesk.graphql.types.DatabaseType
import suwayomi.tachidesk.i18n.LocalizationHelper
import suwayomi.tachidesk.manga.impl.backup.proto.ProtoBackupExport
import suwayomi.tachidesk.manga.impl.download.DownloadManager
@@ -114,6 +115,13 @@ data class ProxySettings(
val proxyPassword: String,
)
data class DatabaseSettings(
val databaseType: DatabaseType,
val databaseUrl: String,
val databaseUsername: String,
val databasePassword: String,
)
val serverConfig: ServerConfig by lazy { GlobalConfigManager.module() }
val androidCompat by lazy { AndroidCompat() }
@@ -365,6 +373,31 @@ fun applicationSetup() {
LocalSource.register()
serverConfig.subscribeTo(
combine<Any, DatabaseSettings>(
serverConfig.databaseType,
serverConfig.databaseUrl,
serverConfig.databaseUsername,
serverConfig.databasePassword,
) { vargs ->
DatabaseSettings(
vargs[0] as DatabaseType,
vargs[1] as String,
vargs[2] as String,
vargs[3] as String,
)
}.distinctUntilChanged(),
{ (databaseType, databaseUrl, databaseUsername, _) ->
logger.info {
"Database changed - type=$databaseType url=$databaseUrl, username=$databaseUsername, password=[REDACTED]"
}
databaseUp()
LocalSource.register()
},
ignoreInitialValue = true,
)
// create system tray
serverConfig.subscribeTo(
serverConfig.systemTrayEnabled,

View File

@@ -13,35 +13,84 @@ import io.github.oshai.kotlinlogging.KotlinLogging
import org.jetbrains.exposed.sql.Database
import org.jetbrains.exposed.sql.DatabaseConfig
import org.jetbrains.exposed.sql.ExperimentalKeywordApi
import org.jetbrains.exposed.sql.Schema
import org.jetbrains.exposed.sql.SchemaUtils
import org.jetbrains.exposed.sql.transactions.TransactionManager
import org.jetbrains.exposed.sql.transactions.transaction
import suwayomi.tachidesk.graphql.types.DatabaseType
import suwayomi.tachidesk.server.ApplicationDirs
import suwayomi.tachidesk.server.ServerConfig
import suwayomi.tachidesk.server.serverConfig
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.sql.SQLException
import kotlin.system.exitProcess
object DBManager {
val db by lazy {
var db: Database? = null
private set
fun setupDatabase(): Database {
if (TransactionManager.isInitialized()) {
val currentDatabase = TransactionManager.currentOrNull()?.db
if (currentDatabase != null) {
TransactionManager.closeAndUnregister(currentDatabase)
}
}
val applicationDirs = Injekt.get<ApplicationDirs>()
Database.connect(
"jdbc:h2:${applicationDirs.dataRoot}/database",
"org.h2.Driver",
databaseConfig =
DatabaseConfig {
useNestedTransactions = true
@OptIn(ExperimentalKeywordApi::class)
preserveKeywordCasing = false
},
)
val dbConfig =
DatabaseConfig {
useNestedTransactions = true
@OptIn(ExperimentalKeywordApi::class)
preserveKeywordCasing = false
}
return when (serverConfig.databaseType.value) {
DatabaseType.POSTGRESQL ->
Database.connect(
"jdbc:${serverConfig.databaseUrl.value}",
"org.postgresql.Driver",
user = serverConfig.databaseUsername.value,
password = serverConfig.databasePassword.value,
databaseConfig = dbConfig,
)
DatabaseType.H2 ->
Database.connect(
"jdbc:h2:${applicationDirs.dataRoot}/database",
"org.h2.Driver",
databaseConfig = dbConfig,
)
}.also { db = it }
}
}
private val logger = KotlinLogging.logger {}
fun databaseUp(db: Database = DBManager.db) {
fun databaseUp() {
val db = DBManager.setupDatabase()
// call db to initialize the lazy object
logger.info {
"Using ${db.vendor} database version ${db.version}"
}
val migrations = loadMigrationsFrom("suwayomi.tachidesk.server.database.migration", ServerConfig::class.java)
runMigrations(migrations)
try {
if (serverConfig.databaseType.value == DatabaseType.POSTGRESQL) {
transaction {
val schema =
Schema(
"suwayomi",
serverConfig.databaseUsername.value.takeIf { it.isNotBlank() },
)
SchemaUtils.createSchema(schema)
SchemaUtils.setSchema(schema)
}
}
val migrations = loadMigrationsFrom("suwayomi.tachidesk.server.database.migration", ServerConfig::class.java)
runMigrations(migrations)
} catch (e: SQLException) {
logger.error(e) { "Error up-to-database migration" }
if (System.getProperty("crashOnFailedMigration").toBoolean()) {
exitProcess(101)
}
}
}

View File

@@ -1,5 +1,7 @@
package suwayomi.tachidesk.server.database.migration
import suwayomi.tachidesk.server.database.migration.helpers.RenameFieldMigration
/*
* Copyright (C) Contributors to the Suwayomi project
*
@@ -7,8 +9,6 @@ package suwayomi.tachidesk.server.database.migration
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import de.neonew.exposed.migrations.helpers.RenameFieldMigration
@Suppress("ClassName", "unused")
class M0002_ChapterTableIndexRename :
RenameFieldMigration(

View File

@@ -1,5 +1,7 @@
package suwayomi.tachidesk.server.database.migration
import suwayomi.tachidesk.server.database.migration.helpers.RenameFieldMigration
/*
* Copyright (C) Contributors to the Suwayomi project
*
@@ -7,8 +9,6 @@ package suwayomi.tachidesk.server.database.migration
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import de.neonew.exposed.migrations.helpers.RenameFieldMigration
@Suppress("ClassName", "unused")
class M0003_DefaultCategory :
RenameFieldMigration(

View File

@@ -8,14 +8,16 @@ package suwayomi.tachidesk.server.database.migration
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import de.neonew.exposed.migrations.helpers.SQLMigration
import suwayomi.tachidesk.server.database.migration.helpers.MAYBE_TYPE_PREFIX
import suwayomi.tachidesk.server.database.migration.helpers.UNLIMITED_TEXT
@Suppress("ClassName", "unused")
class M0014_MangaRemoveLengthLimit : SQLMigration() {
override val sql =
"""
ALTER TABLE MANGA ALTER COLUMN ARTIST VARCHAR(512);
ALTER TABLE MANGA ALTER COLUMN AUTHOR VARCHAR(512);
ALTER TABLE MANGA ALTER COLUMN DESCRIPTION VARCHAR; -- the default length is `Integer.MAX_VALUE`
ALTER TABLE MANGA ALTER COLUMN GENRE VARCHAR; -- the default length is `Integer.MAX_VALUE`
ALTER TABLE MANGA ALTER COLUMN ARTIST ${MAYBE_TYPE_PREFIX}VARCHAR(512);
ALTER TABLE MANGA ALTER COLUMN AUTHOR ${MAYBE_TYPE_PREFIX}VARCHAR(512);
ALTER TABLE MANGA ALTER COLUMN DESCRIPTION ${MAYBE_TYPE_PREFIX}$UNLIMITED_TEXT; -- the default length is `Integer.MAX_VALUE`
ALTER TABLE MANGA ALTER COLUMN GENRE ${MAYBE_TYPE_PREFIX}$UNLIMITED_TEXT; -- the default length is `Integer.MAX_VALUE`
""".trimIndent()
}

View File

@@ -8,12 +8,13 @@ package suwayomi.tachidesk.server.database.migration
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import de.neonew.exposed.migrations.helpers.SQLMigration
import suwayomi.tachidesk.server.database.migration.helpers.MAYBE_TYPE_PREFIX
@Suppress("ClassName", "unused")
class M0015_SourceAndExtensionLangAddLengthLimit : SQLMigration() {
override val sql =
"""
ALTER TABLE SOURCE ALTER COLUMN LANG VARCHAR(32);
ALTER TABLE EXTENSION ALTER COLUMN LANG VARCHAR(32);
ALTER TABLE SOURCE ALTER COLUMN LANG ${MAYBE_TYPE_PREFIX}VARCHAR(32);
ALTER TABLE EXTENSION ALTER COLUMN LANG ${MAYBE_TYPE_PREFIX}VARCHAR(32);
""".trimIndent()
}

View File

@@ -7,12 +7,12 @@ package suwayomi.tachidesk.server.database.migration
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import de.neonew.exposed.migrations.helpers.SQLMigration
import suwayomi.tachidesk.server.database.migration.helpers.RenameFieldMigration
@Suppress("ClassName", "unused")
class M0016_ChapterIndexRenameToSourceOrder : SQLMigration() {
override val sql =
"""
ALTER TABLE CHAPTER ALTER COLUMN INDEX RENAME TO SOURCE_ORDER;
""".trimIndent()
}
class M0016_ChapterIndexRenameToSourceOrder :
RenameFieldMigration(
"CHAPTER",
"INDEX",
"SOURCE_ORDER",
)

View File

@@ -1,6 +1,7 @@
package suwayomi.tachidesk.server.database.migration
import de.neonew.exposed.migrations.helpers.SQLMigration
import suwayomi.tachidesk.server.database.migration.helpers.INITIAL_ORDER_NAME
/*
* Copyright (C) Contributors to the Suwayomi project
@@ -13,6 +14,6 @@ import de.neonew.exposed.migrations.helpers.SQLMigration
class M0027_AddDefaultCategory : SQLMigration() {
override val sql: String =
"""
INSERT INTO CATEGORY (ID, NAME, IS_DEFAULT, "ORDER", INCLUDE_IN_UPDATE) VALUES (0, 'Default', TRUE, 0, -1)
INSERT INTO CATEGORY (ID, NAME, IS_DEFAULT, $INITIAL_ORDER_NAME, INCLUDE_IN_UPDATE) VALUES (0, 'Default', TRUE, 0, -1)
""".trimIndent()
}

View File

@@ -8,12 +8,14 @@ package suwayomi.tachidesk.server.database.migration
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import de.neonew.exposed.migrations.helpers.SQLMigration
import suwayomi.tachidesk.server.database.migration.helpers.MAYBE_TYPE_PREFIX
import suwayomi.tachidesk.server.database.migration.helpers.UNLIMITED_TEXT
@Suppress("ClassName", "unused")
class M0042_MangaRemoveLengthLimit_II : SQLMigration() {
override val sql =
"""
ALTER TABLE MANGA ALTER COLUMN ARTIST VARCHAR; -- the default length is `Integer.MAX_VALUE`
ALTER TABLE MANGA ALTER COLUMN AUTHOR VARCHAR; -- the default length is `Integer.MAX_VALUE`
ALTER TABLE MANGA ALTER COLUMN ARTIST $MAYBE_TYPE_PREFIX$UNLIMITED_TEXT; -- the default length is `Integer.MAX_VALUE`
ALTER TABLE MANGA ALTER COLUMN AUTHOR $MAYBE_TYPE_PREFIX$UNLIMITED_TEXT; -- the default length is `Integer.MAX_VALUE`
""".trimIndent()
}

View File

@@ -1,5 +1,7 @@
package suwayomi.tachidesk.server.database.migration
import suwayomi.tachidesk.server.database.migration.helpers.RenameFieldMigration
/*
* Copyright (C) Contributors to the Suwayomi project
*
@@ -7,8 +9,6 @@ package suwayomi.tachidesk.server.database.migration
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import de.neonew.exposed.migrations.helpers.RenameFieldMigration
@Suppress("ClassName", "unused")
class M0046_RenamePageImageUrlColumn :
RenameFieldMigration(

View File

@@ -8,11 +8,12 @@ package suwayomi.tachidesk.server.database.migration
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import de.neonew.exposed.migrations.helpers.SQLMigration
import suwayomi.tachidesk.server.database.migration.helpers.MAYBE_TYPE_PREFIX
@Suppress("ClassName", "unused")
class M0047_ChapterScanlatorIncreaseLength : SQLMigration() {
override val sql =
"""
ALTER TABLE CHAPTER ALTER COLUMN SCANLATOR VARCHAR(256);
ALTER TABLE CHAPTER ALTER COLUMN SCANLATOR ${MAYBE_TYPE_PREFIX}VARCHAR(256);
""".trimIndent()
}

View File

@@ -8,6 +8,8 @@ package suwayomi.tachidesk.server.database.migration
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import de.neonew.exposed.migrations.helpers.SQLMigration
import suwayomi.tachidesk.server.database.migration.helpers.MAYBE_TYPE_PREFIX
import suwayomi.tachidesk.server.database.migration.helpers.UNLIMITED_TEXT
@Suppress("ClassName", "unused")
class M0050_FixHandlingOfTooLongPageImageUrls : SQLMigration() {
@@ -23,6 +25,6 @@ class M0050_FixHandlingOfTooLongPageImageUrls : SQLMigration() {
ALTER TABLE PAGE DROP CONSTRAINT IF EXISTS UC_PAGE;
ALTER TABLE PAGE ADD CONSTRAINT UC_PAGE UNIQUE (INDEX, CHAPTER);
ALTER TABLE PAGE ALTER COLUMN IMAGE_URL VARCHAR; -- the default length is `Integer.MAX_VALUE`
ALTER TABLE PAGE ALTER COLUMN IMAGE_URL $MAYBE_TYPE_PREFIX$UNLIMITED_TEXT; -- the default length is `Integer.MAX_VALUE`
""".trimIndent()
}

View File

@@ -0,0 +1,19 @@
package suwayomi.tachidesk.server.database.migration
import suwayomi.tachidesk.server.database.migration.helpers.INITIAL_ORDER_NAME
import suwayomi.tachidesk.server.database.migration.helpers.RenameFieldMigration
/*
* Copyright (C) Contributors to the Suwayomi project
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
@Suppress("ClassName", "unused")
class M0052_RenameCategoryOrderColumn :
RenameFieldMigration(
"Category",
INITIAL_ORDER_NAME,
"sort_order",
)

View File

@@ -0,0 +1,38 @@
package suwayomi.tachidesk.server.database.migration.helpers
import de.neonew.exposed.migrations.helpers.SQLMigration
import org.jetbrains.exposed.sql.transactions.TransactionManager
import suwayomi.tachidesk.graphql.types.DatabaseType
import suwayomi.tachidesk.server.serverConfig
fun String.toSqlName(): String =
TransactionManager.current().db.identifierManager.let {
it.quoteIfNecessary(
it.inProperCase(this),
)
}
abstract class RenameFieldMigration(
tableName: String,
originalName: String,
newName: String,
) : SQLMigration() {
private val fixedTableName by lazy { tableName.toSqlName() }
private val fixedOriginalName by lazy { originalName.toSqlName() }
private val fixedNewName by lazy { newName.toSqlName() }
fun postgresRename(): String =
"ALTER TABLE $fixedTableName " +
"RENAME COLUMN $fixedOriginalName TO $fixedNewName;"
fun h2Rename(): String =
"ALTER TABLE $fixedTableName " +
"ALTER COLUMN $fixedOriginalName RENAME TO $fixedNewName"
override val sql by lazy {
when (serverConfig.databaseType.value) {
DatabaseType.H2 -> h2Rename()
DatabaseType.POSTGRESQL -> postgresRename()
}
}
}

View File

@@ -0,0 +1,25 @@
package suwayomi.tachidesk.server.database.migration.helpers
import suwayomi.tachidesk.graphql.types.DatabaseType
import suwayomi.tachidesk.server.serverConfig
val UNLIMITED_TEXT
get() =
when (serverConfig.databaseType.value) {
DatabaseType.H2 -> "VARCHAR" // the default length is `Integer.MAX_VALUE`
DatabaseType.POSTGRESQL -> "TEXT"
}
val MAYBE_TYPE_PREFIX
get() =
when (serverConfig.databaseType.value) {
DatabaseType.H2 -> ""
DatabaseType.POSTGRESQL -> "TYPE "
}
val INITIAL_ORDER_NAME
get() =
when (serverConfig.databaseType.value) {
DatabaseType.H2 -> """"ORDER""""
DatabaseType.POSTGRESQL -> """"order""""
}