TableRecord

public protocol TableRecord

Types that adopt TableRecord declare a particular relationship with a database table.

Types that adopt both TableRecord and FetchableRecord are granted with built-in methods that allow to fetch instances identified by key:

try Player.fetchOne(db, key: 123)  // Player?
try Citizenship.fetchOne(db, key: ["citizenId": 12, "countryId": 45]) // Citizenship?

TableRecord is adopted by Record.

  • databaseTableName Default implementation

    The name of the database table used to build requests.

    struct Player : TableRecord {
        static var databaseTableName = "player"
    }
    
    // SELECT * FROM player
    try Player.fetchAll(db)
    

    Default Implementation

    The default name of the database table used to build requests.

    • Player -> player
    • Place -> place
    • PostalAddress -> postalAddress
    • HTTPRequest -> httpRequest
    • TOEFL -> toefl

    Declaration

    Swift

    static var databaseTableName: String
  • databaseSelection Default implementation

    The default request selection.

    Unless said otherwise, requests select all columns:

    // SELECT * FROM player
    try Player.fetchAll(db)
    

    You can provide a custom implementation and provide an explicit list of columns:

    struct RestrictedPlayer : TableRecord {
        static var databaseTableName = "player"
        static var databaseSelection = [Column("id"), Column("name")]
    }
    
    // SELECT id, name FROM player
    try RestrictedPlayer.fetchAll(db)
    

    You can also add extra columns such as the rowid column:

    struct ExtendedPlayer : TableRecord {
        static var databaseTableName = "player"
        static let databaseSelection: [SQLSelectable] = [AllColumns(), Column.rowID]
    }
    
    // SELECT *, rowid FROM player
    try ExtendedPlayer.fetchAll(db)
    

    Default Implementation

    Default value: [AllColumns()].

    Declaration

    Swift

    static var databaseSelection: [SQLSelectable]
  • belongsTo(_:key:using:) Extension method

    Creates a Belongs To association between Self and the destination type.

    struct Author: TableRecord { ... }
    struct Book: TableRecord {
        static let author = belongsTo(Author.self)
    }
    

    The association will let you define requests that load both the source and the destination type:

    // A request for all books with their authors:
    let request = Book.including(optional: Book.author)
    

    To consume those requests, define a type that adopts both the FetchableRecord and Decodable protocols:

    struct BookInfo: FetchableRecord, Decodable {
        var book: Book
        var author: Author?
    }
    
    let bookInfos = try dbQueue.read { db in
        return try BookInfo.fetchAll(db, request)
    }
    for bookInfo in bookInfos {
        print("\(bookInfo.book.title) by \(bookInfo.author.name)")
    }
    

    It is recommended that you define, alongside the association, a property with the same name:

    struct Book: TableRecord {
        static let author = belongsTo(Author.self)
        var author: QueryInterfaceRequest<Author> {
            return request(for: Book.author)
        }
    }
    

    This property will let you navigate from the source type to the destination type:

    try dbQueue.read { db in
        let book: Book = ...
        let author = try book.author.fetchOne(db) // Author?
    }
    

    Declaration

    Swift

    public static func belongsTo<Destination>(
        _ destination: Destination.Type,
        key: String? = nil,
        using foreignKey: ForeignKey? = nil)
        -> BelongsToAssociation<Self, Destination>
        where Destination: TableRecord

    Parameters

    destination

    The record type at the other side of the association.

    key

    An eventual decoding key for the association. By default, it is destination.databaseTableName.

    foreignKey

    An eventual foreign key. You need to provide an explicit foreign key when GRDB can’t infer one from the database schema. This happens when the schema does not define any foreign key to the destination table, or when the schema defines several foreign keys to the destination table.

  • matching(_:) Extension method

    Returns a QueryInterfaceRequest with a matching predicate.

    // SELECT * FROM book WHERE book MATCH '...'
    var request = Book.matching(pattern)
    

    If the search pattern is nil, the request does not match any database row.

    The selection defaults to all columns. This default can be changed for all requests by the TableRecord.databaseSelection property, or for individual requests with the TableRecord.select method.

    Declaration

    Swift

    public static func matching(_ pattern: FTS3Pattern?) -> QueryInterfaceRequest<Self>
  • hasMany(_:key:using:) Extension method

    Creates a Has many association between Self and the destination type.

    struct Book: TableRecord { ... }
    struct Author: TableRecord {
        static let books = hasMany(Book.self)
    }
    

    The association will let you define requests that load both the source and the destination type:

    // A request for all (author, book) pairs:
    let request = Author.including(required: Author.books)
    

    To consume those requests, define a type that adopts both the FetchableRecord and Decodable protocols:

    struct Authorship: FetchableRecord, Decodable {
        var author: Author
        var book: Book
    }
    
    let authorships = try dbQueue.read { db in
        return try Authorship.fetchAll(db, request)
    }
    for authorship in authorships {
        print("\(authorship.author.name) wrote \(authorship.book.title)")
    }
    

    It is recommended that you define, alongside the association, a property with the same name:

    struct Author: TableRecord {
        static let books = hasMany(Book.self)
        var books: QueryInterfaceRequest<Book> {
            return request(for: Author.books)
        }
    }
    

    This property will let you navigate from the source type to the destination type:

    try dbQueue.read { db in
        let author: Author = ...
        let books = try author.books.fetchAll(db) // [Book]
    }
    

    Declaration

    Swift

    public static func hasMany<Destination>(
        _ destination: Destination.Type,
        key: String? = nil,
        using foreignKey: ForeignKey? = nil)
        -> HasManyAssociation<Self, Destination>
        where Destination: TableRecord

    Parameters

    destination

    The record type at the other side of the association.

    key

    An eventual decoding key for the association. By default, it is destination.databaseTableName.

    foreignKey

    An eventual foreign key. You need to provide an explicit foreign key when GRDB can’t infer one from the database schema. This happens when the schema does not define any foreign key from the destination table, or when the schema defines several foreign keys from the destination table.

  • hasOne(_:key:using:) Extension method

    Creates a Has one association between Self and the destination type.

    struct Demographics: TableRecord { ... }
    struct Country: TableRecord {
        static let demographics = hasOne(Demographics.self)
    }
    

    The association will let you define requests that load both the source and the destination type:

    // A request for all countries with their demographic profile:
    let request = Country.including(optional: Country.demographics)
    

    To consume those requests, define a type that adopts both the FetchableRecord and Decodable protocols:

    struct CountryInfo: FetchableRecord, Decodable {
        var country: Country
        var demographics: Demographics?
    }
    
    let countryInfos = try dbQueue.read { db in
        return try CountryInfo.fetchAll(db, request)
    }
    for countryInfo in countryInfos {
        print("\(countryInfo.country.name) has \(countryInfo.demographics.population) citizens")
    }
    

    It is recommended that you define, alongside the association, a property with the same name:

    struct Country: TableRecord {
        static let demographics = hasOne(Demographics.self)
        var demographics: QueryInterfaceRequest<Demographics> {
            return request(for: Country.demographics)
        }
    }
    

    This property will let you navigate from the source type to the destination type:

    try dbQueue.read { db in
        let country: Country = ...
        let demographics = try country.demographics.fetchOne(db) // Demographics?
    }
    

    Declaration

    Swift

    public static func hasOne<Destination>(
        _ destination: Destination.Type,
        key: String? = nil,
        using foreignKey: ForeignKey? = nil)
        -> HasOneAssociation<Self, Destination>
        where Destination: TableRecord

    Parameters

    destination

    The record type at the other side of the association.

    key

    An eventual decoding key for the association. By default, it is destination.databaseTableName.

    foreignKey

    An eventual foreign key. You need to provide an explicit foreign key when GRDB can’t infer one from the database schema. This happens when the schema does not define any foreign key from the destination table, or when the schema defines several foreign keys from the destination table.

  • including(optional:) Extension method

    Creates a request that includes an association. The columns of the associated record are selected. The returned association does not require that the associated database table contains a matching row.

    Declaration

    Swift

    public static func including<A: Association>(optional association: A) -> QueryInterfaceRequest<Self> where A.OriginRowDecoder == Self
  • including(required:) Extension method

    Creates a request that includes an association. The columns of the associated record are selected. The returned association requires that the associated database table contains a matching row.

    Declaration

    Swift

    public static func including<A: Association>(required association: A) -> QueryInterfaceRequest<Self> where A.OriginRowDecoder == Self
  • joining(optional:) Extension method

    Creates a request that includes an association. The columns of the associated record are not selected. The returned association does not require that the associated database table contains a matching row.

    Declaration

    Swift

    public static func joining<A: Association>(optional association: A) -> QueryInterfaceRequest<Self> where A.OriginRowDecoder == Self
  • joining(required:) Extension method

    Creates a request that includes an association. The columns of the associated record are not selected. The returned association requires that the associated database table contains a matching row.

    Declaration

    Swift

    public static func joining<A: Association>(required association: A) -> QueryInterfaceRequest<Self> where A.OriginRowDecoder == Self
  • all() Extension method

    Creates a request which fetches all records.

    // SELECT * FROM player
    let request = Player.all()
    

    The selection defaults to all columns. This default can be changed for all requests by the TableRecord.databaseSelection property, or for individual requests with the TableRecord.select method.

    Declaration

    Swift

    public static func all() -> QueryInterfaceRequest<Self>
  • none() Extension method

    Creates a request which fetches no record.

    Declaration

    Swift

    public static func none() -> QueryInterfaceRequest<Self>
  • select(_:) Extension method

    Creates a request which selects selection.

    // SELECT id, email FROM player
    let request = Player.select(Column("id"), Column("email"))
    

    Declaration

    Swift

    public static func select(_ selection: SQLSelectable...) -> QueryInterfaceRequest<Self>
  • select(_:) Extension method

    Creates a request which selects selection.

    // SELECT id, email FROM player
    let request = Player.select([Column("id"), Column("email")])
    

    Declaration

    Swift

    public static func select(_ selection: [SQLSelectable]) -> QueryInterfaceRequest<Self>
  • select(sql:arguments:) Extension method

    Creates a request which selects sql.

    // SELECT id, email FROM player
    let request = Player.select(sql: "id, email")
    

    Declaration

    Swift

    public static func select(sql: String, arguments: StatementArguments? = nil) -> QueryInterfaceRequest<Self>
  • filter(_:) Extension method

    Creates a request with the provided predicate.

    // SELECT * FROM player WHERE email = 'arthur@example.com'
    let request = Player.filter(Column("email") == "arthur@example.com")
    

    The selection defaults to all columns. This default can be changed for all requests by the TableRecord.databaseSelection property, or for individual requests with the TableRecord.select method.

    Declaration

    Swift

    public static func filter(_ predicate: SQLExpressible) -> QueryInterfaceRequest<Self>
  • filter(key:) Extension method

    Creates a request with the provided primary key predicate.

    // SELECT * FROM player WHERE id = 1
    let request = Player.filter(key: 1)
    

    The selection defaults to all columns. This default can be changed for all requests by the TableRecord.databaseSelection property, or for individual requests with the TableRecord.select method.

    Declaration

    Swift

    public static func filter<PrimaryKeyType: DatabaseValueConvertible>(key: PrimaryKeyType?) -> QueryInterfaceRequest<Self>
  • filter(keys:) Extension method

    Creates a request with the provided primary key predicate.

    // SELECT * FROM player WHERE id IN (1, 2, 3)
    let request = Player.filter(keys: [1, 2, 3])
    

    The selection defaults to all columns. This default can be changed for all requests by the TableRecord.databaseSelection property, or for individual requests with the TableRecord.select method.

    Declaration

    Swift

    public static func filter<Sequence: Swift.Sequence>(keys: Sequence) -> QueryInterfaceRequest<Self> where Sequence.Element: DatabaseValueConvertible
  • filter(key:) Extension method

    Creates a request with the provided primary key predicate.

    // SELECT * FROM passport WHERE personId = 1 AND countryCode = 'FR'
    let request = Passport.filter(key: ["personId": 1, "countryCode": "FR"])
    

    When executed, this request raises a fatal error if there is no unique index on the key columns.

    The selection defaults to all columns. This default can be changed for all requests by the TableRecord.databaseSelection property, or for individual requests with the TableRecord.select method.

    Declaration

    Swift

    public static func filter(key: [String: DatabaseValueConvertible?]?) -> QueryInterfaceRequest<Self>
  • filter(keys:) Extension method

    Creates a request with the provided primary key predicate.

    // SELECT * FROM passport WHERE (personId = 1 AND countryCode = 'FR') OR ...
    let request = Passport.filter(keys: [["personId": 1, "countryCode": "FR"], ...])
    

    When executed, this request raises a fatal error if there is no unique index on the key columns.

    The selection defaults to all columns. This default can be changed for all requests by the TableRecord.databaseSelection property, or for individual requests with the TableRecord.select method.

    Declaration

    Swift

    public static func filter(keys: [[String: DatabaseValueConvertible?]]) -> QueryInterfaceRequest<Self>
  • filter(sql:arguments:) Extension method

    Creates a request with the provided predicate.

    // SELECT * FROM player WHERE email = 'arthur@example.com'
    let request = Player.filter(sql: "email = ?", arguments: ["arthur@example.com"])
    

    The selection defaults to all columns. This default can be changed for all requests by the TableRecord.databaseSelection property, or for individual requests with the TableRecord.select method.

    Declaration

    Swift

    public static func filter(sql: String, arguments: StatementArguments? = nil) -> QueryInterfaceRequest<Self>
  • order(_:) Extension method

    Creates a request sorted according to the provided orderings.

    // SELECT * FROM player ORDER BY name
    let request = Player.order(Column("name"))
    

    The selection defaults to all columns. This default can be changed for all requests by the TableRecord.databaseSelection property, or for individual requests with the TableRecord.select method.

    Declaration

    Swift

    public static func order(_ orderings: SQLOrderingTerm...) -> QueryInterfaceRequest<Self>
  • order(_:) Extension method

    Creates a request sorted according to the provided orderings.

    // SELECT * FROM player ORDER BY name
    let request = Player.order([Column("name")])
    

    The selection defaults to all columns. This default can be changed for all requests by the TableRecord.databaseSelection property, or for individual requests with the TableRecord.select method.

    Declaration

    Swift

    public static func order(_ orderings: [SQLOrderingTerm]) -> QueryInterfaceRequest<Self>
  • orderByPrimaryKey() Extension method

    Creates a request sorted by primary key.

    // SELECT * FROM player ORDER BY id
    let request = Player.orderByPrimaryKey()
    
    // SELECT * FROM country ORDER BY code
    let request = Country.orderByPrimaryKey()
    

    The selection defaults to all columns. This default can be changed for all requests by the TableRecord.databaseSelection property, or for individual requests with the TableRecord.select method.

    Declaration

    Swift

    public static func orderByPrimaryKey() -> QueryInterfaceRequest<Self>
  • order(sql:arguments:) Extension method

    Creates a request sorted according to sql.

    // SELECT * FROM player ORDER BY name
    let request = Player.order(sql: "name")
    

    The selection defaults to all columns. This default can be changed for all requests by the TableRecord.databaseSelection property, or for individual requests with the TableRecord.select method.

    Declaration

    Swift

    public static func order(sql: String, arguments: StatementArguments? = nil) -> QueryInterfaceRequest<Self>
  • limit(_:offset:) Extension method

    Creates a request which fetches limit rows, starting at offset.

    // SELECT * FROM player LIMIT 1
    let request = Player.limit(1)
    

    The selection defaults to all columns. This default can be changed for all requests by the TableRecord.databaseSelection property, or for individual requests with the TableRecord.select method.

    Declaration

    Swift

    public static func limit(_ limit: Int, offset: Int? = nil) -> QueryInterfaceRequest<Self>
  • aliased(_:) Extension method

    Creates a request that allows you to define expressions that target a specific database table.

    In the example below, the team.avgScore < player.score condition in the ON clause could be not achieved without table aliases.

    struct Player: TableRecord {
        static let team = belongsTo(Team.self)
    }
    
    // SELECT player.*, team.*
    // JOIN team ON ... AND team.avgScore < player.score
    let playerAlias = TableAlias()
    let request = Player
        .aliased(playerAlias)
        .including(required: Player.team.filter(Column("avgScore") < playerAlias[Column("score")])
    

    Declaration

    Swift

    public static func aliased(_ alias: TableAlias) -> QueryInterfaceRequest<Self>
  • fetchCount(_:) Extension method

    The number of records.

    Declaration

    Swift

    public static func fetchCount(_ db: Database) throws -> Int

    Parameters

    db

    A database connection.

  • selectionSQL(alias:) Extension method

    The selection as an SQL String.

    For example:

    struct Player: TableRecord {
        static let databaseTableName = "player"
    }
    
    // SELECT "player".* FROM player
    let sql = "SELECT \(Player.selectionSQL()) FROM player"
    
    // SELECT "p".* FROM player AS p
    let sql = "SELECT \(Player.selectionSQL(alias: "p")) FROM player p"
    

    Declaration

    Swift

    public static func selectionSQL(alias: String? = nil) -> String
  • numberOfSelectedColumns(_:) Extension method

    Returns the number of selected columns.

    For example:

    struct Player: TableRecord {
        static let databaseTableName = "player"
    }
    
    try dbQueue.write { db in
        try db.create(table: "player") { t in
            t.autoIncrementedPrimaryKey("id")
            t.column("name", .text)
            t.column("score", .integer)
        }
    
        // 3
        try Player.numberOfSelectedColumns(db)
    }
    

    Declaration

    Swift

    public static func numberOfSelectedColumns(_ db: Database) throws -> Int