Throw from TypedResult.read/readTable if there's no data

This commit is contained in:
Simon Binder 2021-01-06 12:59:09 +01:00
parent e97aa8bd09
commit ab105cf77f
No known key found for this signature in database
GPG Key ID: 7891917E4147B8C0
5 changed files with 51 additions and 22 deletions

View File

@ -5,6 +5,8 @@
- __Breaking__: Removed `QueryEngine`, its methods have been moved to `DatabaseConnectionUser`
- __Breaking__: Changed the `args` parameter in `QueryExecutor` methods to `List<Object?>`
- __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
- Support null safety
- Changed the sql representation of text types from `VARCHAR` to `TEXT`

View File

@ -126,27 +126,51 @@ String _beginOfSelect(bool distinct) {
/// multiple entities.
class TypedResult {
/// 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<Expression, dynamic>? _parsedExpressions;
final Map<Expression, dynamic> _parsedExpressions;
/// The raw data contained in this row.
final QueryRow rawData;
/// 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) {
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;
}
/// 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
/// as a column, for instance via [JoinedSelectStatement.addColumns].
///
/// To access the underlying columns directly, use [rawData].
D? read<D>(Expression<D> expr) {
if (_parsedExpressions != null) {
return _parsedExpressions![expr] as D;
D read<D>(Expression<D> expr) {
if (_parsedExpressions.containsKey(expr)) {
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.');
}
}

View File

@ -212,8 +212,6 @@ class JoinedSelectStatement<FirstT extends Table, FirstD extends DataClass>
// if all columns of this table are null, skip the table
if (table.$columns.any((c) => row[prefix + c.$name] != null)) {
readTables[table] = table.map(row, tablePrefix: table.$tableName);
} else {
readTables[table] = null;
}
}

View File

@ -1,4 +1,3 @@
//@dart=2.9
import 'package:mockito/mockito.dart';
import 'package:moor/moor.dart' hide isNull;
import 'package:test/test.dart';
@ -6,8 +5,8 @@ import 'data/tables/todos.dart';
import 'data/utils/mocks.dart';
void main() {
TodoDb db;
MockExecutor executor;
late TodoDb db;
late MockExecutor executor;
setUp(() {
executor = MockExecutor();
@ -80,7 +79,7 @@ void main() {
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((_) {
return Future.value([
{
@ -101,7 +100,7 @@ void main() {
expect(result, hasLength(1));
final row = result.single;
expect(row.readTable(db.categories), null);
expect(() => row.readTable(db.categories), throwsArgumentError);
expect(
row.readTable(db.todosTable),
TodoEntry(
@ -258,7 +257,7 @@ void main() {
'GROUP BY c.id HAVING COUNT(t.id) >= ?;',
[10]));
expect(result.readTable(todos), isNull);
expect(result.readTableOrNull(todos), isNull);
expect(
result.readTable(categories),
Category(
@ -272,20 +271,27 @@ void main() {
test('selectWithoutResults', () async {
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 {
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(
'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 {

View File

@ -1,4 +1,3 @@
//@dart=2.9
import 'dart:async';
import 'package:mockito/mockito.dart';
@ -23,8 +22,8 @@ final _todoEntry = TodoEntry(
);
void main() {
TodoDb db;
MockExecutor executor;
late TodoDb db;
late MockExecutor executor;
setUp(() {
executor = MockExecutor();