page method

Future<MssqlPage<TRow>> page({
  1. required int rows,
  2. int offset = 0,
  3. required List<MssqlOrder> orderBy,
  4. MssqlCondition? where,
  5. bool includeTotal = false,
  6. List<MssqlRelation<TRow, Object?>> include = const [],
})

One page, and whether another follows.

One row beyond the page is fetched and discarded, which answers MssqlPage.hasMore without a COUNT(*). The count runs only when includeTotal asks for it.

Implementation

Future<MssqlPage<TRow>> page({
  required int rows,
  int offset = 0,
  required List<MssqlOrder> orderBy,
  MssqlCondition? where,
  bool includeTotal = false,
  List<MssqlRelation<TRow, Object?>> include = const [],
}) async {
  if (offset < 0) {
    throw ArgumentError.value(offset, 'offset', 'Cannot be negative.');
  }
  if (rows <= 0) {
    throw ArgumentError.value(rows, 'rows', 'Must be positive.');
  }
  if (orderBy.isEmpty) {
    throw ArgumentError.value(
      orderBy,
      'orderBy',
      'A page needs an ordering: SQL Server rejects OFFSET without ORDER BY, '
          'and an unordered page is not repeatable between calls.',
    );
  }
  var query = _base;
  if (where != null) query = query.where(where);
  query = query.orderBy(orderBy).paged(offset: offset, rows: rows + 1);

  // Include after dropping the sentinel: loading relations for a parent
  // that is not on the page is wasted work and would attach children to a
  // row the caller never sees.
  final loader = include.isEmpty ? null : _loader(await _dialect());
  loader?.snapshotScopes(include);
  final fetched = await _run(query);
  final hasMore = fetched.length > rows;
  final slice = hasMore ? fetched.sublist(0, rows) : fetched;
  final parents = include.isEmpty
      ? slice
      : await loader!.attach(
          binding,
          slice,
          include,
          onWarning: warnings.add,
        );

  return MssqlPage<TRow>(
    rows: parents,
    offset: offset,
    requestedRows: rows,
    hasMore: hasMore,
    total: includeTotal ? await _count(where) : null,
  );
}