mirror of https://github.com/AMT-Cheif/drift.git
Override hashCode and operator == in common expressions
This commit is contained in:
parent
7609df34f0
commit
0a96769dcb
|
@ -55,4 +55,12 @@ class _NotExpression extends Expression<bool, BoolType> {
|
|||
context.buffer.write('NOT ');
|
||||
writeInner(context, inner);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => inner.hashCode << 1;
|
||||
|
||||
@override
|
||||
bool operator ==(other) {
|
||||
return other is _NotExpression && other.inner == inner;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -104,4 +104,17 @@ class _BetweenExpression extends Expression<bool, BoolType> {
|
|||
context.buffer.write(' AND ');
|
||||
writeInner(context, higher);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => $mrjf($mrjc(target.hashCode,
|
||||
$mrjc(lower.hashCode, $mrjc(higher.hashCode, not.hashCode))));
|
||||
|
||||
@override
|
||||
bool operator ==(other) {
|
||||
return other is _BetweenExpression &&
|
||||
other.target == target &&
|
||||
other.not == not &&
|
||||
other.lower == lower &&
|
||||
other.higher == higher;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,4 +16,14 @@ class CustomExpression<D, S extends SqlType<D>> extends Expression<D, S> {
|
|||
|
||||
@override
|
||||
void writeInto(GenerationContext context) => context.buffer.write(content);
|
||||
|
||||
@override
|
||||
int get hashCode => content.hashCode * 3;
|
||||
|
||||
@override
|
||||
bool operator ==(other) {
|
||||
return other.runtimeType == runtimeType &&
|
||||
// ignore: test_types_in_equals
|
||||
(other as CustomExpression).content == content;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -103,4 +103,14 @@ class _StrftimeSingleFieldExpression extends Expression<int, IntType> {
|
|||
date.writeInto(context);
|
||||
context.buffer.write(', "unixepoch") AS INTEGER)');
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => $mrjf($mrjc(format.hashCode, date.hashCode));
|
||||
|
||||
@override
|
||||
bool operator ==(other) {
|
||||
return other is _StrftimeSingleFieldExpression &&
|
||||
other.format == format &&
|
||||
other.date == date;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
part of '../query_builder.dart';
|
||||
|
||||
const _equality = ListEquality();
|
||||
|
||||
/// Any sql expression that evaluates to some generic value. This does not
|
||||
/// include queries (which might evaluate to multiple values) but individual
|
||||
/// columns, functions and operators.
|
||||
|
@ -38,13 +40,13 @@ abstract class Expression<D, T extends SqlType<D>> implements Component {
|
|||
/// An expression that is true if `this` resolves to any of the values in
|
||||
/// [values].
|
||||
Expression<bool, BoolType> isIn(Iterable<D> values) {
|
||||
return _InExpression(this, values, false);
|
||||
return _InExpression(this, values.toList(), false);
|
||||
}
|
||||
|
||||
/// An expression that is true if `this` does not resolve to any of the values
|
||||
/// in [values].
|
||||
Expression<bool, BoolType> isNotIn(Iterable<D> values) {
|
||||
return _InExpression(this, values, true);
|
||||
return _InExpression(this, values.toList(), true);
|
||||
}
|
||||
|
||||
/// Writes this expression into the [GenerationContext], assuming that there's
|
||||
|
@ -175,6 +177,18 @@ abstract class _InfixOperator<D, T extends SqlType<D>>
|
|||
context.writeWhitespace();
|
||||
writeInner(context, right);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
$mrjf($mrjc(left.hashCode, $mrjc(right.hashCode, operator.hashCode)));
|
||||
|
||||
@override
|
||||
bool operator ==(other) {
|
||||
return other is _InfixOperator &&
|
||||
other.left == left &&
|
||||
other.right == right &&
|
||||
other.operator == operator;
|
||||
}
|
||||
}
|
||||
|
||||
class _BaseInfixOperator<D, T extends SqlType<D>> extends _InfixOperator<D, T> {
|
||||
|
@ -263,6 +277,14 @@ class _UnaryMinus<DT, ST extends SqlType<DT>> extends Expression<DT, ST> {
|
|||
context.buffer.write('-');
|
||||
inner.writeInto(context);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => inner.hashCode * 5;
|
||||
|
||||
@override
|
||||
bool operator ==(other) {
|
||||
return other is _UnaryMinus && other.inner == inner;
|
||||
}
|
||||
}
|
||||
|
||||
class _CastExpression<D1, D2, S1 extends SqlType<D1>, S2 extends SqlType<D2>>
|
||||
|
@ -281,6 +303,14 @@ class _CastExpression<D1, D2, S1 extends SqlType<D1>, S2 extends SqlType<D2>>
|
|||
void writeInto(GenerationContext context) {
|
||||
return inner.writeInto(context);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => inner.hashCode * 7;
|
||||
|
||||
@override
|
||||
bool operator ==(other) {
|
||||
return other is _CastExpression && other.inner == inner;
|
||||
}
|
||||
}
|
||||
|
||||
class _FunctionCallExpression<R, S extends SqlType<R>>
|
||||
|
@ -308,4 +338,15 @@ class _FunctionCallExpression<R, S extends SqlType<R>>
|
|||
|
||||
context.buffer.write(')');
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
$mrjf($mrjc(functionName.hashCode, _equality.hash(arguments)));
|
||||
|
||||
@override
|
||||
bool operator ==(other) {
|
||||
return other is _FunctionCallExpression &&
|
||||
other.functionName == functionName &&
|
||||
_equality.equals(other.arguments, arguments);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ Expression<bool, BoolType> isNotIn<X extends SqlType<T>, T>(
|
|||
class _InExpression<X extends SqlType<T>, T>
|
||||
extends Expression<bool, BoolType> {
|
||||
final Expression<T, X> _expression;
|
||||
final Iterable<T> _values;
|
||||
final List<T> _values;
|
||||
final bool _not;
|
||||
|
||||
@override
|
||||
|
@ -57,4 +57,16 @@ class _InExpression<X extends SqlType<T>, T>
|
|||
|
||||
context.buffer.write(')');
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => $mrjf($mrjc(
|
||||
_expression.hashCode, $mrjc(_equality.hash(_values), _not.hashCode)));
|
||||
|
||||
@override
|
||||
bool operator ==(other) {
|
||||
return other is _InExpression &&
|
||||
other._expression == _expression &&
|
||||
other._values == _values &&
|
||||
other._not == _not;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,4 +30,14 @@ class _NullCheck extends Expression<bool, BoolType> {
|
|||
}
|
||||
context.buffer.write('NULL');
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => $mrjf($mrjc(_inner.hashCode, _isNull.hashCode));
|
||||
|
||||
@override
|
||||
bool operator ==(other) {
|
||||
return other is _NullCheck &&
|
||||
other._inner == _inner &&
|
||||
other._isNull == _isNull;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -72,6 +72,16 @@ class _LikeOperator extends Expression<bool, BoolType> {
|
|||
context.buffer.write(' LIKE ');
|
||||
writeInner(context, regex);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => $mrjf($mrjc(target.hashCode, regex.hashCode));
|
||||
|
||||
@override
|
||||
bool operator ==(other) {
|
||||
return other is _LikeOperator &&
|
||||
other.target == target &&
|
||||
other.regex == regex;
|
||||
}
|
||||
}
|
||||
|
||||
/// Builtin collating functions from sqlite.
|
||||
|
@ -116,6 +126,16 @@ class _CollateOperator extends Expression<String, StringType> {
|
|||
context.buffer..write(' COLLATE ')..write(_operatorNames[collate]);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => $mrjf($mrjc(inner.hashCode, collate.hashCode));
|
||||
|
||||
@override
|
||||
bool operator ==(other) {
|
||||
return other is _CollateOperator &&
|
||||
other.inner == inner &&
|
||||
other.collate == collate;
|
||||
}
|
||||
|
||||
static const Map<Collate, String> _operatorNames = {
|
||||
Collate.binary: 'BINARY',
|
||||
Collate.noCase: 'NOCASE',
|
||||
|
|
|
@ -6,6 +6,9 @@ class Variable<T, S extends SqlType<T>> extends Expression<T, S> {
|
|||
/// The Dart value that will be sent to the database
|
||||
final T value;
|
||||
|
||||
// note that we keep the identity hash/equals here because each variable would
|
||||
// get its own index in sqlite and is thus different.
|
||||
|
||||
@override
|
||||
final Precedence precedence = Precedence.primary;
|
||||
|
||||
|
@ -82,4 +85,14 @@ class Constant<T, S extends SqlType<T>> extends Expression<T, S> {
|
|||
final type = context.typeSystem.forDartType<T>();
|
||||
context.buffer.write(type.mapToSqlConstant(value));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => value.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(other) {
|
||||
return other.runtimeType == runtimeType &&
|
||||
// ignore: test_types_in_equals
|
||||
(other as Constant<T, S>).value == value;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// Mega compilation unit that includes all Dart apis related to generating SQL
|
||||
// at runtime.
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
// hidden because of https://github.com/dart-lang/sdk/issues/39262
|
||||
import 'package:moor/moor.dart'
|
||||
|
|
|
@ -101,6 +101,18 @@ abstract class GeneratedColumn<T, S extends SqlType<T>> extends Column<T, S> {
|
|||
bool get isRequired {
|
||||
return !$nullable && defaultValue == null;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => $mrjf($mrjc(tableName.hashCode, $name.hashCode));
|
||||
|
||||
@override
|
||||
bool operator ==(other) {
|
||||
if (other.runtimeType != runtimeType) return false;
|
||||
|
||||
// ignore: test_types_in_equals
|
||||
final typedOther = other as GeneratedColumn;
|
||||
return typedOther.tableName == tableName && typedOther.$name == $name;
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementation for [TextColumn].
|
||||
|
|
|
@ -15,7 +15,7 @@ mixin TableInfo<TableDsl extends Table, D extends DataClass> on Table {
|
|||
/// also contains auto-increment integers, which are primary key by default.
|
||||
Set<GeneratedColumn> get $primaryKey => null;
|
||||
|
||||
// ensure the primaryKey getter is consistent with $primarKey, which can
|
||||
// ensure the primaryKey getter is consistent with $primaryKey, which can
|
||||
// contain additional columns.
|
||||
@override
|
||||
Set<Column> get primaryKey => $primaryKey;
|
||||
|
|
|
@ -73,6 +73,11 @@ class CustomSelectStatement with Selectable<QueryRow> {
|
|||
/// For custom select statements, represents a row in the result set.
|
||||
class QueryRow {
|
||||
/// The raw data in this row.
|
||||
///
|
||||
/// Note that the values in this map aren't mapped to Dart yet. For instance,
|
||||
/// a [DateTime] would be stored as an [int] in [data] because that's the way
|
||||
/// it's stored in the database. To read a value, use any of the [read]
|
||||
/// methods.
|
||||
final Map<String, dynamic> data;
|
||||
final QueryEngine _db;
|
||||
|
||||
|
|
|
@ -77,6 +77,7 @@ class SimpleSelectStatement<T extends Table, D extends DataClass>
|
|||
return statement;
|
||||
}
|
||||
|
||||
/// {@macro moor_select_addColumns}
|
||||
JoinedSelectStatement addColumns(List<Expression> expressions) {
|
||||
return join(const [])..addColumns(expressions);
|
||||
}
|
||||
|
@ -133,8 +134,10 @@ class TypedResult {
|
|||
return _parsedData[table] as D;
|
||||
}
|
||||
|
||||
/// Reads a single column from an [expr].
|
||||
/// todo expand documentation
|
||||
/// Reads a single column from an [expr]. The expression must have been added
|
||||
/// as a column, for instance via [JoinedSelectStatement.addColumns].
|
||||
///
|
||||
/// To access the underlying columns directly, use
|
||||
D read<D, T extends SqlType<D>>(Expression<D, T> expr) {
|
||||
if (_parsedExpressions != null) {
|
||||
return _parsedExpressions[expr] as D;
|
||||
|
|
|
@ -109,14 +109,25 @@ class JoinedSelectStatement<FirstT extends Table, FirstD extends DataClass>
|
|||
orderByExpr = OrderBy(terms);
|
||||
}
|
||||
|
||||
/// {@template moor_select_addColumns}
|
||||
/// Adds a custom expression to the query.
|
||||
///
|
||||
/// The database will evaluate the [Expression] for each row found for this
|
||||
/// query. The value of the expression can be extracted from the [TypedResult]
|
||||
/// by passing it to [TypedResult.read].
|
||||
///
|
||||
/// As an example, we could calculate the length of a column on the database:
|
||||
/// ```dart
|
||||
/// final contentLength = todos.content.length;
|
||||
/// final results = await select(todos).addColumns([contentLength]).get();
|
||||
///
|
||||
/// // we can now read the result of a column added to addColumns
|
||||
/// final lengthOfFirst = results.first.read(contentLength);
|
||||
/// ```
|
||||
///
|
||||
/// See also:
|
||||
/// - The docs on expressions: https://moor.simonbinder.eu/docs/getting-started/expressions/
|
||||
/// {@endtemplate}
|
||||
void addColumns(Iterable<Expression> expressions) {
|
||||
_selectedColumns.addAll(expressions);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
import 'package:test/test.dart';
|
||||
|
||||
void expectEquals(dynamic a, dynamic expected) {
|
||||
expect(a, equals(expected));
|
||||
expect(a.hashCode, equals(expected.hashCode));
|
||||
}
|
||||
|
||||
void expectNotEquals(dynamic a, dynamic expected) {
|
||||
expect(a, isNot(equals(expected)));
|
||||
expect(a.hashCode, isNot(equals(expected.hashCode)));
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:moor/moor.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import '../data/utils/expect_equality.dart';
|
||||
import '../data/utils/expect_generated.dart';
|
||||
|
||||
void main() {
|
||||
|
@ -16,6 +17,9 @@ void main() {
|
|||
(i1 - i2).expectGenerates('i1 - i2');
|
||||
(i1 - -i2).expectGenerates('i1 - -i2');
|
||||
(i1 / i2).expectGenerates('i1 / i2');
|
||||
|
||||
expectEquals(i1 + i2, i1 + i2);
|
||||
expectNotEquals(i1 + i2, i2 + i1);
|
||||
});
|
||||
|
||||
test('string concatenation', () {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:moor/moor.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import '../data/utils/expect_equality.dart';
|
||||
import '../data/utils/expect_generated.dart';
|
||||
|
||||
// ignore_for_file: deprecated_member_use_from_same_package
|
||||
|
@ -13,11 +14,17 @@ void main() {
|
|||
(a | b).expectGenerates('a OR b');
|
||||
(a & b).expectGenerates('a AND b');
|
||||
a.not().expectGenerates('NOT a');
|
||||
|
||||
expectEquals(a & b, a & b);
|
||||
expectNotEquals(a | b, b | a);
|
||||
});
|
||||
|
||||
test('boolean expressions via top-level methods', () {
|
||||
or(a, b).expectGenerates('a OR b');
|
||||
and(a, b).expectGenerates('a AND b');
|
||||
not(a).expectGenerates('NOT a');
|
||||
|
||||
expectEquals(not(a), not(a));
|
||||
expectNotEquals(not(a), not(b));
|
||||
});
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ import 'package:moor/moor.dart';
|
|||
import 'package:test/test.dart';
|
||||
|
||||
import '../data/tables/todos.dart';
|
||||
import '../data/utils/expect_equality.dart';
|
||||
|
||||
void main() {
|
||||
final expression = GeneratedIntColumn('col', null, false);
|
||||
|
@ -31,6 +32,8 @@ void main() {
|
|||
fn(compare).writeInto(ctx);
|
||||
|
||||
expect(ctx.sql, 'col $value compare');
|
||||
|
||||
expectEquals(fn(compare), fn(compare));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -2,6 +2,7 @@ import 'package:moor/moor.dart';
|
|||
import 'package:test/test.dart';
|
||||
|
||||
import '../data/tables/todos.dart';
|
||||
import '../data/utils/expect_equality.dart';
|
||||
|
||||
void main() {
|
||||
group('string literals', () {
|
||||
|
@ -17,6 +18,16 @@ void main() {
|
|||
testStringMapping('\\\$"', "'\\\$\"'");
|
||||
});
|
||||
});
|
||||
|
||||
test('constant hash and equals', () {
|
||||
// these shouldn't be identical, so no const constructor
|
||||
final first = Constant('hi'); // ignore: prefer_const_constructors
|
||||
final alsoFirst = Constant('hi'); // ignore: prefer_const_constructors
|
||||
final second = const Constant(3);
|
||||
|
||||
expectEquals(first, alsoFirst);
|
||||
expectNotEquals(first, second);
|
||||
});
|
||||
}
|
||||
|
||||
void testStringMapping(String dart, String expectedLiteral) {
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import 'package:moor/moor.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import '../data/utils/expect_equality.dart';
|
||||
|
||||
// ignore_for_file: deprecated_member_use_from_same_package
|
||||
|
||||
typedef Expression<int, IntType> _Extractor(
|
||||
|
@ -26,6 +28,8 @@ void main() {
|
|||
key(column).writeInto(ctx);
|
||||
|
||||
expect(ctx.sql, value);
|
||||
|
||||
expectEquals(key(column), key(column));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -3,6 +3,7 @@ import 'package:moor/moor.dart' as moor;
|
|||
import 'package:test/test.dart';
|
||||
|
||||
import '../data/tables/todos.dart';
|
||||
import '../data/utils/expect_equality.dart';
|
||||
|
||||
void main() {
|
||||
final innerExpression = GeneratedTextColumn('name', null, true);
|
||||
|
@ -23,5 +24,7 @@ void main() {
|
|||
expr.writeInto(context);
|
||||
|
||||
expect(context.sql, 'name IS NOT NULL');
|
||||
|
||||
expectEquals(expr, moor.isNotNull(innerExpression));
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue