Added Optional package

This commit is contained in:
thomashii@dukefirehawk.com 2021-05-03 21:27:12 +08:00
parent 7644d8c2d2
commit d241d7ca77
11 changed files with 74 additions and 56 deletions

View file

@ -15,7 +15,9 @@ void main() async {
..join('companies', 'company_id', 'id'); ..join('companies', 'company_id', 'id');
var richPerson = await query.getOne(_FakeExecutor()); var richPerson = await query.getOne(_FakeExecutor());
print(richPerson?.toJson()); if (richPerson.isPresent) {
print(richPerson.first.toJson());
}
} }
class _FakeExecutor extends QueryExecutor { class _FakeExecutor extends QueryExecutor {
@ -24,7 +26,7 @@ class _FakeExecutor extends QueryExecutor {
@override @override
Future<List<List>> query( Future<List<List>> query(
String tableName, String? query, Map<String, dynamic> substitutionValues, String tableName, String? query, Map<String, dynamic> substitutionValues,
[returningFields]) async { [returningFields = const []]) async {
var now = DateTime.now(); var now = DateTime.now();
print( print(
'_FakeExecutor received query: $query and values: $substitutionValues'); '_FakeExecutor received query: $query and values: $substitutionValues');

View file

@ -6,6 +6,7 @@ import 'query_base.dart';
import 'query_executor.dart'; import 'query_executor.dart';
import 'query_values.dart'; import 'query_values.dart';
import 'query_where.dart'; import 'query_where.dart';
import 'package:optional/optional.dart';
/// A SQL `SELECT` query builder. /// A SQL `SELECT` query builder.
abstract class Query<T, Where extends QueryWhere?> extends QueryBase<T> { abstract class Query<T, Where extends QueryWhere?> extends QueryBase<T> {
@ -125,7 +126,7 @@ abstract class Query<T, Where extends QueryWhere?> extends QueryBase<T> {
} }
String _joinAlias(Set<String>? trampoline) { String _joinAlias(Set<String>? trampoline) {
int i = _joins.length; var i = _joins.length;
while (true) { while (true) {
var a = 'a$i'; var a = 'a$i';
@ -137,13 +138,13 @@ abstract class Query<T, Where extends QueryWhere?> extends QueryBase<T> {
} }
} }
String? Function() _compileJoin(tableName, Set<String>? trampoline) { String? Function() _compileJoin(tableName, Set<String> trampoline) {
if (tableName is String) { if (tableName is String) {
return () => tableName; return () => tableName;
} else if (tableName is Query) { } else if (tableName is Query) {
return () { return () {
var c = tableName.compile(trampoline); var c = tableName.compile(trampoline);
if (c == null) return c; //if (c == null) return c;
return '($c)'; return '($c)';
}; };
} else { } else {
@ -160,7 +161,7 @@ abstract class Query<T, Where extends QueryWhere?> extends QueryBase<T> {
String foreignKey, String foreignKey,
String op, String op,
List<String> additionalFields) { List<String> additionalFields) {
trampoline ??= Set(); trampoline ??= <String>{};
// Pivot tables guard against ambiguous fields by excluding tables // Pivot tables guard against ambiguous fields by excluding tables
// that have already been queried in this scope. // that have already been queried in this scope.
@ -170,19 +171,17 @@ abstract class Query<T, Where extends QueryWhere?> extends QueryBase<T> {
} }
var to = _compileJoin(tableName, trampoline); var to = _compileJoin(tableName, trampoline);
if (to != null) { var alias = _joinAlias(trampoline);
var alias = _joinAlias(trampoline); if (tableName is Query) {
if (tableName is Query) { for (var field in tableName.fields) {
for (var field in tableName.fields) { tableName.aliases[field] = '${alias}_$field';
tableName.aliases[field] = '${alias}_$field';
}
} }
_joins.add(JoinBuilder(type, this, to, localKey, foreignKey,
op: op,
alias: alias,
additionalFields: additionalFields,
aliasAllFields: tableName is Query));
} }
_joins.add(JoinBuilder(type, this, to, localKey, foreignKey,
op: op,
alias: alias,
additionalFields: additionalFields,
aliasAllFields: tableName is Query));
} }
/// Execute an `INNER JOIN` against another table. /// Execute an `INNER JOIN` against another table.
@ -231,14 +230,15 @@ abstract class Query<T, Where extends QueryWhere?> extends QueryBase<T> {
} }
@override @override
String? compile(Set<String>? trampoline, String compile(Set<String> trampoline,
{bool includeTableName = false, {bool includeTableName = false,
String? preamble, String? preamble,
bool withFields = true, bool withFields = true,
String? fromQuery}) { String? fromQuery}) {
// One table MAY appear multiple times in a query. // One table MAY appear multiple times in a query.
if (!canCompile(trampoline)) { if (!canCompile(trampoline)) {
return null; //return null;
throw Exception('One table appear multiple times in a query');
} }
includeTableName = includeTableName || _joins.isNotEmpty; includeTableName = includeTableName || _joins.isNotEmpty;
@ -320,7 +320,7 @@ abstract class Query<T, Where extends QueryWhere?> extends QueryBase<T> {
} }
@override @override
Future<T?> getOne(QueryExecutor executor) { Future<Optional<T>> getOne(QueryExecutor executor) {
//limit(1); //limit(1);
return super.getOne(executor); return super.getOne(executor);
} }
@ -358,7 +358,7 @@ abstract class Query<T, Where extends QueryWhere?> extends QueryBase<T> {
} else { } else {
// TODO: How to do this in a non-Postgres DB? // TODO: How to do this in a non-Postgres DB?
var returning = fields.map(adornWithTableName).join(', '); var returning = fields.map(adornWithTableName).join(', ');
var sql = compile({})!; var sql = compile({});
sql = 'WITH $tableName as ($insertion RETURNING $returning) ' + sql; sql = 'WITH $tableName as ($insertion RETURNING $returning) ' + sql;
return executor return executor
.query(tableName, sql, substitutionValues) .query(tableName, sql, substitutionValues)
@ -381,7 +381,7 @@ abstract class Query<T, Where extends QueryWhere?> extends QueryBase<T> {
if (_limit != null) updateSql.write(' LIMIT $_limit'); if (_limit != null) updateSql.write(' LIMIT $_limit');
var returning = fields.map(adornWithTableName).join(', '); var returning = fields.map(adornWithTableName).join(', ');
var sql = compile({})!; var sql = compile({});
sql = 'WITH $tableName as ($updateSql RETURNING $returning) ' + sql; sql = 'WITH $tableName as ($updateSql RETURNING $returning) ' + sql;
return executor return executor

View file

@ -1,6 +1,7 @@
import 'dart:async'; import 'dart:async';
import 'query_executor.dart'; import 'query_executor.dart';
import 'union.dart'; import 'union.dart';
import 'package:optional/optional.dart';
/// A base class for objects that compile to SQL queries, typically within an ORM. /// A base class for objects that compile to SQL queries, typically within an ORM.
abstract class QueryBase<T> { abstract class QueryBase<T> {
@ -32,9 +33,9 @@ abstract class QueryBase<T> {
} }
}).join(', '); }).join(', ');
String? compile(Set<String> trampoline, String compile(Set<String> trampoline,
{bool includeTableName = false, {bool includeTableName = false,
String? preamble, String preamble = '',
bool withFields = true}); bool withFields = true});
T deserialize(List row); T deserialize(List row);
@ -46,8 +47,10 @@ abstract class QueryBase<T> {
.then((it) => it.map(deserialize).toList()); .then((it) => it.map(deserialize).toList());
} }
Future<T?> getOne(QueryExecutor executor) { Future<Optional<T>> getOne(QueryExecutor executor) {
return get(executor).then((it) => it.isEmpty ? null : it.first); //return get(executor).then((it) => it.isEmpty ? : it.first);
return get(executor).then(
(it) => it.isEmpty ? Optional.empty() : Optional.ofNullable(it.first));
} }
Union<T> union(QueryBase<T> other) { Union<T> union(QueryBase<T> other) {

View file

@ -8,8 +8,8 @@ abstract class QueryExecutor {
/// Executes a single query. /// Executes a single query.
Future<List<List>> query( Future<List<List>> query(
String tableName, String? query, Map<String, dynamic> substitutionValues, String tableName, String query, Map<String, dynamic> substitutionValues,
[List<String>? returningFields]); [List<String> returningFields = const []]);
/// Enters a database transaction, performing the actions within, /// Enters a database transaction, performing the actions within,
/// and returning the results of [f]. /// and returning the results of [f].

View file

@ -28,3 +28,4 @@ dev_dependencies:
build_runner: ^2.0.1 build_runner: ^2.0.1
pedantic: ^1.11.0 pedantic: ^1.11.0
test: ^1.17.3 test: ^1.17.3
optional: ^6.0.0-nullsafety.2

View file

@ -27,3 +27,8 @@ dev_dependencies:
ref: sdk-2.12.x_nnbd ref: sdk-2.12.x_nnbd
path: packages/pretty_logging path: packages/pretty_logging
test: ^1.17.3 test: ^1.17.3
#dependency_overrides:
# angel_orm_test:
# path: ../angel_orm_test
# angel_orm:
# path: ../angel_orm

View file

@ -160,13 +160,13 @@ class TreeQuery extends Query<Tree?, TreeQueryWhere?> {
@override @override
delete(QueryExecutor executor) { delete(QueryExecutor executor) {
return super.delete(executor).then((result) { return super.delete(executor).then((result) {
return result.fold<List<Tree?>>([], (out, model) { return result.fold<List<Tree>>([], (out, model) {
var idx = out.indexWhere((m) => m!.id == model!.id); var idx = out.indexWhere((m) => m.id == model!.id);
if (idx == -1) { if (idx == -1) {
return out..add(model); return out..add(model!);
} else { } else {
var l = out[idx]!; var l = out[idx];
return out return out
..[idx] = l.copyWith( ..[idx] = l.copyWith(
fruits: List<_Fruit>.from(l.fruits ?? []) fruits: List<_Fruit>.from(l.fruits ?? [])

View file

@ -304,18 +304,21 @@ class WeirdJoinQuery extends Query<WeirdJoin?, WeirdJoinQueryWhere?> {
@override @override
get(QueryExecutor executor) { get(QueryExecutor executor) {
return super.get(executor).then((result) { return super.get(executor).then((result) {
return result.fold<List<WeirdJoin?>>([], (out, model) { return result.fold<List<WeirdJoin>>([], (out, model) {
var idx = out.indexWhere((m) => m!.id == model!.id); var idx = out.indexWhere((m) => m.id == model!.id);
if (idx == -1) { if (idx == -1) {
return out..add(model); return out..add(model!);
} else { } else {
var l = out[idx]!; var l = out[idx];
return out return out
..[idx] = l.copyWith( ..[idx] = l.copyWith(
numbas: List<_Numba?>.from(l.numbas ?? []) numbas: List<_Numba>.from(l.numbas ?? [])
..addAll(model!.numbas ?? []), ..addAll(model == null
foos: List<_Foo?>.from(l.foos ?? [])..addAll(model.foos ?? [])); ? []
: List<_Numba>.from(model.numbas ?? [])),
foos: List<_Foo?>.from(l.foos ?? [])
..addAll(model?.foos ?? []));
} }
}); });
}); });
@ -324,13 +327,13 @@ class WeirdJoinQuery extends Query<WeirdJoin?, WeirdJoinQueryWhere?> {
@override @override
update(QueryExecutor executor) { update(QueryExecutor executor) {
return super.update(executor).then((result) { return super.update(executor).then((result) {
return result.fold<List<WeirdJoin?>>([], (out, model) { return result.fold<List<WeirdJoin>>([], (out, model) {
var idx = out.indexWhere((m) => m!.id == model!.id); var idx = out.indexWhere((m) => m.id == model!.id);
if (idx == -1) { if (idx == -1) {
return out..add(model); return out..add(model!);
} else { } else {
var l = out[idx]!; var l = out[idx];
return out return out
..[idx] = l.copyWith( ..[idx] = l.copyWith(
numbas: List<_Numba?>.from(l.numbas ?? []) numbas: List<_Numba?>.from(l.numbas ?? [])
@ -344,13 +347,13 @@ class WeirdJoinQuery extends Query<WeirdJoin?, WeirdJoinQueryWhere?> {
@override @override
delete(QueryExecutor executor) { delete(QueryExecutor executor) {
return super.delete(executor).then((result) { return super.delete(executor).then((result) {
return result.fold<List<WeirdJoin?>>([], (out, model) { return result.fold<List<WeirdJoin>>([], (out, model) {
var idx = out.indexWhere((m) => m!.id == model!.id); var idx = out.indexWhere((m) => m.id == model!.id);
if (idx == -1) { if (idx == -1) {
return out..add(model); return out..add(model!);
} else { } else {
var l = out[idx]!; var l = out[idx];
return out return out
..[idx] = l.copyWith( ..[idx] = l.copyWith(
numbas: List<_Numba?>.from(l.numbas ?? []) numbas: List<_Numba?>.from(l.numbas ?? [])

View file

@ -473,13 +473,13 @@ class RoleQuery extends Query<Role?, RoleQueryWhere?> {
@override @override
get(QueryExecutor executor) { get(QueryExecutor executor) {
return super.get(executor).then((result) { return super.get(executor).then((result) {
return result.fold<List<Role?>>([], (out, model) { return result.fold<List<Role>>([], (out, model) {
var idx = out.indexWhere((m) => m!.id == model!.id); var idx = out.indexWhere((m) => m.id == model!.id);
if (idx == -1) { if (idx == -1) {
return out..add(model); return out..add(model!);
} else { } else {
var l = out[idx]!; var l = out[idx];
return out return out
..[idx] = l.copyWith( ..[idx] = l.copyWith(
users: List<_User>.from(l.users)..addAll(model!.users)); users: List<_User>.from(l.users)..addAll(model!.users));
@ -509,13 +509,13 @@ class RoleQuery extends Query<Role?, RoleQueryWhere?> {
@override @override
delete(QueryExecutor executor) { delete(QueryExecutor executor) {
return super.delete(executor).then((result) { return super.delete(executor).then((result) {
return result.fold<List<Role?>>([], (out, model) { return result.fold<List<Role>>([], (out, model) {
var idx = out.indexWhere((m) => m!.id == model!.id); var idx = out.indexWhere((m) => m.id == model!.id);
if (idx == -1) { if (idx == -1) {
return out..add(model); return out..add(model!);
} else { } else {
var l = out[idx]!; var l = out[idx];
return out return out
..[idx] = l.copyWith( ..[idx] = l.copyWith(
users: List<_User>.from(l.users)..addAll(model!.users)); users: List<_User>.from(l.users)..addAll(model!.users));

View file

@ -130,7 +130,7 @@ standaloneTests(FutureOr<QueryExecutor> Function() createExecutor,
..orWhere((w) => w!.familyFriendly.isTrue); ..orWhere((w) => w!.familyFriendly.isTrue);
print(query.compile(Set(), preamble: 'DELETE FROM "cars"')); print(query.compile(Set(), preamble: 'DELETE FROM "cars"'));
List<Car?> cars = await query.delete(executor); List<Car?>? cars = await query.delete(executor);
expect(cars, hasLength(1)); expect(cars, hasLength(1));
expect(cars.first!.toJson(), ferrari!.toJson()); expect(cars.first!.toJson(), ferrari!.toJson());
}); });

View file

@ -38,3 +38,7 @@ dev_dependencies:
ref: sdk-2.12.x_nnbd ref: sdk-2.12.x_nnbd
path: packages/framework path: packages/framework
build_runner: ^2.0.1 build_runner: ^2.0.1
#dependency_overrides:
# angel_orm:
# path: ../angel_orm