mirror of https://github.com/AMT-Cheif/drift.git
Add BigInt aggregations, arithemtics and tests
This commit is contained in:
parent
2cfd2b41a3
commit
f72f14f3a5
|
@ -112,6 +112,49 @@ extension ArithmeticAggregates<DT extends num> on Expression<DT?> {
|
|||
_AggregateExpression('TOTAL', [this], filter: filter);
|
||||
}
|
||||
|
||||
/// Provides aggregate functions that are available for BigInt expressions.
|
||||
extension BigIntAggregates<DT extends BigInt> on Expression<DT?> {
|
||||
/// Return the average of all non-null values in this group.
|
||||
///
|
||||
/// {@macro drift_aggregate_filter}
|
||||
Expression<double?> avg({Expression<bool?>? filter}) =>
|
||||
_AggregateExpression('AVG', [this], filter: filter);
|
||||
|
||||
/// Return the maximum of all non-null values in this group.
|
||||
///
|
||||
/// If there are no non-null values in the group, returns null.
|
||||
/// {@macro drift_aggregate_filter}
|
||||
Expression<DT?> max({Expression<bool?>? filter}) =>
|
||||
_AggregateExpression('MAX', [this], filter: filter);
|
||||
|
||||
/// Return the minimum of all non-null values in this group.
|
||||
///
|
||||
/// If there are no non-null values in the group, returns null.
|
||||
/// {@macro drift_aggregate_filter}
|
||||
Expression<DT?> min({Expression<bool?>? filter}) =>
|
||||
_AggregateExpression('MIN', [this], filter: filter);
|
||||
|
||||
/// Calculate the sum of all non-null values in the group.
|
||||
///
|
||||
/// If all values are null, evaluates to null as well. If an overflow occurs
|
||||
/// during calculation, sqlite will terminate the query with an "integer
|
||||
/// overflow" exception.
|
||||
///
|
||||
/// See also [total], which behaves similarly but returns a floating point
|
||||
/// value and doesn't throw an overflow exception.
|
||||
/// {@macro drift_aggregate_filter}
|
||||
Expression<DT?> sum({Expression<bool?>? filter}) =>
|
||||
_AggregateExpression('SUM', [this], filter: filter);
|
||||
|
||||
/// Calculate the sum of all non-null values in the group.
|
||||
///
|
||||
/// If all values in the group are null, [total] returns `0.0`. This function
|
||||
/// uses floating-point values internally.
|
||||
/// {@macro drift_aggregate_filter}
|
||||
Expression<double?> total({Expression<bool?>? filter}) =>
|
||||
_AggregateExpression('TOTAL', [this], filter: filter);
|
||||
}
|
||||
|
||||
/// Provides aggregate functions that are available on date time expressions.
|
||||
extension DateTimeAggregate on Expression<DateTime?> {
|
||||
/// Return the average of all non-null values in this group.
|
||||
|
|
|
@ -41,3 +41,45 @@ extension ArithmeticExpr<DT extends num?> on Expression<DT> {
|
|||
return FunctionCallExpression('round', [this]).cast<int>();
|
||||
}
|
||||
}
|
||||
|
||||
/// Defines the `-`, `*` and `/` operators on sql expressions that support it.
|
||||
extension ArithmeticBigIntExpr<DT extends BigInt?> on Expression<DT> {
|
||||
/// Performs an addition (`this` + [other]) in sql.
|
||||
Expression<DT> operator +(Expression<DT> other) {
|
||||
return _BaseInfixOperator(this, '+', other,
|
||||
precedence: Precedence.plusMinus);
|
||||
}
|
||||
|
||||
/// Performs a subtraction (`this` - [other]) in sql.
|
||||
Expression<DT> operator -(Expression<DT> other) {
|
||||
return _BaseInfixOperator(this, '-', other,
|
||||
precedence: Precedence.plusMinus);
|
||||
}
|
||||
|
||||
/// Returns the negation of this value.
|
||||
Expression<DT> operator -() {
|
||||
return _UnaryMinus(this);
|
||||
}
|
||||
|
||||
/// Performs a multiplication (`this` * [other]) in sql.
|
||||
Expression<DT> operator *(Expression<DT> other) {
|
||||
return _BaseInfixOperator(this, '*', other,
|
||||
precedence: Precedence.mulDivide);
|
||||
}
|
||||
|
||||
/// Performs a division (`this` / [other]) in sql.
|
||||
Expression<DT> operator /(Expression<DT> other) {
|
||||
return _BaseInfixOperator(this, '/', other,
|
||||
precedence: Precedence.mulDivide);
|
||||
}
|
||||
|
||||
/// Calculates the absolute value of this number.
|
||||
Expression<DT> abs() {
|
||||
return FunctionCallExpression('abs', [this]);
|
||||
}
|
||||
|
||||
/// Rounds this expression to the nearest integer.
|
||||
Expression<int?> roundToInt() {
|
||||
return FunctionCallExpression('round', [this]).cast<int>();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,3 +36,8 @@ dependency_overrides:
|
|||
path: ../drift_dev
|
||||
sqlparser:
|
||||
path: ../sqlparser
|
||||
sqlite3:
|
||||
git:
|
||||
url: https://github.com/simolus3/sqlite3.dart
|
||||
ref: main
|
||||
path: sqlite3
|
||||
|
|
|
@ -6,6 +6,8 @@ import '../../test_utils/test_utils.dart';
|
|||
void main() {
|
||||
const i1 = CustomExpression<int>('i1', precedence: Precedence.primary);
|
||||
const i2 = CustomExpression<int>('i2', precedence: Precedence.primary);
|
||||
const b1 = CustomExpression<BigInt>('b1', precedence: Precedence.primary);
|
||||
const b2 = CustomExpression<BigInt>('b2', precedence: Precedence.primary);
|
||||
const s1 = CustomExpression<String>('s1', precedence: Precedence.primary);
|
||||
const s2 = CustomExpression<String>('s2', precedence: Precedence.primary);
|
||||
|
||||
|
@ -21,6 +23,18 @@ void main() {
|
|||
expectNotEquals(i1 + i2, i2 + i1);
|
||||
});
|
||||
|
||||
test('BigInt arithmetic test', () {
|
||||
expect(b1 + b2 * b1, generates('b1 + b2 * b1'));
|
||||
expect(b1 + b2 * b1, generates('b1 + b2 * b1'));
|
||||
expect((b1 + b2) * b1, generates('(b1 + b2) * b1'));
|
||||
expect(b1 - b2, generates('b1 - b2'));
|
||||
expect(b1 - -b2, generates('b1 - -b2'));
|
||||
expect(b1 / b2, generates('b1 / b2'));
|
||||
|
||||
expectEquals(i1 + i2, i1 + i2);
|
||||
expectNotEquals(i1 + i2, i2 + i1);
|
||||
});
|
||||
|
||||
test('string concatenation', () {
|
||||
expect(s1 + s2, generates('s1 || s2'));
|
||||
});
|
||||
|
|
|
@ -32,6 +32,10 @@ void main() {
|
|||
expect(const Variable<dynamic>(123), generates('?', [123]));
|
||||
});
|
||||
|
||||
test('big int', () {
|
||||
expect(Variable<dynamic>(BigInt.from(123)), generates('?', [123]));
|
||||
});
|
||||
|
||||
test('date time', () {
|
||||
const stamp = 12345678;
|
||||
final dateTime = DateTime.fromMillisecondsSinceEpoch(stamp * 1000);
|
||||
|
|
|
@ -40,6 +40,17 @@ void main() {
|
|||
[42, 3.1415, anything]));
|
||||
});
|
||||
|
||||
test('can insert BigInt values', () async {
|
||||
await db.into(db.tableWithoutPK).insert(CustomRowClass.map(42, 0,
|
||||
webSafeInt: BigInt.one, custom: MyCustomObject('custom'))
|
||||
.toInsertable());
|
||||
|
||||
verify(executor.runInsert(
|
||||
'INSERT INTO table_without_p_k '
|
||||
'(not_really_an_id, some_float, custom) VALUES (?, ?, ?)',
|
||||
[42, 3.1415, anything]));
|
||||
});
|
||||
|
||||
test('generates insert or replace statements', () async {
|
||||
await db.into(db.todosTable).insert(
|
||||
TodoEntry(
|
||||
|
|
|
@ -70,6 +70,7 @@ const _uuid = Uuid();
|
|||
class TableWithoutPK extends Table {
|
||||
IntColumn get notReallyAnId => integer()();
|
||||
RealColumn get someFloat => real()();
|
||||
Int64Column get webSafeInt => int64().nullable()();
|
||||
|
||||
TextColumn get custom =>
|
||||
text().map(const CustomConverter()).clientDefault(_uuid.v4)();
|
||||
|
@ -78,18 +79,20 @@ class TableWithoutPK extends Table {
|
|||
class CustomRowClass {
|
||||
final int notReallyAnId;
|
||||
final double anotherName;
|
||||
final BigInt? webSafeInt;
|
||||
final MyCustomObject custom;
|
||||
|
||||
final String? notFromDb;
|
||||
|
||||
double get someFloat => anotherName;
|
||||
|
||||
CustomRowClass._(
|
||||
this.notReallyAnId, this.anotherName, this.custom, this.notFromDb);
|
||||
CustomRowClass._(this.notReallyAnId, this.anotherName, this.webSafeInt,
|
||||
this.custom, this.notFromDb);
|
||||
|
||||
factory CustomRowClass.map(int notReallyAnId, double someFloat,
|
||||
{required MyCustomObject custom, String? notFromDb}) {
|
||||
return CustomRowClass._(notReallyAnId, someFloat, custom, notFromDb);
|
||||
{required MyCustomObject custom, BigInt? webSafeInt, String? notFromDb}) {
|
||||
return CustomRowClass._(
|
||||
notReallyAnId, someFloat, webSafeInt, custom, notFromDb);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1053,26 +1053,31 @@ class $SharedTodosTable extends SharedTodos
|
|||
class TableWithoutPKCompanion extends UpdateCompanion<CustomRowClass> {
|
||||
final Value<int> notReallyAnId;
|
||||
final Value<double> someFloat;
|
||||
final Value<BigInt?> webSafeInt;
|
||||
final Value<MyCustomObject> custom;
|
||||
const TableWithoutPKCompanion({
|
||||
this.notReallyAnId = const Value.absent(),
|
||||
this.someFloat = const Value.absent(),
|
||||
this.webSafeInt = const Value.absent(),
|
||||
this.custom = const Value.absent(),
|
||||
});
|
||||
TableWithoutPKCompanion.insert({
|
||||
required int notReallyAnId,
|
||||
required double someFloat,
|
||||
this.webSafeInt = const Value.absent(),
|
||||
this.custom = const Value.absent(),
|
||||
}) : notReallyAnId = Value(notReallyAnId),
|
||||
someFloat = Value(someFloat);
|
||||
static Insertable<CustomRowClass> createCustom({
|
||||
Expression<int>? notReallyAnId,
|
||||
Expression<double>? someFloat,
|
||||
Expression<BigInt?>? webSafeInt,
|
||||
Expression<MyCustomObject>? custom,
|
||||
}) {
|
||||
return RawValuesInsertable({
|
||||
if (notReallyAnId != null) 'not_really_an_id': notReallyAnId,
|
||||
if (someFloat != null) 'some_float': someFloat,
|
||||
if (webSafeInt != null) 'web_safe_int': webSafeInt,
|
||||
if (custom != null) 'custom': custom,
|
||||
});
|
||||
}
|
||||
|
@ -1080,10 +1085,12 @@ class TableWithoutPKCompanion extends UpdateCompanion<CustomRowClass> {
|
|||
TableWithoutPKCompanion copyWith(
|
||||
{Value<int>? notReallyAnId,
|
||||
Value<double>? someFloat,
|
||||
Value<BigInt?>? webSafeInt,
|
||||
Value<MyCustomObject>? custom}) {
|
||||
return TableWithoutPKCompanion(
|
||||
notReallyAnId: notReallyAnId ?? this.notReallyAnId,
|
||||
someFloat: someFloat ?? this.someFloat,
|
||||
webSafeInt: webSafeInt ?? this.webSafeInt,
|
||||
custom: custom ?? this.custom,
|
||||
);
|
||||
}
|
||||
|
@ -1097,6 +1104,9 @@ class TableWithoutPKCompanion extends UpdateCompanion<CustomRowClass> {
|
|||
if (someFloat.present) {
|
||||
map['some_float'] = Variable<double>(someFloat.value);
|
||||
}
|
||||
if (webSafeInt.present) {
|
||||
map['web_safe_int'] = Variable<BigInt?>(webSafeInt.value);
|
||||
}
|
||||
if (custom.present) {
|
||||
final converter = $TableWithoutPKTable.$converter0;
|
||||
map['custom'] = Variable<String>(converter.mapToSql(custom.value)!);
|
||||
|
@ -1109,6 +1119,7 @@ class TableWithoutPKCompanion extends UpdateCompanion<CustomRowClass> {
|
|||
return (StringBuffer('TableWithoutPKCompanion(')
|
||||
..write('notReallyAnId: $notReallyAnId, ')
|
||||
..write('someFloat: $someFloat, ')
|
||||
..write('webSafeInt: $webSafeInt, ')
|
||||
..write('custom: $custom')
|
||||
..write(')'))
|
||||
.toString();
|
||||
|
@ -1126,6 +1137,7 @@ class _$CustomRowClassInsertable implements Insertable<CustomRowClass> {
|
|||
notReallyAnId: Value(_object.notReallyAnId),
|
||||
someFloat: Value(_object.someFloat),
|
||||
custom: Value(_object.custom),
|
||||
webSafeInt: Value(_object.webSafeInt),
|
||||
).toColumns(false);
|
||||
}
|
||||
}
|
||||
|
@ -1153,6 +1165,11 @@ class $TableWithoutPKTable extends TableWithoutPK
|
|||
late final GeneratedColumn<double?> someFloat = GeneratedColumn<double?>(
|
||||
'some_float', aliasedName, false,
|
||||
type: const RealType(), requiredDuringInsert: true);
|
||||
final VerificationMeta _webSafeIntMeta = const VerificationMeta('webSafeInt');
|
||||
@override
|
||||
late final GeneratedColumn<BigInt?> webSafeInt = GeneratedColumn<BigInt?>(
|
||||
'web_safe_int', aliasedName, true,
|
||||
type: const BigIntType(), requiredDuringInsert: false);
|
||||
final VerificationMeta _customMeta = const VerificationMeta('custom');
|
||||
@override
|
||||
late final GeneratedColumnWithTypeConverter<MyCustomObject, String?> custom =
|
||||
|
@ -1162,7 +1179,8 @@ class $TableWithoutPKTable extends TableWithoutPK
|
|||
clientDefault: _uuid.v4)
|
||||
.withConverter<MyCustomObject>($TableWithoutPKTable.$converter0);
|
||||
@override
|
||||
List<GeneratedColumn> get $columns => [notReallyAnId, someFloat, custom];
|
||||
List<GeneratedColumn> get $columns =>
|
||||
[notReallyAnId, someFloat, webSafeInt, custom];
|
||||
@override
|
||||
String get aliasedName => _alias ?? 'table_without_p_k';
|
||||
@override
|
||||
|
@ -1186,6 +1204,12 @@ class $TableWithoutPKTable extends TableWithoutPK
|
|||
} else if (isInserting) {
|
||||
context.missing(_someFloatMeta);
|
||||
}
|
||||
if (data.containsKey('web_safe_int')) {
|
||||
context.handle(
|
||||
_webSafeIntMeta,
|
||||
webSafeInt.isAcceptableOrUnknown(
|
||||
data['web_safe_int']!, _webSafeIntMeta));
|
||||
}
|
||||
context.handle(_customMeta, const VerificationResult.success());
|
||||
return context;
|
||||
}
|
||||
|
@ -1202,6 +1226,8 @@ class $TableWithoutPKTable extends TableWithoutPK
|
|||
.mapFromDatabaseResponse(data['${effectivePrefix}some_float'])!,
|
||||
custom: $TableWithoutPKTable.$converter0.mapToDart(const StringType()
|
||||
.mapFromDatabaseResponse(data['${effectivePrefix}custom']))!,
|
||||
webSafeInt: const BigIntType()
|
||||
.mapFromDatabaseResponse(data['${effectivePrefix}web_safe_int']),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -136,4 +136,14 @@ void main() {
|
|||
),
|
||||
completes);
|
||||
});
|
||||
|
||||
test('insert and select BigInt', () async {
|
||||
await db.into(db.tableWithoutPK).insert(CustomRowClass.map(1, 0.0,
|
||||
webSafeInt: BigInt.parse('9223372036854775807'),
|
||||
custom: MyCustomObject('custom'))
|
||||
.toInsertable());
|
||||
|
||||
final row = await db.select(db.tableWithoutPK).getSingle();
|
||||
expect(row.webSafeInt, BigInt.parse('9223372036854775807'));
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue