From 8cb029a73eb7fb4f493ddf90ba4c10f03747a852 Mon Sep 17 00:00:00 2001 From: Vorkytaka Date: Sun, 28 Aug 2022 15:19:44 +0300 Subject: [PATCH 1/2] Add `NULLS FIRST\LAST` to the `OrderingTerm` --- .../query_builder/components/order_by.dart | 32 +++++++- .../database/statements/order_by_test.dart | 78 +++++++++++++++++++ 2 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 drift/test/database/statements/order_by_test.dart diff --git a/drift/lib/src/runtime/query_builder/components/order_by.dart b/drift/lib/src/runtime/query_builder/components/order_by.dart index d27d7318..e8e1be6c 100644 --- a/drift/lib/src/runtime/query_builder/components/order_by.dart +++ b/drift/lib/src/runtime/query_builder/components/order_by.dart @@ -14,6 +14,20 @@ const _modeToString = { OrderingMode.desc: 'DESC', }; +/// Describes how to order nulls +enum NullsOrder { + /// Place NULLs at the start + first, + + /// Place NULLs at the end + last, +} + +const _nullsOrderToString = { + NullsOrder.first: 'NULLS FIRST', + NullsOrder.last: 'NULLS LAST', +}; + /// A single term in a [OrderBy] clause. The priority of this term is determined /// by its position in [OrderBy.terms]. class OrderingTerm extends Component { @@ -23,9 +37,21 @@ class OrderingTerm extends Component { /// The ordering mode (ascending or descending). final OrderingMode mode; + /// How to order NULLs. + /// When [nullsOrder] 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? nullsOrder; + /// Creates an ordering term by the [expression] and the [mode] (defaults to /// ascending). - OrderingTerm({required this.expression, this.mode = OrderingMode.asc}); + OrderingTerm({ + required this.expression, + this.mode = OrderingMode.asc, + this.nullsOrder, + }); /// Creates an ordering term that sorts for ascending values of [expression]. factory OrderingTerm.asc(Expression expression) { @@ -48,6 +74,10 @@ class OrderingTerm extends Component { expression.writeInto(context); context.writeWhitespace(); context.buffer.write(_modeToString[mode]); + if (nullsOrder != null) { + context.writeWhitespace(); + context.buffer.write(_nullsOrderToString[nullsOrder]); + } } } diff --git a/drift/test/database/statements/order_by_test.dart b/drift/test/database/statements/order_by_test.dart new file mode 100644 index 00000000..880936cd --- /dev/null +++ b/drift/test/database/statements/order_by_test.dart @@ -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, + nullsOrder: 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, + nullsOrder: 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, + nullsOrder: NullsOrder.first, + ), + (tbl) => OrderingTerm( + expression: tbl.creationTime, + ), + (tbl) => OrderingTerm( + expression: tbl.profilePicture, + nullsOrder: 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), + )); + }); +} From a4bff1f9ef38dbc378faf29f527b0b542a0ed037 Mon Sep 17 00:00:00 2001 From: Vorkytaka Date: Sun, 28 Aug 2022 20:20:55 +0300 Subject: [PATCH 2/2] Rename `nullsOrder` to `nulls`, move sql strings to enums, add `nulls` argument to the factory methods --- .../query_builder/components/order_by.dart | 64 +++++++++++-------- .../database/statements/order_by_test.dart | 8 +-- 2 files changed, 40 insertions(+), 32 deletions(-) diff --git a/drift/lib/src/runtime/query_builder/components/order_by.dart b/drift/lib/src/runtime/query_builder/components/order_by.dart index e8e1be6c..cc81cf9c 100644 --- a/drift/lib/src/runtime/query_builder/components/order_by.dart +++ b/drift/lib/src/runtime/query_builder/components/order_by.dart @@ -3,30 +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'); -const _modeToString = { - OrderingMode.asc: 'ASC', - OrderingMode.desc: 'DESC', -}; + final String _mode; + + const OrderingMode._(this._mode); +} /// Describes how to order nulls enum NullsOrder { /// Place NULLs at the start - first, + first._('NULLS FIRST'), /// Place NULLs at the end - last, -} + last._('NULLS LAST'); -const _nullsOrderToString = { - NullsOrder.first: 'NULLS FIRST', - NullsOrder.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]. @@ -38,29 +36,39 @@ class OrderingTerm extends Component { final OrderingMode mode; /// How to order NULLs. - /// When [nullsOrder] is [null], then it's ignored. + /// 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? nullsOrder; + final NullsOrder? nulls; - /// Creates an ordering term by the [expression] and the [mode] (defaults to - /// ascending). + /// Creates an ordering term by the [expression], the [mode] (defaults to + /// ascending) and the [nulls]. OrderingTerm({ required this.expression, this.mode = OrderingMode.asc, - this.nullsOrder, + this.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 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 @@ -73,10 +81,10 @@ class OrderingTerm extends Component { void writeInto(GenerationContext context) { expression.writeInto(context); context.writeWhitespace(); - context.buffer.write(_modeToString[mode]); - if (nullsOrder != null) { + context.buffer.write(mode._mode); + if (nulls != null) { context.writeWhitespace(); - context.buffer.write(_nullsOrderToString[nullsOrder]); + context.buffer.write(nulls?._order); } } } diff --git a/drift/test/database/statements/order_by_test.dart b/drift/test/database/statements/order_by_test.dart index 880936cd..77ba9b80 100644 --- a/drift/test/database/statements/order_by_test.dart +++ b/drift/test/database/statements/order_by_test.dart @@ -29,7 +29,7 @@ void main() { query.orderBy([ (tbl) => OrderingTerm( expression: tbl.name, - nullsOrder: NullsOrder.last, + nulls: NullsOrder.last, ), ]); await query.get(); @@ -44,7 +44,7 @@ void main() { query.orderBy([ (tbl) => OrderingTerm( expression: tbl.name, - nullsOrder: NullsOrder.first, + nulls: NullsOrder.first, ), ]); await query.get(); @@ -59,14 +59,14 @@ void main() { query.orderBy([ (tbl) => OrderingTerm( expression: tbl.name, - nullsOrder: NullsOrder.first, + nulls: NullsOrder.first, ), (tbl) => OrderingTerm( expression: tbl.creationTime, ), (tbl) => OrderingTerm( expression: tbl.profilePicture, - nullsOrder: NullsOrder.last, + nulls: NullsOrder.last, ), ]); await query.get();