TODO.md
PRAGMA locking_mode = EXCLUSIVE improves performances? https://sqlite.org/forum/forumpost/866bf3407a Detect when ColumnExpression.noOverwrite excludes a non-existing column. Code is in DOA.upsertStatement()`. We should not silently ignore non-existing columns, as demonstrated by https://github.com/groue/GRDB.swift/issues/1539.
Can Swift 5.5 help us with select(.all) (request of RowDecoder), select(.id) (request of RowDecoder.ID), select(.rowid) (request of Int64)?
Direct access to statement for bindings
Property wrapper that decodes dictionaries (but how to tell the key column?)
See if SQLITE_FCNTL_DATA_VERSION could help working around the lack of snapshots in order to avoid double initial fetch of ValueObservation. Result: no, it does not look it returns values that are comparable between two distinct SQLite connections (from the initial reader, and from the writer thhat starts the observation)
Grab all FTS tokens in a string
[NO] Can we generate EXISTS with association? Team.annotated(with: Team.players.exists)
No. We already have Team.annotated(with: Team.players.isEmpty == false).
It does not use an EXISTS expression, but a JOIN, and this is better for
the internal consistency of the query interface, and the rules that deal
with association keys.
GRDB 6: have DatabaseRegionObservation produce DatabaseCancellable just as ValueObservation.
RangeReplaceableCollection should have append(contentsOf: cursor)
GRDB 6: choose persistence table
GRDB 6: decoding errors
GRDB 6: Batch insert & Batch insert RETURNING - https://stackoverflow.com/questions/1609637/is-it-possible-to-insert-multiple-rows-at-a-time-in-an-sqlite-database
GRDB 6: INSERT or UPDATE columns to their default value
GRDB 6: afterNextTransactionCommit -> afterNextTransaction(onCommit:onRollback:)
GRDB 6: encoding errors for record (EncodableRecord.encode(to:))
[?] GRDB 6: protocol-based record container? This could avoid computing & encoding values we do not need.
GRDB 6: encoding & statement binding errors for database values (conversion to DatabaseValue, statement binding, etc)
GRDB 6: Swift 5.7
GRDB 6: any / some
GRDB 6: primary associated types (cursor, requests, ...)
GRDB 6: remove existential/generic duplicated methods
GRDB 6: remove useless AnyXXX Type erasers
GRDB 6: conflict resolution in persistence methods
GRDB 6: UPSERT
GRDB 6: support for RETURNING
Player.insert(db, as: FullPlayer.self)[?] GRDB 6: allow mutating update (for timestamps)
[?] GRDB 6: let record choose persistence table (insert(into:) ?)
[?] GRDB 6: Support opaque return types (macOS Catalina, iOS 13, tvOS 13, watchOS 6 and later: https://stackoverflow.com/questions/56518406)
Long run edition. Use case: user edits the database (CRUD) but the application wants to commit and the end of the editing session.
Can we use generated columns to makes it convenient to index on inserted JSON objects? https://github.com/apple/swift-package-manager/pull/3090#issuecomment-740091760
Look at @FetchRequest: managed object context is stored in the environment, and error processing happens somewhere else (where?).
Handle SQLITE_LIMIT_VARIABLE_NUMBER in deleteAll(_:keys:) and similar APIs. https://www.sqlite.org/limits.html
Subqueries: request.isEmpty / [X] request.exists()
Subqueries: request.count
Extract one row from a hasMany association (the one with the maximum date, the one with a flag set, etc.) https://stackoverflow.com/questions/43188771/sqlite-join-query-most-recent-posts-by-each-user (failed PR: https://github.com/groue/GRDB.swift/pull/767)
Turn a hasMany to hasOne without first/last : hasMany(Book.self).filter(Column("isBest") /* assume a single book is flagged best */).asOne()
Support for more kinds of joins: https://github.com/groue/GRDB.swift/issues/740
HasAndBelongsToMany: https://github.com/groue/GRDB.swift/issues/711
Support UNION https://github.com/groue/GRDB.swift/issues/671 (https://www.sqlite.org/lang_select.html#compound)
Measure the duration of transactions
Improve SQL generation for Player.....fetchCount(db), especially with distinct. Try to avoid SELECT COUNT(*) FROM (SELECT DISTINCT player.* ...)
Alternative technique for custom SQLite builds: see the Podfile at https://github.com/CocoaPods/CocoaPods/issues/9104, and https://github.com/clemensg/sqlite3pod
Attach databases. Interesting question: what happens when one attaches a non-WAL db to a databasePool?
SQL Generation
Allow concurrent reads from a snapshot
More schema alterations
Database.clearSchemaCache() is fine, but what about dbPool readers? Can we invalidate the cache for a whole pool?
What can we do with cross-module-optimization? See https://github.com/apple/swift-homomorphic-encryption
GRDB7/BREAKING: insertAndFetch, saveAndFetch, and updateAndFetch no longer return optionals (32f41472)
GRDB7/BREAKING: AsyncValueObservation does not need any scheduler (83c0e643)
GRDB7/BREAKING: Stop exporting SQLite (679d6463)
GRDB7/BREAKING: Remove Configuration.defaultTransactionKind (2661ff46)
GRDB7: Replace LockedBox with Mutex (00ccab06)
GRDB7: Sendable: BusyCallback (e0d8e20b)
GRDB7: Sendable: BusyMode (e0d8e20b)
GRDB7: Sendable: TransactionClock (f7dc72a5)
GRDB7: Sendable: Configuration (54ffb21f)
GRDB7: Sendable: DatabaseDataEncodingStrategy (264d7fb5)
GRDB7: Sendable: DatabaseDateEncodingStrategy (264d7fb5)
GRDB7: Sendable: DatabaseColumnEncodingStrategy (264d7fb5)
GRDB7: Sendable: DatabaseDataDecodingStrategy (264d7fb5)
GRDB7: Sendable: DatabaseDateDecodingStrategy (264d7fb5)
GRDB7: Sendable: DatabaseColumnDecodingStrategy (264d7fb5)
GRDB7/BREAKING: Remove DatabaseFuture and concurrentRead (05f7d3c8)
GRDB7: Sendable: DatabaseFunction (6e691fe7)
GRDB7: Sendable: DatabaseMigrator (22114ad4)
GRDB7: Not Sendable: FilterCursor (b26e9709)
GRDB7: Sendable: RowAdapter (d138af26)
GRDB7: Sendable: ValueObservationScheduler (8429eb68)
GRDB7: Sendable: DatabaseCollation (4d9d67dd)
GRDB7: Sendable: LogErrorFunction (f362518d)
GRDB7: Sendable: ReadWriteBox (57a86a0e)
GRDB7: Sendable: Pool (f13b2d2e)
GRDB7: Sendable: OnDemandFuture fulfill (2aabc4c1)
GRDB7: Sendable: WALSnapshotTransaction (7fd34012)
[-] GRDB7: sending closures for SerializedDatabase
[-] GRDB7: sending closures for ValueObservationScheduler
GRDB7: Sendable closures for ValueObservation.handleEvents
GRDB7: Not Sendable: Record (make it explicit if subclasses can be made sendable)
GRDB7: Not Sendable: databasepublishers/databaseregion, migrate, read, value, write
GRDB7: Sendable closures for writePublisher
GRDB7: Sendable closures for readPublisher
[-] GRDB7: Not Sendable: fts5customtokenizer, fts5tokenizer, fts5wrappertokenizer
GRDB7: Sendable: DatabasePromise (05899228, 5a2c15b8)
GRDB7: Sendable: TableAlias (f2b0b186)
GRDB7: Sendable: SQLRelation (9545bf70)
GRDB7: Sendable: SQL (ac33856f)
GRDB7: Split Row.swift (2ce8a619)
GRDB7: Cleanup ValueReducer (6c73b1c5)
GRDB7: DatabaseCursor has a primary associated type (b11c5dd2)
GRDB7: Enable Strict Concurrency Checks (6aa43ded)
GRDB7: Sendable: OrderedDictionary (e022c35b)
GRDB7: Rename ReadWriteBox to ReadWriteLock (7f5205ef)
GRDB7: Sendable: DatabaseRegionConvertible (b4677ded)
GRDB7: Sendable: ValueConcurrentObserver (87b9db65, 5465d056)
GRDB7: Sendable: ValueWriteOnlyObserver (ff2a7548)
GRDB7: Sendable: DatabaseCancellable (2f93f00b, 8f486a5e)
GRDB7: ValueObservation closures
[?] GRDB7: DatabasePublishers.ValueSubscription
GRDB7: Sendable: ValueObservation (93f6f982)
[?] GRDB7: Not Sendable: SharedValueObservation
GRDB7: doc (c0838cf9)
GRDB7/BREAKING: PersistenceContainer is Sendable (50eefa8c)
GRDB7: TableRecord.databaseSelection must be declared as a computed property (24d232aa)
GRDB7: Sendable: Association (b06aaee4)
GRDB7/Tests: Sendable: ValueObservationRecorder (2947b3d7)
GRDB7: ValueObservation.print cautiously uses its stream argument (5f8b39b7)
GRDB7/Tests: use a single and Sendable test TextOutputStream (bbb1a736)
GRDB7: ValueObservation needs a ValueReducer, not a _ValueReducer (08733108)
GRDB7: Database support for cancellation (4ddf4bca)
GRDB7: SerializedDatabase support for async db access with support for Task cancellation (737cb149)
GRDB7: DatabaseWriter async methods support Task cancellation (a5226501)
GRDB7: DatabaseReader async methods support Task cancellation (10c9d311)
GRDB7: Document that async methods can throw CancellationError (8df18fb8)
[-] GRDB7: Sendable: AssociationAggregate (48ad10ae)
GRDB7: Sendable: AsyncValueObservation (necessary for async algorithm) (ce63cdfa)
GRDB7: Sendable: DatabaseRegionObservation (b4ff52fb)
[-] GRDB7: DispatchQueue.asyncSending (7b075e6b)
GRDB7: Replace sequences with collection (e.g. https://github.com/tidal-music/tidal-sdk-ios/pull/39)
GRDB7: Replace some DatabaseReader/Writer with any where possible, in order to avoid issues with accessing DatabaseContext from GRDBQuery (if the problem exists in Xcode 16)
GRDB7: bump to iOS 13, macOS 10.15, tvOS 13 (for ValueObservation support for MainActor)
GRDB7: DatabasePublishers.Value should carry the type of the Scheduler, so that we can rely on main-actor-isolated callbacks.
GRDB7: Remove warning about "products" in Package.swift
GRDB7: Fixits
GRDB7: Swift Concurrency recommendations
GRDB7: stop fostering the Record class
GRDB7: Breaking changes documentation
TableRecord.databaseSelection should be declared as a computed static propertyGRDB7: Review experimental apis
[?] GRDB7: Change ValueObservation callback argument so that it could expose snapshots? https://github.com/groue/GRDB.swift/discussions/1523#discussioncomment-9092500
Association limits: Author.including(optional: Author.books.order(date.desc).first)
Joins and full-text tables
Support for "INSERT INTO ... SELECT ...".
Look at the jazzy configuration of https://github.com/bignerdranch/Deferred
Predicates, so that a filter can be evaluated both on the database, and on a record instance.
After investigation, we can't do it reliably without knowing the collation used by a column. And SQLite does not provide this information elsewhere than in the full CREATE TABLE statement stored in sqlite_master.
ValueObservation erasure
// Do better than this
observation.mapReducer { _, reducer in AnyValueReducer(reducer) }
new.updateChanges(from: old) vs. old.updateChanges(with: { old.a = new.a }). This is confusing.