upsert

fun <T : Table> T.upsert(conflictIndex: Index, onUpdate: UpsertBuilder.(UpdateStatement) -> Unit? = null, onUpdateExclude: List<Column<*>>? = null, where: () -> Op<Boolean>? = null, body: T.(UpsertStatement<Long>) -> Unit): UpsertStatement<Long>

Convenience overload of Exposed's native upsert that takes a unique Index as the conflict target, forwarding its columns as the keys of the underlying ON CONFLICT (...) clause. A named index definition therefore stays the single source of truth for the conflict columns.

The index must be a plain column index: a functional or partial index cannot be named as a conflict target by its columns, and forwarding no columns would make Exposed fall back to the primary key or the first unique index, silently matching on a different key. MySQL and MariaDB are not supported, because their ON DUPLICATE KEY UPDATE fires on any unique key and cannot be limited to one index; use Exposed's own upsert there.

onUpdate, onUpdateExclude and where are passed straight through to Exposed's upsert. How much of that each database supports is dialect-specific: the MERGE-based statement H2 uses, for example, rejects where.

Return

the executed UpsertStatement

Parameters

conflictIndex

the unique index whose columns form the conflict target; it must belong to this table

onUpdate

the columns to set when a row already exists; defaults to updating every inserted column

onUpdateExclude

the columns to leave untouched when a row already exists

where

a condition limiting which existing rows are updated

body

assigns the values to insert

Type Parameters

T

the table type

Throws

if conflictIndex is not unique, belongs to a different table, or is a functional or partial index

UnsupportedByDialectException

on MySQL and MariaDB