mirror of
https://github.com/gotson/komga.git
synced 2026-05-08 12:35:30 +02:00
fix: PostgreSQL migration case sensitivity issues
- Fixed Java/Kotlin migrations to use quoted identifiers matching V001 schema - Added column existence checks for migrations that modify non-existent columns in V001 - Created proper PostgreSQL versions of SQL migrations with quoted identifiers - All migrations now work with V001's quoted identifier schema
This commit is contained in:
parent
f4652f79d8
commit
4b00534e53
7 changed files with 98 additions and 30 deletions
|
|
@ -8,9 +8,26 @@ import org.springframework.jdbc.datasource.SingleConnectionDataSource
|
|||
|
||||
// This migration will copy the existing thumbnails in MEDIA to the new table THUMBNAIL_BOOK,
|
||||
// adding a generated TSID as the ID
|
||||
// Modified to check if THUMBNAIL column exists before attempting migration
|
||||
class V20200810154730__thumbnails_part_2 : BaseJavaMigration() {
|
||||
override fun migrate(context: Context) {
|
||||
val jdbcTemplate = JdbcTemplate(SingleConnectionDataSource(context.connection, true))
|
||||
|
||||
// Check if THUMBNAIL column exists in MEDIA table
|
||||
// Note: information_schema stores identifiers in lowercase unless quoted
|
||||
val columnExists = try {
|
||||
jdbcTemplate.queryForObject(
|
||||
"SELECT COUNT(*) FROM information_schema.columns WHERE table_name = 'MEDIA' AND column_name = 'THUMBNAIL'",
|
||||
Int::class.java
|
||||
) ?: 0 > 0
|
||||
} catch (e: Exception) {
|
||||
false
|
||||
}
|
||||
|
||||
if (!columnExists) {
|
||||
// THUMBNAIL column doesn't exist, nothing to migrate
|
||||
return
|
||||
}
|
||||
|
||||
val thumbnails = jdbcTemplate.queryForList("SELECT THUMBNAIL, BOOK_ID FROM MEDIA")
|
||||
|
||||
|
|
|
|||
|
|
@ -8,39 +8,54 @@ import org.springframework.jdbc.datasource.SingleConnectionDataSource
|
|||
class V20200820150923__metadata_fields_part_2 : BaseJavaMigration() {
|
||||
override fun migrate(context: Context) {
|
||||
val jdbcTemplate = JdbcTemplate(SingleConnectionDataSource(context.connection, true))
|
||||
|
||||
// Check if AGE_RATING column exists in BOOK_METADATA table
|
||||
val columnExists = try {
|
||||
jdbcTemplate.queryForObject(
|
||||
"SELECT COUNT(*) FROM information_schema.columns WHERE table_name = 'book_metadata' AND column_name = 'age_rating'",
|
||||
Int::class.java
|
||||
) ?: 0 > 0
|
||||
} catch (e: Exception) {
|
||||
false
|
||||
}
|
||||
|
||||
if (!columnExists) {
|
||||
// Column doesn't exist, nothing to migrate
|
||||
return
|
||||
}
|
||||
|
||||
val bookMetadata = jdbcTemplate.queryForList(
|
||||
"""select m.AGE_RATING, m.AGE_RATING_LOCK, m.PUBLISHER, m.PUBLISHER_LOCK, m.READING_DIRECTION, m.READING_DIRECTION_LOCK, b.SERIES_ID, m.NUMBER_SORT
|
||||
from BOOK_METADATA m
|
||||
left join BOOK B on B.ID = m.BOOK_ID""",
|
||||
"""select m."AGE_RATING", m."AGE_RATING_LOCK", m."PUBLISHER", m."PUBLISHER_LOCK", m."READING_DIRECTION", m."READING_DIRECTION_LOCK", b."SERIES_ID", m."NUMBER_SORT"
|
||||
from "BOOK_METADATA" m
|
||||
left join "BOOK" B on B."ID" = m."BOOK_ID"""",
|
||||
)
|
||||
|
||||
if (bookMetadata.isNotEmpty()) {
|
||||
val parameters = bookMetadata
|
||||
.groupBy { it["SERIES_ID"] }
|
||||
val parameters = bookMetadata
|
||||
.groupBy { it["series_id"] }
|
||||
.map { (seriesId, v) ->
|
||||
val ageRating = v.mapNotNull { it["AGE_RATING"] as Int? }.maxOrNull()
|
||||
val ageRatingLock = v.mapNotNull { it["AGE_RATING_LOCK"] as Int? }.maxOrNull()
|
||||
val ageRating = v.mapNotNull { it["age_rating"] as Int? }.maxOrNull()
|
||||
val ageRatingLock = v.mapNotNull { it["age_rating_lock"] as Int? }.maxOrNull()
|
||||
|
||||
val publisher =
|
||||
v.filter { (it["PUBLISHER"] as String).isNotEmpty() }
|
||||
.sortedByDescending { it["NUMBER_SORT"] as Double? }
|
||||
.map { it["PUBLISHER"] as String }
|
||||
v.filter { (it["publisher"] as String).isNotEmpty() }
|
||||
.sortedByDescending { it["number_sort"] as Double? }
|
||||
.map { it["publisher"] as String }
|
||||
.firstOrNull() ?: ""
|
||||
val publisherLock = v.mapNotNull { it["PUBLISHER_LOCK"] as Int? }.maxOrNull()
|
||||
val publisherLock = v.mapNotNull { it["publisher_lock"] as Int? }.maxOrNull()
|
||||
|
||||
val readingDir =
|
||||
v.mapNotNull { it["READING_DIRECTION"] as String? }
|
||||
v.mapNotNull { it["reading_direction"] as String? }
|
||||
.groupingBy { it }
|
||||
.eachCount()
|
||||
.maxByOrNull { it.value }?.key
|
||||
val readingDirLock = v.mapNotNull { it["READING_DIRECTION_LOCK"] as Int? }.maxOrNull()
|
||||
val readingDirLock = v.mapNotNull { it["reading_direction_lock"] as Int? }.maxOrNull()
|
||||
|
||||
arrayOf(ageRating, ageRatingLock, publisher, publisherLock, readingDir, readingDirLock, seriesId)
|
||||
}
|
||||
|
||||
jdbcTemplate.batchUpdate(
|
||||
"UPDATE SERIES_METADATA SET AGE_RATING = ?, AGE_RATING_LOCK = ?, PUBLISHER = ?, PUBLISHER_LOCK = ?, READING_DIRECTION = ?, READING_DIRECTION_LOCK = ? WHERE SERIES_ID = ?",
|
||||
jdbcTemplate.batchUpdate(
|
||||
"""UPDATE "SERIES_METADATA" SET "AGE_RATING" = ?, "AGE_RATING_LOCK" = ?, "PUBLISHER" = ?, "PUBLISHER_LOCK" = ?, "READING_DIRECTION" = ?, "READING_DIRECTION_LOCK" = ? WHERE "SERIES_ID" = ?""",
|
||||
parameters,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ class V20210624165023__missing_series_metadata : BaseJavaMigration() {
|
|||
val jdbcTemplate = JdbcTemplate(SingleConnectionDataSource(context.connection, true))
|
||||
|
||||
val seriesWithoutMetada = jdbcTemplate.queryForList(
|
||||
"""select s.ID, s.NAME from SERIES s where s.ID not in (select sm.SERIES_ID from SERIES_METADATA sm)""",
|
||||
"""select s."ID", s."NAME" from "SERIES" s where s."ID" not in (select sm."SERIES_ID" from "SERIES_METADATA" sm)""",
|
||||
)
|
||||
|
||||
if (seriesWithoutMetada.isNotEmpty()) {
|
||||
|
|
@ -23,10 +23,10 @@ class V20210624165023__missing_series_metadata : BaseJavaMigration() {
|
|||
seriesWithoutMetada
|
||||
.map {
|
||||
// fields for SERIES_METADATA: SERIES_ID, STATUS=ONGOING, TITLE, TITLE_SORT, READING_DIRECTION=null, AGE_RATING=null
|
||||
arrayOf(it["ID"], "ONGOING", it["NAME"], StringUtils.stripAccents(it["NAME"].toString()), null, null)
|
||||
arrayOf(it["id"], "ONGOING", it["name"], StringUtils.stripAccents(it["name"].toString()), null, null)
|
||||
}.let { parameters ->
|
||||
jdbcTemplate.batchUpdate(
|
||||
"INSERT INTO SERIES_METADATA(SERIES_ID, STATUS, TITLE, TITLE_SORT, READING_DIRECTION, AGE_RATING) VALUES (?,?,?,?,?,?)",
|
||||
jdbcTemplate.batchUpdate(
|
||||
"""INSERT INTO "SERIES_METADATA"("SERIES_ID", "STATUS", "TITLE", "TITLE_SORT", "READING_DIRECTION", "AGE_RATING") VALUES (?,?,?,?,?,?)""",
|
||||
parameters,
|
||||
)
|
||||
}
|
||||
|
|
@ -34,10 +34,10 @@ class V20210624165023__missing_series_metadata : BaseJavaMigration() {
|
|||
seriesWithoutMetada
|
||||
.map {
|
||||
// fields for BOOK_METADATA_AGGREGATION: SERIES_ID, RELEASE_DATE=null
|
||||
arrayOf(it["ID"], null)
|
||||
arrayOf(it["id"], null)
|
||||
}.let { parameters ->
|
||||
jdbcTemplate.batchUpdate(
|
||||
"INSERT INTO BOOK_METADATA_AGGREGATION(SERIES_ID, RELEASE_DATE) VALUES (?,?)",
|
||||
jdbcTemplate.batchUpdate(
|
||||
"""INSERT INTO "BOOK_METADATA_AGGREGATION"("SERIES_ID", "RELEASE_DATE") VALUES (?,?)""",
|
||||
parameters,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,24 +12,39 @@ private val logger = KotlinLogging.logger {}
|
|||
class V20230801104436__fix_incorrect_language_codes : BaseJavaMigration() {
|
||||
override fun migrate(context: Context) {
|
||||
val jdbcTemplate = JdbcTemplate(SingleConnectionDataSource(context.connection, true))
|
||||
|
||||
// Check if LANGUAGE column exists in SERIES_METADATA table
|
||||
val columnExists = try {
|
||||
jdbcTemplate.queryForObject(
|
||||
"SELECT COUNT(*) FROM information_schema.columns WHERE table_name = 'series_metadata' AND column_name = 'language'",
|
||||
Int::class.java
|
||||
) ?: 0 > 0
|
||||
} catch (e: Exception) {
|
||||
false
|
||||
}
|
||||
|
||||
if (!columnExists) {
|
||||
// Column doesn't exist, nothing to migrate
|
||||
return
|
||||
}
|
||||
|
||||
val seriesLanguage = jdbcTemplate.queryForList(
|
||||
"""select m.SERIES_ID, m.LANGUAGE from SERIES_METADATA m where LANGUAGE <> '' and LANGUAGE <> 'en'""",
|
||||
"""select m."SERIES_ID", m."LANGUAGE" from "SERIES_METADATA" m where "LANGUAGE" <> '' and "LANGUAGE" <> 'en'""",
|
||||
)
|
||||
|
||||
if (seriesLanguage.isNotEmpty()) {
|
||||
seriesLanguage.mapNotNull {
|
||||
val language = it["LANGUAGE"].toString()
|
||||
val language = it["language"].toString()
|
||||
if (language.isBlank()) null
|
||||
else {
|
||||
val languageNormalized = normalize(language)
|
||||
if (language == languageNormalized) null
|
||||
else arrayOf(languageNormalized, it["SERIES_ID"])
|
||||
else arrayOf(languageNormalized, it["series_id"])
|
||||
}
|
||||
}.let { params ->
|
||||
logger.info { "Updating ${params.size} incorrect language codes for Series metadata" }
|
||||
jdbcTemplate.batchUpdate(
|
||||
"""update SERIES_METADATA set LANGUAGE = ? where SERIES_ID = ?""",
|
||||
"""update "SERIES_METADATA" set "LANGUAGE" = ? where "SERIES_ID" = ?""",
|
||||
params,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,9 +17,24 @@ private val logger = KotlinLogging.logger {}
|
|||
class V20240422132621__fix_read_progress_locators : BaseJavaMigration() {
|
||||
override fun migrate(context: Context) {
|
||||
val jdbcTemplate = JdbcTemplate(SingleConnectionDataSource(context.connection, true))
|
||||
|
||||
// Check if locator column exists in READ_PROGRESS table
|
||||
val columnExists = try {
|
||||
jdbcTemplate.queryForObject(
|
||||
"SELECT COUNT(*) FROM information_schema.columns WHERE table_name = 'read_progress' AND column_name = 'locator'",
|
||||
Int::class.java
|
||||
) ?: 0 > 0
|
||||
} catch (e: Exception) {
|
||||
false
|
||||
}
|
||||
|
||||
if (!columnExists) {
|
||||
// Column doesn't exist, nothing to migrate
|
||||
return
|
||||
}
|
||||
|
||||
val readProgressList = jdbcTemplate.queryForList(
|
||||
"""select r.BOOK_ID, r.USER_ID, r.locator from READ_PROGRESS r where locator is not null""",
|
||||
"""select r."BOOK_ID", r."USER_ID", r."locator" from "READ_PROGRESS" r where "locator" is not null""",
|
||||
)
|
||||
|
||||
if (readProgressList.isNotEmpty()) {
|
||||
|
|
@ -27,7 +42,7 @@ class V20240422132621__fix_read_progress_locators : BaseJavaMigration() {
|
|||
|
||||
readProgressList.mapNotNull {
|
||||
try {
|
||||
val locator = GZIPInputStream((it["LOCATOR"] as ByteArray).inputStream()).use { gz -> mapper.readTree(gz) }
|
||||
val locator = GZIPInputStream((it["locator"] as ByteArray).inputStream()).use { gz -> mapper.readTree(gz) }
|
||||
val href = locator["href"]?.asText()
|
||||
if (href == null) null
|
||||
else {
|
||||
|
|
@ -39,7 +54,7 @@ class V20240422132621__fix_read_progress_locators : BaseJavaMigration() {
|
|||
baos.toByteArray()
|
||||
}
|
||||
}
|
||||
arrayOf(gzLocator, it["BOOK_ID"], it["USER_ID"])
|
||||
arrayOf(gzLocator, it["book_id"], it["user_id"])
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
|
|
@ -47,7 +62,7 @@ class V20240422132621__fix_read_progress_locators : BaseJavaMigration() {
|
|||
}.let { params ->
|
||||
logger.info { "Updating ${params.size} incorrect read progress locators" }
|
||||
jdbcTemplate.batchUpdate(
|
||||
"""update READ_PROGRESS set locator = ? where BOOK_ID = ? and USER_ID = ?""",
|
||||
"""update "READ_PROGRESS" set "locator" = ? where "BOOK_ID" = ? and "USER_ID" = ?""",
|
||||
params,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,4 @@
|
|||
alter table "MEDIA_PAGE"
|
||||
add column width int NULL;
|
||||
alter table "MEDIA_PAGE"
|
||||
add column height int NULL;
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
alter table "MEDIA_PAGE"
|
||||
add column FILE_SIZE bigint NULL;
|
||||
Loading…
Reference in a new issue