From 28d465f69c3e7235539766483c6725d347ea4f03 Mon Sep 17 00:00:00 2001 From: Ali Date: Tue, 5 Oct 2021 18:25:53 +0300 Subject: [PATCH] implements where for onConflict add test for where for onConflict --- .../query_builder/statements/insert.dart | 21 ++++- moor/test/insert_test.dart | 83 +++++++++++++++++++ .../insert_integration_test.dart | 50 +++++++++++ 3 files changed, 151 insertions(+), 3 deletions(-) diff --git a/moor/lib/src/runtime/query_builder/statements/insert.dart b/moor/lib/src/runtime/query_builder/statements/insert.dart index 12250034..70811bb3 100644 --- a/moor/lib/src/runtime/query_builder/statements/insert.dart +++ b/moor/lib/src/runtime/query_builder/statements/insert.dart @@ -197,6 +197,13 @@ class InsertStatement { first = false; } + + if (onConflict._where != null) { + ctx.writeWhitespace(); + final where = onConflict._where!( + table.asDslTable, table.createAlias('excluded').asDslTable); + where.writeInto(ctx); + } } if (onConflict is DoUpdate) { @@ -301,6 +308,8 @@ abstract class UpsertClause {} /// For an example, see [InsertStatement.insert]. class DoUpdate extends UpsertClause { final Insertable Function(T old, T excluded) _creator; + final Where Function(T old, T excluded)? _where; + final bool _usesExcludedTable; /// An optional list of columns to serve as an "conflict target", which @@ -317,8 +326,10 @@ class DoUpdate extends UpsertClause { /// been inserted, use [DoUpdate.withExcluded]. /// /// For an example, see [InsertStatement.insert]. - DoUpdate(Insertable Function(T old) update, {this.target}) + DoUpdate(Insertable Function(T old) update, + {this.target, Expression Function(T old)? where}) : _creator = ((old, _) => update(old)), + _where = where == null ? null : ((old, _) => Where(where(old))), _usesExcludedTable = false; /// Creates a `DO UPDATE` clause. @@ -330,8 +341,12 @@ class DoUpdate extends UpsertClause { /// parameter. /// /// For an example, see [InsertStatement.insert]. - DoUpdate.withExcluded(this._creator, {this.target}) - : _usesExcludedTable = true; + DoUpdate.withExcluded(this._creator, + {this.target, Expression Function(T old, T excluded)? where}) + : _usesExcludedTable = true, + _where = where == null + ? null + : ((old, excluded) => Where(where(old, excluded))); Insertable _createInsertable(TableInfo table) { return _creator(table.asDslTable, table.createAlias('excluded').asDslTable); diff --git a/moor/test/insert_test.dart b/moor/test/insert_test.dart index 2e7fb2ac..89d01748 100644 --- a/moor/test/insert_test.dart +++ b/moor/test/insert_test.dart @@ -205,6 +205,23 @@ void main() { )); }); + test('can use an upsert clause with where', () async { + await db.into(db.todosTable).insert( + TodosTableCompanion.insert(content: 'my content'), + onConflict: DoUpdate((old) { + return TodosTableCompanion.custom( + content: const Variable('important: ') + old.content); + }, where: (old) => old.category.equals(1)), + ); + + verify(executor.runInsert( + 'INSERT INTO todos (content) VALUES (?) ' + 'ON CONFLICT(id) DO UPDATE SET content = ? || content ' + 'WHERE category = ?', + argThat(equals(['my content', 'important: ', 1])), + )); + }); + test( 'can use multiple upsert targets', () async { @@ -236,6 +253,36 @@ void main() { }, ); + test( + 'can use multiple upsert targets with where', + () async { + await db + .into(db.todosTable) + .insert(TodosTableCompanion.insert(content: 'my content'), + onConflict: UpsertMultiple([ + DoUpdate((old) { + return TodosTableCompanion.custom( + content: const Variable('important: ') + old.content); + }, where: (old) => old.category.equals(1)), + DoUpdate((old) { + return TodosTableCompanion.custom( + content: const Variable('second: ') + old.content); + }, + target: [db.todosTable.content], + where: (old) => old.category.equals(1)), + ])); + + verify(executor.runInsert( + 'INSERT INTO todos (content) VALUES (?) ' + 'ON CONFLICT(id) DO UPDATE SET content = ? || content ' + 'WHERE category = ? ' + 'ON CONFLICT(content) DO UPDATE SET content = ? || content ' + 'WHERE category = ?', + argThat(equals(['my content', 'important: ', 1, 'second: ', 1])), + )); + }, + ); + test('can use a custom conflict clause', () async { await db.into(db.todosTable).insert( TodosTableCompanion.insert(content: 'my content'), @@ -252,6 +299,23 @@ void main() { )); }); + test('can use a custom conflict clause with where', () async { + await db.into(db.todosTable).insert( + TodosTableCompanion.insert(content: 'my content'), + onConflict: DoUpdate( + (old) => TodosTableCompanion.insert(content: 'changed'), + target: [db.todosTable.content], + where: (old) => old.content.equalsExp(old.title)), + ); + + verify(executor.runInsert( + 'INSERT INTO todos (content) VALUES (?) ' + 'ON CONFLICT(content) DO UPDATE SET content = ? ' + 'WHERE content = title', + argThat(equals(['my content', 'changed'])), + )); + }); + test('insertOnConflictUpdate', () async { when(executor.runInsert(any, any)).thenAnswer((_) => Future.value(3)); @@ -284,6 +348,25 @@ void main() { )); }); + test('can access excluded row in upsert with where', () async { + await db.into(db.todosTable).insert( + TodosTableCompanion.insert(content: 'content'), + onConflict: DoUpdate.withExcluded( + (old, excluded) => TodosTableCompanion.custom( + content: old.content + excluded.content, + ), + where: (old, excluded) => old.title.equalsExp(excluded.title)), + ); + + verify(executor.runInsert( + 'INSERT INTO todos (content) VALUES (?) ' + 'ON CONFLICT(id) DO UPDATE ' + 'SET content = todos.content || excluded.content ' + 'WHERE todos.title = excluded.title', + ['content'], + )); + }); + test('applies implicit type converter', () async { await db.into(db.categories).insert(CategoriesCompanion.insert( description: 'description', diff --git a/moor/test/integration_tests/insert_integration_test.dart b/moor/test/integration_tests/insert_integration_test.dart index fa044dc9..e0f604fe 100644 --- a/moor/test/integration_tests/insert_integration_test.dart +++ b/moor/test/integration_tests/insert_integration_test.dart @@ -47,6 +47,56 @@ void main() { expect(row.description, 'original description new description'); }); + test('insert with DoUpdate and excluded row and where statement true', + () async { + await db.into(db.categories).insert( + CategoriesCompanion.insert(description: 'original description')); + + var row = await db.select(db.categories).getSingle(); + + await db.into(db.categories).insert( + CategoriesCompanion( + id: Value(row.id), + priority: const Value(CategoryPriority.medium), + description: const Value('new description'), + ), + onConflict: DoUpdate.withExcluded( + (old, excluded) => CategoriesCompanion.custom( + description: old.description + + const Constant(' ') + + excluded.description), + where: (old, excluded) => + old.priority.isBiggerOrEqual(excluded.priority))); + + row = await db.select(db.categories).getSingle(); + expect(row.description, 'original description'); + }); + + test('insert with DoUpdate and excluded row and where statement false', + () async { + await db.into(db.categories).insert( + CategoriesCompanion.insert(description: 'original description')); + + var row = await db.select(db.categories).getSingle(); + + await db.into(db.categories).insert( + CategoriesCompanion( + id: Value(row.id), + priority: const Value(CategoryPriority.low), + description: const Value('new description'), + ), + onConflict: DoUpdate.withExcluded( + (old, excluded) => CategoriesCompanion.custom( + description: old.description + + const Constant(' ') + + excluded.description), + where: (old, excluded) => + old.priority.isBiggerOrEqual(excluded.priority))); + + row = await db.select(db.categories).getSingle(); + expect(row.description, 'original description new description'); + }); + test('returning', () async { final entry = await db.into(db.categories).insertReturning( CategoriesCompanion.insert(description: 'Description'));