GRDB.swift v2.9.0 Release Notes

  • ๐Ÿš€ Released February 25, 2018 • diff

    ๐Ÿ†• New

    • ๐Ÿ”„ Changes tracking overhaul: changes tracking, a feature previously restricted to the Record class and its subclasses, is now available for all records. And it has a better looking API (documentation).
    • Database snapshots: Database pools can now take database snapshots. A snapshot sees an unchanging database content, as it existed at the moment the snapshot was created (documentation).
    • ๐Ÿ‘Œ Improved support for joined queries: more than a set on new APIs, we provide a set of guidelines that will help you deal with your wildest joined queries. Check the new Joined Queries Support documentation chapter.
    • Database.columns(in:) returns information about the columns of a table.
    • Request.adapted(_:) is no longer experimental.
    • ๐Ÿ‘€ Configuration.allowsUnsafeTransactions lets you leave transactions opened between two database accesses (see below).
    • ๐Ÿ‘Œ Support for explicit transaction management, via the new Database.beginTransaction, commit, and rollback methods.

    ๐Ÿ›  Fixed

    • It is now a programmer error to leave a transaction opened at the end of a database access block:

      // Fatal error: A transaction has been left opened at the end of a database access
      try dbQueue.inDatabase { db in
          try db.beginTransaction()
      }
      

      One can still opt-in for the unsafe behavior by setting the new allowsUnsafeTransactions configuration flag:

      var config = Configuration()
      config.allowsUnsafeTransactions = true
      let dbQueue = DatabaseQueue(configuration: config)
      
      // OK
      try dbQueue.inDatabase { db in
          try db.beginTransaction()
      }
      

    ๐Ÿ—„ Deprecated

    • ๐Ÿ—„ Database.columnCount(in:) is deprecated. Use db.columns(in:).count instead.
    • ๐Ÿ—„ RecordBox, introduced in 2.7.0, was ill-advised. It has been deprecated. Use changes tracking methods on the Persistable protocol instead.
    • ๐Ÿ—„ Record.hasPersistentChangedValues has been deprecated, renamed hasDatabaseChanges.
    • ๐Ÿ—„ Record.persistentChangedValues has been deprecated, renamed databaseChanges.

    ๐Ÿ“š Documentation Diff

    API diff

    +struct ColumnInfo {
    +    let name: String
    +    let type: String
    +    let isNotNull: Bool
    +    let defaultValueSQL: String?
    +    let primaryKeyIndex: Int
    +}
    
     struct Configuration {
    +    var allowsUnsafeTransactions: Bool
     }
    
     class Database {
    +     @available(*, deprecated, message: "Use db.columns(in: tableName).count instead")
          func columnCount(in tableName: String) throws -> Int
    +     func columns(in tableName: String) throws -> [ColumnInfo]
    +     func beginTransaction(_ kind: TransactionKind? = nil) throws
    +     func rollback() throws
    +     func commit() throws
     }
    
     class DatabasePool {
    +    func makeSnapshot() throws -> DatabaseSnapshot
     }
    
    +class DatabaseSnapshot: DatabaseReader { }
    
     extension MutablePersistable {
    +    @discardableResult
    +    func updateChanges(_ db: Database, from record: MutablePersistable) throws -> Bool
    +    func databaseEqual(_ record: Self) -> Bool
    +    func databaseChanges(from record: MutablePersistable) -> [String: DatabaseValue]
     }
     class Record {
    -    final func updateChanges(_ db: Database) throws
    +    @discardableResult
    +    final func updateChanges(_ db: Database) throws -> Bool
     }
    
    +@available(*, deprecated, message: "Prefer changes methods defined on the MutablePersistable protocol: databaseEqual(_:), databaseChanges(from:), updateChanges(from:)")
     class RecordBox: Record { }
    
     class Row {
    +    var unscoped: Row
    +    var containsNonNullValue: Bool
    +    func hasNull(atIndex index: Int) -> Bool
    +    subscript<Record: RowConvertible>(_ scope: String) -> Record
    +    subscript<Record: RowConvertible>(_ scope: String) -> Record?
     }
    
     extension TableMapping {
    +    static func selectionSQL(alias: String? = nil) -> String
    +    static func numberOfSelectedColumns(_ db: Database) throws -> Int
     }
    
    +struct EmptyRowAdapter: RowAdapter { }
    
     struct ScopeAdapter {
    +    init(base: RowAdapter, scopes: [String: RowAdapter])
     }
    
    +func splittingRowAdapters(columnCounts: [Int]) -> [RowAdapter]