diff --git a/surf-cloud-api/surf-cloud-api-server/src/main/kotlin/dev/slne/surf/cloud/api/server/exposed/columns/CharUuidColumnType.kt b/surf-cloud-api/surf-cloud-api-server/src/main/kotlin/dev/slne/surf/cloud/api/server/exposed/columns/CharUuidColumnType.kt index 74167be2..73ce2c3b 100644 --- a/surf-cloud-api/surf-cloud-api-server/src/main/kotlin/dev/slne/surf/cloud/api/server/exposed/columns/CharUuidColumnType.kt +++ b/surf-cloud-api/surf-cloud-api-server/src/main/kotlin/dev/slne/surf/cloud/api/server/exposed/columns/CharUuidColumnType.kt @@ -11,11 +11,12 @@ class CharUuidColumnType : ColumnType() { @Language("SQL") override fun sqlType(): String = "CHAR(36)" - override fun valueFromDB(value: Any): UUID? = when { - value is UUID -> value - value is ByteArray -> ByteBuffer.wrap(value).let { UUID(it.long, it.long) } - value is String && value.matches(uuidRegexp) -> UUID.fromString(value) - value is String -> ByteBuffer.wrap(value.toByteArray()).let { UUID(it.long, it.long) } + override fun valueFromDB(value: Any): UUID? = when (value) { + is UUID -> value + is ByteArray -> ByteBuffer.wrap(value).let { UUID(it.long, it.long) } + is String if value.matches(uuidRegexp) -> UUID.fromString(value) + is String -> ByteBuffer.wrap(value.toByteArray()).let { UUID(it.long, it.long) } + is ByteBuffer -> UUID(value.long, value.long) else -> error("Unexpected value of type UUID: $value of ${value::class.qualifiedName}") } @@ -23,7 +24,7 @@ class CharUuidColumnType : ColumnType() { override fun nonNullValueToString(value: UUID): String = "'$value'" companion object { - private val uuidRegexp = + internal val uuidRegexp = Regex( "[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}", RegexOption.IGNORE_CASE diff --git a/surf-cloud-api/surf-cloud-api-server/src/main/kotlin/dev/slne/surf/cloud/api/server/exposed/columns/NativeUuidColumnType.kt b/surf-cloud-api/surf-cloud-api-server/src/main/kotlin/dev/slne/surf/cloud/api/server/exposed/columns/NativeUuidColumnType.kt new file mode 100644 index 00000000..38df1192 --- /dev/null +++ b/surf-cloud-api/surf-cloud-api-server/src/main/kotlin/dev/slne/surf/cloud/api/server/exposed/columns/NativeUuidColumnType.kt @@ -0,0 +1,25 @@ +package dev.slne.surf.cloud.api.server.exposed.columns + +import org.intellij.lang.annotations.Language +import org.jetbrains.exposed.sql.Column +import org.jetbrains.exposed.sql.ColumnType +import org.jetbrains.exposed.sql.Table +import java.nio.ByteBuffer +import java.util.* + +class NativeUuidColumnType : ColumnType() { + @Language("SQL") + override fun sqlType() = "UUID" + + override fun valueFromDB(value: Any): UUID? = when (value) { + is UUID -> value + is String if value.matches(CharUuidColumnType.uuidRegexp) -> UUID.fromString(value) + is String -> ByteBuffer.wrap(value.toByteArray()).let { UUID(it.long, it.long) } + is ByteArray -> ByteBuffer.wrap(value).let { UUID(it.long, it.long) } + is ByteBuffer -> UUID(value.long, value.long) + else -> error("Unexpected value of type UUID: $value of ${value::class.qualifiedName}") + } +} + +fun Table.nativeUuid(name: String): Column = + registerColumn(name, NativeUuidColumnType()) \ No newline at end of file diff --git a/surf-cloud-standalone/src/main/kotlin/dev/slne/surf/cloud/standalone/GenerateExposedMigrationScript.kt b/surf-cloud-standalone/src/main/kotlin/dev/slne/surf/cloud/standalone/GenerateExposedMigrationScript.kt index ba1c0a5d..8a802195 100644 --- a/surf-cloud-standalone/src/main/kotlin/dev/slne/surf/cloud/standalone/GenerateExposedMigrationScript.kt +++ b/surf-cloud-standalone/src/main/kotlin/dev/slne/surf/cloud/standalone/GenerateExposedMigrationScript.kt @@ -2,15 +2,7 @@ package dev.slne.surf.cloud.standalone import MigrationUtils import dev.slne.surf.cloud.api.common.config.properties.requiredSystemProperty -import dev.slne.surf.cloud.standalone.player.db.exposed.punishment.table.BanPunishmentIpAddressTable -import dev.slne.surf.cloud.standalone.player.db.exposed.punishment.table.BanPunishmentNoteTable -import dev.slne.surf.cloud.standalone.player.db.exposed.punishment.table.BanPunishmentTable -import dev.slne.surf.cloud.standalone.player.db.exposed.punishment.table.KickPunishmentNoteTable -import dev.slne.surf.cloud.standalone.player.db.exposed.punishment.table.KickPunishmentTable -import dev.slne.surf.cloud.standalone.player.db.exposed.punishment.table.MutePunishmentNoteTable -import dev.slne.surf.cloud.standalone.player.db.exposed.punishment.table.MutePunishmentTable -import dev.slne.surf.cloud.standalone.player.db.exposed.punishment.table.WarnPunishmentNoteTable -import dev.slne.surf.cloud.standalone.player.db.exposed.punishment.table.WarnPunishmentTable +import dev.slne.surf.cloud.standalone.player.db.exposed.punishment.table.* import org.jetbrains.exposed.sql.Database import org.jetbrains.exposed.sql.ExperimentalDatabaseMigrationApi import org.jetbrains.exposed.sql.transactions.transaction @@ -35,7 +27,7 @@ fun main() { MutePunishmentNoteTable, WarnPunishmentNoteTable, scriptDirectory = "src/main/resources/db/migration", - scriptName = "V4__add_punishment_table_indexes", + scriptName = "V5__replace_string_uuid_with_native_uuid", ) } } \ No newline at end of file diff --git a/surf-cloud-standalone/src/main/kotlin/dev/slne/surf/cloud/standalone/player/db/exposed/CloudPlayerTables.kt b/surf-cloud-standalone/src/main/kotlin/dev/slne/surf/cloud/standalone/player/db/exposed/CloudPlayerTables.kt index 9df01c42..f25cb411 100644 --- a/surf-cloud-standalone/src/main/kotlin/dev/slne/surf/cloud/standalone/player/db/exposed/CloudPlayerTables.kt +++ b/surf-cloud-standalone/src/main/kotlin/dev/slne/surf/cloud/standalone/player/db/exposed/CloudPlayerTables.kt @@ -1,13 +1,13 @@ package dev.slne.surf.cloud.standalone.player.db.exposed -import dev.slne.surf.cloud.api.server.exposed.columns.charUuid import dev.slne.surf.cloud.api.server.exposed.columns.inet +import dev.slne.surf.cloud.api.server.exposed.columns.nativeUuid import dev.slne.surf.cloud.api.server.exposed.columns.zonedDateTime import dev.slne.surf.cloud.api.server.exposed.table.AuditableLongIdTable import java.net.Inet4Address object CloudPlayerTable : AuditableLongIdTable("cloud_player") { - val uuid = charUuid("uuid").uniqueIndex() + val uuid = nativeUuid("uuid").uniqueIndex() val lastServer = char("last_server", 255).nullable() val lastSeen = zonedDateTime("last_seen").nullable() val lastIpAddress = inet("last_ip_address") diff --git a/surf-cloud-standalone/src/main/kotlin/dev/slne/surf/cloud/standalone/player/db/exposed/punishment/table/AbstractPunishmentTable.kt b/surf-cloud-standalone/src/main/kotlin/dev/slne/surf/cloud/standalone/player/db/exposed/punishment/table/AbstractPunishmentTable.kt index e67fcb44..5590eff4 100644 --- a/surf-cloud-standalone/src/main/kotlin/dev/slne/surf/cloud/standalone/player/db/exposed/punishment/table/AbstractPunishmentTable.kt +++ b/surf-cloud-standalone/src/main/kotlin/dev/slne/surf/cloud/standalone/player/db/exposed/punishment/table/AbstractPunishmentTable.kt @@ -1,12 +1,12 @@ package dev.slne.surf.cloud.standalone.player.db.exposed.punishment.table -import dev.slne.surf.cloud.api.server.exposed.columns.charUuid +import dev.slne.surf.cloud.api.server.exposed.columns.nativeUuid import dev.slne.surf.cloud.api.server.exposed.table.AuditableLongIdTable -abstract class AbstractPunishmentTable(name: String): AuditableLongIdTable(name) { +abstract class AbstractPunishmentTable(name: String) : AuditableLongIdTable(name) { val punishmentId = char("punishment_id", 8).uniqueIndex() - val punishedUuid = charUuid("punished_uuid") - val issuerUuid = charUuid("issuer_uuid").nullable() + val punishedUuid = nativeUuid("punished_uuid") + val issuerUuid = nativeUuid("issuer_uuid").nullable() val reason = largeText("reason").nullable() init { diff --git a/surf-cloud-standalone/src/main/kotlin/dev/slne/surf/cloud/standalone/player/db/exposed/punishment/table/AbstractUnpunishableExpirablePunishmentTable.kt b/surf-cloud-standalone/src/main/kotlin/dev/slne/surf/cloud/standalone/player/db/exposed/punishment/table/AbstractUnpunishableExpirablePunishmentTable.kt index f31ba635..f3120f33 100644 --- a/surf-cloud-standalone/src/main/kotlin/dev/slne/surf/cloud/standalone/player/db/exposed/punishment/table/AbstractUnpunishableExpirablePunishmentTable.kt +++ b/surf-cloud-standalone/src/main/kotlin/dev/slne/surf/cloud/standalone/player/db/exposed/punishment/table/AbstractUnpunishableExpirablePunishmentTable.kt @@ -1,12 +1,13 @@ package dev.slne.surf.cloud.standalone.player.db.exposed.punishment.table -import dev.slne.surf.cloud.api.server.exposed.columns.charUuid +import dev.slne.surf.cloud.api.server.exposed.columns.nativeUuid import dev.slne.surf.cloud.api.server.exposed.columns.zonedDateTime -abstract class AbstractUnpunishableExpirablePunishmentTable(name: String) : AbstractPunishmentTable(name) { +abstract class AbstractUnpunishableExpirablePunishmentTable(name: String) : + AbstractPunishmentTable(name) { val unpunished = bool("unpunished").default(false) val unpunishedDate = zonedDateTime("unpunished_date").nullable().default(null) - val unpunisherUuid = charUuid("unpunisher_uuid").nullable().default(null) + val unpunisherUuid = nativeUuid("unpunisher_uuid").nullable().default(null) val expirationDate = zonedDateTime("expiration_date").nullable().default(null) val permanent = bool("permanent").default(false) diff --git a/surf-cloud-standalone/src/main/kotlin/dev/slne/surf/cloud/standalone/player/db/exposed/punishment/table/PunishmentNoteTables.kt b/surf-cloud-standalone/src/main/kotlin/dev/slne/surf/cloud/standalone/player/db/exposed/punishment/table/PunishmentNoteTables.kt index b41e845a..abb4db9a 100644 --- a/surf-cloud-standalone/src/main/kotlin/dev/slne/surf/cloud/standalone/player/db/exposed/punishment/table/PunishmentNoteTables.kt +++ b/surf-cloud-standalone/src/main/kotlin/dev/slne/surf/cloud/standalone/player/db/exposed/punishment/table/PunishmentNoteTables.kt @@ -1,6 +1,6 @@ package dev.slne.surf.cloud.standalone.player.db.exposed.punishment.table -import dev.slne.surf.cloud.api.server.exposed.columns.charUuid +import dev.slne.surf.cloud.api.server.exposed.columns.nativeUuid import dev.slne.surf.cloud.api.server.exposed.table.AuditableLongIdTable import org.jetbrains.exposed.sql.ReferenceOption import java.util.* @@ -9,7 +9,7 @@ abstract class AbstractPunishmentNoteTable( name: String, foreignPunishmentTable: AbstractPunishmentTable ) : AuditableLongIdTable(name) { - val noteId = charUuid("note_id").uniqueIndex().clientDefault { UUID.randomUUID() } + val noteId = nativeUuid("note_id").uniqueIndex().clientDefault { UUID.randomUUID() } val punishment = reference("punishment_id", foreignPunishmentTable, onDelete = ReferenceOption.CASCADE) val note = largeText("note") diff --git a/surf-cloud-standalone/src/main/resources/db/migration/V5__replace_string_uuid_with_native_uuid.sql b/surf-cloud-standalone/src/main/resources/db/migration/V5__replace_string_uuid_with_native_uuid.sql new file mode 100644 index 00000000..83159d8d --- /dev/null +++ b/surf-cloud-standalone/src/main/resources/db/migration/V5__replace_string_uuid_with_native_uuid.sql @@ -0,0 +1,45 @@ +-- V5__replace_string_uuid_with_native_uuid.sql +-- Migration to replace CHAR(36) UUID columns with native UUID types +-- Assumes data is UUID-valid and castable + +-- Convert `cloud_player`.`uuid` +ALTER TABLE cloud_player + MODIFY COLUMN uuid UUID NOT NULL; + +-- Convert `punish_bans` UUID columns +ALTER TABLE punish_bans + MODIFY COLUMN punished_uuid UUID NOT NULL, + MODIFY COLUMN issuer_uuid UUID NULL, + MODIFY COLUMN unpunisher_uuid UUID NULL; + +-- Convert `punish_kicks` UUID columns +ALTER TABLE punish_kicks + MODIFY COLUMN punished_uuid UUID NOT NULL, + MODIFY COLUMN issuer_uuid UUID NULL; + +-- Convert `punish_mutes` UUID columns +ALTER TABLE punish_mutes + MODIFY COLUMN punished_uuid UUID NOT NULL, + MODIFY COLUMN issuer_uuid UUID NULL, + MODIFY COLUMN unpunisher_uuid UUID NULL; + +-- Convert `punish_notes_ban`.`note_id` +ALTER TABLE punish_notes_ban + MODIFY COLUMN note_id UUID NOT NULL; + +-- Convert `punish_notes_kick`.`note_id` +ALTER TABLE punish_notes_kick + MODIFY COLUMN note_id UUID NOT NULL; + +-- Convert `punish_notes_mute`.`note_id` +ALTER TABLE punish_notes_mute + MODIFY COLUMN note_id UUID NOT NULL; + +-- Convert `punish_notes_warn`.`note_id` +ALTER TABLE punish_notes_warn + MODIFY COLUMN note_id UUID NOT NULL; + +-- Convert `punish_warnings` UUID columns +ALTER TABLE punish_warnings + MODIFY COLUMN punished_uuid UUID NOT NULL, + MODIFY COLUMN issuer_uuid UUID NULL;