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`
|
To obtain the current date or the current time as an expression, use the `currentDate`
|
||||||
and `currentDateAndTime` constants provided by moor.
|
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`
|
## `IN` and `NOT IN`
|
||||||
You can check whether an expression is in a list of values by using the `isIn` and `isNotIn`
|
You can check whether an expression is in a list of values by using the `isIn` and `isNotIn`
|
||||||
methods:
|
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
|
// for moor, date times are just unix timestamps, so we don't need to rewrite
|
||||||
// anything when converting
|
// anything when converting
|
||||||
Expression<int> get secondsSinceEpoch => dartCast();
|
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
|
/// 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> {
|
class _BaseInfixOperator<D> extends _InfixOperator<D> {
|
||||||
@override
|
@override
|
||||||
final Expression<D> left;
|
final Expression left;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
final String operator;
|
final String operator;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
final Expression<D> right;
|
final Expression right;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
final Precedence precedence;
|
final Precedence precedence;
|
||||||
|
|
|
@ -84,7 +84,10 @@ class IntType extends SqlType<int> {
|
||||||
const IntType();
|
const IntType();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int mapFromDatabaseResponse(dynamic response) => response as int;
|
int mapFromDatabaseResponse(dynamic response) {
|
||||||
|
if (response is int) return response;
|
||||||
|
return int.parse(response.toString());
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String mapToSqlConstant(int content) => content?.toString() ?? 'NULL';
|
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
|
/// Matcher for [Component]-subclasses. Expect that a component generates the
|
||||||
/// matching [sql] and, optionally, the matching [variables].
|
/// matching [sql] and, optionally, the matching [variables].
|
||||||
Matcher generates(dynamic sql, [dynamic 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);
|
return _GeneratesSqlMatcher(wrapMatcher(sql), variablesMatcher);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,8 +19,9 @@ class _GeneratesSqlMatcher extends Matcher {
|
||||||
description = description.add('generates sql ').addDescriptionOf(_matchSql);
|
description = description.add('generates sql ').addDescriptionOf(_matchSql);
|
||||||
|
|
||||||
if (_matchVariables != null) {
|
if (_matchVariables != null) {
|
||||||
description =
|
description = description
|
||||||
description.add('and variables').addDescriptionOf(_matchVariables);
|
.add(' and variables that ')
|
||||||
|
.addDescriptionOf(_matchVariables);
|
||||||
}
|
}
|
||||||
return description;
|
return description;
|
||||||
}
|
}
|
||||||
|
@ -36,15 +37,15 @@ class _GeneratesSqlMatcher extends Matcher {
|
||||||
|
|
||||||
mismatchDescription = mismatchDescription.add('generated $sql, which ');
|
mismatchDescription = mismatchDescription.add('generated $sql, which ');
|
||||||
mismatchDescription = _matchSql.describeMismatch(
|
mismatchDescription = _matchSql.describeMismatch(
|
||||||
sql, mismatchDescription, matchState, verbose);
|
sql, mismatchDescription, matchState['sql_match'] as Map, verbose);
|
||||||
}
|
}
|
||||||
if (matchState.containsKey('vars')) {
|
if (matchState.containsKey('vars')) {
|
||||||
final vars = matchState['vars'] as List;
|
final vars = matchState['vars'] as List;
|
||||||
|
|
||||||
mismatchDescription =
|
mismatchDescription =
|
||||||
mismatchDescription.add('used variables $vars, which ');
|
mismatchDescription.add('generated variables $vars, which ');
|
||||||
mismatchDescription = _matchVariables.describeMismatch(
|
mismatchDescription = _matchVariables.describeMismatch(
|
||||||
vars, mismatchDescription, matchState, verbose);
|
vars, mismatchDescription, matchState['vars_match'] as Map, verbose);
|
||||||
}
|
}
|
||||||
return mismatchDescription;
|
return mismatchDescription;
|
||||||
}
|
}
|
||||||
|
@ -52,7 +53,7 @@ class _GeneratesSqlMatcher extends Matcher {
|
||||||
@override
|
@override
|
||||||
bool matches(dynamic item, Map matchState) {
|
bool matches(dynamic item, Map matchState) {
|
||||||
if (item is! Component) {
|
if (item is! Component) {
|
||||||
addStateInfo(matchState, {'wrong_type': true});
|
matchState['wrong_type'] = true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,14 +63,18 @@ class _GeneratesSqlMatcher extends Matcher {
|
||||||
|
|
||||||
var matches = true;
|
var matches = true;
|
||||||
|
|
||||||
if (!_matchSql.matches(ctx.sql, matchState)) {
|
final sqlMatchState = {};
|
||||||
addStateInfo(matchState, {'sql': ctx.sql});
|
if (!_matchSql.matches(ctx.sql, sqlMatchState)) {
|
||||||
|
matchState['sql'] = ctx.sql;
|
||||||
|
matchState['sql_match'] = sqlMatchState;
|
||||||
matches = false;
|
matches = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final argsMatchState = {};
|
||||||
if (_matchVariables != null &&
|
if (_matchVariables != null &&
|
||||||
!_matchVariables.matches(ctx.boundVariables, matchState)) {
|
!_matchVariables.matches(ctx.boundVariables, argsMatchState)) {
|
||||||
addStateInfo(matchState, {'vars': ctx.boundVariables});
|
matchState['vars'] = ctx.boundVariables;
|
||||||
|
matchState['vars_match'] = argsMatchState;
|
||||||
matches = false;
|
matches = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ import 'package:moor/moor.dart';
|
||||||
import 'package:test/test.dart';
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
import '../data/utils/expect_equality.dart';
|
import '../data/utils/expect_equality.dart';
|
||||||
|
import '../data/utils/expect_generated.dart';
|
||||||
|
|
||||||
typedef _Extractor = Expression<int> Function(Expression<DateTime> d);
|
typedef _Extractor = Expression<int> Function(Expression<DateTime> d);
|
||||||
|
|
||||||
|
@ -37,4 +38,13 @@ void main() {
|
||||||
|
|
||||||
expect(ctx.sql, 'strftime(\'%s\', CURRENT_TIMESTAMP) + 10');
|
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', () {
|
group('custom updates', () {
|
||||||
test('execute the correct sql', () async {
|
test('execute the correct sql', () async {
|
||||||
await db.customUpdate('DELETE FROM users');
|
await db.customUpdate('DELETE FROM users');
|
||||||
|
|
Loading…
Reference in New Issue