Skip to content

Commit

Permalink
fix! EXPOSED-458 Stop sending default and null values in insert state…
Browse files Browse the repository at this point in the history
…ments
  • Loading branch information
obabichevjb committed Nov 4, 2024
1 parent 9f3396c commit 4ed6c2a
Show file tree
Hide file tree
Showing 17 changed files with 280 additions and 145 deletions.
7 changes: 3 additions & 4 deletions exposed-core/api/exposed-core.api
Original file line number Diff line number Diff line change
Expand Up @@ -3091,7 +3091,6 @@ public abstract class org/jetbrains/exposed/sql/statements/BaseBatchInsertStatem
public fun prepared (Lorg/jetbrains/exposed/sql/Transaction;Ljava/lang/String;)Lorg/jetbrains/exposed/sql/statements/api/PreparedStatementApi;
public fun set (Lorg/jetbrains/exposed/sql/Column;Ljava/lang/Object;)V
public fun setArguments (Ljava/util/List;)V
protected fun valuesAndDefaults (Ljava/util/Map;)Ljava/util/Map;
}

public final class org/jetbrains/exposed/sql/statements/BatchDataInconsistentException : java/lang/Exception {
Expand Down Expand Up @@ -3134,7 +3133,6 @@ public class org/jetbrains/exposed/sql/statements/BatchUpsertStatement : org/jet
public final fun getOnUpdateExclude ()Ljava/util/List;
public final fun getWhere ()Lorg/jetbrains/exposed/sql/Op;
public fun insertValue (Lorg/jetbrains/exposed/sql/Column;)Lorg/jetbrains/exposed/sql/ExpressionWithColumnType;
protected fun isColumnValuePreferredFromResultSet (Lorg/jetbrains/exposed/sql/Column;Ljava/lang/Object;)Z
public fun prepareSQL (Lorg/jetbrains/exposed/sql/Transaction;Z)Ljava/lang/String;
public fun prepared (Lorg/jetbrains/exposed/sql/Transaction;Ljava/lang/String;)Lorg/jetbrains/exposed/sql/statements/api/PreparedStatementApi;
public fun storeUpdateValues (Lkotlin/jvm/functions/Function2;)V
Expand Down Expand Up @@ -3197,6 +3195,7 @@ public class org/jetbrains/exposed/sql/statements/InsertStatement : org/jetbrain
public synthetic fun <init> (Lorg/jetbrains/exposed/sql/Table;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V
public synthetic fun arguments ()Ljava/lang/Iterable;
public fun arguments ()Ljava/util/List;
protected final fun clientDefaultColumns ()Ljava/util/List;
protected fun execInsertFunction (Lorg/jetbrains/exposed/sql/statements/api/PreparedStatementApi;)Lkotlin/Pair;
public fun executeInternal (Lorg/jetbrains/exposed/sql/statements/api/PreparedStatementApi;Lorg/jetbrains/exposed/sql/Transaction;)Ljava/lang/Integer;
public synthetic fun executeInternal (Lorg/jetbrains/exposed/sql/statements/api/PreparedStatementApi;Lorg/jetbrains/exposed/sql/Transaction;)Ljava/lang/Object;
Expand All @@ -3208,14 +3207,15 @@ public class org/jetbrains/exposed/sql/statements/InsertStatement : org/jetbrain
public final fun getOrNull (Lorg/jetbrains/exposed/sql/Column;)Ljava/lang/Object;
public final fun getResultedValues ()Ljava/util/List;
public final fun getTable ()Lorg/jetbrains/exposed/sql/Table;
protected fun isColumnValuePreferredFromResultSet (Lorg/jetbrains/exposed/sql/Column;Ljava/lang/Object;)Z
protected final fun isEntityIdClientSideGeneratedUUID (Lorg/jetbrains/exposed/sql/Column;)Z
public final fun isIgnore ()Z
public fun prepareSQL (Lorg/jetbrains/exposed/sql/Transaction;Z)Ljava/lang/String;
public fun prepared (Lorg/jetbrains/exposed/sql/Transaction;Ljava/lang/String;)Lorg/jetbrains/exposed/sql/statements/api/PreparedStatementApi;
public fun setArguments (Ljava/util/List;)V
public final fun setInsertedCount (I)V
protected final fun toSqlString (Ljava/util/List;Z)Ljava/lang/String;
protected final fun valuesAndClientDefaults (Ljava/util/Map;)Ljava/util/Map;
public static synthetic fun valuesAndClientDefaults$default (Lorg/jetbrains/exposed/sql/statements/InsertStatement;Ljava/util/Map;ILjava/lang/Object;)Ljava/util/Map;
protected fun valuesAndDefaults (Ljava/util/Map;)Ljava/util/Map;
public static synthetic fun valuesAndDefaults$default (Lorg/jetbrains/exposed/sql/statements/InsertStatement;Ljava/util/Map;ILjava/lang/Object;)Ljava/util/Map;
}
Expand Down Expand Up @@ -3482,7 +3482,6 @@ public class org/jetbrains/exposed/sql/statements/UpsertStatement : org/jetbrain
public final fun getOnUpdateExclude ()Ljava/util/List;
public final fun getWhere ()Lorg/jetbrains/exposed/sql/Op;
public fun insertValue (Lorg/jetbrains/exposed/sql/Column;)Lorg/jetbrains/exposed/sql/ExpressionWithColumnType;
protected fun isColumnValuePreferredFromResultSet (Lorg/jetbrains/exposed/sql/Column;Ljava/lang/Object;)Z
public fun prepareSQL (Lorg/jetbrains/exposed/sql/Transaction;Z)Ljava/lang/String;
public fun prepared (Lorg/jetbrains/exposed/sql/Transaction;Ljava/lang/String;)Lorg/jetbrains/exposed/sql/statements/api/PreparedStatementApi;
public fun storeUpdateValues (Lkotlin/jvm/functions/Function2;)V
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,8 @@ class Column<T>(
val expressionSQL = currentDialect.dataTypeProvider.processForDefaultValue(defaultValue)
if (!currentDialect.isAllowedAsColumnDefault(defaultValue)) {
val clientDefault = when {
defaultValueFun != null -> " Expression will be evaluated on the client."
// Check for client default value
defaultValueFun != null && dbDefaultValue == null -> " Expression will be evaluated on the client."
!columnType.nullable -> " Column will be created with NULL marker."
else -> ""
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,10 @@ open class ColumnWithTransform<Unwrapped, Wrapped>(
}
}

internal fun unwrapColumnValues(values: Map<Column<*>, Any?>): Map<Column<*>, Any?> = values.mapValues { (col, value) ->
value?.let { (col.columnType as? ColumnWithTransform<Any, Any>)?.unwrapRecursive(it) } ?: value
}

/**
* A class that handles the transformation between a source column type and a target type,
* but also supports transformations involving `null` values.
Expand Down Expand Up @@ -1014,7 +1018,11 @@ class BlobColumnType(
else -> error("Unexpected value of type Blob: $value of ${value::class.qualifiedName}")
}

override fun nonNullValueToString(value: ExposedBlob): String = currentDialect.dataTypeProvider.hexToDb(value.hexString())
override fun nonNullValueToString(value: ExposedBlob): String {
// For H2 Blobs the original dataTypeProvider must be taken (even if H2 in other DB mode)
return ((currentDialect as? H2Dialect)?.originalDataTypeProvider ?: 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
@@ -1,11 +1,6 @@
package org.jetbrains.exposed.sql.statements

import org.jetbrains.exposed.sql.Column
import org.jetbrains.exposed.sql.EntityIDColumnType
import org.jetbrains.exposed.sql.ResultRow
import org.jetbrains.exposed.sql.Table
import org.jetbrains.exposed.sql.Transaction
import org.jetbrains.exposed.sql.isAutoInc
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.statements.api.PreparedStatementApi
import org.jetbrains.exposed.sql.transactions.TransactionManager

Expand Down Expand Up @@ -88,18 +83,26 @@ abstract class BaseBatchInsertStatement(

override var arguments: List<List<Pair<Column<*>, Any?>>>? = null
get() = field ?: run {
val nullableColumns by lazy {
allColumnsInDataSet().filter { it.columnType.nullable && !it.isDatabaseGenerated }
}
data.map { single ->
val valuesAndDefaults = super.valuesAndDefaults(single) as MutableMap
val nullableMap = (nullableColumns - valuesAndDefaults.keys).associateWith { null }
valuesAndDefaults.putAll(nullableMap)
valuesAndDefaults.toList().sortedBy { it.first }
}.apply { field = this }
}
val columnsToInsert = (allColumnsInDataSet() + clientDefaultColumns()).toSet()

override fun valuesAndDefaults(values: Map<Column<*>, Any?>) = arguments!!.first().toMap()
data
.map { valuesAndClientDefaults(it) as MutableMap }
.map { values ->
columnsToInsert.map { column ->
column to when {
values.contains(column) -> values[column]
column.dbDefaultValue != null || column.isDatabaseGenerated -> DefaultValueMarker
else -> {
require(column.columnType.nullable) {
"The value for the column ${column.name} was not provided. " +
"The value for non-nullable column without defaults must be specified."
}
null
}
}
}
}.apply { field = this }
}

override fun prepared(transaction: Transaction, sql: String): PreparedStatementApi {
return if (!shouldReturnGeneratedValues) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package org.jetbrains.exposed.sql.statements
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.statements.api.PreparedStatementApi
import org.jetbrains.exposed.sql.vendors.MysqlFunctionProvider
import org.jetbrains.exposed.sql.vendors.OracleDialect
import org.jetbrains.exposed.sql.vendors.currentDialect

/**
Expand Down Expand Up @@ -54,8 +55,9 @@ open class BatchUpsertStatement(
val keyColumns = if (functionProvider is MysqlFunctionProvider) keys.toList() else getKeyColumns(keys = keys)
val insertValues = arguments!!.first()
val insertValuesSql = insertValues.toSqlString(prepared)
val updateExcludeColumns = (onUpdateExclude ?: emptyList()) + if (dialect is OracleDialect) keyColumns else emptyList()
val updateExpressions = updateValues.takeIf { it.isNotEmpty() }?.toList()
?: getUpdateExpressions(insertValues.unzip().first, onUpdateExclude, keyColumns)
?: getUpdateExpressions(insertValues.unzip().first, updateExcludeColumns, keyColumns)
return functionProvider.upsert(table, insertValues, insertValuesSql, updateExpressions, keyColumns, where, transaction)
}

Expand All @@ -75,8 +77,5 @@ open class BatchUpsertStatement(
return super.prepared(transaction, sql)
}

override fun isColumnValuePreferredFromResultSet(column: Column<*>, value: Any?): Boolean {
return isEntityIdClientSideGeneratedUUID(column) ||
super.isColumnValuePreferredFromResultSet(column, value)
}
// override fun clientDefaultColumns() = targets.flatMap { it.columns.filter { column -> column.defaultValueFun != null } }
}
Loading

0 comments on commit 4ed6c2a

Please sign in to comment.