GRDB.swift v1.2.0 Release Notes

  • ๐Ÿš€ Released July 13, 2017 • diff

    ๐Ÿ†• New

    • ๐Ÿ‘ Record types that do not provide values for all table columns in their encode(to:) method are now supported.

    • The table creation API has been enhanced:

      • SQLite supports untyped columns:

        try dbQueue.inDatabase { db in
            // CREATE TABLE t(a, b)
            try db.create(table: "t") { t in
                t.column("a")
                t.column("b")
            }
        }
        

        Untyped columns behave like decimal, boolean, date columns, and generally all columns with NUMERIC type affinity.

        This feature addresses #169.

      • The indexed() methods lets you create a non-unique index on a table column:

        try dbQueue.inDatabase { db in
            // CREATE TABLE rounds(score INTEGER)
            // CREATE INDEX rounds_on_score ON rounds(score)
            try db.create(table: "rounds") { t in
                t.column("score", .integer).indexed()
            }
        }
        
      • It is now possible to define references to tables without any explicit primary key. The generated SQL then uses the rowid hidden primary key column:

        try dbQueue.inDatabase { db in
            // CREATE TABLE nodes(
            //   name TEXT,
            //   parentId INTEGER REFERENCES nodes(rowid)
            // )
            try db.create(table: "nodes") { t in
                t.column("name", .text)
                t.column("parentId", .integer).references("nodes")
            }
        }
        
    • ๐Ÿ“š DatabaseQueue, DatabasePool and their common protocol DatabaseReader can now perform "unsafe reentrant reads" (documentation):

      try dbPool.read { db in
          // This is allowed
          try dbPool.unsafeReentrantRead { db in
              ...
          }
      }
      

    ๐Ÿ›  Fixed

    • DatabasePool.read, and DatabasePool.unsafeRead now raise a fatal error when used in a reentrant way:

      try dbPool.read { db in
          // fatal error: "Database methods are not reentrant."
          try dbPool.read { db in
              ...
          }
      }
      

      While this change may appear as a breaking change, it is really a fix: reentrant reads deadlock as soon as the maximum number of readers has been reached.

    API diff

     final class ColumnDefinition {
    +    @discardableResult func indexed() -> Self
     }
    
     final class TableDefinition {
    -    func column(_ name: String, _ type: Database.ColumnType) -> ColumnDefinition
    +    func column(_ name: String, _ type: Database.ColumnType? = nil) -> ColumnDefinition
     }
    
     protocol DatabaseReader {
    +    func unsafeReentrantRead<T>(_ block: (Database) throws -> T) throws -> T
     }