Introduce extension for DateTime expressions

This commit is contained in:
Simon Binder 2019-11-09 13:07:49 +01:00
parent 82a6fd9f2d
commit 5b3bcb1916
No known key found for this signature in database
GPG Key ID: 7891917E4147B8C0
4 changed files with 97 additions and 23 deletions

View File

@ -2,35 +2,39 @@ part of '../query_builder.dart';
/// Extracts the (UTC) year from the given expression that resolves
/// to a datetime.
@Deprecated('Use date.year instead')
Expression<int, IntType> year(Expression<DateTime, DateTimeType> date) =>
_StrftimeSingleFieldExpression('%Y', date);
date.year;
/// Extracts the (UTC) month from the given expression that resolves
/// to a datetime.
@Deprecated('Use date.month instead')
Expression<int, IntType> month(Expression<DateTime, DateTimeType> date) =>
_StrftimeSingleFieldExpression('%m', date);
date.month;
/// Extracts the (UTC) day from the given expression that resolves
/// to a datetime.
@Deprecated('Use date.day instead')
Expression<int, IntType> day(Expression<DateTime, DateTimeType> date) =>
_StrftimeSingleFieldExpression('%d', date);
date.day;
/// Extracts the (UTC) hour from the given expression that resolves
/// to a datetime.
@Deprecated('Use date.hour instead')
Expression<int, IntType> hour(Expression<DateTime, DateTimeType> date) =>
_StrftimeSingleFieldExpression('%H', date);
date.hour;
/// Extracts the (UTC) minute from the given expression that resolves
/// to a datetime.
@Deprecated('Use date.minute instead')
Expression<int, IntType> minute(Expression<DateTime, DateTimeType> date) =>
_StrftimeSingleFieldExpression('%M', date);
date.minute;
/// Extracts the (UTC) second from the given expression that resolves
/// to a datetime.
@Deprecated('Use date.second instead')
Expression<int, IntType> second(Expression<DateTime, DateTimeType> date) =>
_StrftimeSingleFieldExpression('%S', date);
// todo: Add difference and unixSeconds method, also convert to extension
date.second;
/// A sql expression that evaluates to the current date represented as a unix
/// timestamp. The hour, minute and second fields will be set to 0.
@ -52,7 +56,38 @@ class _CustomDateTimeExpression
/// Provides expressions to extract information from date time values, or to
/// calculate the difference between datetimes.
extension DateTimeExpressions on Expression<DateTime, DateTimeType> {}
extension DateTimeExpressions on Expression<DateTime, DateTimeType> {
/// Extracts the (UTC) year from `this` datetime expression.
Expression<int, IntType> get year =>
_StrftimeSingleFieldExpression('%Y', this);
/// Extracts the (UTC) month from `this` datetime expression.
Expression<int, IntType> get month =>
_StrftimeSingleFieldExpression('%m', this);
/// Extracts the (UTC) day from `this` datetime expression.
Expression<int, IntType> get day =>
_StrftimeSingleFieldExpression('%d', this);
/// Extracts the (UTC) hour from `this` datetime expression.
Expression<int, IntType> get hour =>
_StrftimeSingleFieldExpression('%H', this);
/// Extracts the (UTC) minute from `this` datetime expression.
Expression<int, IntType> get minute =>
_StrftimeSingleFieldExpression('%M', this);
/// Extracts the (UTC) second from `this` datetime expression.
Expression<int, IntType> get second =>
_StrftimeSingleFieldExpression('%S', this);
/// Returns an expression containing the amount of seconds from the unix
/// epoch (January 1st, 1970) to `this` datetime expression. The datetime is
/// assumed to be in utc.
// for moor, date times are just unix timestamps, so we don't need to rewrite
// anything when converting
Expression<int, IntType> get secondsSinceEpoch => dartCast();
}
/// Expression that extracts components out of a date time by using the builtin
/// sqlite function "strftime" and casting the result to an integer.

View File

@ -26,6 +26,13 @@ abstract class Expression<D, T extends SqlType<D>> implements Component {
Expression<bool, BoolType> equals(D compare) =>
_Comparison.equal(this, Variable<D, T>(compare));
/// Casts this expression to an expression with [D] and [T] parameter without
/// changing what's written with [writeInto]. In particular, using [dartCast]
/// will __NOT__ generate a `CAST` expression in sql.
Expression<D2, T2> dartCast<D2, T2 extends SqlType<D2>>() {
return _CastExpression<D, D2, T, T2>(this);
}
/// Writes this expression into the [GenerationContext], assuming that there's
/// an outer expression with [precedence]. If the [Expression.precedence] of
/// `this` expression is lower, it will be wrapped in
@ -238,3 +245,21 @@ class _UnaryMinus<DT, ST extends SqlType<DT>> extends Expression<DT, ST> {
inner.writeInto(context);
}
}
class _CastExpression<D1, D2, S1 extends SqlType<D1>, S2 extends SqlType<D2>>
extends Expression<D2, S2> {
final Expression<D1, S1> inner;
_CastExpression(this.inner);
@override
Precedence get precedence => inner.precedence;
@override
bool get isLiteral => inner.isLiteral;
@override
void writeInto(GenerationContext context) {
return inner.writeInto(context);
}
}

View File

@ -3,7 +3,8 @@
import 'package:meta/meta.dart';
// hidden because of https://github.com/dart-lang/sdk/issues/39262
import 'package:moor/moor.dart' hide BooleanExpressionOperators;
import 'package:moor/moor.dart'
hide BooleanExpressionOperators, DateTimeExpressions;
import 'package:moor/sqlite_keywords.dart';
import 'package:moor/src/runtime/executor/stream_queries.dart';
import 'package:moor/src/runtime/types/sql_types.dart';

View File

@ -1,27 +1,40 @@
import 'package:moor/moor.dart';
import 'package:test/test.dart';
// ignore_for_file: deprecated_member_use_from_same_package
typedef Expression<int, IntType> _Extractor(
Expression<DateTime, DateTimeType> d);
/// Tests the top level [year], [month], ..., [second] methods
void main() {
final expectedResults = <_Extractor, String>{
year: 'CAST(strftime("%Y", val, "unixepoch") AS INTEGER)',
month: 'CAST(strftime("%m", val, "unixepoch") AS INTEGER)',
day: 'CAST(strftime("%d", val, "unixepoch") AS INTEGER)',
hour: 'CAST(strftime("%H", val, "unixepoch") AS INTEGER)',
minute: 'CAST(strftime("%M", val, "unixepoch") AS INTEGER)',
second: 'CAST(strftime("%S", val, "unixepoch") AS INTEGER)',
};
final column = GeneratedDateTimeColumn('val', null, false);
expectedResults.forEach((key, value) {
test('should extract field', () {
final ctx = GenerationContext(SqlTypeSystem.defaultInstance, null);
key(column).writeInto(ctx);
group('extracting information via top-level method', () {
final expectedResults = <_Extractor, String>{
year: 'CAST(strftime("%Y", val, "unixepoch") AS INTEGER)',
month: 'CAST(strftime("%m", val, "unixepoch") AS INTEGER)',
day: 'CAST(strftime("%d", val, "unixepoch") AS INTEGER)',
hour: 'CAST(strftime("%H", val, "unixepoch") AS INTEGER)',
minute: 'CAST(strftime("%M", val, "unixepoch") AS INTEGER)',
second: 'CAST(strftime("%S", val, "unixepoch") AS INTEGER)',
};
expect(ctx.sql, value);
expectedResults.forEach((key, value) {
test('should extract field', () {
final ctx = GenerationContext(SqlTypeSystem.defaultInstance, null);
key(column).writeInto(ctx);
expect(ctx.sql, value);
});
});
});
test('can cast datetimes to unix timestamps without rewriting', () {
final expr = currentDateAndTime.secondsSinceEpoch + const Constant(10);
final ctx = GenerationContext(SqlTypeSystem.defaultInstance, null);
expr.writeInto(ctx);
expect(ctx.sql, 'strftime(\'%s\', CURRENT_TIMESTAMP) + 10');
});
}