Merge pull request #2024 from Vorkytaka/order_by_nulls

Add `NULLS FIRST\LAST` to the `OrderingTerm`
This commit is contained in:
Simon Binder 2022-08-28 22:05:06 +02:00 committed by GitHub
commit 98cb4be7cc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 132 additions and 16 deletions

View File

@ -3,16 +3,28 @@ part of '../query_builder.dart';
/// Describes how to order rows
enum OrderingMode {
/// Ascending ordering mode (lowest items first)
asc,
asc._('ASC'),
/// Descending ordering mode (highest items first)
desc
desc._('DESC');
final String _mode;
const OrderingMode._(this._mode);
}
const _modeToString = {
OrderingMode.asc: 'ASC',
OrderingMode.desc: 'DESC',
};
/// Describes how to order nulls
enum NullsOrder {
/// Place NULLs at the start
first._('NULLS FIRST'),
/// Place NULLs at the end
last._('NULLS LAST');
final String _order;
const NullsOrder._(this._order);
}
/// A single term in a [OrderBy] clause. The priority of this term is determined
/// by its position in [OrderBy.terms].
@ -23,18 +35,40 @@ class OrderingTerm extends Component {
/// The ordering mode (ascending or descending).
final OrderingMode mode;
/// Creates an ordering term by the [expression] and the [mode] (defaults to
/// ascending).
OrderingTerm({required this.expression, this.mode = OrderingMode.asc});
/// How to order NULLs.
/// When [nulls] is [null], then it's ignored.
///
/// Note that this feature are only available in sqlite3 version `3.30.0` and
/// newer. When using `sqlite3_flutter_libs` or a web database, this is not
/// a problem.
final NullsOrder? nulls;
/// Creates an ordering term that sorts for ascending values of [expression].
factory OrderingTerm.asc(Expression expression) {
return OrderingTerm(expression: expression, mode: OrderingMode.asc);
/// Creates an ordering term by the [expression], the [mode] (defaults to
/// ascending) and the [nulls].
OrderingTerm({
required this.expression,
this.mode = OrderingMode.asc,
this.nulls,
});
/// Creates an ordering term that sorts for ascending values
/// of [expression] and the [nulls].
factory OrderingTerm.asc(Expression expression, [NullsOrder? nulls]) {
return OrderingTerm(
expression: expression,
mode: OrderingMode.asc,
nulls: nulls,
);
}
/// Creates an ordering term that sorts for descending values of [expression].
factory OrderingTerm.desc(Expression expression) {
return OrderingTerm(expression: expression, mode: OrderingMode.desc);
/// Creates an ordering term that sorts for descending values
/// of [expression] and the [nulls].
factory OrderingTerm.desc(Expression expression, [NullsOrder? nulls]) {
return OrderingTerm(
expression: expression,
mode: OrderingMode.desc,
nulls: nulls,
);
}
/// Creates an ordering term to get a number of random rows
@ -47,7 +81,11 @@ class OrderingTerm extends Component {
void writeInto(GenerationContext context) {
expression.writeInto(context);
context.writeWhitespace();
context.buffer.write(_modeToString[mode]);
context.buffer.write(mode._mode);
if (nulls != null) {
context.writeWhitespace();
context.buffer.write(nulls?._order);
}
}
}

View File

@ -0,0 +1,78 @@
import 'package:drift/drift.dart' hide isNull;
import 'package:mockito/mockito.dart';
import 'package:test/test.dart';
import '../../generated/todos.dart';
import '../../test_utils/test_utils.dart';
void main() {
late TodoDb db;
late MockExecutor executor;
setUp(() {
executor = MockExecutor();
db = TodoDb(executor);
});
test('when nullsOrder is null it ignored', () async {
final query = db.select(db.users);
query.orderBy([(tbl) => OrderingTerm(expression: tbl.name)]);
await query.get();
verify(executor.runSelect(
'SELECT * FROM users ORDER BY name ASC;',
argThat(isEmpty),
));
});
test('nullsOrder is last', () async {
final query = db.select(db.users);
query.orderBy([
(tbl) => OrderingTerm(
expression: tbl.name,
nulls: NullsOrder.last,
),
]);
await query.get();
verify(executor.runSelect(
'SELECT * FROM users ORDER BY name ASC NULLS LAST;',
argThat(isEmpty),
));
});
test('nullsOrder is first', () async {
final query = db.select(db.users);
query.orderBy([
(tbl) => OrderingTerm(
expression: tbl.name,
nulls: NullsOrder.first,
),
]);
await query.get();
verify(executor.runSelect(
'SELECT * FROM users ORDER BY name ASC NULLS FIRST;',
argThat(isEmpty),
));
});
test('complex order by with different nullsOrder', () async {
final query = db.select(db.users);
query.orderBy([
(tbl) => OrderingTerm(
expression: tbl.name,
nulls: NullsOrder.first,
),
(tbl) => OrderingTerm(
expression: tbl.creationTime,
),
(tbl) => OrderingTerm(
expression: tbl.profilePicture,
nulls: NullsOrder.last,
),
]);
await query.get();
verify(executor.runSelect(
'SELECT * FROM users ORDER BY name ASC NULLS FIRST, creation_time ASC, profile_picture ASC NULLS LAST;',
argThat(isEmpty),
));
});
}