diff --git a/src/main/kotlin/org/jetbrains/exposed/sql/vendors/PostgreSQL.kt b/src/main/kotlin/org/jetbrains/exposed/sql/vendors/PostgreSQL.kt index 20fbe99085..5f00471747 100644 --- a/src/main/kotlin/org/jetbrains/exposed/sql/vendors/PostgreSQL.kt +++ b/src/main/kotlin/org/jetbrains/exposed/sql/vendors/PostgreSQL.kt @@ -29,6 +29,33 @@ internal object PostgreSQLFunctionProvider : FunctionProvider() { if (limit != null) transaction.throwUnsupportedException("PostgreSQL doesn't support LIMIT in UPDATE clause.") return super.update(targets, columnsAndValues, limit, where, transaction) } + + override fun replace(table: Table, data: List, Any?>>, transaction: Transaction): String { + + val builder = QueryBuilder(true) + val sql = if (data.isEmpty()) "" + else data.joinToString(prefix = "VALUES (", postfix = ")") { (col, value) -> + builder.registerArgument(col, value) + } + + val columns = data.map { it.first } + + val def = super.insert(false, table, columns, sql, transaction) + + val uniqueCols = columns.filter { it.indexInPK != null }.sortedBy { it.indexInPK } + if (uniqueCols.isEmpty()) + transaction.throwUnsupportedException("Postgres replace table must supply at least one primary key") + val conflictKey = uniqueCols.joinToString { transaction.identity(it) } + return def + "ON CONFLICT ($conflictKey) DO UPDATE SET " + columns.joinToString { "${transaction.identity(it)}=EXCLUDED.${transaction.identity(it)}" } + } + + override fun insert(ignore: Boolean, table: Table, columns: List>, expr: String, transaction: Transaction): String { + val def = super.insert(false, table, columns, expr, transaction) + return if (ignore) "$def $onConflictIgnore" else def + } + + private const val onConflictIgnore = "ON CONFLICT DO NOTHING" + } internal class PostgreSQLDialect : VendorDialect(dialectName, PostgreSQLDataTypeProvider, PostgreSQLFunctionProvider) {