Skip to content

Commit 15d8ef9

Browse files
bulk insert or update
1 parent d6c23b8 commit 15d8ef9

File tree

3 files changed

+98
-9
lines changed

3 files changed

+98
-9
lines changed

ktorm-support-postgresql/src/main/kotlin/org/ktorm/support/postgresql/BulkInsert.kt

Lines changed: 93 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,12 @@ import org.ktorm.expression.ColumnExpression
2525
import org.ktorm.expression.SqlExpression
2626
import org.ktorm.expression.TableExpression
2727
import org.ktorm.schema.BaseTable
28+
import org.ktorm.schema.Column
2829

2930
/**
3031
* Bulk insert expression, represents a bulk insert statement in PostgreSQL.
3132
*
32-
* For example:
33+
* For example:
3334
* `insert into table (column1, column2) values (?, ?), (?, ?), (?, ?)... on conflict (...) do update set ...`.
3435
*
3536
* @property table the table to be inserted.
@@ -85,7 +86,9 @@ public data class BulkInsertExpression(
8586
* @return the effected row count.
8687
* @see batchInsert
8788
*/
88-
public fun <T : BaseTable<*>> Database.bulkInsert(table: T, block: BulkInsertStatementBuilder<T>.() -> Unit): Int {
89+
public fun <T : BaseTable<*>> Database.bulkInsert(
90+
table: T, block: BulkInsertStatementBuilder<T>.() -> Unit
91+
): Int {
8992
val builder = BulkInsertStatementBuilder(table).apply(block)
9093

9194
val expression = AliasRemover.visit(
@@ -95,11 +98,80 @@ public fun <T : BaseTable<*>> Database.bulkInsert(table: T, block: BulkInsertSta
9598
return executeUpdate(expression)
9699
}
97100

101+
/**
102+
* Bulk insert records to the table, determining if there is a key conflict while inserting each of them,
103+
* and automatically performs updates if any conflict exists.
104+
*
105+
* Usage:
106+
*
107+
* ```kotlin
108+
* database.bulkInsertOrUpdate(Employees) {
109+
* item {
110+
* set(it.id, 1)
111+
* set(it.name, "vince")
112+
* set(it.job, "engineer")
113+
* set(it.salary, 1000)
114+
* set(it.hireDate, LocalDate.now())
115+
* set(it.departmentId, 1)
116+
* }
117+
* item {
118+
* set(it.id, 5)
119+
* set(it.name, "vince")
120+
* set(it.job, "engineer")
121+
* set(it.salary, 1000)
122+
* set(it.hireDate, LocalDate.now())
123+
* set(it.departmentId, 1)
124+
* }
125+
* onConflict {
126+
* set(it.salary, it.salary + 900)
127+
* }
128+
* }
129+
* ```
130+
*
131+
* Generated SQL:
132+
*
133+
* ```sql
134+
* insert into t_employee (id, name, job, salary, hire_date, department_id)
135+
* values (?, ?, ?, ?, ?, ?), (?, ?, ?, ?, ?, ?)
136+
* on conflict (id) do update set salary = salary + ?
137+
* ```
138+
*
139+
* @since 3.3.0
140+
* @param table the table to be inserted.
141+
* @param block the DSL block used to construct the expression.
142+
* @return the effected row count.
143+
* @see bulkInsert
144+
*/
145+
public fun <T : BaseTable<*>> Database.bulkInsertOrUpdate(
146+
table: T, block: BulkInsertOrUpdateStatementBuilder<T>.() -> Unit
147+
): Int {
148+
val builder = BulkInsertOrUpdateStatementBuilder(table).apply(block)
149+
150+
val primaryKeys = table.primaryKeys
151+
if (primaryKeys.isEmpty() && builder.conflictColumns.isEmpty()) {
152+
val msg =
153+
"Table '$table' doesn't have a primary key, " +
154+
"you must specify the conflict columns when calling onConflict(col) { .. }"
155+
throw IllegalStateException(msg)
156+
}
157+
158+
val expression = AliasRemover.visit(
159+
BulkInsertExpression(
160+
table = table.asExpression(),
161+
assignments = builder.assignments,
162+
conflictColumns = builder.conflictColumns.ifEmpty { primaryKeys }.map { it.asExpression() },
163+
updateAssignments = builder.updateAssignments
164+
)
165+
)
166+
167+
return executeUpdate(expression)
168+
}
169+
98170
/**
99171
* DSL builder for bulk insert statements.
100172
*/
101173
@KtormDsl
102-
public class BulkInsertStatementBuilder<T : BaseTable<*>>(internal val table: T) {
174+
public open class BulkInsertStatementBuilder<T : BaseTable<*>>(internal val table: T) {
103175
internal val assignments = ArrayList<List<ColumnAssignmentExpression<*>>>()
104176

105177
/**
@@ -118,3 +190,21 @@ public class BulkInsertStatementBuilder<T : BaseTable<*>>(internal val table: T)
118190
}
119191
}
120192
}
193+
194+
/**
195+
* DSL builder for bulk insert or update statements.
196+
*/
197+
@KtormDsl
198+
public class BulkInsertOrUpdateStatementBuilder<T : BaseTable<*>>(table: T) : BulkInsertStatementBuilder<T>(table) {
199+
internal val updateAssignments = ArrayList<ColumnAssignmentExpression<*>>()
200+
internal val conflictColumns = ArrayList<Column<*>>()
201+
202+
/**
203+
* Specify the update assignments while any key conflict exists.
204+
*/
205+
public fun onConflict(vararg columns: Column<*>, block: AssignmentsBuilder.() -> Unit) {
206+
val builder = PostgreSqlAssignmentsBuilder().apply(block)
207+
updateAssignments += builder.assignments
208+
conflictColumns += columns
209+
}
210+
}

ktorm-support-postgresql/src/main/kotlin/org/ktorm/support/postgresql/Global.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,6 @@ public fun <T : BaseTable<*>> T.insertOrUpdate(block: InsertOrUpdateStatementBui
116116
* @return the effected row count.
117117
* @see batchInsert
118118
*/
119-
public fun <T : BaseTable<*>> T.bulkInsert(block: BulkInsertStatementBuilder<T>.() -> Unit): Int {
120-
return Database.global.bulkInsert(this, block)
121-
}
119+
//public fun <T : BaseTable<*>> T.bulkInsert(block: BulkInsertStatementBuilder<T>.() -> Unit): Int {
120+
// return Database.global.bulkInsert(this, block)
121+
//}

ktorm-support-postgresql/src/main/kotlin/org/ktorm/support/postgresql/InsertOrUpdate.kt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ public data class InsertOrUpdateExpression(
6868
*
6969
* ```sql
7070
* insert into t_employee (id, name, job, salary, hire_date, department_id) values (?, ?, ?, ?, ?, ?)
71-
* on conflict (id) do update set salary = t_employee.salary + ?
71+
* on conflict (id) do update set salary = salary + ?
7272
* ```
7373
*
7474
* @since 2.7
@@ -77,8 +77,7 @@ public data class InsertOrUpdateExpression(
7777
* @return the effected row count.
7878
*/
7979
public fun <T : BaseTable<*>> Database.insertOrUpdate(
80-
table: T,
81-
block: InsertOrUpdateStatementBuilder.(T) -> Unit
80+
table: T, block: InsertOrUpdateStatementBuilder.(T) -> Unit
8281
): Int {
8382
val builder = InsertOrUpdateStatementBuilder().apply { block(table) }
8483

0 commit comments

Comments
 (0)