Skip to content

Commit

Permalink
fix: EXPOSED-123 ExposedBlob.getBytes() fails on Oracle with IOExcept…
Browse files Browse the repository at this point in the history
…ion Mark invalid (#1824)

The following tests fail when run using Oracle:

DDLTests/testBlob()
DDLTests/testBlobDefault()
EntityTests/testBlobField()

They all fail with the same error:
Caused by: java.io.IOException: Mark invalid or stream not marked.
	at oracle.jdbc.driver.OracleBlobInputStream.reset(OracleBlobInputStream.java:308)
	at org.jetbrains.exposed.sql.statements.api.ExposedBlob.getBytes(ExposedBlob.kt:13)

The exception is thrown after a BLOB value is retrieved and read from the DB because
the type retrieved is oracle.jdbc.driver.OracleBlobInputStream, which either does
not set markedByte to not be default by calling mark() or invalidates mark. This
makes reset() throw.

This is unlike an ExposedBlob instantiated using the secondary constructor that
creates a ByteArrayInputStream from a ByteArray, which overrides reset() to not throw.

This exception is handled inside getBytes() to ensure that the retrieved BLOB
value is still read as a stream.
  • Loading branch information
bog-walk authored Aug 7, 2023
1 parent f732f99 commit 7211d42
Show file tree
Hide file tree
Showing 3 changed files with 26 additions and 19 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package org.jetbrains.exposed.sql.statements.api

import org.jetbrains.exposed.sql.vendors.OracleDialect
import org.jetbrains.exposed.sql.vendors.currentDialectIfAvailable
import java.io.IOException
import java.io.InputStream

class ExposedBlob(inputStream: InputStream) {
Expand All @@ -8,13 +11,20 @@ class ExposedBlob(inputStream: InputStream) {
var inputStream = inputStream
private set

val bytes get() = inputStream.readBytes().also {
if (inputStream.markSupported()) {
inputStream.reset()
} else {
inputStream = it.inputStream()
val bytes: ByteArray
get() = inputStream.readBytes().also {
if (inputStream.markSupported()) {
try {
inputStream.reset()
} catch (_: IOException) {
if (currentDialectIfAvailable is OracleDialect) {
inputStream = it.inputStream()
}
}
} else {
inputStream = it.inputStream()
}
}
}

override fun equals(other: Any?): Boolean {
if (this === other) return true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -442,7 +442,8 @@ class DDLTests : DatabaseTestsBase() {
}
}

@Test fun testBlob() {
@Test
fun testBlob() {
val t = object : Table("t1") {
val id = integer("id").autoIncrement()
val b = blob("blob")
Expand All @@ -455,11 +456,6 @@ class DDLTests : DatabaseTestsBase() {
val longBytes = Random.nextBytes(1024)
val shortBlob = ExposedBlob(shortBytes)
val longBlob = ExposedBlob(longBytes)
// if (currentDialectTest.dataTypeProvider.blobAsStream) {
// SerialBlob(bytes)
// } else connection.createBlob().apply {
// setBytes(1, bytes)
// }

val id1 = t.insert {
it[t.b] = shortBlob
Expand Down Expand Up @@ -497,7 +493,7 @@ class DDLTests : DatabaseTestsBase() {
val defaultBlobStr = "test"
val defaultBlob = ExposedBlob(defaultBlobStr.encodeToByteArray())

val TestTable = object : Table("TestTable") {
val testTable = object : Table("TestTable") {
val number = integer("number")
val blobWithDefault = blob("blobWithDefault").default(defaultBlob)
}
Expand All @@ -506,18 +502,18 @@ class DDLTests : DatabaseTestsBase() {
when (testDb) {
TestDB.MYSQL -> {
expectException<ExposedSQLException> {
SchemaUtils.create(TestTable)
SchemaUtils.create(testTable)
}
}
else -> {
SchemaUtils.create(TestTable)
SchemaUtils.create(testTable)

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

SchemaUtils.drop(TestTable)
SchemaUtils.drop(testTable)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,8 @@ class EntityTests : DatabaseTestsBase() {
}
}

@Test fun testBlobField() {
@Test
fun testBlobField() {
withTables(EntityTestsData.YTable) {
val y1 = EntityTestsData.YEntity.new {
x = false
Expand Down

0 comments on commit 7211d42

Please sign in to comment.