mirror of https://github.com/AMT-Cheif/drift.git
Merge pull request #2349 from FaFre/upsert_do_nothing
Upsert constraint support DO NOTHING
This commit is contained in:
commit
fadc30df1d
|
@ -9,6 +9,7 @@
|
|||
in the database class, the schema version in the database will now be downgraded.
|
||||
- When using a drift isolate in the same engine group, errors on the remote end are
|
||||
reported as-is instead of wrapping them in a `DriftRemoteException`.
|
||||
- Added support for `DO NOTHING` during upsert operations with constraint violations
|
||||
|
||||
## 2.5.0
|
||||
|
||||
|
|
|
@ -271,6 +271,29 @@ class InsertStatement<T extends Table, D> {
|
|||
Insertable<D>? originalEntry,
|
||||
UpsertClause<T, D>? onConflict,
|
||||
) {
|
||||
void writeOnConflictConstraint(List<Column<Object>>? target) {
|
||||
ctx.buffer.write(' ON CONFLICT(');
|
||||
|
||||
final conflictTarget = target ?? table.$primaryKey.toList();
|
||||
|
||||
if (conflictTarget.isEmpty) {
|
||||
throw ArgumentError(
|
||||
'Table has no primary key, so a conflict target is needed.');
|
||||
}
|
||||
|
||||
var first = true;
|
||||
for (final target in conflictTarget) {
|
||||
if (!first) ctx.buffer.write(', ');
|
||||
|
||||
// Writing the escaped name directly because it should not have a table
|
||||
// name in front of it.
|
||||
ctx.buffer.write(target.escapedName);
|
||||
first = false;
|
||||
}
|
||||
|
||||
ctx.buffer.write(')');
|
||||
}
|
||||
|
||||
if (onConflict is DoUpdate<T, D>) {
|
||||
if (onConflict._usesExcludedTable) {
|
||||
ctx.hasMultipleTables = true;
|
||||
|
@ -290,32 +313,15 @@ class InsertStatement<T extends Table, D> {
|
|||
|
||||
final updateSet = upsertInsertable.toColumns(true);
|
||||
|
||||
ctx.buffer.write(' ON CONFLICT(');
|
||||
|
||||
final conflictTarget = onConflict.target ?? table.$primaryKey.toList();
|
||||
|
||||
if (conflictTarget.isEmpty) {
|
||||
throw ArgumentError(
|
||||
'Table has no primary key, so a conflict target is needed.');
|
||||
}
|
||||
|
||||
var first = true;
|
||||
for (final target in conflictTarget) {
|
||||
if (!first) ctx.buffer.write(', ');
|
||||
|
||||
// Writing the escaped name directly because it should not have a table
|
||||
// name in front of it.
|
||||
ctx.buffer.write(target.escapedName);
|
||||
first = false;
|
||||
}
|
||||
writeOnConflictConstraint(onConflict.target);
|
||||
|
||||
if (ctx.dialect == SqlDialect.postgres &&
|
||||
mode == InsertMode.insertOrIgnore) {
|
||||
ctx.buffer.write(') DO NOTHING ');
|
||||
ctx.buffer.write(' DO NOTHING ');
|
||||
} else {
|
||||
ctx.buffer.write(') DO UPDATE SET ');
|
||||
ctx.buffer.write(' DO UPDATE SET ');
|
||||
|
||||
first = true;
|
||||
var first = true;
|
||||
for (final update in updateSet.entries) {
|
||||
final column = ctx.identifier(update.key);
|
||||
|
||||
|
@ -337,6 +343,9 @@ class InsertStatement<T extends Table, D> {
|
|||
for (final clause in onConflict.clauses) {
|
||||
_writeOnConflict(ctx, mode, originalEntry, clause);
|
||||
}
|
||||
} else if (onConflict is DoNothing<T, D>) {
|
||||
writeOnConflictConstraint(onConflict.target);
|
||||
ctx.buffer.write(' DO NOTHING');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -510,3 +519,15 @@ class UpsertMultiple<T extends Table, D> extends UpsertClause<T, D> {
|
|||
/// 03-12).
|
||||
UpsertMultiple(this.clauses);
|
||||
}
|
||||
|
||||
/// Upsert clause that does nothing on conflict
|
||||
class DoNothing<T extends Table, D> extends UpsertClause<T, D> {
|
||||
/// An optional list of columns to serve as an "conflict target", which
|
||||
/// specifies the uniqueness constraint that will trigger the upsert.
|
||||
///
|
||||
/// By default, the primary key of the table will be used.
|
||||
final List<Column>? target;
|
||||
|
||||
/// Creates an upsert clause that does nothing on conflict
|
||||
DoNothing({this.target});
|
||||
}
|
||||
|
|
|
@ -340,6 +340,34 @@ void main() {
|
|||
));
|
||||
});
|
||||
|
||||
test('can use do nothing on upsert', () async {
|
||||
await db.into(db.todosTable).insert(
|
||||
TodosTableCompanion.insert(content: 'my content'),
|
||||
onConflict: DoNothing(),
|
||||
);
|
||||
|
||||
verify(executor.runInsert(
|
||||
'INSERT INTO "todos" ("content") VALUES (?) '
|
||||
'ON CONFLICT("id") DO NOTHING',
|
||||
argThat(equals(['my content'])),
|
||||
));
|
||||
});
|
||||
|
||||
test('can use a custom conflict clause with do nothing', () async {
|
||||
await db.into(db.todosTable).insert(
|
||||
TodosTableCompanion.insert(content: 'my content'),
|
||||
onConflict: DoNothing(
|
||||
target: [db.todosTable.content],
|
||||
),
|
||||
);
|
||||
|
||||
verify(executor.runInsert(
|
||||
'INSERT INTO "todos" ("content") VALUES (?) '
|
||||
'ON CONFLICT("content") DO NOTHING',
|
||||
argThat(equals(['my content'])),
|
||||
));
|
||||
});
|
||||
|
||||
test('insertOnConflictUpdate', () async {
|
||||
when(executor.runInsert(any, any)).thenAnswer((_) => Future.value(3));
|
||||
|
||||
|
|
Loading…
Reference in New Issue