mirror of https://github.com/AMT-Cheif/drift.git
Throw from TypedResult.read/readTable if there's no data
This commit is contained in:
parent
e97aa8bd09
commit
ab105cf77f
|
@ -5,6 +5,8 @@
|
||||||
- __Breaking__: Removed `QueryEngine`, its methods have been moved to `DatabaseConnectionUser`
|
- __Breaking__: Removed `QueryEngine`, its methods have been moved to `DatabaseConnectionUser`
|
||||||
- __Breaking__: Changed the `args` parameter in `QueryExecutor` methods to `List<Object?>`
|
- __Breaking__: Changed the `args` parameter in `QueryExecutor` methods to `List<Object?>`
|
||||||
- __Breaking__: Removed the second type parameter from `TypedResult.read`
|
- __Breaking__: Removed the second type parameter from `TypedResult.read`
|
||||||
|
- __Breaking__: `TypedResult.read` and `TypedResult.readTable` now throw if the row does not contain
|
||||||
|
the requested table or expression (they used to return `null`).
|
||||||
- __Breaking__: `MoorWebStorage.indexedDbIfSupported` now returns a future
|
- __Breaking__: `MoorWebStorage.indexedDbIfSupported` now returns a future
|
||||||
- Support null safety
|
- Support null safety
|
||||||
- Changed the sql representation of text types from `VARCHAR` to `TEXT`
|
- Changed the sql representation of text types from `VARCHAR` to `TEXT`
|
||||||
|
|
|
@ -126,27 +126,51 @@ String _beginOfSelect(bool distinct) {
|
||||||
/// multiple entities.
|
/// multiple entities.
|
||||||
class TypedResult {
|
class TypedResult {
|
||||||
/// Creates the result from the parsed table data.
|
/// Creates the result from the parsed table data.
|
||||||
TypedResult(this._parsedData, this.rawData, [this._parsedExpressions]);
|
TypedResult(this._parsedData, this.rawData,
|
||||||
|
[this._parsedExpressions = const {}]);
|
||||||
|
|
||||||
final Map<TableInfo, dynamic> _parsedData;
|
final Map<TableInfo, dynamic> _parsedData;
|
||||||
final Map<Expression, dynamic>? _parsedExpressions;
|
final Map<Expression, dynamic> _parsedExpressions;
|
||||||
|
|
||||||
/// The raw data contained in this row.
|
/// The raw data contained in this row.
|
||||||
final QueryRow rawData;
|
final QueryRow rawData;
|
||||||
|
|
||||||
/// Reads all data that belongs to the given [table] from this row.
|
/// Reads all data that belongs to the given [table] from this row.
|
||||||
|
///
|
||||||
|
/// If this row does not contain non-null columns of the [table], this method
|
||||||
|
/// will throw an [ArgumentError]. Use [readTableOrNull] for nullable tables.
|
||||||
D readTable<T extends Table, D extends DataClass>(TableInfo<T, D> table) {
|
D readTable<T extends Table, D extends DataClass>(TableInfo<T, D> table) {
|
||||||
|
if (!_parsedData.containsKey(table)) {
|
||||||
|
throw ArgumentError(
|
||||||
|
'Invalid table passed to readTable: ${table.tableName}. This row '
|
||||||
|
'does not contain values for that table.');
|
||||||
|
}
|
||||||
|
|
||||||
return _parsedData[table] as D;
|
return _parsedData[table] as D;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Reads all data that belongs to the given [table] from this row.
|
||||||
|
///
|
||||||
|
/// Returns `null` if this row does not contain non-null values of the
|
||||||
|
/// [table].
|
||||||
|
///
|
||||||
|
/// See also: [readTable], which throws instead of returning `null`.
|
||||||
|
D? readTableOrNull<T extends Table, D extends DataClass>(
|
||||||
|
TableInfo<T, D> table) {
|
||||||
|
return _parsedData[table] as D?;
|
||||||
|
}
|
||||||
|
|
||||||
/// Reads a single column from an [expr]. The expression must have been added
|
/// Reads a single column from an [expr]. The expression must have been added
|
||||||
/// as a column, for instance via [JoinedSelectStatement.addColumns].
|
/// as a column, for instance via [JoinedSelectStatement.addColumns].
|
||||||
///
|
///
|
||||||
/// To access the underlying columns directly, use [rawData].
|
/// To access the underlying columns directly, use [rawData].
|
||||||
D? read<D>(Expression<D> expr) {
|
D read<D>(Expression<D> expr) {
|
||||||
if (_parsedExpressions != null) {
|
if (_parsedExpressions.containsKey(expr)) {
|
||||||
return _parsedExpressions![expr] as D;
|
return _parsedExpressions[expr] as D;
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
|
throw ArgumentError(
|
||||||
|
'Invalid call to read(): $expr. This result set does not have a column '
|
||||||
|
'for that expression.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -212,8 +212,6 @@ class JoinedSelectStatement<FirstT extends Table, FirstD extends DataClass>
|
||||||
// if all columns of this table are null, skip the table
|
// if all columns of this table are null, skip the table
|
||||||
if (table.$columns.any((c) => row[prefix + c.$name] != null)) {
|
if (table.$columns.any((c) => row[prefix + c.$name] != null)) {
|
||||||
readTables[table] = table.map(row, tablePrefix: table.$tableName);
|
readTables[table] = table.map(row, tablePrefix: table.$tableName);
|
||||||
} else {
|
|
||||||
readTables[table] = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
//@dart=2.9
|
|
||||||
import 'package:mockito/mockito.dart';
|
import 'package:mockito/mockito.dart';
|
||||||
import 'package:moor/moor.dart' hide isNull;
|
import 'package:moor/moor.dart' hide isNull;
|
||||||
import 'package:test/test.dart';
|
import 'package:test/test.dart';
|
||||||
|
@ -6,8 +5,8 @@ import 'data/tables/todos.dart';
|
||||||
import 'data/utils/mocks.dart';
|
import 'data/utils/mocks.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
TodoDb db;
|
late TodoDb db;
|
||||||
MockExecutor executor;
|
late MockExecutor executor;
|
||||||
|
|
||||||
setUp(() {
|
setUp(() {
|
||||||
executor = MockExecutor();
|
executor = MockExecutor();
|
||||||
|
@ -80,7 +79,7 @@ void main() {
|
||||||
verify(executor.runSelect(argThat(contains('DISTINCT')), any));
|
verify(executor.runSelect(argThat(contains('DISTINCT')), any));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('reports null when no data is available', () async {
|
test('throws when no data is available', () async {
|
||||||
when(executor.runSelect(any, any)).thenAnswer((_) {
|
when(executor.runSelect(any, any)).thenAnswer((_) {
|
||||||
return Future.value([
|
return Future.value([
|
||||||
{
|
{
|
||||||
|
@ -101,7 +100,7 @@ void main() {
|
||||||
expect(result, hasLength(1));
|
expect(result, hasLength(1));
|
||||||
|
|
||||||
final row = result.single;
|
final row = result.single;
|
||||||
expect(row.readTable(db.categories), null);
|
expect(() => row.readTable(db.categories), throwsArgumentError);
|
||||||
expect(
|
expect(
|
||||||
row.readTable(db.todosTable),
|
row.readTable(db.todosTable),
|
||||||
TodoEntry(
|
TodoEntry(
|
||||||
|
@ -258,7 +257,7 @@ void main() {
|
||||||
'GROUP BY c.id HAVING COUNT(t.id) >= ?;',
|
'GROUP BY c.id HAVING COUNT(t.id) >= ?;',
|
||||||
[10]));
|
[10]));
|
||||||
|
|
||||||
expect(result.readTable(todos), isNull);
|
expect(result.readTableOrNull(todos), isNull);
|
||||||
expect(
|
expect(
|
||||||
result.readTable(categories),
|
result.readTable(categories),
|
||||||
Category(
|
Category(
|
||||||
|
@ -272,20 +271,27 @@ void main() {
|
||||||
|
|
||||||
test('selectWithoutResults', () async {
|
test('selectWithoutResults', () async {
|
||||||
final avgLength = db.todosTable.content.length.avg();
|
final avgLength = db.todosTable.content.length.avg();
|
||||||
final query = db.selectOnly(db.todosTable)..addColumns([avgLength]);
|
final maxLength = db.todosTable.content.length.max();
|
||||||
|
final minLength = db.todosTable.content.length.min();
|
||||||
|
final query = db.selectOnly(db.todosTable)
|
||||||
|
..addColumns([avgLength, maxLength]);
|
||||||
|
|
||||||
when(executor.runSelect(any, any)).thenAnswer((_) async {
|
when(executor.runSelect(any, any)).thenAnswer((_) async {
|
||||||
return [
|
return [
|
||||||
{'c0': 3.0}
|
{'c0': 3.0, 'c1': null},
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
|
|
||||||
final result = await query.map((row) => row.read(avgLength)).getSingle();
|
final row = await query.getSingle();
|
||||||
|
|
||||||
verify(executor.runSelect(
|
verify(executor.runSelect(
|
||||||
'SELECT AVG(LENGTH(todos.content)) AS "c0" FROM todos;', []));
|
'SELECT AVG(LENGTH(todos.content)) AS "c0", '
|
||||||
|
'MAX(LENGTH(todos.content)) AS "c1" FROM todos;',
|
||||||
|
[]));
|
||||||
|
|
||||||
expect(result, 3.0);
|
expect(row.read(avgLength), 3.0);
|
||||||
|
expect(row.read(maxLength), isNull);
|
||||||
|
expect(() => row.read(minLength), throwsArgumentError);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('join on JoinedSelectStatement', () async {
|
test('join on JoinedSelectStatement', () async {
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
//@dart=2.9
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:mockito/mockito.dart';
|
import 'package:mockito/mockito.dart';
|
||||||
|
@ -23,8 +22,8 @@ final _todoEntry = TodoEntry(
|
||||||
);
|
);
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
TodoDb db;
|
late TodoDb db;
|
||||||
MockExecutor executor;
|
late MockExecutor executor;
|
||||||
|
|
||||||
setUp(() {
|
setUp(() {
|
||||||
executor = MockExecutor();
|
executor = MockExecutor();
|
||||||
|
|
Loading…
Reference in New Issue