implements where for onConflict

add test for where for onConflict
This commit is contained in:
Ali 2021-10-05 18:25:53 +03:00
parent c83b630514
commit 28d465f69c
3 changed files with 151 additions and 3 deletions

View File

@ -197,6 +197,13 @@ class InsertStatement<T extends Table, D> {
first = false; 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<T, D>) { if (onConflict is DoUpdate<T, D>) {
@ -301,6 +308,8 @@ abstract class UpsertClause<T extends Table, D> {}
/// For an example, see [InsertStatement.insert]. /// For an example, see [InsertStatement.insert].
class DoUpdate<T extends Table, D> extends UpsertClause<T, D> { class DoUpdate<T extends Table, D> extends UpsertClause<T, D> {
final Insertable<D> Function(T old, T excluded) _creator; final Insertable<D> Function(T old, T excluded) _creator;
final Where Function(T old, T excluded)? _where;
final bool _usesExcludedTable; final bool _usesExcludedTable;
/// An optional list of columns to serve as an "conflict target", which /// An optional list of columns to serve as an "conflict target", which
@ -317,8 +326,10 @@ class DoUpdate<T extends Table, D> extends UpsertClause<T, D> {
/// been inserted, use [DoUpdate.withExcluded]. /// been inserted, use [DoUpdate.withExcluded].
/// ///
/// For an example, see [InsertStatement.insert]. /// For an example, see [InsertStatement.insert].
DoUpdate(Insertable<D> Function(T old) update, {this.target}) DoUpdate(Insertable<D> Function(T old) update,
{this.target, Expression<bool?> Function(T old)? where})
: _creator = ((old, _) => update(old)), : _creator = ((old, _) => update(old)),
_where = where == null ? null : ((old, _) => Where(where(old))),
_usesExcludedTable = false; _usesExcludedTable = false;
/// Creates a `DO UPDATE` clause. /// Creates a `DO UPDATE` clause.
@ -330,8 +341,12 @@ class DoUpdate<T extends Table, D> extends UpsertClause<T, D> {
/// parameter. /// parameter.
/// ///
/// For an example, see [InsertStatement.insert]. /// For an example, see [InsertStatement.insert].
DoUpdate.withExcluded(this._creator, {this.target}) DoUpdate.withExcluded(this._creator,
: _usesExcludedTable = true; {this.target, Expression<bool?> Function(T old, T excluded)? where})
: _usesExcludedTable = true,
_where = where == null
? null
: ((old, excluded) => Where(where(old, excluded)));
Insertable<D> _createInsertable(TableInfo<T, D> table) { Insertable<D> _createInsertable(TableInfo<T, D> table) {
return _creator(table.asDslTable, table.createAlias('excluded').asDslTable); return _creator(table.asDslTable, table.createAlias('excluded').asDslTable);

View File

@ -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( test(
'can use multiple upsert targets', 'can use multiple upsert targets',
() async { () 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 { test('can use a custom conflict clause', () async {
await db.into(db.todosTable).insert( await db.into(db.todosTable).insert(
TodosTableCompanion.insert(content: 'my content'), 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 { test('insertOnConflictUpdate', () async {
when(executor.runInsert(any, any)).thenAnswer((_) => Future.value(3)); 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 { test('applies implicit type converter', () async {
await db.into(db.categories).insert(CategoriesCompanion.insert( await db.into(db.categories).insert(CategoriesCompanion.insert(
description: 'description', description: 'description',

View File

@ -47,6 +47,56 @@ void main() {
expect(row.description, 'original description new description'); 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 { test('returning', () async {
final entry = await db.into(db.categories).insertReturning( final entry = await db.into(db.categories).insertReturning(
CategoriesCompanion.insert(description: 'Description')); CategoriesCompanion.insert(description: 'Description'));