Skip to content

Commit

Permalink
fix: EXPOSED-263 Null arg parameter in exec() throws if logger enabled (
Browse files Browse the repository at this point in the history
#1973)

The type for exec() parameterized arguments is declared as args: Iterable<Pair<IColumnType, Any?>>,
so passing null as the second element correctly sets the statement parameter to SQL NULL.
But if a logger is enabled, this exception is thrown:
java.lang.IllegalStateException: NULL in non-nullable column.
This is because all IColumnType default to being not nullable and fail the check
in `ColumnType.valueToString()` when attempting to parse the parameter value.

This exception can be avoided by always providing SQL NULL directly, via Op.nullOp<T>().
But given the type hints, the accepted parameter values should match what would
be accepted by any other DSL statement.

Even if the column in the Exposed table object is declared as being `nullable()`,
the column type in `exec()` has no knowledge of this because it is meant to accept
plain SQL using an anonymous Statement object.
This fix sets the nullable parameter of any column type in args to true so that
no exception will be thrown when the logger invokes valueToString().
  • Loading branch information
bog-walk committed Jan 23, 2024
1 parent 1ddde14 commit 0a37dda
Show file tree
Hide file tree
Showing 3 changed files with 22 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,11 @@ open class Transaction(

override fun prepareSQL(transaction: Transaction, prepared: Boolean): String = stmt

override fun arguments(): Iterable<Iterable<Pair<IColumnType, Any?>>> = listOf(args)
override fun arguments(): Iterable<Iterable<Pair<IColumnType, Any?>>> = listOf(
args.map { (columnType, value) ->
columnType.apply { nullable = true } to value
}
)
})
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@ import org.jetbrains.exposed.sql.transactions.transaction
import org.junit.Assume
import org.junit.Test
import kotlin.test.assertNotNull
import kotlin.test.assertNull

class ParameterizationTests : DatabaseTestsBase() {
object TempTable : Table("tmp") {
val name = varchar("foo", 50)
val name = varchar("foo", 50).nullable()
}

private val supportMultipleStatements by lazy {
Expand Down Expand Up @@ -146,4 +147,19 @@ class ParameterizationTests : DatabaseTestsBase() {

TransactionManager.closeAndUnregister(db)
}

@Test
fun testNullParameterWithLogger() {
withTables(TempTable) {
// the logger is left in to test that it does not throw IllegalStateException with null parameter arg
addLogger(StdOutSqlLogger)

exec(
stmt = "INSERT INTO ${TempTable.tableName} (${TempTable.name.name}) VALUES (?)",
args = listOf(VarCharColumnType() to null)
)

assertNull(TempTable.selectAll().single()[TempTable.name])
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,6 @@ class SequencesTests : DatabaseTestsBase() {
fun `test insert LongIdTable with auth-increment with sequence`() {
withDb {
if (currentDialectTest.supportsSequenceAsGeneratedKeys) {
addLogger(StdOutSqlLogger)
try {
SchemaUtils.create(DeveloperWithAutoIncrementBySequence)
val developerId = DeveloperWithAutoIncrementBySequence.insertAndGetId {
Expand Down

0 comments on commit 0a37dda

Please sign in to comment.