Implement order-by clauses for select statements

This commit is contained in:
Simon Binder 2019-02-19 11:10:27 +01:00
parent a26f84ceaf
commit e018f6d725
5 changed files with 96 additions and 0 deletions

View File

@ -4,6 +4,7 @@ export 'package:sally/src/dsl/table.dart';
export 'package:sally/src/dsl/columns.dart';
export 'package:sally/src/dsl/database.dart';
export 'package:sally/src/runtime/components/order_by.dart';
export 'package:sally/src/runtime/executor/executor.dart';
export 'package:sally/src/runtime/executor/type_system.dart';
export 'package:sally/src/runtime/expressions/comparable.dart';

View File

@ -0,0 +1,64 @@
import 'package:meta/meta.dart';
import 'package:sally/src/runtime/components/component.dart';
import 'package:sally/src/runtime/expressions/expression.dart';
enum OrderingMode {
/// Ascending ordering mode (lowest items first)
asc,
/// Descending ordering mode (highest items first)
desc
}
const _modeToString = {
OrderingMode.asc: 'ASC',
OrderingMode.desc: 'DESC',
};
/// A single term in a [OrderBy] clause. The priority of this term is determined
/// by its position in [OrderBy.terms].
class OrderingTerm extends Component {
/// The expression after which the ordering should happen
final Expression expression;
/// The ordering mode (ascending or descending).
final OrderingMode mode;
OrderingTerm({@required this.expression, this.mode = OrderingMode.asc});
@override
void writeInto(GenerationContext context) {
expression.writeInto(context);
context.writeWhitespace();
context.buffer.write(_modeToString[mode]);
}
}
/// An order-by clause as part of a select statement. The clause can consist
/// of multiple [OrderingTerm]s, with the first terms being more important and
/// the later terms only being considered if the first term considers two rows
/// equal.
class OrderBy extends Component {
final List<OrderingTerm> terms;
OrderBy(this.terms);
@override
void writeInto(GenerationContext context) {
var first = true;
context.buffer.write('ORDER BY ');
for (var term in terms) {
if (first) {
first = false;
} else {
context.buffer.write(', ');
}
term.writeInto(context);
}
}
}

View File

@ -1,6 +1,7 @@
import 'package:meta/meta.dart';
import 'package:sally/src/runtime/components/component.dart';
import 'package:sally/src/runtime/components/limit.dart';
import 'package:sally/src/runtime/components/order_by.dart';
import 'package:sally/src/runtime/components/where.dart';
import 'package:sally/src/runtime/executor/executor.dart';
import 'package:sally/src/runtime/expressions/bools.dart';
@ -20,6 +21,8 @@ abstract class Query<Table, DataClass> {
@protected
Where whereExpr;
@protected
OrderBy orderByExpr;
@protected
Limit limitExpr;
/// Subclasses must override this and write the part of the statement that
@ -53,6 +56,13 @@ abstract class Query<Table, DataClass> {
needsWhitespace = true;
}
if (orderByExpr != null) {
if (needsWhitespace) ctx.writeWhitespace();
orderByExpr.writeInto(ctx);
needsWhitespace = true;
}
if (limitExpr != null) {
if (needsWhitespace) ctx.writeWhitespace();

View File

@ -1,9 +1,12 @@
import 'package:sally/sally.dart';
import 'package:sally/src/runtime/components/component.dart';
import 'package:sally/src/runtime/components/limit.dart';
import 'package:sally/src/runtime/executor/executor.dart';
import 'package:sally/src/runtime/statements/query.dart';
import 'package:sally/src/runtime/structure/table_info.dart';
typedef OrderingTerm OrderClauseGenerator<T>(T tbl);
class SelectStatement<T, D> extends Query<T, D> {
SelectStatement(GeneratedDatabase database, TableInfo<T, D> table)
: super(database, table);
@ -30,6 +33,13 @@ class SelectStatement<T, D> extends Query<T, D> {
limitExpr = Limit(limit, offset);
}
/// Orders the result by the given clauses. The clauses coming first in the
/// list have a higher priority, the later clauses are only considered if the
/// first clause considers two rows to be equal.
void orderBy(List<OrderClauseGenerator<T>> clauses) {
orderByExpr = OrderBy(clauses.map((t) => t(table.asDslTable)).toList());
}
/// Creates an auto-updating stream that emits new items whenever this table
/// changes.
Stream<List<D>> watch() {

View File

@ -31,6 +31,17 @@ void main() {
.runSelect('SELECT * FROM users WHERE name LIKE ?;', ['Dash%']));
});
test('with order-by clauses', () async {
await (db.select(db.users)
..orderBy([
(u) => OrderingTerm(expression: u.isAwesome, mode: OrderingMode.desc),
(u) => OrderingTerm(expression: u.id)
])).get();
verify(executor.runSelect('SELECT * FROM users ORDER BY '
'(is_awesome = 1) DESC, id ASC;', argThat(isEmpty)));
});
test('with complex predicates', () {
(db.select(db.users)
..where((u) =>