Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support merge statement #1441

Closed
vlsi opened this issue Jan 29, 2022 · 0 comments · Fixed by #1743
Closed

Support merge statement #1441

vlsi opened this issue Jan 29, 2022 · 0 comments · Fixed by #1743
Assignees

Comments

@vlsi
Copy link

vlsi commented Jan 29, 2022

Hi,

I see there's "replace" statement which is related, yet it might be a bit different from MERGE.

I've implemented a small function for MERGE for Oracle DB (I can contribute it or something like that).
I use it instead of batchInsert as it enables me to automatically insert or update the rows.

The caveat is that MERGE in Oracle does not support "return generated keys" (there's no syntax for RETURNING), and I see your batchReplace uses return generated keys by default.

WDYT? (the code below can be used under Apache 2.0 if you wonder)

fun <T : Table, E> T.batchMerge(
    data: Iterable<E>,
    whenMatched: Boolean = true,
    whenNotMatched: Boolean = true,
    body: BatchMergeStatement.(E) -> Unit
) {
    if (!data.iterator().hasNext()) {
        return
    }
    val statement = BatchMergeStatement(this, whenMatched, whenNotMatched)
    data.forEach {
        statement.body(it)
        statement.addBatch()
    }
    statement.execute(TransactionManager.current())
}
class BatchMergeStatement(
    val table: Table,
    val whenMatched: Boolean,
    val whenNotMatched: Boolean,
) : UpdateStatement(table, limit = null, where = null) {
    val data = ArrayList<Map<Column<*>, Any?>>()
    override val firstDataSet: List<Pair<Column<*>, Any?>> get() = data.first().toList()

    override fun arguments(): Iterable<Iterable<Pair<IColumnType, Any?>>> {
        return data.map { it.map { (k, v) -> k.columnType to v } }
    }

    fun addBatch() {
        data.add(values.toMutableMap())
        values.clear()
    }

    override fun PreparedStatementApi.executeInternal(transaction: Transaction): Int =
        if (data.size > 1 || isAlwaysBatch) executeBatch().sum() else executeUpdate()

    override fun prepareSQL(transaction: Transaction): String {
        return QueryBuilder(true).run {
            +"MERGE INTO ${transaction.identity(table)} t"
            +" USING ("
            firstDataSet.appendTo(
                this,
                prefix = "SELECT ",
                postfix = " FROM DUAL"
            ) { (col, value) ->
                registerArgument(col, value)
                +" "
                +transaction.identity(col)
            }
            +") n"
            +" ON ("
            table.primaryKey!!.columns.appendTo(separator = " AND ") { col ->
                val columnName = transaction.identity(col)
                +"t.$columnName = n.$columnName"
            }
            +")"
            val allColumns = firstDataSet.map { it.first }
            if (whenMatched) {
                val updateColumns = allColumns.filterNot { it in table.primaryKey!!.columns }
                updateColumns.appendTo(prefix = " WHEN MATCHED THEN UPDATE SET ") { col ->
                    val columnName = transaction.identity(col)
                    +"t.$columnName = n.$columnName"
                }
            }
            if (whenNotMatched) {
                allColumns.appendTo(
                    prefix = " WHEN NOT MATCHED THEN INSERT(",
                    postfix = ")"
                ) { col ->
                    +transaction.identity(col)
                }
                allColumns.appendTo(prefix = " VALUES(", postfix = ")") { col ->
                    +"n."
                    +transaction.identity(col)
                }
            }
            toString()
        }
    }
}
@bog-walk bog-walk self-assigned this May 16, 2023
@bog-walk bog-walk linked a pull request May 20, 2023 that will close this issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants