Skip to content

Commit

Permalink
Initial impl of MysqlQuirks: #8
Browse files Browse the repository at this point in the history
  • Loading branch information
mvysny committed Jan 18, 2019
1 parent d073124 commit 3c9917e
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 2 deletions.
46 changes: 46 additions & 0 deletions src/main/kotlin/com/github/vokorm/DB.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,15 @@ import org.sql2o.Connection
import org.sql2o.converters.Converter
import org.sql2o.converters.ConverterException
import org.sql2o.converters.ConvertersProvider
import org.sql2o.quirks.NoQuirks
import org.sql2o.quirks.Quirks
import org.sql2o.quirks.QuirksProvider
import java.io.Closeable
import java.sql.Timestamp
import java.time.Instant
import java.time.LocalDate
import java.time.ZoneId
import java.util.*
import javax.sql.DataSource
import javax.validation.NoProviderFoundException
import javax.validation.Validation
Expand Down Expand Up @@ -142,3 +146,45 @@ class VokConvertersProvider : ConvertersProvider {
}
}
}

/**
* Converts [UUID] to [ByteArray] to be able to store UUID into binary(16).
* See https://github.com/mvysny/vok-orm/issues/8 for more details.
*/
private class MysqlUuidConverter : Converter<UUID> {
override fun toDatabaseParam(`val`: UUID?): Any? = when(`val`) {
null -> null
else -> `val`.toByteArray()
}

override fun convert(`val`: Any?): UUID? = when(`val`) {
null -> null
is ByteArray -> uuidFromByteArray(`val`)
is UUID -> `val`
else -> throw IllegalArgumentException("Failed to convert $`val` to UUID")
}
}

/**
* Works around MySQL drivers not able to convert [UUID] to [ByteArray].
* See https://github.com/mvysny/vok-orm/issues/8 for more details.
*/
class MysqlQuirks : NoQuirks(mapOf(UUID::class.java to MysqlUuidConverter()))

/**
* Provides specialized quirks for MySQL. See [MysqlQuirks] for more details.
*/
class VokOrmQuirksProvider : QuirksProvider {
override fun forURL(jdbcUrl: String): Quirks? = when {
jdbcUrl.startsWith("jdbc:mysql:") || jdbcUrl.startsWith("jdbc:mariadb:") -> MysqlQuirks()
else -> null
}

override fun forObject(jdbcObject: Any): Quirks? {
val className = jdbcObject.javaClass.canonicalName
return when {
className.startsWith("com.mysql.") || className.startsWith("org.mariadb.jdbc.") -> MysqlQuirks()
else -> null
}
}
}
25 changes: 23 additions & 2 deletions src/main/kotlin/com/github/vokorm/Utils.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package com.github.vokorm

import org.slf4j.LoggerFactory
import java.io.Closeable
import java.io.*
import java.util.*
import javax.validation.ConstraintViolation
import javax.validation.ValidationException
import javax.validation.Validator
Expand Down Expand Up @@ -39,4 +40,24 @@ object NoopValidator : Validator {
override fun <T : Any?> unwrap(type: Class<T>?): T = throw ValidationException("unsupported $type")

override fun forExecutables(): ExecutableValidator = throw UnsupportedOperationException("unimplemented")
}
}

/**
* Converts UUID to a byte array with the length of 16. Writes [UUID.getMostSignificantBits] first, then
* [UUID.getLeastSignificantBits].
*/
fun UUID.toByteArray(): ByteArray = ByteArrayOutputStream().apply {
val dout = DataOutputStream(this)
dout.writeLong(mostSignificantBits)
dout.writeLong(leastSignificantBits)
}.toByteArray()

/**
* Reads UUID from [bytes].
* @param bytes 16-byte-long byte array; first is the [UUID.getMostSignificantBits], then [UUID.getLeastSignificantBits].
*/
fun uuidFromByteArray(bytes: ByteArray): UUID {
check(bytes.size == 16) { "Expected 16-bytes array but got ${bytes.size}" }
val din = DataInputStream(ByteArrayInputStream(bytes))
return UUID(din.readLong(), din.readLong())
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
com.github.vokorm.VokOrmQuirksProvider
12 changes: 12 additions & 0 deletions src/test/kotlin/com/github/vokorm/UtilsTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.github.vokorm

import com.github.mvysny.dynatest.DynaTest
import java.util.*
import kotlin.test.expect

class UtilsTest : DynaTest({
test("UUID-to-ByteArray") {
val uuid = UUID.randomUUID()
expect(uuid) { uuidFromByteArray(uuid.toByteArray()) }
}
})

0 comments on commit 3c9917e

Please sign in to comment.