cursorPage method

Future<MssqlCursorPage<R>> cursorPage({
  1. int size = 25,
  2. MssqlCursor? after,
  3. MssqlCursor? before,
  4. MssqlReadConsistency consistency = MssqlReadConsistency.committed,
})

A keyset page of projected rows. after and before are exclusive.

Implementation

Future<MssqlCursorPage<R>> cursorPage({
  int size = 25,
  MssqlCursor? after,
  MssqlCursor? before,
  MssqlReadConsistency consistency = MssqlReadConsistency.committed,
}) async {
  if (after != null && before != null) {
    throw ArgumentError('cursorPage cannot take after and before together.');
  }
  if (size <= 0) {
    throw ArgumentError.value(size, 'size', 'Must be positive.');
  }
  final dialect = await _resolvedDialect();
  final base = _select;
  if (base.ordering.isEmpty) {
    throw StateError(
      'Projection ${projection.name} needs an explicit unique orderBy '
      'before cursorPage().',
    );
  }
  MssqlKeyset.requireProjectedColumns(
    base.ordering,
    projection.columns.map((c) => c.alias),
    projection.name,
  );
  final orders = base.ordering;
  final orderSig = MssqlKeyset.orderSignature(orders, dialect);
  final filterSig = MssqlKeyset.queryFilterSignature(base, dialect);
  if (after != null) {
    MssqlKeyset.check(
      after,
      orderSignature: orderSig,
      filterSignature: filterSig,
      keyCount: orders.length,
    );
  }
  if (before != null) {
    MssqlKeyset.check(
      before,
      orderSignature: orderSig,
      filterSignature: filterSig,
      keyCount: orders.length,
    );
  }
  final walking = before != null ? MssqlKeyset.reverseOrders(orders) : orders;
  final cursor = after ?? before;
  return mssqlRunConsistent(
    session: session,
    consistency: consistency,
    body: (s) async {
      var query = base.orderBy(walking);
      if (cursor != null) {
        query = query.where(MssqlKeyset.after(walking, cursor.keys));
      }
      final fetched = await query
          .top(size + 1)
          .get(
            s,
            dialect: dialect,
            timeout: timeout,
            cancellationToken: options.cancellationToken,
            options: options,
          );
      final hasExtra = fetched.length > size;
      final kept = hasExtra ? fetched.sublist(0, size) : fetched;
      var mapped = kept.map(_map).toList();
      if (before != null) {
        mapped = mapped.reversed.toList();
      }
      if (kept.isEmpty) {
        return MssqlCursorPage<R>(rows: mapped, hasMore: false);
      }
      final firstRaw = before != null ? kept.last : kept.first;
      final lastRaw = before != null ? kept.first : kept.last;
      MssqlCursor make(MssqlRow row) => MssqlKeyset.encode(
        orderSignature: orderSig,
        filterSignature: filterSig,
        keys: MssqlKeyset.keysFromRow(row, orders),
      );
      return MssqlCursorPage<R>(
        rows: mapped,
        hasMore: hasExtra,
        next: before != null || hasExtra ? make(lastRaw) : null,
        previous: after != null || (before != null && hasExtra)
            ? make(firstRaw)
            : null,
      );
    },
  );
}