mirror of
https://github.com/Suwayomi/Tachidesk.git
synced 2025-12-10 06:42:07 +01:00
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:
48
.github/workflows/build_pull_request.yml
vendored
48
.github/workflows/build_pull_request.yml
vendored
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -52,6 +52,7 @@ dependencies {
|
||||
|
||||
// Exposed ORM
|
||||
implementation(libs.bundles.exposed)
|
||||
implementation(libs.postgres)
|
||||
implementation(libs.h2)
|
||||
|
||||
// Exposed Migrations
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
package suwayomi.tachidesk.graphql.types
|
||||
|
||||
enum class DatabaseType {
|
||||
H2,
|
||||
POSTGRESQL,
|
||||
}
|
||||
@@ -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 **/
|
||||
|
||||
@@ -4,6 +4,7 @@ enum class SettingGroup(
|
||||
val value: String,
|
||||
) {
|
||||
NETWORK("Network"),
|
||||
DATABASE("Database"),
|
||||
PROXY("Proxy"),
|
||||
WEB_UI("WebUI"),
|
||||
DOWNLOADER("Downloader"),
|
||||
|
||||
@@ -72,7 +72,7 @@ class JavalinGraphQLRequestParser : GraphQLRequestParser<Context> {
|
||||
)
|
||||
}
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
} catch (_: IOException) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
)
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
)
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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""""
|
||||
}
|
||||
Reference in New Issue
Block a user