mirror of https://github.com/AMT-Cheif/drift.git
Implement delete
This commit is contained in:
parent
64ceb2acfa
commit
2a69573330
|
@ -21,6 +21,6 @@ class Users extends Table {
|
|||
class ShopDb extends SallyDb with _$ShopDbMixin {
|
||||
|
||||
Future<List<User>> allUsers() => users.select().get();
|
||||
Future<User> userByName(String name) => users.select().where((u) => u.name.equals(name)).single();
|
||||
Future<User> userByName(String name) => (users.select()..where((u) => u.name.equals(name))).single();
|
||||
|
||||
}
|
|
@ -8,6 +8,10 @@ class _$ShopDbMixin implements QueryExecutor {
|
|||
return null;
|
||||
}
|
||||
|
||||
Future<int> executeDelete(String sql, [dynamic params]) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class StructuredUsersTable extends Users with TableStructure<Users, User> {
|
||||
|
|
|
@ -7,6 +7,7 @@ class UseData {
|
|||
|
||||
abstract class QueryExecutor {
|
||||
Future<List<Map<String, dynamic>>> executeQuery(String sql, [dynamic params]);
|
||||
Future<int> executeDelete(String sql, [dynamic params]);
|
||||
}
|
||||
|
||||
abstract class SallyDb {}
|
||||
|
|
|
@ -7,18 +7,18 @@ class Column<T> {
|
|||
Predicate equals(T compare) => null;
|
||||
}
|
||||
|
||||
class IntColumn extends Column<int> {
|
||||
Predicate isBiggerThan(int i) => null;
|
||||
Predicate isSmallerThan(int i) => null;
|
||||
abstract class IntColumn extends Column<int> {
|
||||
Predicate isBiggerThan(int i);
|
||||
Predicate isSmallerThan(int i);
|
||||
}
|
||||
|
||||
class BoolColumn extends Column<bool> {
|
||||
Predicate isTrue() => null;
|
||||
Predicate isFalse() => null;
|
||||
abstract class BoolColumn extends Column<bool> {
|
||||
Predicate isTrue();
|
||||
Predicate isFalse();
|
||||
}
|
||||
|
||||
class TextColumn extends Column<String> {
|
||||
Predicate like(String regex) => null;
|
||||
abstract class TextColumn extends Column<String> {
|
||||
Predicate like(String regex);
|
||||
}
|
||||
|
||||
class ColumnBuilder<T> {
|
||||
|
|
|
@ -13,3 +13,16 @@ class Variable extends SqlExpression {
|
|||
context.buffer.write('? ');
|
||||
}
|
||||
}
|
||||
|
||||
class HardcodedConstant extends SqlExpression {
|
||||
|
||||
final dynamic value;
|
||||
|
||||
HardcodedConstant(this.value);
|
||||
|
||||
@override
|
||||
void writeInto(GenerationContext context) {
|
||||
context.buffer.write(context.harcodedSqlValue(value));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -5,4 +5,8 @@ class GenerationContext {
|
|||
void addBoundVariable(dynamic data) {
|
||||
boundVariables.add(data);
|
||||
}
|
||||
|
||||
String harcodedSqlValue(dynamic value) {
|
||||
return value.toString();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
import 'package:sally/src/queries/expressions/expressions.dart';
|
||||
import 'package:sally/src/queries/statement/statements.dart';
|
||||
import 'package:sally/src/queries/table_structure.dart';
|
||||
|
||||
class DeleteStatement<Table> with Limitable, WhereFilterable<Table, dynamic> {
|
||||
|
||||
DeleteStatement(TableStructure<Table, dynamic> table) {
|
||||
super.table = table;
|
||||
}
|
||||
|
||||
/// Deletes all records matched by the optional where and limit statements.
|
||||
/// Returns the amount of deleted rows.
|
||||
Future<int> performDelete() {
|
||||
GenerationContext context = GenerationContext();
|
||||
context.buffer.write('DELETE FROM ');
|
||||
context.buffer.write(table.sqlTableName);
|
||||
context.buffer.write(' ');
|
||||
|
||||
if (hasWhere) whereExpression.writeInto(context);
|
||||
if (hasLimit) limitExpression.writeInto(context);
|
||||
|
||||
return table.executor.executeDelete(context.buffer.toString(), context.boundVariables);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
import 'package:sally/src/queries/expressions/expressions.dart';
|
||||
import 'package:sally/src/queries/statement/statements.dart';
|
||||
import 'package:sally/src/queries/table_structure.dart';
|
||||
|
||||
class SelectStatement<Table, Result> with Limitable, WhereFilterable<Table, Result> {
|
||||
|
||||
SelectStatement(TableStructure<Table, Result> table) {
|
||||
super.table = table;
|
||||
}
|
||||
|
||||
GenerationContext _buildQuery() {
|
||||
GenerationContext context = GenerationContext();
|
||||
context.buffer.write('SELECT * FROM ');
|
||||
context.buffer.write(table.sqlTableName);
|
||||
context.buffer.write(' ');
|
||||
|
||||
if (hasWhere) whereExpression.writeInto(context);
|
||||
if (hasLimit) limitExpression.writeInto(context);
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
/// Executes the select statement on the database and maps the returned rows
|
||||
/// to the right dataclass.
|
||||
Future<List<Result>> get() async {
|
||||
final ctx = _buildQuery();
|
||||
final sql = ctx.buffer.toString();
|
||||
final vars = ctx.boundVariables;
|
||||
|
||||
final result = await table.executor.executeQuery(sql, vars);
|
||||
return result.map(table.parse).toList();
|
||||
}
|
||||
|
||||
/// Similar to [get], but it will only load one item by setting [limit()]
|
||||
/// appropriately. This method will throw if no results where found. If you're
|
||||
/// ok with no result existing, try [singleOrNull] instead.
|
||||
Future<Result> single() async {
|
||||
final element = singleOrNull();
|
||||
if (element == null)
|
||||
throw StateError("No item was returned by the query called with single()");
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
/// Similar to [get], but only uses one row of the result by setting the limit
|
||||
/// accordingly. If no item was found, null will be returned instead.
|
||||
Future<Result> singleOrNull() async {
|
||||
// limit to one item, using the existing offset if it exists
|
||||
limitExpression = LimitExpression(1, limitExpression.offset ?? 0);
|
||||
|
||||
final results = await get();
|
||||
if (results.isEmpty)
|
||||
return null;
|
||||
return results.single;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
import 'package:meta/meta.dart';
|
||||
import 'package:sally/src/queries/expressions/expressions.dart';
|
||||
import 'package:sally/src/queries/expressions/limit.dart';
|
||||
import 'package:sally/src/queries/expressions/where.dart';
|
||||
import 'package:sally/src/queries/predicates/predicate.dart';
|
||||
import 'package:sally/src/queries/table_structure.dart';
|
||||
|
||||
/// Mixin for statements that allow a LIMIT operator
|
||||
class Limitable {
|
||||
|
||||
@protected
|
||||
LimitExpression limitExpression;
|
||||
|
||||
void limit({int amount, int offset}) {
|
||||
limitExpression = LimitExpression(amount, offset);
|
||||
}
|
||||
|
||||
@protected
|
||||
bool get hasLimit => limitExpression != null;
|
||||
|
||||
}
|
||||
|
||||
/// Mixin for statements that allow a WHERE operator on a specific table.
|
||||
class WhereFilterable<Table, Result> {
|
||||
|
||||
@protected
|
||||
TableStructure<Table, Result> table;
|
||||
@protected
|
||||
WhereExpression whereExpression;
|
||||
|
||||
bool get hasWhere => whereExpression != null;
|
||||
|
||||
void where(Predicate filter(Table table)) {
|
||||
final addedPredicate = filter(table.asTable);
|
||||
|
||||
if (hasWhere) {
|
||||
// merge existing where expression together with new one by and-ing them
|
||||
// together.
|
||||
whereExpression = WhereExpression(whereExpression.predicate.and(addedPredicate));
|
||||
} else {
|
||||
whereExpression = WhereExpression(addedPredicate);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,71 +0,0 @@
|
|||
import 'package:sally/src/queries/expressions/limit.dart';
|
||||
import 'package:sally/src/queries/expressions/where.dart';
|
||||
import 'package:sally/src/queries/generation_context.dart';
|
||||
import 'package:sally/src/queries/predicates/predicate.dart';
|
||||
import 'package:sally/src/queries/table_structure.dart';
|
||||
|
||||
abstract class SqlStatement {
|
||||
GenerationContext _buildQuery();
|
||||
}
|
||||
|
||||
abstract class StatementForExistingData<Table, Result> extends SqlStatement {
|
||||
final TableStructure<Table, Result> _table;
|
||||
|
||||
StatementForExistingData(this._table);
|
||||
|
||||
WhereExpression _where;
|
||||
LimitExpression _limit;
|
||||
|
||||
Future<List<Result>> get() async {
|
||||
final ctx = _buildQuery();
|
||||
final sql = ctx.buffer.toString();
|
||||
final vars = ctx.boundVariables;
|
||||
|
||||
final result = await _table.executor.executeQuery(sql, vars);
|
||||
return result.map(_table.parse).toList();
|
||||
}
|
||||
|
||||
Future<Result> single() async {
|
||||
// limit to one item, using the existing offset if it exists
|
||||
_limit = LimitExpression(1, _limit?.offset ?? 0);
|
||||
|
||||
return (await get()).single;
|
||||
}
|
||||
|
||||
StatementForExistingData<Table, Result> where(
|
||||
Predicate extractor(Table tbl)) {
|
||||
final addedPredicate = extractor(_table.asTable);
|
||||
|
||||
if (_where != null) {
|
||||
// merge existing where expression together with new one by and-ing them
|
||||
// together.
|
||||
_where = WhereExpression(_where.predicate.and(addedPredicate));
|
||||
} else {
|
||||
_where = WhereExpression(addedPredicate);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
StatementForExistingData<Table, Result> limit({int amount, int offset}) {
|
||||
_limit = LimitExpression(amount, offset);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
class SelectStatement<T, R> extends StatementForExistingData<T, R> {
|
||||
SelectStatement(TableStructure<T, R> table) : super(table);
|
||||
|
||||
@override
|
||||
GenerationContext _buildQuery() {
|
||||
GenerationContext context = GenerationContext();
|
||||
context.buffer.write('SELECT * FROM ');
|
||||
context.buffer.write(_table.sqlTableName);
|
||||
context.buffer.write(' ');
|
||||
|
||||
if (_where != null) _where.writeInto(context);
|
||||
if (_limit != null) _limit.writeInto(context);
|
||||
|
||||
return context;
|
||||
}
|
||||
}
|
|
@ -6,7 +6,8 @@ import 'package:sally/src/queries/generation_context.dart';
|
|||
import 'package:sally/src/queries/predicates/numbers.dart';
|
||||
import 'package:sally/src/queries/predicates/predicate.dart';
|
||||
import 'package:sally/src/queries/predicates/text.dart';
|
||||
import 'package:sally/src/queries/statements.dart';
|
||||
import 'package:sally/src/queries/statement/delete.dart';
|
||||
import 'package:sally/src/queries/statement/select.dart';
|
||||
|
||||
abstract class TableStructure<UserSpecifiedTable, ResolvedType> {
|
||||
QueryExecutor executor;
|
||||
|
@ -18,6 +19,8 @@ abstract class TableStructure<UserSpecifiedTable, ResolvedType> {
|
|||
|
||||
SelectStatement<UserSpecifiedTable, ResolvedType> select() =>
|
||||
SelectStatement<UserSpecifiedTable, ResolvedType>(this);
|
||||
|
||||
DeleteStatement<UserSpecifiedTable> delete() => DeleteStatement(this);
|
||||
}
|
||||
|
||||
class StructuredColumn<T> implements SqlExpression, Column<T> {
|
||||
|
@ -51,14 +54,15 @@ class StructuredBoolColumn extends StructuredColumn<bool>
|
|||
implements BoolColumn {
|
||||
StructuredBoolColumn(String sqlName) : super(sqlName);
|
||||
|
||||
@override
|
||||
Predicate isFalse() {
|
||||
return not(isTrue());
|
||||
}
|
||||
// Booleans will be stored as integers, where 0 means false and 1 means true
|
||||
|
||||
@override
|
||||
Predicate isFalse() {
|
||||
return EqualityPredicate(this, HardcodedConstant(0));
|
||||
}
|
||||
@override
|
||||
Predicate isTrue() {
|
||||
return BooleanExpressionPredicate(this);
|
||||
return EqualityPredicate(this, HardcodedConstant(1));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -56,25 +56,40 @@ void main() {
|
|||
});
|
||||
|
||||
test("generates limit statements", () {
|
||||
users.select().limit(amount: 10).get();
|
||||
(users.select()..limit(amount: 10)).get();
|
||||
verify(executor.executeQuery("SELECT * FROM users LIMIT 10 ", any));
|
||||
});
|
||||
|
||||
test("generates like expressions", () {
|
||||
users.select().where((u) => u.name.like("Dash%")).get();
|
||||
(users.select()..where((u) => u.name.like("Dash%"))).get();
|
||||
verify(executor
|
||||
.executeQuery("SELECT * FROM users WHERE name LIKE ? ", ["Dash%"]));
|
||||
});
|
||||
|
||||
test("generates complex predicates", () {
|
||||
users
|
||||
.select()
|
||||
.where((u) => not(u.name.equals("Dash")).and(u.id.isBiggerThan(12)))
|
||||
(users.select()
|
||||
..where(
|
||||
(u) => not(u.name.equals("Dash")).and(u.id.isBiggerThan(12))))
|
||||
.get();
|
||||
|
||||
verify(executor.executeQuery(
|
||||
"SELECT * FROM users WHERE (NOT name = ? ) AND (id > ? ) ",
|
||||
["Dash", 12]));
|
||||
});
|
||||
|
||||
test("generates expressions from boolean fields", () {
|
||||
(users.select()..where((u) => u.isAwesome.isTrue())).get();
|
||||
|
||||
verify(executor.executeQuery(
|
||||
"SELECT * FROM users WHERE is_awesome = 1", any));
|
||||
});
|
||||
});
|
||||
|
||||
group("Generates DELETE statements", () {
|
||||
test("without any constaints", () {
|
||||
users.delete().performDelete();
|
||||
|
||||
verify(executor.executeDelete("DELETE FROM users ", argThat(isEmpty)));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue