mirror of https://github.com/AMT-Cheif/drift.git
Support + and - operators on DateTime
This commit is contained in:
parent
84bac1bf1d
commit
a7ac6db55d
|
@ -76,6 +76,13 @@ that you can use operators and comparisons on them.
|
|||
To obtain the current date or the current time as an expression, use the `currentDate`
|
||||
and `currentDateAndTime` constants provided by moor.
|
||||
|
||||
You can also use the `+` and `-` operators to add or subtract a duration from a time column:
|
||||
|
||||
```dart
|
||||
final toNextWeek = TasksCompanion.custom(dueDate: tasks.dueDate + Duration(weeks: 1));
|
||||
update(tasks).write(toNextWeek);
|
||||
```
|
||||
|
||||
## `IN` and `NOT IN`
|
||||
You can check whether an expression is in a list of values by using the `isIn` and `isNotIn`
|
||||
methods:
|
||||
|
|
|
@ -44,6 +44,18 @@ extension DateTimeExpressions on Expression<DateTime> {
|
|||
// for moor, date times are just unix timestamps, so we don't need to rewrite
|
||||
// anything when converting
|
||||
Expression<int> get secondsSinceEpoch => dartCast();
|
||||
|
||||
/// Adds [duration] from this date.
|
||||
Expression<DateTime> operator +(Duration duration) {
|
||||
return _BaseInfixOperator(this, '+', Variable<int>(duration.inSeconds),
|
||||
precedence: Precedence.plusMinus);
|
||||
}
|
||||
|
||||
/// Subtracts [duration] from this date.
|
||||
Expression<DateTime> operator -(Duration duration) {
|
||||
return _BaseInfixOperator(this, '-', Variable<int>(duration.inSeconds),
|
||||
precedence: Precedence.plusMinus);
|
||||
}
|
||||
}
|
||||
|
||||
/// Expression that extracts components out of a date time by using the builtin
|
||||
|
|
|
@ -203,13 +203,13 @@ abstract class _InfixOperator<D> extends Expression<D> {
|
|||
|
||||
class _BaseInfixOperator<D> extends _InfixOperator<D> {
|
||||
@override
|
||||
final Expression<D> left;
|
||||
final Expression left;
|
||||
|
||||
@override
|
||||
final String operator;
|
||||
|
||||
@override
|
||||
final Expression<D> right;
|
||||
final Expression right;
|
||||
|
||||
@override
|
||||
final Precedence precedence;
|
||||
|
|
|
@ -84,7 +84,10 @@ class IntType extends SqlType<int> {
|
|||
const IntType();
|
||||
|
||||
@override
|
||||
int mapFromDatabaseResponse(dynamic response) => response as int;
|
||||
int mapFromDatabaseResponse(dynamic response) {
|
||||
if (response is int) return response;
|
||||
return int.parse(response.toString());
|
||||
}
|
||||
|
||||
@override
|
||||
String mapToSqlConstant(int content) => content?.toString() ?? 'NULL';
|
||||
|
|
|
@ -4,7 +4,7 @@ import 'package:test/test.dart';
|
|||
/// Matcher for [Component]-subclasses. Expect that a component generates the
|
||||
/// matching [sql] and, optionally, the matching [variables].
|
||||
Matcher generates(dynamic sql, [dynamic variables]) {
|
||||
final variablesMatcher = variables != null ? wrapMatcher(variables) : null;
|
||||
final variablesMatcher = variables != null ? wrapMatcher(variables) : isEmpty;
|
||||
return _GeneratesSqlMatcher(wrapMatcher(sql), variablesMatcher);
|
||||
}
|
||||
|
||||
|
@ -19,8 +19,9 @@ class _GeneratesSqlMatcher extends Matcher {
|
|||
description = description.add('generates sql ').addDescriptionOf(_matchSql);
|
||||
|
||||
if (_matchVariables != null) {
|
||||
description =
|
||||
description.add('and variables').addDescriptionOf(_matchVariables);
|
||||
description = description
|
||||
.add(' and variables that ')
|
||||
.addDescriptionOf(_matchVariables);
|
||||
}
|
||||
return description;
|
||||
}
|
||||
|
@ -36,15 +37,15 @@ class _GeneratesSqlMatcher extends Matcher {
|
|||
|
||||
mismatchDescription = mismatchDescription.add('generated $sql, which ');
|
||||
mismatchDescription = _matchSql.describeMismatch(
|
||||
sql, mismatchDescription, matchState, verbose);
|
||||
sql, mismatchDescription, matchState['sql_match'] as Map, verbose);
|
||||
}
|
||||
if (matchState.containsKey('vars')) {
|
||||
final vars = matchState['vars'] as List;
|
||||
|
||||
mismatchDescription =
|
||||
mismatchDescription.add('used variables $vars, which ');
|
||||
mismatchDescription.add('generated variables $vars, which ');
|
||||
mismatchDescription = _matchVariables.describeMismatch(
|
||||
vars, mismatchDescription, matchState, verbose);
|
||||
vars, mismatchDescription, matchState['vars_match'] as Map, verbose);
|
||||
}
|
||||
return mismatchDescription;
|
||||
}
|
||||
|
@ -52,7 +53,7 @@ class _GeneratesSqlMatcher extends Matcher {
|
|||
@override
|
||||
bool matches(dynamic item, Map matchState) {
|
||||
if (item is! Component) {
|
||||
addStateInfo(matchState, {'wrong_type': true});
|
||||
matchState['wrong_type'] = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -62,14 +63,18 @@ class _GeneratesSqlMatcher extends Matcher {
|
|||
|
||||
var matches = true;
|
||||
|
||||
if (!_matchSql.matches(ctx.sql, matchState)) {
|
||||
addStateInfo(matchState, {'sql': ctx.sql});
|
||||
final sqlMatchState = {};
|
||||
if (!_matchSql.matches(ctx.sql, sqlMatchState)) {
|
||||
matchState['sql'] = ctx.sql;
|
||||
matchState['sql_match'] = sqlMatchState;
|
||||
matches = false;
|
||||
}
|
||||
|
||||
final argsMatchState = {};
|
||||
if (_matchVariables != null &&
|
||||
!_matchVariables.matches(ctx.boundVariables, matchState)) {
|
||||
addStateInfo(matchState, {'vars': ctx.boundVariables});
|
||||
!_matchVariables.matches(ctx.boundVariables, argsMatchState)) {
|
||||
matchState['vars'] = ctx.boundVariables;
|
||||
matchState['vars_match'] = argsMatchState;
|
||||
matches = false;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ import 'package:moor/moor.dart';
|
|||
import 'package:test/test.dart';
|
||||
|
||||
import '../data/utils/expect_equality.dart';
|
||||
import '../data/utils/expect_generated.dart';
|
||||
|
||||
typedef _Extractor = Expression<int> Function(Expression<DateTime> d);
|
||||
|
||||
|
@ -37,4 +38,13 @@ void main() {
|
|||
|
||||
expect(ctx.sql, 'strftime(\'%s\', CURRENT_TIMESTAMP) + 10');
|
||||
});
|
||||
|
||||
test('plus and minus durations', () {
|
||||
final expr = currentDateAndTime +
|
||||
const Duration(days: 3) -
|
||||
const Duration(seconds: 5);
|
||||
|
||||
expect(expr,
|
||||
generates('strftime(\'%s\', CURRENT_TIMESTAMP) + ? - ?', [259200, 5]));
|
||||
});
|
||||
}
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
import 'package:moor/moor.dart';
|
||||
@TestOn('vm')
|
||||
import 'package:test/test.dart';
|
||||
import 'package:moor_ffi/moor_ffi.dart';
|
||||
|
||||
import '../data/tables/todos.dart';
|
||||
|
||||
void main() {
|
||||
TodoDb db;
|
||||
|
||||
setUp(() async {
|
||||
db = TodoDb(VmDatabase.memory());
|
||||
|
||||
// we selectOnly from users for the lack of a better option. Insert one
|
||||
// row so that getSingle works
|
||||
await db.into(db.users).insert(
|
||||
UsersCompanion.insert(name: 'User name', profilePicture: Uint8List(0)));
|
||||
});
|
||||
|
||||
tearDown(() => db.close());
|
||||
|
||||
test('plus and minus on DateTimes', () async {
|
||||
final nowExpr = currentDateAndTime;
|
||||
final tomorrow = nowExpr + const Duration(days: 1);
|
||||
final nowStamp = nowExpr.secondsSinceEpoch;
|
||||
final tomorrowStamp = tomorrow.secondsSinceEpoch;
|
||||
|
||||
final row = await (db.selectOnly(db.users)
|
||||
..addColumns([nowStamp, tomorrowStamp]))
|
||||
.getSingle();
|
||||
|
||||
expect(row.read(tomorrowStamp) - row.read(nowStamp),
|
||||
const Duration(days: 1).inSeconds);
|
||||
});
|
||||
}
|
|
@ -118,6 +118,20 @@ void main() {
|
|||
});
|
||||
});
|
||||
|
||||
test('can update with custom companions', () async {
|
||||
await db.update(db.todosTable).replace(TodosTableCompanion.custom(
|
||||
id: const Variable(4),
|
||||
content: db.todosTable.content,
|
||||
targetDate: db.todosTable.targetDate + const Duration(days: 1),
|
||||
));
|
||||
|
||||
verify(executor.runUpdate(
|
||||
'UPDATE todos SET content = content, target_date = target_date + ? '
|
||||
'WHERE id = ?;',
|
||||
argThat(equals([86400, 4])),
|
||||
));
|
||||
});
|
||||
|
||||
group('custom updates', () {
|
||||
test('execute the correct sql', () async {
|
||||
await db.customUpdate('DELETE FROM users');
|
||||
|
|
Loading…
Reference in New Issue