Skip to content

Commit

Permalink
fix: EXPOSED-42 Can't create BLOB column with default value (#1740)
Browse files Browse the repository at this point in the history
* chore: Fix typo in DDLTests.testBlob

* fix: EXPOSED-42 Can't create BLOB column with default value

In `nonNullValueToString` for `BlobColumnType`, the `ExposedBlob` is now converted into its hexadecimal representation as a `String`

* chore: add hexToDb function in DataTypeProvider

* chore: reverse condition to have early return
  • Loading branch information
joc-a committed May 23, 2023
1 parent 7851f6c commit f030818
Show file tree
Hide file tree
Showing 10 changed files with 57 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,7 @@ import org.jetbrains.exposed.dao.id.IdTable
import org.jetbrains.exposed.sql.statements.DefaultValueMarker
import org.jetbrains.exposed.sql.statements.api.ExposedBlob
import org.jetbrains.exposed.sql.statements.api.PreparedStatementApi
import org.jetbrains.exposed.sql.vendors.H2Dialect
import org.jetbrains.exposed.sql.vendors.MariaDBDialect
import org.jetbrains.exposed.sql.vendors.OracleDialect
import org.jetbrains.exposed.sql.vendors.SQLServerDialect
import org.jetbrains.exposed.sql.vendors.currentDialect
import org.jetbrains.exposed.sql.vendors.h2Mode
import org.jetbrains.exposed.sql.vendors.*
import java.io.InputStream
import java.math.BigDecimal
import java.math.MathContext
Expand Down Expand Up @@ -743,7 +738,12 @@ class BlobColumnType : ColumnType() {
}
}

override fun nonNullValueToString(value: Any): String = "?"
override fun nonNullValueToString(value: Any): String {
if (value !is ExposedBlob)
error("Unexpected value of type Blob: $value of ${value::class.qualifiedName}")

return currentDialect.dataTypeProvider.hexToDb(value.hexString())
}

override fun readObject(rs: ResultSet, index: Int) = when {
currentDialect is SQLServerDialect -> rs.getBytes(index)?.let(::ExposedBlob)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,9 @@ class ExposedBlob(inputStream: InputStream) {
}

override fun hashCode(): Int = bytes.contentHashCode()

fun hexString(): String = bytes.toHexString()

/** Returns the hex-encoded string of a ByteArray. */
private fun ByteArray.toHexString(): String = joinToString(separator = "") { eachByte -> "%02x".format(eachByte) }
}
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,9 @@ abstract class DataTypeProvider {
open fun precessOrderByClause(queryBuilder: QueryBuilder, expression: Expression<*>, sortOrder: SortOrder) {
queryBuilder.append((expression as? ExpressionAlias<*>)?.alias ?: expression, " ", sortOrder.code)
}

/** Returns the hex-encoded value to be inserted into the database. */
abstract fun hexToDb(hexString: String): String
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ internal object H2DataTypeProvider : DataTypeProvider() {

override fun uuidType(): String = "UUID"
override fun dateTimeType(): String = "DATETIME(9)"
override fun hexToDb(hexString: String): String = "X'$hexString'"
}

internal object H2FunctionProvider : FunctionProvider() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ internal object MysqlDataTypeProvider : DataTypeProvider() {
}
}
}

override fun hexToDb(hexString: String): String = "0x$hexString"
}

internal open class MysqlFunctionProvider : FunctionProvider() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ internal object OracleDataTypeProvider : DataTypeProvider() {
e is LiteralOp<*> && e.columnType is IDateColumnType -> "TIMESTAMP ${super.processForDefaultValue(e)}"
else -> super.processForDefaultValue(e)
}

override fun hexToDb(hexString: String): String = "HEXTORAW('$hexString')"
}

internal object OracleFunctionProvider : FunctionProvider() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ internal object PostgreSQLDataTypeProvider : DataTypeProvider() {
override fun uuidToDB(value: UUID): Any = value
override fun dateTimeType(): String = "TIMESTAMP"
override fun ubyteType(): String = "SMALLINT"
override fun hexToDb(hexString: String): String = """E'\\x$hexString'"""
}

internal object PostgreSQLFunctionProvider : FunctionProvider() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ internal object SQLServerDataTypeProvider : DataTypeProvider() {
}
}
}

override fun hexToDb(hexString: String): String = "0x$hexString"
}

internal object SQLServerFunctionProvider : FunctionProvider() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ internal object SQLiteDataTypeProvider : DataTypeProvider() {
override fun dateTimeType(): String = "TEXT"
override fun dateType(): String = "TEXT"
override fun booleanToStatementString(bool: Boolean) = if (bool) "1" else "0"
override fun hexToDb(hexString: String): String = "X'$hexString'"
}

internal object SQLiteFunctionProvider : FunctionProvider() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,7 @@ class DDLTests : DatabaseTestsBase() {
withTables(t) {
val shortBytes = "Hello there!".toByteArray()
val longBytes = Random.nextBytes(1024)
val shotBlob = ExposedBlob(shortBytes)
val shortBlob = ExposedBlob(shortBytes)
val longBlob = ExposedBlob(longBytes)
// if (currentDialectTest.dataTypeProvider.blobAsStream) {
// SerialBlob(bytes)
Expand All @@ -415,7 +415,7 @@ class DDLTests : DatabaseTestsBase() {
// }

val id1 = t.insert {
it[t.b] = shotBlob
it[t.b] = shortBlob
} get (t.id)

val id2 = t.insert {
Expand All @@ -438,6 +438,37 @@ class DDLTests : DatabaseTestsBase() {
}
}

@Test
fun testBlobDefault() {
val defaultBlobStr = "test"
val defaultBlob = ExposedBlob(defaultBlobStr.encodeToByteArray())

val TestTable = object : Table("TestTable") {
val number = integer("number")
val blobWithDefault = blob("blobWithDefault").default(defaultBlob)
}

withDb { testDb ->
when (testDb) {
TestDB.MYSQL -> {
expectException<ExposedSQLException> {
SchemaUtils.create(TestTable)
}
}
else -> {
SchemaUtils.create(TestTable)

TestTable.insert {
it[number] = 1
}
assertEquals(defaultBlobStr, String(TestTable.selectAll().first()[TestTable.blobWithDefault].bytes))

SchemaUtils.drop(TestTable)
}
}
}
}

@Test
fun testBinaryWithoutLength() {
val tableWithBinary = object : Table("TableWithBinary") {
Expand Down

0 comments on commit f030818

Please sign in to comment.