Skip to content

Latest commit

 

History

History
146 lines (128 loc) · 10.3 KB

File metadata and controls

146 lines (128 loc) · 10.3 KB

Cleanup

Documentation

  • Enhance the introduction to SQLRequest, based on the feedback in groue#617
  • Association: document how to use aggregates with inner join (testAnnotatedWithHasManyDefaultMaxJoiningRequired)
  • Should we document that PRAGMA locking_mode = EXCLUSIVE improves performances? https://sqlite.org/forum/forumpost/866bf3407a

Features

  • 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
    • throwing FetchableRecord initializer FIRST
    • throwing Decodable FetchableRecord initializer SECOND
    • deal with as much value decoding error as possible
    • [?] expose throwing row accessors
  • 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:))
    • throwing EncodableRecord.encode FIRST
  • [?] 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)
    • Prevent Date > 9999 from being encoded
  • 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
    • Support for default values: 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.
    • Create an edition SQLite connection with an open transaction (a new kind of DatabaseWriter with a save() method)
    • All other writes will fail with SQLITE_BUSY. Unless they are schedules in a target dispatch queue which is paused during the edition.
  • Can we use generated columns to makes it convenient to index on inserted JSON objects? swiftlang/swift-package-manager#3090 (comment)
  • 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: groue#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: groue#740
  • HasAndBelongsToMany: groue#711
  • Support UNION groue#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 CocoaPods/CocoaPods#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
  • Check https://sqlite.org/sqlar.html
  • More schema alterations
  • Database.clearSchemaCache() is fine, but what about dbPool readers? Can we invalidate the cache for a whole pool?

Unsure if necessary

Unsure how

  • Association limits: Author.including(optional: Author.books.order(date.desc).first)

  • Joins and full-text tables

  • UPSERT https://www.sqlite.org/lang_UPSERT.html

  • 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.

Reading list