[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-6739":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":16,"subscribersCount":16,"size":16,"stars1d":15,"stars7d":17,"stars30d":18,"stars90d":16,"forks30d":16,"starsTrendScore":19,"compositeScore":20,"rankGlobal":10,"rankLanguage":10,"license":21,"archived":22,"fork":22,"defaultBranch":23,"hasWiki":24,"hasPages":24,"topics":25,"createdAt":10,"pushedAt":10,"updatedAt":34,"readmeContent":35,"aiSummary":36,"trendingCount":16,"starSnapshotCount":16,"syncStatus":37,"lastSyncTime":38,"discoverSource":39},6739,"GRDB.swift","groue\u002FGRDB.swift","groue","A toolkit for SQLite databases, with a focus on application development","",null,"Swift",8460,948,81,4,0,22,88,18,39.93,"MIT License",false,"master",true,[26,27,28,29,30,31,32,33],"database","database-observation","grdb","spm","sql","sql-builder","sqlite","sqlite-databases","2026-06-12 02:01:29","\u003Cpicture>\n    \u003Csource media=\"(prefers-color-scheme: dark)\" srcset=\"https:\u002F\u002Fraw.githubusercontent.com\u002Fgroue\u002FGRDB.swift\u002Fmaster\u002FGRDB~dark.png\">\n    \u003Csource media=\"(prefers-color-scheme: light)\" srcset=\"https:\u002F\u002Fraw.githubusercontent.com\u002Fgroue\u002FGRDB.swift\u002Fmaster\u002FGRDB.png\">\n    \u003Cimg alt=\"GRDB: A toolkit for SQLite databases, with a focus on application development.\" src=\"https:\u002F\u002Fraw.githubusercontent.com\u002Fgroue\u002FGRDB.swift\u002Fmaster\u002FGRDB.png\">\n\u003C\u002Fpicture>\n\n\u003Cp align=\"center\">\n    \u003Cstrong>A toolkit for SQLite databases, with a focus on application development\u003C\u002Fstrong>\u003Cbr>\n    Proudly serving the community since 2015\n\u003C\u002Fp>\n\n\u003Cp align=\"center\">\n    \u003Ca href=\"https:\u002F\u002Fdeveloper.apple.com\u002Fswift\u002F\">\u003Cimg alt=\"Swift 6.1\" src=\"https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fswift-6.1-orange.svg?style=flat\">\u003C\u002Fa>\n    \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fgroue\u002FGRDB.swift\u002Fblob\u002Fmaster\u002FLICENSE\">\u003Cimg alt=\"License\" src=\"https:\u002F\u002Fimg.shields.io\u002Fgithub\u002Flicense\u002Fgroue\u002FGRDB.swift.svg?maxAge=2592000\">\u003C\u002Fa>\n    \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fgroue\u002FGRDB.swift\u002Factions\u002Fworkflows\u002FCI.yml\">\u003Cimg alt=\"CI Status\" src=\"https:\u002F\u002Fgithub.com\u002Fgroue\u002FGRDB.swift\u002Factions\u002Fworkflows\u002FCI.yml\u002Fbadge.svg?branch=master\">\u003C\u002Fa>\n\u003C\u002Fp>\n\n**Latest release**: February 15, 2026 • [version 7.10.0](https:\u002F\u002Fgithub.com\u002Fgroue\u002FGRDB.swift\u002Ftree\u002Fv7.10.0) • [CHANGELOG](CHANGELOG.md) • [Migrating From GRDB 6 to GRDB 7](Documentation\u002FGRDB7MigrationGuide.md)\n\n**Requirements**: iOS 13.0+ \u002F macOS 10.15+ \u002F tvOS 13.0+ \u002F watchOS 7.0+ &bull; SQLite 3.20.0+ &bull; Swift 6.1+ \u002F Xcode 16.3+\n\n**Contact**:\n\n- Release announcements and usage tips: follow [@groue@hachyderm.io](https:\u002F\u002Fhachyderm.io\u002F@groue) on Mastodon.\n- Report bugs in a [Github issue](https:\u002F\u002Fgithub.com\u002Fgroue\u002FGRDB.swift\u002Fissues\u002Fnew). Make sure you check the [existing issues](https:\u002F\u002Fgithub.com\u002Fgroue\u002FGRDB.swift\u002Fissues?q=is%3Aopen) first.\n- A question? Looking for advice? Do you wonder how to contribute? Fancy a chat? Go to the [GitHub discussions](https:\u002F\u002Fgithub.com\u002Fgroue\u002FGRDB.swift\u002Fdiscussions), or the [GRDB forums](https:\u002F\u002Fforums.swift.org\u002Fc\u002Frelated-projects\u002Fgrdb).\n\n\n## What is GRDB?\n\nUse this library to save your application’s permanent data into SQLite databases. It comes with built-in tools that address common needs:\n\n- **SQL Generation**\n    \n    Enhance your application models with persistence and fetching methods, so that you don't have to deal with SQL and raw database rows when you don't want to.\n\n- **Database Observation**\n    \n    Get notifications when database values are modified. \n\n- **Robust Concurrency**\n    \n    Multi-threaded applications can efficiently use their databases, including WAL databases that support concurrent reads and writes. \n\n- **Migrations**\n    \n    Evolve the schema of your database as you ship new versions of your application.\n    \n- **Leverage your SQLite skills**\n\n    Not all developers need advanced SQLite features. But when you do, GRDB is as sharp as you want it to be. Come with your SQL and SQLite skills, or learn new ones as you go!\n\n---\n\n\u003Cp align=\"center\">\n    \u003Ca href=\"#usage\">Usage\u003C\u002Fa> &bull;\n    \u003Ca href=\"#documentation\">Documentation\u003C\u002Fa> &bull;\n    \u003Ca href=\"#installation\">Installation\u003C\u002Fa> &bull;\n    \u003Ca href=\"#faq\">FAQ\u003C\u002Fa>\n\u003C\u002Fp>\n\n---\n\n## Usage\n\n\u003Cdetails open>\n  \u003Csummary>Start using the database in four steps\u003C\u002Fsummary>\n\n```swift\nimport GRDB\n\n\u002F\u002F 1. Open a database connection\nlet dbQueue = try DatabaseQueue(path: \"\u002Fpath\u002Fto\u002Fdatabase.sqlite\")\n\n\u002F\u002F 2. Define the database schema\ntry dbQueue.write { db in\n    try db.create(table: \"player\") { t in\n        t.primaryKey(\"id\", .text)\n        t.column(\"name\", .text).notNull()\n        t.column(\"score\", .integer).notNull()\n    }\n}\n\n\u002F\u002F 3. Define a record type\nstruct Player: Codable, Identifiable, FetchableRecord, PersistableRecord {\n    var id: String\n    var name: String\n    var score: Int\n    \n    enum Columns {\n        static let name = Column(CodingKeys.name)\n        static let score = Column(CodingKeys.score)\n    }\n}\n\n\u002F\u002F 4. Write and read in the database\ntry dbQueue.write { db in\n    try Player(id: \"1\", name: \"Arthur\", score: 100).insert(db)\n    try Player(id: \"2\", name: \"Barbara\", score: 1000).insert(db)\n}\n\ntry dbQueue.read { db in\n    let player = try Player.find(db, id: \"1\")\n    \n    let bestPlayers = try Player\n        .order(\\.score.desc)\n        .limit(10)\n        .fetchAll(db)\n}\n```\n\n\u003C\u002Fdetails>\n\n\u003Cdetails>\n    \u003Csummary>Access to raw SQL\u003C\u002Fsummary>\n\n```swift\ntry dbQueue.write { db in\n    try db.execute(sql: \"\"\"\n        CREATE TABLE player (\n          id TEXT PRIMARY KEY,\n          name TEXT NOT NULL,\n          score INT NOT NULL)\n        \"\"\")\n    \n    try db.execute(sql: \"\"\"\n        INSERT INTO player (id, name, score)\n        VALUES (?, ?, ?)\n        \"\"\", arguments: [\"1\", \"Arthur\", 100])\n    \n    \u002F\u002F Avoid SQL injection with SQL interpolation\n    let id = \"2\"\n    let name = \"O'Brien\"\n    let score = 1000\n    try db.execute(literal: \"\"\"\n        INSERT INTO player (id, name, score)\n        VALUES (\\(id), \\(name), \\(score))\n        \"\"\")\n}\n```\n\nSee [Executing Updates](#executing-updates)\n\n\u003C\u002Fdetails>\n\n\u003Cdetails>\n    \u003Csummary>Access to raw database rows and values\u003C\u002Fsummary>\n\n```swift\ntry dbQueue.read { db in\n    \u002F\u002F Fetch database rows\n    let rows = try Row.fetchCursor(db, sql: \"SELECT * FROM player\")\n    while let row = try rows.next() {\n        let id: String = row[\"id\"]\n        let name: String = row[\"name\"]\n        let score: Int = row[\"score\"]\n    }\n    \n    \u002F\u002F Fetch values\n    let playerCount = try Int.fetchOne(db, sql: \"SELECT COUNT(*) FROM player\")! \u002F\u002F Int\n    let playerNames = try String.fetchAll(db, sql: \"SELECT name FROM player\") \u002F\u002F [String]\n}\n\nlet playerCount = try dbQueue.read { db in\n    try Int.fetchOne(db, sql: \"SELECT COUNT(*) FROM player\")!\n}\n```\n\nSee [Fetch Queries](#fetch-queries)\n\n\u003C\u002Fdetails>\n\n\u003Cdetails>\n    \u003Csummary>Database model types aka \"records\"\u003C\u002Fsummary>\n\n```swift\nstruct Player: Codable, Identifiable, FetchableRecord, PersistableRecord {\n    var id: String\n    var name: String\n    var score: Int\n    \n    enum Columns {\n        static let name = Column(CodingKeys.name)\n        static let score = Column(CodingKeys.score)\n    }\n}\n\ntry dbQueue.write { db in\n    \u002F\u002F Create database table\n    try db.create(table: \"player\") { t in\n        t.primaryKey(\"id\", .text)\n        t.column(\"name\", .text).notNull()\n        t.column(\"score\", .integer).notNull()\n    }\n    \n    \u002F\u002F Insert a record\n    var player = Player(id: \"1\", name: \"Arthur\", score: 100)\n    try player.insert(db)\n    \n    \u002F\u002F Update a record\n    player.score += 10\n    try player.update(db)\n    \n    try player.updateChanges { $0.score += 10 }\n    \n    \u002F\u002F Delete a record\n    try player.delete(db)\n}\n```\n\nSee [Records](#records)\n\n\u003C\u002Fdetails>\n\n\u003Cdetails>\n    \u003Csummary>Query the database with the Swift query interface\u003C\u002Fsummary>\n\n```swift\ntry dbQueue.read { db in\n    \u002F\u002F Player\n    let player = try Player.find(db, id: \"1\")\n    \n    \u002F\u002F Player?\n    let arthur = try Player.filter { $0.name == \"Arthur\" }.fetchOne(db)\n    \n    \u002F\u002F [Player]\n    let bestPlayers = try Player.order(\\.score.desc).limit(10).fetchAll(db)\n    \n    \u002F\u002F Int\n    let playerCount = try Player.fetchCount(db)\n    \n    \u002F\u002F SQL is always welcome\n    let players = try Player.fetchAll(db, sql: \"SELECT * FROM player\")\n}\n```\n\nSee the [Query Interface](#the-query-interface)\n\n\u003C\u002Fdetails>\n\n\u003Cdetails>\n    \u003Csummary>Database changes notifications\u003C\u002Fsummary>\n\n```swift\n\u002F\u002F Define the observed value\nlet observation = ValueObservation.tracking { db in\n    try Player.fetchAll(db)\n}\n\n\u002F\u002F Start observation\nlet cancellable = observation.start(\n    in: dbQueue,\n    onError: { error in ... },\n    onChange: { (players: [Player]) in print(\"Fresh players: \\(players)\") })\n```\n\nReady-made support for Combine and RxSwift:\n\n```swift\n\u002F\u002F Swift concurrency\nfor try await players in observation.values(in: dbQueue) {\n    print(\"Fresh players: \\(players)\")\n}\n\n\u002F\u002F Combine\nlet cancellable = observation.publisher(in: dbQueue).sink(\n    receiveCompletion: { completion in ... },\n    receiveValue: { (players: [Player]) in print(\"Fresh players: \\(players)\") })\n\n\u002F\u002F RxSwift\nlet disposable = observation.rx.observe(in: dbQueue).subscribe(\n    onNext: { (players: [Player]) in print(\"Fresh players: \\(players)\") },\n    onError: { error in ... })\n```\n\nSee [Database Observation], [Combine Support], [RxGRDB].\n\n\u003C\u002Fdetails>\n\nDocumentation\n=============\n\n**GRDB runs on top of SQLite**: you should get familiar with the [SQLite FAQ](http:\u002F\u002Fwww.sqlite.org\u002Ffaq.html). For general and detailed information, jump to the [SQLite Documentation](http:\u002F\u002Fwww.sqlite.org\u002Fdocs.html).\n\n\n#### Demo Applications & Frequently Asked Questions\n\n- [Demo Applications]\n- [FAQ]\n\n#### Reference\n\n- 📖 [GRDB Reference](https:\u002F\u002Fswiftpackageindex.com\u002Fgroue\u002FGRDB.swift\u002Fdocumentation\u002Fgrdb\u002F)\n\n#### Getting Started\n\n- [Installation](#installation)\n- [Database Connections]: Connect to SQLite databases\n\n#### SQLite and SQL\n\n- [SQLite API](#sqlite-api): The low-level SQLite API &bull; [executing updates](#executing-updates) &bull; [fetch queries](#fetch-queries) &bull; [SQL Interpolation]\n\n#### Records and the Query Interface\n\n- [Records](#records): Fetching and persistence methods for your custom structs and class hierarchies\n- [Query Interface](#the-query-interface): A swift way to generate SQL &bull; [create tables, indexes, etc](https:\u002F\u002Fswiftpackageindex.com\u002Fgroue\u002FGRDB.swift\u002Fdocumentation\u002Fgrdb\u002Fdatabaseschema) &bull; [requests](#requests) • [associations between record types](Documentation\u002FAssociationsBasics.md)\n\n#### Application Tools\n\n- [Migrations]: Transform your database as your application evolves.\n- [Full-Text Search]: Perform efficient and customizable full-text searches.\n- [Database Observation]: Observe database changes and transactions.\n- [Encryption](#encryption): Encrypt your database with SQLCipher.\n- [Backup](#backup): Dump the content of a database to another.\n- [Interrupt a Database](#interrupt-a-database): Abort any pending database operation.\n- [Sharing a Database]: How to share an SQLite database between multiple processes - recommendations for App Group containers, App Extensions, App Sandbox, and file coordination.\n\n#### Good to Know\n\n- [Concurrency]: How to access databases in a multi-threaded application.\n- [Combine](Documentation\u002FCombine.md): Access and observe the database with Combine publishers.\n- [Avoiding SQL Injection](#avoiding-sql-injection)\n- [Error Handling](#error-handling)\n- [Unicode](#unicode)\n- [Memory Management](#memory-management)\n- [Data Protection](https:\u002F\u002Fswiftpackageindex.com\u002Fgroue\u002FGRDB.swift\u002Fdocumentation\u002Fgrdb\u002Fdatabaseconnections)\n- :bulb: [Migrating From GRDB 6 to GRDB 7](Documentation\u002FGRDB7MigrationGuide.md)\n- :bulb: [Why Adopt GRDB?](Documentation\u002FWhyAdoptGRDB.md)\n- :bulb: [Recommended Practices for Designing Record Types](https:\u002F\u002Fswiftpackageindex.com\u002Fgroue\u002FGRDB.swift\u002Fdocumentation\u002Fgrdb\u002Frecordrecommendedpractices)\n\n#### Companion Libraries\n\n- [GRDBQuery](https:\u002F\u002Fgithub.com\u002Fgroue\u002FGRDBQuery): Access and observe the database from your SwiftUI views.\n- [GRDBSnapshotTesting](https:\u002F\u002Fgithub.com\u002Fgroue\u002FGRDBSnapshotTesting): Test your database. \n\n**[FAQ]**\n\n**[Sample Code](#sample-code)**\n\n\nInstallation\n============\n\n**The installation procedures below have GRDB use the version of SQLite that ships with the target operating system.**\n\nSee [Encryption](#encryption) for the installation procedure of GRDB with SQLCipher.\n\nSee [Custom SQLite builds](Documentation\u002FCustomSQLiteBuilds.md) for the installation procedure of GRDB with a customized build of SQLite.\n\n\n## Swift Package Manager\n\nThe [Swift Package Manager](https:\u002F\u002Fswift.org\u002Fpackage-manager\u002F) automates the distribution of Swift code. To use GRDB with SPM, add a dependency to `https:\u002F\u002Fgithub.com\u002Fgroue\u002FGRDB.swift.git`\n\nGRDB offers two libraries, `GRDB` and `GRDB-dynamic`. Pick only one. When in doubt, prefer `GRDB`. The `GRDB-dynamic` library can reveal useful if you are going to link it with multiple targets within your app and only wish to link to a shared, dynamic framework once. See [How to link a Swift Package as dynamic](https:\u002F\u002Fforums.swift.org\u002Ft\u002Fhow-to-link-a-swift-package-as-dynamic\u002F32062) for more information.\n\n> **Note**: Linux support is provided by contributors. It is not automatically tested, and not officially maintained. If you notice a build or runtime failure on Linux, please open a pull request with the necessary fix, thank you!\n\n\n## CocoaPods\n\n[CocoaPods](http:\u002F\u002Fcocoapods.org\u002F) is a dependency manager for Xcode projects. To use GRDB with CocoaPods (version 1.2 or higher), specify in your `Podfile`:\n\n```ruby\npod 'GRDB.swift'\n```\n\nGRDB can be installed as a framework, or a static library.\n\n**Important Note for CocoaPods installation**\n\nDue to an [issue](https:\u002F\u002Fgithub.com\u002FCocoaPods\u002FCocoaPods\u002Fissues\u002F11839) in CocoaPods, it is currently not possible to deploy new versions of GRDB to CocoaPods. The last version available on CocoaPods is 6.24.1. To install later versions of GRDB using CocoaPods, use one of the following workarounds:\n\n- Depend on the `GRDB7` branch. This is more or less equivalent to what `pod 'GRDB.swift', '~> 7.0'` would normally do, if CocoaPods would accept new GRDB versions to be published:\n\n    ```ruby\n    # Can't use semantic versioning due to https:\u002F\u002Fgithub.com\u002FCocoaPods\u002FCocoaPods\u002Fissues\u002F11839\n    pod 'GRDB.swift', git: 'https:\u002F\u002Fgithub.com\u002Fgroue\u002FGRDB.swift.git', branch: 'GRDB7'\n    ```\n\n- Depend on a specific version explicitly (Replace the tag with the version you want to use):\n\n    ```ruby\n    # Can't use semantic versioning due to https:\u002F\u002Fgithub.com\u002FCocoaPods\u002FCocoaPods\u002Fissues\u002F11839\n    # Replace the tag with the tag that you want to use.\n    pod 'GRDB.swift', git: 'https:\u002F\u002Fgithub.com\u002Fgroue\u002FGRDB.swift.git', tag: 'v6.29.0' \n    ```\n\n## Carthage\n\n[Carthage](https:\u002F\u002Fgithub.com\u002FCarthage\u002FCarthage) is **unsupported**. For some context about this decision, see [#433](https:\u002F\u002Fgithub.com\u002Fgroue\u002FGRDB.swift\u002Fissues\u002F433).\n\n\n## Manually\n\n1. [Download](https:\u002F\u002Fgithub.com\u002Fgroue\u002FGRDB.swift\u002Freleases) a copy of GRDB, or clone its repository and make sure you checkout the latest tagged version.\n\n2. Embed the `GRDB.xcodeproj` project in your own project.\n\n3. Add the `GRDB` target in the **Target Dependencies** section of the **Build Phases** tab of your application target (extension target for WatchOS).\n\n4. Add the `GRDB.framework` to the **Embedded Binaries** section of the **General**  tab of your application target (extension target for WatchOS).\n\n\nDatabase Connections\n====================\n\nGRDB provides two classes for accessing SQLite databases: [`DatabaseQueue`] and [`DatabasePool`]:\n\n```swift\nimport GRDB\n\n\u002F\u002F Pick one:\nlet dbQueue = try DatabaseQueue(path: \"\u002Fpath\u002Fto\u002Fdatabase.sqlite\")\nlet dbPool = try DatabasePool(path: \"\u002Fpath\u002Fto\u002Fdatabase.sqlite\")\n```\n\nThe differences are:\n\n- Database pools allow concurrent database accesses (this can improve the performance of multithreaded applications).\n- Database pools open your SQLite database in the [WAL mode](https:\u002F\u002Fwww.sqlite.org\u002Fwal.html) (unless read-only).\n- Database queues support [in-memory databases](https:\u002F\u002Fwww.sqlite.org\u002Finmemorydb.html).\n\n**If you are not sure, choose [`DatabaseQueue`].** You will always be able to switch to [`DatabasePool`] later.\n\nFor more information and tips when opening connections, see [Database Connections](https:\u002F\u002Fswiftpackageindex.com\u002Fgroue\u002FGRDB.swift\u002Fdocumentation\u002Fgrdb\u002Fdatabaseconnections).\n\n\nSQLite API\n==========\n\n**In this section of the documentation, we will talk SQL.** Jump to the [query interface](#the-query-interface) if SQL is not your cup of tea.\n\n- [Executing Updates](#executing-updates)\n- [Fetch Queries](#fetch-queries)\n    - [Fetching Methods](#fetching-methods)\n    - [Row Queries](#row-queries)\n    - [Value Queries](#value-queries)\n- [Values](#values)\n    - [Data](#data-and-memory-savings)\n    - [Date and DateComponents](#date-and-datecomponents)\n    - [NSNumber, NSDecimalNumber, and Decimal](#nsnumber-nsdecimalnumber-and-decimal)\n    - [Swift enums](#swift-enums)\n    - [`DatabaseValueConvertible`]: the protocol for custom value types\n- [Transactions and Savepoints]\n- [SQL Interpolation]\n\nAdvanced topics:\n\n- [Prepared Statements]\n- [Custom SQL Functions and Aggregates](#custom-sql-functions-and-aggregates)\n- [Database Schema Introspection](https:\u002F\u002Fswiftpackageindex.com\u002Fgroue\u002FGRDB.swift\u002Fdocumentation\u002Fgrdb\u002Fdatabaseschemaintrospection)\n- [Row Adapters](https:\u002F\u002Fswiftpackageindex.com\u002Fgroue\u002FGRDB.swift\u002Fdocumentation\u002Fgrdb\u002Frowadapter)\n- [Raw SQLite Pointers](#raw-sqlite-pointers)\n\n\n## Executing Updates\n\nOnce granted with a [database connection], the [`execute(sql:arguments:)`](https:\u002F\u002Fswiftpackageindex.com\u002Fgroue\u002FGRDB.swift\u002Fdocumentation\u002Fgrdb\u002Fdatabase\u002Fexecute(sql:arguments:)) method executes the SQL statements that do not return any database row, such as `CREATE TABLE`, `INSERT`, `DELETE`, `ALTER`, etc.\n\nFor example:\n\n```swift\ntry dbQueue.write { db in\n    try db.execute(sql: \"\"\"\n        CREATE TABLE player (\n            id INTEGER PRIMARY KEY AUTOINCREMENT,\n            name TEXT NOT NULL,\n            score INT)\n        \"\"\")\n    \n    try db.execute(\n        sql: \"INSERT INTO player (name, score) VALUES (?, ?)\",\n        arguments: [\"Barbara\", 1000])\n    \n    try db.execute(\n        sql: \"UPDATE player SET score = :score WHERE id = :id\",\n        arguments: [\"score\": 1000, \"id\": 1])\n    }\n}\n```\n\nThe `?` and colon-prefixed keys like `:score` in the SQL query are the **statements arguments**. You pass arguments with arrays or dictionaries, as in the example above. See [Values](#values) for more information on supported arguments types (Bool, Int, String, Date, Swift enums, etc.), and [`StatementArguments`] for a detailed documentation of SQLite arguments.\n\nYou can also embed query arguments right into your SQL queries, with [`execute(literal:)`](https:\u002F\u002Fswiftpackageindex.com\u002Fgroue\u002FGRDB.swift\u002Fdocumentation\u002Fgrdb\u002Fdatabase\u002Fexecute(literal:)), as in the example below. See [SQL Interpolation] for more details.\n\n```swift\ntry dbQueue.write { db in\n    let name = \"O'Brien\"\n    let score = 550\n    try db.execute(literal: \"\"\"\n        INSERT INTO player (name, score) VALUES (\\(name), \\(score))\n        \"\"\")\n}\n```\n\n**Never ever embed values directly in your raw SQL strings**. See [Avoiding SQL Injection](#avoiding-sql-injection) for more information:\n\n```swift\n\u002F\u002F WRONG: don't embed values in raw SQL strings\nlet id = 123\nlet name = textField.text\ntry db.execute(\n    sql: \"UPDATE player SET name = '\\(name)' WHERE id = \\(id)\")\n\n\u002F\u002F CORRECT: use arguments dictionary\ntry db.execute(\n    sql: \"UPDATE player SET name = :name WHERE id = :id\",\n    arguments: [\"name\": name, \"id\": id])\n\n\u002F\u002F CORRECT: use arguments array\ntry db.execute(\n    sql: \"UPDATE player SET name = ? WHERE id = ?\",\n    arguments: [name, id])\n\n\u002F\u002F CORRECT: use SQL Interpolation\ntry db.execute(\n    literal: \"UPDATE player SET name = \\(name) WHERE id = \\(id)\")\n```\n\n**Join multiple statements with a semicolon**:\n\n```swift\ntry db.execute(sql: \"\"\"\n    INSERT INTO player (name, score) VALUES (?, ?);\n    INSERT INTO player (name, score) VALUES (?, ?);\n    \"\"\", arguments: [\"Arthur\", 750, \"Barbara\", 1000])\n\ntry db.execute(literal: \"\"\"\n    INSERT INTO player (name, score) VALUES (\\(\"Arthur\"), \\(750));\n    INSERT INTO player (name, score) VALUES (\\(\"Barbara\"), \\(1000));\n    \"\"\")\n```\n\nWhen you want to make sure that a single statement is executed, use a prepared [`Statement`].\n\n**After an INSERT statement**, you can get the row ID of the inserted row with [`lastInsertedRowID`](https:\u002F\u002Fswiftpackageindex.com\u002Fgroue\u002FGRDB.swift\u002Fdocumentation\u002Fgrdb\u002Fdatabase\u002Flastinsertedrowid):\n\n```swift\ntry db.execute(\n    sql: \"INSERT INTO player (name, score) VALUES (?, ?)\",\n    arguments: [\"Arthur\", 1000])\nlet playerId = db.lastInsertedRowID\n```\n\nDon't miss [Records](#records), that provide classic **persistence methods**:\n\n```swift\nvar player = Player(name: \"Arthur\", score: 1000)\ntry player.insert(db)\nlet playerId = player.id\n```\n\n\n## Fetch Queries\n\n[Database connections] let you fetch database rows, plain values, and custom models aka \"records\".\n\n**Rows** are the raw results of SQL queries:\n\n```swift\ntry dbQueue.read { db in\n    if let row = try Row.fetchOne(db, sql: \"SELECT * FROM wine WHERE id = ?\", arguments: [1]) {\n        let name: String = row[\"name\"]\n        let color: Color = row[\"color\"]\n        print(name, color)\n    }\n}\n```\n\n\n**Values** are the Bool, Int, String, Date, Swift enums, etc. stored in row columns:\n\n```swift\ntry dbQueue.read { db in\n    let urls = try URL.fetchCursor(db, sql: \"SELECT url FROM wine\")\n    while let url = try urls.next() {\n        print(url)\n    }\n}\n```\n\n\n**Records** are your application objects that can initialize themselves from rows:\n\n```swift\nlet wines = try dbQueue.read { db in\n    try Wine.fetchAll(db, sql: \"SELECT * FROM wine\")\n}\n```\n\n- [Fetching Methods](#fetching-methods) and [Cursors](#cursors)\n- [Row Queries](#row-queries)\n- [Value Queries](#value-queries)\n- [Records](#records)\n\n\n### Fetching Methods\n\n**Throughout GRDB**, you can always fetch *cursors*, *arrays*, *sets*, or *single values* of any fetchable type (database [row](#row-queries), simple [value](#value-queries), or custom [record](#records)):\n\n```swift\ntry Row.fetchCursor(...) \u002F\u002F A Cursor of Row\ntry Row.fetchAll(...)    \u002F\u002F [Row]\ntry Row.fetchSet(...)    \u002F\u002F Set\u003CRow>\ntry Row.fetchOne(...)    \u002F\u002F Row?\n```\n\n- `fetchCursor` returns a **[cursor](#cursors)** over fetched values:\n    \n    ```swift\n    let rows = try Row.fetchCursor(db, sql: \"SELECT ...\") \u002F\u002F A Cursor of Row\n    ```\n    \n- `fetchAll` returns an **array**:\n    \n    ```swift\n    let players = try Player.fetchAll(db, sql: \"SELECT ...\") \u002F\u002F [Player]\n    ```\n\n- `fetchSet` returns a **set**:\n    \n    ```swift\n    let names = try String.fetchSet(db, sql: \"SELECT ...\") \u002F\u002F Set\u003CString>\n    ```\n\n- `fetchOne` returns a **single optional value**, and consumes a single database row (if any).\n    \n    ```swift\n    let count = try Int.fetchOne(db, sql: \"SELECT COUNT(*) ...\") \u002F\u002F Int?\n    ```\n\n**All those fetching methods require an SQL string that contains a single SQL statement.** When you want to fetch from multiple statements joined with a semicolon, iterate the multiple [prepared statements] found in the SQL string.\n\n### Cursors\n\n📖 [`Cursor`](https:\u002F\u002Fswiftpackageindex.com\u002Fgroue\u002FGRDB.swift\u002Fdocumentation\u002Fgrdb\u002Fcursor)\n\n**Whenever you consume several rows from the database, you can fetch an Array, a Set, or a Cursor**.\n\nThe `fetchAll()` and `fetchSet()` methods return regular Swift array and sets, that you iterate like all other arrays and sets:\n\n```swift\ntry dbQueue.read { db in\n    \u002F\u002F [Player]\n    let players = try Player.fetchAll(db, sql: \"SELECT ...\")\n    for player in players {\n        \u002F\u002F use player\n    }\n}\n```\n\nUnlike arrays and sets, cursors returned by `fetchCursor()` load their results step after step:\n\n```swift\ntry dbQueue.read { db in\n    \u002F\u002F Cursor of Player\n    let players = try Player.fetchCursor(db, sql: \"SELECT ...\")\n    while let player = try players.next() {\n        \u002F\u002F use player\n    }\n}\n```\n\n- **Cursors can not be used on any thread**: you must consume a cursor on the dispatch queue it was created in. Particularly, don't extract a cursor out of a database access method:\n    \n    ```swift\n    \u002F\u002F Wrong\n    let cursor = try dbQueue.read { db in\n        try Player.fetchCursor(db, ...)\n    }\n    while let player = try cursor.next() { ... }\n    ```\n    \n    Conversely, arrays and sets may be consumed on any thread:\n    \n    ```swift\n    \u002F\u002F OK\n    let array = try dbQueue.read { db in\n        try Player.fetchAll(db, ...)\n    }\n    for player in array { ... }\n    ```\n    \n- **Cursors can be iterated only one time.** Arrays and sets can be iterated many times.\n\n- **Cursors iterate database results in a lazy fashion**, and don't consume much memory. Arrays and sets contain copies of database values, and may take a lot of memory when there are many fetched results.\n\n- **Cursors are granted with direct access to SQLite,** unlike arrays and sets that have to take the time to copy database values. If you look after extra performance, you may prefer cursors.\n\n- **Cursors can feed Swift collections.**\n    \n    You will most of the time use `fetchAll` or `fetchSet` when you want an array or a set. For more specific needs, you may prefer one of the initializers below. All of them accept an extra optional `minimumCapacity` argument which helps optimizing your app when you have an idea of the number of elements in a cursor (the built-in `fetchAll` and `fetchSet` do not perform such an optimization).\n    \n    **Arrays** and all types conforming to `RangeReplaceableCollection`:\n    \n    ```swift\n    \u002F\u002F [String]\n    let cursor = try String.fetchCursor(db, ...)\n    let array = try Array(cursor)\n    ```\n    \n    **Sets**:\n    \n    ```swift\n    \u002F\u002F Set\u003CInt>\n    let cursor = try Int.fetchCursor(db, ...)\n    let set = try Set(cursor)\n    ```\n    \n    **Dictionaries**:\n    \n    ```swift\n    \u002F\u002F [Int64: [Player]]\n    let cursor = try Player.fetchCursor(db)\n    let dictionary = try Dictionary(grouping: cursor, by: { $0.teamID })\n    \n    \u002F\u002F [Int64: Player]\n    let cursor = try Player.fetchCursor(db).map { ($0.id, $0) }\n    let dictionary = try Dictionary(uniqueKeysWithValues: cursor)\n    ```\n\n- **Cursors adopt the [Cursor](https:\u002F\u002Fswiftpackageindex.com\u002Fgroue\u002FGRDB.swift\u002Fdocumentation\u002Fgrdb\u002Fcursor) protocol, which looks a lot like standard [lazy sequences](https:\u002F\u002Fdeveloper.apple.com\u002Freference\u002Fswift\u002Flazysequenceprotocol) of Swift.** As such, cursors come with many convenience methods: `compactMap`, `contains`, `dropFirst`, `dropLast`, `drop(while:)`, `enumerated`, `filter`, `first`, `flatMap`, `forEach`, `joined`, `joined(separator:)`, `max`, `max(by:)`, `min`, `min(by:)`, `map`, `prefix`, `prefix(while:)`, `reduce`, `reduce(into:)`, `suffix`:\n    \n    ```swift\n    \u002F\u002F Prints all Github links\n    try URL\n        .fetchCursor(db, sql: \"SELECT url FROM link\")\n        .filter { url in url.host == \"github.com\" }\n        .forEach { url in print(url) }\n    \n    \u002F\u002F An efficient cursor of coordinates:\n    let locations = try Row.\n        .fetchCursor(db, sql: \"SELECT latitude, longitude FROM place\")\n        .map { row in\n            CLLocationCoordinate2D(latitude: row[0], longitude: row[1])\n        }\n    ```\n\n- **Cursors are not Swift sequences.** That's because Swift sequences can't handle iteration errors, when reading SQLite results may fail at any time.\n\n- **Cursors require a little care**:\n    \n    - Don't modify the results during a cursor iteration:\n        \n        ```swift\n        \u002F\u002F Undefined behavior\n        while let player = try players.next() {\n            try db.execute(sql: \"DELETE ...\")\n        }\n        ```\n    \n    - Don't turn a cursor of `Row` into an array or a set. You would not get the distinct rows you expect. To get a array of rows, use `Row.fetchAll(...)`. To get a set of rows, use `Row.fetchSet(...)`. Generally speaking, make sure you copy a row whenever you extract it from a cursor for later use: `row.copy()`.\n\nIf you don't see, or don't care about the difference, use arrays. If you care about memory and performance, use cursors when appropriate.\n\n\n### Row Queries\n\n- [Fetching Rows](#fetching-rows)\n- [Column Values](#column-values)\n- [DatabaseValue](#databasevalue)\n- [Rows as Dictionaries](#rows-as-dictionaries)\n- 📖 [`Row`](https:\u002F\u002Fswiftpackageindex.com\u002Fgroue\u002FGRDB.swift\u002Fdocumentation\u002Fgrdb\u002Frow)\n\n\n#### Fetching Rows\n\nFetch **cursors** of rows, **arrays**, **sets**, or **single** rows (see [fetching methods](#fetching-methods)):\n\n```swift\ntry dbQueue.read { db in\n    try Row.fetchCursor(db, sql: \"SELECT ...\", arguments: ...) \u002F\u002F A Cursor of Row\n    try Row.fetchAll(db, sql: \"SELECT ...\", arguments: ...)    \u002F\u002F [Row]\n    try Row.fetchSet(db, sql: \"SELECT ...\", arguments: ...)    \u002F\u002F Set\u003CRow>\n    try Row.fetchOne(db, sql: \"SELECT ...\", arguments: ...)    \u002F\u002F Row?\n    \n    let rows = try Row.fetchCursor(db, sql: \"SELECT * FROM wine\")\n    while let row = try rows.next() {\n        let name: String = row[\"name\"]\n        let color: Color = row[\"color\"]\n        print(name, color)\n    }\n}\n\nlet rows = try dbQueue.read { db in\n    try Row.fetchAll(db, sql: \"SELECT * FROM player\")\n}\n```\n\nArguments are optional arrays or dictionaries that fill the positional `?` and colon-prefixed keys like `:name` in the query:\n\n```swift\nlet rows = try Row.fetchAll(db,\n    sql: \"SELECT * FROM player WHERE name = ?\",\n    arguments: [\"Arthur\"])\n\nlet rows = try Row.fetchAll(db,\n    sql: \"SELECT * FROM player WHERE name = :name\",\n    arguments: [\"name\": \"Arthur\"])\n```\n\nSee [Values](#values) for more information on supported arguments types (Bool, Int, String, Date, Swift enums, etc.), and [`StatementArguments`] for a detailed documentation of SQLite arguments.\n\nUnlike row arrays that contain copies of the database rows, row cursors are close to the SQLite metal, and require a little care:\n\n> **Note**: **Don't turn a cursor of `Row` into an array or a set**. You would not get the distinct rows you expect. To get a array of rows, use `Row.fetchAll(...)`. To get a set of rows, use `Row.fetchSet(...)`. Generally speaking, make sure you copy a row whenever you extract it from a cursor for later use: `row.copy()`.\n\n\n#### Column Values\n\n**Read column values** by index or column name:\n\n```swift\nlet name: String = row[0]      \u002F\u002F 0 is the leftmost column\nlet name: String = row[\"name\"] \u002F\u002F Leftmost matching column - lookup is case-insensitive\nlet name: String = row[Column(\"name\")] \u002F\u002F Using query interface's Column\n```\n\nMake sure to ask for an optional when the value may be NULL:\n\n```swift\nlet name: String? = row[\"name\"]\n```\n\nThe `row[]` subscript returns the type you ask for. See [Values](#values) for more information on supported value types:\n\n```swift\nlet bookCount: Int     = row[\"bookCount\"]\nlet bookCount64: Int64 = row[\"bookCount\"]\nlet hasBooks: Bool     = row[\"bookCount\"] \u002F\u002F false when 0\n\nlet string: String     = row[\"date\"]      \u002F\u002F \"2015-09-11 18:14:15.123\"\nlet date: Date         = row[\"date\"]      \u002F\u002F Date\nself.date = row[\"date\"] \u002F\u002F Depends on the type of the property.\n```\n\nYou can also use the `as` type casting operator:\n\n```swift\nrow[...] as Int\nrow[...] as Int?\n```\n\nThrowing accessors exist as well. Their use is not encouraged, because a database decoding error is a programming error. If an application stores invalid data in the database file, that is a bug that needs to be fixed:\n\n```swift\nlet name = try row.decode(String.self, atIndex: 0)\nlet bookCount = try row.decode(Int.self, forColumn: \"bookCount\")\n```\n\n> **Warning**: avoid the `as!` and `as?` operators:\n> \n> ```swift\n> if let int = row[...] as? Int { ... } \u002F\u002F BAD - doesn't work\n> if let int = row[...] as Int? { ... } \u002F\u002F GOOD\n> ```\n\n> **Warning**: avoid nil-coalescing row values, and prefer the `coalesce` method instead:\n>\n> ```swift\n> let name: String? = row[\"nickname\"] ?? row[\"name\"]     \u002F\u002F BAD - doesn't work\n> let name: String? = row.coalesce([\"nickname\", \"name\"]) \u002F\u002F GOOD\n> ```\n\nGenerally speaking, you can extract the type you need, provided it can be converted from the underlying SQLite value:\n\n- **Successful conversions include:**\n    \n    - All numeric SQLite values to all numeric Swift types, and Bool (zero is the only false boolean).\n    - Text SQLite values to Swift String.\n    - Blob SQLite values to Foundation Data.\n    \n    See [Values](#values) for more information on supported types (Bool, Int, String, Date, Swift enums, etc.)\n    \n- **NULL returns nil.**\n    \n    ```swift\n    let row = try Row.fetchOne(db, sql: \"SELECT NULL\")!\n    row[0] as Int? \u002F\u002F nil\n    row[0] as Int  \u002F\u002F fatal error: could not convert NULL to Int.\n    ```\n    \n    There is one exception, though: the [DatabaseValue](#databasevalue) type:\n    \n    ```swift\n    row[0] as DatabaseValue \u002F\u002F DatabaseValue.null\n    ```\n    \n- **Missing columns return nil.**\n    \n    ```swift\n    let row = try Row.fetchOne(db, sql: \"SELECT 'foo' AS foo\")!\n    row[\"missing\"] as String? \u002F\u002F nil\n    row[\"missing\"] as String  \u002F\u002F fatal error: no such column: missing\n    ```\n    \n    You can explicitly check for a column presence with the `hasColumn` method.\n\n- **Invalid conversions throw a fatal error.**\n    \n    ```swift\n    let row = try Row.fetchOne(db, sql: \"SELECT 'Mom’s birthday'\")!\n    row[0] as String \u002F\u002F \"Mom’s birthday\"\n    row[0] as Date?  \u002F\u002F fatal error: could not convert \"Mom’s birthday\" to Date.\n    row[0] as Date   \u002F\u002F fatal error: could not convert \"Mom’s birthday\" to Date.\n    \n    let row = try Row.fetchOne(db, sql: \"SELECT 256\")!\n    row[0] as Int    \u002F\u002F 256\n    row[0] as UInt8? \u002F\u002F fatal error: could not convert 256 to UInt8.\n    row[0] as UInt8  \u002F\u002F fatal error: could not convert 256 to UInt8.\n    ```\n    \n    Those conversion fatal errors can be avoided with the [DatabaseValue](#databasevalue) type:\n    \n    ```swift\n    let row = try Row.fetchOne(db, sql: \"SELECT 'Mom’s birthday'\")!\n    let dbValue: DatabaseValue = row[0]\n    if dbValue.isNull {\n        \u002F\u002F Handle NULL\n    } else if let date = Date.fromDatabaseValue(dbValue) {\n        \u002F\u002F Handle valid date\n    } else {\n        \u002F\u002F Handle invalid date\n    }\n    ```\n    \n    This extra verbosity is the consequence of having to deal with an untrusted database: you may consider fixing the content of your database instead. See [Fatal Errors](#fatal-errors) for more information.\n    \n- **SQLite has a weak type system, and provides [convenience conversions](https:\u002F\u002Fwww.sqlite.org\u002Fc3ref\u002Fcolumn_blob.html) that can turn String to Int, Double to Blob, etc.**\n    \n    GRDB will sometimes let those conversions go through:\n    \n    ```swift\n    let rows = try Row.fetchCursor(db, sql: \"SELECT '20 small cigars'\")\n    while let row = try rows.next() {\n        row[0] as Int   \u002F\u002F 20\n    }\n    ```\n    \n    Don't freak out: those conversions did not prevent SQLite from becoming the immensely successful database engine you want to use. And GRDB adds safety checks described just above. You can also prevent those convenience conversions altogether by using the [DatabaseValue](#databasevalue) type.\n\n\n#### DatabaseValue\n\n📖 [`DatabaseValue`](https:\u002F\u002Fswiftpackageindex.com\u002Fgroue\u002FGRDB.swift\u002Fdocumentation\u002Fgrdb\u002Fdatabasevalue)\n\n**`DatabaseValue` is an intermediate type between SQLite and your values, which gives information about the raw value stored in the database.**\n\nYou get `DatabaseValue` just like other value types:\n\n```swift\nlet dbValue: DatabaseValue = row[0]\nlet dbValue: DatabaseValue? = row[\"name\"] \u002F\u002F nil if and only if column does not exist\n\n\u002F\u002F Check for NULL:\ndbValue.isNull \u002F\u002F Bool\n\n\u002F\u002F The stored value:\ndbValue.storage.value \u002F\u002F Int64, Double, String, Data, or nil\n\n\u002F\u002F All the five storage classes supported by SQLite:\nswitch dbValue.storage {\ncase .null:                 print(\"NULL\")\ncase .int64(let int64):     print(\"Int64: \\(int64)\")\ncase .double(let double):   print(\"Double: \\(double)\")\ncase .string(let string):   print(\"String: \\(string)\")\ncase .blob(let data):       print(\"Data: \\(data)\")\n}\n```\n\nYou can extract regular [values](#values) (Bool, Int, String, Date, Swift enums, etc.) from `DatabaseValue` with the [fromDatabaseValue()](https:\u002F\u002Fswiftpackageindex.com\u002Fgroue\u002FGRDB.swift\u002Fdocumentation\u002Fgrdb\u002Fdatabasevalueconvertible\u002Ffromdatabasevalue(_:)-21zzv) method:\n\n```swift\nlet dbValue: DatabaseValue = row[\"bookCount\"]\nlet bookCount   = Int.fromDatabaseValue(dbValue)   \u002F\u002F Int?\nlet bookCount64 = Int64.fromDatabaseValue(dbValue) \u002F\u002F Int64?\nlet hasBooks    = Bool.fromDatabaseValue(dbValue)  \u002F\u002F Bool?, false when 0\n\nlet dbValue: DatabaseValue = row[\"date\"]\nlet string = String.fromDatabaseValue(dbValue)     \u002F\u002F \"2015-09-11 18:14:15.123\"\nlet date   = Date.fromDatabaseValue(dbValue)       \u002F\u002F Date?\n```\n\n`fromDatabaseValue` returns nil for invalid conversions:\n\n```swift\nlet row = try Row.fetchOne(db, sql: \"SELECT 'Mom’s birthday'\")!\nlet dbValue: DatabaseValue = row[0]\nlet string = String.fromDatabaseValue(dbValue) \u002F\u002F \"Mom’s birthday\"\nlet int    = Int.fromDatabaseValue(dbValue)    \u002F\u002F nil\nlet date   = Date.fromDatabaseValue(dbValue)   \u002F\u002F nil\n```\n\n\n#### Rows as Dictionaries\n\nRow adopts the standard [RandomAccessCollection](https:\u002F\u002Fdeveloper.apple.com\u002Fdocumentation\u002Fswift\u002Frandomaccesscollection) protocol, and can be seen as a dictionary of [DatabaseValue](#databasevalue):\n\n```swift\n\u002F\u002F All the (columnName, dbValue) tuples, from left to right:\nfor (columnName, dbValue) in row {\n    ...\n}\n```\n\n**You can build rows from dictionaries** (standard Swift dictionaries and NSDictionary). See [Values](#values) for more information on supported types:\n\n```swift\nlet row: Row = [\"name\": \"foo\", \"date\": nil]\nlet row = Row([\"name\": \"foo\", \"date\": nil])\nlet row = Row(\u002F* [AnyHashable: Any] *\u002F) \u002F\u002F nil if invalid dictionary\n```\n\nYet rows are not real dictionaries: they may contain duplicate columns:\n\n```swift\nlet row = try Row.fetchOne(db, sql: \"SELECT 1 AS foo, 2 AS foo\")!\nrow.columnNames    \u002F\u002F [\"foo\", \"foo\"]\nrow.databaseValues \u002F\u002F [1, 2]\nrow[\"foo\"]         \u002F\u002F 1 (leftmost matching column)\nfor (columnName, dbValue) in row { ... } \u002F\u002F (\"foo\", 1), (\"foo\", 2)\n```\n\n**When you build a dictionary from a row**, you have to disambiguate identical columns, and choose how to present database values. For example:\n\n- A `[String: DatabaseValue]` dictionary that keeps leftmost value in case of duplicated column name:\n\n    ```swift\n    let dict = Dictionary(row, uniquingKeysWith: { (left, _) in left })\n    ```\n\n- A `[String: AnyObject]` dictionary which keeps rightmost value in case of duplicated column name. This dictionary is identical to FMResultSet's resultDictionary from FMDB. It contains NSNull values for null columns, and can be shared with Objective-C:\n\n    ```swift\n    let dict = Dictionary(\n        row.map { (column, dbValue) in\n            (column, dbValue.storage.value as AnyObject)\n        },\n        uniquingKeysWith: { (_, right) in right })\n    ```\n\n- A `[String: Any]` dictionary that can feed, for example, JSONSerialization:\n    \n    ```swift\n    let dict = Dictionary(\n        row.map { (column, dbValue) in\n            (column, dbValue.storage.value)\n        },\n        uniquingKeysWith: { (left, _) in left })\n    ```\n\nSee the documentation of [`Dictionary.init(_:uniquingKeysWith:)`](https:\u002F\u002Fdeveloper.apple.com\u002Fdocumentation\u002Fswift\u002Fdictionary\u002F2892961-init) for more information.\n\n\n### Value Queries\n\n📖 [`DatabaseValueConvertible`](https:\u002F\u002Fswiftpackageindex.com\u002Fgroue\u002FGRDB.swift\u002Fdocumentation\u002Fgrdb\u002Fdatabasevalueconvertible)\n\n**Instead of rows, you can directly fetch values.** There are many supported [value types](#values) (Bool, Int, String, Date, Swift enums, etc.).\n\nLike rows, fetch values as **cursors**, **arrays**, **sets**, or **single** values (see [fetching methods](#fetching-methods)). Values are extracted from the leftmost column of the SQL queries:\n\n```swift\ntry dbQueue.read { db in\n    try Int.fetchCursor(db, sql: \"SELECT ...\", arguments: ...) \u002F\u002F A Cursor of Int\n    try Int.fetchAll(db, sql: \"SELECT ...\", arguments: ...)    \u002F\u002F [Int]\n    try Int.fetchSet(db, sql: \"SELECT ...\", arguments: ...)    \u002F\u002F Set\u003CInt>\n    try Int.fetchOne(db, sql: \"SELECT ...\", arguments: ...)    \u002F\u002F Int?\n    \n    let maxScore = try Int.fetchOne(db, sql: \"SELECT MAX(score) FROM player\") \u002F\u002F Int?\n    let names = try String.fetchAll(db, sql: \"SELECT name FROM player\")       \u002F\u002F [String]\n}\n```\n\n`Int.fetchOne` returns nil in two cases: either the SELECT statement yielded no row, or one row with a NULL value:\n\n```swift\n\u002F\u002F No row:\ntry Int.fetchOne(db, sql: \"SELECT 42 WHERE FALSE\") \u002F\u002F nil\n\n\u002F\u002F One row with a NULL value:\ntry Int.fetchOne(db, sql: \"SELECT NULL\")           \u002F\u002F nil\n\n\u002F\u002F One row with a non-NULL value:\ntry Int.fetchOne(db, sql: \"SELECT 42\")             \u002F\u002F 42\n```\n\nFor requests which may contain NULL, fetch optionals:\n\n```swift\ntry dbQueue.read { db in\n    try Optional\u003CInt>.fetchCursor(db, sql: \"SELECT ...\", arguments: ...) \u002F\u002F A Cursor of Int?\n    try Optional\u003CInt>.fetchAll(db, sql: \"SELECT ...\", arguments: ...)    \u002F\u002F [Int?]\n    try Optional\u003CInt>.fetchSet(db, sql: \"SELECT ...\", arguments: ...)    \u002F\u002F Set\u003CInt?>\n}\n```\n\n> :bulb: **Tip**: One advanced use case, when you fetch one value, is to distinguish the cases of a statement that yields no row, or one row with a NULL value. To do so, use `Optional\u003CInt>.fetchOne`, which returns a double optional `Int??`:\n> \n> ```swift\n> \u002F\u002F No row:\n> try Optional\u003CInt>.fetchOne(db, sql: \"SELECT 42 WHERE FALSE\") \u002F\u002F .none\n> \u002F\u002F One row with a NULL value:\n> try Optional\u003CInt>.fetchOne(db, sql: \"SELECT NULL\")           \u002F\u002F .some(.none)\n> \u002F\u002F One row with a non-NULL value:\n> try Optional\u003CInt>.fetchOne(db, sql: \"SELECT 42\")             \u002F\u002F .some(.some(42))\n> ```\n\nThere are many supported value types (Bool, Int, String, Date, Swift enums, etc.). See [Values](#values) for more information.\n\n\n## Values\n\nGRDB ships with built-in support for the following value types:\n\n- **Swift Standard Library**: Bool, Double, Float, all signed and unsigned integer types, String, [Swift enums](#swift-enums).\n    \n- **Foundation**: [Data](#data-and-memory-savings), [Date](#date-and-datecomponents), [DateComponents](#date-and-datecomponents), [Decimal](#nsnumber-nsdecimalnumber-and-decimal), NSNull, [NSNumber](#nsnumber-nsdecimalnumber-and-decimal), NSString, URL, [UUID](#uuid).\n    \n- **CoreGraphics**: CGFloat.\n\n- **[DatabaseValue](#databasevalue)**, the type which gives information about the raw value stored in the database.\n\n- **Full-Text Patterns**: [FTS3Pattern](Documentation\u002FFullTextSearch.md#fts3pattern) and [FTS5Pattern](Documentation\u002FFullTextSearch.md#fts5pattern).\n\n- Generally speaking, all types that adopt the [`DatabaseValueConvertible`] protocol.\n\nValues can be used as [statement arguments](https:\u002F\u002Fswiftpackageindex.com\u002Fgroue\u002FGRDB.swift\u002Fdocumentation\u002Fgrdb\u002Fstatementarguments):\n\n```swift\nlet url: URL = ...\nlet verified: Bool = ...\ntry db.execute(\n    sql: \"INSERT INTO link (url, verified) VALUES (?, ?)\",\n    arguments: [url, verified])\n```\n\nValues can be [extracted from rows](#column-values):\n\n```swift\nlet rows = try Row.fetchCursor(db, sql: \"SELECT * FROM link\")\nwhile let row = try rows.next() {\n    let url: URL = row[\"url\"]\n    let verified: Bool = row[\"verified\"]\n}\n```\n\nValues can be [directly fetched](#value-queries):\n\n```swift\nlet urls = try URL.fetchAll(db, sql: \"SELECT url FROM link\")  \u002F\u002F [URL]\n```\n\nUse values in [Records](#records):\n\n```swift\nstruct Link: FetchableRecord {\n    var url: URL\n    var isVerified: Bool\n    \n    init(row: Row) {\n        url = row[\"url\"]\n        isVerified = row[\"verified\"]\n    }\n}\n```\n\nUse values in the [query interface](#the-query-interface):\n\n```swift\nlet url: URL = ...\nlet link = try Link.filter { $0.url == url }.fetchOne(db)\n```\n\n\n### Data (and Memory Savings)\n\n**Data** suits the BLOB SQLite columns. It can be stored and fetched from the database just like other [values](#values):\n\n```swift\nlet rows = try Row.fetchCursor(db, sql: \"SELECT data, ...\")\nwhile let row = try rows.next() {\n    let data: Data = row[\"data\"]\n}\n```\n\nAt each step of the request iteration, the `row[]` subscript creates *two copies* of the database bytes: one fetched by SQLite, and another, stored in the Swift Data value.\n\n**You have the opportunity to save memory** by not copying the data fetched by SQLite:\n\n```swift\nwhile let row = try rows.next() {\n    try row.withUnsafeData(name: \"data\") { (data: Data?) in\n        ...\n    }\n}\n```\n\nThe non-copied data does not live longer than the iteration step: make sure that you do not use it past this point.\n\n\n### Date and DateComponents\n\n[**Date**](#date) and [**DateComponents**](#datecomponents) can be stored and fetched from the database.\n\nHere is how GRDB supports the various [date formats](https:\u002F\u002Fwww.sqlite.org\u002Flang_datefunc.html) supported by SQLite:\n\n| SQLite format                | Date               | DateComponents |\n|:---------------------------- |:------------------:|:--------------:|\n| YYYY-MM-DD                   |       Read ¹       | Read \u002F Write   |\n| YYYY-MM-DD HH:MM             |       Read ¹ ²     | Read ² \u002F Write |\n| YYYY-MM-DD HH:MM:SS          |       Read ¹ ²     | Read ² \u002F Write |\n| YYYY-MM-DD HH:MM:SS.SSS      | Read ¹ ² \u002F Write ¹ | Read ² \u002F Write |\n| YYYY-MM-DD**T**HH:MM         |       Read ¹ ²     |      Read ²    |\n| YYYY-MM-DD**T**HH:MM:SS      |       Read ¹ ²     |      Read ²    |\n| YYYY-MM-DD**T**HH:MM:SS.SSS  |       Read ¹ ²     |      Read ²    |\n| HH:MM                        |                    | Read ² \u002F Write |\n| HH:MM:SS                     |                    | Read ² \u002F Write |\n| HH:MM:SS.SSS                 |                    | Read ² \u002F Write |\n| Timestamps since unix epoch  |       Read ³       |                |\n| `now`                        |                    |                |\n\n¹ Missing components are assumed to be zero. Dates are stored and read in the UTC time zone, unless the format is followed by a timezone indicator ⁽²⁾.\n\n² This format may be optionally followed by a timezone indicator of the form `[+-]HH:MM` or just `Z`.\n\n³ GRDB 2+ interprets numerical values as timestamps that fuel `Date(timeIntervalSince1970:)`. Previous GRDB versions used to interpret numbers as [julian days](https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FJulian_day). Julian days are still supported, with the `Date(julianDay:)` initializer.\n\n> **Warning**: the range of valid years in the SQLite date formats is 0000-9999. You will need to pick another date format when your application needs to process years outside of this range. See the following chapters.\n\n\n#### Date\n\n**Date** can be stored and fetched from the database just like other [values](#values):\n\n```swift\ntry db.execute(\n    sql: \"INSERT INTO player (creationDate, ...) VALUES (?, ...)\",\n    arguments: [Date(), ...])\n\nlet row = try Row.fetchOne(db, ...)!\nlet creationDate: Date = row[\"creationDate\"]\n```\n\nDates are stored using the format \"YYYY-MM-DD HH:MM:SS.SSS\" in the UTC time zone. It is precise to the millisecond.\n\n> **Note**: this format was chosen because it is the only format that is:\n> \n> - Comparable (`ORDER BY date` works)\n> - Comparable with the SQLite keyword CURRENT_TIMESTAMP (`WHERE date > CURRENT_TIMESTAMP` works)\n> - Able to feed [SQLite date & time functions](https:\u002F\u002Fwww.sqlite.org\u002Flang_datefunc.html)\n> - Precise enough\n>\n> **Warning**: the range of valid years in the SQLite date format is 0000-9999. You will experience problems with years outside of this range, such as decoding errors, or invalid date computations with [SQLite date & time functions](https:\u002F\u002Fwww.sqlite.org\u002Flang_datefunc.html).\n\nSome applications may prefer another date format:\n\n- Some may prefer ISO-8601, with a `T` separator.\n- Some may prefer ISO-8601, with a time zone.\n- Some may need to store years beyond the 0000-9999 range.\n- Some may need sub-millisecond precision.\n- Some may need exact `Date` roundtrip.\n- Etc.\n\n**You should think twice before choosing a different date format:**\n\n- ISO-8601 is about *exchange and communication*, when SQLite is about *storage and data manipulation*. Sharing the same representation in your database and in JSON files only provides a superficial convenience, and should be the least of your priorities. Don't store dates as ISO-8601 without understanding what you lose. For example, ISO-8601 time zones forbid database-level date comparison. \n- Sub-millisecond precision and exact `Date` roundtrip are not as obvious needs as it seems at first sight. Dates generally don't precisely roundtrip as soon as they leave your application anyway, because the other systems your app communicates with use their own date representation (the Android version of your app, the server your application is talking to, etc.) On top of that, `Date` comparison is at least as hard and nasty as [floating point comparison](https:\u002F\u002Fwww.google.com\u002Fsearch?q=floating+point+comparison+is+hard).\n\nThe customization of date format is explicit. For example:\n\n```swift\nlet date = Date()\nlet timeInterval = date.timeIntervalSinceReferenceDate\ntry db.execute(\n    sql: \"INSERT INTO player (creationDate, ...) VALUES (?, ...)\",\n    arguments: [timeInterval, ...])\n\nif let row = try Row.fetchOne(db, ...) {\n    let timeInterval: TimeInterval = row[\"creationDate\"]\n    let creationDate = Date(timeIntervalSinceReferenceDate: timeInterval)\n}\n```\n\nSee also [Codable Records] for more date customization options, and [`DatabaseValueConvertible`] if you want to define a Date-wrapping type with customized database representation.\n\n\n#### DateComponents\n\nDateComponents is indirectly supported, through the **DatabaseDateComponents** helper type.\n\nDatabaseDateComponents reads date components from all [date formats supported by SQLite](https:\u002F\u002Fwww.sqlite.org\u002Flang_datefunc.html), and stores them in the format of your choice, from HH:MM to YYYY-MM-DD HH:MM:SS.SSS.\n\n> **Warning**: the range of valid years is 0000-9999. You will experience problems with years outside of this range, such as decoding errors, or invalid date computations with [SQLite date & time functions](https:\u002F\u002Fwww.sqlite.org\u002Flang_datefunc.html). See [Date](#date) for more information.\n\nDatabaseDateComponents can be stored and fetched from the database just like other [values](#values):\n\n```swift\nlet components = DateComponents()\ncomponents.year = 1973\ncomponents.month = 9\ncomponents.day = 18\n\n\u002F\u002F Store \"1973-09-18\"\nlet dbComponents = DatabaseDateComponents(components, format: .YMD)\ntry db.execute(\n    sql: \"INSERT INTO player (birthDate, ...) VALUES (?, ...)\",\n    arguments: [dbComponents, ...])\n\n\u002F\u002F Read \"1973-09-18\"\nlet row = try Row.fetchOne(db, sql: \"SELECT birthDate ...\")!\nlet dbComponents: DatabaseDateComponents = row[\"birthDate\"]\ndbComponents.format         \u002F\u002F .YMD (the actual format found in the database)\ndbComponents.dateComponents \u002F\u002F DateComponents\n```\n\n\n### NSNumber, NSDecimalNumber, and Decimal\n\n**NSNumber** and **Decimal** can be stored and fetched from the database just like other [values](#values).\n\nHere is how GRDB supports the various data types supported by SQLite:\n\n|                 |    Integer   |     Double   |    String    |\n|:--------------- |:------------:|:------------:|:------------:|\n| NSNumber        | Read \u002F Write | Read \u002F Write |     Read     |\n| NSDecimalNumber | Read \u002F Write | Read \u002F Write |     Read     |\n| Decimal         |     Read     |     Read     | Read \u002F Write |\n\n- All three types can decode database integers and doubles:\n\n    ```swift\n    let number = try NSNumber.fetchOne(db, sql: \"SELECT 10\")            \u002F\u002F NSNumber\n    let number = try NSDecimalNumber.fetchOne(db, sql: \"SELECT 1.23\")   \u002F\u002F NSDecimalNumber\n    let number = try Decimal.fetchOne(db, sql: \"SELECT -100\")           \u002F\u002F Decimal\n    ```\n    \n- All three types decode database strings as decimal numbers:\n\n    ```swift\n    let number = try NSNumber.fetchOne(db, sql: \"SELECT '10'\")          \u002F\u002F NSDecimalNumber (sic)\n    let number = try NSDecimalNumber.fetchOne(db, sql: \"SELECT '1.23'\") \u002F\u002F NSDecimalNumber\n    let number = try Decimal.fetchOne(db, sql: \"SELECT '-100'\")         \u002F\u002F Decimal\n    ```\n\n- `NSNumber` and `NSDecimalNumber` send 64-bit signed integers and doubles in the database:\n\n    ```swift\n    \u002F\u002F INSERT INTO transfer VALUES (10)\n    try db.execute(sql: \"INSERT INTO transfer VALUES (?)\", arguments: [NSNumber(value: 10)])\n    \n    \u002F\u002F INSERT INTO transfer VALUES (10.0)\n    try db.execute(sql: \"INSERT INTO transfer VALUES (?)\", arguments: [NSNumber(value: 10.0)])\n    \n    \u002F\u002F INSERT INTO transfer VALUES (10)\n    try db.execute(sql: \"INSERT INTO transfer VALUES (?)\", arguments: [NSDecimalNumber(string: \"10.0\")])\n    \n    \u002F\u002F INSERT INTO transfer VALUES (10.5)\n    try db.execute(sql: \"INSERT INTO transfer VALUES (?)\", arguments: [NSDecimalNumber(string: \"10.5\")])\n    ```\n    \n    > **Warning**: since SQLite does not support decimal numbers, sending a non-integer `NSDecimalNumber` can result in a loss of precision during the conversion to double.\n    >\n    > Instead of sending non-integer `NSDecimalNumber` to the database, you may prefer:\n    >\n    > - Send `Decimal` instead (those store decimal strings in the database).\n    > - Send integers instead (for example, store amounts of cents instead of amounts of Euros).\n\n- `Decimal` sends decimal strings in the database:\n\n    ```swift\n    \u002F\u002F INSERT INTO transfer VALUES ('10')\n    try db.execute(sql: \"INSERT INTO transfer VALUES (?)\", arguments: [Decimal(10)])\n    \n    \u002F\u002F INSERT INTO transfer VALUES ('10.5')\n    try db.execute(sql: \"INSERT INTO transfer VALUES (?)\", arguments: [Decimal(string: \"10.5\")!])\n    ```\n\n\n### UUID\n\n**UUID** can be stored and fetched from the database just like other [values](#values).\n\nGRDB stores uuids as 16-bytes data blobs, and decodes them from both 16-bytes data blobs and strings such as \"E621E1F8-C36C-495A-93FC-0C247A3E6E5F\".\n\n\n### Swift Enums\n\n**Swift enums** and generally all types that adopt the [RawRepresentable](https:\u002F\u002Fdeveloper.apple.com\u002Flibrary\u002Ftvos\u002Fdocumentation\u002FSwift\u002FReference\u002FSwift_RawRepresentable_Protocol\u002Findex.html) protocol can be stored and fetched from the database just like their raw [values](#values):\n\n```swift\nenum Color : Int {\n    case red, white, rose\n}\n\nenum Grape : String {\n    case chardonnay, merlot, riesling\n}\n\n\u002F\u002F Declare empty DatabaseValueConvertible adoption\nextension Color : DatabaseValueConvertible { }\nextension Grape : DatabaseValueConvertible { }\n\n\u002F\u002F Store\ntry db.execute(\n    sql: \"INSERT INTO wine (grape, color) VALUES (?, ?)\",\n    arguments: [Grape.merlot, Color.red])\n\n\u002F\u002F Read\nlet rows = try Row.fetchCursor(db, sql: \"SELECT * FROM wine\")\nwhile let row = try rows.next() {\n    let grape: Grape = row[\"grape\"]\n    let color: Color = row[\"color\"]\n}\n```\n\n**When a database value does not match any enum case**, you get a fatal error. This fatal error can be avoided with the [DatabaseValue](#databasevalue) type:\n\n```swift\nlet row = try Row.fetchOne(db, sql: \"SELECT 'syrah'\")!\n\nrow[0] as String  \u002F\u002F \"syrah\"\nrow[0] as Grape?  \u002F\u002F fatal error: could not convert \"syrah\" to Grape.\nrow[0] as Grape   \u002F\u002F fatal error: could not convert \"syrah\" to Grape.\n\nlet dbValue: DatabaseValue = row[0]\nif dbValue.isNull {\n    \u002F\u002F Handle NULL\n} else if let grape = Grape.fromDatabaseValue(dbValue) {\n    \u002F\u002F Handle valid grape\n} else {\n    \u002F\u002F Handle unknown grape\n}\n```\n\n\n## Custom SQL Functions and Aggregates\n\n**SQLite lets you define SQL functions and aggregates.**\n\nA custom SQL function or aggregate extends SQLite:\n\n```sql\nSELECT reverse(name) FROM player;   -- custom function\nSELECT maxLength(name) FROM player; -- custom aggregate\n```\n\n- [Custom SQL Functions](#custom-sql-functions)\n- [Custom Aggregates](#custom-aggregates)\n\n\n### Custom SQL Functions\n\n📖 [`DatabaseFunction`](https:\u002F\u002Fswiftpackageindex.com\u002Fgroue\u002FGRDB.swift\u002Fdocumentation\u002Fgrdb\u002Fdatabasefunction)\n\nA *function* argument takes an array of [DatabaseValue](#databasevalue), and returns any valid [value](#values) (Bool, Int, String, Date, Swift enums, etc.) The number of database values is guaranteed to be *argumentCount*.\n\nSQLite has the opportunity to perform additional optimizations when functions are \"pure\", which means that their result only depends on their arguments. So make sure to set the *pure* argument to true when possible.\n\n```swift\nlet reverse = DatabaseFunction(\"reverse\", argumentCount: 1, pure: true) { (values: [DatabaseValue]) in\n    \u002F\u002F Extract string value, if any...\n    guard let string = String.fromDatabaseValue(values[0]) else {\n        return nil\n    }\n    \u002F\u002F ... and return reversed string:\n    return String(string.reversed())\n}\n```\n\nYou make a function available to a database connection through its configuration:\n\n```swift\nvar config = Configuration()\nconfig.prepareDatabase { db in\n    db.add(function: reverse)\n}\nlet dbQueue = try DatabaseQueue(path: dbPath, configuration: config)\n\ntry dbQueue.read { db in\n    \u002F\u002F \"oof\"\n    try String.fetchOne(db, sql: \"SELECT reverse('foo')\")!\n}\n```\n\n\n**Functions can take a variable number of arguments:**\n\nWhen you don't provide any explicit *argumentCount*, the function can take any number of arguments:\n\n```swift\nlet averageOf = DatabaseFunction(\"averageOf\", pure: true) { (values: [DatabaseValue]) in\n    let doubles = values.compactMap { Double.fromDatabaseValue($0) }\n    return doubles.reduce(0, +) \u002F Double(doubles.count)\n}\ndb.add(function: averageOf)\n\n\u002F\u002F 2.0\ntry Double.fetchOne(db, sql: \"SELECT averageOf(1, 2, 3)\")!\n```\n\n\n**Functions can throw:**\n\n```swift\nlet sqrt = DatabaseFunction(\"sqrt\", argumentCount: 1, pure: true) { (values: [DatabaseValue]) in\n    guard let double = Double.fromDatabaseValue(values[0]) else {\n        return nil\n    }\n    guard double >= 0 else {\n        throw DatabaseError(message: \"invalid negative number\")\n    }\n    return sqrt(double)\n}\ndb.add(function: sqrt)\n\n\u002F\u002F SQLite error 1 with statement `SELECT sqrt(-1)`: invalid negative number\ntry Double.fetchOne(db, sql: \"SELECT sqrt(-1)\")!\n```\n\n\n**Use custom functions in the [query interface](#the-query-interface):**\n\n```swift\n\u002F\u002F SELECT reverseString(\"name\") FROM player\nPlayer.select { reverseString($0.name) }\n```\n\n\n**GRDB ships with built-in SQL functions that perform unicode-aware string transformations.** See [Unicode](#unicode).\n\n\n### Custom Aggregates\n\n📖 [`DatabaseFunction`](https:\u002F\u002Fswiftpackageindex.com\u002Fgroue\u002FGRDB.swift\u002Fdocumentation\u002Fgrdb\u002Fdatabasefunction), [`DatabaseAggregate`](https:\u002F\u002Fswiftpackageindex.com\u002Fgroue\u002FGRDB.swift\u002Fdocumentation\u002Fgrdb\u002Fdatabaseaggregate)\n\nBefore registering a custom aggregate, you need to define a type that adopts the `DatabaseAggregate` protocol:\n\n```swift\nprotocol DatabaseAggregate {\n    \u002F\u002F Initializes an aggregate\n    init()\n    \n    \u002F\u002F Called at each step of the aggregation\n    mutating func step(_ dbValues: [DatabaseValue]) throws\n    \n    \u002F\u002F Returns the final result\n    func finalize() throws -> DatabaseValueConvertible?\n}\n```\n\nFor example:\n\n```swift\nstruct MaxLength : DatabaseAggregate {\n    var maxLength: Int = 0\n    \n    mutating func step(_ dbValues: [DatabaseValue]) {\n        \u002F\u002F At each step, extract string value, if any...\n        guard let string = String.fromDatabaseValue(dbValues[0]) else {\n            return\n        }\n        \u002F\u002F ... and update the result\n        let length = string.count\n        if length > maxLength {\n            maxLength = length\n        }\n    }\n    \n    func finalize() -> DatabaseValueConvertible? {\n        maxLength\n    }\n}\n\nlet maxLength = DatabaseFunction(\n    \"maxLength\",\n    argumentCount: 1,\n    pure: true,\n    aggregate: MaxLength.self)\n```\n\nLike [custom SQL Functions](#custom-sql-functions), you make an aggregate function available to a database connection through its configuration:\n\n```swift\nvar config = Configuration()\nconfig.prepareDatabase { db in\n    db.add(function: maxLength)\n}\nlet dbQueue = try DatabaseQueue(path: dbPath, configuration: config)\n\ntry dbQueue.read { db in\n    \u002F\u002F Some Int\n    try Int.fetchOne(db, sql: \"SELECT maxLength(name) FROM player\")!\n}\n```\n\nThe `step` method of the aggregate takes an array of [DatabaseValue](#databasevalue). This array contains as many values as the *argumentCount* parameter (or any number of values, when *argumentCount* is omitted).\n\nThe `finalize` method of the aggregate returns the final aggregated [value](#values) (Bool, Int, String, Date, Swift enums, etc.).\n\nSQLite has the opportunity to perform additional optimizations when aggregates are \"pure\", which means that their result only depends on their inputs. So make sure to set the *pure* argument to true when possible.\n\n\n**Use custom aggregates in the [query interface](#the-query-interface):**\n\n```swift\n\u002F\u002F SELECT maxLength(\"name\") FROM player\nlet request = Player.select { maxLength($0.name) }\ntry Int.fetchOne(db, request) \u002F\u002F Int?\n```\n\n\n## Raw SQLite Pointers\n\n**If not all SQLite APIs are exposed in GRDB, you can still use the [SQLite C Interface](https:\u002F\u002Fwww.sqlite.org\u002Fc3ref\u002Fintro.html) and call [SQLite C functions](https:\u002F\u002Fwww.sqlite.org\u002Fc3ref\u002Ffunclist.html).**\n\nTo access the C SQLite functions from SQLCipher or the system SQLite, you need to perform an extra import:\n\n```swift\nimport SQLite3   \u002F\u002F System SQLite\nimport SQLCipher \u002F\u002F SQLCipher\n\nlet sqliteVersion = String(cString: sqlite3_libversion())\n```\n\nRaw pointers to database connections and statements are available through the `Database.sqliteConnection` and `Statement.sqliteStatement` properties:\n\n```swift\ntry dbQueue.read { db in\n    \u002F\u002F The raw pointer to a database connection:\n    let sqliteConnection = db.sqliteConnection\n\n    \u002F\u002F The raw pointer to a statement:\n    let statement = try db.makeStatement(sql: \"SELECT ...\")\n    let sqliteStatement = statement.sqliteStatement\n}\n```\n\n> **Note**\n>\n> - Those pointers are owned by GRDB: don't close connections or finalize statements created by GRDB.\n> - GRDB opens SQLite connections in the \"[multi-thread mode](https:\u002F\u002Fwww.sqlite.org\u002Fthreadsafe.html)\", which (oddly) means that **they are not thread-safe**. Make sure you touch raw databases and statements inside their dedicated dispatch queues.\n> - Use the raw SQLite C Interface at your own risk. GRDB won't prevent you from shooting yourself in the foot.\n\n\nRecords\n=======\n\n**On top of the [SQLite API](#sqlite-api), GRDB provides protocols** that help manipulating database rows as regular objects named \"records\":\n\n```swift\ntry dbQueue.write { db in\n    if var player = try Player.fetchOne(db, id: 1) {\n        player.score += 10\n        try player.update(db)\n    }\n}\n```\n\nOf course, you need to open a [database connection], and [create database tables](https:\u002F\u002Fswiftpackageindex.com\u002Fgroue\u002FGRDB.swift\u002Fdocumentation\u002Fgrdb\u002Fdatabaseschema) first.\n\nTo define a record type, define a type and extend it with database protocols:\n\n- `FetchableRecord` makes it possible to fetch instances from the database.\n- `PersistableRecord` makes it possible to save instances into the database.\n- `Codable` (not mandatory) provides ready-made serialization to and from database rows.\n- `Identifiable` (not mandatory) provides extra convenience database methods.\n\nTo make it easier to customize database requests, also nest a `Columns` enum: \n\n```swift\nstruct Player: Codable, Identifiable {\n    var id: Int64\n    var name: String\n    var score: Int\n    var team: String?\n}\n\n\u002F\u002F Add database support\nextension Player: FetchableRecord, PersistableRecord {\n    enum Columns {\n        static let name = Column(CodingKeys.name)\n        static let score = Column(CodingKeys.score)\n        static let team = Column(CodingKeys.team)\n    }\n}\n```\n\nSee more [examples of record definitions](#examples-of-record-definitions) below.\n\n> Note: if you are familiar with Core Data's NSManagedObject or Realm's Object, you may experience a cultural shock: GRDB records are not uniqued, do not auto-update, and do not lazy-load. This is both a purpose, and a consequence of protocol-oriented programming.\n>\n> Tip: The [Recommended Practices for Designing Record Types](https:\u002F\u002Fswiftpackageindex.com\u002Fgroue\u002FGRDB.swift\u002Fdocumentation\u002Fgrdb\u002Frecordrecommendedpractices) guide provides general guidance..\n>\n> Tip: See the [Demo Applications] for sample apps that uses records.\n\n**Overview**\n\n- [Inserting Records](#inserting-records)\n- [Fetching Records](#fetching-records)\n- [Updating Records](#updating-records)\n- [Deleting Records](#deleting-records)\n- [Counting Records](#counting-records)\n\n**Protocols and the Record Class**\n\n- [Record Protocols Overview](#record-protocols-overview)\n- [FetchableRecord Protocol](#fetchablerecord-protocol)\n- [TableRecord Protocol](#tablerecord-protocol)\n- [PersistableRecord Protocol](#persistablerecord-protocol)\n    - [Persistence Methods]\n    - [Persistence Methods and the `RETURNING` clause]\n    - [Persistence Callbacks]\n- [Identifiable Records]\n- [Codable Records]\n- [Record Comparison]\n- [Record Customization Options]\n- [Record Timestamps and Transaction Date]\n\n\n### Inserting Records\n\nTo insert a record in the database, call the `insert` method:\n\n```swift\nlet player = Player(id: 1, name: \"Arthur\", score: 1000)\ntry player.insert(db)\n```\n\n:point_right: `insert` is available for types that adopt the [PersistableRecord] protocol.\n\n\n### Fetching Records\n\nTo fetch records from the database, call a [fetching method](#fetching-methods):\n\n```swift\nlet arthur = try Player.fetchOne(db,            \u002F\u002F Player?\n    sql: \"SELECT * FROM players WHERE name = ?\",\n    arguments: [\"Arthur\"])\n\nlet bestPlayers = try Player                    \u002F\u002F [Player]\n    .order(\\.score.desc)\n    .limit(10)\n    .fetchAll(db)\n    \nlet spain = try Country.fetchOne(db, id: \"ES\")  \u002F\u002F Country?\nlet italy = try Country.find(db, id: \"IT\")      \u002F\u002F Country\n```\n\n:point_right: Fetching from raw SQL is available for types that adopt the [FetchableRecord] protocol.\n\n:point_right: Fetching without SQL, using the [query interface](#the-query-interface), is available for types that adopt both [FetchableRecord] and [TableRecord] protocol.\n\n\n### Updating Records\n\nTo update a record in the database, call the `update` method:\n\n```swift\nvar player: Player = ...\nplayer.score = 1000\ntry player.update(db)\n```\n\nIt is possible to [avoid useless updates](#record-comparison):\n\n```swift\n\u002F\u002F does not hit the database if score has not changed\ntry player.updateChanges(db) {\n    $0.score = 1000\n}\n```\n\nSee the [query interface](#the-query-interface) for batch updates:\n\n```swift\ntry Player\n    .filter { $0.team == \"red\" }\n    .updateAll(db) { $0.score += 1 }\n```\n\n:point_right: update methods are available for types that adopt the [PersistableRecord] protocol. Batch updates are available on the [TableRecord] protocol.\n\n\n### Deleting Records\n\nTo delete a record in the database, call the `delete` method:\n\n```swift\nlet player: Player = ...\ntry player.delete(db)\n```\n\nYou can also delete by primary key, unique key, or perform batch ","GRDB.swift 是一个专注于应用程序开发的 SQLite 数据库工具包。它提供了SQL生成、数据库观察、健壮的并发处理以及数据库迁移等核心功能，使得开发者能够更高效地管理和操作数据。通过增强应用模型以支持持久化和获取方法，简化了与数据库交互的过程；同时，其对多线程的支持确保了在并发读写场景下的高效性能。适用于需要使用SQLite作为后端存储的各种iOS、macOS、tvOS或watchOS平台上的应用程序开发项目。",2,"2026-06-11 03:08:39","top_language"]