@@ -354,140 +354,3 @@ final class InMemoryRowProvider: RowProvider {
354354 return safeBuffer. rows [ displayIndex]
355355 }
356356}
357-
358- // MARK: - Database Row Provider (for virtualized access via driver)
359-
360- /// Row provider that fetches data on-demand from database.
361- /// Cache is bounded to `maxCacheSize` entries; oldest entries by row index
362- /// are evicted when the limit is exceeded.
363- final class DatabaseRowProvider : RowProvider {
364- private static let logger = Logger ( subsystem: " com.TablePro " , category: " RowProvider " )
365- private static let maxCacheSize = 10_000
366-
367- private let driver : DatabaseDriver
368- private let baseQuery : String
369- private var cache : [ Int : TableRowData ] = [ : ]
370- private let pageSize : Int
371- private var prefetchTask : Task < Void , Never > ?
372- private var inFlightRange : Range < Int > ?
373-
374- private( set) var totalRowCount : Int = 0
375- private( set) var columns : [ String ]
376- private( set) var columnDefaults : [ String : String ? ]
377-
378- private var isInitialized = false
379-
380- init ( driver: DatabaseDriver , query: String , columns: [ String ] , columnDefaults: [ String : String ? ] = [ : ] , pageSize: Int = 200 ) {
381- self . driver = driver
382- self . baseQuery = query
383- self . columns = columns
384- self . columnDefaults = columnDefaults
385- self . pageSize = pageSize
386- }
387-
388- /// Initialize by fetching total row count
389- func initialize( ) async throws {
390- guard !isInitialized else { return }
391-
392- totalRowCount = try await driver. fetchRowCount ( query: baseQuery)
393- isInitialized = true
394- }
395-
396- func fetchRows( offset: Int , limit: Int ) -> [ TableRowData ] {
397- var result : [ TableRowData ] = [ ]
398-
399- for i in offset..< min ( offset + limit, totalRowCount) {
400- if let cached = cache [ i] {
401- result. append ( cached)
402- } else {
403- // Return placeholder - actual data filled via prefetch
404- let placeholder = TableRowData ( index: i, values: Array ( repeating: " ... " , count: columns. count) )
405- result. append ( placeholder)
406- }
407- }
408-
409- return result
410- }
411-
412- func prefetchRows( at indices: [ Int ] ) {
413- let missingIndices = indices. filter { cache [ $0] == nil }
414- guard !missingIndices. isEmpty else { return }
415-
416- guard let minIndex = missingIndices. min ( ) ,
417- let maxIndex = missingIndices. max ( ) else { return }
418-
419- let offset = minIndex
420- let limit = min ( maxIndex - minIndex + pageSize, totalRowCount - offset)
421- let fetchRange = offset..< ( offset + limit)
422-
423- if let inFlight = inFlightRange,
424- inFlight. contains ( offset) && inFlight. contains ( offset + limit - 1 ) {
425- return
426- }
427-
428- prefetchTask? . cancel ( )
429- let driver = self . driver
430- let baseQuery = self . baseQuery
431-
432- inFlightRange = fetchRange
433- prefetchTask = Task { [ weak self] in
434- do {
435- let result = try await driver. fetchRows ( query: baseQuery, offset: offset, limit: limit)
436- guard !Task. isCancelled else { return }
437- await MainActor . run { [ weak self] in
438- guard let self else { return }
439- for (i, row) in result. rows. enumerated ( ) {
440- self . cache [ offset + i] = TableRowData ( index: offset + i, values: row)
441- }
442- self . evictCacheIfNeeded ( nearIndex: offset)
443- self . inFlightRange = nil
444- }
445- } catch {
446- guard !Task. isCancelled else { return }
447- Self . logger. error ( " Prefetch error: \( error) " )
448- await MainActor . run { [ weak self] in
449- self ? . inFlightRange = nil
450- }
451- }
452- }
453- }
454-
455- func invalidateCache( ) {
456- prefetchTask? . cancel ( )
457- prefetchTask = nil
458- inFlightRange = nil
459- cache. removeAll ( )
460- isInitialized = false
461- }
462-
463- /// Synchronously fetch and cache rows (for initial load)
464- func loadRows( offset: Int , limit: Int ) async throws {
465- let result = try await driver. fetchRows ( query: baseQuery, offset: offset, limit: limit)
466- for (i, row) in result. rows. enumerated ( ) {
467- let rowData = TableRowData ( index: offset + i, values: row)
468- cache [ offset + i] = rowData
469- }
470- evictCacheIfNeeded ( nearIndex: offset)
471- }
472-
473- /// Get row data at index (nil if not cached)
474- func row( at index: Int ) -> TableRowData ? {
475- cache [ index]
476- }
477-
478- /// Update a cached cell value
479- func updateValue( _ value: String ? , at rowIndex: Int , columnIndex: Int ) {
480- cache [ rowIndex] ? . setValue ( value, at: columnIndex)
481- }
482-
483- // MARK: - Private
484-
485- /// Evict entries when cache exceeds `maxCacheSize`.
486- /// Keeps the half of entries closest to `nearIndex` (the current access window)
487- /// and discards the rest.
488- private func evictCacheIfNeeded( nearIndex: Int ) {
489- guard cache. count > Self . maxCacheSize else { return }
490- let halfSize = Self . maxCacheSize / 2
491- cache = cache. filter { abs ( $0. key - nearIndex) <= halfSize }
492- }
493- }
0 commit comments