Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 142 additions & 0 deletions exposed-core/api/exposed-core.api

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -301,14 +301,14 @@ interface IStatementBuilder {
vararg keys: Column<*>,
onUpdate: (UpsertBuilder.(UpdateStatement) -> Unit)? = null,
onUpdateExclude: List<Column<*>>? = null,
where: (SqlExpressionBuilder.() -> Op<Boolean>)? = null,
where: (UpsertSqlExpressionBuilder.() -> Op<Boolean>)? = null,
body: T.(UpsertStatement<Long>) -> Unit
): UpsertStatement<Long> {
return UpsertStatement<Long>(
table = this,
keys = keys,
onUpdateExclude = onUpdateExclude,
where = where?.let { SqlExpressionBuilder.it() }
where = where?.let { UpsertSqlExpressionBuilder.it() }
).apply {
onUpdate?.let { storeUpdateValues(it) }
body(this)
Expand Down Expand Up @@ -371,7 +371,7 @@ interface IStatementBuilder {
onUpdateList: List<Pair<Column<*>, Any?>>? = null,
onUpdate: (UpsertBuilder.(UpdateStatement) -> Unit)? = null,
onUpdateExclude: List<Column<*>>? = null,
where: (SqlExpressionBuilder.() -> Op<Boolean>)? = null,
where: (UpsertSqlExpressionBuilder.() -> Op<Boolean>)? = null,
shouldReturnGeneratedValues: Boolean = true,
vararg keys: Column<*>,
body: BatchUpsertStatement.(E) -> Unit
Expand All @@ -380,7 +380,7 @@ interface IStatementBuilder {
table = this,
keys = keys,
onUpdateExclude = onUpdateExclude,
where = where?.let { SqlExpressionBuilder.it() },
where = where?.let { UpsertSqlExpressionBuilder.it() },
shouldReturnGeneratedValues = shouldReturnGeneratedValues
).apply {
onUpdate?.let { storeUpdateValues(it) }
Expand Down Expand Up @@ -425,6 +425,7 @@ interface IStatementBuilder {
): MergeSelectStatement {
return MergeSelectStatement(this, selectQuery, SqlExpressionBuilder.on()).apply(body)
}

private fun Column<*>.isValidIfAutoIncrement(): Boolean =
!columnType.isAutoInc || autoIncColumnType?.nextValExpression != null
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.jetbrains.exposed.v1.core.statements

import org.jetbrains.exposed.v1.core.*
import org.jetbrains.exposed.v1.core.statements.UpsertBuilder.Companion.getFunctionProvider
import org.jetbrains.exposed.v1.core.transactions.CoreTransactionManager
import org.jetbrains.exposed.v1.core.vendors.*

Expand Down Expand Up @@ -89,18 +90,6 @@ sealed interface UpsertBuilder {
*/
fun <T> insertValue(column: Column<T>): ExpressionWithColumnType<T> = InsertValue(column, column.columnType)

@OptIn(InternalApi::class)
private class InsertValue<T>(
val column: Column<T>,
override val columnType: IColumnType<T & Any>
) : ExpressionWithColumnType<T>() {
override fun toQueryBuilder(queryBuilder: QueryBuilder) {
val transaction = CoreTransactionManager.currentTransaction()
val functionProvider = getFunctionProvider(transaction.db.dialect)
functionProvider.insertValue(transaction.identity(column), queryBuilder)
}
}

companion object {
/** Returns the [FunctionProvider] for valid upsert statement syntax. */
fun getFunctionProvider(dialect: DatabaseDialect): FunctionProvider = when (dialect) {
Expand Down Expand Up @@ -153,3 +142,20 @@ internal fun UpsertBuilder.getAdditionalArgs(
where?.toQueryBuilder(this)
}.args
}

internal class InsertValue<T>(
val column: Column<T>,
override val columnType: IColumnType<T & Any>
) : ExpressionWithColumnType<T>() {
@OptIn(InternalApi::class)
override fun toQueryBuilder(queryBuilder: QueryBuilder) {
val transaction = CoreTransactionManager.currentTransaction()
val functionProvider = getFunctionProvider(transaction.db.dialect)
functionProvider.insertValue(transaction.identity(column), queryBuilder)
}
}

/** Builder object for creating SQL expressions in UpsertStatement */
object UpsertSqlExpressionBuilder : ISqlExpressionBuilder by SqlExpressionBuilder {
fun <T> insertValue(column: Column<T>): ExpressionWithColumnType<T> = InsertValue(column, column.columnType)
}
Original file line number Diff line number Diff line change
Expand Up @@ -680,7 +680,7 @@ fun <T : Table> T.upsert(
vararg keys: Column<*>,
onUpdate: (UpsertBuilder.(UpdateStatement) -> Unit)? = null,
onUpdateExclude: List<Column<*>>? = null,
where: (SqlExpressionBuilder.() -> Op<Boolean>)? = null,
where: (UpsertSqlExpressionBuilder.() -> Op<Boolean>)? = null,
body: T.(UpsertStatement<Long>) -> Unit
): UpsertStatement<Long> {
val stmt = buildStatement { upsert(keys = keys, onUpdate, onUpdateExclude, where, body) }
Expand Down Expand Up @@ -739,7 +739,7 @@ fun <T : Table, E : Any> T.batchUpsert(
vararg keys: Column<*>,
onUpdate: (UpsertBuilder.(UpdateStatement) -> Unit)? = null,
onUpdateExclude: List<Column<*>>? = null,
where: (SqlExpressionBuilder.() -> Op<Boolean>)? = null,
where: (UpsertSqlExpressionBuilder.() -> Op<Boolean>)? = null,
shouldReturnGeneratedValues: Boolean = true,
body: BatchUpsertStatement.(E) -> Unit
): List<ResultRow> {
Expand Down Expand Up @@ -768,7 +768,7 @@ fun <T : Table, E : Any> T.batchUpsert(
vararg keys: Column<*>,
onUpdate: (UpsertBuilder.(UpdateStatement) -> Unit)? = null,
onUpdateExclude: List<Column<*>>? = null,
where: (SqlExpressionBuilder.() -> Op<Boolean>)? = null,
where: (UpsertSqlExpressionBuilder.() -> Op<Boolean>)? = null,
shouldReturnGeneratedValues: Boolean = true,
body: BatchUpsertStatement.(E) -> Unit
): List<ResultRow> {
Expand All @@ -781,7 +781,7 @@ private fun <T : Table, E> T.batchUpsert(
onUpdateList: List<Pair<Column<*>, Any?>>? = null,
onUpdate: (UpsertBuilder.(UpdateStatement) -> Unit)? = null,
onUpdateExclude: List<Column<*>>? = null,
where: (SqlExpressionBuilder.() -> Op<Boolean>)? = null,
where: (UpsertSqlExpressionBuilder.() -> Op<Boolean>)? = null,
shouldReturnGeneratedValues: Boolean = true,
vararg keys: Column<*>,
body: BatchUpsertStatement.(E) -> Unit
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import org.junit.Test
import java.lang.Integer.parseInt
import java.util.*
import kotlin.properties.Delegates
import kotlin.test.assertEquals
import kotlin.test.assertNotEquals
import kotlin.test.assertNotNull

Expand Down Expand Up @@ -894,4 +895,60 @@ class UpsertTests : R2dbcDatabaseTestsBase() {
assertEqualLists(uuidList, value[tester.uuidArray])
}
}

@Test
fun testExcludedValueInWhereCondition() {
val tester = object : Table("upsert_where_excluded") {
val id = integer("id")
.uniqueIndex()
val name = text("name")
val order = integer("order")
}

class TesterData(val id: Int, val name: String, val order: Int)

suspend fun testerBatchUpsert(data: List<TesterData>) {
tester.batchUpsert(
data,
tester.id,
onUpdate = {
it[tester.name] = insertValue(tester.name)
it[tester.order] = insertValue(tester.order)
},
where = {
tester.order less insertValue(tester.order)
}
) {
this[tester.id] = it.id
this[tester.name] = it.name
this[tester.order] = it.order
}
}

suspend fun assertTesterName(id: Int, name: String) {
assertEquals(name, tester.selectAll().where { tester.id eq id }.single()[tester.name])
}

withTables(excludeSettings = TestDB.ALL_MYSQL_LIKE + upsertViaMergeDB, tester) {
testerBatchUpsert(
listOf(
TesterData(1, "tester1", 10),
TesterData(2, "tester2", 10),
)
)

assertTesterName(1, "tester1")
assertTesterName(2, "tester2")

testerBatchUpsert(
listOf(
TesterData(1, "tester1-modified", 5),
TesterData(2, "tester2-modified", 20),
)
)

assertTesterName(1, "tester1")
assertTesterName(2, "tester2-modified")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -673,7 +673,7 @@ suspend fun <T : Table> T.upsert(
vararg keys: Column<*>,
onUpdate: (UpsertBuilder.(UpdateStatement) -> Unit)? = null,
onUpdateExclude: List<Column<*>>? = null,
where: (SqlExpressionBuilder.() -> Op<Boolean>)? = null,
where: (UpsertSqlExpressionBuilder.() -> Op<Boolean>)? = null,
body: T.(UpsertStatement<Long>) -> Unit
): UpsertStatement<Long> {
val stmt = buildStatement { upsert(keys = keys, onUpdate, onUpdateExclude, where, body) }
Expand Down Expand Up @@ -732,7 +732,7 @@ suspend fun <T : Table, E : Any> T.batchUpsert(
vararg keys: Column<*>,
onUpdate: (UpsertBuilder.(UpdateStatement) -> Unit)? = null,
onUpdateExclude: List<Column<*>>? = null,
where: (SqlExpressionBuilder.() -> Op<Boolean>)? = null,
where: (UpsertSqlExpressionBuilder.() -> Op<Boolean>)? = null,
shouldReturnGeneratedValues: Boolean = true,
body: BatchUpsertStatement.(E) -> Unit
): List<ResultRow> {
Expand Down Expand Up @@ -761,7 +761,7 @@ suspend fun <T : Table, E : Any> T.batchUpsert(
vararg keys: Column<*>,
onUpdate: (UpsertBuilder.(UpdateStatement) -> Unit)? = null,
onUpdateExclude: List<Column<*>>? = null,
where: (SqlExpressionBuilder.() -> Op<Boolean>)? = null,
where: (UpsertSqlExpressionBuilder.() -> Op<Boolean>)? = null,
shouldReturnGeneratedValues: Boolean = true,
body: BatchUpsertStatement.(E) -> Unit
): List<ResultRow> {
Expand All @@ -774,7 +774,7 @@ private suspend fun <T : Table, E> T.batchUpsert(
onUpdateList: List<Pair<Column<*>, Any?>>? = null,
onUpdate: (UpsertBuilder.(UpdateStatement) -> Unit)? = null,
onUpdateExclude: List<Column<*>>? = null,
where: (SqlExpressionBuilder.() -> Op<Boolean>)? = null,
where: (UpsertSqlExpressionBuilder.() -> Op<Boolean>)? = null,
shouldReturnGeneratedValues: Boolean = true,
vararg keys: Column<*>,
body: BatchUpsertStatement.(E) -> Unit
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,30 @@
package org.jetbrains.exposed.v1.tests.shared.dml

import org.jetbrains.exposed.v1.core.*
import org.jetbrains.exposed.v1.core.SqlExpressionBuilder.concat
import org.jetbrains.exposed.v1.core.SqlExpressionBuilder.less
import org.jetbrains.exposed.v1.core.SqlExpressionBuilder.like
import org.jetbrains.exposed.v1.core.SqlExpressionBuilder.minus
import org.jetbrains.exposed.v1.core.SqlExpressionBuilder.neq
import org.jetbrains.exposed.v1.core.SqlExpressionBuilder.plus
import org.jetbrains.exposed.v1.core.SqlExpressionBuilder.times
import org.jetbrains.exposed.v1.core.Table
import org.jetbrains.exposed.v1.core.and
import org.jetbrains.exposed.v1.core.dao.id.IntIdTable
import org.jetbrains.exposed.v1.core.dao.id.UUIDTable
import org.jetbrains.exposed.v1.core.intLiteral
import org.jetbrains.exposed.v1.core.statements.BatchUpsertStatement
import org.jetbrains.exposed.v1.core.statements.UpdateStatement
import org.jetbrains.exposed.v1.core.statements.UpsertBuilder
import org.jetbrains.exposed.v1.core.stringLiteral
import org.jetbrains.exposed.v1.core.stringParam
import org.jetbrains.exposed.v1.exceptions.UnsupportedByDialectException
import org.jetbrains.exposed.v1.jdbc.*
import org.jetbrains.exposed.v1.jdbc.batchUpsert
import org.jetbrains.exposed.v1.jdbc.insert
import org.jetbrains.exposed.v1.jdbc.insertAndGetId
import org.jetbrains.exposed.v1.jdbc.select
import org.jetbrains.exposed.v1.jdbc.selectAll
import org.jetbrains.exposed.v1.jdbc.transactions.transaction
import org.jetbrains.exposed.v1.jdbc.upsert
import org.jetbrains.exposed.v1.tests.DatabaseTestsBase
import org.jetbrains.exposed.v1.tests.TestDB
import org.jetbrains.exposed.v1.tests.shared.assertEqualLists
Expand All @@ -25,6 +34,7 @@ import org.junit.Test
import java.lang.Integer.parseInt
import java.util.*
import kotlin.properties.Delegates
import kotlin.test.assertEquals
import kotlin.test.assertNotEquals
import kotlin.test.assertNotNull

Expand Down Expand Up @@ -879,4 +889,63 @@ class UpsertTests : DatabaseTestsBase() {
assertEqualLists(uuidList, value[tester.uuidArray])
}
}

@Test
fun testExcludedValueInWhereCondition() {
val tester = object : Table("upsert_where_excluded") {
val id = integer("id")
.uniqueIndex()
val name = text("name")
val order = integer("order")
}

class TesterData(val id: Int, val name: String, val order: Int)

fun testerBatchUpsert(data: List<TesterData>) {
tester.batchUpsert(
data,
tester.id,
onUpdate = {
it[tester.name] = insertValue(tester.name)
it[tester.order] = insertValue(tester.order)
},
where = {
tester.order less insertValue(tester.order)
}
) {
this[tester.id] = it.id
this[tester.name] = it.name
this[tester.order] = it.order
}
}

fun assertTesterName(id: Int, name: String) {
assertEquals(name, tester.selectAll().where { tester.id eq id }.single()[tester.name])
}

// SQL seems correct, but the statement fails on POSTGRESNG.
// It looks like POSTGRESNG driver fails if batch insert fails if the amount of result rows less
// than amount of statements in batch (due to `where` condition in this case)
withTables(excludeSettings = TestDB.ALL_MYSQL_LIKE + upsertViaMergeDB + TestDB.POSTGRESQLNG, tester) {
testerBatchUpsert(
listOf(
TesterData(1, "tester1", 10),
TesterData(2, "tester2", 10),
)
)

assertTesterName(1, "tester1")
assertTesterName(2, "tester2")

testerBatchUpsert(
listOf(
TesterData(1, "tester1-modified", 5),
TesterData(2, "tester2-modified", 20),
)
)

assertTesterName(1, "tester1")
assertTesterName(2, "tester2-modified")
}
}
}
Loading