From 44097d32651d093e826e16ce5d2fa13d4c774c3a Mon Sep 17 00:00:00 2001
From: Dave DeLong <davedelong@me.com>
Date: Mon, 16 Apr 2018 16:13:04 +0200
Subject: [PATCH 001/326] Add `and` and `or` functions

When you're programmatically building a query to execute, you have times where you build a variable number of clauses by which you filter a table. There isn't a great way to turn an `Array<Expression<Bool>>` into an `Expression<Bool>`, short of making a large `(a AND (b AND (c AND (d AND e))))` expression.

`and` and `or` fix this by allowing you to turn an `Array<Expression<Bool>>` directly into a lower-complexity clause: `(a AND b AND c AND d AND e)`
---
 Sources/SQLite/Helpers.swift           |  6 +++++-
 Sources/SQLite/Typed/Operators.swift   | 12 ++++++++++++
 Tests/SQLiteTests/OperatorsTests.swift | 20 ++++++++++++++++++++
 3 files changed, 37 insertions(+), 1 deletion(-)

diff --git a/Sources/SQLite/Helpers.swift b/Sources/SQLite/Helpers.swift
index ac831667..b4ff360e 100644
--- a/Sources/SQLite/Helpers.swift
+++ b/Sources/SQLite/Helpers.swift
@@ -73,7 +73,11 @@ extension String {
     }
 
     func infix<T>(_ lhs: Expressible, _ rhs: Expressible, wrap: Bool = true) -> Expression<T> {
-        let expression = Expression<T>(" \(self) ".join([lhs, rhs]).expression)
+        return infix([lhs, rhs])
+    }
+    
+    func infix<T>(_ terms: [Expressible], wrap: Bool = true) -> Expression<T> {
+        let expression = Expression<T>(" \(self) ".join(terms).expression)
         guard wrap else {
             return expression
         }
diff --git a/Sources/SQLite/Typed/Operators.swift b/Sources/SQLite/Typed/Operators.swift
index d97e52b9..a6df717f 100644
--- a/Sources/SQLite/Typed/Operators.swift
+++ b/Sources/SQLite/Typed/Operators.swift
@@ -516,6 +516,12 @@ public func ~=<V : Value>(lhs: PartialRangeFrom<V>, rhs: Expression<V?>) -> Expr
 
 // MARK: -
 
+public func and(_ terms: Expression<Bool>...) -> Expression<Bool> {
+    return "AND".infix(terms)
+}
+public func and(_ terms: [Expression<Bool>]) -> Expression<Bool> {
+    return "AND".infix(terms)
+}
 public func &&(lhs: Expression<Bool>, rhs: Expression<Bool>) -> Expression<Bool> {
     return "AND".infix(lhs, rhs)
 }
@@ -541,6 +547,12 @@ public func &&(lhs: Bool, rhs: Expression<Bool?>) -> Expression<Bool?> {
     return "AND".infix(lhs, rhs)
 }
 
+public func or(_ terms: Expression<Bool>...) -> Expression<Bool> {
+    return "OR".infix(terms)
+}
+public func or(_ terms: [Expression<Bool>]) -> Expression<Bool> {
+    return "OR".infix(terms)
+}
 public func ||(lhs: Expression<Bool>, rhs: Expression<Bool>) -> Expression<Bool> {
     return "OR".infix(lhs, rhs)
 }
diff --git a/Tests/SQLiteTests/OperatorsTests.swift b/Tests/SQLiteTests/OperatorsTests.swift
index 948eb0a4..050b1e39 100644
--- a/Tests/SQLiteTests/OperatorsTests.swift
+++ b/Tests/SQLiteTests/OperatorsTests.swift
@@ -290,6 +290,16 @@ class OperatorsTests : XCTestCase {
         AssertSQL("(1 AND \"bool\")", true && bool)
         AssertSQL("(1 AND \"boolOptional\")", true && boolOptional)
     }
+    
+    func test_andFunction_withBooleanExpressions_buildsCompoundExpression() {
+        AssertSQL("(\"bool\" AND \"bool\" AND \"bool\")", and([bool, bool, bool]))
+        AssertSQL("(\"bool\" AND \"bool\")", and([bool, bool]))
+        AssertSQL("(\"bool\")", and([bool]))
+        
+        AssertSQL("(\"bool\" AND \"bool\" AND \"bool\")", and(bool, bool, bool))
+        AssertSQL("(\"bool\" AND \"bool\")", and(bool, bool))
+        AssertSQL("(\"bool\")", and(bool))
+    }
 
     func test_doubleOrOperator_withBooleanExpressions_buildsCompoundExpression() {
         AssertSQL("(\"bool\" OR \"bool\")", bool || bool)
@@ -301,6 +311,16 @@ class OperatorsTests : XCTestCase {
         AssertSQL("(1 OR \"bool\")", true || bool)
         AssertSQL("(1 OR \"boolOptional\")", true || boolOptional)
     }
+    
+    func test_orFunction_withBooleanExpressions_buildsCompoundExpression() {
+        AssertSQL("(\"bool\" OR \"bool\" OR \"bool\")", or([bool, bool, bool]))
+        AssertSQL("(\"bool\" OR \"bool\")", or([bool, bool]))
+        AssertSQL("(\"bool\")", or([bool]))
+        
+        AssertSQL("(\"bool\" OR \"bool\" OR \"bool\")", or(bool, bool, bool))
+        AssertSQL("(\"bool\" OR \"bool\")", or(bool, bool))
+        AssertSQL("(\"bool\")", or(bool))
+    }
 
     func test_unaryNotOperator_withBooleanExpressions_buildsNotExpression() {
         AssertSQL("NOT (\"bool\")", !bool)

From e080a3ac0d2dc8fea15dde5681939bbbc0848b3e Mon Sep 17 00:00:00 2001
From: Dave DeLong <davedelong@me.com>
Date: Mon, 16 Apr 2018 16:15:09 +0200
Subject: [PATCH 002/326] Forgot to pass on a parameter

---
 Sources/SQLite/Helpers.swift | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Sources/SQLite/Helpers.swift b/Sources/SQLite/Helpers.swift
index b4ff360e..4b755078 100644
--- a/Sources/SQLite/Helpers.swift
+++ b/Sources/SQLite/Helpers.swift
@@ -73,7 +73,7 @@ extension String {
     }
 
     func infix<T>(_ lhs: Expressible, _ rhs: Expressible, wrap: Bool = true) -> Expression<T> {
-        return infix([lhs, rhs])
+        return infix([lhs, rhs], wrap: wrap)
     }
     
     func infix<T>(_ terms: [Expressible], wrap: Bool = true) -> Expression<T> {

From 1f84745513aa56bbd38e428e0a5db5d55de3381e Mon Sep 17 00:00:00 2001
From: Serg <redetection@gmail.com>
Date: Sun, 15 Jul 2018 23:36:52 +0700
Subject: [PATCH 003/326] Support Int64 coding

---
 Sources/SQLite/Typed/Coding.swift | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Sources/SQLite/Typed/Coding.swift b/Sources/SQLite/Typed/Coding.swift
index 7c70db33..3e4b5226 100644
--- a/Sources/SQLite/Typed/Coding.swift
+++ b/Sources/SQLite/Typed/Coding.swift
@@ -152,7 +152,7 @@ fileprivate class SQLiteEncoder: Encoder {
         }
 
         func encode(_ value: Int64, forKey key: Key) throws {
-            throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: self.codingPath, debugDescription: "encoding an Int64 is not supported"))
+            self.encoder.setters.append(Expression(key.stringValue) <- value)
         }
 
         func encode(_ value: UInt, forKey key: Key) throws {
@@ -249,7 +249,7 @@ fileprivate class SQLiteDecoder : Decoder {
         }
 
         func decode(_ type: Int64.Type, forKey key: Key) throws -> Int64 {
-            throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: self.codingPath, debugDescription: "decoding an UInt64 is not supported"))
+            return try self.row.get(Expression(key.stringValue))
         }
 
         func decode(_ type: UInt.Type, forKey key: Key) throws -> UInt {

From 304c39d42f45c07eece380f1d64e4ba40f3fadb1 Mon Sep 17 00:00:00 2001
From: Jason Peterson <jasonwpeterson@icloud.com>
Date: Sat, 29 Sep 2018 21:27:34 -0500
Subject: [PATCH 004/326] added sample code for copying a 'seed' database

---
 Documentation/Index.md | 24 +++++++++++++++++++++++-
 1 file changed, 23 insertions(+), 1 deletion(-)

diff --git a/Documentation/Index.md b/Documentation/Index.md
index 628e7464..12810f94 100644
--- a/Documentation/Index.md
+++ b/Documentation/Index.md
@@ -258,6 +258,28 @@ let path = NSSearchPathForDirectoriesInDomains(
 let db = try Connection("\(path)/db.sqlite3")
 ```
 
+If you have bundled it your application, you can use FileManager to copy it to the Documents directory:
+
+```swift
+func copyDatabaseIfNeeded(sourcePath : String) -> Bool {
+    var destinationPath = NSSearchPathForDirectoriesInDomains(
+        .documentDirectory, .userDomainMask, true
+    ).first!
+    destinationPath = destinationPath + "/db.sqlite3"
+    let databaseExistsWhereNeeded = FileManager.default.fileExists(atPath: destinationPath)
+    if (!databaseExistsWhereNeeded) {
+        do {
+            try FileManager.default.copyItem(atPath: sourcePath, toPath: destinationPath)
+            print("db copied")
+        }
+        catch {
+            print("error during file copy: \(error)")
+        }
+    }
+    return true
+}
+```
+
 On OS X, you can use your app’s **Application Support** directory:
 
 ```swift
@@ -295,7 +317,7 @@ let db = try Connection(path, readonly: true)
 > See these two Stack Overflow questions for more information about iOS apps
 > with SQLite databases: [1](https://stackoverflow.com/questions/34609746/what-different-between-store-database-in-different-locations-in-ios),
 > [2](https://stackoverflow.com/questions/34614968/ios-how-to-copy-pre-seeded-database-at-the-first-running-app-with-sqlite-swift).
-> We welcome sample code to show how to successfully copy and use a bundled "seed"
+> We welcome changes to the above sample code to show how to successfully copy and use a bundled "seed"
 > database for writing in an app.
 
 #### In-Memory Databases

From ad63c37cc044af3d4bc1588faa6c41c5600b71b0 Mon Sep 17 00:00:00 2001
From: Madalin Mamuleanu <mamuleanu.madalin@gmail.com>
Date: Tue, 2 Oct 2018 00:21:03 +0300
Subject: [PATCH 005/326] Documentation update. Fixes #841

---
 Documentation/Index.md | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Documentation/Index.md b/Documentation/Index.md
index 628e7464..f214b50a 100644
--- a/Documentation/Index.md
+++ b/Documentation/Index.md
@@ -266,8 +266,8 @@ var path = NSSearchPathForDirectoriesInDomains(
 ).first! + "/" + Bundle.main.bundleIdentifier!
 
 // create parent directory iff it doesn’t exist
-try FileManager.default.createDirectoryAtPath(
-    path, withIntermediateDirectories: true, attributes: nil
+try FileManager.default.createDirectory(
+atPath: path, withIntermediateDirectories: true, attributes: nil
 )
 
 let db = try Connection("\(path)/db.sqlite3")

From 7c0bdfd01fd3461d273d24cb4f0205614567d4c9 Mon Sep 17 00:00:00 2001
From: Christian Roman <hello@christianroman.net>
Date: Fri, 11 Jan 2019 09:05:41 -0600
Subject: [PATCH 006/326] Codable - Add Date support

---
 Documentation/Index.md              |  2 +-
 Sources/SQLite/Typed/Coding.swift   |  7 +++++++
 Tests/SQLiteTests/QueryTests.swift  | 27 +++++++++++++++------------
 Tests/SQLiteTests/TestHelpers.swift |  5 ++++-
 4 files changed, 27 insertions(+), 14 deletions(-)

diff --git a/Documentation/Index.md b/Documentation/Index.md
index 628e7464..7fc74ea0 100644
--- a/Documentation/Index.md
+++ b/Documentation/Index.md
@@ -1557,7 +1557,7 @@ Both of the above methods also have the following optional parameter:
 There are a few restrictions on using Codable types:
 
 - The encodable and decodable objects can only use the following types:
-    - Int, Bool, Float, Double, String
+    - Int, Bool, Float, Double, String, Date
     - Nested Codable types that will be encoded as JSON to a single column
 - These methods will not handle object relationships for you. You must write
   your own Codable and Decodable implementations if you wish to support this.
diff --git a/Sources/SQLite/Typed/Coding.swift b/Sources/SQLite/Typed/Coding.swift
index c3fb931b..0eb02a55 100644
--- a/Sources/SQLite/Typed/Coding.swift
+++ b/Sources/SQLite/Typed/Coding.swift
@@ -132,6 +132,9 @@ fileprivate class SQLiteEncoder: Encoder {
             if let data = value as? Data {
                 self.encoder.setters.append(Expression(key.stringValue) <- data)
             }
+            else if let date = value as? Date {
+                self.encoder.setters.append(Expression(key.stringValue) <- date.datatypeValue)
+            }
             else {
                 let encoded = try JSONEncoder().encode(value)
                 let string = String(data: encoded, encoding: .utf8)
@@ -290,6 +293,10 @@ fileprivate class SQLiteDecoder : Decoder {
                 let data = try self.row.get(Expression<Data>(key.stringValue))
                 return data as! T
             }
+            else if type == Date.self {
+                let date = try self.row.get(Expression<Date>(key.stringValue))
+                return date as! T
+            }
             guard let JSONString = try self.row.get(Expression<String?>(key.stringValue)) else {
                 throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: self.codingPath, debugDescription: "an unsupported type was found"))
             }
diff --git a/Tests/SQLiteTests/QueryTests.swift b/Tests/SQLiteTests/QueryTests.swift
index 2a9e4ecb..8f48264f 100644
--- a/Tests/SQLiteTests/QueryTests.swift
+++ b/Tests/SQLiteTests/QueryTests.swift
@@ -249,23 +249,23 @@ class QueryTests : XCTestCase {
 
     func test_insert_encodable() throws {
         let emails = Table("emails")
-        let value = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4, optional: nil, sub: nil)
+        let value = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4, date: Date(timeIntervalSince1970: 0), optional: nil, sub: nil)
         let insert = try emails.insert(value)
         AssertSQL(
-            "INSERT INTO \"emails\" (\"int\", \"string\", \"bool\", \"float\", \"double\") VALUES (1, '2', 1, 3.0, 4.0)",
+            "INSERT INTO \"emails\" (\"int\", \"string\", \"bool\", \"float\", \"double\", \"date\") VALUES (1, '2', 1, 3.0, 4.0, '1970-01-01T00:00:00.000')",
             insert
         )
     }
 
     func test_insert_encodable_with_nested_encodable() throws {
         let emails = Table("emails")
-        let value1 = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4, optional: nil, sub: nil)
-        let value = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4, optional: "optional", sub: value1)
+        let value1 = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4, date: Date(timeIntervalSince1970: 0), optional: nil, sub: nil)
+        let value = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4, date: Date(timeIntervalSince1970: 0), optional: "optional", sub: value1)
         let insert = try emails.insert(value)
         let encodedJSON = try JSONEncoder().encode(value1)
         let encodedJSONString = String(data: encodedJSON, encoding: .utf8)!
         AssertSQL(
-            "INSERT INTO \"emails\" (\"int\", \"string\", \"bool\", \"float\", \"double\", \"optional\", \"sub\") VALUES (1, '2', 1, 3.0, 4.0, 'optional', '\(encodedJSONString)')",
+            "INSERT INTO \"emails\" (\"int\", \"string\", \"bool\", \"float\", \"double\", \"date\", \"optional\", \"sub\") VALUES (1, '2', 1, 3.0, 4.0, '1970-01-01T00:00:00.000', 'optional', '\(encodedJSONString)')",
             insert
         )
     }
@@ -286,23 +286,23 @@ class QueryTests : XCTestCase {
 
     func test_update_encodable() throws {
         let emails = Table("emails")
-        let value = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4, optional: nil, sub: nil)
+        let value = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4, date: Date(timeIntervalSince1970: 0), optional: nil, sub: nil)
         let update = try emails.update(value)
         AssertSQL(
-            "UPDATE \"emails\" SET \"int\" = 1, \"string\" = '2', \"bool\" = 1, \"float\" = 3.0, \"double\" = 4.0",
+            "UPDATE \"emails\" SET \"int\" = 1, \"string\" = '2', \"bool\" = 1, \"float\" = 3.0, \"double\" = 4.0, \"date\" = '1970-01-01T00:00:00.000'",
             update
         )
     }
 
     func test_update_encodable_with_nested_encodable() throws {
         let emails = Table("emails")
-        let value1 = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4, optional: nil, sub: nil)
-        let value = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4, optional: nil, sub: value1)
+        let value1 = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4, date: Date(timeIntervalSince1970: 0), optional: nil, sub: nil)
+        let value = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4, date: Date(timeIntervalSince1970: 0), optional: nil, sub: value1)
         let update = try emails.update(value)
         let encodedJSON = try JSONEncoder().encode(value1)
         let encodedJSONString = String(data: encodedJSON, encoding: .utf8)!
         AssertSQL(
-            "UPDATE \"emails\" SET \"int\" = 1, \"string\" = '2', \"bool\" = 1, \"float\" = 3.0, \"double\" = 4.0, \"sub\" = '\(encodedJSONString)'",
+            "UPDATE \"emails\" SET \"int\" = 1, \"string\" = '2', \"bool\" = 1, \"float\" = 3.0, \"double\" = 4.0, \"date\" = '1970-01-01T00:00:00.000', \"sub\" = '\(encodedJSONString)'",
             update
         )
     }
@@ -438,12 +438,13 @@ class QueryIntegrationTests : SQLiteTestCase {
             builder.column(Expression<Bool>("bool"))
             builder.column(Expression<Double>("float"))
             builder.column(Expression<Double>("double"))
+            builder.column(Expression<Date>("date"))
             builder.column(Expression<String?>("optional"))
             builder.column(Expression<Data>("sub"))
         })
 
-        let value1 = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4, optional: nil, sub: nil)
-        let value = TestCodable(int: 5, string: "6", bool: true, float: 7, double: 8, optional: "optional", sub: value1)
+        let value1 = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4, date: Date(timeIntervalSince1970: 0), optional: nil, sub: nil)
+        let value = TestCodable(int: 5, string: "6", bool: true, float: 7, double: 8, date: Date(timeIntervalSince1970: 5000), optional: "optional", sub: value1)
 
         try db.run(table.insert(value))
 
@@ -455,12 +456,14 @@ class QueryIntegrationTests : SQLiteTestCase {
         XCTAssertEqual(values[0].bool, true)
         XCTAssertEqual(values[0].float, 7)
         XCTAssertEqual(values[0].double, 8)
+        XCTAssertEqual(values[0].date, Date(timeIntervalSince1970: 5000))
         XCTAssertEqual(values[0].optional, "optional")
         XCTAssertEqual(values[0].sub?.int, 1)
         XCTAssertEqual(values[0].sub?.string, "2")
         XCTAssertEqual(values[0].sub?.bool, true)
         XCTAssertEqual(values[0].sub?.float, 3)
         XCTAssertEqual(values[0].sub?.double, 4)
+        XCTAssertEqual(values[0].sub?.date, Date(timeIntervalSince1970: 0))
         XCTAssertNil(values[0].sub?.optional)
         XCTAssertNil(values[0].sub?.sub)
     }
diff --git a/Tests/SQLiteTests/TestHelpers.swift b/Tests/SQLiteTests/TestHelpers.swift
index 2e491b01..247013b4 100644
--- a/Tests/SQLiteTests/TestHelpers.swift
+++ b/Tests/SQLiteTests/TestHelpers.swift
@@ -26,6 +26,7 @@ class SQLiteTestCase : XCTestCase {
                 salary REAL,
                 admin BOOLEAN NOT NULL DEFAULT 0 CHECK (admin IN (0, 1)),
                 manager_id INTEGER,
+                created_at DATETIME,
                 FOREIGN KEY(manager_id) REFERENCES users(id)
             )
             """
@@ -111,15 +112,17 @@ class TestCodable: Codable {
     let bool: Bool
     let float: Float
     let double: Double
+    let date: Date
     let optional: String?
     let sub: TestCodable?
 
-    init(int: Int, string: String, bool: Bool, float: Float, double: Double, optional: String?, sub: TestCodable?) {
+    init(int: Int, string: String, bool: Bool, float: Float, double: Double, date: Date, optional: String?, sub: TestCodable?) {
         self.int = int
         self.string = string
         self.bool = bool
         self.float = float
         self.double = double
+        self.date = date
         self.optional = optional
         self.sub = sub
     }

From 2b82d152e1870d353cedae0bcbfce0b31a511275 Mon Sep 17 00:00:00 2001
From: Gregory Todd Williams <greg@evilfunhouse.com>
Date: Thu, 21 Feb 2019 21:08:29 -0800
Subject: [PATCH 007/326] Add prototype implementation for
 Connection.createAggregation.

---
 SQLite.xcodeproj/project.pbxproj              |   8 ++
 Sources/SQLite/Core/Connection.swift          | 106 ++++++++++++++++++
 .../SQLiteTests/CustomAggregationTests.swift  |  73 ++++++++++++
 3 files changed, 187 insertions(+)
 create mode 100644 Tests/SQLiteTests/CustomAggregationTests.swift

diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj
index a155a115..1635f651 100644
--- a/SQLite.xcodeproj/project.pbxproj
+++ b/SQLite.xcodeproj/project.pbxproj
@@ -81,6 +81,9 @@
 		19A17FB80B94E882050AA908 /* FoundationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1794CC4D7827E997E32A7 /* FoundationTests.swift */; };
 		19A17FDA323BAFDEC627E76F /* fixtures in Resources */ = {isa = PBXBuildFile; fileRef = 19A17E2695737FAB5D6086E3 /* fixtures */; };
 		19A17FF4A10B44D3937C8CAC /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1710E73A46D5AC721CDA9 /* Errors.swift */; };
+		3717F908221F5D8800B9BD3D /* CustomAggregationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3717F907221F5D7C00B9BD3D /* CustomAggregationTests.swift */; };
+		3717F909221F5D8900B9BD3D /* CustomAggregationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3717F907221F5D7C00B9BD3D /* CustomAggregationTests.swift */; };
+		3717F90A221F5D8900B9BD3D /* CustomAggregationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3717F907221F5D7C00B9BD3D /* CustomAggregationTests.swift */; };
 		3D67B3E61DB2469200A4F4C6 /* libsqlite3.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 3D67B3E51DB2469200A4F4C6 /* libsqlite3.tbd */; };
 		3D67B3E71DB246BA00A4F4C6 /* Blob.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AEE1C3F06E900AE3E12 /* Blob.swift */; };
 		3D67B3E81DB246BA00A4F4C6 /* Connection.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AEF1C3F06E900AE3E12 /* Connection.swift */; };
@@ -225,6 +228,7 @@
 		19A17B93B48B5560E6E51791 /* Fixtures.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Fixtures.swift; sourceTree = "<group>"; };
 		19A17BA55DABB480F9020C8A /* DateAndTimeFunctions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DateAndTimeFunctions.swift; sourceTree = "<group>"; };
 		19A17E2695737FAB5D6086E3 /* fixtures */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = folder; path = fixtures; sourceTree = "<group>"; };
+		3717F907221F5D7C00B9BD3D /* CustomAggregationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomAggregationTests.swift; sourceTree = "<group>"; };
 		3D67B3E51DB2469200A4F4C6 /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = Platforms/WatchOS.platform/Developer/SDKs/WatchOS3.0.sdk/usr/lib/libsqlite3.tbd; sourceTree = DEVELOPER_DIR; };
 		49EB68C31F7B3CB400D89D40 /* Coding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Coding.swift; sourceTree = "<group>"; };
 		A121AC451CA35C79005A31D1 /* SQLite.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SQLite.framework; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -401,6 +405,7 @@
 				EE247B1D1C3F137700AE3E12 /* ConnectionTests.swift */,
 				EE247B1E1C3F137700AE3E12 /* CoreFunctionsTests.swift */,
 				EE247B1F1C3F137700AE3E12 /* CustomFunctionsTests.swift */,
+				3717F907221F5D7C00B9BD3D /* CustomAggregationTests.swift */,
 				EE247B201C3F137700AE3E12 /* ExpressionTests.swift */,
 				EE247B211C3F137700AE3E12 /* FTS4Tests.swift */,
 				EE247B2A1C3F141E00AE3E12 /* OperatorsTests.swift */,
@@ -834,6 +839,7 @@
 				03A65E921C6BB3030062603F /* SetterTests.swift in Sources */,
 				03A65E891C6BB3030062603F /* ConnectionTests.swift in Sources */,
 				03A65E8A1C6BB3030062603F /* CoreFunctionsTests.swift in Sources */,
+				3717F90A221F5D8900B9BD3D /* CustomAggregationTests.swift in Sources */,
 				03A65E931C6BB3030062603F /* StatementTests.swift in Sources */,
 				03A65E911C6BB3030062603F /* SchemaTests.swift in Sources */,
 				03A65E8D1C6BB3030062603F /* FTS4Tests.swift in Sources */,
@@ -922,6 +928,7 @@
 				EE247B271C3F137700AE3E12 /* CustomFunctionsTests.swift in Sources */,
 				EE247B341C3F142E00AE3E12 /* StatementTests.swift in Sources */,
 				EE247B301C3F141E00AE3E12 /* RTreeTests.swift in Sources */,
+				3717F908221F5D8800B9BD3D /* CustomAggregationTests.swift in Sources */,
 				EE247B231C3F137700AE3E12 /* BlobTests.swift in Sources */,
 				EE247B351C3F142E00AE3E12 /* ValueTests.swift in Sources */,
 				EE247B2F1C3F141E00AE3E12 /* QueryTests.swift in Sources */,
@@ -980,6 +987,7 @@
 				EE247B5F1C3F3FC700AE3E12 /* StatementTests.swift in Sources */,
 				EE247B5C1C3F3FC700AE3E12 /* RTreeTests.swift in Sources */,
 				EE247B571C3F3FC700AE3E12 /* CustomFunctionsTests.swift in Sources */,
+				3717F909221F5D8900B9BD3D /* CustomAggregationTests.swift in Sources */,
 				EE247B601C3F3FC700AE3E12 /* ValueTests.swift in Sources */,
 				EE247B551C3F3FC700AE3E12 /* ConnectionTests.swift in Sources */,
 				EE247B611C3F3FC700AE3E12 /* TestHelpers.swift in Sources */,
diff --git a/Sources/SQLite/Core/Connection.swift b/Sources/SQLite/Core/Connection.swift
index 1bbf7f73..1fce790f 100644
--- a/Sources/SQLite/Core/Connection.swift
+++ b/Sources/SQLite/Core/Connection.swift
@@ -597,8 +597,114 @@ public final class Connection {
         if functions[function] == nil { self.functions[function] = [:] }
         functions[function]?[argc] = box
     }
+    
+    /// Creates or redefines a custom SQL aggregate.
+    ///
+    /// - Parameters:
+    ///
+    ///   - aggregate: The name of the aggregate to create or redefine.
+    ///
+    ///   - argumentCount: The number of arguments that the aggregate takes. If
+    ///     `nil`, the aggregate may take any number of arguments.
+    ///
+    ///     Default: `nil`
+    ///
+    ///   - deterministic: Whether or not the aggregate is deterministic (_i.e._
+    ///     the aggregate always returns the same result for a given input).
+    ///
+    ///     Default: `false`
+    ///
+    ///   - step: A block of code to run for each row of an aggregation group.
+    ///     The block is called with an array of raw SQL values mapped to the
+    ///     aggregate’s parameters, and an UnsafeMutablePointer to a state
+    ///     variable.
+    ///
+    ///   - final: A block of code to run after each row of an aggregation group
+    ///     is processed. The block is called with an UnsafeMutablePointer to a
+    ///     state variable, and should return a raw SQL value (or nil).
+    ///
+    ///   - state: A block of code to run to produce a fresh state variable for
+    ///     each aggregation group. The block should return an
+    ///     UnsafeMutablePointer to the fresh state variable.
+    public func createAggregation<T>(_ aggregate: String, argumentCount: UInt? = nil, deterministic: Bool = false, step: @escaping ([Binding?], UnsafeMutablePointer<T>) -> (), final: @escaping (UnsafeMutablePointer<T>) -> Binding?, state: @escaping () -> UnsafeMutablePointer<T>) {
+        let argc = argumentCount.map { Int($0) } ?? -1
+        let box : Aggregate = { (stepFlag: Int, context: OpaquePointer?, argc: Int32, argv: UnsafeMutablePointer<OpaquePointer?>?) in
+            let ptr = sqlite3_aggregate_context(context, 64)! // needs to be at least as large as uintptr_t; better way to do this?
+            let p = ptr.assumingMemoryBound(to: UnsafeMutableRawPointer.self)
+            if stepFlag > 0 {
+                let arguments: [Binding?] = (0..<Int(argc)).map { idx in
+                    let value = argv![idx]
+                    switch sqlite3_value_type(value) {
+                    case SQLITE_BLOB:
+                        return Blob(bytes: sqlite3_value_blob(value), length: Int(sqlite3_value_bytes(value)))
+                    case SQLITE_FLOAT:
+                        return sqlite3_value_double(value)
+                    case SQLITE_INTEGER:
+                        return sqlite3_value_int64(value)
+                    case SQLITE_NULL:
+                        return nil
+                    case SQLITE_TEXT:
+                        return String(cString: UnsafePointer(sqlite3_value_text(value)))
+                    case let type:
+                        fatalError("unsupported value type: \(type)")
+                    }
+                }
+                
+                if ptr.assumingMemoryBound(to: Int64.self).pointee == 0 {
+                    let v = state()
+                    p.pointee = UnsafeMutableRawPointer(mutating: v)
+                }
+                step(arguments, p.pointee.assumingMemoryBound(to: T.self))
+            } else {
+                let result = final(p.pointee.assumingMemoryBound(to: T.self))
+                if let result = result as? Blob {
+                    sqlite3_result_blob(context, result.bytes, Int32(result.bytes.count), nil)
+                } else if let result = result as? Double {
+                    sqlite3_result_double(context, result)
+                } else if let result = result as? Int64 {
+                    sqlite3_result_int64(context, result)
+                } else if let result = result as? String {
+                    sqlite3_result_text(context, result, Int32(result.count), SQLITE_TRANSIENT)
+                } else if result == nil {
+                    sqlite3_result_null(context)
+                } else {
+                    fatalError("unsupported result type: \(String(describing: result))")
+                }
+            }
+        }
+        
+        var flags = SQLITE_UTF8
+        #if !os(Linux)
+        if deterministic {
+            flags |= SQLITE_DETERMINISTIC
+        }
+        #endif
+        
+        sqlite3_create_function_v2(
+            handle,
+            aggregate,
+            Int32(argc),
+            flags,
+            unsafeBitCast(box, to: UnsafeMutableRawPointer.self),
+            nil,
+            { context, argc, value in
+                let function = unsafeBitCast(sqlite3_user_data(context), to: Aggregate.self)
+                function(1, context, argc, value)
+            },
+            { context in
+                let function = unsafeBitCast(sqlite3_user_data(context), to: Aggregate.self)
+                function(0, context, 0, nil)
+            },
+            nil
+        )
+        if aggregations[aggregate] == nil { self.aggregations[aggregate] = [:] }
+        aggregations[aggregate]?[argc] = box
+    }
+    
+    fileprivate typealias Aggregate = @convention(block) (Int, OpaquePointer?, Int32, UnsafeMutablePointer<OpaquePointer?>?) -> Void
     fileprivate typealias Function = @convention(block) (OpaquePointer?, Int32, UnsafeMutablePointer<OpaquePointer?>?) -> Void
     fileprivate var functions = [String: [Int: Function]]()
+    fileprivate var aggregations = [String: [Int: Aggregate]]()
 
     /// Defines a new collating sequence.
     ///
diff --git a/Tests/SQLiteTests/CustomAggregationTests.swift b/Tests/SQLiteTests/CustomAggregationTests.swift
new file mode 100644
index 00000000..56c3c011
--- /dev/null
+++ b/Tests/SQLiteTests/CustomAggregationTests.swift
@@ -0,0 +1,73 @@
+import XCTest
+import Foundation
+import Dispatch
+@testable import SQLite
+
+#if SQLITE_SWIFT_STANDALONE
+import sqlite3
+#elseif SQLITE_SWIFT_SQLCIPHER
+import SQLCipher
+#elseif os(Linux)
+import CSQLite
+#else
+import SQLite3
+#endif
+
+class CustomAggregationTests : SQLiteTestCase {
+    override func setUp() {
+        super.setUp()
+        CreateUsersTable()
+        try! InsertUser("Alice", age: 30, admin: true)
+        try! InsertUser("Bob", age: 25, admin: true)
+        try! InsertUser("Eve", age: 28, admin: false)
+    }
+    
+    func testCustomSum() {
+        let step = { (bindings: [Binding?], state: UnsafeMutablePointer<Int64>) in
+            if let v = bindings[0] as? Int64 {
+                state.pointee += v
+            }
+        }
+
+        let final = { (state: UnsafeMutablePointer<Int64>) -> Binding? in
+            let v = state.pointee
+            let p = UnsafeMutableBufferPointer(start: state, count: 1)
+            p.deallocate()
+            return v
+        }
+        let _ = db.createAggregation("mySUM", step: step, final: final) {
+            let v = UnsafeMutableBufferPointer<Int64>.allocate(capacity: 1)
+            v[0] = 0
+            return v.baseAddress!
+        }
+        let result = try! db.prepare("SELECT mySUM(age) AS s FROM users")
+        let i = result.columnNames.index(of: "s")!
+        for row in result {
+            let value = row[i] as? Int64
+            XCTAssertEqual(83, value)
+        }
+    }
+    
+    func testCustomSumGrouping() {
+        let step = { (bindings: [Binding?], state: UnsafeMutablePointer<Int64>) in
+            if let v = bindings[0] as? Int64 {
+                state.pointee += v
+            }
+        }
+        let final = { (state: UnsafeMutablePointer<Int64>) -> Binding? in
+            let v = state.pointee
+            let p = UnsafeMutableBufferPointer(start: state, count: 1)
+            p.deallocate()
+            return v
+        }
+        let _ = db.createAggregation("mySUM", step: step, final: final) {
+            let v = UnsafeMutableBufferPointer<Int64>.allocate(capacity: 1)
+            v[0] = 0
+            return v.baseAddress!
+        }
+        let result = try! db.prepare("SELECT mySUM(age) AS s FROM users GROUP BY admin ORDER BY s")
+        let i = result.columnNames.index(of: "s")!
+        let values = result.compactMap { $0[i] as? Int64 }
+        XCTAssertTrue(values.elementsEqual([28, 55]))
+    }
+}

From 1e6bfbf2c046606adc08c9ca2ee4d7bc17f7cb2a Mon Sep 17 00:00:00 2001
From: Gregory Todd Williams <greg@evilfunhouse.com>
Date: Thu, 21 Feb 2019 21:58:34 -0800
Subject: [PATCH 008/326] Added convenience overloads for
 Connection.createAggregation.

---
 Sources/SQLite/Core/Connection.swift          | 16 +++--
 Sources/SQLite/Typed/CustomFunctions.swift    | 65 +++++++++++++++++
 .../SQLiteTests/CustomAggregationTests.swift  | 71 +++++++++++++++++--
 3 files changed, 141 insertions(+), 11 deletions(-)

diff --git a/Sources/SQLite/Core/Connection.swift b/Sources/SQLite/Core/Connection.swift
index 1fce790f..349c18b6 100644
--- a/Sources/SQLite/Core/Connection.swift
+++ b/Sources/SQLite/Core/Connection.swift
@@ -626,7 +626,15 @@ public final class Connection {
     ///   - state: A block of code to run to produce a fresh state variable for
     ///     each aggregation group. The block should return an
     ///     UnsafeMutablePointer to the fresh state variable.
-    public func createAggregation<T>(_ aggregate: String, argumentCount: UInt? = nil, deterministic: Bool = false, step: @escaping ([Binding?], UnsafeMutablePointer<T>) -> (), final: @escaping (UnsafeMutablePointer<T>) -> Binding?, state: @escaping () -> UnsafeMutablePointer<T>) {
+    public func createAggregation<T>(
+        _ aggregate: String,
+        argumentCount: UInt? = nil,
+        deterministic: Bool = false,
+        step: @escaping ([Binding?], UnsafeMutablePointer<T>) -> (),
+        final: @escaping (UnsafeMutablePointer<T>) -> Binding?,
+        state: @escaping () -> UnsafeMutablePointer<T>) {
+        
+        
         let argc = argumentCount.map { Int($0) } ?? -1
         let box : Aggregate = { (stepFlag: Int, context: OpaquePointer?, argc: Int32, argv: UnsafeMutablePointer<OpaquePointer?>?) in
             let ptr = sqlite3_aggregate_context(context, 64)! // needs to be at least as large as uintptr_t; better way to do this?
@@ -690,17 +698,17 @@ public final class Connection {
             { context, argc, value in
                 let function = unsafeBitCast(sqlite3_user_data(context), to: Aggregate.self)
                 function(1, context, argc, value)
-            },
+        },
             { context in
                 let function = unsafeBitCast(sqlite3_user_data(context), to: Aggregate.self)
                 function(0, context, 0, nil)
-            },
+        },
             nil
         )
         if aggregations[aggregate] == nil { self.aggregations[aggregate] = [:] }
         aggregations[aggregate]?[argc] = box
     }
-    
+
     fileprivate typealias Aggregate = @convention(block) (Int, OpaquePointer?, Int32, UnsafeMutablePointer<OpaquePointer?>?) -> Void
     fileprivate typealias Function = @convention(block) (OpaquePointer?, Int32, UnsafeMutablePointer<OpaquePointer?>?) -> Void
     fileprivate var functions = [String: [Int: Function]]()
diff --git a/Sources/SQLite/Typed/CustomFunctions.swift b/Sources/SQLite/Typed/CustomFunctions.swift
index 2389901f..375a6b99 100644
--- a/Sources/SQLite/Typed/CustomFunctions.swift
+++ b/Sources/SQLite/Typed/CustomFunctions.swift
@@ -133,4 +133,69 @@ public extension Connection {
         }
     }
 
+    // MARK: -
+    
+    public func createAggregation<T: AnyObject>(
+        _ aggregate: String,
+        argumentCount: UInt? = nil,
+        deterministic: Bool = false,
+        initialValue: T,
+        reduce: @escaping (T, [Binding?]) -> T,
+        result: @escaping (T) -> Binding?
+        ) {
+        
+        let step: ([Binding?], UnsafeMutablePointer<UnsafeMutableRawPointer>) -> () = { (bindings, ptr) in
+            let p = ptr.pointee.assumingMemoryBound(to: T.self)
+            let current = Unmanaged<T>.fromOpaque(p).takeRetainedValue()
+            let next = reduce(current, bindings)
+            ptr.pointee = Unmanaged.passRetained(next).toOpaque()
+        }
+        
+        let final: (UnsafeMutablePointer<UnsafeMutableRawPointer>) -> Binding? = { (ptr) in
+            let p = ptr.pointee.assumingMemoryBound(to: T.self)
+            let obj = Unmanaged<T>.fromOpaque(p).takeRetainedValue()
+            let value = result(obj)
+            ptr.deallocate()
+            return value
+        }
+        
+        let state: () -> UnsafeMutablePointer<UnsafeMutableRawPointer> = {
+            let p = UnsafeMutablePointer<UnsafeMutableRawPointer>.allocate(capacity: 1)
+            p.pointee = Unmanaged.passRetained(initialValue).toOpaque()
+            return p
+        }
+        
+        createAggregation(aggregate, step: step, final: final, state: state)
+    }
+    
+    public func createAggregation<T>(
+        _ aggregate: String,
+        argumentCount: UInt? = nil,
+        deterministic: Bool = false,
+        initialValue: T,
+        reduce: @escaping (T, [Binding?]) -> T,
+        result: @escaping (T) -> Binding?
+        ) {
+        
+        let step: ([Binding?], UnsafeMutablePointer<T>) -> () = { (bindings, p) in
+            let current = p.pointee
+            let next = reduce(current, bindings)
+            p.pointee = next
+        }
+        
+        let final: (UnsafeMutablePointer<T>) -> Binding? = { (p) in
+            let v = result(p.pointee)
+            p.deallocate()
+            return v
+        }
+        
+        let state: () -> UnsafeMutablePointer<T> = {
+            let p = UnsafeMutablePointer<T>.allocate(capacity: 1)
+            p.pointee = initialValue
+            return p
+        }
+        
+        createAggregation(aggregate, step: step, final: final, state: state)
+    }
+
 }
diff --git a/Tests/SQLiteTests/CustomAggregationTests.swift b/Tests/SQLiteTests/CustomAggregationTests.swift
index 56c3c011..1035f67c 100644
--- a/Tests/SQLiteTests/CustomAggregationTests.swift
+++ b/Tests/SQLiteTests/CustomAggregationTests.swift
@@ -22,25 +22,25 @@ class CustomAggregationTests : SQLiteTestCase {
         try! InsertUser("Eve", age: 28, admin: false)
     }
     
-    func testCustomSum() {
+    func testUnsafeCustomSum() {
         let step = { (bindings: [Binding?], state: UnsafeMutablePointer<Int64>) in
             if let v = bindings[0] as? Int64 {
                 state.pointee += v
             }
         }
-
+        
         let final = { (state: UnsafeMutablePointer<Int64>) -> Binding? in
             let v = state.pointee
             let p = UnsafeMutableBufferPointer(start: state, count: 1)
             p.deallocate()
             return v
         }
-        let _ = db.createAggregation("mySUM", step: step, final: final) {
+        let _ = db.createAggregation("mySUM1", step: step, final: final) {
             let v = UnsafeMutableBufferPointer<Int64>.allocate(capacity: 1)
             v[0] = 0
             return v.baseAddress!
         }
-        let result = try! db.prepare("SELECT mySUM(age) AS s FROM users")
+        let result = try! db.prepare("SELECT mySUM1(age) AS s FROM users")
         let i = result.columnNames.index(of: "s")!
         for row in result {
             let value = row[i] as? Int64
@@ -48,7 +48,7 @@ class CustomAggregationTests : SQLiteTestCase {
         }
     }
     
-    func testCustomSumGrouping() {
+    func testUnsafeCustomSumGrouping() {
         let step = { (bindings: [Binding?], state: UnsafeMutablePointer<Int64>) in
             if let v = bindings[0] as? Int64 {
                 state.pointee += v
@@ -60,14 +60,71 @@ class CustomAggregationTests : SQLiteTestCase {
             p.deallocate()
             return v
         }
-        let _ = db.createAggregation("mySUM", step: step, final: final) {
+        let _ = db.createAggregation("mySUM2", step: step, final: final) {
             let v = UnsafeMutableBufferPointer<Int64>.allocate(capacity: 1)
             v[0] = 0
             return v.baseAddress!
         }
-        let result = try! db.prepare("SELECT mySUM(age) AS s FROM users GROUP BY admin ORDER BY s")
+        let result = try! db.prepare("SELECT mySUM2(age) AS s FROM users GROUP BY admin ORDER BY s")
         let i = result.columnNames.index(of: "s")!
         let values = result.compactMap { $0[i] as? Int64 }
         XCTAssertTrue(values.elementsEqual([28, 55]))
     }
+    
+    func testCustomSum() {
+        let reduce : (Int64, [Binding?]) -> Int64 = { (last, bindings) in
+            let v = (bindings[0] as? Int64) ?? 0
+            return last + v
+        }
+        let _ = db.createAggregation("myReduceSUM1", initialValue: Int64(2000), reduce: reduce, result: { $0 })
+        let result = try! db.prepare("SELECT myReduceSUM1(age) AS s FROM users")
+        let i = result.columnNames.index(of: "s")!
+        for row in result {
+            let value = row[i] as? Int64
+            XCTAssertEqual(2083, value)
+        }
+    }
+
+    func testCustomSumGrouping() {
+        let reduce : (Int64, [Binding?]) -> Int64 = { (last, bindings) in
+            let v = (bindings[0] as? Int64) ?? 0
+            return last + v
+        }
+        let _ = db.createAggregation("myReduceSUM2", initialValue: Int64(3000), reduce: reduce, result: { $0 })
+        let result = try! db.prepare("SELECT myReduceSUM2(age) AS s FROM users GROUP BY admin ORDER BY s")
+        let i = result.columnNames.index(of: "s")!
+        let values = result.compactMap { $0[i] as? Int64 }
+        XCTAssertTrue(values.elementsEqual([3028, 3055]))
+    }
+    
+    func testCustomObjectSum() {
+        {
+            let initial = TestObject(value: 1000)
+            let reduce : (TestObject, [Binding?]) -> TestObject = { (last, bindings) in
+                let v = (bindings[0] as? Int64) ?? 0
+                return TestObject(value: last.value + v)
+            }
+            let _ = db.createAggregation("myReduceSUMX", initialValue: initial, reduce: reduce, result: { $0.value })
+            // end this scope to ensure that the initial value is retained
+            // by the createAggregation call.
+        }()
+        let result = try! db.prepare("SELECT myReduceSUMX(age) AS s FROM users")
+        let i = result.columnNames.index(of: "s")!
+        for row in result {
+            let value = row[i] as? Int64
+            XCTAssertEqual(1083, value)
+        }
+    }
+}
+
+/// This class is used to test that aggregation state variables
+/// can be reference types and are properly memory managed when
+/// crossing the Swift<->C boundary multiple times.
+class TestObject {
+    var value: Int64
+    init(value: Int64) {
+        self.value = value
+    }
+    deinit {
+    }
 }

From b1d3a4036deddac396b46d725866192eb29df0ff Mon Sep 17 00:00:00 2001
From: Gregory Todd Williams <greg@evilfunhouse.com>
Date: Fri, 22 Feb 2019 08:25:08 -0800
Subject: [PATCH 009/326] Add extra test for custom aggregation.

---
 Tests/SQLiteTests/CustomAggregationTests.swift | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/Tests/SQLiteTests/CustomAggregationTests.swift b/Tests/SQLiteTests/CustomAggregationTests.swift
index 1035f67c..74a19df0 100644
--- a/Tests/SQLiteTests/CustomAggregationTests.swift
+++ b/Tests/SQLiteTests/CustomAggregationTests.swift
@@ -97,6 +97,21 @@ class CustomAggregationTests : SQLiteTestCase {
         XCTAssertTrue(values.elementsEqual([3028, 3055]))
     }
     
+    func testCustomStringAgg() {
+        let initial = String(repeating: " ", count: 64)
+        let reduce : (String, [Binding?]) -> String = { (last, bindings) in
+            let v = (bindings[0] as? String) ?? ""
+            return last + v
+        }
+        let _ = db.createAggregation("myReduceSUM3", initialValue: initial, reduce: reduce, result: { $0 })
+        let result = try! db.prepare("SELECT myReduceSUM3(email) AS s FROM users")
+        let i = result.columnNames.index(of: "s")!
+        for row in result {
+            let value = row[i] as? String
+            XCTAssertEqual("\(initial)Alice@example.comBob@example.comEve@example.com", value)
+        }
+    }
+    
     func testCustomObjectSum() {
         {
             let initial = TestObject(value: 1000)

From 4a28df38c187a428561c12ee9d0a3e3099ba336b Mon Sep 17 00:00:00 2001
From: Gregory Todd Williams <greg@evilfunhouse.com>
Date: Fri, 22 Feb 2019 09:05:24 -0800
Subject: [PATCH 010/326] Track retains/releases that occur during custom
 aggregation testing.

---
 .../SQLiteTests/CustomAggregationTests.swift  | 22 ++++++++++++++-----
 1 file changed, 16 insertions(+), 6 deletions(-)

diff --git a/Tests/SQLiteTests/CustomAggregationTests.swift b/Tests/SQLiteTests/CustomAggregationTests.swift
index 74a19df0..f8efc7e4 100644
--- a/Tests/SQLiteTests/CustomAggregationTests.swift
+++ b/Tests/SQLiteTests/CustomAggregationTests.swift
@@ -122,13 +122,18 @@ class CustomAggregationTests : SQLiteTestCase {
             let _ = db.createAggregation("myReduceSUMX", initialValue: initial, reduce: reduce, result: { $0.value })
             // end this scope to ensure that the initial value is retained
             // by the createAggregation call.
+        }();
+        {
+            XCTAssertEqual(TestObject.inits, 1)
+            let result = try! db.prepare("SELECT myReduceSUMX(age) AS s FROM users")
+            let i = result.columnNames.index(of: "s")!
+            for row in result {
+                let value = row[i] as? Int64
+                XCTAssertEqual(1083, value)
+            }
         }()
-        let result = try! db.prepare("SELECT myReduceSUMX(age) AS s FROM users")
-        let i = result.columnNames.index(of: "s")!
-        for row in result {
-            let value = row[i] as? Int64
-            XCTAssertEqual(1083, value)
-        }
+        XCTAssertEqual(TestObject.inits, 4)
+        XCTAssertEqual(TestObject.deinits, 3) // the initial value is still retained by the aggregate's state block, so deinits is one less than inits
     }
 }
 
@@ -136,10 +141,15 @@ class CustomAggregationTests : SQLiteTestCase {
 /// can be reference types and are properly memory managed when
 /// crossing the Swift<->C boundary multiple times.
 class TestObject {
+    static var inits = 0
+    static var deinits = 0
+    
     var value: Int64
     init(value: Int64) {
         self.value = value
+        TestObject.inits += 1
     }
     deinit {
+        TestObject.deinits += 1
     }
 }

From 0fa531d1118bf88e83ffb893d89b8898bfdd85b8 Mon Sep 17 00:00:00 2001
From: Andrew Finnell <andy@losingfight.com>
Date: Sun, 24 Feb 2019 13:54:35 -0500
Subject: [PATCH 011/326] Implement Upsert [#482]

# Problem

Apps need the ability to insert or, if the row already exists, update the existing row. This is commonly called "upsert" and is [documented for SQLite here](https://www.sqlite.org/lang_UPSERT.html). The existing "replace" on conflict clause for `insert()` often does not have the desired behavior. It will cascade (with deletes, if specified) along foreign keys if the value exists and has to be replaced ([see Issue 842](https://github.com/stephencelis/SQLite.swift/issues/842)).

# Solution

Create an `upsert()` method for queries. The basic form allows for the fallback setters to be manually specified. A slightly more ergonomic version will compute the setters from the inserted values by removing the "conflicting" column, and then using the inserted values via the "excluded" qualifier. Additionally, an `Encodable` version of `upsert` is provided.
---
 Documentation/Index.md             | 28 +++++++++++++++++++++
 Sources/SQLite/Typed/Coding.swift  | 23 ++++++++++++++++++
 Sources/SQLite/Typed/Query.swift   | 39 ++++++++++++++++++++++++++++++
 Sources/SQLite/Typed/Setter.swift  |  5 ++++
 Tests/SQLiteTests/QueryTests.swift | 35 ++++++++++++++++++++++++++-
 5 files changed, 129 insertions(+), 1 deletion(-)

diff --git a/Documentation/Index.md b/Documentation/Index.md
index 628e7464..61c287d6 100644
--- a/Documentation/Index.md
+++ b/Documentation/Index.md
@@ -35,6 +35,7 @@
       - [Sorting Rows](#sorting-rows)
       - [Limiting and Paging Results](#limiting-and-paging-results)
       - [Aggregation](#aggregation)
+  - [Upserting Rows](#upserting-rows)
   - [Updating Rows](#updating-rows)
   - [Deleting Rows](#deleting-rows)
   - [Transactions and Savepoints](#transactions-and-savepoints)
@@ -1098,6 +1099,33 @@ let count = try db.scalar(users.filter(name != nil).count)
 > // SELECT count(DISTINCT "name") FROM "users"
 > ```
 
+## Upserting Rows
+
+We can upsert rows into a table by calling a [query’s](#queries) `upsert`
+function with a list of [setters](#setters)—typically [typed column
+expressions](#expressions) and values (which can also be expressions)—each
+joined by the `<-` operator. Upserting is like inserting, except if there is a 
+conflict on the specified column value, SQLite will perform an update on the row instead.
+
+```swift
+try db.run(users.upsert(email <- "alice@mac.com", name <- "Alice"), onConflictOf: email)
+// INSERT INTO "users" ("email", "name") VALUES ('alice@mac.com', 'Alice') ON CONFLICT (\"email\") DO UPDATE SET \"name\" = \"excluded\".\"name\"
+```
+
+The `upsert` function, when run successfully, returns an `Int64` representing
+the inserted row’s [`ROWID`][ROWID].
+
+```swift
+do {
+    let rowid = try db.run(users.upsert(email <- "alice@mac.com", name <- "Alice", onConflictOf: email))
+    print("inserted id: \(rowid)")
+} catch {
+    print("insertion failed: \(error)")
+}
+```
+
+The [`insert`](#inserting-rows), [`update`](#updating-rows), and [`delete`](#deleting-rows) functions
+follow similar patterns.
 
 ## Updating Rows
 
diff --git a/Sources/SQLite/Typed/Coding.swift b/Sources/SQLite/Typed/Coding.swift
index c3fb931b..6ed8c987 100644
--- a/Sources/SQLite/Typed/Coding.swift
+++ b/Sources/SQLite/Typed/Coding.swift
@@ -45,6 +45,29 @@ extension QueryType {
         return self.insert(encoder.setters + otherSetters)
     }
 
+
+    /// Creates an `INSERT ON CONFLICT DO UPDATE` statement, aka upsert, by encoding the given object
+    /// This method converts any custom nested types to JSON data and does not handle any sort
+    /// of object relationships. If you want to support relationships between objects you will
+    /// have to provide your own Encodable implementations that encode the correct ids.
+    ///
+    /// - Parameters:
+    ///
+    ///   - encodable: An encodable object to insert
+    ///
+    ///   - userInfo: User info to be passed to encoder
+    ///
+    ///   - otherSetters: Any other setters to include in the insert
+    ///
+    ///   - onConflictOf: The column that if conflicts should trigger an update instead of insert.
+    ///
+    /// - Returns: An `INSERT` statement fort the encodable object
+    public func upsert(_ encodable: Encodable, userInfo: [CodingUserInfoKey:Any] = [:], otherSetters: [Setter] = [], onConflictOf conflicting: Expressible) throws -> Insert {
+        let encoder = SQLiteEncoder(userInfo: userInfo)
+        try encodable.encode(to: encoder)
+        return self.upsert(encoder.setters + otherSetters, onConflictOf: conflicting)
+    }
+
     /// Creates an `UPDATE` statement by encoding the given object
     /// This method converts any custom nested types to JSON data and does not handle any sort
     /// of object relationships. If you want to support relationships between objects you will
diff --git a/Sources/SQLite/Typed/Query.swift b/Sources/SQLite/Typed/Query.swift
index f6ef6df8..8793acae 100644
--- a/Sources/SQLite/Typed/Query.swift
+++ b/Sources/SQLite/Typed/Query.swift
@@ -672,6 +672,45 @@ extension QueryType {
             query.expression
         ]).expression)
     }
+    
+    // MARK: UPSERT
+    
+    public func upsert(_ insertValues: Setter..., onConflictOf conflicting: Expressible) -> Insert {
+        return upsert(insertValues, onConflictOf: conflicting)
+    }
+    
+    public func upsert(_ insertValues: [Setter], onConflictOf conflicting: Expressible) -> Insert {
+        let setValues = insertValues.filter { $0.column.asSQL() != conflicting.asSQL() }
+            .map { Setter(excluded: $0.column) }
+        return upsert(insertValues, onConflictOf: conflicting, set: setValues)
+    }
+    
+    public func upsert(_ insertValues: Setter..., onConflictOf conflicting: Expressible, set setValues: [Setter]) -> Insert {
+        return upsert(insertValues, onConflictOf: conflicting, set: setValues)
+    }
+    
+    public func upsert(_ insertValues: [Setter], onConflictOf conflicting: Expressible, set setValues: [Setter]) -> Insert {
+        let insert = insertValues.reduce((columns: [Expressible](), values: [Expressible]())) { insert, setter in
+            (insert.columns + [setter.column], insert.values + [setter.value])
+        }
+        
+        let clauses: [Expressible?] = [
+            Expression<Void>(literal: "INSERT"),
+            Expression<Void>(literal: "INTO"),
+            tableName(),
+            "".wrap(insert.columns) as Expression<Void>,
+            Expression<Void>(literal: "VALUES"),
+            "".wrap(insert.values) as Expression<Void>,
+            whereClause,
+            Expression<Void>(literal: "ON CONFLICT"),
+            "".wrap(conflicting) as Expression<Void>,
+            Expression<Void>(literal: "DO UPDATE SET"),
+            ", ".join(setValues.map { $0.expression })
+        ]
+        
+        return Insert(" ".join(clauses.compactMap { $0 }).expression)
+    }
+
 
     // MARK: UPDATE
 
diff --git a/Sources/SQLite/Typed/Setter.swift b/Sources/SQLite/Typed/Setter.swift
index 86f16fca..3d3170f6 100644
--- a/Sources/SQLite/Typed/Setter.swift
+++ b/Sources/SQLite/Typed/Setter.swift
@@ -60,6 +60,11 @@ public struct Setter {
         self.value = Expression<V?>(value: value)
     }
 
+    init(excluded column: Expressible) {
+        let excluded = Expression<Void>("excluded")
+        self.column = column
+        self.value = ".".join([excluded, column.expression])
+    }
 }
 
 extension Setter : Expressible {
diff --git a/Tests/SQLiteTests/QueryTests.swift b/Tests/SQLiteTests/QueryTests.swift
index 2a9e4ecb..694e7f26 100644
--- a/Tests/SQLiteTests/QueryTests.swift
+++ b/Tests/SQLiteTests/QueryTests.swift
@@ -270,6 +270,24 @@ class QueryTests : XCTestCase {
         )
     }
 
+    func test_upsert_withOnConflict_compilesInsertOrOnConflictExpression() {
+        AssertSQL(
+            "INSERT INTO \"users\" (\"email\", \"age\") VALUES ('alice@example.com', 30) ON CONFLICT (\"email\") DO UPDATE SET \"age\" = \"excluded\".\"age\"",
+            users.upsert(email <- "alice@example.com", age <- 30, onConflictOf: email)
+        )
+    }
+
+    func test_upsert_encodable() throws {
+        let emails = Table("emails")
+        let string = Expression<String>("string")
+        let value = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4, optional: nil, sub: nil)
+        let insert = try emails.upsert(value, onConflictOf: string)
+        AssertSQL(
+            "INSERT INTO \"emails\" (\"int\", \"string\", \"bool\", \"float\", \"double\") VALUES (1, '2', 1, 3.0, 4.0) ON CONFLICT (\"string\") DO UPDATE SET \"int\" = \"excluded\".\"int\", \"bool\" = \"excluded\".\"bool\", \"float\" = \"excluded\".\"float\", \"double\" = \"excluded\".\"double\"",
+            insert
+        )
+    }
+
     func test_update_compilesUpdateExpression() {
         AssertSQL(
             "UPDATE \"users\" SET \"age\" = 30, \"admin\" = 1 WHERE (\"id\" = 1)",
@@ -378,7 +396,8 @@ class QueryIntegrationTests : SQLiteTestCase {
 
     let id = Expression<Int64>("id")
     let email = Expression<String>("email")
-
+    let age = Expression<Int>("age")
+    
     override func setUp() {
         super.setUp()
 
@@ -483,6 +502,20 @@ class QueryIntegrationTests : SQLiteTestCase {
         XCTAssertEqual(1, id)
     }
 
+    func test_upsert() throws {
+        let fetchAge = { () throws -> Int? in
+            return try self.db.pluck(self.users.filter(self.email == "alice@example.com")).flatMap { $0[self.age] }
+        }
+        
+        let id = try db.run(users.upsert(email <- "alice@example.com", age <- 30, onConflictOf: email))
+        XCTAssertEqual(1, id)
+        XCTAssertEqual(30, try fetchAge())
+        
+        let nextId = try db.run(users.upsert(email <- "alice@example.com", age <- 42, onConflictOf: email))
+        XCTAssertEqual(1, nextId)
+        XCTAssertEqual(42, try fetchAge())
+    }
+    
     func test_update() {
         let changes = try! db.run(users.update(email <- "alice@example.com"))
         XCTAssertEqual(0, changes)

From 78089e2bdebbf1f634eaf85876d719981ed3c147 Mon Sep 17 00:00:00 2001
From: Stephan Heilner <stephanheilner@gmail.com>
Date: Thu, 31 May 2018 15:14:02 -0600
Subject: [PATCH 012/326] Fixed bug not allowing columns from multiple tables
 in the .select

---
 SQLite.xcodeproj/project.pbxproj    |  8 +++++
 Sources/SQLite/Typed/Query.swift    |  1 -
 Tests/SQLiteTests/SelectTests.swift | 45 +++++++++++++++++++++++++++++
 3 files changed, 53 insertions(+), 1 deletion(-)
 create mode 100644 Tests/SQLiteTests/SelectTests.swift

diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj
index 2eb11fd9..9143d1b2 100644
--- a/SQLite.xcodeproj/project.pbxproj
+++ b/SQLite.xcodeproj/project.pbxproj
@@ -108,6 +108,9 @@
 		49EB68C51F7B3CB400D89D40 /* Coding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49EB68C31F7B3CB400D89D40 /* Coding.swift */; };
 		49EB68C61F7B3CB400D89D40 /* Coding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49EB68C31F7B3CB400D89D40 /* Coding.swift */; };
 		49EB68C71F7B3CB400D89D40 /* Coding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49EB68C31F7B3CB400D89D40 /* Coding.swift */; };
+		D4DB368C20C09CFB00D5A58E /* SelectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4DB368A20C09C9B00D5A58E /* SelectTests.swift */; };
+		D4DB368D20C09CFC00D5A58E /* SelectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4DB368A20C09C9B00D5A58E /* SelectTests.swift */; };
+		D4DB368E20C09CFD00D5A58E /* SelectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4DB368A20C09C9B00D5A58E /* SelectTests.swift */; };
 		EE247AD71C3F04ED00AE3E12 /* SQLite.h in Headers */ = {isa = PBXBuildFile; fileRef = EE247AD61C3F04ED00AE3E12 /* SQLite.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		EE247ADE1C3F04ED00AE3E12 /* SQLite.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EE247AD31C3F04ED00AE3E12 /* SQLite.framework */; };
 		EE247B031C3F06E900AE3E12 /* Blob.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AEE1C3F06E900AE3E12 /* Blob.swift */; };
@@ -228,6 +231,7 @@
 		3D67B3E51DB2469200A4F4C6 /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = Platforms/WatchOS.platform/Developer/SDKs/WatchOS3.0.sdk/usr/lib/libsqlite3.tbd; sourceTree = DEVELOPER_DIR; };
 		49EB68C31F7B3CB400D89D40 /* Coding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Coding.swift; sourceTree = "<group>"; };
 		A121AC451CA35C79005A31D1 /* SQLite.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SQLite.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+		D4DB368A20C09C9B00D5A58E /* SelectTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectTests.swift; sourceTree = "<group>"; };
 		EE247AD31C3F04ED00AE3E12 /* SQLite.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SQLite.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		EE247AD61C3F04ED00AE3E12 /* SQLite.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SQLite.h; sourceTree = "<group>"; };
 		EE247AD81C3F04ED00AE3E12 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@@ -418,6 +422,7 @@
 				19A17B93B48B5560E6E51791 /* Fixtures.swift */,
 				19A175C1F9CB3BBAB8FCEC7B /* RowTests.swift */,
 				19A1729B75C33F9A0B9A89C1 /* DateAndTimeFunctionTests.swift */,
+				D4DB368A20C09C9B00D5A58E /* SelectTests.swift */,
 			);
 			name = SQLiteTests;
 			path = Tests/SQLiteTests;
@@ -847,6 +852,7 @@
 				19A17F60B685636D1F83C2DD /* Fixtures.swift in Sources */,
 				19A1785195182AF8731A8BDA /* RowTests.swift in Sources */,
 				19A1769C1F3A7542BECF50FF /* DateAndTimeFunctionTests.swift in Sources */,
+				D4DB368E20C09CFD00D5A58E /* SelectTests.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -935,6 +941,7 @@
 				19A17408007B182F884E3A53 /* Fixtures.swift in Sources */,
 				19A1720B67ED13E6150C6A3D /* RowTests.swift in Sources */,
 				19A17C80076860CF7751A056 /* DateAndTimeFunctionTests.swift in Sources */,
+				D4DB368C20C09CFB00D5A58E /* SelectTests.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -993,6 +1000,7 @@
 				19A1709C3E7A406E62293B2A /* Fixtures.swift in Sources */,
 				19A171967CC511C4F6F773C9 /* RowTests.swift in Sources */,
 				19A172EB202970561E5C4245 /* DateAndTimeFunctionTests.swift in Sources */,
+				D4DB368D20C09CFC00D5A58E /* SelectTests.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
diff --git a/Sources/SQLite/Typed/Query.swift b/Sources/SQLite/Typed/Query.swift
index f6ef6df8..7ec5e581 100644
--- a/Sources/SQLite/Typed/Query.swift
+++ b/Sources/SQLite/Typed/Query.swift
@@ -963,7 +963,6 @@ extension Connection {
                             try expandGlob(true)(q)
                             continue column
                         }
-                        throw QueryError.noSuchTable(name: namespace)
                     }
                     throw QueryError.noSuchTable(name: namespace)
                 }
diff --git a/Tests/SQLiteTests/SelectTests.swift b/Tests/SQLiteTests/SelectTests.swift
new file mode 100644
index 00000000..bca01092
--- /dev/null
+++ b/Tests/SQLiteTests/SelectTests.swift
@@ -0,0 +1,45 @@
+import XCTest
+@testable import SQLite
+
+class SelectTests: SQLiteTestCase {
+    
+    override func setUp() {
+        super.setUp()
+        CreateUsersTable()
+        CreateUsersDataTable()
+    }
+    
+    func CreateUsersDataTable() {
+        try! db.execute("""
+            CREATE TABLE users_name (
+                id INTEGER,
+                user_id INTEGER REFERENCES users(id),
+                name TEXT
+            )
+            """
+        )
+    }
+    
+    func test_select_columns_from_multiple_tables() {
+        let usersData = Table("users_name")
+        let users = Table("users")
+        
+        let name = Expression<String>("name")
+        let id = Expression<Int64>("id")
+        let userID = Expression<Int64>("user_id")
+        let email = Expression<String>("email")
+        
+        try! InsertUser("Joey")
+        try! db.run(usersData.insert(
+            id <- 1,
+            userID <- 1,
+            name <- "Joey"
+        ))
+        
+        try! db.prepare(users.select(name, email).join(usersData, on: userID == users[id])).forEach {
+            XCTAssertEqual($0[name], "Joey")
+            XCTAssertEqual($0[email], "Joey@example.com")
+        }
+    }
+    
+}

From 026c5c264f925c0796d1cb72ee4927019c1dec4e Mon Sep 17 00:00:00 2001
From: Krzysztof <krzysztof.kapitan@gmail.com>
Date: Fri, 26 Apr 2019 22:27:16 +0200
Subject: [PATCH 013/326] Introduce support for backup

---
 SQLite.xcodeproj/project.pbxproj    |  18 +++
 Sources/SQLite/Core/Backup.swift    | 176 ++++++++++++++++++++++++++++
 Tests/SQLiteTests/BackupTests.swift |  36 ++++++
 3 files changed, 230 insertions(+)
 create mode 100644 Sources/SQLite/Core/Backup.swift
 create mode 100644 Tests/SQLiteTests/BackupTests.swift

diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj
index 9d38dbc4..aec22743 100644
--- a/SQLite.xcodeproj/project.pbxproj
+++ b/SQLite.xcodeproj/project.pbxproj
@@ -7,6 +7,13 @@
 	objects = {
 
 /* Begin PBXBuildFile section */
+		02A43A9822738CF100FEC494 /* Backup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02A43A9722738CF100FEC494 /* Backup.swift */; };
+		02A43A9922738CF100FEC494 /* Backup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02A43A9722738CF100FEC494 /* Backup.swift */; };
+		02A43A9A22738CF100FEC494 /* Backup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02A43A9722738CF100FEC494 /* Backup.swift */; };
+		02A43A9B22738CF100FEC494 /* Backup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02A43A9722738CF100FEC494 /* Backup.swift */; };
+		02A43A9D22738E2900FEC494 /* BackupTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02A43A9C22738E2900FEC494 /* BackupTests.swift */; };
+		02A43A9E22738E2900FEC494 /* BackupTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02A43A9C22738E2900FEC494 /* BackupTests.swift */; };
+		02A43A9F22738E2900FEC494 /* BackupTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02A43A9C22738E2900FEC494 /* BackupTests.swift */; };
 		03A65E641C6BB0F60062603F /* SQLite.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 03A65E5A1C6BB0F50062603F /* SQLite.framework */; };
 		03A65E721C6BB2D30062603F /* SQLite.h in Headers */ = {isa = PBXBuildFile; fileRef = EE247AD61C3F04ED00AE3E12 /* SQLite.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		03A65E731C6BB2D80062603F /* Foundation.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AF71C3F06E900AE3E12 /* Foundation.swift */; };
@@ -211,6 +218,8 @@
 /* End PBXContainerItemProxy section */
 
 /* Begin PBXFileReference section */
+		02A43A9722738CF100FEC494 /* Backup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Backup.swift; sourceTree = "<group>"; };
+		02A43A9C22738E2900FEC494 /* BackupTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackupTests.swift; sourceTree = "<group>"; };
 		03A65E5A1C6BB0F50062603F /* SQLite.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SQLite.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		03A65E631C6BB0F60062603F /* SQLiteTests tvOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "SQLiteTests tvOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
 		03A65E961C6BB3210062603F /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS.sdk/usr/lib/libsqlite3.tbd; sourceTree = DEVELOPER_DIR; };
@@ -398,6 +407,7 @@
 				19A17E2695737FAB5D6086E3 /* fixtures */,
 				EE247B1A1C3F137700AE3E12 /* AggregateFunctionsTests.swift */,
 				EE247B1B1C3F137700AE3E12 /* BlobTests.swift */,
+				02A43A9C22738E2900FEC494 /* BackupTests.swift */,
 				EE247B1D1C3F137700AE3E12 /* ConnectionTests.swift */,
 				EE247B1E1C3F137700AE3E12 /* CoreFunctionsTests.swift */,
 				EE247B1F1C3F137700AE3E12 /* CustomFunctionsTests.swift */,
@@ -434,6 +444,7 @@
 				EE247AF21C3F06E900AE3E12 /* Statement.swift */,
 				EE247AF31C3F06E900AE3E12 /* Value.swift */,
 				19A1710E73A46D5AC721CDA9 /* Errors.swift */,
+				02A43A9722738CF100FEC494 /* Backup.swift */,
 			);
 			path = Core;
 			sourceTree = "<group>";
@@ -817,6 +828,7 @@
 				03A65E7E1C6BB2FB0062603F /* AggregateFunctions.swift in Sources */,
 				19A17EC0D68BA8C03288ADF7 /* FTS5.swift in Sources */,
 				19A179E76EA6207669B60C1B /* Cipher.swift in Sources */,
+				02A43A9A22738CF100FEC494 /* Backup.swift in Sources */,
 				19A17FF4A10B44D3937C8CAC /* Errors.swift in Sources */,
 				19A1737286A74F3CF7412906 /* DateAndTimeFunctions.swift in Sources */,
 			);
@@ -839,6 +851,7 @@
 				03A65E911C6BB3030062603F /* SchemaTests.swift in Sources */,
 				03A65E8D1C6BB3030062603F /* FTS4Tests.swift in Sources */,
 				03A65E8C1C6BB3030062603F /* ExpressionTests.swift in Sources */,
+				02A43A9F22738E2900FEC494 /* BackupTests.swift in Sources */,
 				03A65E8E1C6BB3030062603F /* OperatorsTests.swift in Sources */,
 				03A65E951C6BB3030062603F /* TestHelpers.swift in Sources */,
 				19A17254FBA7894891F7297B /* FTS5Tests.swift in Sources */,
@@ -875,6 +888,7 @@
 				3D67B3E71DB246BA00A4F4C6 /* Blob.swift in Sources */,
 				3D67B3E81DB246BA00A4F4C6 /* Connection.swift in Sources */,
 				19A179CCF9671E345E5A9811 /* Cipher.swift in Sources */,
+				02A43A9B22738CF100FEC494 /* Backup.swift in Sources */,
 				19A17DC282E36C4F41AA440B /* Errors.swift in Sources */,
 				19A173668D948AD4DF1F5352 /* DateAndTimeFunctions.swift in Sources */,
 			);
@@ -905,6 +919,7 @@
 				EE247B0D1C3F06E900AE3E12 /* AggregateFunctions.swift in Sources */,
 				19A1717B10CC941ACB5533D6 /* FTS5.swift in Sources */,
 				19A171F12AB8B07F2FD7201A /* Cipher.swift in Sources */,
+				02A43A9822738CF100FEC494 /* Backup.swift in Sources */,
 				19A1792C0520D4E83C2EB075 /* Errors.swift in Sources */,
 				19A17E29278A12BC4F542506 /* DateAndTimeFunctions.swift in Sources */,
 			);
@@ -927,6 +942,7 @@
 				EE247B351C3F142E00AE3E12 /* ValueTests.swift in Sources */,
 				EE247B2F1C3F141E00AE3E12 /* QueryTests.swift in Sources */,
 				EE247B221C3F137700AE3E12 /* AggregateFunctionsTests.swift in Sources */,
+				02A43A9D22738E2900FEC494 /* BackupTests.swift in Sources */,
 				EE247B2E1C3F141E00AE3E12 /* OperatorsTests.swift in Sources */,
 				EE247B251C3F137700AE3E12 /* ConnectionTests.swift in Sources */,
 				19A171E6FA242F72A308C594 /* FTS5Tests.swift in Sources */,
@@ -963,6 +979,7 @@
 				EE247B6D1C3F3FEC00AE3E12 /* AggregateFunctions.swift in Sources */,
 				19A1750CEE9B05267995CF3D /* FTS5.swift in Sources */,
 				19A17835FD5886FDC5A3228F /* Cipher.swift in Sources */,
+				02A43A9922738CF100FEC494 /* Backup.swift in Sources */,
 				19A17490543609FCED53CACC /* Errors.swift in Sources */,
 				19A17152E32A9585831E3FE0 /* DateAndTimeFunctions.swift in Sources */,
 			);
@@ -985,6 +1002,7 @@
 				EE247B551C3F3FC700AE3E12 /* ConnectionTests.swift in Sources */,
 				EE247B611C3F3FC700AE3E12 /* TestHelpers.swift in Sources */,
 				EE247B581C3F3FC700AE3E12 /* ExpressionTests.swift in Sources */,
+				02A43A9E22738E2900FEC494 /* BackupTests.swift in Sources */,
 				EE247B5E1C3F3FC700AE3E12 /* SetterTests.swift in Sources */,
 				EE247B5B1C3F3FC700AE3E12 /* QueryTests.swift in Sources */,
 				19A174D78559CD30679BCCCB /* FTS5Tests.swift in Sources */,
diff --git a/Sources/SQLite/Core/Backup.swift b/Sources/SQLite/Core/Backup.swift
new file mode 100644
index 00000000..d001ae7d
--- /dev/null
+++ b/Sources/SQLite/Core/Backup.swift
@@ -0,0 +1,176 @@
+//
+// SQLite.swift
+// https://github.com/stephencelis/SQLite.swift
+// Copyright © 2014-2015 Stephen Celis.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+import Foundation
+import Dispatch
+#if SQLITE_SWIFT_STANDALONE
+import sqlite3
+#elseif SQLITE_SWIFT_SQLCIPHER
+import SQLCipher
+#elseif os(Linux)
+import CSQLite
+#else
+import SQLite3
+#endif
+
+/// An object representing database backup.
+///
+/// See: <https://www.sqlite.org/backup.html>
+public final class Backup {
+    
+    /// The name of the database to backup
+    public enum DatabaseName {
+        
+        /// The main database
+        case main
+        
+        /// The temporary database
+        case temp
+        
+        /// A database added to the connection with ATTACH statement
+        case attached(name: String)
+        
+        var name: String {
+            switch self {
+            case .main:
+                return "main"
+            case .temp:
+                return "temp"
+            case .attached(let name):
+                return name
+            }
+        }
+    }
+    
+    /// Number of pages to copy while performing a backup step
+    public enum Pages {
+        
+        /// Indicates all remaining pages should be copied
+        case all
+        
+        /// Indicates the maximal number of pages to be copied in single step
+        case limited(number: Int32)
+        
+        var number: Int32 {
+            switch self {
+            case .all:
+                return -1
+            case .limited(let number):
+                return number
+            }
+        }
+    }
+    
+    /// Total number of pages to copy
+    ///
+    /// See: <https://www.sqlite.org/c3ref/backup_finish.html#sqlite3backuppagecount>
+    public var pageCount: Int32 {
+        return handle.map { sqlite3_backup_pagecount($0) } ?? 0
+    }
+    
+    /// Number of remaining pages to copy.
+    ///
+    /// See: <https://www.sqlite.org/c3ref/backup_finish.html#sqlite3backupremaining>
+    public var remainingPages: Int32 {
+        return handle.map { sqlite3_backup_remaining($0) } ?? 0
+    }
+    
+    private let targetConnection: Connection
+    private let sourceConnection: Connection
+    
+    private var handle: OpaquePointer?
+    
+    /// Initializes a new SQLite backup.
+    ///
+    /// - Parameters:
+    ///
+    ///   - targetConnection: The connection to the database to save backup into.
+    ///
+    ///   - targetConnection: The name of the database to save backup into.
+    ///
+    ///     Default: `.main`.
+    ///
+    ///   - sourceConnection: The connection to the database to backup.
+    ///
+    ///   - targetConnection: The name of the database to backup.
+    /// 
+    ///     Default: `.main`.
+    ///
+    /// - Returns: A new database backup.
+    ///
+    /// See: <https://www.sqlite.org/c3ref/backup_finish.html#sqlite3backupinit>
+    public init(targetConnection: Connection,
+                targetName: DatabaseName = .main,
+                sourceConnection: Connection,
+                sourceName: DatabaseName = .main) throws {
+        
+        self.targetConnection = targetConnection
+        self.sourceConnection = sourceConnection
+        
+        self.handle = sqlite3_backup_init(targetConnection.handle,
+                                          targetName.name.withCString { $0 },
+                                          sourceConnection.handle,
+                                          sourceName.name.withCString { $0 })
+        
+        if self.handle == nil, let error = Result(errorCode: sqlite3_errcode(targetConnection.handle), connection: targetConnection) {
+            throw error
+        }
+    }
+    
+    /// Performs a backup step.
+    ///
+    /// - Parameter pagesToCopy: The maximal number of pages to copy in one step
+    ///
+    /// - Throws: `Result.Error` if step fails.
+    //
+    /// See: <https://www.sqlite.org/c3ref/backup_finish.html#sqlite3backupstep>
+    public func step(pagesToCopy pages: Pages = .all) throws {
+        let status = sqlite3_backup_step(handle, pages.number)
+        
+        guard status != SQLITE_DONE else {
+            finish()
+            return
+        }
+        
+        if let error = Result(errorCode: status, connection: targetConnection) {
+            throw error
+        }
+    }
+    
+    /// Finalizes backup.
+    ///
+    /// See: <https://www.sqlite.org/c3ref/backup_finish.html#sqlite3backupfinish>
+    public func finish() {
+        guard let handle = self.handle else {
+            return
+        }
+        
+        sqlite3_backup_finish(handle)
+        self.handle = nil
+    }
+    
+    deinit {
+        finish()
+    }
+}
diff --git a/Tests/SQLiteTests/BackupTests.swift b/Tests/SQLiteTests/BackupTests.swift
new file mode 100644
index 00000000..284ba610
--- /dev/null
+++ b/Tests/SQLiteTests/BackupTests.swift
@@ -0,0 +1,36 @@
+import XCTest
+import Foundation
+import Dispatch
+@testable import SQLite
+
+#if SQLITE_SWIFT_STANDALONE
+import sqlite3
+#elseif SQLITE_SWIFT_SQLCIPHER
+import SQLCipher
+#elseif os(Linux)
+import CSQLite
+#else
+import SQLite3
+#endif
+
+class BackupTests : SQLiteTestCase {
+    override func setUp() {
+        super.setUp()
+        
+        CreateUsersTable()
+    }
+    
+    func test_backup_copies_database() throws {
+        let source = db!
+        let target = try Connection()
+
+        try InsertUsers("alice", "betsy")
+        
+        let backup = try Backup(targetConnection: target, sourceConnection: source)
+        try backup.step()
+        
+        let users = try target.prepare("SELECT email FROM users ORDER BY email")
+        XCTAssertEqual(users.map { $0[0] as? String }, ["alice@example.com", "betsy@example.com"])
+    }
+}
+

From d10c9da458377c245a59f8a817196b1ca221414a Mon Sep 17 00:00:00 2001
From: Krzysztof <krzysztof.kapitan@gmail.com>
Date: Sat, 27 Apr 2019 00:21:17 +0200
Subject: [PATCH 014/326] Pass swift strings instead of UnsafePointers while
 initializing backup

---
 Sources/SQLite/Core/Backup.swift | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Sources/SQLite/Core/Backup.swift b/Sources/SQLite/Core/Backup.swift
index d001ae7d..e4fc6204 100644
--- a/Sources/SQLite/Core/Backup.swift
+++ b/Sources/SQLite/Core/Backup.swift
@@ -129,9 +129,9 @@ public final class Backup {
         self.sourceConnection = sourceConnection
         
         self.handle = sqlite3_backup_init(targetConnection.handle,
-                                          targetName.name.withCString { $0 },
+                                          targetName.name,
                                           sourceConnection.handle,
-                                          sourceName.name.withCString { $0 })
+                                          sourceName.name)
         
         if self.handle == nil, let error = Result(errorCode: sqlite3_errcode(targetConnection.handle), connection: targetConnection) {
             throw error

From 69ca8fed4574a8fac8f3074b4ca025ed5afdeac5 Mon Sep 17 00:00:00 2001
From: Krzysztof <krzysztof.kapitan@gmail.com>
Date: Mon, 29 Apr 2019 18:07:45 +0200
Subject: [PATCH 015/326] More convenient API for backup

---
 SQLite.xcodeproj/project.pbxproj        |  8 ------
 Sources/SQLite/Core/Backup.swift        |  4 +--
 Sources/SQLite/Core/Connection.swift    | 29 +++++++++++++++++++-
 Tests/SQLiteTests/BackupTests.swift     | 36 -------------------------
 Tests/SQLiteTests/ConnectionTests.swift | 12 +++++++++
 5 files changed, 42 insertions(+), 47 deletions(-)
 delete mode 100644 Tests/SQLiteTests/BackupTests.swift

diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj
index aec22743..b0bf35e7 100644
--- a/SQLite.xcodeproj/project.pbxproj
+++ b/SQLite.xcodeproj/project.pbxproj
@@ -11,9 +11,6 @@
 		02A43A9922738CF100FEC494 /* Backup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02A43A9722738CF100FEC494 /* Backup.swift */; };
 		02A43A9A22738CF100FEC494 /* Backup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02A43A9722738CF100FEC494 /* Backup.swift */; };
 		02A43A9B22738CF100FEC494 /* Backup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02A43A9722738CF100FEC494 /* Backup.swift */; };
-		02A43A9D22738E2900FEC494 /* BackupTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02A43A9C22738E2900FEC494 /* BackupTests.swift */; };
-		02A43A9E22738E2900FEC494 /* BackupTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02A43A9C22738E2900FEC494 /* BackupTests.swift */; };
-		02A43A9F22738E2900FEC494 /* BackupTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02A43A9C22738E2900FEC494 /* BackupTests.swift */; };
 		03A65E641C6BB0F60062603F /* SQLite.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 03A65E5A1C6BB0F50062603F /* SQLite.framework */; };
 		03A65E721C6BB2D30062603F /* SQLite.h in Headers */ = {isa = PBXBuildFile; fileRef = EE247AD61C3F04ED00AE3E12 /* SQLite.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		03A65E731C6BB2D80062603F /* Foundation.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AF71C3F06E900AE3E12 /* Foundation.swift */; };
@@ -219,7 +216,6 @@
 
 /* Begin PBXFileReference section */
 		02A43A9722738CF100FEC494 /* Backup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Backup.swift; sourceTree = "<group>"; };
-		02A43A9C22738E2900FEC494 /* BackupTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackupTests.swift; sourceTree = "<group>"; };
 		03A65E5A1C6BB0F50062603F /* SQLite.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SQLite.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		03A65E631C6BB0F60062603F /* SQLiteTests tvOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "SQLiteTests tvOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
 		03A65E961C6BB3210062603F /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS.sdk/usr/lib/libsqlite3.tbd; sourceTree = DEVELOPER_DIR; };
@@ -407,7 +403,6 @@
 				19A17E2695737FAB5D6086E3 /* fixtures */,
 				EE247B1A1C3F137700AE3E12 /* AggregateFunctionsTests.swift */,
 				EE247B1B1C3F137700AE3E12 /* BlobTests.swift */,
-				02A43A9C22738E2900FEC494 /* BackupTests.swift */,
 				EE247B1D1C3F137700AE3E12 /* ConnectionTests.swift */,
 				EE247B1E1C3F137700AE3E12 /* CoreFunctionsTests.swift */,
 				EE247B1F1C3F137700AE3E12 /* CustomFunctionsTests.swift */,
@@ -851,7 +846,6 @@
 				03A65E911C6BB3030062603F /* SchemaTests.swift in Sources */,
 				03A65E8D1C6BB3030062603F /* FTS4Tests.swift in Sources */,
 				03A65E8C1C6BB3030062603F /* ExpressionTests.swift in Sources */,
-				02A43A9F22738E2900FEC494 /* BackupTests.swift in Sources */,
 				03A65E8E1C6BB3030062603F /* OperatorsTests.swift in Sources */,
 				03A65E951C6BB3030062603F /* TestHelpers.swift in Sources */,
 				19A17254FBA7894891F7297B /* FTS5Tests.swift in Sources */,
@@ -942,7 +936,6 @@
 				EE247B351C3F142E00AE3E12 /* ValueTests.swift in Sources */,
 				EE247B2F1C3F141E00AE3E12 /* QueryTests.swift in Sources */,
 				EE247B221C3F137700AE3E12 /* AggregateFunctionsTests.swift in Sources */,
-				02A43A9D22738E2900FEC494 /* BackupTests.swift in Sources */,
 				EE247B2E1C3F141E00AE3E12 /* OperatorsTests.swift in Sources */,
 				EE247B251C3F137700AE3E12 /* ConnectionTests.swift in Sources */,
 				19A171E6FA242F72A308C594 /* FTS5Tests.swift in Sources */,
@@ -1002,7 +995,6 @@
 				EE247B551C3F3FC700AE3E12 /* ConnectionTests.swift in Sources */,
 				EE247B611C3F3FC700AE3E12 /* TestHelpers.swift in Sources */,
 				EE247B581C3F3FC700AE3E12 /* ExpressionTests.swift in Sources */,
-				02A43A9E22738E2900FEC494 /* BackupTests.swift in Sources */,
 				EE247B5E1C3F3FC700AE3E12 /* SetterTests.swift in Sources */,
 				EE247B5B1C3F3FC700AE3E12 /* QueryTests.swift in Sources */,
 				19A174D78559CD30679BCCCB /* FTS5Tests.swift in Sources */,
diff --git a/Sources/SQLite/Core/Backup.swift b/Sources/SQLite/Core/Backup.swift
index e4fc6204..1ea9637d 100644
--- a/Sources/SQLite/Core/Backup.swift
+++ b/Sources/SQLite/Core/Backup.swift
@@ -107,13 +107,13 @@ public final class Backup {
     ///
     ///   - targetConnection: The connection to the database to save backup into.
     ///
-    ///   - targetConnection: The name of the database to save backup into.
+    ///   - targetName: The name of the database to save backup into.
     ///
     ///     Default: `.main`.
     ///
     ///   - sourceConnection: The connection to the database to backup.
     ///
-    ///   - targetConnection: The name of the database to backup.
+    ///   - sourceName: The name of the database to backup.
     /// 
     ///     Default: `.main`.
     ///
diff --git a/Sources/SQLite/Core/Connection.swift b/Sources/SQLite/Core/Connection.swift
index 1bbf7f73..c23076af 100644
--- a/Sources/SQLite/Core/Connection.swift
+++ b/Sources/SQLite/Core/Connection.swift
@@ -627,7 +627,34 @@ public final class Connection {
     }
     fileprivate typealias Collation = @convention(block) (UnsafeRawPointer, UnsafeRawPointer) -> Int32
     fileprivate var collations = [String: Collation]()
-
+    
+    // MARK: - Backup
+    
+    /// Prepares a new backup for current connection.
+    ///
+    /// - Parameters:
+    ///
+    ///   - databaseName: The name of the database to backup.
+    ///
+    ///     Default: `.main`
+    ///
+    ///   - targetConnection: The name of the database to save backup into.
+    ///
+    ///   - targetDatabaseName: The name of the database to save backup into.
+    ///
+    ///     Default: `.main`.
+    ///
+    /// - Returns: A new database backup.
+    
+    public func backup(databaseName: Backup.DatabaseName = .main,
+                       usingConnection targetConnection: Connection,
+                       andDatabaseName targetDatabaseName: Backup.DatabaseName = .main) throws -> Backup {
+        return try Backup(targetConnection: targetConnection,
+                          targetName: targetDatabaseName,
+                          sourceConnection: self,
+                          sourceName: databaseName)
+    }
+    
     // MARK: - Error Handling
 
     func sync<T>(_ block: () throws -> T) rethrows -> T {
diff --git a/Tests/SQLiteTests/BackupTests.swift b/Tests/SQLiteTests/BackupTests.swift
deleted file mode 100644
index 284ba610..00000000
--- a/Tests/SQLiteTests/BackupTests.swift
+++ /dev/null
@@ -1,36 +0,0 @@
-import XCTest
-import Foundation
-import Dispatch
-@testable import SQLite
-
-#if SQLITE_SWIFT_STANDALONE
-import sqlite3
-#elseif SQLITE_SWIFT_SQLCIPHER
-import SQLCipher
-#elseif os(Linux)
-import CSQLite
-#else
-import SQLite3
-#endif
-
-class BackupTests : SQLiteTestCase {
-    override func setUp() {
-        super.setUp()
-        
-        CreateUsersTable()
-    }
-    
-    func test_backup_copies_database() throws {
-        let source = db!
-        let target = try Connection()
-
-        try InsertUsers("alice", "betsy")
-        
-        let backup = try Backup(targetConnection: target, sourceConnection: source)
-        try backup.step()
-        
-        let users = try target.prepare("SELECT email FROM users ORDER BY email")
-        XCTAssertEqual(users.map { $0[0] as? String }, ["alice@example.com", "betsy@example.com"])
-    }
-}
-
diff --git a/Tests/SQLiteTests/ConnectionTests.swift b/Tests/SQLiteTests/ConnectionTests.swift
index eab3cf00..bae13971 100644
--- a/Tests/SQLiteTests/ConnectionTests.swift
+++ b/Tests/SQLiteTests/ConnectionTests.swift
@@ -141,6 +141,18 @@ class ConnectionTests : SQLiteTestCase {
 
         AssertSQL("BEGIN EXCLUSIVE TRANSACTION")
     }
+    
+    func test_backup_copiesDatabase() throws {
+        let target = try Connection()
+        
+        try InsertUsers("alice", "betsy")
+        
+        let backup = try db.backup(usingConnection: target)
+        try backup.step()
+        
+        let users = try target.prepare("SELECT email FROM users ORDER BY email")
+        XCTAssertEqual(users.map { $0[0] as? String }, ["alice@example.com", "betsy@example.com"])
+    }
 
     func test_transaction_beginsAndCommitsTransactions() {
         let stmt = try! db.prepare("INSERT INTO users (email) VALUES (?)", "alice@example.com")

From 115ad41835b9de8c2f69fa510c222ce85e526a71 Mon Sep 17 00:00:00 2001
From: Johannes Ebeling <johannes.ebeling@wandelbots.com>
Date: Sat, 17 Aug 2019 00:15:34 +0200
Subject: [PATCH 016/326] Enable the encodable api to pass the onConflict
 parameter to the QueryType.insert function

---
 Sources/SQLite/Typed/Coding.swift | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/Sources/SQLite/Typed/Coding.swift b/Sources/SQLite/Typed/Coding.swift
index c3fb931b..d4f99e6e 100644
--- a/Sources/SQLite/Typed/Coding.swift
+++ b/Sources/SQLite/Typed/Coding.swift
@@ -44,6 +44,30 @@ extension QueryType {
         try encodable.encode(to: encoder)
         return self.insert(encoder.setters + otherSetters)
     }
+    
+    /// Creates an `INSERT` statement by encoding the given object
+    /// This method converts any custom nested types to JSON data and does not handle any sort
+    /// of object relationships. If you want to support relationships between objects you will
+    /// have to provide your own Encodable implementations that encode the correct ids.
+    /// The onConflict will be passed to the actual insert function to define what should happen
+    /// when an error occurs during the insert operation.
+    ///
+    /// - Parameters:
+    ///
+    ///   - onConlict: Define what happens when an insert operation fails
+    ///
+    ///   - encodable: An encodable object to insert
+    ///
+    ///   - userInfo: User info to be passed to encoder
+    ///
+    ///   - otherSetters: Any other setters to include in the insert
+    ///
+    /// - Returns: An `INSERT` statement fort the encodable object
+    public func insert(or onConflict: OnConflict, encodable: Encodable, userInfo: [CodingUserInfoKey:Any] = [:], otherSetters: [Setter] = []) throws -> Insert {
+        let encoder = SQLiteEncoder(userInfo: userInfo)
+        try encodable.encode(to: encoder)
+        return self.insert(or: onConflict, encoder.setters + otherSetters)
+    }
 
     /// Creates an `UPDATE` statement by encoding the given object
     /// This method converts any custom nested types to JSON data and does not handle any sort

From 23f4aa42553119aab2e0118afc6b080e70a8d101 Mon Sep 17 00:00:00 2001
From: Svetlana Korosteleva <svetlana.korosteleva@concur.com>
Date: Mon, 2 Dec 2019 15:14:59 -0800
Subject: [PATCH 017/326] Add interface to perform migration to new major
 SQLCipher immediately after setting a key

This change adds `keyAndMigrate` methods which perform "PRAGMA cipher_migrate;" call
immediately after calling `sqlite3_key_v2`
This call is needed to open the database files created in older major SQLCipher version (e.g. 3.x.x)
by newer SQLCipher version (e.g. 4.x.x).
These calls should be performed only once, when opening older database files for the first time.
See https://www.zetetic.net/sqlcipher/sqlcipher-api/#cipher_migrate
and https://discuss.zetetic.net/t/upgrading-to-sqlcipher-4/3283 for more details regarding SQLCipher upgrade
---
 Sources/SQLite/Extensions/Cipher.swift | 33 +++++++++++++++++++++++++-
 1 file changed, 32 insertions(+), 1 deletion(-)

diff --git a/Sources/SQLite/Extensions/Cipher.swift b/Sources/SQLite/Extensions/Cipher.swift
index 44919aab..4d8d426b 100644
--- a/Sources/SQLite/Extensions/Cipher.swift
+++ b/Sources/SQLite/Extensions/Cipher.swift
@@ -32,6 +32,25 @@ extension Connection {
         try _key_v2(db: db, keyPointer: key.bytes, keySize: key.bytes.count)
     }
 
+    /// Same as `key(_ key: String, db: String = "main")`, running "PRAGMA cipher_migrate;"
+    /// immediately after calling `sqlite3_key_v2`, which performs the migration of
+    /// SQLCipher database created by older major version of SQLCipher, to be able to
+    /// open this database with new major version of SQLCipher
+    /// (e.g. to open database created by SQLCipher version 3.x.x with SQLCipher version 4.x.x).
+    /// As "PRAGMA cipher_migrate;" is time-consuming, it is recommended to use this function
+    /// only after failure of `key(_ key: String, db: String = "main")`, if older versions of
+    /// your app may ise older version of SQLCipher
+    /// See https://www.zetetic.net/sqlcipher/sqlcipher-api/#cipher_migrate
+    /// and https://discuss.zetetic.net/t/upgrading-to-sqlcipher-4/3283
+    /// for more details regarding SQLCipher upgrade
+    public func keyAndMigrate(_ key: String, db: String = "main") throws {
+        try _key_v2(db: db, keyPointer: key, keySize: key.utf8.count, migrate: true)
+    }
+
+    /// Same as `[`keyAndMigrate(_ key: String, db: String = "main")` accepting byte array as key
+    public func keyAndMigrate(_ key: Blob, db: String = "main") throws {
+        try _key_v2(db: db, keyPointer: key.bytes, keySize: key.bytes.count, migrate: true)
+    }
 
     /// Change the key on an open database.  If the current database is not encrypted, this routine
     /// will encrypt it.
@@ -47,8 +66,20 @@ extension Connection {
     }
 
     // MARK: - private
-    private func _key_v2(db: String, keyPointer: UnsafePointer<UInt8>, keySize: Int) throws {
+    private func _key_v2(db: String,
+                         keyPointer: UnsafePointer<UInt8>,
+                         keySize: Int,
+                         migrate: Bool = false) throws {
         try check(sqlite3_key_v2(handle, db, keyPointer, Int32(keySize)))
+        if migrate {
+            // Run "PRAGMA cipher_migrate;" immediately after `sqlite3_key_v2`
+            // per recommendation of SQLCipher authors
+            let migrateResult = try scalar("PRAGMA cipher_migrate;")
+            if (migrateResult as? String) != "0" {
+                // "0" is the result of successfull migration
+                throw Result.error(message: "Error in cipher migration, result \(migrateResult.debugDescription)", code: 1, statement: nil)
+            }
+        }
         try cipher_key_check()
     }
 

From f040e145d328042a4a0f042086ac09b28f4b1b41 Mon Sep 17 00:00:00 2001
From: Daniel Shelley <dshelley@ancestry.com>
Date: Fri, 17 Jul 2020 10:59:47 -0600
Subject: [PATCH 018/326] set deployment target to iOS 9 to fix Archiving with
 Xcode 12

---
 SQLite.xcodeproj/project.pbxproj | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj
index 30a40214..80719c33 100644
--- a/SQLite.xcodeproj/project.pbxproj
+++ b/SQLite.xcodeproj/project.pbxproj
@@ -1173,7 +1173,7 @@
 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
-				IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
 				MACOSX_DEPLOYMENT_TARGET = 10.9;
 				MTL_ENABLE_DEBUG_INFO = YES;
 				ONLY_ACTIVE_ARCH = YES;
@@ -1229,7 +1229,7 @@
 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
-				IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
 				MACOSX_DEPLOYMENT_TARGET = 10.9;
 				MTL_ENABLE_DEBUG_INFO = NO;
 				PRODUCT_NAME = "";
@@ -1255,7 +1255,7 @@
 				DYLIB_INSTALL_NAME_BASE = "@rpath";
 				INFOPLIST_FILE = "$(SRCROOT)/Sources/SQLite/Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
-				IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
 				PRODUCT_NAME = SQLite;
@@ -1276,7 +1276,7 @@
 				DYLIB_INSTALL_NAME_BASE = "@rpath";
 				INFOPLIST_FILE = "$(SRCROOT)/Sources/SQLite/Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
-				IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
 				PRODUCT_NAME = SQLite;

From 140374a1f25df9a50c9e014679844bb2de70542d Mon Sep 17 00:00:00 2001
From: Geoff MacDonald <geoffmacd@gmail.com>
Date: Fri, 30 Apr 2021 11:09:45 -0700
Subject: [PATCH 019/326] implement batch insert, insertMany()

---
 Sources/SQLite/Typed/Coding.swift  | 25 +++++++++++++++++++-
 Sources/SQLite/Typed/Query.swift   | 37 ++++++++++++++++++++++++++++++
 Tests/SQLiteTests/QueryTests.swift | 37 ++++++++++++++++++++++++++++++
 3 files changed, 98 insertions(+), 1 deletion(-)

diff --git a/Sources/SQLite/Typed/Coding.swift b/Sources/SQLite/Typed/Coding.swift
index c3fb931b..34edbc49 100644
--- a/Sources/SQLite/Typed/Coding.swift
+++ b/Sources/SQLite/Typed/Coding.swift
@@ -38,13 +38,36 @@ extension QueryType {
     ///
     ///   - otherSetters: Any other setters to include in the insert
     ///
-    /// - Returns: An `INSERT` statement fort the encodable object
+    /// - Returns: An `INSERT` statement for the encodable object
     public func insert(_ encodable: Encodable, userInfo: [CodingUserInfoKey:Any] = [:], otherSetters: [Setter] = []) throws -> Insert {
         let encoder = SQLiteEncoder(userInfo: userInfo)
         try encodable.encode(to: encoder)
         return self.insert(encoder.setters + otherSetters)
     }
 
+    /// Creates a batch `INSERT` statement by encoding the array of given objects
+    /// This method converts any custom nested types to JSON data and does not handle any sort
+    /// of object relationships. If you want to support relationships between objects you will
+    /// have to provide your own Encodable implementations that encode the correct ids.
+    ///
+    /// - Parameters:
+    ///
+    ///   - encodables: Encodable objects to insert
+    ///
+    ///   - userInfo: User info to be passed to encoder
+    ///
+    ///   - otherSetters: Any other setters to include in the inserts, per row/object.
+    ///
+    /// - Returns: An `INSERT` statement for the encodable objects
+    public func insertMany(_ encodables: [Encodable], userInfo: [CodingUserInfoKey:Any] = [:], otherSetters: [Setter] = []) throws -> Insert {
+        let combinedSetters = try encodables.map { encodable -> [Setter] in
+            let encoder = SQLiteEncoder(userInfo: userInfo)
+            try encodable.encode(to: encoder)
+            return encoder.setters + otherSetters
+        }
+        return self.insertMany(combinedSetters)
+    }
+
     /// Creates an `UPDATE` statement by encoding the given object
     /// This method converts any custom nested types to JSON data and does not handle any sort
     /// of object relationships. If you want to support relationships between objects you will
diff --git a/Sources/SQLite/Typed/Query.swift b/Sources/SQLite/Typed/Query.swift
index f6ef6df8..61deaa1f 100644
--- a/Sources/SQLite/Typed/Query.swift
+++ b/Sources/SQLite/Typed/Query.swift
@@ -631,6 +631,18 @@ extension QueryType {
         return insert(onConflict, values)
     }
 
+    public func insertMany( _ values: [[Setter]]) -> Insert {
+        return insertMany(nil, values)
+    }
+
+    public func insertMany(or onConflict: OnConflict, _ values: [[Setter]]) -> Insert {
+        return insertMany(onConflict, values)
+    }
+
+    public func insertMany(or onConflict: OnConflict, _ values: [Setter]...) -> Insert {
+        return insertMany(onConflict, values)
+    }
+
     fileprivate func insert(_ or: OnConflict?, _ values: [Setter]) -> Insert {
         let insert = values.reduce((columns: [Expressible](), values: [Expressible]())) { insert, setter in
             (insert.columns + [setter.column], insert.values + [setter.value])
@@ -650,6 +662,29 @@ extension QueryType {
         return Insert(" ".join(clauses.compactMap { $0 }).expression)
     }
 
+    fileprivate func insertMany(_ or: OnConflict?, _ values: [[Setter]]) -> Insert {
+        guard values.count > 0 else {
+            return insert()
+        }
+        let insertRows = values.map { rowValues in
+            rowValues.reduce((columns: [Expressible](), values: [Expressible]())) { insert, setter in
+                (insert.columns + [setter.column], insert.values + [setter.value])
+            }
+        }
+
+        let clauses: [Expressible?] = [
+            Expression<Void>(literal: "INSERT"),
+            or.map { Expression<Void>(literal: "OR \($0.rawValue)") },
+            Expression<Void>(literal: "INTO"),
+            tableName(),
+            "".wrap(insertRows[0].columns) as Expression<Void>,
+            Expression<Void>(literal: "VALUES"),
+            ", ".join(insertRows.map(\.values).map({ "".wrap($0) as Expression<Void> })),
+            whereClause
+        ]
+        return Insert(" ".join(clauses.compactMap { $0 }).expression)
+    }
+
     /// Runs an `INSERT` statement against the query with `DEFAULT VALUES`.
     public func insert() -> Insert {
         return Insert(" ".join([
@@ -1010,6 +1045,8 @@ extension Connection {
     /// - SeeAlso: `QueryType.insert(value:_:)`
     /// - SeeAlso: `QueryType.insert(values:)`
     /// - SeeAlso: `QueryType.insert(or:_:)`
+    /// - SeeAlso: `QueryType.insertMany(values:)`
+    /// - SeeAlso: `QueryType.insertMany(or:_:)`
     /// - SeeAlso: `QueryType.insert()`
     ///
     /// - Parameter query: An insert query.
diff --git a/Tests/SQLiteTests/QueryTests.swift b/Tests/SQLiteTests/QueryTests.swift
index 2a9e4ecb..f48f49b9 100644
--- a/Tests/SQLiteTests/QueryTests.swift
+++ b/Tests/SQLiteTests/QueryTests.swift
@@ -247,6 +247,26 @@ class QueryTests : XCTestCase {
         )
     }
 
+    func test_insert_many_compilesInsertManyExpression() {
+        AssertSQL(
+            "INSERT INTO \"users\" (\"email\", \"age\") VALUES ('alice@example.com', 30), ('geoff@example.com', 32), ('alex@example.com', 83)",
+            users.insertMany([[email <- "alice@example.com", age <- 30], [email <- "geoff@example.com", age <- 32], [email <- "alex@example.com", age <- 83]])
+        )
+    }
+    func test_insert_many_compilesInsertManyNoneExpression() {
+        AssertSQL(
+            "INSERT INTO \"users\" DEFAULT VALUES",
+            users.insertMany([])
+        )
+    }
+
+    func test_insert_many_withOnConflict_compilesInsertManyOrOnConflictExpression() {
+        AssertSQL(
+            "INSERT OR REPLACE INTO \"users\" (\"email\", \"age\") VALUES ('alice@example.com', 30), ('geoff@example.com', 32), ('alex@example.com', 83)",
+            users.insertMany(or: .replace, [[email <- "alice@example.com", age <- 30], [email <- "geoff@example.com", age <- 32], [email <- "alex@example.com", age <- 83]])
+        )
+    }
+
     func test_insert_encodable() throws {
         let emails = Table("emails")
         let value = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4, optional: nil, sub: nil)
@@ -270,6 +290,18 @@ class QueryTests : XCTestCase {
         )
     }
 
+    func test_insert_many_encodable() throws {
+        let emails = Table("emails")
+        let value1 = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4, optional: nil, sub: nil)
+        let value2 = TestCodable(int: 2, string: "3", bool: true, float: 3, double: 5, optional: nil, sub: nil)
+        let value3 = TestCodable(int: 3, string: "4", bool: true, float: 3, double: 6, optional: nil, sub: nil)
+        let insert = try emails.insertMany([value1, value2, value3])
+        AssertSQL(
+            "INSERT INTO \"emails\" (\"int\", \"string\", \"bool\", \"float\", \"double\") VALUES (1, '2', 1, 3.0, 4.0), (2, '3', 1, 3.0, 5.0), (3, '4', 1, 3.0, 6.0)",
+            insert
+        )
+    }
+
     func test_update_compilesUpdateExpression() {
         AssertSQL(
             "UPDATE \"users\" SET \"age\" = 30, \"admin\" = 1 WHERE (\"id\" = 1)",
@@ -483,6 +515,11 @@ class QueryIntegrationTests : SQLiteTestCase {
         XCTAssertEqual(1, id)
     }
 
+    func test_insert_many() {
+        let id = try! db.run(users.insertMany([[email <- "alice@example.com"], [email <- "geoff@example.com"]]))
+        XCTAssertEqual(2, id)
+    }
+
     func test_update() {
         let changes = try! db.run(users.update(email <- "alice@example.com"))
         XCTAssertEqual(0, changes)

From 087264792a7c75c446106ef4090d9fd669b37e8a Mon Sep 17 00:00:00 2001
From: Geoff MacDonald <geoffmacd@gmail.com>
Date: Fri, 30 Apr 2021 11:27:42 -0700
Subject: [PATCH 020/326] Update Index.md

---
 Documentation/Index.md | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/Documentation/Index.md b/Documentation/Index.md
index 70d67c2d..2f441c8c 100644
--- a/Documentation/Index.md
+++ b/Documentation/Index.md
@@ -638,6 +638,18 @@ do {
 }
 ```
 
+Multiple rows can be inserted at once by similarily calling `insertMany` with an array of per-row [setters](#setters).
+
+```swift
+do {
+    let rowid = try db.run(users.insertMany([mail <- "alice@mac.com"], [email <- "geoff@mac.com"]))
+    print("inserted id: \(rowid)")
+} catch {
+    print("insertion failed: \(error)")
+}
+```
+
+
 The [`update`](#updating-rows) and [`delete`](#deleting-rows) functions
 follow similar patterns.
 

From 05c404fcec8df8043d68d38617a9c69f54772f50 Mon Sep 17 00:00:00 2001
From: Geoff MacDonald <geoffmacd@gmail.com>
Date: Fri, 30 Apr 2021 11:37:22 -0700
Subject: [PATCH 021/326] cleanup sql creation

---
 Sources/SQLite/Typed/Query.swift | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/Sources/SQLite/Typed/Query.swift b/Sources/SQLite/Typed/Query.swift
index 61deaa1f..54e44fa5 100644
--- a/Sources/SQLite/Typed/Query.swift
+++ b/Sources/SQLite/Typed/Query.swift
@@ -663,12 +663,14 @@ extension QueryType {
     }
 
     fileprivate func insertMany(_ or: OnConflict?, _ values: [[Setter]]) -> Insert {
-        guard values.count > 0 else {
+        guard let firstInsert = values.first else {
+            // must be at least 1 object or else we don't know columns. Default to default inserts.
             return insert()
         }
-        let insertRows = values.map { rowValues in
-            rowValues.reduce((columns: [Expressible](), values: [Expressible]())) { insert, setter in
-                (insert.columns + [setter.column], insert.values + [setter.value])
+        let columns = firstInsert.map { $0.column }
+        let insertValues = values.map { rowValues in
+            rowValues.reduce([Expressible]()) { insert, setter in
+                insert + [setter.value]
             }
         }
 
@@ -677,9 +679,9 @@ extension QueryType {
             or.map { Expression<Void>(literal: "OR \($0.rawValue)") },
             Expression<Void>(literal: "INTO"),
             tableName(),
-            "".wrap(insertRows[0].columns) as Expression<Void>,
+            "".wrap(columns) as Expression<Void>,
             Expression<Void>(literal: "VALUES"),
-            ", ".join(insertRows.map(\.values).map({ "".wrap($0) as Expression<Void> })),
+            ", ".join(insertValues.map({ "".wrap($0) as Expression<Void> })),
             whereClause
         ]
         return Insert(" ".join(clauses.compactMap { $0 }).expression)

From 4fde8dba065b213ae0a5a067a1d8d31680091b5a Mon Sep 17 00:00:00 2001
From: Geoff MacDonald <geoffmac@dropbox.com>
Date: Fri, 30 Apr 2021 14:52:52 -0700
Subject: [PATCH 022/326] try and fix travis?

---
 .travis.yml | 11 ++++-------
 1 file changed, 4 insertions(+), 7 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index c8fc5feb..ea60b063 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,11 +1,11 @@
 language: objective-c
-rvm: 2.3
+rvm: 2.7.3
 # https://docs.travis-ci.com/user/reference/osx
-osx_image: xcode10.2
+osx_image: xcode12.2
 env:
   global:
-    - IOS_SIMULATOR="iPhone XS"
-    - IOS_VERSION="12.2"
+    - IOS_SIMULATOR="iPhone 11"
+    - IOS_VERSION="14.2"
 matrix:
   include:
     - env: BUILD_SCHEME="SQLite iOS"
@@ -25,7 +25,4 @@ before_install:
   - brew update
   - brew outdated carthage || brew upgrade carthage
 script:
-# Workaround for Xcode 10.2/tvOS 9.1 bug
-# See https://stackoverflow.com/questions/55389080/xcode-10-2-failed-to-run-app-on-simulator-with-ios-10
-  - sudo mkdir /Library/Developer/CoreSimulator/Profiles/Runtimes/tvOS\ 9.1.simruntime/Contents/Resources/RuntimeRoot/usr/lib/swift
   - ./run-tests.sh

From 183a43948f40637643c15e6e19a4b983d2df8325 Mon Sep 17 00:00:00 2001
From: Geoff MacDonald <geoffmac@dropbox.com>
Date: Fri, 30 Apr 2021 15:32:43 -0700
Subject: [PATCH 023/326] Update Planning.md

---
 Documentation/Planning.md | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/Documentation/Planning.md b/Documentation/Planning.md
index 5f885de8..62df1f24 100644
--- a/Documentation/Planning.md
+++ b/Documentation/Planning.md
@@ -33,6 +33,3 @@ be referred to when it comes time to add the corresponding feature._
 _Features that are not actively being considered, perhaps because of no clean
 type-safe way to implement them with the current Swift, or bugs, or just
 general uncertainty._
-
- * provide a mechanism for INSERT INTO multiple values, per
-   [#168](https://github.com/stephencelis/SQLite.swift/issues/168)

From b260b0a4aa1754b609ac48c637578ef1cea0898c Mon Sep 17 00:00:00 2001
From: Geoff MacDonald <geoffmac@dropbox.com>
Date: Fri, 30 Apr 2021 15:35:12 -0700
Subject: [PATCH 024/326] upgrade test deps to force it working

---
 .travis.yml                  |  2 +-
 Tests/CocoaPods/Gemfile.lock | 46 ++++++++++++++++++------------------
 2 files changed, 24 insertions(+), 24 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index ea60b063..2a0460bb 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -20,7 +20,7 @@ matrix:
     - env: CARTHAGE_PLATFORM="tvOS"
     - env: PACKAGE_MANAGER_COMMAND="test"
 before_install:
-  - gem update bundler
+ -  gem install bundler
   - gem install xcpretty --no-document
   - brew update
   - brew outdated carthage || brew upgrade carthage
diff --git a/Tests/CocoaPods/Gemfile.lock b/Tests/CocoaPods/Gemfile.lock
index b5172144..e5c53ea3 100644
--- a/Tests/CocoaPods/Gemfile.lock
+++ b/Tests/CocoaPods/Gemfile.lock
@@ -1,18 +1,18 @@
 GEM
   remote: https://rubygems.org/
   specs:
-    CFPropertyList (3.0.0)
-    activesupport (4.2.11)
+    CFPropertyList (3.0.3)
+    activesupport (4.2.11.3)
       i18n (~> 0.7)
       minitest (~> 5.1)
       thread_safe (~> 0.3, >= 0.3.4)
       tzinfo (~> 1.1)
     atomos (0.1.3)
-    claide (1.0.2)
-    cocoapods (1.6.0.beta.2)
+    claide (1.0.3)
+    cocoapods (1.6.2)
       activesupport (>= 4.0.2, < 5)
       claide (>= 1.0.2, < 2.0)
-      cocoapods-core (= 1.6.0.beta.2)
+      cocoapods-core (= 1.6.2)
       cocoapods-deintegrate (>= 1.0.2, < 2.0)
       cocoapods-downloader (>= 1.2.2, < 2.0)
       cocoapods-plugins (>= 1.0.0, < 2.0)
@@ -22,56 +22,56 @@ GEM
       cocoapods-try (>= 1.1.0, < 2.0)
       colored2 (~> 3.1)
       escape (~> 0.0.4)
-      fourflusher (~> 2.0.1)
+      fourflusher (>= 2.2.0, < 3.0)
       gh_inspector (~> 1.0)
       molinillo (~> 0.6.6)
       nap (~> 1.0)
-      ruby-macho (~> 1.3, >= 1.3.1)
-      xcodeproj (>= 1.7.0, < 2.0)
-    cocoapods-core (1.6.0.beta.2)
+      ruby-macho (~> 1.4)
+      xcodeproj (>= 1.8.1, < 2.0)
+    cocoapods-core (1.6.2)
       activesupport (>= 4.0.2, < 6)
       fuzzy_match (~> 2.0.4)
       nap (~> 1.0)
-    cocoapods-deintegrate (1.0.2)
-    cocoapods-downloader (1.2.2)
+    cocoapods-deintegrate (1.0.4)
+    cocoapods-downloader (1.4.0)
     cocoapods-plugins (1.0.0)
       nap
     cocoapods-search (1.0.0)
-    cocoapods-stats (1.0.0)
-    cocoapods-trunk (1.3.1)
+    cocoapods-stats (1.1.0)
+    cocoapods-trunk (1.5.0)
       nap (>= 0.8, < 2.0)
       netrc (~> 0.11)
-    cocoapods-try (1.1.0)
+    cocoapods-try (1.2.0)
     colored2 (3.1.2)
-    concurrent-ruby (1.1.4)
+    concurrent-ruby (1.1.8)
     escape (0.0.4)
-    fourflusher (2.0.1)
+    fourflusher (2.3.1)
     fuzzy_match (2.0.4)
     gh_inspector (1.1.3)
     i18n (0.9.5)
       concurrent-ruby (~> 1.0)
     minitest (5.11.3)
     molinillo (0.6.6)
-    nanaimo (0.2.6)
+    nanaimo (0.3.0)
     nap (1.1.0)
     netrc (0.11.0)
-    ruby-macho (1.3.1)
+    ruby-macho (1.4.0)
     thread_safe (0.3.6)
-    tzinfo (1.2.5)
+    tzinfo (1.2.9)
       thread_safe (~> 0.1)
-    xcodeproj (1.7.0)
+    xcodeproj (1.19.0)
       CFPropertyList (>= 2.3.3, < 4.0)
       atomos (~> 0.1.3)
       claide (>= 1.0.2, < 2.0)
       colored2 (~> 3.1)
-      nanaimo (~> 0.2.6)
+      nanaimo (~> 0.3.0)
 
 PLATFORMS
   ruby
 
 DEPENDENCIES
-  cocoapods (~> 1.6.0beta2)
+  cocoapods (~> 1.6.1)
   minitest
 
 BUNDLED WITH
-   1.17.1
+   1.17.2

From e40e3369c14319df540bbac476c419cfea20294e Mon Sep 17 00:00:00 2001
From: Geoff MacDonald <geoffmac@dropbox.com>
Date: Fri, 30 Apr 2021 17:43:16 -0700
Subject: [PATCH 025/326] update deps

---
 Tests/CocoaPods/Gemfile             |  2 +-
 Tests/CocoaPods/Gemfile.lock        | 51 +++++++++++++++++++----------
 Tests/CocoaPods/integration_test.rb |  2 +-
 3 files changed, 36 insertions(+), 19 deletions(-)

diff --git a/Tests/CocoaPods/Gemfile b/Tests/CocoaPods/Gemfile
index 77d90eec..19db2b36 100644
--- a/Tests/CocoaPods/Gemfile
+++ b/Tests/CocoaPods/Gemfile
@@ -1,4 +1,4 @@
 source 'https://rubygems.org'
 
-gem 'cocoapods', '~> 1.6.1'
+gem 'cocoapods', '~> 1.10.1'
 gem 'minitest'
diff --git a/Tests/CocoaPods/Gemfile.lock b/Tests/CocoaPods/Gemfile.lock
index e5c53ea3..d99ac49a 100644
--- a/Tests/CocoaPods/Gemfile.lock
+++ b/Tests/CocoaPods/Gemfile.lock
@@ -2,42 +2,51 @@ GEM
   remote: https://rubygems.org/
   specs:
     CFPropertyList (3.0.3)
-    activesupport (4.2.11.3)
-      i18n (~> 0.7)
+    activesupport (5.2.5)
+      concurrent-ruby (~> 1.0, >= 1.0.2)
+      i18n (>= 0.7, < 2)
       minitest (~> 5.1)
-      thread_safe (~> 0.3, >= 0.3.4)
       tzinfo (~> 1.1)
+    addressable (2.7.0)
+      public_suffix (>= 2.0.2, < 5.0)
+    algoliasearch (1.27.5)
+      httpclient (~> 2.8, >= 2.8.3)
+      json (>= 1.5.1)
     atomos (0.1.3)
     claide (1.0.3)
-    cocoapods (1.6.2)
-      activesupport (>= 4.0.2, < 5)
+    cocoapods (1.10.1)
+      addressable (~> 2.6)
       claide (>= 1.0.2, < 2.0)
-      cocoapods-core (= 1.6.2)
-      cocoapods-deintegrate (>= 1.0.2, < 2.0)
-      cocoapods-downloader (>= 1.2.2, < 2.0)
+      cocoapods-core (= 1.10.1)
+      cocoapods-deintegrate (>= 1.0.3, < 2.0)
+      cocoapods-downloader (>= 1.4.0, < 2.0)
       cocoapods-plugins (>= 1.0.0, < 2.0)
       cocoapods-search (>= 1.0.0, < 2.0)
-      cocoapods-stats (>= 1.0.0, < 2.0)
-      cocoapods-trunk (>= 1.3.1, < 2.0)
+      cocoapods-trunk (>= 1.4.0, < 2.0)
       cocoapods-try (>= 1.1.0, < 2.0)
       colored2 (~> 3.1)
       escape (~> 0.0.4)
-      fourflusher (>= 2.2.0, < 3.0)
+      fourflusher (>= 2.3.0, < 3.0)
       gh_inspector (~> 1.0)
       molinillo (~> 0.6.6)
       nap (~> 1.0)
       ruby-macho (~> 1.4)
-      xcodeproj (>= 1.8.1, < 2.0)
-    cocoapods-core (1.6.2)
-      activesupport (>= 4.0.2, < 6)
+      xcodeproj (>= 1.19.0, < 2.0)
+    cocoapods-core (1.10.1)
+      activesupport (> 5.0, < 6)
+      addressable (~> 2.6)
+      algoliasearch (~> 1.0)
+      concurrent-ruby (~> 1.1)
       fuzzy_match (~> 2.0.4)
       nap (~> 1.0)
+      netrc (~> 0.11)
+      public_suffix
+      typhoeus (~> 1.0)
     cocoapods-deintegrate (1.0.4)
     cocoapods-downloader (1.4.0)
     cocoapods-plugins (1.0.0)
       nap
     cocoapods-search (1.0.0)
-    cocoapods-stats (1.1.0)
     cocoapods-trunk (1.5.0)
       nap (>= 0.8, < 2.0)
       netrc (~> 0.11)
@@ -45,18 +54,26 @@ GEM
     colored2 (3.1.2)
     concurrent-ruby (1.1.8)
     escape (0.0.4)
+    ethon (0.14.0)
+      ffi (>= 1.15.0)
+    ffi (1.15.0)
     fourflusher (2.3.1)
     fuzzy_match (2.0.4)
     gh_inspector (1.1.3)
-    i18n (0.9.5)
+    httpclient (2.8.3)
+    i18n (1.8.10)
       concurrent-ruby (~> 1.0)
+    json (2.5.1)
     minitest (5.11.3)
     molinillo (0.6.6)
     nanaimo (0.3.0)
     nap (1.1.0)
     netrc (0.11.0)
+    public_suffix (4.0.6)
     ruby-macho (1.4.0)
     thread_safe (0.3.6)
+    typhoeus (1.4.0)
+      ethon (>= 0.9.0)
     tzinfo (1.2.9)
       thread_safe (~> 0.1)
     xcodeproj (1.19.0)
@@ -70,7 +87,7 @@ PLATFORMS
   ruby
 
 DEPENDENCIES
-  cocoapods (~> 1.6.1)
+  cocoapods (~> 1.10.1)
   minitest
 
 BUNDLED WITH
diff --git a/Tests/CocoaPods/integration_test.rb b/Tests/CocoaPods/integration_test.rb
index 98a539b5..2ce2997c 100755
--- a/Tests/CocoaPods/integration_test.rb
+++ b/Tests/CocoaPods/integration_test.rb
@@ -40,7 +40,7 @@ def test_pod
       super unless consumer.platform_name == :watchos
     end
 
-    def xcodebuild(action, scheme, configuration)
+    def xcodebuild(action, scheme, configuration, _)
       require 'fourflusher'
       command = %W(#{action} -workspace #{File.join(validation_dir, 'App.xcworkspace')} -scheme #{scheme} -configuration #{configuration})
       case consumer.platform_name

From 0742ee04976029504502b6212b60462017519887 Mon Sep 17 00:00:00 2001
From: Geoff MacDonald <geoffmac@dropbox.com>
Date: Fri, 30 Apr 2021 17:46:47 -0700
Subject: [PATCH 026/326] apparently carthage doesnt support xcode 12 out of
 box

---
 .travis.yml             | 8 ++++----
 Makefile                | 2 +-
 Tests/Carthage/Makefile | 2 +-
 3 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index 2a0460bb..b8b2eaba 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,11 +1,11 @@
 language: objective-c
 rvm: 2.7.3
 # https://docs.travis-ci.com/user/reference/osx
-osx_image: xcode12.2
+osx_image: xcode11.6
 env:
   global:
-    - IOS_SIMULATOR="iPhone 11"
-    - IOS_VERSION="14.2"
+    - IOS_SIMULATOR="iPhone XS"
+    - IOS_VERSION="13.6"
 matrix:
   include:
     - env: BUILD_SCHEME="SQLite iOS"
@@ -20,7 +20,7 @@ matrix:
     - env: CARTHAGE_PLATFORM="tvOS"
     - env: PACKAGE_MANAGER_COMMAND="test"
 before_install:
- -  gem install bundler
+  - gem install bundler
   - gem install xcpretty --no-document
   - brew update
   - brew outdated carthage || brew upgrade carthage
diff --git a/Makefile b/Makefile
index 50d07148..b38b90e0 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
 BUILD_TOOL = xcodebuild
 BUILD_SCHEME = SQLite Mac
 IOS_SIMULATOR = iPhone XS
-IOS_VERSION = 12.2
+IOS_VERSION = 13.6
 ifeq ($(BUILD_SCHEME),SQLite iOS)
 	BUILD_ARGUMENTS = -scheme "$(BUILD_SCHEME)" -destination "platform=iOS Simulator,name=$(IOS_SIMULATOR),OS=$(IOS_VERSION)"
 else
diff --git a/Tests/Carthage/Makefile b/Tests/Carthage/Makefile
index f28eb25b..7f068985 100644
--- a/Tests/Carthage/Makefile
+++ b/Tests/Carthage/Makefile
@@ -3,7 +3,7 @@ CARTHAGE_PLATFORM := iOS
 CARTHAGE_CONFIGURATION := Release
 CARTHAGE_DIR := Carthage
 CARTHAGE_ARGS := --no-use-binaries
-CARTHAGE_TOOLCHAIN := com.apple.dt.toolchain.Swift_3_0
+CARTHAGE_TOOLCHAIN := com.apple.dt.toolchain.XcodeDefault
 CARTHAGE_CMDLINE := --configuration $(CARTHAGE_CONFIGURATION) --platform $(CARTHAGE_PLATFORM) --toolchain $(CARTHAGE_TOOLCHAIN) $(CARTHAGE_ARGS)
 
 test: $(CARTHAGE) Cartfile

From a78d8c4ae303c7d4875a29a01bc307e48095f7d6 Mon Sep 17 00:00:00 2001
From: Geoff MacDonald <geoffmac@dropbox.com>
Date: Fri, 30 Apr 2021 18:04:29 -0700
Subject: [PATCH 027/326] try 2.6

---
 .travis.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.travis.yml b/.travis.yml
index b8b2eaba..e9d5ec2a 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,5 +1,5 @@
 language: objective-c
-rvm: 2.7.3
+rvm: 2.6
 # https://docs.travis-ci.com/user/reference/osx
 osx_image: xcode11.6
 env:

From 5052cbbd6d881396c87a9ede9de2e30a8f86a706 Mon Sep 17 00:00:00 2001
From: Geoff MacDonald <geoffmac@dropbox.com>
Date: Fri, 30 Apr 2021 18:13:28 -0700
Subject: [PATCH 028/326] use iphone 11

---
 .travis.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.travis.yml b/.travis.yml
index e9d5ec2a..7bff9ce2 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,7 +4,7 @@ rvm: 2.6
 osx_image: xcode11.6
 env:
   global:
-    - IOS_SIMULATOR="iPhone XS"
+    - IOS_SIMULATOR="iPhone 11"
     - IOS_VERSION="13.6"
 matrix:
   include:

From b7302f6ed19b1994ef9d68882d1cb447be8fd238 Mon Sep 17 00:00:00 2001
From: Nathan Fallet <contact@nathanfallet.me>
Date: Wed, 18 Aug 2021 23:21:01 +0200
Subject: [PATCH 029/326] SPM as first installation method

---
 README.md | 42 +++++++++++++++++++++---------------------
 1 file changed, 21 insertions(+), 21 deletions(-)

diff --git a/README.md b/README.md
index 009167b0..81fb4b5d 100644
--- a/README.md
+++ b/README.md
@@ -126,6 +126,27 @@ and the [companion repository][SQLiteDataAccessLayer2].
 
 > _Note:_ Version 0.11.6 and later requires Swift 5 (and [Xcode](https://developer.apple.com/xcode/downloads/) 10.2) or greater. Version 0.11.5 requires Swift 4.2 (and [Xcode](https://developer.apple.com/xcode/downloads/) 10.1) or greater.
 
+### Swift Package Manager
+
+The [Swift Package Manager][] is a tool for managing the distribution of
+Swift code.
+
+1. Add the following to your `Package.swift` file:
+
+  ```swift
+  dependencies: [
+      .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.12.0")
+  ]
+  ```
+
+2. Build your project:
+
+  ```sh
+  $ swift build
+  ```
+
+[Swift Package Manager]: https://swift.org/package-manager
+
 ### Carthage
 
 [Carthage][] is a simple, decentralized dependency manager for Cocoa. To
@@ -177,27 +198,6 @@ SQLite.swift with CocoaPods:
 [CocoaPods]: https://cocoapods.org
 [CocoaPods Installation]: https://guides.cocoapods.org/using/getting-started.html#getting-started
 
-### Swift Package Manager
-
-The [Swift Package Manager][] is a tool for managing the distribution of
-Swift code.
-
-1. Add the following to your `Package.swift` file:
-
-  ```swift
-  dependencies: [
-      .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.12.0")
-  ]
-  ```
-
-2. Build your project:
-
-  ```sh
-  $ swift build
-  ```
-
-[Swift Package Manager]: https://swift.org/package-manager
-
 ### Manual
 
 To install SQLite.swift as an Xcode sub-project:

From c5b7258ac175751b966a25bc6ba06f1be4ae2caa Mon Sep 17 00:00:00 2001
From: Nathan Fallet <contact@nathanfallet.me>
Date: Thu, 19 Aug 2021 00:03:18 +0200
Subject: [PATCH 030/326] Fixing missing date in upsert test

---
 Tests/SQLiteTests/QueryTests.swift | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Tests/SQLiteTests/QueryTests.swift b/Tests/SQLiteTests/QueryTests.swift
index 9a19945f..fc47ab27 100644
--- a/Tests/SQLiteTests/QueryTests.swift
+++ b/Tests/SQLiteTests/QueryTests.swift
@@ -280,10 +280,10 @@ class QueryTests : XCTestCase {
     func test_upsert_encodable() throws {
         let emails = Table("emails")
         let string = Expression<String>("string")
-        let value = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4, optional: nil, sub: nil)
+        let value = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4, date: Date(timeIntervalSince1970: 0), optional: nil, sub: nil)
         let insert = try emails.upsert(value, onConflictOf: string)
         AssertSQL(
-            "INSERT INTO \"emails\" (\"int\", \"string\", \"bool\", \"float\", \"double\") VALUES (1, '2', 1, 3.0, 4.0) ON CONFLICT (\"string\") DO UPDATE SET \"int\" = \"excluded\".\"int\", \"bool\" = \"excluded\".\"bool\", \"float\" = \"excluded\".\"float\", \"double\" = \"excluded\".\"double\"",
+            "INSERT INTO \"emails\" (\"int\", \"string\", \"bool\", \"float\", \"double\", \"date\") VALUES (1, '2', 1, 3.0, 4.0, '1970-01-01T00:00:00.000') ON CONFLICT (\"string\") DO UPDATE SET \"int\" = \"excluded\".\"int\", \"bool\" = \"excluded\".\"bool\", \"float\" = \"excluded\".\"float\", \"double\" = \"excluded\".\"double\"",
             insert
         )
     }

From 5c49d0ed90279d5f90a22997c95ca4c6bfb5bb9f Mon Sep 17 00:00:00 2001
From: Nathan Fallet <contact@nathanfallet.me>
Date: Thu, 19 Aug 2021 00:09:34 +0200
Subject: [PATCH 031/326] Oups

---
 Tests/SQLiteTests/QueryTests.swift | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Tests/SQLiteTests/QueryTests.swift b/Tests/SQLiteTests/QueryTests.swift
index fc47ab27..9b845a97 100644
--- a/Tests/SQLiteTests/QueryTests.swift
+++ b/Tests/SQLiteTests/QueryTests.swift
@@ -283,7 +283,7 @@ class QueryTests : XCTestCase {
         let value = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4, date: Date(timeIntervalSince1970: 0), optional: nil, sub: nil)
         let insert = try emails.upsert(value, onConflictOf: string)
         AssertSQL(
-            "INSERT INTO \"emails\" (\"int\", \"string\", \"bool\", \"float\", \"double\", \"date\") VALUES (1, '2', 1, 3.0, 4.0, '1970-01-01T00:00:00.000') ON CONFLICT (\"string\") DO UPDATE SET \"int\" = \"excluded\".\"int\", \"bool\" = \"excluded\".\"bool\", \"float\" = \"excluded\".\"float\", \"double\" = \"excluded\".\"double\"",
+            "INSERT INTO \"emails\" (\"int\", \"string\", \"bool\", \"float\", \"double\", \"date\") VALUES (1, '2', 1, 3.0, 4.0, '1970-01-01T00:00:00.000') ON CONFLICT (\"string\") DO UPDATE SET \"int\" = \"excluded\".\"int\", \"bool\" = \"excluded\".\"bool\", \"float\" = \"excluded\".\"float\", \"double\" = \"excluded\".\"double\", \"date\" = \"excluded\".\"date\"",
             insert
         )
     }

From b20d6baca11636ac28de51aa797312811d50c2b5 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Thu, 19 Aug 2021 00:11:54 +0200
Subject: [PATCH 032/326] Fix spm package structure

---
 Package.swift                                 | 78 +++++++++----------
 SQLite.xcodeproj/project.pbxproj              | 20 ++---
 Sources/SQLiteObjc/{ => include}/SQLiteObjc.h |  0
 3 files changed, 48 insertions(+), 50 deletions(-)
 rename Sources/SQLiteObjc/{ => include}/SQLiteObjc.h (100%)

diff --git a/Package.swift b/Package.swift
index 73339b7b..7522fa45 100644
--- a/Package.swift
+++ b/Package.swift
@@ -4,52 +4,50 @@ import PackageDescription
 let package = Package(
     name: "SQLite.swift",
     products: [
-			.library(
-				name: "SQLite", 
-				targets: ["SQLite"]
-			)
-		],
+        .library(
+            name: "SQLite",
+            targets: ["SQLite"]
+        )
+    ],
     targets: [
         .target(
-					name: "SQLite", 
-					dependencies: ["SQLiteObjc"],
-					exclude: [
-						"Info.plist"
-					]
-				),
+            name: "SQLite",
+            dependencies: ["SQLiteObjc"],
+            exclude: [
+                "Info.plist"
+            ]
+        ),
         .target(
-					name: "SQLiteObjc",
-					dependencies: [],
-					exclude: [
-						"SQLiteObjc.h",
-						"fts3_tokenizer.h",
-						"include/README.md"
-					]
-				),
+            name: "SQLiteObjc",
+            dependencies: [],
+            exclude: [
+                "fts3_tokenizer.h"
+            ]
+        ),
         .testTarget(
-					name: "SQLiteTests", 
-					dependencies: [
-						"SQLite"
-					], 
-					path: "Tests/SQLiteTests",
-					exclude: [
-						"Info.plist"
-					],
-					resources: [
-						.copy("fixtures/encrypted-3.x.sqlite"),
-						.copy("fixtures/encrypted-4.x.sqlite")
-					]
-				)
+            name: "SQLiteTests",
+            dependencies: [
+                "SQLite"
+            ],
+            path: "Tests/SQLiteTests",
+            exclude: [
+                "Info.plist"
+            ],
+            resources: [
+                .copy("fixtures/encrypted-3.x.sqlite"),
+                .copy("fixtures/encrypted-4.x.sqlite")
+            ]
+        )
     ]
 )
 
 #if os(Linux)
-    package.dependencies = [.package(url: "https://github.com/stephencelis/CSQLite.git", from: "0.0.3")]
-    package.targets = [
-        .target(name: "SQLite", exclude: ["Extensions/FTS4.swift", "Extensions/FTS5.swift"]),
-        .testTarget(name: "SQLiteTests", dependencies: ["SQLite"], path: "Tests/SQLiteTests", exclude: [
-            "FTS4Tests.swift",
-            "FTS5Tests.swift"
-        ])
-    ]
+package.dependencies = [.package(url: "https://github.com/stephencelis/CSQLite.git", from: "0.0.3")]
+package.targets = [
+    .target(name: "SQLite", exclude: ["Extensions/FTS4.swift", "Extensions/FTS5.swift"]),
+    .testTarget(name: "SQLiteTests", dependencies: ["SQLite"], path: "Tests/SQLiteTests", exclude: [
+        "FTS4Tests.swift",
+        "FTS5Tests.swift"
+    ])
+]
 #endif
diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj
index 1b5bb0f4..a730284d 100644
--- a/SQLite.xcodeproj/project.pbxproj
+++ b/SQLite.xcodeproj/project.pbxproj
@@ -11,7 +11,6 @@
 		03A65E721C6BB2D30062603F /* SQLite.h in Headers */ = {isa = PBXBuildFile; fileRef = EE247AD61C3F04ED00AE3E12 /* SQLite.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		03A65E731C6BB2D80062603F /* Foundation.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AF71C3F06E900AE3E12 /* Foundation.swift */; };
 		03A65E741C6BB2DA0062603F /* Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AF81C3F06E900AE3E12 /* Helpers.swift */; };
-		03A65E751C6BB2DF0062603F /* SQLiteObjc.h in Headers */ = {isa = PBXBuildFile; fileRef = EE91808D1C46E5230038162A /* SQLiteObjc.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		03A65E761C6BB2E60062603F /* Blob.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AEE1C3F06E900AE3E12 /* Blob.swift */; };
 		03A65E771C6BB2E60062603F /* Connection.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AEF1C3F06E900AE3E12 /* Connection.swift */; };
 		03A65E781C6BB2EA0062603F /* fts3_tokenizer.h in Headers */ = {isa = PBXBuildFile; fileRef = EE247AF01C3F06E900AE3E12 /* fts3_tokenizer.h */; };
@@ -101,9 +100,12 @@
 		3D67B3F71DB246D700A4F4C6 /* Foundation.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AF71C3F06E900AE3E12 /* Foundation.swift */; };
 		3D67B3F81DB246D700A4F4C6 /* Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AF81C3F06E900AE3E12 /* Helpers.swift */; };
 		3D67B3F91DB246E700A4F4C6 /* SQLiteObjc.m in Sources */ = {isa = PBXBuildFile; fileRef = EE247AF11C3F06E900AE3E12 /* SQLiteObjc.m */; };
-		3D67B3FB1DB2470600A4F4C6 /* SQLiteObjc.h in Headers */ = {isa = PBXBuildFile; fileRef = EE91808D1C46E5230038162A /* SQLiteObjc.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		3D67B3FC1DB2471B00A4F4C6 /* SQLite.h in Headers */ = {isa = PBXBuildFile; fileRef = EE247AD61C3F04ED00AE3E12 /* SQLite.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		3D67B3FD1DB2472D00A4F4C6 /* fts3_tokenizer.h in Headers */ = {isa = PBXBuildFile; fileRef = EE247AF01C3F06E900AE3E12 /* fts3_tokenizer.h */; };
+		3DDC112F26CDBA0200CE369F /* SQLiteObjc.h in Headers */ = {isa = PBXBuildFile; fileRef = 3DDC112E26CDBA0200CE369F /* SQLiteObjc.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		3DDC113626CDBE1900CE369F /* SQLiteObjc.h in Headers */ = {isa = PBXBuildFile; fileRef = 3DDC112E26CDBA0200CE369F /* SQLiteObjc.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		3DDC113726CDBE1900CE369F /* SQLiteObjc.h in Headers */ = {isa = PBXBuildFile; fileRef = 3DDC112E26CDBA0200CE369F /* SQLiteObjc.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		3DDC113826CDBE1C00CE369F /* SQLiteObjc.h in Headers */ = {isa = PBXBuildFile; fileRef = 3DDC112E26CDBA0200CE369F /* SQLiteObjc.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		49EB68C41F7B3CB400D89D40 /* Coding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49EB68C31F7B3CB400D89D40 /* Coding.swift */; };
 		49EB68C51F7B3CB400D89D40 /* Coding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49EB68C31F7B3CB400D89D40 /* Coding.swift */; };
 		49EB68C61F7B3CB400D89D40 /* Coding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49EB68C31F7B3CB400D89D40 /* Coding.swift */; };
@@ -183,8 +185,6 @@
 		EE247B731C3F3FEC00AE3E12 /* Query.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B001C3F06E900AE3E12 /* Query.swift */; };
 		EE247B741C3F3FEC00AE3E12 /* Schema.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B011C3F06E900AE3E12 /* Schema.swift */; };
 		EE247B751C3F3FEC00AE3E12 /* Setter.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B021C3F06E900AE3E12 /* Setter.swift */; };
-		EE91808E1C46E5230038162A /* SQLiteObjc.h in Headers */ = {isa = PBXBuildFile; fileRef = EE91808D1C46E5230038162A /* SQLiteObjc.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		EE91808F1C46E76D0038162A /* SQLiteObjc.h in Headers */ = {isa = PBXBuildFile; fileRef = EE91808D1C46E5230038162A /* SQLiteObjc.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		EE9180941C46EA210038162A /* libsqlite3.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = EE9180931C46EA210038162A /* libsqlite3.tbd */; };
 		EE9180951C46EBCC0038162A /* libsqlite3.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = EE9180911C46E9D30038162A /* libsqlite3.tbd */; };
 /* End PBXBuildFile section */
@@ -229,6 +229,7 @@
 		19A17BA55DABB480F9020C8A /* DateAndTimeFunctions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DateAndTimeFunctions.swift; sourceTree = "<group>"; };
 		19A17E2695737FAB5D6086E3 /* fixtures */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = folder; path = fixtures; sourceTree = "<group>"; };
 		3D67B3E51DB2469200A4F4C6 /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = Platforms/WatchOS.platform/Developer/SDKs/WatchOS3.0.sdk/usr/lib/libsqlite3.tbd; sourceTree = DEVELOPER_DIR; };
+		3DDC112E26CDBA0200CE369F /* SQLiteObjc.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SQLiteObjc.h; path = ../SQLiteObjc/include/SQLiteObjc.h; sourceTree = "<group>"; };
 		49EB68C31F7B3CB400D89D40 /* Coding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Coding.swift; sourceTree = "<group>"; };
 		A121AC451CA35C79005A31D1 /* SQLite.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SQLite.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		D4DB368A20C09C9B00D5A58E /* SelectTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectTests.swift; sourceTree = "<group>"; };
@@ -280,7 +281,6 @@
 		EE247B911C3F822500AE3E12 /* installation@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "installation@2x.png"; sourceTree = "<group>"; };
 		EE247B921C3F822600AE3E12 /* playground@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "playground@2x.png"; sourceTree = "<group>"; };
 		EE247B931C3F826100AE3E12 /* SQLite.swift.podspec */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; path = SQLite.swift.podspec; sourceTree = "<group>"; };
-		EE91808D1C46E5230038162A /* SQLiteObjc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SQLiteObjc.h; path = ../../SQLiteObjc/SQLiteObjc.h; sourceTree = "<group>"; };
 		EE9180911C46E9D30038162A /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/usr/lib/libsqlite3.tbd; sourceTree = DEVELOPER_DIR; };
 		EE9180931C46EA210038162A /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = usr/lib/libsqlite3.tbd; sourceTree = SDKROOT; };
 /* End PBXFileReference section */
@@ -384,6 +384,7 @@
 			isa = PBXGroup;
 			children = (
 				EE247AD61C3F04ED00AE3E12 /* SQLite.h */,
+				3DDC112E26CDBA0200CE369F /* SQLiteObjc.h */,
 				EE247AF71C3F06E900AE3E12 /* Foundation.swift */,
 				EE247AF81C3F06E900AE3E12 /* Helpers.swift */,
 				EE247AD81C3F04ED00AE3E12 /* Info.plist */,
@@ -430,7 +431,6 @@
 		EE247AED1C3F06E900AE3E12 /* Core */ = {
 			isa = PBXGroup;
 			children = (
-				EE91808D1C46E5230038162A /* SQLiteObjc.h */,
 				EE247AEE1C3F06E900AE3E12 /* Blob.swift */,
 				EE247AEF1C3F06E900AE3E12 /* Connection.swift */,
 				EE247AF01C3F06E900AE3E12 /* fts3_tokenizer.h */,
@@ -512,7 +512,7 @@
 			buildActionMask = 2147483647;
 			files = (
 				03A65E781C6BB2EA0062603F /* fts3_tokenizer.h in Headers */,
-				03A65E751C6BB2DF0062603F /* SQLiteObjc.h in Headers */,
+				3DDC113626CDBE1900CE369F /* SQLiteObjc.h in Headers */,
 				03A65E721C6BB2D30062603F /* SQLite.h in Headers */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
@@ -521,8 +521,8 @@
 			isa = PBXHeadersBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				3D67B3FB1DB2470600A4F4C6 /* SQLiteObjc.h in Headers */,
 				3D67B3FC1DB2471B00A4F4C6 /* SQLite.h in Headers */,
+				3DDC113726CDBE1900CE369F /* SQLiteObjc.h in Headers */,
 				3D67B3FD1DB2472D00A4F4C6 /* fts3_tokenizer.h in Headers */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
@@ -531,8 +531,8 @@
 			isa = PBXHeadersBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				EE91808E1C46E5230038162A /* SQLiteObjc.h in Headers */,
 				EE247B051C3F06E900AE3E12 /* fts3_tokenizer.h in Headers */,
+				3DDC113826CDBE1C00CE369F /* SQLiteObjc.h in Headers */,
 				EE247AD71C3F04ED00AE3E12 /* SQLite.h in Headers */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
@@ -542,8 +542,8 @@
 			buildActionMask = 2147483647;
 			files = (
 				EE247B671C3F3FEC00AE3E12 /* fts3_tokenizer.h in Headers */,
+				3DDC112F26CDBA0200CE369F /* SQLiteObjc.h in Headers */,
 				EE247B621C3F3FDB00AE3E12 /* SQLite.h in Headers */,
-				EE91808F1C46E76D0038162A /* SQLiteObjc.h in Headers */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
diff --git a/Sources/SQLiteObjc/SQLiteObjc.h b/Sources/SQLiteObjc/include/SQLiteObjc.h
similarity index 100%
rename from Sources/SQLiteObjc/SQLiteObjc.h
rename to Sources/SQLiteObjc/include/SQLiteObjc.h

From 4ddcb1cc6fdec3e4dd24d89e35a1f31630d81632 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Thu, 19 Aug 2021 00:12:59 +0200
Subject: [PATCH 033/326] Fix query tests

---
 Tests/SQLiteTests/QueryTests.swift | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/Tests/SQLiteTests/QueryTests.swift b/Tests/SQLiteTests/QueryTests.swift
index 9b845a97..29cbd7e4 100644
--- a/Tests/SQLiteTests/QueryTests.swift
+++ b/Tests/SQLiteTests/QueryTests.swift
@@ -397,7 +397,7 @@ class QueryIntegrationTests : SQLiteTestCase {
     let id = Expression<Int64>("id")
     let email = Expression<String>("email")
     let age = Expression<Int>("age")
-    
+
     override func setUp() {
         super.setUp()
 
@@ -509,16 +509,16 @@ class QueryIntegrationTests : SQLiteTestCase {
         let fetchAge = { () throws -> Int? in
             return try self.db.pluck(self.users.filter(self.email == "alice@example.com")).flatMap { $0[self.age] }
         }
-        
+
         let id = try db.run(users.upsert(email <- "alice@example.com", age <- 30, onConflictOf: email))
         XCTAssertEqual(1, id)
         XCTAssertEqual(30, try fetchAge())
-        
+
         let nextId = try db.run(users.upsert(email <- "alice@example.com", age <- 42, onConflictOf: email))
         XCTAssertEqual(1, nextId)
         XCTAssertEqual(42, try fetchAge())
     }
-    
+
     func test_update() {
         let changes = try! db.run(users.update(email <- "alice@example.com"))
         XCTAssertEqual(0, changes)
@@ -528,24 +528,24 @@ class QueryIntegrationTests : SQLiteTestCase {
         let changes = try! db.run(users.delete())
         XCTAssertEqual(0, changes)
     }
-    
+
     func test_union() throws {
         let expectedIDs = [
             try db.run(users.insert(email <- "alice@example.com")),
             try db.run(users.insert(email <- "sally@example.com"))
         ]
-        
+
         let query1 = users.filter(email == "alice@example.com")
         let query2 = users.filter(email == "sally@example.com")
-        
+
         let actualIDs = try db.prepare(query1.union(query2)).map { $0[id] }
         XCTAssertEqual(expectedIDs, actualIDs)
-        
+
         let query3 = users.select(users[*], Expression<Int>(literal: "1 AS weight")).filter(email == "sally@example.com")
         let query4 = users.select(users[*], Expression<Int>(literal: "2 AS weight")).filter(email == "alice@example.com")
-        
+
         print(query3.union(query4).order(Expression<Int>(literal: "weight")).asSQL())
-        
+
         let orderedIDs = try db.prepare(query3.union(query4).order(Expression<Int>(literal: "weight"), email)).map { $0[id] }
         XCTAssertEqual(Array(expectedIDs.reversed()), orderedIDs)
     }

From 75a177a688ada617cef0efbc910f2fc3bdfbd1ff Mon Sep 17 00:00:00 2001
From: Nathan Fallet <contact@nathanfallet.me>
Date: Sat, 21 Aug 2021 18:03:32 +0200
Subject: [PATCH 034/326] Fixed codable insert many

---
 Tests/SQLiteTests/QueryTests.swift | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/Tests/SQLiteTests/QueryTests.swift b/Tests/SQLiteTests/QueryTests.swift
index 9e6e9f7e..79e6871e 100644
--- a/Tests/SQLiteTests/QueryTests.swift
+++ b/Tests/SQLiteTests/QueryTests.swift
@@ -310,12 +310,12 @@ class QueryTests : XCTestCase {
 
     func test_insert_many_encodable() throws {
         let emails = Table("emails")
-        let value1 = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4, optional: nil, sub: nil)
-        let value2 = TestCodable(int: 2, string: "3", bool: true, float: 3, double: 5, optional: nil, sub: nil)
-        let value3 = TestCodable(int: 3, string: "4", bool: true, float: 3, double: 6, optional: nil, sub: nil)
+        let value1 = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4, date: Date(timeIntervalSince1970: 0), optional: nil, sub: nil)
+        let value2 = TestCodable(int: 2, string: "3", bool: true, float: 3, double: 5, date: Date(timeIntervalSince1970: 0), optional: nil, sub: nil)
+        let value3 = TestCodable(int: 3, string: "4", bool: true, float: 3, double: 6, date: Date(timeIntervalSince1970: 0), optional: nil, sub: nil)
         let insert = try emails.insertMany([value1, value2, value3])
         AssertSQL(
-            "INSERT INTO \"emails\" (\"int\", \"string\", \"bool\", \"float\", \"double\") VALUES (1, '2', 1, 3.0, 4.0), (2, '3', 1, 3.0, 5.0), (3, '4', 1, 3.0, 6.0)",
+            "INSERT INTO \"emails\" (\"int\", \"string\", \"bool\", \"float\", \"double\", \"date\") VALUES (1, '2', 1, 3.0, 4.0, '1970-01-01T00:00:00.000'), (2, '3', 1, 3.0, 5.0, '1970-01-01T00:00:00.000'), (3, '4', 1, 3.0, 6.0, '1970-01-01T00:00:00.000')",
             insert
         )
     }

From 449edebfd6c91c2dd376ee61359b4c4b65032032 Mon Sep 17 00:00:00 2001
From: Nathan Fallet <contact@nathanfallet.me>
Date: Sat, 21 Aug 2021 19:24:45 +0200
Subject: [PATCH 035/326] Updating to 0.13.0

---
 .swift-version       | 2 +-
 SQLite.swift.podspec | 6 +++---
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/.swift-version b/.swift-version
index bf77d549..819e07a2 100644
--- a/.swift-version
+++ b/.swift-version
@@ -1 +1 @@
-4.2
+5.0
diff --git a/SQLite.swift.podspec b/SQLite.swift.podspec
index bcefa5fe..499012c1 100644
--- a/SQLite.swift.podspec
+++ b/SQLite.swift.podspec
@@ -1,6 +1,6 @@
 Pod::Spec.new do |s|
   s.name             = "SQLite.swift"
-  s.version          = "0.12.2"
+  s.version          = "0.13.0"
   s.summary          = "A type-safe, Swift-language layer over SQLite3 for iOS and macOS."
 
   s.description      = <<-DESC
@@ -16,12 +16,12 @@ Pod::Spec.new do |s|
 
   s.module_name      = 'SQLite'
   s.default_subspec  = 'standard'
-  s.swift_versions = ['4.2', '5']
+  s.swift_versions = ['5']
   
   
   ios_deployment_target = '9.0'
   tvos_deployment_target = '9.1'
-  osx_deployment_target = '10.10'
+  osx_deployment_target = '10.15'
   watchos_deployment_target = '3.0'
   
   s.ios.deployment_target = ios_deployment_target

From 8096e9b9c5cc694f373d2176f96889d3e4afe053 Mon Sep 17 00:00:00 2001
From: Nathan Fallet <contact@nathanfallet.me>
Date: Sat, 21 Aug 2021 20:22:10 +0200
Subject: [PATCH 036/326] Deleted .swift-version

---
 .swift-version | 1 -
 1 file changed, 1 deletion(-)
 delete mode 100644 .swift-version

diff --git a/.swift-version b/.swift-version
deleted file mode 100644
index 819e07a2..00000000
--- a/.swift-version
+++ /dev/null
@@ -1 +0,0 @@
-5.0

From 31f667f71f5e6b401a4ec2391d8e44d835e5d7fa Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Sun, 22 Aug 2021 03:13:48 +0200
Subject: [PATCH 037/326] Pod lint fixes

---
 Sources/SQLite/Core/Connection.swift | 82 +++++++++++++---------------
 Tests/SQLiteTests/QueryTests.swift   | 15 ++++-
 2 files changed, 50 insertions(+), 47 deletions(-)

diff --git a/Sources/SQLite/Core/Connection.swift b/Sources/SQLite/Core/Connection.swift
index 1bbf7f73..f4a9e8cb 100644
--- a/Sources/SQLite/Core/Connection.swift
+++ b/Sources/SQLite/Core/Connection.swift
@@ -415,17 +415,17 @@ public final class Connection {
     ///
     ///       db.trace { SQL in print(SQL) }
     public func trace(_ callback: ((String) -> Void)?) {
-        #if SQLITE_SWIFT_SQLCIPHER || os(Linux)
+        if #available(iOS 10.0, OSX 10.12, tvOS 10.0, watchOS 3.0, *) {
+            trace_v2(callback)
+        } else {
             trace_v1(callback)
-        #else
-            if #available(iOS 10.0, OSX 10.12, tvOS 10.0, watchOS 3.0, *) {
-                trace_v2(callback)
-            } else {
-                trace_v1(callback)
-            }
-        #endif
+        }
     }
 
+    @available(OSX, deprecated: 10.2)
+    @available(iOS, deprecated: 10.0)
+    @available(watchOS, deprecated: 3.0)
+    @available(tvOS, deprecated: 10.0)
     fileprivate func trace_v1(_ callback: ((String) -> Void)?) {
         guard let callback = callback else {
             sqlite3_trace(handle, nil /* xCallback */, nil /* pCtx */)
@@ -447,8 +447,36 @@ public final class Connection {
         trace = box
     }
 
+    @available(iOS 10.0, OSX 10.12, tvOS 10.0, watchOS 3.0, *)
+    fileprivate func trace_v2(_ callback: ((String) -> Void)?) {
+        guard let callback = callback else {
+            // If the X callback is NULL or if the M mask is zero, then tracing is disabled.
+            sqlite3_trace_v2(handle, 0 /* mask */, nil /* xCallback */, nil /* pCtx */)
+            trace = nil
+            return
+        }
 
-
+        let box: Trace = { (pointer: UnsafeRawPointer) in
+            callback(String(cString: pointer.assumingMemoryBound(to: UInt8.self)))
+        }
+        sqlite3_trace_v2(handle, UInt32(SQLITE_TRACE_STMT) /* mask */,
+             {
+                 // A trace callback is invoked with four arguments: callback(T,C,P,X).
+                 // The T argument is one of the SQLITE_TRACE constants to indicate why the
+                 // callback was invoked. The C argument is a copy of the context pointer.
+                 // The P and X arguments are pointers whose meanings depend on T.
+                 (T: UInt32, C: UnsafeMutableRawPointer?, P: UnsafeMutableRawPointer?, X: UnsafeMutableRawPointer?) in
+                 if let P = P,
+                    let expandedSQL = sqlite3_expanded_sql(OpaquePointer(P)) {
+                     unsafeBitCast(C, to: Trace.self)(expandedSQL)
+                     sqlite3_free(expandedSQL)
+                 }
+                 return Int32(0) // currently ignored
+             },
+             unsafeBitCast(box, to: UnsafeMutableRawPointer.self) /* pCtx */
+        )
+        trace = box
+    }
 
     fileprivate typealias Trace = @convention(block) (UnsafeRawPointer) -> Void
     fileprivate var trace: Trace?
@@ -712,39 +740,3 @@ extension Result : CustomStringConvertible {
         }
     }
 }
-
-#if !SQLITE_SWIFT_SQLCIPHER && !os(Linux)
-@available(iOS 10.0, OSX 10.12, tvOS 10.0, watchOS 3.0, *)
-extension Connection {
-    fileprivate func trace_v2(_ callback: ((String) -> Void)?) {
-        guard let callback = callback else {
-            // If the X callback is NULL or if the M mask is zero, then tracing is disabled.
-            sqlite3_trace_v2(handle, 0 /* mask */, nil /* xCallback */, nil /* pCtx */)
-            trace = nil
-            return
-        }
-
-        let box: Trace = { (pointer: UnsafeRawPointer) in
-            callback(String(cString: pointer.assumingMemoryBound(to: UInt8.self)))
-        }
-        sqlite3_trace_v2(handle,
-            UInt32(SQLITE_TRACE_STMT) /* mask */,
-            {
-                // A trace callback is invoked with four arguments: callback(T,C,P,X).
-                // The T argument is one of the SQLITE_TRACE constants to indicate why the
-                // callback was invoked. The C argument is a copy of the context pointer.
-                // The P and X arguments are pointers whose meanings depend on T.
-                (T: UInt32, C: UnsafeMutableRawPointer?, P: UnsafeMutableRawPointer?, X: UnsafeMutableRawPointer?) in
-                    if let P = P,
-                       let expandedSQL = sqlite3_expanded_sql(OpaquePointer(P)) {
-                        unsafeBitCast(C, to: Trace.self)(expandedSQL)
-                        sqlite3_free(expandedSQL)
-                    }
-                    return Int32(0) // currently ignored
-            },
-            unsafeBitCast(box, to: UnsafeMutableRawPointer.self) /* pCtx */
-        )
-        trace = box
-    }
-}
-#endif
diff --git a/Tests/SQLiteTests/QueryTests.swift b/Tests/SQLiteTests/QueryTests.swift
index 79e6871e..041b7d0c 100644
--- a/Tests/SQLiteTests/QueryTests.swift
+++ b/Tests/SQLiteTests/QueryTests.swift
@@ -541,10 +541,11 @@ class QueryIntegrationTests : SQLiteTestCase {
         let id = try! db.run(users.insertMany([[email <- "alice@example.com"], [email <- "geoff@example.com"]]))
         XCTAssertEqual(2, id)
     }
-    
+
     func test_upsert() throws {
+        guard db.satisfiesMinimumVersion(minor: 24) else { return }
         let fetchAge = { () throws -> Int? in
-            return try self.db.pluck(self.users.filter(self.email == "alice@example.com")).flatMap { $0[self.age] }
+            try self.db.pluck(self.users.filter(self.email == "alice@example.com")).flatMap { $0[self.age] }
         }
 
         let id = try db.run(users.upsert(email <- "alice@example.com", age <- 30, onConflictOf: email))
@@ -613,3 +614,13 @@ class QueryIntegrationTests : SQLiteTestCase {
         }
     }
 }
+
+private extension Connection {
+    func satisfiesMinimumVersion(minor: Int, patch: Int = 0) -> Bool {
+        guard let version = try? scalar("SELECT sqlite_version()") as? String else { return false }
+        let components = version.split(separator: ".", maxSplits: 3).compactMap { Int($0) }
+        guard components.count == 3 else { return false }
+
+        return components[1] >= minor && components[2] >= patch
+    }
+}

From af4801483adf9fa26fa4e80df120b1913b380652 Mon Sep 17 00:00:00 2001
From: Nathan Fallet <contact@nathanfallet.me>
Date: Sun, 22 Aug 2021 17:11:01 +0200
Subject: [PATCH 038/326] Adding a `vacuum()` fonction to `Connection`

---
 Sources/SQLite/Core/Connection.swift    | 11 +++++++++++
 Tests/SQLiteTests/ConnectionTests.swift |  4 ++++
 2 files changed, 15 insertions(+)

diff --git a/Sources/SQLite/Core/Connection.swift b/Sources/SQLite/Core/Connection.swift
index f4a9e8cb..8fd07cc1 100644
--- a/Sources/SQLite/Core/Connection.swift
+++ b/Sources/SQLite/Core/Connection.swift
@@ -252,6 +252,17 @@ public final class Connection {
     @discardableResult public func run(_ statement: String, _ bindings: [String: Binding?]) throws -> Statement {
         return try prepare(statement).run(bindings)
     }
+    
+    // MARK: - VACUUM
+    
+    /// Run a vacuum on the database
+    ///
+    /// - Throws: `Result.Error` if query execution fails.
+    ///
+    /// - Returns: The statement.
+    @discardableResult public func vacuum() throws -> Statement {
+        return try run("VACUUM")
+    }
 
     // MARK: - Scalar
 
diff --git a/Tests/SQLiteTests/ConnectionTests.swift b/Tests/SQLiteTests/ConnectionTests.swift
index fdfc9637..a05cceb5 100644
--- a/Tests/SQLiteTests/ConnectionTests.swift
+++ b/Tests/SQLiteTests/ConnectionTests.swift
@@ -111,6 +111,10 @@ class ConnectionTests : SQLiteTestCase {
         try! db.run("SELECT * FROM users WHERE admin = $admin", ["$admin": 0])
         AssertSQL("SELECT * FROM users WHERE admin = 0", 4)
     }
+    
+    func test_vacuum() {
+        try! db.vacuum()
+    }
 
     func test_scalar_preparesRunsAndReturnsScalarValues() {
         XCTAssertEqual(0, try! db.scalar("SELECT count(*) FROM users WHERE admin = 0") as? Int64)

From e48d1469f7fda8175de4abdc45638a25e3409f6b Mon Sep 17 00:00:00 2001
From: Nathan Fallet <contact@nathanfallet.me>
Date: Sun, 22 Aug 2021 17:28:37 +0200
Subject: [PATCH 039/326] SPM as first installation method

---
 Documentation/Index.md | 51 +++++++++++++++++++++---------------------
 1 file changed, 25 insertions(+), 26 deletions(-)

diff --git a/Documentation/Index.md b/Documentation/Index.md
index bc8bb52e..14b29b37 100644
--- a/Documentation/Index.md
+++ b/Documentation/Index.md
@@ -1,9 +1,9 @@
 # SQLite.swift Documentation
 
   - [Installation](#installation)
+    - [Swift Package Manager](#swift-package-manager)
     - [Carthage](#carthage)
     - [CocoaPods](#cocoapods)
-    - [Swift Package Manager](#swift-package-manager)
     - [Manual](#manual)
   - [Getting Started](#getting-started)
     - [Connecting to a Database](#connecting-to-a-database)
@@ -71,6 +71,30 @@
 > _Note:_ SQLite.swift requires Swift 5 (and
 > [Xcode 10.2](https://developer.apple.com/xcode/downloads/)) or greater.
 
+### Swift Package Manager
+
+The [Swift Package Manager][] is a tool for managing the distribution of
+Swift code. It’s integrated with the Swift build system to automate the
+process of downloading, compiling, and linking dependencies.
+
+It is the recommended approach for using SQLite.swift in OSX CLI
+applications.
+
+ 1. Add the following to your `Package.swift` file:
+
+  ```swift
+  dependencies: [
+    .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.12.0")
+  ]
+  ```
+
+ 2. Build your project:
+
+  ```sh
+  $ swift build
+  ```
+
+[Swift Package Manager]: https://swift.org/package-manager
 
 ### Carthage
 
@@ -169,31 +193,6 @@ try db.rekey("another secret")
 [sqlite3pod]: https://github.com/clemensg/sqlite3pod
 [SQLCipher]: https://www.zetetic.net/sqlcipher/
 
-### Swift Package Manager
-
-The [Swift Package Manager][] is a tool for managing the distribution of
-Swift code. It’s integrated with the Swift build system to automate the
-process of downloading, compiling, and linking dependencies.
-
-It is the recommended approach for using SQLite.swift in OSX CLI
-applications.
-
- 1. Add the following to your `Package.swift` file:
-
-  ```swift
-  dependencies: [
-    .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.12.0")
-  ]
-  ```
-
- 2. Build your project:
-
-  ```sh
-  $ swift build
-  ```
-
-[Swift Package Manager]: https://swift.org/package-manager
-
 ### Manual
 
 To install SQLite.swift as an Xcode sub-project:

From c648408e467e84d9d2e191fe354080fde23b980c Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Sun, 22 Aug 2021 21:14:27 +0200
Subject: [PATCH 040/326] Ensure file can be cleaned up

---
 Tests/SQLiteTests/CipherTests.swift | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/Tests/SQLiteTests/CipherTests.swift b/Tests/SQLiteTests/CipherTests.swift
index abecffba..a27ac17d 100644
--- a/Tests/SQLiteTests/CipherTests.swift
+++ b/Tests/SQLiteTests/CipherTests.swift
@@ -88,6 +88,11 @@ class CipherTests: XCTestCase {
         try! FileManager.default.setAttributes([FileAttributeKey.immutable : 1], ofItemAtPath: encryptedFile)
         XCTAssertFalse(FileManager.default.isWritableFile(atPath: encryptedFile))
 
+        defer {
+            // ensure file can be cleaned up afterwards
+            try! FileManager.default.setAttributes([FileAttributeKey.immutable : 0], ofItemAtPath: encryptedFile)
+        }
+
         let conn = try! Connection(encryptedFile)
         try! conn.key("sqlcipher-test")
         XCTAssertEqual(1, try! conn.scalar("SELECT count(*) FROM foo") as? Int64)

From 368c873e75cc3de9e39464ff82a09fc65649607e Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Sun, 22 Aug 2021 21:42:01 +0200
Subject: [PATCH 041/326] Update CHANGELOG

---
 CHANGELOG.md | 25 +++++++++++++++++++------
 1 file changed, 19 insertions(+), 6 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1f29afeb..465438bc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,12 +1,22 @@
-0.12.3 (18-05-2021), [diff][diff-0.12.2]
+0.13.0 (22-08-2021), [diff][diff-0.13.0]
 ========================================
 
-* Swift 5.3 support.
-* Xcode 12.5 support.
-* Bumps minimum deployment versions.
-* Fixes up Package.swift to build SQLiteObjc module.
+* Swift 5.3 support
+* Xcode 12.5 support
+* Bumps minimum deployment versions
+* Fixes up Package.swift to build SQLiteObjc module
 
-0.11.6 (xxx), [diff][diff-0.11.6]
+0.12.1, 0.12.2 (21-06-2019) [diff][diff-0.12.2]
+========================================
+
+* CocoaPods modular headers support
+
+0.12.0 (24-04-2019) [diff][diff-0.12.0]
+========================================
+
+* Version with Swift 5 Support
+
+0.11.6 (19-04-2019), [diff][diff-0.11.6]
 ========================================
 
 * Swift 4.2, SQLCipher 4.x ([#866][])
@@ -71,6 +81,9 @@
 [diff-0.11.4]: https://github.com/stephencelis/SQLite.swift/compare/0.11.3...0.11.4
 [diff-0.11.5]: https://github.com/stephencelis/SQLite.swift/compare/0.11.4...0.11.5
 [diff-0.11.6]: https://github.com/stephencelis/SQLite.swift/compare/0.11.5...0.11.6
+[diff-0.12.0]: https://github.com/stephencelis/SQLite.swift/compare/0.11.6...0.12.0
+[diff-0.12.2]: https://github.com/stephencelis/SQLite.swift/compare/0.12.0...0.12.2
+[diff-0.13.0]: https://github.com/stephencelis/SQLite.swift/compare/0.12.2...0.13.0
 
 [#142]: https://github.com/stephencelis/SQLite.swift/issues/142
 [#315]: https://github.com/stephencelis/SQLite.swift/issues/315

From d27a078f24164c58827f3adba612415b10016190 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Sun, 22 Aug 2021 21:53:16 +0200
Subject: [PATCH 042/326] Update version

---
 README.md | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/README.md b/README.md
index 81fb4b5d..98a625c4 100644
--- a/README.md
+++ b/README.md
@@ -135,7 +135,7 @@ Swift code.
 
   ```swift
   dependencies: [
-      .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.12.0")
+      .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.13.0")
   ]
   ```
 
@@ -157,7 +157,7 @@ install SQLite.swift with Carthage:
  2. Update your Cartfile to include the following:
 
     ```ruby
-    github "stephencelis/SQLite.swift" ~> 0.12.0
+    github "stephencelis/SQLite.swift" ~> 0.13.0
     ```
 
  3. Run `carthage update` and
@@ -189,7 +189,7 @@ SQLite.swift with CocoaPods:
     use_frameworks!
 
     target 'YourAppTargetName' do
-        pod 'SQLite.swift', '~> 0.12.0'
+        pod 'SQLite.swift', '~> 0.13.0'
     end
     ```
 

From 9af51e2edf491c0ea632e369a6566e09b65aa333 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Sun, 22 Aug 2021 22:09:47 +0200
Subject: [PATCH 043/326] Bump version

---
 Documentation/Index.md           | 12 ++++++------
 SQLite.xcodeproj/project.pbxproj |  4 ++--
 2 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/Documentation/Index.md b/Documentation/Index.md
index 14b29b37..21b1c995 100644
--- a/Documentation/Index.md
+++ b/Documentation/Index.md
@@ -84,7 +84,7 @@ applications.
 
   ```swift
   dependencies: [
-    .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.12.0")
+    .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.13.0")
   ]
   ```
 
@@ -105,7 +105,7 @@ install SQLite.swift with Carthage:
  2. Update your Cartfile to include the following:
 
     ```ruby
-    github "stephencelis/SQLite.swift" ~> 0.12.0
+    github "stephencelis/SQLite.swift" ~> 0.13.0
     ```
 
  3. Run `carthage update` and [add the appropriate framework][Carthage Usage].
@@ -135,7 +135,7 @@ install SQLite.swift with Carthage:
     use_frameworks!
 
     target 'YourAppTargetName' do
-        pod 'SQLite.swift', '~> 0.12.0'
+        pod 'SQLite.swift', '~> 0.13.0'
     end
     ```
 
@@ -149,7 +149,7 @@ with the OS you can require the `standalone` subspec:
 
 ```ruby
 target 'YourAppTargetName' do
-  pod 'SQLite.swift/standalone', '~> 0.12.0'
+  pod 'SQLite.swift/standalone', '~> 0.13.0'
 end
 ```
 
@@ -159,7 +159,7 @@ dependency to sqlite3 or one of its subspecs:
 
 ```ruby
 target 'YourAppTargetName' do
-  pod 'SQLite.swift/standalone', '~> 0.12.0'
+  pod 'SQLite.swift/standalone', '~> 0.13.0'
   pod 'sqlite3/fts5', '= 3.15.0'  # SQLite 3.15.0 with FTS5 enabled
 end
 ```
@@ -173,7 +173,7 @@ If you want to use [SQLCipher][] with SQLite.swift you can require the
 
 ```ruby
 target 'YourAppTargetName' do
-  pod 'SQLite.swift/SQLCipher', '~> 0.12.0'
+  pod 'SQLite.swift/SQLCipher', '~> 0.13.0'
 end
 ```
 
diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj
index a730284d..3056bd74 100644
--- a/SQLite.xcodeproj/project.pbxproj
+++ b/SQLite.xcodeproj/project.pbxproj
@@ -1265,7 +1265,7 @@
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
 				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
-				MARKETING_VERSION = 0.12.3;
+				MARKETING_VERSION = 0.13.0;
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
 				PRODUCT_NAME = SQLite;
 				SKIP_INSTALL = YES;
@@ -1287,7 +1287,7 @@
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
 				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
-				MARKETING_VERSION = 0.12.3;
+				MARKETING_VERSION = 0.13.0;
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
 				PRODUCT_NAME = SQLite;
 				SKIP_INSTALL = YES;

From 474f966e1159fb7b14e9ca20e891f980a4f9e84c Mon Sep 17 00:00:00 2001
From: Nathan Fallet <contact@nathanfallet.me>
Date: Mon, 23 Aug 2021 11:05:13 +0200
Subject: [PATCH 044/326] Start GitHub Actions workflow


From 1e5ab952f37e3fb8fb6759dc14c151c618395c23 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Mon, 23 Aug 2021 01:18:03 +0200
Subject: [PATCH 045/326] Add linting

---
 .github/workflows/build.yml                   |   3 +
 .swiftlint.yml                                |  32 ++
 Makefile                                      |   3 +
 Sources/SQLite/Core/Blob.swift                |   4 +-
 Sources/SQLite/Core/Connection.swift          |  42 +-
 Sources/SQLite/Core/Statement.swift           |  17 +-
 Sources/SQLite/Core/Value.swift               |  18 +-
 Sources/SQLite/Extensions/Cipher.swift        |   4 +-
 Sources/SQLite/Extensions/FTS4.swift          |  19 +-
 Sources/SQLite/Extensions/FTS5.swift          |   4 +-
 Sources/SQLite/Extensions/RTree.swift         |   6 +-
 Sources/SQLite/Foundation.swift               |   6 +-
 Sources/SQLite/Helpers.swift                  |   9 +-
 Sources/SQLite/Typed/AggregateFunctions.swift |  14 +-
 Sources/SQLite/Typed/Coding.swift             | 119 ++--
 Sources/SQLite/Typed/Collation.swift          |   6 +-
 Sources/SQLite/Typed/CoreFunctions.swift      |  28 +-
 Sources/SQLite/Typed/CustomFunctions.swift    |  58 +-
 Sources/SQLite/Typed/Expression.swift         |  12 +-
 Sources/SQLite/Typed/Operators.swift          | 328 +++++------
 Sources/SQLite/Typed/Query.swift              |  84 ++-
 Sources/SQLite/Typed/Schema.swift             | 172 ++++--
 Sources/SQLite/Typed/Setter.swift             | 130 ++---
 Tests/.swiftlint.yml                          |  20 +
 .../SQLiteTests/AggregateFunctionsTests.swift |  78 +--
 Tests/SQLiteTests/BlobTests.swift             |   2 +-
 Tests/SQLiteTests/CipherTests.swift           |   6 +-
 Tests/SQLiteTests/ConnectionTests.swift       | 113 ++--
 Tests/SQLiteTests/CoreFunctionsTests.swift    | 146 ++---
 Tests/SQLiteTests/CustomFunctionsTests.swift  |  16 +-
 .../DateAndTimeFunctionTests.swift            |  42 +-
 Tests/SQLiteTests/ExpressionTests.swift       |   3 +-
 Tests/SQLiteTests/FTS4Tests.swift             |  51 +-
 Tests/SQLiteTests/FTS5Tests.swift             |   5 +-
 Tests/SQLiteTests/FoundationTests.swift       |   2 +-
 Tests/SQLiteTests/OperatorsTests.swift        | 530 +++++++++---------
 Tests/SQLiteTests/QueryTests.swift            | 263 +++++----
 Tests/SQLiteTests/RTreeTests.swift            |   4 +-
 Tests/SQLiteTests/RowTests.swift              |   2 +-
 Tests/SQLiteTests/SchemaTests.swift           |  98 +++-
 Tests/SQLiteTests/SelectTests.swift           |  21 +-
 Tests/SQLiteTests/SetterTests.swift           | 180 +++---
 Tests/SQLiteTests/StatementTests.swift        |   6 +-
 Tests/SQLiteTests/TestHelpers.swift           |  29 +-
 Tests/SQLiteTests/ValueTests.swift            |   2 +-
 45 files changed, 1509 insertions(+), 1228 deletions(-)
 create mode 100644 .swiftlint.yml
 create mode 100644 Tests/.swiftlint.yml

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 1c47e3a6..0b9d0cf1 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -14,6 +14,9 @@ jobs:
         gem install xcpretty --no-document
         brew update
         brew outdated carthage || brew upgrade carthage
+        brew install swiftlint
+    - name: "Lint"
+      run: make lint
     - name: "Run tests (BUILD_SCHEME: SQLite iOS)"
       env:
         BUILD_SCHEME: SQLite iOS
diff --git a/.swiftlint.yml b/.swiftlint.yml
new file mode 100644
index 00000000..e18c09a1
--- /dev/null
+++ b/.swiftlint.yml
@@ -0,0 +1,32 @@
+disabled_rules: # rule identifiers to exclude from running
+  - todo
+  - operator_whitespace
+  - large_tuple
+  - closure_parameter_position
+included: # paths to include during linting. `--path` is ignored if present. takes precendence over `excluded`.
+  - Sources
+  - Tests
+excluded: # paths to ignore during linting. overridden by `included`.
+
+identifier_name:
+  excluded:
+    - db
+    - to
+    - by
+    - or
+    - eq
+    - gt
+    - lt
+    - fn
+    - a
+    - b
+
+line_length:
+  warning: 150
+  error: 150
+  ignores_comments: true
+
+
+file_length:
+  warning: 700
+  error: 700
diff --git a/Makefile b/Makefile
index f3f355ab..5e4ff388 100644
--- a/Makefile
+++ b/Makefile
@@ -18,6 +18,9 @@ default: test
 build:
 	$(BUILD_TOOL) $(BUILD_ARGUMENTS)
 
+lint:
+	swiftlint
+
 test:
 ifdef XCPRETTY
 	@set -o pipefail && $(BUILD_TOOL) $(BUILD_ARGUMENTS) $(TEST_ACTIONS) | $(XCPRETTY) -c
diff --git a/Sources/SQLite/Core/Blob.swift b/Sources/SQLite/Core/Blob.swift
index 2f5d2a14..a49ef23b 100644
--- a/Sources/SQLite/Core/Blob.swift
+++ b/Sources/SQLite/Core/Blob.swift
@@ -43,7 +43,7 @@ public struct Blob {
 
 }
 
-extension Blob : CustomStringConvertible {
+extension Blob: CustomStringConvertible {
 
     public var description: String {
         return "x'\(toHex())'"
@@ -51,7 +51,7 @@ extension Blob : CustomStringConvertible {
 
 }
 
-extension Blob : Equatable {
+extension Blob: Equatable {
 
 }
 
diff --git a/Sources/SQLite/Core/Connection.swift b/Sources/SQLite/Core/Connection.swift
index 9273b36a..a4dad4a6 100644
--- a/Sources/SQLite/Core/Connection.swift
+++ b/Sources/SQLite/Core/Connection.swift
@@ -70,7 +70,7 @@ public final class Connection {
         /// A DELETE operation.
         case delete
 
-        fileprivate init(rawValue:Int32) {
+        fileprivate init(rawValue: Int32) {
             switch rawValue {
             case SQLITE_INSERT:
                 self = .insert
@@ -86,7 +86,7 @@ public final class Connection {
 
     public var handle: OpaquePointer { return _handle! }
 
-    fileprivate var _handle: OpaquePointer? = nil
+    fileprivate var _handle: OpaquePointer?
 
     /// Initializes a new SQLite connection.
     ///
@@ -252,9 +252,9 @@ public final class Connection {
     @discardableResult public func run(_ statement: String, _ bindings: [String: Binding?]) throws -> Statement {
         return try prepare(statement).run(bindings)
     }
-    
+
     // MARK: - VACUUM
-    
+
     /// Run a vacuum on the database
     ///
     /// - Throws: `Result.Error` if query execution fails.
@@ -311,7 +311,7 @@ public final class Connection {
     // MARK: - Transactions
 
     /// The mode in which a transaction acquires a lock.
-    public enum TransactionMode : String {
+    public enum TransactionMode: String {
 
         /// Defers locking the database till the first read/write executes.
         case deferred = "DEFERRED"
@@ -446,11 +446,9 @@ public final class Connection {
         let box: Trace = { (pointer: UnsafeRawPointer) in
             callback(String(cString: pointer.assumingMemoryBound(to: UInt8.self)))
         }
-        sqlite3_trace(handle,
-            {
-                (C: UnsafeMutableRawPointer?, SQL: UnsafePointer<Int8>?) in
-                    if let C = C, let SQL = SQL {
-                        unsafeBitCast(C, to: Trace.self)(SQL)
+        sqlite3_trace(handle, { (context: UnsafeMutableRawPointer?, SQL: UnsafePointer<Int8>?) in
+                    if let context = context, let SQL = SQL {
+                        unsafeBitCast(context, to: Trace.self)(SQL)
                     }
             },
             unsafeBitCast(box, to: UnsafeMutableRawPointer.self)
@@ -470,13 +468,12 @@ public final class Connection {
         let box: Trace = { (pointer: UnsafeRawPointer) in
             callback(String(cString: pointer.assumingMemoryBound(to: UInt8.self)))
         }
-        sqlite3_trace_v2(handle, UInt32(SQLITE_TRACE_STMT) /* mask */,
-             {
+        sqlite3_trace_v2(handle, UInt32(SQLITE_TRACE_STMT) /* mask */, {
                  // A trace callback is invoked with four arguments: callback(T,C,P,X).
                  // The T argument is one of the SQLITE_TRACE constants to indicate why the
                  // callback was invoked. The C argument is a copy of the context pointer.
                  // The P and X arguments are pointers whose meanings depend on T.
-                 (T: UInt32, C: UnsafeMutableRawPointer?, P: UnsafeMutableRawPointer?, X: UnsafeMutableRawPointer?) in
+                 (_: UInt32, C: UnsafeMutableRawPointer?, P: UnsafeMutableRawPointer?, _: UnsafeMutableRawPointer?) in
                  if let P = P,
                     let expandedSQL = sqlite3_expanded_sql(OpaquePointer(P)) {
                      unsafeBitCast(C, to: Trace.self)(expandedSQL)
@@ -588,7 +585,9 @@ public final class Connection {
     ///   - block: A block of code to run when the function is called. The block
     ///     is called with an array of raw SQL values mapped to the function’s
     ///     parameters and should return a raw SQL value (or nil).
-    public func createFunction(_ function: String, argumentCount: UInt? = nil, deterministic: Bool = false, _ block: @escaping (_ args: [Binding?]) -> Binding?) {
+    // swiftlint:disable:next cyclomatic_complexity
+    public func createFunction(_ function: String, argumentCount: UInt? = nil, deterministic: Bool = false,
+                               _ block: @escaping (_ args: [Binding?]) -> Binding?) {
         let argc = argumentCount.map { Int($0) } ?? -1
         let box: Function = { context, argc, argv in
             let arguments: [Binding?] = (0..<Int(argc)).map { idx in
@@ -629,7 +628,8 @@ public final class Connection {
             flags |= SQLITE_DETERMINISTIC
         }
         #endif
-        sqlite3_create_function_v2(handle, function, Int32(argc), flags, unsafeBitCast(box, to: UnsafeMutableRawPointer.self), { context, argc, value in
+        sqlite3_create_function_v2(handle, function, Int32(argc), flags,
+                                   unsafeBitCast(box, to: UnsafeMutableRawPointer.self), { context, argc, value in
             let function = unsafeBitCast(sqlite3_user_data(context), to: Function.self)
             function(context, argc, value)
         }, nil, nil, nil)
@@ -768,8 +768,8 @@ public final class Connection {
             return Int32(block(lstr, rstr).rawValue)
         }
         try check(sqlite3_create_collation_v2(handle, collation, SQLITE_UTF8,
-            unsafeBitCast(box, to: UnsafeMutableRawPointer.self),
-            { (callback: UnsafeMutableRawPointer?, _, lhs: UnsafeRawPointer?, _, rhs: UnsafeRawPointer?) in /* xCompare */
+            unsafeBitCast(box, to: UnsafeMutableRawPointer.self), { (callback: UnsafeMutableRawPointer?, _,
+                                                                     lhs: UnsafeRawPointer?, _, rhs: UnsafeRawPointer?) in /* xCompare */
             if let lhs = lhs, let rhs = rhs {
                 return unsafeBitCast(callback, to: Collation.self)(lhs, rhs)
             } else {
@@ -807,7 +807,7 @@ public final class Connection {
 
 }
 
-extension Connection : CustomStringConvertible {
+extension Connection: CustomStringConvertible {
 
     public var description: String {
         return String(cString: sqlite3_db_filename(handle, nil))
@@ -815,7 +815,7 @@ extension Connection : CustomStringConvertible {
 
 }
 
-extension Connection.Location : CustomStringConvertible {
+extension Connection.Location: CustomStringConvertible {
 
     public var description: String {
         switch self {
@@ -830,7 +830,7 @@ extension Connection.Location : CustomStringConvertible {
 
 }
 
-public enum Result : Error {
+public enum Result: Error {
 
     fileprivate static let successCodes: Set = [SQLITE_OK, SQLITE_ROW, SQLITE_DONE]
 
@@ -852,7 +852,7 @@ public enum Result : Error {
 
 }
 
-extension Result : CustomStringConvertible {
+extension Result: CustomStringConvertible {
 
     public var description: String {
         switch self {
diff --git a/Sources/SQLite/Core/Statement.swift b/Sources/SQLite/Core/Statement.swift
index 237d4812..dfb67d22 100644
--- a/Sources/SQLite/Core/Statement.swift
+++ b/Sources/SQLite/Core/Statement.swift
@@ -35,7 +35,7 @@ import SQLite3
 /// A single SQL statement.
 public final class Statement {
 
-    fileprivate var handle: OpaquePointer? = nil
+    fileprivate var handle: OpaquePointer?
 
     fileprivate let connection: Connection
 
@@ -173,7 +173,6 @@ public final class Statement {
         return try bind(bindings).scalar()
     }
 
-
     /// - Parameter bindings: A dictionary of named parameters to bind to the
     ///   statement.
     ///
@@ -188,12 +187,12 @@ public final class Statement {
 
     fileprivate func reset(clearBindings shouldClear: Bool = true) {
         sqlite3_reset(handle)
-        if (shouldClear) { sqlite3_clear_bindings(handle) }
+        if shouldClear { sqlite3_clear_bindings(handle) }
     }
 
 }
 
-extension Statement : Sequence {
+extension Statement: Sequence {
 
     public func makeIterator() -> Statement {
         reset(clearBindings: false)
@@ -202,7 +201,7 @@ extension Statement : Sequence {
 
 }
 
-public protocol FailableIterator : IteratorProtocol {
+public protocol FailableIterator: IteratorProtocol {
     func failableNext() throws -> Self.Element?
 }
 
@@ -221,14 +220,14 @@ extension Array {
     }
 }
 
-extension Statement : FailableIterator {
+extension Statement: FailableIterator {
     public typealias Element = [Binding?]
     public func failableNext() throws -> [Binding?]? {
         return try step() ? Array(row) : nil
     }
 }
 
-extension Statement : CustomStringConvertible {
+extension Statement: CustomStringConvertible {
 
     public var description: String {
         return String(cString: sqlite3_sql(handle))
@@ -283,7 +282,7 @@ public struct Cursor {
 }
 
 /// Cursors provide direct access to a statement’s current row.
-extension Cursor : Sequence {
+extension Cursor: Sequence {
 
     public subscript(idx: Int) -> Binding? {
         switch sqlite3_column_type(handle, Int32(idx)) {
@@ -306,7 +305,7 @@ extension Cursor : Sequence {
         var idx = 0
         return AnyIterator {
             if idx >= self.columnCount {
-                return Optional<Binding?>.none
+                return Binding??.none
             } else {
                 idx += 1
                 return self[idx - 1]
diff --git a/Sources/SQLite/Core/Value.swift b/Sources/SQLite/Core/Value.swift
index 608f0ce6..b8686eab 100644
--- a/Sources/SQLite/Core/Value.swift
+++ b/Sources/SQLite/Core/Value.swift
@@ -29,13 +29,13 @@
 ///   protocol, instead.
 public protocol Binding {}
 
-public protocol Number : Binding {}
+public protocol Number: Binding {}
 
-public protocol Value : Expressible { // extensions cannot have inheritance clauses
+public protocol Value: Expressible { // extensions cannot have inheritance clauses
 
     associatedtype ValueType = Self
 
-    associatedtype Datatype : Binding
+    associatedtype Datatype: Binding
 
     static var declaredDatatype: String { get }
 
@@ -45,7 +45,7 @@ public protocol Value : Expressible { // extensions cannot have inheritance clau
 
 }
 
-extension Double : Number, Value {
+extension Double: Number, Value {
 
     public static let declaredDatatype = "REAL"
 
@@ -59,7 +59,7 @@ extension Double : Number, Value {
 
 }
 
-extension Int64 : Number, Value {
+extension Int64: Number, Value {
 
     public static let declaredDatatype = "INTEGER"
 
@@ -73,7 +73,7 @@ extension Int64 : Number, Value {
 
 }
 
-extension String : Binding, Value {
+extension String: Binding, Value {
 
     public static let declaredDatatype = "TEXT"
 
@@ -87,7 +87,7 @@ extension String : Binding, Value {
 
 }
 
-extension Blob : Binding, Value {
+extension Blob: Binding, Value {
 
     public static let declaredDatatype = "BLOB"
 
@@ -103,7 +103,7 @@ extension Blob : Binding, Value {
 
 // MARK: -
 
-extension Bool : Binding, Value {
+extension Bool: Binding, Value {
 
     public static var declaredDatatype = Int64.declaredDatatype
 
@@ -117,7 +117,7 @@ extension Bool : Binding, Value {
 
 }
 
-extension Int : Number, Value {
+extension Int: Number, Value {
 
     public static var declaredDatatype = Int64.declaredDatatype
 
diff --git a/Sources/SQLite/Extensions/Cipher.swift b/Sources/SQLite/Extensions/Cipher.swift
index 44919aab..9fce42f7 100644
--- a/Sources/SQLite/Extensions/Cipher.swift
+++ b/Sources/SQLite/Extensions/Cipher.swift
@@ -1,7 +1,6 @@
 #if SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
 
-
 /// Extension methods for [SQLCipher](https://www.zetetic.net/sqlcipher/).
 /// @see [sqlcipher api](https://www.zetetic.net/sqlcipher/sqlcipher-api/)
 extension Connection {
@@ -32,7 +31,6 @@ extension Connection {
         try _key_v2(db: db, keyPointer: key.bytes, keySize: key.bytes.count)
     }
 
-
     /// Change the key on an open database.  If the current database is not encrypted, this routine
     /// will encrypt it.
     /// To change the key on an existing encrypted database, it must first be unlocked with the
@@ -60,7 +58,7 @@ extension Connection {
     // the key provided is incorrect. To test that the database can be successfully opened with the
     // provided key, it is necessary to perform some operation on the database (i.e. read from it).
     private func cipher_key_check() throws {
-        let _ = try scalar("SELECT count(*) FROM sqlite_master;")
+        _ = try scalar("SELECT count(*) FROM sqlite_master;")
     }
 }
 #endif
diff --git a/Sources/SQLite/Extensions/FTS4.swift b/Sources/SQLite/Extensions/FTS4.swift
index f0184565..a3c253f2 100644
--- a/Sources/SQLite/Extensions/FTS4.swift
+++ b/Sources/SQLite/Extensions/FTS4.swift
@@ -97,7 +97,8 @@ public struct Tokenizer {
 
     public static let Porter = Tokenizer("porter")
 
-    public static func Unicode61(removeDiacritics: Bool? = nil, tokenchars: Set<Character> = [], separators: Set<Character> = []) -> Tokenizer {
+    public static func Unicode61(removeDiacritics: Bool? = nil, tokenchars: Set<Character> = [],
+                                 separators: Set<Character> = []) -> Tokenizer {
         var arguments = [String]()
 
         if let removeDiacritics = removeDiacritics {
@@ -134,7 +135,7 @@ public struct Tokenizer {
 
 }
 
-extension Tokenizer : CustomStringConvertible {
+extension Tokenizer: CustomStringConvertible {
 
     public var description: String {
         return ([name] + arguments).joined(separator: " ")
@@ -145,13 +146,13 @@ extension Tokenizer : CustomStringConvertible {
 extension Connection {
 
     public func registerTokenizer(_ submoduleName: String, next: @escaping (String) -> (String, Range<String.Index>)?) throws {
-        try check(_SQLiteRegisterTokenizer(handle, Tokenizer.moduleName, submoduleName) { (
-                input: UnsafePointer<Int8>, offset: UnsafeMutablePointer<Int32>, length: UnsafeMutablePointer<Int32>) in
+        try check(_SQLiteRegisterTokenizer(handle, Tokenizer.moduleName, submoduleName) {
+            (input: UnsafePointer<Int8>, offset: UnsafeMutablePointer<Int32>, length: UnsafeMutablePointer<Int32>) in
             let string = String(cString: input)
 
             guard let (token, range) = next(string) else { return nil }
 
-            let view:String.UTF8View = string.utf8
+            let view: String.UTF8View = string.utf8
 
             if let from = range.lowerBound.samePosition(in: view),
                let to = range.upperBound.samePosition(in: view) {
@@ -231,7 +232,7 @@ open class FTSConfig {
         if let tokenizer = tokenizer {
             options.append("tokenize", value: Expression<Void>(literal: tokenizer.description))
         }
-        options.appendCommaSeparated("prefix", values:prefixes.sorted().map { String($0) })
+        options.appendCommaSeparated("prefix", values: prefixes.sorted().map { String($0) })
         if isContentless {
             options.append("content", value: "")
         } else if let externalContentSchema = externalContentSchema {
@@ -274,9 +275,9 @@ open class FTSConfig {
 }
 
 /// Configuration for the [FTS4](https://www.sqlite.org/fts3.html) extension.
-open class FTS4Config : FTSConfig {
+open class FTS4Config: FTSConfig {
     /// [The matchinfo= option](https://www.sqlite.org/fts3.html#section_6_4)
-    public enum MatchInfo : CustomStringConvertible {
+    public enum MatchInfo: CustomStringConvertible {
         case fts3
         public var description: String {
             return "fts3"
@@ -284,7 +285,7 @@ open class FTS4Config : FTSConfig {
     }
 
     /// [FTS4 options](https://www.sqlite.org/fts3.html#fts4_options)
-    public enum Order : CustomStringConvertible {
+    public enum Order: CustomStringConvertible {
         /// Data structures are optimized for returning results in ascending order by docid (default)
         case asc
         /// FTS4 stores its data in such a way as to optimize returning results in descending order by docid.
diff --git a/Sources/SQLite/Extensions/FTS5.swift b/Sources/SQLite/Extensions/FTS5.swift
index cf13f3d8..07f49ffc 100644
--- a/Sources/SQLite/Extensions/FTS5.swift
+++ b/Sources/SQLite/Extensions/FTS5.swift
@@ -32,8 +32,8 @@ extension Module {
 ///
 /// **Note:** this is currently only applicable when using SQLite.swift together with a FTS5-enabled version
 /// of SQLite.
-open class FTS5Config : FTSConfig {
-    public enum Detail : CustomStringConvertible {
+open class FTS5Config: FTSConfig {
+    public enum Detail: CustomStringConvertible {
         /// store rowid, column number, term offset
         case full
         /// store rowid, column number
diff --git a/Sources/SQLite/Extensions/RTree.swift b/Sources/SQLite/Extensions/RTree.swift
index 4fc1a235..5ecdf78b 100644
--- a/Sources/SQLite/Extensions/RTree.swift
+++ b/Sources/SQLite/Extensions/RTree.swift
@@ -23,8 +23,9 @@
 //
 
 extension Module {
-
-    public static func RTree<T : Value, U : Value>(_ primaryKey: Expression<T>, _ pairs: (Expression<U>, Expression<U>)...) -> Module where T.Datatype == Int64, U.Datatype == Double {
+    public static func RTree<T: Value, U: Value>(_ primaryKey: Expression<T>,
+                                                 _ pairs: (Expression<U>, Expression<U>)...)
+                    -> Module where T.Datatype == Int64, U.Datatype == Double {
         var arguments: [Expressible] = [primaryKey]
 
         for pair in pairs {
@@ -33,5 +34,4 @@ extension Module {
 
         return Module(name: "rtree", arguments: arguments)
     }
-
 }
diff --git a/Sources/SQLite/Foundation.swift b/Sources/SQLite/Foundation.swift
index 9986f581..d837d8ba 100644
--- a/Sources/SQLite/Foundation.swift
+++ b/Sources/SQLite/Foundation.swift
@@ -24,7 +24,7 @@
 
 import Foundation
 
-extension Data : Value {
+extension Data: Value {
 
     public static var declaredDatatype: String {
         return Blob.declaredDatatype
@@ -42,7 +42,7 @@ extension Data : Value {
 
 }
 
-extension Date : Value {
+extension Date: Value {
 
     public static var declaredDatatype: String {
         return String.declaredDatatype
@@ -69,7 +69,7 @@ public var dateFormatter: DateFormatter = {
     return formatter
 }()
 
-extension UUID : Value {
+extension UUID: Value {
 
     public static var declaredDatatype: String {
         return String.declaredDatatype
diff --git a/Sources/SQLite/Helpers.swift b/Sources/SQLite/Helpers.swift
index 44aaec19..0e3385e3 100644
--- a/Sources/SQLite/Helpers.swift
+++ b/Sources/SQLite/Helpers.swift
@@ -44,7 +44,7 @@ public protocol _OptionalType {
 
 }
 
-extension Optional : _OptionalType {
+extension Optional: _OptionalType {
 
     public typealias WrappedType = Wrapped
 
@@ -75,7 +75,7 @@ extension String {
     func infix<T>(_ lhs: Expressible, _ rhs: Expressible, wrap: Bool = true) -> Expression<T> {
         return infix([lhs, rhs], wrap: wrap)
     }
-    
+
     func infix<T>(_ terms: [Expressible], wrap: Bool = true) -> Expression<T> {
         let expression = Expression<T>(" \(self) ".join(terms).expression)
         guard wrap else {
@@ -115,10 +115,11 @@ func transcode(_ literal: Binding?) -> String {
     }
 }
 
+//swiftlint:disable force_cast
 func value<A: Value>(_ v: Binding) -> A {
-    return A.fromDatatypeValue(v as! A.Datatype) as! A
+    A.fromDatatypeValue(v as! A.Datatype) as! A
 }
 
 func value<A: Value>(_ v: Binding?) -> A {
-    return value(v!)
+    value(v!)
 }
diff --git a/Sources/SQLite/Typed/AggregateFunctions.swift b/Sources/SQLite/Typed/AggregateFunctions.swift
index 2ec28288..325b0277 100644
--- a/Sources/SQLite/Typed/AggregateFunctions.swift
+++ b/Sources/SQLite/Typed/AggregateFunctions.swift
@@ -29,13 +29,13 @@ private enum Function: String {
     case avg
     case sum
     case total
-    
+
     func wrap<T>(_ expression: Expressible) -> Expression<T> {
         return self.rawValue.wrap(expression)
     }
 }
 
-extension ExpressionType where UnderlyingType : Value {
+extension ExpressionType where UnderlyingType: Value {
 
     /// Builds a copy of the expression prefixed with the `DISTINCT` keyword.
     ///
@@ -66,7 +66,7 @@ extension ExpressionType where UnderlyingType : Value {
 
 }
 
-extension ExpressionType where UnderlyingType : _OptionalType, UnderlyingType.WrappedType : Value {
+extension ExpressionType where UnderlyingType: _OptionalType, UnderlyingType.WrappedType: Value {
 
     /// Builds a copy of the expression prefixed with the `DISTINCT` keyword.
     ///
@@ -97,7 +97,7 @@ extension ExpressionType where UnderlyingType : _OptionalType, UnderlyingType.Wr
 
 }
 
-extension ExpressionType where UnderlyingType : Value, UnderlyingType.Datatype : Comparable {
+extension ExpressionType where UnderlyingType: Value, UnderlyingType.Datatype: Comparable {
 
     /// Builds a copy of the expression wrapped with the `max` aggregate
     /// function.
@@ -127,7 +127,7 @@ extension ExpressionType where UnderlyingType : Value, UnderlyingType.Datatype :
 
 }
 
-extension ExpressionType where UnderlyingType : _OptionalType, UnderlyingType.WrappedType : Value, UnderlyingType.WrappedType.Datatype : Comparable {
+extension ExpressionType where UnderlyingType: _OptionalType, UnderlyingType.WrappedType: Value, UnderlyingType.WrappedType.Datatype: Comparable {
 
     /// Builds a copy of the expression wrapped with the `max` aggregate
     /// function.
@@ -157,7 +157,7 @@ extension ExpressionType where UnderlyingType : _OptionalType, UnderlyingType.Wr
 
 }
 
-extension ExpressionType where UnderlyingType : Value, UnderlyingType.Datatype : Number {
+extension ExpressionType where UnderlyingType: Value, UnderlyingType.Datatype: Number {
 
     /// Builds a copy of the expression wrapped with the `avg` aggregate
     /// function.
@@ -200,7 +200,7 @@ extension ExpressionType where UnderlyingType : Value, UnderlyingType.Datatype :
 
 }
 
-extension ExpressionType where UnderlyingType : _OptionalType, UnderlyingType.WrappedType : Value, UnderlyingType.WrappedType.Datatype : Number {
+extension ExpressionType where UnderlyingType: _OptionalType, UnderlyingType.WrappedType: Value, UnderlyingType.WrappedType.Datatype: Number {
 
     /// Builds a copy of the expression wrapped with the `avg` aggregate
     /// function.
diff --git a/Sources/SQLite/Typed/Coding.swift b/Sources/SQLite/Typed/Coding.swift
index 7044bc37..5a672883 100644
--- a/Sources/SQLite/Typed/Coding.swift
+++ b/Sources/SQLite/Typed/Coding.swift
@@ -39,12 +39,12 @@ extension QueryType {
     ///   - otherSetters: Any other setters to include in the insert
     ///
     /// - Returns: An `INSERT` statement for the encodable object
-    public func insert(_ encodable: Encodable, userInfo: [CodingUserInfoKey:Any] = [:], otherSetters: [Setter] = []) throws -> Insert {
+    public func insert(_ encodable: Encodable, userInfo: [CodingUserInfoKey: Any] = [:], otherSetters: [Setter] = []) throws -> Insert {
         let encoder = SQLiteEncoder(userInfo: userInfo)
         try encodable.encode(to: encoder)
         return self.insert(encoder.setters + otherSetters)
     }
-    
+
     /// Creates an `INSERT` statement by encoding the given object
     /// This method converts any custom nested types to JSON data and does not handle any sort
     /// of object relationships. If you want to support relationships between objects you will
@@ -63,7 +63,8 @@ extension QueryType {
     ///   - otherSetters: Any other setters to include in the insert
     ///
     /// - Returns: An `INSERT` statement fort the encodable object
-    public func insert(or onConflict: OnConflict, encodable: Encodable, userInfo: [CodingUserInfoKey:Any] = [:], otherSetters: [Setter] = []) throws -> Insert {
+    public func insert(or onConflict: OnConflict, encodable: Encodable, userInfo: [CodingUserInfoKey: Any] = [:],
+                       otherSetters: [Setter] = []) throws -> Insert {
         let encoder = SQLiteEncoder(userInfo: userInfo)
         try encodable.encode(to: encoder)
         return self.insert(or: onConflict, encoder.setters + otherSetters)
@@ -83,7 +84,8 @@ extension QueryType {
     ///   - otherSetters: Any other setters to include in the inserts, per row/object.
     ///
     /// - Returns: An `INSERT` statement for the encodable objects
-    public func insertMany(_ encodables: [Encodable], userInfo: [CodingUserInfoKey:Any] = [:], otherSetters: [Setter] = []) throws -> Insert {
+    public func insertMany(_ encodables: [Encodable], userInfo: [CodingUserInfoKey: Any] = [:],
+                           otherSetters: [Setter] = []) throws -> Insert {
         let combinedSetters = try encodables.map { encodable -> [Setter] in
             let encoder = SQLiteEncoder(userInfo: userInfo)
             try encodable.encode(to: encoder)
@@ -108,7 +110,8 @@ extension QueryType {
     ///   - onConflictOf: The column that if conflicts should trigger an update instead of insert.
     ///
     /// - Returns: An `INSERT` statement fort the encodable object
-    public func upsert(_ encodable: Encodable, userInfo: [CodingUserInfoKey:Any] = [:], otherSetters: [Setter] = [], onConflictOf conflicting: Expressible) throws -> Insert {
+    public func upsert(_ encodable: Encodable, userInfo: [CodingUserInfoKey: Any] = [:],
+                       otherSetters: [Setter] = [], onConflictOf conflicting: Expressible) throws -> Insert {
         let encoder = SQLiteEncoder(userInfo: userInfo)
         try encodable.encode(to: encoder)
         return self.upsert(encoder.setters + otherSetters, onConflictOf: conflicting)
@@ -128,7 +131,8 @@ extension QueryType {
     ///   - otherSetters: Any other setters to include in the insert
     ///
     /// - Returns: An `UPDATE` statement fort the encodable object
-    public func update(_ encodable: Encodable, userInfo: [CodingUserInfoKey:Any] = [:], otherSetters: [Setter] = []) throws -> Update {
+    public func update(_ encodable: Encodable, userInfo: [CodingUserInfoKey: Any] = [:],
+                       otherSetters: [Setter] = []) throws -> Update {
         let encoder = SQLiteEncoder(userInfo: userInfo)
         try encodable.encode(to: encoder)
         return self.update(encoder.setters + otherSetters)
@@ -154,8 +158,9 @@ extension Row {
 }
 
 /// Generates a list of settings for an Encodable object
-fileprivate class SQLiteEncoder: Encoder {
+private class SQLiteEncoder: Encoder {
     class SQLiteKeyedEncodingContainer<MyKey: CodingKey>: KeyedEncodingContainerProtocol {
+        // swiftlint:disable nesting
         typealias Key = MyKey
 
         let encoder: SQLiteEncoder
@@ -197,14 +202,12 @@ fileprivate class SQLiteEncoder: Encoder {
             self.encoder.setters.append(Expression(key.stringValue) <- value)
         }
 
-        func encode<T>(_ value: T, forKey key: Key) throws where T : Swift.Encodable {
+        func encode<T>(_ value: T, forKey key: Key) throws where T: Swift.Encodable {
             if let data = value as? Data {
                 self.encoder.setters.append(Expression(key.stringValue) <- data)
-            }
-            else if let date = value as? Date {
+            } else if let date = value as? Date {
                 self.encoder.setters.append(Expression(key.stringValue) <- date.datatypeValue)
-            }
-            else {
+            } else {
                 let encoded = try JSONEncoder().encode(value)
                 let string = String(data: encoded, encoding: .utf8)
                 self.encoder.setters.append(Expression(key.stringValue) <- string)
@@ -212,15 +215,18 @@ fileprivate class SQLiteEncoder: Encoder {
         }
 
         func encode(_ value: Int8, forKey key: Key) throws {
-            throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: self.codingPath, debugDescription: "encoding an Int8 is not supported"))
+            throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: self.codingPath,
+                                                                          debugDescription: "encoding an Int8 is not supported"))
         }
 
         func encode(_ value: Int16, forKey key: Key) throws {
-            throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: self.codingPath, debugDescription: "encoding an Int16 is not supported"))
+            throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: self.codingPath,
+                                                                          debugDescription: "encoding an Int16 is not supported"))
         }
 
         func encode(_ value: Int32, forKey key: Key) throws {
-            throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: self.codingPath, debugDescription: "encoding an Int32 is not supported"))
+            throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: self.codingPath,
+                                                                          debugDescription: "encoding an Int32 is not supported"))
         }
 
         func encode(_ value: Int64, forKey key: Key) throws {
@@ -228,26 +234,32 @@ fileprivate class SQLiteEncoder: Encoder {
         }
 
         func encode(_ value: UInt, forKey key: Key) throws {
-            throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: self.codingPath, debugDescription: "encoding an UInt is not supported"))
+            throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: self.codingPath,
+                                                                          debugDescription: "encoding an UInt is not supported"))
         }
 
         func encode(_ value: UInt8, forKey key: Key) throws {
-            throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: self.codingPath, debugDescription: "encoding an UInt8 is not supported"))
+            throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: self.codingPath,
+                                                                          debugDescription: "encoding an UInt8 is not supported"))
         }
 
         func encode(_ value: UInt16, forKey key: Key) throws {
-            throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: self.codingPath, debugDescription: "encoding an UInt16 is not supported"))
+            throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: self.codingPath,
+                                                                          debugDescription: "encoding an UInt16 is not supported"))
         }
 
         func encode(_ value: UInt32, forKey key: Key) throws {
-            throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: self.codingPath, debugDescription: "encoding an UInt32 is not supported"))
+            throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: self.codingPath,
+                                                                          debugDescription: "encoding an UInt32 is not supported"))
         }
 
         func encode(_ value: UInt64, forKey key: Key) throws {
-            throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: self.codingPath, debugDescription: "encoding an UInt64 is not supported"))
+            throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: self.codingPath,
+                                                                          debugDescription: "encoding an UInt64 is not supported"))
         }
 
-        func nestedContainer<NestedKey>(keyedBy keyType: NestedKey.Type, forKey key: Key) -> KeyedEncodingContainer<NestedKey> where NestedKey : CodingKey {
+        func nestedContainer<NestedKey>(keyedBy keyType: NestedKey.Type, forKey key: Key)
+                        -> KeyedEncodingContainer<NestedKey> where NestedKey: CodingKey {
             fatalError("encoding a nested container is not supported")
         }
 
@@ -272,13 +284,13 @@ fileprivate class SQLiteEncoder: Encoder {
         fatalError("not supported")
     }
 
-    func container<Key>(keyedBy type: Key.Type) -> KeyedEncodingContainer<Key> where Key : CodingKey {
+    func container<Key>(keyedBy type: Key.Type) -> KeyedEncodingContainer<Key> where Key: CodingKey {
         return KeyedEncodingContainer(SQLiteKeyedEncodingContainer(encoder: self))
     }
 }
 
-fileprivate class SQLiteDecoder : Decoder {
-    class SQLiteKeyedDecodingContainer<MyKey: CodingKey> : KeyedDecodingContainerProtocol {
+private class SQLiteDecoder: Decoder {
+    class SQLiteKeyedDecodingContainer<MyKey: CodingKey>: KeyedDecodingContainerProtocol {
         typealias Key = MyKey
 
         let codingPath: [CodingKey] = []
@@ -309,15 +321,18 @@ fileprivate class SQLiteDecoder : Decoder {
         }
 
         func decode(_ type: Int8.Type, forKey key: Key) throws -> Int8 {
-            throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: self.codingPath, debugDescription: "decoding an Int8 is not supported"))
+            throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: self.codingPath,
+                                                                         debugDescription: "decoding an Int8 is not supported"))
         }
 
         func decode(_ type: Int16.Type, forKey key: Key) throws -> Int16 {
-            throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: self.codingPath, debugDescription: "decoding an Int16 is not supported"))
+            throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: self.codingPath,
+                                                                         debugDescription: "decoding an Int16 is not supported"))
         }
 
         func decode(_ type: Int32.Type, forKey key: Key) throws -> Int32 {
-            throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: self.codingPath, debugDescription: "decoding an Int32 is not supported"))
+            throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: self.codingPath,
+                                                                         debugDescription: "decoding an Int32 is not supported"))
         }
 
         func decode(_ type: Int64.Type, forKey key: Key) throws -> Int64 {
@@ -325,24 +340,29 @@ fileprivate class SQLiteDecoder : Decoder {
         }
 
         func decode(_ type: UInt.Type, forKey key: Key) throws -> UInt {
-            throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: self.codingPath, debugDescription: "decoding an UInt is not supported"))
+            throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: self.codingPath,
+                                                                         debugDescription: "decoding an UInt is not supported"))
 
         }
 
         func decode(_ type: UInt8.Type, forKey key: Key) throws -> UInt8 {
-            throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: self.codingPath, debugDescription: "decoding an UInt8 is not supported"))
+            throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: self.codingPath,
+                                                                         debugDescription: "decoding an UInt8 is not supported"))
         }
 
         func decode(_ type: UInt16.Type, forKey key: Key) throws -> UInt16 {
-            throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: self.codingPath, debugDescription: "decoding an UInt16 is not supported"))
+            throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: self.codingPath,
+                                                                         debugDescription: "decoding an UInt16 is not supported"))
         }
 
         func decode(_ type: UInt32.Type, forKey key: Key) throws -> UInt32 {
-            throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: self.codingPath, debugDescription: "decoding an UInt32 is not supported"))
+            throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: self.codingPath,
+                                                                         debugDescription: "decoding an UInt32 is not supported"))
         }
 
         func decode(_ type: UInt64.Type, forKey key: Key) throws -> UInt64 {
-            throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: self.codingPath, debugDescription: "decoding an UInt64 is not supported"))
+            throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: self.codingPath,
+                                                                         debugDescription: "decoding an UInt64 is not supported"))
         }
 
         func decode(_ type: Float.Type, forKey key: Key) throws -> Float {
@@ -358,37 +378,45 @@ fileprivate class SQLiteDecoder : Decoder {
         }
 
         func decode<T>(_ type: T.Type, forKey key: Key) throws -> T where T: Swift.Decodable {
+            // swiftlint:disable force_cast
             if type == Data.self {
                 let data = try self.row.get(Expression<Data>(key.stringValue))
                 return data as! T
-            }
-            else if type == Date.self {
+            } else if type == Date.self {
                 let date = try self.row.get(Expression<Date>(key.stringValue))
                 return date as! T
             }
+            // swiftlint:enable force_cast
             guard let JSONString = try self.row.get(Expression<String?>(key.stringValue)) else {
-                throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: self.codingPath, debugDescription: "an unsupported type was found"))
+                throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: self.codingPath,
+                                                                             debugDescription: "an unsupported type was found"))
             }
             guard let data = JSONString.data(using: .utf8) else {
-                throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "invalid utf8 data found"))
+                throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath,
+                                                                        debugDescription: "invalid utf8 data found"))
             }
             return try JSONDecoder().decode(type, from: data)
         }
 
-        func nestedContainer<NestedKey>(keyedBy type: NestedKey.Type, forKey key: Key) throws -> KeyedDecodingContainer<NestedKey> where NestedKey : CodingKey {
-            throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "decoding nested containers is not supported"))
+        func nestedContainer<NestedKey>(keyedBy type: NestedKey.Type, forKey key: Key) throws
+                        -> KeyedDecodingContainer<NestedKey> where NestedKey: CodingKey {
+            throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath,
+                                                                    debugDescription: "decoding nested containers is not supported"))
         }
 
         func nestedUnkeyedContainer(forKey key: Key) throws -> UnkeyedDecodingContainer {
-            throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "decoding unkeyed containers is not supported"))
+            throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath,
+                                                                    debugDescription: "decoding unkeyed containers is not supported"))
         }
 
         func superDecoder() throws -> Swift.Decoder {
-            throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "decoding super encoders containers is not supported"))
+            throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath,
+                                                                    debugDescription: "decoding super encoders containers is not supported"))
         }
 
         func superDecoder(forKey key: Key) throws -> Swift.Decoder {
-            throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "decoding super decoders is not supported"))
+            throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath,
+                                                                    debugDescription: "decoding super decoders is not supported"))
         }
     }
 
@@ -401,16 +429,17 @@ fileprivate class SQLiteDecoder : Decoder {
         self.userInfo = userInfo
     }
 
-    func container<Key>(keyedBy type: Key.Type) throws -> KeyedDecodingContainer<Key> where Key : CodingKey {
+    func container<Key>(keyedBy type: Key.Type) throws -> KeyedDecodingContainer<Key> where Key: CodingKey {
         return KeyedDecodingContainer(SQLiteKeyedDecodingContainer(row: self.row))
     }
 
     func unkeyedContainer() throws -> UnkeyedDecodingContainer {
-        throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "decoding an unkeyed container is not supported"))
+        throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath,
+                                                                debugDescription: "decoding an unkeyed container is not supported"))
     }
 
     func singleValueContainer() throws -> SingleValueDecodingContainer {
-        throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath, debugDescription: "decoding a single value container is not supported"))
+        throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath,
+                                                                debugDescription: "decoding a single value container is not supported"))
     }
 }
-
diff --git a/Sources/SQLite/Typed/Collation.swift b/Sources/SQLite/Typed/Collation.swift
index e2ff9d10..3b268ce0 100644
--- a/Sources/SQLite/Typed/Collation.swift
+++ b/Sources/SQLite/Typed/Collation.swift
@@ -43,7 +43,7 @@ public enum Collation {
 
 }
 
-extension Collation : Expressible {
+extension Collation: Expressible {
 
     public var expression: Expression<Void> {
         return Expression(literal: description)
@@ -51,9 +51,9 @@ extension Collation : Expressible {
 
 }
 
-extension Collation : CustomStringConvertible {
+extension Collation: CustomStringConvertible {
 
-    public var description : String {
+    public var description: String {
         switch self {
         case .binary:
             return "BINARY"
diff --git a/Sources/SQLite/Typed/CoreFunctions.swift b/Sources/SQLite/Typed/CoreFunctions.swift
index 068dcf02..053f17e5 100644
--- a/Sources/SQLite/Typed/CoreFunctions.swift
+++ b/Sources/SQLite/Typed/CoreFunctions.swift
@@ -45,21 +45,21 @@ private enum Function: String {
     case regexp = "REGEXP"
     case collate = "COLLATE"
     case ifnull
-    
+
     func infix<T>(_ lhs: Expressible, _ rhs: Expressible, wrap: Bool = true) -> Expression<T> {
         return self.rawValue.infix(lhs, rhs, wrap: wrap)
     }
-    
+
     func wrap<T>(_ expression: Expressible) -> Expression<T> {
         return self.rawValue.wrap(expression)
     }
-    
+
     func wrap<T>(_ expressions: [Expressible]) -> Expression<T> {
         return self.rawValue.wrap(", ".join(expressions))
     }
 }
 
-extension ExpressionType where UnderlyingType : Number {
+extension ExpressionType where UnderlyingType: Number {
 
     /// Builds a copy of the expression wrapped with the `abs` function.
     ///
@@ -68,13 +68,13 @@ extension ExpressionType where UnderlyingType : Number {
     ///     // abs("x")
     ///
     /// - Returns: A copy of the expression wrapped with the `abs` function.
-    public var absoluteValue : Expression<UnderlyingType> {
+    public var absoluteValue: Expression<UnderlyingType> {
         return Function.abs.wrap(self)
     }
 
 }
 
-extension ExpressionType where UnderlyingType : _OptionalType, UnderlyingType.WrappedType : Number {
+extension ExpressionType where UnderlyingType: _OptionalType, UnderlyingType.WrappedType: Number {
 
     /// Builds a copy of the expression wrapped with the `abs` function.
     ///
@@ -83,7 +83,7 @@ extension ExpressionType where UnderlyingType : _OptionalType, UnderlyingType.Wr
     ///     // abs("x")
     ///
     /// - Returns: A copy of the expression wrapped with the `abs` function.
-    public var absoluteValue : Expression<UnderlyingType> {
+    public var absoluteValue: Expression<UnderlyingType> {
         return Function.abs.wrap(self)
     }
 
@@ -129,7 +129,7 @@ extension ExpressionType where UnderlyingType == Double? {
 
 }
 
-extension ExpressionType where UnderlyingType : Value, UnderlyingType.Datatype == Int64 {
+extension ExpressionType where UnderlyingType: Value, UnderlyingType.Datatype == Int64 {
 
     /// Builds an expression representing the `random` function.
     ///
@@ -481,7 +481,7 @@ extension ExpressionType where UnderlyingType == String? {
         }
         return Expression("(\(template) LIKE ? ESCAPE ?)", bindings + [pattern, String(character)])
     }
-    
+
     /// Builds a copy of the expression appended with a `LIKE` query against the
     /// given pattern.
     ///
@@ -671,7 +671,7 @@ extension ExpressionType where UnderlyingType == String? {
 
 }
 
-extension Collection where Iterator.Element : Value {
+extension Collection where Iterator.Element: Value {
 
     /// Builds a copy of the expression prepended with an `IN` check against the
     /// collection.
@@ -708,7 +708,7 @@ extension Collection where Iterator.Element : Value {
 }
 
 extension String {
-    
+
     /// Builds a copy of the expression appended with a `LIKE` query against the
     /// given pattern.
     ///
@@ -751,7 +751,7 @@ extension String {
 ///
 /// - Returns: A copy of the given expressions wrapped with the `ifnull`
 ///   function.
-public func ??<V : Value>(optional: Expression<V?>, defaultValue: V) -> Expression<V> {
+public func ??<V: Value>(optional: Expression<V?>, defaultValue: V) -> Expression<V> {
     return Function.ifnull.wrap([optional, defaultValue])
 }
 
@@ -771,7 +771,7 @@ public func ??<V : Value>(optional: Expression<V?>, defaultValue: V) -> Expressi
 ///
 /// - Returns: A copy of the given expressions wrapped with the `ifnull`
 ///   function.
-public func ??<V : Value>(optional: Expression<V?>, defaultValue: Expression<V>) -> Expression<V> {
+public func ??<V: Value>(optional: Expression<V?>, defaultValue: Expression<V>) -> Expression<V> {
     return Function.ifnull.wrap([optional, defaultValue])
 }
 
@@ -791,6 +791,6 @@ public func ??<V : Value>(optional: Expression<V?>, defaultValue: Expression<V>)
 ///
 /// - Returns: A copy of the given expressions wrapped with the `ifnull`
 ///   function.
-public func ??<V : Value>(optional: Expression<V?>, defaultValue: Expression<V?>) -> Expression<V> {
+public func ??<V: Value>(optional: Expression<V?>, defaultValue: Expression<V?>) -> Expression<V> {
     return Function.ifnull.wrap([optional, defaultValue])
 }
diff --git a/Sources/SQLite/Typed/CustomFunctions.swift b/Sources/SQLite/Typed/CustomFunctions.swift
index 4d3e69ee..88fb5aaa 100644
--- a/Sources/SQLite/Typed/CustomFunctions.swift
+++ b/Sources/SQLite/Typed/CustomFunctions.swift
@@ -39,83 +39,107 @@ public extension Connection {
     ///     The assigned types must be explicit.
     ///
     /// - Returns: A closure returning an SQL expression to call the function.
-    func createFunction<Z : Value>(_ function: String, deterministic: Bool = false, _ block: @escaping () -> Z) throws -> (() -> Expression<Z>) {
+    func createFunction<Z: Value>(_ function: String, deterministic: Bool = false, _ block: @escaping () -> Z) throws
+                    -> () -> Expression<Z> {
         let fn = try createFunction(function, 0, deterministic) { _ in block() }
         return { fn([]) }
     }
 
-    func createFunction<Z : Value>(_ function: String, deterministic: Bool = false, _ block: @escaping () -> Z?) throws -> (() -> Expression<Z?>) {
+    func createFunction<Z: Value>(_ function: String, deterministic: Bool = false, _ block: @escaping () -> Z?) throws
+                    -> () -> Expression<Z?> {
         let fn = try createFunction(function, 0, deterministic) { _ in block() }
         return { fn([]) }
     }
 
     // MARK: -
 
-    func createFunction<Z : Value, A : Value>(_ function: String, deterministic: Bool = false, _ block: @escaping (A) -> Z) throws -> ((Expression<A>) -> Expression<Z>) {
+    func createFunction<Z: Value, A: Value>(_ function: String, deterministic: Bool = false, _ block: @escaping (A) -> Z) throws
+                    -> (Expression<A>) -> Expression<Z> {
         let fn = try createFunction(function, 1, deterministic) { args in block(value(args[0])) }
         return { arg in fn([arg]) }
     }
 
-    func createFunction<Z : Value, A : Value>(_ function: String, deterministic: Bool = false, _ block: @escaping (A?) -> Z) throws -> ((Expression<A?>) -> Expression<Z>) {
+    func createFunction<Z: Value, A: Value>(_ function: String, deterministic: Bool = false, _ block: @escaping (A?) -> Z) throws
+                    -> (Expression<A?>) -> Expression<Z> {
         let fn = try createFunction(function, 1, deterministic) { args in block(args[0].map(value)) }
         return { arg in fn([arg]) }
     }
 
-    func createFunction<Z : Value, A : Value>(_ function: String, deterministic: Bool = false, _ block: @escaping (A) -> Z?) throws -> ((Expression<A>) -> Expression<Z?>) {
+    func createFunction<Z: Value, A: Value>(_ function: String, deterministic: Bool = false, _ block: @escaping (A) -> Z?) throws
+                    -> (Expression<A>) -> Expression<Z?> {
         let fn = try createFunction(function, 1, deterministic) { args in block(value(args[0])) }
         return { arg in fn([arg]) }
     }
 
-    func createFunction<Z : Value, A : Value>(_ function: String, deterministic: Bool = false, _ block: @escaping (A?) -> Z?) throws -> ((Expression<A?>) -> Expression<Z?>) {
+    func createFunction<Z: Value, A: Value>(_ function: String, deterministic: Bool = false, _ block: @escaping (A?) -> Z?) throws
+                    -> (Expression<A?>) -> Expression<Z?> {
         let fn = try createFunction(function, 1, deterministic) { args in block(args[0].map(value)) }
         return { arg in fn([arg]) }
     }
 
     // MARK: -
 
-    func createFunction<Z : Value, A : Value, B : Value>(_ function: String, deterministic: Bool = false, _ block: @escaping (A, B) -> Z) throws -> (Expression<A>, Expression<B>) -> Expression<Z> {
+    func createFunction<Z: Value, A: Value, B: Value>(_ function: String, deterministic: Bool = false,
+                                                      _ block: @escaping (A, B) -> Z) throws -> (Expression<A>, Expression<B>)
+    -> Expression<Z> {
         let fn = try createFunction(function, 2, deterministic) { args in block(value(args[0]), value(args[1])) }
         return { a, b in fn([a, b]) }
     }
 
-    func createFunction<Z : Value, A : Value, B : Value>(_ function: String, deterministic: Bool = false, _ block: @escaping (A?, B) -> Z) throws -> (Expression<A?>, Expression<B>) -> Expression<Z> {
+    func createFunction<Z: Value, A: Value, B: Value>(_ function: String, deterministic: Bool = false,
+                                                      _ block: @escaping (A?, B) -> Z) throws
+                    -> (Expression<A?>, Expression<B>) -> Expression<Z> {
         let fn = try createFunction(function, 2, deterministic) { args in block(args[0].map(value), value(args[1])) }
         return { a, b in fn([a, b]) }
     }
 
-    func createFunction<Z : Value, A : Value, B : Value>(_ function: String, deterministic: Bool = false, _ block: @escaping (A, B?) -> Z) throws -> (Expression<A>, Expression<B?>) -> Expression<Z> {
+    func createFunction<Z: Value, A: Value, B: Value>(_ function: String, deterministic: Bool = false,
+                                                      _ block: @escaping (A, B?) -> Z) throws ->
+            (Expression<A>, Expression<B?>) -> Expression<Z> {
         let fn = try createFunction(function, 2, deterministic) { args in block(value(args[0]), args[1].map(value)) }
         return { a, b in fn([a, b]) }
     }
 
-    func createFunction<Z : Value, A : Value, B : Value>(_ function: String, deterministic: Bool = false, _ block: @escaping (A, B) -> Z?) throws -> (Expression<A>, Expression<B>) -> Expression<Z?> {
+    func createFunction<Z: Value, A: Value, B: Value>(_ function: String, deterministic: Bool = false,
+                                                      _ block: @escaping (A, B) -> Z?) throws
+                    -> (Expression<A>, Expression<B>) -> Expression<Z?> {
         let fn = try createFunction(function, 2, deterministic) { args in block(value(args[0]), value(args[1])) }
         return { a, b in fn([a, b]) }
     }
 
-    func createFunction<Z : Value, A : Value, B : Value>(_ function: String, deterministic: Bool = false, _ block: @escaping (A?, B?) -> Z) throws -> (Expression<A?>, Expression<B?>) -> Expression<Z> {
+    func createFunction<Z: Value, A: Value, B: Value>(_ function: String, deterministic: Bool = false,
+                                                      _ block: @escaping (A?, B?) -> Z) throws
+                    -> (Expression<A?>, Expression<B?>) -> Expression<Z> {
         let fn = try createFunction(function, 2, deterministic) { args in block(args[0].map(value), args[1].map(value)) }
         return { a, b in fn([a, b]) }
     }
 
-    func createFunction<Z : Value, A : Value, B : Value>(_ function: String, deterministic: Bool = false, _ block: @escaping (A?, B) -> Z?) throws -> (Expression<A?>, Expression<B>) -> Expression<Z?> {
+    func createFunction<Z: Value, A: Value, B: Value>(_ function: String, deterministic: Bool = false,
+                                                      _ block: @escaping (A?, B) -> Z?) throws
+                    -> (Expression<A?>, Expression<B>) -> Expression<Z?> {
         let fn = try createFunction(function, 2, deterministic) { args in block(args[0].map(value), value(args[1])) }
         return { a, b in fn([a, b]) }
     }
 
-    func createFunction<Z : Value, A : Value, B : Value>(_ function: String, deterministic: Bool = false, _ block: @escaping (A, B?) -> Z?) throws -> (Expression<A>, Expression<B?>) -> Expression<Z?> {
+    func createFunction<Z: Value, A: Value, B: Value>(_ function: String, deterministic: Bool = false,
+                                                      _ block: @escaping (A, B?) -> Z?) throws
+                    -> (Expression<A>, Expression<B?>) -> Expression<Z?> {
         let fn = try createFunction(function, 2, deterministic) { args in block(value(args[0]), args[1].map(value)) }
         return { a, b in fn([a, b]) }
     }
 
-    func createFunction<Z : Value, A : Value, B : Value>(_ function: String, deterministic: Bool = false, _ block: @escaping (A?, B?) -> Z?) throws -> (Expression<A?>, Expression<B?>) -> Expression<Z?> {
+    func createFunction<Z: Value, A: Value, B: Value>(_ function: String, deterministic: Bool = false,
+                                                      _ block: @escaping (A?, B?) -> Z?) throws
+                    -> (Expression<A?>, Expression<B?>) -> Expression<Z?> {
         let fn = try createFunction(function, 2, deterministic) { args in block(args[0].map(value), args[1].map(value)) }
         return { a, b in fn([a, b]) }
     }
 
     // MARK: -
 
-    fileprivate func createFunction<Z : Value>(_ function: String, _ argumentCount: UInt, _ deterministic: Bool, _ block: @escaping ([Binding?]) -> Z) throws -> (([Expressible]) -> Expression<Z>) {
+    fileprivate func createFunction<Z: Value>(_ function: String, _ argumentCount: UInt, _ deterministic: Bool,
+                                              _ block: @escaping ([Binding?]) -> Z) throws
+                    -> ([Expressible]) -> Expression<Z> {
         createFunction(function, argumentCount: argumentCount, deterministic: deterministic) { arguments in
             block(arguments).datatypeValue
         }
@@ -124,7 +148,9 @@ public extension Connection {
         }
     }
 
-    fileprivate func createFunction<Z : Value>(_ function: String, _ argumentCount: UInt, _ deterministic: Bool, _ block: @escaping ([Binding?]) -> Z?) throws -> (([Expressible]) -> Expression<Z?>) {
+    fileprivate func createFunction<Z: Value>(_ function: String, _ argumentCount: UInt, _ deterministic: Bool,
+                                              _ block: @escaping ([Binding?]) -> Z?) throws
+                    -> ([Expressible]) -> Expression<Z?> {
         createFunction(function, argumentCount: argumentCount, deterministic: deterministic) { arguments in
             block(arguments)?.datatypeValue
         }
diff --git a/Sources/SQLite/Typed/Expression.swift b/Sources/SQLite/Typed/Expression.swift
index 76ba04c4..39a3c62f 100644
--- a/Sources/SQLite/Typed/Expression.swift
+++ b/Sources/SQLite/Typed/Expression.swift
@@ -22,7 +22,7 @@
 // THE SOFTWARE.
 //
 
-public protocol ExpressionType : Expressible { // extensions cannot have inheritance clauses
+public protocol ExpressionType: Expressible { // extensions cannot have inheritance clauses
 
     associatedtype UnderlyingType = Void
 
@@ -43,14 +43,14 @@ extension ExpressionType {
         self.init(literal: identifier.quote())
     }
 
-    public init<U : ExpressionType>(_ expression: U) {
+    public init<U: ExpressionType>(_ expression: U) {
         self.init(expression.template, expression.bindings)
     }
 
 }
 
 /// An `Expression` represents a raw SQL fragment and any associated bindings.
-public struct Expression<Datatype> : ExpressionType {
+public struct Expression<Datatype>: ExpressionType {
 
     public typealias UnderlyingType = Datatype
 
@@ -79,7 +79,7 @@ extension Expressible {
         var idx = 0
         return expressed.template.reduce("") { template, character in
             let transcoded: String
-            
+
             if character == "?" {
                 transcoded = transcode(expressed.bindings[idx])
                 idx += 1
@@ -108,7 +108,7 @@ extension ExpressionType {
 
 }
 
-extension ExpressionType where UnderlyingType : Value {
+extension ExpressionType where UnderlyingType: Value {
 
     public init(value: UnderlyingType) {
         self.init("?", [value.datatypeValue])
@@ -116,7 +116,7 @@ extension ExpressionType where UnderlyingType : Value {
 
 }
 
-extension ExpressionType where UnderlyingType : _OptionalType, UnderlyingType.WrappedType : Value {
+extension ExpressionType where UnderlyingType: _OptionalType, UnderlyingType.WrappedType: Value {
 
     public static var null: Self {
         return self.init(value: nil)
diff --git a/Sources/SQLite/Typed/Operators.swift b/Sources/SQLite/Typed/Operators.swift
index 954a4af4..32d39fa7 100644
--- a/Sources/SQLite/Typed/Operators.swift
+++ b/Sources/SQLite/Typed/Operators.swift
@@ -45,11 +45,11 @@ private enum Operator: String {
     case gte = ">="
     case lte = "<="
     case concatenate = "||"
-    
+
     func infix<T>(_ lhs: Expressible, _ rhs: Expressible, wrap: Bool = true) -> Expression<T> {
         return self.rawValue.infix(lhs, rhs, wrap: wrap)
     }
-    
+
     func wrap<T>(_ expression: Expressible) -> Expression<T> {
         return self.rawValue.wrap(expression)
     }
@@ -83,520 +83,521 @@ public func +(lhs: String, rhs: Expression<String?>) -> Expression<String?> {
 
 // MARK: -
 
-public func +<V : Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<V> where V.Datatype : Number {
+public func +<V: Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<V> where V.Datatype: Number {
     return Operator.plus.infix(lhs, rhs)
 }
-public func +<V : Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype : Number {
+public func +<V: Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype: Number {
     return Operator.plus.infix(lhs, rhs)
 }
-public func +<V : Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<V?> where V.Datatype : Number {
+public func +<V: Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<V?> where V.Datatype: Number {
     return Operator.plus.infix(lhs, rhs)
 }
-public func +<V : Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype : Number {
+public func +<V: Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype: Number {
     return Operator.plus.infix(lhs, rhs)
 }
-public func +<V : Value>(lhs: Expression<V>, rhs: V) -> Expression<V> where V.Datatype : Number {
+public func +<V: Value>(lhs: Expression<V>, rhs: V) -> Expression<V> where V.Datatype: Number {
     return Operator.plus.infix(lhs, rhs)
 }
-public func +<V : Value>(lhs: Expression<V?>, rhs: V) -> Expression<V?> where V.Datatype : Number {
+public func +<V: Value>(lhs: Expression<V?>, rhs: V) -> Expression<V?> where V.Datatype: Number {
     return Operator.plus.infix(lhs, rhs)
 }
-public func +<V : Value>(lhs: V, rhs: Expression<V>) -> Expression<V> where V.Datatype : Number {
+public func +<V: Value>(lhs: V, rhs: Expression<V>) -> Expression<V> where V.Datatype: Number {
     return Operator.plus.infix(lhs, rhs)
 }
-public func +<V: Value>(lhs: V, rhs: Expression<V?>) -> Expression<V?> where V.Datatype : Number {
+public func +<V: Value>(lhs: V, rhs: Expression<V?>) -> Expression<V?> where V.Datatype: Number {
     return Operator.plus.infix(lhs, rhs)
 }
 
-public func -<V : Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<V> where V.Datatype : Number {
+public func -<V: Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<V> where V.Datatype: Number {
     return Operator.minus.infix(lhs, rhs)
 }
-public func -<V : Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype : Number {
+public func -<V: Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype: Number {
     return Operator.minus.infix(lhs, rhs)
 }
-public func -<V : Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<V?> where V.Datatype : Number {
+public func -<V: Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<V?> where V.Datatype: Number {
     return Operator.minus.infix(lhs, rhs)
 }
-public func -<V : Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype : Number {
+public func -<V: Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype: Number {
     return Operator.minus.infix(lhs, rhs)
 }
-public func -<V : Value>(lhs: Expression<V>, rhs: V) -> Expression<V> where V.Datatype : Number {
+public func -<V: Value>(lhs: Expression<V>, rhs: V) -> Expression<V> where V.Datatype: Number {
     return Operator.minus.infix(lhs, rhs)
 }
-public func -<V : Value>(lhs: Expression<V?>, rhs: V) -> Expression<V?> where V.Datatype : Number {
+public func -<V: Value>(lhs: Expression<V?>, rhs: V) -> Expression<V?> where V.Datatype: Number {
     return Operator.minus.infix(lhs, rhs)
 }
-public func -<V : Value>(lhs: V, rhs: Expression<V>) -> Expression<V> where V.Datatype : Number {
+public func -<V: Value>(lhs: V, rhs: Expression<V>) -> Expression<V> where V.Datatype: Number {
     return Operator.minus.infix(lhs, rhs)
 }
-public func -<V: Value>(lhs: V, rhs: Expression<V?>) -> Expression<V?> where V.Datatype : Number {
+public func -<V: Value>(lhs: V, rhs: Expression<V?>) -> Expression<V?> where V.Datatype: Number {
     return Operator.minus.infix(lhs, rhs)
 }
 
-public func *<V : Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<V> where V.Datatype : Number {
+public func *<V: Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<V> where V.Datatype: Number {
     return Operator.mul.infix(lhs, rhs)
 }
-public func *<V : Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype : Number {
+public func *<V: Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype: Number {
     return Operator.mul.infix(lhs, rhs)
 }
-public func *<V : Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<V?> where V.Datatype : Number {
+public func *<V: Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<V?> where V.Datatype: Number {
     return Operator.mul.infix(lhs, rhs)
 }
-public func *<V : Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype : Number {
+public func *<V: Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype: Number {
     return Operator.mul.infix(lhs, rhs)
 }
-public func *<V : Value>(lhs: Expression<V>, rhs: V) -> Expression<V> where V.Datatype : Number {
+public func *<V: Value>(lhs: Expression<V>, rhs: V) -> Expression<V> where V.Datatype: Number {
     return Operator.mul.infix(lhs, rhs)
 }
-public func *<V : Value>(lhs: Expression<V?>, rhs: V) -> Expression<V?> where V.Datatype : Number {
+public func *<V: Value>(lhs: Expression<V?>, rhs: V) -> Expression<V?> where V.Datatype: Number {
     return Operator.mul.infix(lhs, rhs)
 }
-public func *<V : Value>(lhs: V, rhs: Expression<V>) -> Expression<V> where V.Datatype : Number {
+public func *<V: Value>(lhs: V, rhs: Expression<V>) -> Expression<V> where V.Datatype: Number {
     return Operator.mul.infix(lhs, rhs)
 }
-public func *<V: Value>(lhs: V, rhs: Expression<V?>) -> Expression<V?> where V.Datatype : Number {
+public func *<V: Value>(lhs: V, rhs: Expression<V?>) -> Expression<V?> where V.Datatype: Number {
     return Operator.mul.infix(lhs, rhs)
 }
 
-public func /<V : Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<V> where V.Datatype : Number {
+public func /<V: Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<V> where V.Datatype: Number {
     return Operator.div.infix(lhs, rhs)
 }
-public func /<V : Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype : Number {
+public func /<V: Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype: Number {
     return Operator.div.infix(lhs, rhs)
 }
-public func /<V : Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<V?> where V.Datatype : Number {
+public func /<V: Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<V?> where V.Datatype: Number {
     return Operator.div.infix(lhs, rhs)
 }
-public func /<V : Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype : Number {
+public func /<V: Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype: Number {
     return Operator.div.infix(lhs, rhs)
 }
-public func /<V : Value>(lhs: Expression<V>, rhs: V) -> Expression<V> where V.Datatype : Number {
+public func /<V: Value>(lhs: Expression<V>, rhs: V) -> Expression<V> where V.Datatype: Number {
     return Operator.div.infix(lhs, rhs)
 }
-public func /<V : Value>(lhs: Expression<V?>, rhs: V) -> Expression<V?> where V.Datatype : Number {
+public func /<V: Value>(lhs: Expression<V?>, rhs: V) -> Expression<V?> where V.Datatype: Number {
     return Operator.div.infix(lhs, rhs)
 }
-public func /<V : Value>(lhs: V, rhs: Expression<V>) -> Expression<V> where V.Datatype : Number {
+public func /<V: Value>(lhs: V, rhs: Expression<V>) -> Expression<V> where V.Datatype: Number {
     return Operator.div.infix(lhs, rhs)
 }
-public func /<V: Value>(lhs: V, rhs: Expression<V?>) -> Expression<V?> where V.Datatype : Number {
+public func /<V: Value>(lhs: V, rhs: Expression<V?>) -> Expression<V?> where V.Datatype: Number {
     return Operator.div.infix(lhs, rhs)
 }
 
-public prefix func -<V : Value>(rhs: Expression<V>) -> Expression<V> where V.Datatype : Number {
+public prefix func -<V: Value>(rhs: Expression<V>) -> Expression<V> where V.Datatype: Number {
     return Operator.minus.wrap(rhs)
 }
-public prefix func -<V : Value>(rhs: Expression<V?>) -> Expression<V?> where V.Datatype : Number {
+public prefix func -<V: Value>(rhs: Expression<V?>) -> Expression<V?> where V.Datatype: Number {
     return Operator.minus.wrap(rhs)
 }
 
 // MARK: -
 
-public func %<V : Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<V> where V.Datatype == Int64 {
+public func %<V: Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<V> where V.Datatype == Int64 {
     return Operator.mod.infix(lhs, rhs)
 }
-public func %<V : Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
+public func %<V: Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
     return Operator.mod.infix(lhs, rhs)
 }
-public func %<V : Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<V?> where V.Datatype == Int64 {
+public func %<V: Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<V?> where V.Datatype == Int64 {
     return Operator.mod.infix(lhs, rhs)
 }
-public func %<V : Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
+public func %<V: Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
     return Operator.mod.infix(lhs, rhs)
 }
-public func %<V : Value>(lhs: Expression<V>, rhs: V) -> Expression<V> where V.Datatype == Int64 {
+public func %<V: Value>(lhs: Expression<V>, rhs: V) -> Expression<V> where V.Datatype == Int64 {
     return Operator.mod.infix(lhs, rhs)
 }
-public func %<V : Value>(lhs: Expression<V?>, rhs: V) -> Expression<V?> where V.Datatype == Int64 {
+public func %<V: Value>(lhs: Expression<V?>, rhs: V) -> Expression<V?> where V.Datatype == Int64 {
     return Operator.mod.infix(lhs, rhs)
 }
-public func %<V : Value>(lhs: V, rhs: Expression<V>) -> Expression<V> where V.Datatype == Int64 {
+public func %<V: Value>(lhs: V, rhs: Expression<V>) -> Expression<V> where V.Datatype == Int64 {
     return Operator.mod.infix(lhs, rhs)
 }
-public func %<V : Value>(lhs: V, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
+public func %<V: Value>(lhs: V, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
     return Operator.mod.infix(lhs, rhs)
 }
 
-public func <<<V : Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<V> where V.Datatype == Int64 {
+public func <<<V: Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<V> where V.Datatype == Int64 {
     return Operator.bitwiseLeft.infix(lhs, rhs)
 }
-public func <<<V : Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
+public func <<<V: Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
     return Operator.bitwiseLeft.infix(lhs, rhs)
 }
-public func <<<V : Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<V?> where V.Datatype == Int64 {
+public func <<<V: Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<V?> where V.Datatype == Int64 {
     return Operator.bitwiseLeft.infix(lhs, rhs)
 }
-public func <<<V : Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
+public func <<<V: Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
     return Operator.bitwiseLeft.infix(lhs, rhs)
 }
-public func <<<V : Value>(lhs: Expression<V>, rhs: V) -> Expression<V> where V.Datatype == Int64 {
+public func <<<V: Value>(lhs: Expression<V>, rhs: V) -> Expression<V> where V.Datatype == Int64 {
     return Operator.bitwiseLeft.infix(lhs, rhs)
 }
-public func <<<V : Value>(lhs: Expression<V?>, rhs: V) -> Expression<V?> where V.Datatype == Int64 {
+public func <<<V: Value>(lhs: Expression<V?>, rhs: V) -> Expression<V?> where V.Datatype == Int64 {
     return Operator.bitwiseLeft.infix(lhs, rhs)
 }
-public func <<<V : Value>(lhs: V, rhs: Expression<V>) -> Expression<V> where V.Datatype == Int64 {
+public func <<<V: Value>(lhs: V, rhs: Expression<V>) -> Expression<V> where V.Datatype == Int64 {
     return Operator.bitwiseLeft.infix(lhs, rhs)
 }
-public func <<<V : Value>(lhs: V, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
+public func <<<V: Value>(lhs: V, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
     return Operator.bitwiseLeft.infix(lhs, rhs)
 }
 
-public func >><V : Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<V> where V.Datatype == Int64 {
+public func >><V: Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<V> where V.Datatype == Int64 {
     return Operator.bitwiseRight.infix(lhs, rhs)
 }
-public func >><V : Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
+public func >><V: Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
     return Operator.bitwiseRight.infix(lhs, rhs)
 }
-public func >><V : Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<V?> where V.Datatype == Int64 {
+public func >><V: Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<V?> where V.Datatype == Int64 {
     return Operator.bitwiseRight.infix(lhs, rhs)
 }
-public func >><V : Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
+public func >><V: Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
     return Operator.bitwiseRight.infix(lhs, rhs)
 }
-public func >><V : Value>(lhs: Expression<V>, rhs: V) -> Expression<V> where V.Datatype == Int64 {
+public func >><V: Value>(lhs: Expression<V>, rhs: V) -> Expression<V> where V.Datatype == Int64 {
     return Operator.bitwiseRight.infix(lhs, rhs)
 }
-public func >><V : Value>(lhs: Expression<V?>, rhs: V) -> Expression<V?> where V.Datatype == Int64 {
+public func >><V: Value>(lhs: Expression<V?>, rhs: V) -> Expression<V?> where V.Datatype == Int64 {
     return Operator.bitwiseRight.infix(lhs, rhs)
 }
-public func >><V : Value>(lhs: V, rhs: Expression<V>) -> Expression<V> where V.Datatype == Int64 {
+public func >><V: Value>(lhs: V, rhs: Expression<V>) -> Expression<V> where V.Datatype == Int64 {
     return Operator.bitwiseRight.infix(lhs, rhs)
 }
-public func >><V : Value>(lhs: V, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
+public func >><V: Value>(lhs: V, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
     return Operator.bitwiseRight.infix(lhs, rhs)
 }
 
-public func &<V : Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<V> where V.Datatype == Int64 {
+public func &<V: Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<V> where V.Datatype == Int64 {
     return Operator.bitwiseAnd.infix(lhs, rhs)
 }
-public func &<V : Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
+public func &<V: Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
     return Operator.bitwiseAnd.infix(lhs, rhs)
 }
-public func &<V : Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<V?> where V.Datatype == Int64 {
+public func &<V: Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<V?> where V.Datatype == Int64 {
     return Operator.bitwiseAnd.infix(lhs, rhs)
 }
-public func &<V : Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
+public func &<V: Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
     return Operator.bitwiseAnd.infix(lhs, rhs)
 }
-public func &<V : Value>(lhs: Expression<V>, rhs: V) -> Expression<V> where V.Datatype == Int64 {
+public func &<V: Value>(lhs: Expression<V>, rhs: V) -> Expression<V> where V.Datatype == Int64 {
     return Operator.bitwiseAnd.infix(lhs, rhs)
 }
-public func &<V : Value>(lhs: Expression<V?>, rhs: V) -> Expression<V?> where V.Datatype == Int64 {
+public func &<V: Value>(lhs: Expression<V?>, rhs: V) -> Expression<V?> where V.Datatype == Int64 {
     return Operator.bitwiseAnd.infix(lhs, rhs)
 }
-public func &<V : Value>(lhs: V, rhs: Expression<V>) -> Expression<V> where V.Datatype == Int64 {
+public func &<V: Value>(lhs: V, rhs: Expression<V>) -> Expression<V> where V.Datatype == Int64 {
     return Operator.bitwiseAnd.infix(lhs, rhs)
 }
-public func &<V : Value>(lhs: V, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
+public func &<V: Value>(lhs: V, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
     return Operator.bitwiseAnd.infix(lhs, rhs)
 }
 
-public func |<V : Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<V> where V.Datatype == Int64 {
+public func |<V: Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<V> where V.Datatype == Int64 {
     return Operator.bitwiseOr.infix(lhs, rhs)
 }
-public func |<V : Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
+public func |<V: Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
     return Operator.bitwiseOr.infix(lhs, rhs)
 }
-public func |<V : Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<V?> where V.Datatype == Int64 {
+public func |<V: Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<V?> where V.Datatype == Int64 {
     return Operator.bitwiseOr.infix(lhs, rhs)
 }
-public func |<V : Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
+public func |<V: Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
     return Operator.bitwiseOr.infix(lhs, rhs)
 }
-public func |<V : Value>(lhs: Expression<V>, rhs: V) -> Expression<V> where V.Datatype == Int64 {
+public func |<V: Value>(lhs: Expression<V>, rhs: V) -> Expression<V> where V.Datatype == Int64 {
     return Operator.bitwiseOr.infix(lhs, rhs)
 }
-public func |<V : Value>(lhs: Expression<V?>, rhs: V) -> Expression<V?> where V.Datatype == Int64 {
+public func |<V: Value>(lhs: Expression<V?>, rhs: V) -> Expression<V?> where V.Datatype == Int64 {
     return Operator.bitwiseOr.infix(lhs, rhs)
 }
-public func |<V : Value>(lhs: V, rhs: Expression<V>) -> Expression<V> where V.Datatype == Int64 {
+public func |<V: Value>(lhs: V, rhs: Expression<V>) -> Expression<V> where V.Datatype == Int64 {
     return Operator.bitwiseOr.infix(lhs, rhs)
 }
-public func |<V : Value>(lhs: V, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
+public func |<V: Value>(lhs: V, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
     return Operator.bitwiseOr.infix(lhs, rhs)
 }
 
-public func ^<V : Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<V> where V.Datatype == Int64 {
+public func ^<V: Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<V> where V.Datatype == Int64 {
     return (~(lhs & rhs)) & (lhs | rhs)
 }
-public func ^<V : Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
+public func ^<V: Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
     return (~(lhs & rhs)) & (lhs | rhs)
 }
-public func ^<V : Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<V?> where V.Datatype == Int64 {
+public func ^<V: Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<V?> where V.Datatype == Int64 {
     return (~(lhs & rhs)) & (lhs | rhs)
 }
-public func ^<V : Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
+public func ^<V: Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
     return (~(lhs & rhs)) & (lhs | rhs)
 }
-public func ^<V : Value>(lhs: Expression<V>, rhs: V) -> Expression<V> where V.Datatype == Int64 {
+public func ^<V: Value>(lhs: Expression<V>, rhs: V) -> Expression<V> where V.Datatype == Int64 {
     return (~(lhs & rhs)) & (lhs | rhs)
 }
-public func ^<V : Value>(lhs: Expression<V?>, rhs: V) -> Expression<V?> where V.Datatype == Int64 {
+public func ^<V: Value>(lhs: Expression<V?>, rhs: V) -> Expression<V?> where V.Datatype == Int64 {
     return (~(lhs & rhs)) & (lhs | rhs)
 }
-public func ^<V : Value>(lhs: V, rhs: Expression<V>) -> Expression<V> where V.Datatype == Int64 {
+public func ^<V: Value>(lhs: V, rhs: Expression<V>) -> Expression<V> where V.Datatype == Int64 {
     return (~(lhs & rhs)) & (lhs | rhs)
 }
-public func ^<V : Value>(lhs: V, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
+public func ^<V: Value>(lhs: V, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
     return (~(lhs & rhs)) & (lhs | rhs)
 }
 
-public prefix func ~<V : Value>(rhs: Expression<V>) -> Expression<V> where V.Datatype == Int64 {
+public prefix func ~<V: Value>(rhs: Expression<V>) -> Expression<V> where V.Datatype == Int64 {
     return Operator.bitwiseXor.wrap(rhs)
 }
-public prefix func ~<V : Value>(rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
+public prefix func ~<V: Value>(rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
     return Operator.bitwiseXor.wrap(rhs)
 }
 
 // MARK: -
 
-public func ==<V : Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<Bool> where V.Datatype : Equatable {
+public func ==<V: Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<Bool> where V.Datatype: Equatable {
     return Operator.eq.infix(lhs, rhs)
 }
-public func ==<V : Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Equatable {
+public func ==<V: Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype: Equatable {
     return Operator.eq.infix(lhs, rhs)
 }
-public func ==<V : Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<Bool?> where V.Datatype : Equatable {
+public func ==<V: Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<Bool?> where V.Datatype: Equatable {
     return Operator.eq.infix(lhs, rhs)
 }
-public func ==<V : Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Equatable {
+public func ==<V: Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype: Equatable {
     return Operator.eq.infix(lhs, rhs)
 }
-public func ==<V : Value>(lhs: Expression<V>, rhs: V) -> Expression<Bool> where V.Datatype : Equatable {
+public func ==<V: Value>(lhs: Expression<V>, rhs: V) -> Expression<Bool> where V.Datatype: Equatable {
     return Operator.eq.infix(lhs, rhs)
 }
-public func ==<V : Value>(lhs: Expression<V?>, rhs: V?) -> Expression<Bool?> where V.Datatype : Equatable {
+public func ==<V: Value>(lhs: Expression<V?>, rhs: V?) -> Expression<Bool?> where V.Datatype: Equatable {
     guard let rhs = rhs else { return "IS".infix(lhs, Expression<V?>(value: nil)) }
     return Operator.eq.infix(lhs, rhs)
 }
-public func ==<V : Value>(lhs: V, rhs: Expression<V>) -> Expression<Bool> where V.Datatype : Equatable {
+public func ==<V: Value>(lhs: V, rhs: Expression<V>) -> Expression<Bool> where V.Datatype: Equatable {
     return Operator.eq.infix(lhs, rhs)
 }
-public func ==<V : Value>(lhs: V?, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Equatable {
+public func ==<V: Value>(lhs: V?, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype: Equatable {
     guard let lhs = lhs else { return "IS".infix(Expression<V?>(value: nil), rhs) }
     return Operator.eq.infix(lhs, rhs)
 }
 
-public func ===<V : Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<Bool> where V.Datatype : Equatable {
+public func ===<V: Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<Bool> where V.Datatype: Equatable {
     return "IS".infix(lhs, rhs)
 }
-public func ===<V : Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Equatable {
+public func ===<V: Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype: Equatable {
     return "IS".infix(lhs, rhs)
 }
-public func ===<V : Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<Bool?> where V.Datatype : Equatable {
+public func ===<V: Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<Bool?> where V.Datatype: Equatable {
     return "IS".infix(lhs, rhs)
 }
-public func ===<V : Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Equatable {
+public func ===<V: Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype: Equatable {
     return "IS".infix(lhs, rhs)
 }
-public func ===<V : Value>(lhs: Expression<V>, rhs: V) -> Expression<Bool> where V.Datatype : Equatable {
+public func ===<V: Value>(lhs: Expression<V>, rhs: V) -> Expression<Bool> where V.Datatype: Equatable {
     return "IS".infix(lhs, rhs)
 }
-public func ===<V : Value>(lhs: Expression<V?>, rhs: V?) -> Expression<Bool?> where V.Datatype : Equatable {
+public func ===<V: Value>(lhs: Expression<V?>, rhs: V?) -> Expression<Bool?> where V.Datatype: Equatable {
     guard let rhs = rhs else { return "IS".infix(lhs, Expression<V?>(value: nil)) }
     return "IS".infix(lhs, rhs)
 }
-public func ===<V : Value>(lhs: V, rhs: Expression<V>) -> Expression<Bool> where V.Datatype : Equatable {
+public func ===<V: Value>(lhs: V, rhs: Expression<V>) -> Expression<Bool> where V.Datatype: Equatable {
     return "IS".infix(lhs, rhs)
 }
-public func ===<V : Value>(lhs: V?, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Equatable {
+public func ===<V: Value>(lhs: V?, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype: Equatable {
     guard let lhs = lhs else { return "IS".infix(Expression<V?>(value: nil), rhs) }
     return "IS".infix(lhs, rhs)
 }
 
-public func !=<V : Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<Bool> where V.Datatype : Equatable {
+public func !=<V: Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<Bool> where V.Datatype: Equatable {
     return Operator.neq.infix(lhs, rhs)
 }
-public func !=<V : Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Equatable {
+public func !=<V: Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype: Equatable {
     return Operator.neq.infix(lhs, rhs)
 }
-public func !=<V : Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<Bool?> where V.Datatype : Equatable {
+public func !=<V: Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<Bool?> where V.Datatype: Equatable {
     return Operator.neq.infix(lhs, rhs)
 }
-public func !=<V : Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Equatable {
+public func !=<V: Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype: Equatable {
     return Operator.neq.infix(lhs, rhs)
 }
-public func !=<V : Value>(lhs: Expression<V>, rhs: V) -> Expression<Bool> where V.Datatype : Equatable {
+public func !=<V: Value>(lhs: Expression<V>, rhs: V) -> Expression<Bool> where V.Datatype: Equatable {
     return Operator.neq.infix(lhs, rhs)
 }
-public func !=<V : Value>(lhs: Expression<V?>, rhs: V?) -> Expression<Bool?> where V.Datatype : Equatable {
+public func !=<V: Value>(lhs: Expression<V?>, rhs: V?) -> Expression<Bool?> where V.Datatype: Equatable {
     guard let rhs = rhs else { return "IS NOT".infix(lhs, Expression<V?>(value: nil)) }
     return Operator.neq.infix(lhs, rhs)
 }
-public func !=<V : Value>(lhs: V, rhs: Expression<V>) -> Expression<Bool> where V.Datatype : Equatable {
+public func !=<V: Value>(lhs: V, rhs: Expression<V>) -> Expression<Bool> where V.Datatype: Equatable {
     return Operator.neq.infix(lhs, rhs)
 }
-public func !=<V : Value>(lhs: V?, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Equatable {
+public func !=<V: Value>(lhs: V?, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype: Equatable {
     guard let lhs = lhs else { return "IS NOT".infix(Expression<V?>(value: nil), rhs) }
     return Operator.neq.infix(lhs, rhs)
 }
 
-public func !==<V : Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<Bool> where V.Datatype : Equatable {
+public func !==<V: Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<Bool> where V.Datatype: Equatable {
     return "IS NOT".infix(lhs, rhs)
 }
-public func !==<V : Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Equatable {
+public func !==<V: Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype: Equatable {
     return "IS NOT".infix(lhs, rhs)
 }
-public func !==<V : Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<Bool?> where V.Datatype : Equatable {
+public func !==<V: Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<Bool?> where V.Datatype: Equatable {
     return "IS NOT".infix(lhs, rhs)
 }
-public func !==<V : Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Equatable {
+public func !==<V: Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype: Equatable {
     return "IS NOT".infix(lhs, rhs)
 }
-public func !==<V : Value>(lhs: Expression<V>, rhs: V) -> Expression<Bool> where V.Datatype : Equatable {
+public func !==<V: Value>(lhs: Expression<V>, rhs: V) -> Expression<Bool> where V.Datatype: Equatable {
     return "IS NOT".infix(lhs, rhs)
 }
-public func !==<V : Value>(lhs: Expression<V?>, rhs: V?) -> Expression<Bool?> where V.Datatype : Equatable {
+public func !==<V: Value>(lhs: Expression<V?>, rhs: V?) -> Expression<Bool?> where V.Datatype: Equatable {
     guard let rhs = rhs else { return "IS NOT".infix(lhs, Expression<V?>(value: nil)) }
     return "IS NOT".infix(lhs, rhs)
 }
-public func !==<V : Value>(lhs: V, rhs: Expression<V>) -> Expression<Bool> where V.Datatype : Equatable {
+public func !==<V: Value>(lhs: V, rhs: Expression<V>) -> Expression<Bool> where V.Datatype: Equatable {
     return "IS NOT".infix(lhs, rhs)
 }
-public func !==<V : Value>(lhs: V?, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Equatable {
+public func !==<V: Value>(lhs: V?, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype: Equatable {
     guard let lhs = lhs else { return "IS NOT".infix(Expression<V?>(value: nil), rhs) }
     return "IS NOT".infix(lhs, rhs)
 }
 
-
-public func ><V : Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<Bool> where V.Datatype : Comparable {
+public func ><V: Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<Bool> where V.Datatype: Comparable {
     return Operator.gt.infix(lhs, rhs)
 }
-public func ><V : Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Comparable {
+public func ><V: Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype: Comparable {
     return Operator.gt.infix(lhs, rhs)
 }
-public func ><V : Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<Bool?> where V.Datatype : Comparable {
+public func ><V: Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<Bool?> where V.Datatype: Comparable {
     return Operator.gt.infix(lhs, rhs)
 }
-public func ><V : Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Comparable {
+public func ><V: Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype: Comparable {
     return Operator.gt.infix(lhs, rhs)
 }
-public func ><V : Value>(lhs: Expression<V>, rhs: V) -> Expression<Bool> where V.Datatype : Comparable {
+public func ><V: Value>(lhs: Expression<V>, rhs: V) -> Expression<Bool> where V.Datatype: Comparable {
     return Operator.gt.infix(lhs, rhs)
 }
-public func ><V : Value>(lhs: Expression<V?>, rhs: V) -> Expression<Bool?> where V.Datatype : Comparable {
+public func ><V: Value>(lhs: Expression<V?>, rhs: V) -> Expression<Bool?> where V.Datatype: Comparable {
     return Operator.gt.infix(lhs, rhs)
 }
-public func ><V : Value>(lhs: V, rhs: Expression<V>) -> Expression<Bool> where V.Datatype : Comparable {
+public func ><V: Value>(lhs: V, rhs: Expression<V>) -> Expression<Bool> where V.Datatype: Comparable {
     return Operator.gt.infix(lhs, rhs)
 }
-public func ><V : Value>(lhs: V, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Comparable {
+public func ><V: Value>(lhs: V, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype: Comparable {
     return Operator.gt.infix(lhs, rhs)
 }
 
-public func >=<V : Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<Bool> where V.Datatype : Comparable {
+public func >=<V: Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<Bool> where V.Datatype: Comparable {
     return Operator.gte.infix(lhs, rhs)
 }
-public func >=<V : Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Comparable {
+public func >=<V: Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype: Comparable {
     return Operator.gte.infix(lhs, rhs)
 }
-public func >=<V : Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<Bool?> where V.Datatype : Comparable {
+public func >=<V: Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<Bool?> where V.Datatype: Comparable {
     return Operator.gte.infix(lhs, rhs)
 }
-public func >=<V : Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Comparable {
+public func >=<V: Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype: Comparable {
     return Operator.gte.infix(lhs, rhs)
 }
-public func >=<V : Value>(lhs: Expression<V>, rhs: V) -> Expression<Bool> where V.Datatype : Comparable {
+public func >=<V: Value>(lhs: Expression<V>, rhs: V) -> Expression<Bool> where V.Datatype: Comparable {
     return Operator.gte.infix(lhs, rhs)
 }
-public func >=<V : Value>(lhs: Expression<V?>, rhs: V) -> Expression<Bool?> where V.Datatype : Comparable {
+public func >=<V: Value>(lhs: Expression<V?>, rhs: V) -> Expression<Bool?> where V.Datatype: Comparable {
     return Operator.gte.infix(lhs, rhs)
 }
-public func >=<V : Value>(lhs: V, rhs: Expression<V>) -> Expression<Bool> where V.Datatype : Comparable {
+public func >=<V: Value>(lhs: V, rhs: Expression<V>) -> Expression<Bool> where V.Datatype: Comparable {
     return Operator.gte.infix(lhs, rhs)
 }
-public func >=<V : Value>(lhs: V, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Comparable {
+public func >=<V: Value>(lhs: V, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype: Comparable {
     return Operator.gte.infix(lhs, rhs)
 }
 
-public func <<V : Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<Bool> where V.Datatype : Comparable {
+public func <<V: Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<Bool> where V.Datatype: Comparable {
     return Operator.lt.infix(lhs, rhs)
 }
-public func <<V : Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Comparable {
+public func <<V: Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype: Comparable {
     return Operator.lt.infix(lhs, rhs)
 }
-public func <<V : Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<Bool?> where V.Datatype : Comparable {
+public func <<V: Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<Bool?> where V.Datatype: Comparable {
     return Operator.lt.infix(lhs, rhs)
 }
-public func <<V : Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Comparable {
+public func <<V: Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype: Comparable {
     return Operator.lt.infix(lhs, rhs)
 }
-public func <<V : Value>(lhs: Expression<V>, rhs: V) -> Expression<Bool> where V.Datatype : Comparable {
+public func <<V: Value>(lhs: Expression<V>, rhs: V) -> Expression<Bool> where V.Datatype: Comparable {
     return Operator.lt.infix(lhs, rhs)
 }
-public func <<V : Value>(lhs: Expression<V?>, rhs: V) -> Expression<Bool?> where V.Datatype : Comparable {
+public func <<V: Value>(lhs: Expression<V?>, rhs: V) -> Expression<Bool?> where V.Datatype: Comparable {
     return Operator.lt.infix(lhs, rhs)
 }
-public func <<V : Value>(lhs: V, rhs: Expression<V>) -> Expression<Bool> where V.Datatype : Comparable {
+public func <<V: Value>(lhs: V, rhs: Expression<V>) -> Expression<Bool> where V.Datatype: Comparable {
     return Operator.lt.infix(lhs, rhs)
 }
-public func <<V : Value>(lhs: V, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Comparable {
+public func <<V: Value>(lhs: V, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype: Comparable {
     return Operator.lt.infix(lhs, rhs)
 }
 
-public func <=<V : Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<Bool> where V.Datatype : Comparable {
+public func <=<V: Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<Bool> where V.Datatype: Comparable {
     return Operator.lte.infix(lhs, rhs)
 }
-public func <=<V : Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Comparable {
+public func <=<V: Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype: Comparable {
     return Operator.lte.infix(lhs, rhs)
 }
-public func <=<V : Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<Bool?> where V.Datatype : Comparable {
+public func <=<V: Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<Bool?> where V.Datatype: Comparable {
     return Operator.lte.infix(lhs, rhs)
 }
-public func <=<V : Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Comparable {
+public func <=<V: Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype: Comparable {
     return Operator.lte.infix(lhs, rhs)
 }
-public func <=<V : Value>(lhs: Expression<V>, rhs: V) -> Expression<Bool> where V.Datatype : Comparable {
+public func <=<V: Value>(lhs: Expression<V>, rhs: V) -> Expression<Bool> where V.Datatype: Comparable {
     return Operator.lte.infix(lhs, rhs)
 }
-public func <=<V : Value>(lhs: Expression<V?>, rhs: V) -> Expression<Bool?> where V.Datatype : Comparable {
+public func <=<V: Value>(lhs: Expression<V?>, rhs: V) -> Expression<Bool?> where V.Datatype: Comparable {
     return Operator.lte.infix(lhs, rhs)
 }
-public func <=<V : Value>(lhs: V, rhs: Expression<V>) -> Expression<Bool> where V.Datatype : Comparable {
+public func <=<V: Value>(lhs: V, rhs: Expression<V>) -> Expression<Bool> where V.Datatype: Comparable {
     return Operator.lte.infix(lhs, rhs)
 }
-public func <=<V : Value>(lhs: V, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Comparable {
+public func <=<V: Value>(lhs: V, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype: Comparable {
     return Operator.lte.infix(lhs, rhs)
 }
 
-public func ~=<V : Value>(lhs: ClosedRange<V>, rhs: Expression<V>) -> Expression<Bool> where V.Datatype : Comparable & Value {
+public func ~=<V: Value>(lhs: ClosedRange<V>, rhs: Expression<V>) -> Expression<Bool> where V.Datatype: Comparable & Value {
     return Expression("\(rhs.template) BETWEEN ? AND ?", rhs.bindings + [lhs.lowerBound.datatypeValue, lhs.upperBound.datatypeValue])
 }
 
-public func ~=<V : Value>(lhs: ClosedRange<V>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Comparable & Value {
+public func ~=<V: Value>(lhs: ClosedRange<V>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype: Comparable & Value {
     return Expression("\(rhs.template) BETWEEN ? AND ?", rhs.bindings + [lhs.lowerBound.datatypeValue, lhs.upperBound.datatypeValue])
 }
 
-public func ~=<V : Value>(lhs: Range<V>, rhs: Expression<V>) -> Expression<Bool> where V.Datatype : Comparable & Value {
-    return Expression("\(rhs.template) >= ? AND \(rhs.template) < ?", rhs.bindings + [lhs.lowerBound.datatypeValue] + rhs.bindings + [lhs.upperBound.datatypeValue])
+public func ~=<V: Value>(lhs: Range<V>, rhs: Expression<V>) -> Expression<Bool> where V.Datatype: Comparable & Value {
+    return Expression("\(rhs.template) >= ? AND \(rhs.template) < ?", rhs.bindings + [lhs.lowerBound.datatypeValue] +
+            rhs.bindings + [lhs.upperBound.datatypeValue])
 }
 
-public func ~=<V : Value>(lhs: Range<V>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Comparable & Value {
-    return Expression("\(rhs.template) >= ? AND \(rhs.template) < ?", rhs.bindings + [lhs.lowerBound.datatypeValue] + rhs.bindings + [lhs.upperBound.datatypeValue])
+public func ~=<V: Value>(lhs: Range<V>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype: Comparable & Value {
+    return Expression("\(rhs.template) >= ? AND \(rhs.template) < ?", rhs.bindings + [lhs.lowerBound.datatypeValue] +
+            rhs.bindings + [lhs.upperBound.datatypeValue])
 }
 
-public func ~=<V : Value>(lhs: PartialRangeThrough<V>, rhs: Expression<V>) -> Expression<Bool> where V.Datatype : Comparable & Value {
+public func ~=<V: Value>(lhs: PartialRangeThrough<V>, rhs: Expression<V>) -> Expression<Bool> where V.Datatype: Comparable & Value {
     return Expression("\(rhs.template) <= ?", rhs.bindings + [lhs.upperBound.datatypeValue])
 }
 
-public func ~=<V : Value>(lhs: PartialRangeThrough<V>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Comparable & Value {
+public func ~=<V: Value>(lhs: PartialRangeThrough<V>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype: Comparable & Value {
     return Expression("\(rhs.template) <= ?", rhs.bindings + [lhs.upperBound.datatypeValue])
 }
 
-public func ~=<V : Value>(lhs: PartialRangeUpTo<V>, rhs: Expression<V>) -> Expression<Bool> where V.Datatype : Comparable & Value {
+public func ~=<V: Value>(lhs: PartialRangeUpTo<V>, rhs: Expression<V>) -> Expression<Bool> where V.Datatype: Comparable & Value {
     return Expression("\(rhs.template) < ?", rhs.bindings + [lhs.upperBound.datatypeValue])
 }
 
-public func ~=<V : Value>(lhs: PartialRangeUpTo<V>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Comparable & Value {
+public func ~=<V: Value>(lhs: PartialRangeUpTo<V>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype: Comparable & Value {
     return Expression("\(rhs.template) < ?", rhs.bindings + [lhs.upperBound.datatypeValue])
 }
 
-public func ~=<V : Value>(lhs: PartialRangeFrom<V>, rhs: Expression<V>) -> Expression<Bool> where V.Datatype : Comparable & Value {
+public func ~=<V: Value>(lhs: PartialRangeFrom<V>, rhs: Expression<V>) -> Expression<Bool> where V.Datatype: Comparable & Value {
     return Expression("\(rhs.template) >= ?", rhs.bindings + [lhs.lowerBound.datatypeValue])
 }
 
-public func ~=<V : Value>(lhs: PartialRangeFrom<V>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype : Comparable & Value {
+public func ~=<V: Value>(lhs: PartialRangeFrom<V>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype: Comparable & Value {
     return Expression("\(rhs.template) >= ?", rhs.bindings + [lhs.lowerBound.datatypeValue])
 }
 
@@ -667,6 +668,7 @@ public func ||(lhs: Bool, rhs: Expression<Bool?>) -> Expression<Bool?> {
 public prefix func !(rhs: Expression<Bool>) -> Expression<Bool> {
     return Operator.not.wrap(rhs)
 }
+
 public prefix func !(rhs: Expression<Bool?>) -> Expression<Bool?> {
     return Operator.not.wrap(rhs)
 }
diff --git a/Sources/SQLite/Typed/Query.swift b/Sources/SQLite/Typed/Query.swift
index 669001d3..8d3ded52 100644
--- a/Sources/SQLite/Typed/Query.swift
+++ b/Sources/SQLite/Typed/Query.swift
@@ -21,10 +21,10 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 //
-
+// swiftlint:disable file_length
 import Foundation
 
-public protocol QueryType : Expressible {
+public protocol QueryType: Expressible {
 
     var clauses: QueryClauses { get set }
 
@@ -32,7 +32,7 @@ public protocol QueryType : Expressible {
 
 }
 
-public protocol SchemaType : QueryType {
+public protocol SchemaType: QueryType {
 
     static var identifier: String { get }
 
@@ -141,10 +141,10 @@ extension SchemaType {
     /// - Parameter all: A list of expressions to select.
     ///
     /// - Returns: A query with the given `SELECT` clause applied.
-    public func select<V : Value>(_ column: Expression<V>) -> ScalarQuery<V> {
+    public func select<V: Value>(_ column: Expression<V>) -> ScalarQuery<V> {
         return select(false, [column])
     }
-    public func select<V : Value>(_ column: Expression<V?>) -> ScalarQuery<V?> {
+    public func select<V: Value>(_ column: Expression<V?>) -> ScalarQuery<V?> {
         return select(false, [column])
     }
 
@@ -160,10 +160,10 @@ extension SchemaType {
     /// - Parameter column: A list of expressions to select.
     ///
     /// - Returns: A query with the given `SELECT DISTINCT` clause applied.
-    public func select<V : Value>(distinct column: Expression<V>) -> ScalarQuery<V> {
+    public func select<V: Value>(distinct column: Expression<V>) -> ScalarQuery<V> {
         return select(true, [column])
     }
-    public func select<V : Value>(distinct column: Expression<V?>) -> ScalarQuery<V?> {
+    public func select<V: Value>(distinct column: Expression<V?>) -> ScalarQuery<V?> {
         return select(true, [column])
     }
 
@@ -175,7 +175,7 @@ extension SchemaType {
 
 extension QueryType {
 
-    fileprivate func select<Q : QueryType>(_ distinct: Bool, _ columns: [Expressible]) -> Q {
+    fileprivate func select<Q: QueryType>(_ distinct: Bool, _ columns: [Expressible]) -> Q {
         var query = Q.init(clauses.from.name, database: clauses.from.database)
         query.clauses = clauses
         query.clauses.select = (distinct, columns)
@@ -183,7 +183,7 @@ extension QueryType {
     }
 
     // MARK: UNION
-    
+
     /// Adds a `UNION` clause to the query.
     ///
     ///     let users = Table("users")
@@ -202,7 +202,7 @@ extension QueryType {
         query.clauses.union.append(table)
         return query
     }
-    
+
     // MARK: JOIN
 
     /// Adds a `JOIN` clause to the query.
@@ -589,12 +589,12 @@ extension QueryType {
             Expression<Void>(literal: "OFFSET \(offset)")
         ])
     }
-    
+
     fileprivate var unionClause: Expressible? {
         guard !clauses.union.isEmpty else {
             return nil
         }
-        
+
         return " ".join(clauses.union.map { query in
             " ".join([
                 Expression<Void>(literal: "UNION"),
@@ -709,28 +709,28 @@ extension QueryType {
             query.expression
         ]).expression)
     }
-    
+
     // MARK: UPSERT
-    
+
     public func upsert(_ insertValues: Setter..., onConflictOf conflicting: Expressible) -> Insert {
         return upsert(insertValues, onConflictOf: conflicting)
     }
-    
+
     public func upsert(_ insertValues: [Setter], onConflictOf conflicting: Expressible) -> Insert {
         let setValues = insertValues.filter { $0.column.asSQL() != conflicting.asSQL() }
             .map { Setter(excluded: $0.column) }
         return upsert(insertValues, onConflictOf: conflicting, set: setValues)
     }
-    
+
     public func upsert(_ insertValues: Setter..., onConflictOf conflicting: Expressible, set setValues: [Setter]) -> Insert {
         return upsert(insertValues, onConflictOf: conflicting, set: setValues)
     }
-    
+
     public func upsert(_ insertValues: [Setter], onConflictOf conflicting: Expressible, set setValues: [Setter]) -> Insert {
         let insert = insertValues.reduce((columns: [Expressible](), values: [Expressible]())) { insert, setter in
             (insert.columns + [setter.column], insert.values + [setter.value])
         }
-        
+
         let clauses: [Expressible?] = [
             Expression<Void>(literal: "INSERT"),
             Expression<Void>(literal: "INTO"),
@@ -744,11 +744,10 @@ extension QueryType {
             Expression<Void>(literal: "DO UPDATE SET"),
             ", ".join(setValues.map { $0.expression })
         ]
-        
+
         return Insert(" ".join(clauses.compactMap { $0 }).expression)
     }
 
-
     // MARK: UPDATE
 
     public func update(_ values: Setter...) -> Update {
@@ -826,7 +825,7 @@ extension QueryType {
 
     // TODO: alias support
     func tableName(alias aliased: Bool = false) -> Expressible {
-        guard let alias = clauses.from.alias , aliased else {
+        guard let alias = clauses.from.alias, aliased else {
             return database(namespace: clauses.from.alias ?? clauses.from.name)
         }
 
@@ -874,7 +873,7 @@ extension QueryType {
 
 /// Queries a collection of chainable helper functions and expressions to build
 /// executable SQL statements.
-public struct Table : SchemaType {
+public struct Table: SchemaType {
 
     public static let identifier = "TABLE"
 
@@ -886,7 +885,7 @@ public struct Table : SchemaType {
 
 }
 
-public struct View : SchemaType {
+public struct View: SchemaType {
 
     public static let identifier = "VIEW"
 
@@ -898,7 +897,7 @@ public struct View : SchemaType {
 
 }
 
-public struct VirtualTable : SchemaType {
+public struct VirtualTable: SchemaType {
 
     public static let identifier = "VIRTUAL TABLE"
 
@@ -912,7 +911,7 @@ public struct VirtualTable : SchemaType {
 
 // TODO: make `ScalarQuery` work in `QueryType.select()`, `.filter()`, etc.
 
-public struct ScalarQuery<V> : QueryType {
+public struct ScalarQuery<V>: QueryType {
 
     public var clauses: QueryClauses
 
@@ -924,7 +923,7 @@ public struct ScalarQuery<V> : QueryType {
 
 // TODO: decide: simplify the below with a boxed type instead
 
-public struct Select<T> : ExpressionType {
+public struct Select<T>: ExpressionType {
 
     public var template: String
     public var bindings: [Binding?]
@@ -936,7 +935,7 @@ public struct Select<T> : ExpressionType {
 
 }
 
-public struct Insert : ExpressionType {
+public struct Insert: ExpressionType {
 
     public var template: String
     public var bindings: [Binding?]
@@ -948,7 +947,7 @@ public struct Insert : ExpressionType {
 
 }
 
-public struct Update : ExpressionType {
+public struct Update: ExpressionType {
 
     public var template: String
     public var bindings: [Binding?]
@@ -960,7 +959,7 @@ public struct Update : ExpressionType {
 
 }
 
-public struct Delete : ExpressionType {
+public struct Delete: ExpressionType {
 
     public var template: String
     public var bindings: [Binding?]
@@ -972,7 +971,6 @@ public struct Delete : ExpressionType {
 
 }
 
-
 public struct RowIterator: FailableIterator {
     public typealias Element = Row
     let statement: Statement
@@ -1003,7 +1001,6 @@ extension Connection {
             AnyIterator { statement.next().map { Row(columnNames, $0) } }
         }
     }
-    
 
     public func prepareRowIterator(_ query: QueryType) throws -> RowIterator {
         let expression = query.expression
@@ -1017,9 +1014,9 @@ extension Connection {
             var names = each.expression.template.split { $0 == "." }.map(String.init)
             let column = names.removeLast()
             let namespace = names.joined(separator: ".")
-            
+
             func expandGlob(_ namespace: Bool) -> ((QueryType) throws -> Void) {
-                return { (query: QueryType) throws -> (Void) in
+                return { (query: QueryType) throws -> Void in
                     var q = type(of: query).init(query.clauses.from.name, database: query.clauses.from.database)
                     q.clauses.select = query.clauses.select
                     let e = q.expression
@@ -1028,7 +1025,7 @@ extension Connection {
                     for name in names { columnNames[name] = idx; idx += 1 }
                 }
             }
-            
+
             if column == "*" {
                 var select = query
                 select.clauses.select = (false, [Expression<Void>(literal: "*") as Expressible])
@@ -1047,30 +1044,30 @@ extension Connection {
                 }
                 continue
             }
-            
+
             columnNames[each.expression.template] = idx
             idx += 1
         }
         return columnNames
     }
 
-    public func scalar<V : Value>(_ query: ScalarQuery<V>) throws -> V {
+    public func scalar<V: Value>(_ query: ScalarQuery<V>) throws -> V {
         let expression = query.expression
         return value(try scalar(expression.template, expression.bindings))
     }
 
-    public func scalar<V : Value>(_ query: ScalarQuery<V?>) throws -> V.ValueType? {
+    public func scalar<V: Value>(_ query: ScalarQuery<V?>) throws -> V.ValueType? {
         let expression = query.expression
         guard let value = try scalar(expression.template, expression.bindings) as? V.Datatype else { return nil }
         return V.fromDatatypeValue(value)
     }
 
-    public func scalar<V : Value>(_ query: Select<V>) throws -> V {
+    public func scalar<V: Value>(_ query: Select<V>) throws -> V {
         let expression = query.expression
         return value(try scalar(expression.template, expression.bindings))
     }
 
-    public func scalar<V : Value>(_ query: Select<V?>) throws ->  V.ValueType? {
+    public func scalar<V: Value>(_ query: Select<V?>) throws ->  V.ValueType? {
         let expression = query.expression
         guard let value = try scalar(expression.template, expression.bindings) as? V.Datatype else { return nil }
         return V.fromDatatypeValue(value)
@@ -1186,17 +1183,17 @@ public struct Row {
         return valueAtIndex(idx)
     }
 
-    public subscript<T : Value>(column: Expression<T>) -> T {
+    public subscript<T: Value>(column: Expression<T>) -> T {
         return try! get(column)
     }
 
-    public subscript<T : Value>(column: Expression<T?>) -> T? {
+    public subscript<T: Value>(column: Expression<T?>) -> T? {
         return try! get(column)
     }
 }
 
 /// Determines the join operator for a query’s `JOIN` clause.
-public enum JoinType : String {
+public enum JoinType: String {
 
     /// A `CROSS` join.
     case cross = "CROSS"
@@ -1241,7 +1238,7 @@ public struct QueryClauses {
     var order = [Expressible]()
 
     var limit: (length: Int, offset: Int?)?
-    
+
     var union = [QueryType]()
 
     fileprivate init(_ name: String, alias: String?, database: String?) {
@@ -1249,4 +1246,3 @@ public struct QueryClauses {
     }
 
 }
-
diff --git a/Sources/SQLite/Typed/Schema.swift b/Sources/SQLite/Typed/Schema.swift
index febfe68c..d8d45e41 100644
--- a/Sources/SQLite/Typed/Schema.swift
+++ b/Sources/SQLite/Typed/Schema.swift
@@ -36,7 +36,8 @@ extension Table {
 
     // MARK: - CREATE TABLE
 
-    public func create(temporary: Bool = false, ifNotExists: Bool = false, withoutRowid: Bool = false, block: (TableBuilder) -> Void) -> String {
+    public func create(temporary: Bool = false, ifNotExists: Bool = false, withoutRowid: Bool = false,
+                       block: (TableBuilder) -> Void) -> String {
         let builder = TableBuilder()
 
         block(builder)
@@ -62,51 +63,59 @@ extension Table {
 
     // MARK: - ALTER TABLE … ADD COLUMN
 
-    public func addColumn<V : Value>(_ name: Expression<V>, check: Expression<Bool>? = nil, defaultValue: V) -> String {
+    public func addColumn<V: Value>(_ name: Expression<V>, check: Expression<Bool>? = nil, defaultValue: V) -> String {
         return addColumn(definition(name, V.declaredDatatype, nil, false, false, check, defaultValue, nil, nil))
     }
 
-    public func addColumn<V : Value>(_ name: Expression<V>, check: Expression<Bool?>, defaultValue: V) -> String {
+    public func addColumn<V: Value>(_ name: Expression<V>, check: Expression<Bool?>, defaultValue: V) -> String {
         return addColumn(definition(name, V.declaredDatatype, nil, false, false, check, defaultValue, nil, nil))
     }
 
-    public func addColumn<V : Value>(_ name: Expression<V?>, check: Expression<Bool>? = nil, defaultValue: V? = nil) -> String {
+    public func addColumn<V: Value>(_ name: Expression<V?>, check: Expression<Bool>? = nil, defaultValue: V? = nil) -> String {
         return addColumn(definition(name, V.declaredDatatype, nil, true, false, check, defaultValue, nil, nil))
     }
 
-    public func addColumn<V : Value>(_ name: Expression<V?>, check: Expression<Bool?>, defaultValue: V? = nil) -> String {
+    public func addColumn<V: Value>(_ name: Expression<V?>, check: Expression<Bool?>, defaultValue: V? = nil) -> String {
         return addColumn(definition(name, V.declaredDatatype, nil, true, false, check, defaultValue, nil, nil))
     }
 
-    public func addColumn<V : Value>(_ name: Expression<V>, unique: Bool = false, check: Expression<Bool>? = nil, references table: QueryType, _ other: Expression<V>) -> String where V.Datatype == Int64 {
+    public func addColumn<V: Value>(_ name: Expression<V>, unique: Bool = false, check: Expression<Bool>? = nil,
+                                    references table: QueryType, _ other: Expression<V>) -> String where V.Datatype == Int64 {
         return addColumn(definition(name, V.declaredDatatype, nil, false, unique, check, nil, (table, other), nil))
     }
 
-    public func addColumn<V : Value>(_ name: Expression<V>, unique: Bool = false, check: Expression<Bool?>, references table: QueryType, _ other: Expression<V>) -> String where V.Datatype == Int64 {
+    public func addColumn<V: Value>(_ name: Expression<V>, unique: Bool = false, check: Expression<Bool?>,
+                                    references table: QueryType, _ other: Expression<V>) -> String where V.Datatype == Int64 {
         return addColumn(definition(name, V.declaredDatatype, nil, false, unique, check, nil, (table, other), nil))
     }
 
-    public func addColumn<V : Value>(_ name: Expression<V?>, unique: Bool = false, check: Expression<Bool>? = nil, references table: QueryType, _ other: Expression<V>) -> String where V.Datatype == Int64 {
+    public func addColumn<V: Value>(_ name: Expression<V?>, unique: Bool = false, check: Expression<Bool>? = nil,
+                                    references table: QueryType, _ other: Expression<V>) -> String where V.Datatype == Int64 {
         return addColumn(definition(name, V.declaredDatatype, nil, true, unique, check, nil, (table, other), nil))
     }
 
-    public func addColumn<V : Value>(_ name: Expression<V?>, unique: Bool = false, check: Expression<Bool?>, references table: QueryType, _ other: Expression<V>) -> String where V.Datatype == Int64 {
+    public func addColumn<V: Value>(_ name: Expression<V?>, unique: Bool = false, check: Expression<Bool?>,
+                                    references table: QueryType, _ other: Expression<V>) -> String where V.Datatype == Int64 {
         return addColumn(definition(name, V.declaredDatatype, nil, true, unique, check, nil, (table, other), nil))
     }
 
-    public func addColumn<V : Value>(_ name: Expression<V>, check: Expression<Bool>? = nil, defaultValue: V, collate: Collation) -> String where V.Datatype == String {
+    public func addColumn<V: Value>(_ name: Expression<V>, check: Expression<Bool>? = nil, defaultValue: V,
+                                    collate: Collation) -> String where V.Datatype == String {
         return addColumn(definition(name, V.declaredDatatype, nil, false, false, check, defaultValue, nil, collate))
     }
 
-    public func addColumn<V : Value>(_ name: Expression<V>, check: Expression<Bool?>, defaultValue: V, collate: Collation) -> String where V.Datatype == String {
+    public func addColumn<V: Value>(_ name: Expression<V>, check: Expression<Bool?>, defaultValue: V,
+                                    collate: Collation) -> String where V.Datatype == String {
         return addColumn(definition(name, V.declaredDatatype, nil, false, false, check, defaultValue, nil, collate))
     }
 
-    public func addColumn<V : Value>(_ name: Expression<V?>, check: Expression<Bool>? = nil, defaultValue: V? = nil, collate: Collation) -> String where V.Datatype == String {
+    public func addColumn<V: Value>(_ name: Expression<V?>, check: Expression<Bool>? = nil, defaultValue: V? = nil,
+                                    collate: Collation) -> String where V.Datatype == String {
         return addColumn(definition(name, V.declaredDatatype, nil, true, false, check, defaultValue, nil, collate))
     }
 
-    public func addColumn<V : Value>(_ name: Expression<V?>, check: Expression<Bool?>, defaultValue: V? = nil, collate: Collation) -> String where V.Datatype == String {
+    public func addColumn<V: Value>(_ name: Expression<V?>, check: Expression<Bool?>, defaultValue: V? = nil,
+                                    collate: Collation) -> String where V.Datatype == String {
         return addColumn(definition(name, V.declaredDatatype, nil, true, false, check, defaultValue, nil, collate))
     }
 
@@ -140,7 +149,6 @@ extension Table {
 
     // MARK: - DROP INDEX
 
-
     public func dropIndex(_ columns: Expressible..., ifExists: Bool = false) -> String {
         return drop("INDEX", indexName(columns), ifExists)
     }
@@ -211,138 +219,175 @@ public final class TableBuilder {
 
     fileprivate var definitions = [Expressible]()
 
-    public func column<V : Value>(_ name: Expression<V>, unique: Bool = false, check: Expression<Bool>? = nil, defaultValue: Expression<V>? = nil) {
+    public func column<V: Value>(_ name: Expression<V>, unique: Bool = false, check: Expression<Bool>? = nil,
+                                 defaultValue: Expression<V>? = nil) {
         column(name, V.declaredDatatype, nil, false, unique, check, defaultValue, nil, nil)
     }
 
-    public func column<V : Value>(_ name: Expression<V>, unique: Bool = false, check: Expression<Bool>? = nil, defaultValue: V) {
+    public func column<V: Value>(_ name: Expression<V>, unique: Bool = false, check: Expression<Bool>? = nil,
+                                 defaultValue: V) {
         column(name, V.declaredDatatype, nil, false, unique, check, defaultValue, nil, nil)
     }
 
-    public func column<V : Value>(_ name: Expression<V>, unique: Bool = false, check: Expression<Bool?>, defaultValue: Expression<V>? = nil) {
+    public func column<V: Value>(_ name: Expression<V>, unique: Bool = false, check: Expression<Bool?>,
+                                 defaultValue: Expression<V>? = nil) {
         column(name, V.declaredDatatype, nil, false, unique, check, defaultValue, nil, nil)
     }
 
-    public func column<V : Value>(_ name: Expression<V>, unique: Bool = false, check: Expression<Bool?>, defaultValue: V) {
+    public func column<V: Value>(_ name: Expression<V>, unique: Bool = false, check: Expression<Bool?>,
+                                 defaultValue: V) {
         column(name, V.declaredDatatype, nil, false, unique, check, defaultValue, nil, nil)
     }
 
-    public func column<V : Value>(_ name: Expression<V?>, unique: Bool = false, check: Expression<Bool>? = nil, defaultValue: Expression<V>? = nil) {
+    public func column<V: Value>(_ name: Expression<V?>, unique: Bool = false, check: Expression<Bool>? = nil,
+                                 defaultValue: Expression<V>? = nil) {
         column(name, V.declaredDatatype, nil, true, unique, check, defaultValue, nil, nil)
     }
 
-    public func column<V : Value>(_ name: Expression<V?>, unique: Bool = false, check: Expression<Bool>? = nil, defaultValue: Expression<V?>) {
+    public func column<V: Value>(_ name: Expression<V?>, unique: Bool = false, check: Expression<Bool>? = nil,
+                                 defaultValue: Expression<V?>) {
         column(name, V.declaredDatatype, nil, true, unique, check, defaultValue, nil, nil)
     }
 
-    public func column<V : Value>(_ name: Expression<V?>, unique: Bool = false, check: Expression<Bool>? = nil, defaultValue: V) {
+    public func column<V: Value>(_ name: Expression<V?>, unique: Bool = false, check: Expression<Bool>? = nil,
+                                 defaultValue: V) {
         column(name, V.declaredDatatype, nil, true, unique, check, defaultValue, nil, nil)
     }
 
-    public func column<V : Value>(_ name: Expression<V?>, unique: Bool = false, check: Expression<Bool?>, defaultValue: Expression<V>? = nil) {
+    public func column<V: Value>(_ name: Expression<V?>, unique: Bool = false, check: Expression<Bool?>,
+                                 defaultValue: Expression<V>? = nil) {
         column(name, V.declaredDatatype, nil, true, unique, check, defaultValue, nil, nil)
     }
 
-    public func column<V : Value>(_ name: Expression<V?>, unique: Bool = false, check: Expression<Bool?>, defaultValue: Expression<V?>) {
+    public func column<V: Value>(_ name: Expression<V?>, unique: Bool = false, check: Expression<Bool?>,
+                                 defaultValue: Expression<V?>) {
         column(name, V.declaredDatatype, nil, true, unique, check, defaultValue, nil, nil)
     }
 
-    public func column<V : Value>(_ name: Expression<V?>, unique: Bool = false, check: Expression<Bool?>, defaultValue: V) {
+    public func column<V: Value>(_ name: Expression<V?>, unique: Bool = false, check: Expression<Bool?>,
+                                 defaultValue: V) {
         column(name, V.declaredDatatype, nil, true, unique, check, defaultValue, nil, nil)
     }
 
-    public func column<V : Value>(_ name: Expression<V>, primaryKey: Bool, check: Expression<Bool>? = nil, defaultValue: Expression<V>? = nil) {
+    public func column<V: Value>(_ name: Expression<V>, primaryKey: Bool, check: Expression<Bool>? = nil,
+                                 defaultValue: Expression<V>? = nil) {
         column(name, V.declaredDatatype, primaryKey ? .default : nil, false, false, check, defaultValue, nil, nil)
     }
 
-    public func column<V : Value>(_ name: Expression<V>, primaryKey: Bool, check: Expression<Bool?>, defaultValue: Expression<V>? = nil) {
+    public func column<V: Value>(_ name: Expression<V>, primaryKey: Bool, check: Expression<Bool?>,
+                                 defaultValue: Expression<V>? = nil) {
         column(name, V.declaredDatatype, primaryKey ? .default : nil, false, false, check, defaultValue, nil, nil)
     }
 
-    public func column<V : Value>(_ name: Expression<V>, primaryKey: PrimaryKey, check: Expression<Bool>? = nil) where V.Datatype == Int64 {
+    public func column<V: Value>(_ name: Expression<V>, primaryKey: PrimaryKey,
+                                 check: Expression<Bool>? = nil) where V.Datatype == Int64 {
         column(name, V.declaredDatatype, primaryKey, false, false, check, nil, nil, nil)
     }
 
-    public func column<V : Value>(_ name: Expression<V>, primaryKey: PrimaryKey, check: Expression<Bool?>) where V.Datatype == Int64 {
+    public func column<V: Value>(_ name: Expression<V>, primaryKey: PrimaryKey,
+                                 check: Expression<Bool?>) where V.Datatype == Int64 {
         column(name, V.declaredDatatype, primaryKey, false, false, check, nil, nil, nil)
     }
 
-    public func column<V : Value>(_ name: Expression<V>, unique: Bool = false, check: Expression<Bool>? = nil, references table: QueryType, _ other: Expression<V>) where V.Datatype == Int64 {
+    public func column<V: Value>(_ name: Expression<V>, unique: Bool = false, check: Expression<Bool>? = nil,
+                                 references table: QueryType, _ other: Expression<V>) where V.Datatype == Int64 {
         column(name, V.declaredDatatype, nil, false, unique, check, nil, (table, other), nil)
     }
 
-    public func column<V : Value>(_ name: Expression<V>, unique: Bool = false, check: Expression<Bool?>, references table: QueryType, _ other: Expression<V>) where V.Datatype == Int64 {
+    public func column<V: Value>(_ name: Expression<V>, unique: Bool = false, check: Expression<Bool?>,
+                                 references table: QueryType, _ other: Expression<V>) where V.Datatype == Int64 {
         column(name, V.declaredDatatype, nil, false, unique, check, nil, (table, other), nil)
     }
 
-    public func column<V : Value>(_ name: Expression<V?>, unique: Bool = false, check: Expression<Bool>? = nil, references table: QueryType, _ other: Expression<V>) where V.Datatype == Int64 {
+    public func column<V: Value>(_ name: Expression<V?>, unique: Bool = false, check: Expression<Bool>? = nil,
+                                 references table: QueryType, _ other: Expression<V>) where V.Datatype == Int64 {
         column(name, V.declaredDatatype, nil, true, unique, check, nil, (table, other), nil)
     }
 
-    public func column<V : Value>(_ name: Expression<V?>, unique: Bool = false, check: Expression<Bool?>, references table: QueryType, _ other: Expression<V>) where V.Datatype == Int64 {
+    public func column<V: Value>(_ name: Expression<V?>, unique: Bool = false, check: Expression<Bool?>,
+                                 references table: QueryType, _ other: Expression<V>) where V.Datatype == Int64 {
         column(name, V.declaredDatatype, nil, true, unique, check, nil, (table, other), nil)
     }
 
-    public func column<V : Value>(_ name: Expression<V>, unique: Bool = false, check: Expression<Bool>? = nil, defaultValue: Expression<V>? = nil, collate: Collation) where V.Datatype == String {
+    public func column<V: Value>(_ name: Expression<V>, unique: Bool = false, check: Expression<Bool>? = nil,
+                                 defaultValue: Expression<V>? = nil, collate: Collation) where V.Datatype == String {
         column(name, V.declaredDatatype, nil, false, unique, check, defaultValue, nil, collate)
     }
 
-    public func column<V : Value>(_ name: Expression<V>, unique: Bool = false, check: Expression<Bool>? = nil, defaultValue: V, collate: Collation) where V.Datatype == String {
+    public func column<V: Value>(_ name: Expression<V>, unique: Bool = false, check: Expression<Bool>? = nil,
+                                 defaultValue: V, collate: Collation) where V.Datatype == String {
         column(name, V.declaredDatatype, nil, false, unique, check, defaultValue, nil, collate)
     }
 
-    public func column<V : Value>(_ name: Expression<V>, unique: Bool = false, check: Expression<Bool?>, defaultValue: Expression<V>? = nil, collate: Collation) where V.Datatype == String {
+    public func column<V: Value>(_ name: Expression<V>, unique: Bool = false, check: Expression<Bool?>,
+                                 defaultValue: Expression<V>? = nil, collate: Collation) where V.Datatype == String {
         column(name, V.declaredDatatype, nil, false, unique, check, defaultValue, nil, collate)
     }
 
-    public func column<V : Value>(_ name: Expression<V>, unique: Bool = false, check: Expression<Bool?>, defaultValue: V, collate: Collation) where V.Datatype == String {
+    public func column<V: Value>(_ name: Expression<V>, unique: Bool = false, check: Expression<Bool?>,
+                                 defaultValue: V, collate: Collation) where V.Datatype == String {
         column(name, V.declaredDatatype, nil, false, unique, check, defaultValue, nil, collate)
     }
 
-    public func column<V : Value>(_ name: Expression<V?>, unique: Bool = false, check: Expression<Bool>? = nil, defaultValue: Expression<V>? = nil, collate: Collation) where V.Datatype == String {
+    public func column<V: Value>(_ name: Expression<V?>, unique: Bool = false, check: Expression<Bool>? = nil,
+                                 defaultValue: Expression<V>? = nil, collate: Collation) where V.Datatype == String {
         column(name, V.declaredDatatype, nil, true, unique, check, defaultValue, nil, collate)
     }
 
-    public func column<V : Value>(_ name: Expression<V?>, unique: Bool = false, check: Expression<Bool>? = nil, defaultValue: Expression<V?>, collate: Collation) where V.Datatype == String {
+    public func column<V: Value>(_ name: Expression<V?>, unique: Bool = false, check: Expression<Bool>? = nil,
+                                 defaultValue: Expression<V?>, collate: Collation) where V.Datatype == String {
         column(name, V.declaredDatatype, nil, true, unique, check, defaultValue, nil, collate)
     }
 
-    public func column<V : Value>(_ name: Expression<V?>, unique: Bool = false, check: Expression<Bool>? = nil, defaultValue: V, collate: Collation) where V.Datatype == String {
+    public func column<V: Value>(_ name: Expression<V?>, unique: Bool = false, check: Expression<Bool>? = nil,
+                                 defaultValue: V, collate: Collation) where V.Datatype == String {
         column(name, V.declaredDatatype, nil, true, unique, check, defaultValue, nil, collate)
     }
 
-    public func column<V : Value>(_ name: Expression<V?>, unique: Bool = false, check: Expression<Bool?>, defaultValue: Expression<V>? = nil, collate: Collation) where V.Datatype == String {
+    public func column<V: Value>(_ name: Expression<V?>, unique: Bool = false, check: Expression<Bool?>,
+                                 defaultValue: Expression<V>? = nil, collate: Collation) where V.Datatype == String {
         column(name, V.declaredDatatype, nil, true, unique, check, defaultValue, nil, collate)
     }
 
-    public func column<V : Value>(_ name: Expression<V?>, unique: Bool = false, check: Expression<Bool?>, defaultValue: Expression<V?>, collate: Collation) where V.Datatype == String {
+    public func column<V: Value>(_ name: Expression<V?>, unique: Bool = false, check: Expression<Bool?>,
+                                 defaultValue: Expression<V?>, collate: Collation) where V.Datatype == String {
         column(name, V.declaredDatatype, nil, true, unique, check, defaultValue, nil, collate)
     }
 
-    public func column<V : Value>(_ name: Expression<V?>, unique: Bool = false, check: Expression<Bool?>, defaultValue: V, collate: Collation) where V.Datatype == String {
+    public func column<V: Value>(_ name: Expression<V?>, unique: Bool = false, check: Expression<Bool?>,
+                                 defaultValue: V, collate: Collation) where V.Datatype == String {
         column(name, V.declaredDatatype, nil, true, unique, check, defaultValue, nil, collate)
     }
 
-    fileprivate func column(_ name: Expressible, _ datatype: String, _ primaryKey: PrimaryKey?, _ null: Bool, _ unique: Bool, _ check: Expressible?, _ defaultValue: Expressible?, _ references: (QueryType, Expressible)?, _ collate: Collation?) {
+    // swiftlint:disable:next function_parameter_count
+    fileprivate func column(_ name: Expressible, _ datatype: String, _ primaryKey: PrimaryKey?, _ null: Bool,
+                            _ unique: Bool, _ check: Expressible?, _ defaultValue: Expressible?,
+                            _ references: (QueryType, Expressible)?, _ collate: Collation?) {
         definitions.append(definition(name, datatype, primaryKey, null, unique, check, defaultValue, references, collate))
     }
 
     // MARK: -
 
-    public func primaryKey<T : Value>(_ column: Expression<T>) {
+    public func primaryKey<T: Value>(_ column: Expression<T>) {
         primaryKey([column])
     }
 
-    public func primaryKey<T : Value, U : Value>(_ compositeA: Expression<T>, _ b: Expression<U>) {
-        primaryKey([compositeA, b])
+    public func primaryKey<T: Value, U: Value>(_ compositeA: Expression<T>,
+                                               _ expr: Expression<U>) {
+        primaryKey([compositeA, expr])
     }
 
-    public func primaryKey<T : Value, U : Value, V : Value>(_ compositeA: Expression<T>, _ b: Expression<U>, _ c: Expression<V>) {
-        primaryKey([compositeA, b, c])
+    public func primaryKey<T: Value, U: Value, V: Value>(_ compositeA: Expression<T>,
+                                                         _ expr1: Expression<U>,
+                                                         _ expr2: Expression<V>) {
+        primaryKey([compositeA, expr1, expr2])
     }
 
-    public func primaryKey<T : Value, U : Value, V : Value, W : Value>(_ compositeA: Expression<T>, _ b: Expression<U>, _ c: Expression<V>, _ d: Expression<W>) {
-        primaryKey([compositeA, b, c, d])
+    public func primaryKey<T: Value, U: Value, V: Value, W: Value>(_ compositeA: Expression<T>,
+                                                                   _ expr1: Expression<U>,
+                                                                   _ expr2: Expression<V>,
+                                                                   _ expr3: Expression<W>) {
+        primaryKey([compositeA, expr1, expr2, expr3])
     }
 
     fileprivate func primaryKey(_ composite: [Expressible]) {
@@ -379,29 +424,37 @@ public final class TableBuilder {
 
     }
 
-    public func foreignKey<T : Value>(_ column: Expression<T>, references table: QueryType, _ other: Expression<T>, update: Dependency? = nil, delete: Dependency? = nil) {
+    public func foreignKey<T: Value>(_ column: Expression<T>, references table: QueryType, _ other: Expression<T>,
+                                     update: Dependency? = nil, delete: Dependency? = nil) {
         foreignKey(column, (table, other), update, delete)
     }
 
-    public func foreignKey<T : Value>(_ column: Expression<T?>, references table: QueryType, _ other: Expression<T>, update: Dependency? = nil, delete: Dependency? = nil) {
+    public func foreignKey<T: Value>(_ column: Expression<T?>, references table: QueryType, _ other: Expression<T>,
+                                     update: Dependency? = nil, delete: Dependency? = nil) {
         foreignKey(column, (table, other), update, delete)
     }
 
-    public func foreignKey<T : Value, U : Value>(_ composite: (Expression<T>, Expression<U>), references table: QueryType, _ other: (Expression<T>, Expression<U>), update: Dependency? = nil, delete: Dependency? = nil) {
+    public func foreignKey<T: Value, U: Value>(_ composite: (Expression<T>, Expression<U>),
+                                               references table: QueryType, _ other: (Expression<T>, Expression<U>),
+                                               update: Dependency? = nil, delete: Dependency? = nil) {
         let composite = ", ".join([composite.0, composite.1])
         let references = (table, ", ".join([other.0, other.1]))
 
         foreignKey(composite, references, update, delete)
     }
 
-    public func foreignKey<T : Value, U : Value, V : Value>(_ composite: (Expression<T>, Expression<U>, Expression<V>), references table: QueryType, _ other: (Expression<T>, Expression<U>, Expression<V>), update: Dependency? = nil, delete: Dependency? = nil) {
+    public func foreignKey<T: Value, U: Value, V: Value>(_ composite: (Expression<T>, Expression<U>, Expression<V>),
+                                                         references table: QueryType,
+                                                         _ other: (Expression<T>, Expression<U>, Expression<V>),
+                                                         update: Dependency? = nil, delete: Dependency? = nil) {
         let composite = ", ".join([composite.0, composite.1, composite.2])
         let references = (table, ", ".join([other.0, other.1, other.2]))
 
         foreignKey(composite, references, update, delete)
     }
 
-    fileprivate func foreignKey(_ column: Expressible, _ references: (QueryType, Expressible), _ update: Dependency?, _ delete: Dependency?) {
+    fileprivate func foreignKey(_ column: Expressible, _ references: (QueryType, Expressible),
+                                _ update: Dependency?, _ delete: Dependency?) {
         let clauses: [Expressible?] = [
             "FOREIGN KEY".prefix(column),
             reference(references),
@@ -439,7 +492,7 @@ public struct Module {
 
 }
 
-extension Module : Expressible {
+extension Module: Expressible {
 
     public var expression: Expression<Void> {
         return name.wrap(arguments)
@@ -484,7 +537,10 @@ private extension QueryType {
 
 }
 
-private func definition(_ column: Expressible, _ datatype: String, _ primaryKey: PrimaryKey?, _ null: Bool, _ unique: Bool, _ check: Expressible?, _ defaultValue: Expressible?, _ references: (QueryType, Expressible)?, _ collate: Collation?) -> Expressible {
+// swiftlint:disable:next function_parameter_count
+private func definition(_ column: Expressible, _ datatype: String, _ primaryKey: PrimaryKey?, _ null: Bool,
+                        _ unique: Bool, _ check: Expressible?, _ defaultValue: Expressible?,
+                        _ references: (QueryType, Expressible)?, _ collate: Collation?) -> Expressible {
     let clauses: [Expressible?] = [
         column,
         Expression<Void>(literal: datatype),
@@ -508,7 +564,7 @@ private func reference(_ primary: (QueryType, Expressible)) -> Expressible {
     ])
 }
 
-private enum Modifier : String {
+private enum Modifier: String {
 
     case unique = "UNIQUE"
 
diff --git a/Sources/SQLite/Typed/Setter.swift b/Sources/SQLite/Typed/Setter.swift
index 3d3170f6..25e88342 100644
--- a/Sources/SQLite/Typed/Setter.swift
+++ b/Sources/SQLite/Typed/Setter.swift
@@ -35,27 +35,27 @@ public struct Setter {
     let column: Expressible
     let value: Expressible
 
-    fileprivate init<V : Value>(column: Expression<V>, value: Expression<V>) {
+    fileprivate init<V: Value>(column: Expression<V>, value: Expression<V>) {
         self.column = column
         self.value = value
     }
 
-    fileprivate init<V : Value>(column: Expression<V>, value: V) {
+    fileprivate init<V: Value>(column: Expression<V>, value: V) {
         self.column = column
         self.value = value
     }
 
-    fileprivate init<V : Value>(column: Expression<V?>, value: Expression<V>) {
+    fileprivate init<V: Value>(column: Expression<V?>, value: Expression<V>) {
         self.column = column
         self.value = value
     }
 
-    fileprivate init<V : Value>(column: Expression<V?>, value: Expression<V?>) {
+    fileprivate init<V: Value>(column: Expression<V?>, value: Expression<V?>) {
         self.column = column
         self.value = value
     }
 
-    fileprivate init<V : Value>(column: Expression<V?>, value: V?) {
+    fileprivate init<V: Value>(column: Expression<V?>, value: V?) {
         self.column = column
         self.value = Expression<V?>(value: value)
     }
@@ -67,7 +67,7 @@ public struct Setter {
     }
 }
 
-extension Setter : Expressible {
+extension Setter: Expressible {
 
     public var expression: Expression<Void> {
         return "=".infix(column, value, wrap: false)
@@ -75,19 +75,19 @@ extension Setter : Expressible {
 
 }
 
-public func <-<V : Value>(column: Expression<V>, value: Expression<V>) -> Setter {
+public func <-<V: Value>(column: Expression<V>, value: Expression<V>) -> Setter {
     return Setter(column: column, value: value)
 }
-public func <-<V : Value>(column: Expression<V>, value: V) -> Setter {
+public func <-<V: Value>(column: Expression<V>, value: V) -> Setter {
     return Setter(column: column, value: value)
 }
-public func <-<V : Value>(column: Expression<V?>, value: Expression<V>) -> Setter {
+public func <-<V: Value>(column: Expression<V?>, value: Expression<V>) -> Setter {
     return Setter(column: column, value: value)
 }
-public func <-<V : Value>(column: Expression<V?>, value: Expression<V?>) -> Setter {
+public func <-<V: Value>(column: Expression<V?>, value: Expression<V?>) -> Setter {
     return Setter(column: column, value: value)
 }
-public func <-<V : Value>(column: Expression<V?>, value: V?) -> Setter {
+public func <-<V: Value>(column: Expression<V?>, value: V?) -> Setter {
     return Setter(column: column, value: value)
 }
 
@@ -107,176 +107,176 @@ public func +=(column: Expression<String?>, value: String) -> Setter {
     return column <- column + value
 }
 
-public func +=<V : Value>(column: Expression<V>, value: Expression<V>) -> Setter where V.Datatype : Number {
+public func +=<V: Value>(column: Expression<V>, value: Expression<V>) -> Setter where V.Datatype: Number {
     return column <- column + value
 }
-public func +=<V : Value>(column: Expression<V>, value: V) -> Setter where V.Datatype : Number {
+public func +=<V: Value>(column: Expression<V>, value: V) -> Setter where V.Datatype: Number {
     return column <- column + value
 }
-public func +=<V : Value>(column: Expression<V?>, value: Expression<V>) -> Setter where V.Datatype : Number {
+public func +=<V: Value>(column: Expression<V?>, value: Expression<V>) -> Setter where V.Datatype: Number {
     return column <- column + value
 }
-public func +=<V : Value>(column: Expression<V?>, value: Expression<V?>) -> Setter where V.Datatype : Number {
+public func +=<V: Value>(column: Expression<V?>, value: Expression<V?>) -> Setter where V.Datatype: Number {
     return column <- column + value
 }
-public func +=<V : Value>(column: Expression<V?>, value: V) -> Setter where V.Datatype : Number {
+public func +=<V: Value>(column: Expression<V?>, value: V) -> Setter where V.Datatype: Number {
     return column <- column + value
 }
 
-public func -=<V : Value>(column: Expression<V>, value: Expression<V>) -> Setter where V.Datatype : Number {
+public func -=<V: Value>(column: Expression<V>, value: Expression<V>) -> Setter where V.Datatype: Number {
     return column <- column - value
 }
-public func -=<V : Value>(column: Expression<V>, value: V) -> Setter where V.Datatype : Number {
+public func -=<V: Value>(column: Expression<V>, value: V) -> Setter where V.Datatype: Number {
     return column <- column - value
 }
-public func -=<V : Value>(column: Expression<V?>, value: Expression<V>) -> Setter where V.Datatype : Number {
+public func -=<V: Value>(column: Expression<V?>, value: Expression<V>) -> Setter where V.Datatype: Number {
     return column <- column - value
 }
-public func -=<V : Value>(column: Expression<V?>, value: Expression<V?>) -> Setter where V.Datatype : Number {
+public func -=<V: Value>(column: Expression<V?>, value: Expression<V?>) -> Setter where V.Datatype: Number {
     return column <- column - value
 }
-public func -=<V : Value>(column: Expression<V?>, value: V) -> Setter where V.Datatype : Number {
+public func -=<V: Value>(column: Expression<V?>, value: V) -> Setter where V.Datatype: Number {
     return column <- column - value
 }
 
-public func *=<V : Value>(column: Expression<V>, value: Expression<V>) -> Setter where V.Datatype : Number {
+public func *=<V: Value>(column: Expression<V>, value: Expression<V>) -> Setter where V.Datatype: Number {
     return column <- column * value
 }
-public func *=<V : Value>(column: Expression<V>, value: V) -> Setter where V.Datatype : Number {
+public func *=<V: Value>(column: Expression<V>, value: V) -> Setter where V.Datatype: Number {
     return column <- column * value
 }
-public func *=<V : Value>(column: Expression<V?>, value: Expression<V>) -> Setter where V.Datatype : Number {
+public func *=<V: Value>(column: Expression<V?>, value: Expression<V>) -> Setter where V.Datatype: Number {
     return column <- column * value
 }
-public func *=<V : Value>(column: Expression<V?>, value: Expression<V?>) -> Setter where V.Datatype : Number {
+public func *=<V: Value>(column: Expression<V?>, value: Expression<V?>) -> Setter where V.Datatype: Number {
     return column <- column * value
 }
-public func *=<V : Value>(column: Expression<V?>, value: V) -> Setter where V.Datatype : Number {
+public func *=<V: Value>(column: Expression<V?>, value: V) -> Setter where V.Datatype: Number {
     return column <- column * value
 }
 
-public func /=<V : Value>(column: Expression<V>, value: Expression<V>) -> Setter where V.Datatype : Number {
+public func /=<V: Value>(column: Expression<V>, value: Expression<V>) -> Setter where V.Datatype: Number {
     return column <- column / value
 }
-public func /=<V : Value>(column: Expression<V>, value: V) -> Setter where V.Datatype : Number {
+public func /=<V: Value>(column: Expression<V>, value: V) -> Setter where V.Datatype: Number {
     return column <- column / value
 }
-public func /=<V : Value>(column: Expression<V?>, value: Expression<V>) -> Setter where V.Datatype : Number {
+public func /=<V: Value>(column: Expression<V?>, value: Expression<V>) -> Setter where V.Datatype: Number {
     return column <- column / value
 }
-public func /=<V : Value>(column: Expression<V?>, value: Expression<V?>) -> Setter where V.Datatype : Number {
+public func /=<V: Value>(column: Expression<V?>, value: Expression<V?>) -> Setter where V.Datatype: Number {
     return column <- column / value
 }
-public func /=<V : Value>(column: Expression<V?>, value: V) -> Setter where V.Datatype : Number {
+public func /=<V: Value>(column: Expression<V?>, value: V) -> Setter where V.Datatype: Number {
     return column <- column / value
 }
 
-public func %=<V : Value>(column: Expression<V>, value: Expression<V>) -> Setter where V.Datatype == Int64 {
+public func %=<V: Value>(column: Expression<V>, value: Expression<V>) -> Setter where V.Datatype == Int64 {
     return column <- column % value
 }
-public func %=<V : Value>(column: Expression<V>, value: V) -> Setter where V.Datatype == Int64 {
+public func %=<V: Value>(column: Expression<V>, value: V) -> Setter where V.Datatype == Int64 {
     return column <- column % value
 }
-public func %=<V : Value>(column: Expression<V?>, value: Expression<V>) -> Setter where V.Datatype == Int64 {
+public func %=<V: Value>(column: Expression<V?>, value: Expression<V>) -> Setter where V.Datatype == Int64 {
     return column <- column % value
 }
-public func %=<V : Value>(column: Expression<V?>, value: Expression<V?>) -> Setter where V.Datatype == Int64 {
+public func %=<V: Value>(column: Expression<V?>, value: Expression<V?>) -> Setter where V.Datatype == Int64 {
     return column <- column % value
 }
-public func %=<V : Value>(column: Expression<V?>, value: V) -> Setter where V.Datatype == Int64 {
+public func %=<V: Value>(column: Expression<V?>, value: V) -> Setter where V.Datatype == Int64 {
     return column <- column % value
 }
 
-public func <<=<V : Value>(column: Expression<V>, value: Expression<V>) -> Setter where V.Datatype == Int64 {
+public func <<=<V: Value>(column: Expression<V>, value: Expression<V>) -> Setter where V.Datatype == Int64 {
     return column <- column << value
 }
-public func <<=<V : Value>(column: Expression<V>, value: V) -> Setter where V.Datatype == Int64 {
+public func <<=<V: Value>(column: Expression<V>, value: V) -> Setter where V.Datatype == Int64 {
     return column <- column << value
 }
-public func <<=<V : Value>(column: Expression<V?>, value: Expression<V>) -> Setter where V.Datatype == Int64 {
+public func <<=<V: Value>(column: Expression<V?>, value: Expression<V>) -> Setter where V.Datatype == Int64 {
     return column <- column << value
 }
-public func <<=<V : Value>(column: Expression<V?>, value: Expression<V?>) -> Setter where V.Datatype == Int64 {
+public func <<=<V: Value>(column: Expression<V?>, value: Expression<V?>) -> Setter where V.Datatype == Int64 {
     return column <- column << value
 }
-public func <<=<V : Value>(column: Expression<V?>, value: V) -> Setter where V.Datatype == Int64 {
+public func <<=<V: Value>(column: Expression<V?>, value: V) -> Setter where V.Datatype == Int64 {
     return column <- column << value
 }
 
-public func >>=<V : Value>(column: Expression<V>, value: Expression<V>) -> Setter where V.Datatype == Int64 {
+public func >>=<V: Value>(column: Expression<V>, value: Expression<V>) -> Setter where V.Datatype == Int64 {
     return column <- column >> value
 }
-public func >>=<V : Value>(column: Expression<V>, value: V) -> Setter where V.Datatype == Int64 {
+public func >>=<V: Value>(column: Expression<V>, value: V) -> Setter where V.Datatype == Int64 {
     return column <- column >> value
 }
-public func >>=<V : Value>(column: Expression<V?>, value: Expression<V>) -> Setter where V.Datatype == Int64 {
+public func >>=<V: Value>(column: Expression<V?>, value: Expression<V>) -> Setter where V.Datatype == Int64 {
     return column <- column >> value
 }
-public func >>=<V : Value>(column: Expression<V?>, value: Expression<V?>) -> Setter where V.Datatype == Int64 {
+public func >>=<V: Value>(column: Expression<V?>, value: Expression<V?>) -> Setter where V.Datatype == Int64 {
     return column <- column >> value
 }
-public func >>=<V : Value>(column: Expression<V?>, value: V) -> Setter where V.Datatype == Int64 {
+public func >>=<V: Value>(column: Expression<V?>, value: V) -> Setter where V.Datatype == Int64 {
     return column <- column >> value
 }
 
-public func &=<V : Value>(column: Expression<V>, value: Expression<V>) -> Setter where V.Datatype == Int64 {
+public func &=<V: Value>(column: Expression<V>, value: Expression<V>) -> Setter where V.Datatype == Int64 {
     return column <- column & value
 }
-public func &=<V : Value>(column: Expression<V>, value: V) -> Setter where V.Datatype == Int64 {
+public func &=<V: Value>(column: Expression<V>, value: V) -> Setter where V.Datatype == Int64 {
     return column <- column & value
 }
-public func &=<V : Value>(column: Expression<V?>, value: Expression<V>) -> Setter where V.Datatype == Int64 {
+public func &=<V: Value>(column: Expression<V?>, value: Expression<V>) -> Setter where V.Datatype == Int64 {
     return column <- column & value
 }
-public func &=<V : Value>(column: Expression<V?>, value: Expression<V?>) -> Setter where V.Datatype == Int64 {
+public func &=<V: Value>(column: Expression<V?>, value: Expression<V?>) -> Setter where V.Datatype == Int64 {
     return column <- column & value
 }
-public func &=<V : Value>(column: Expression<V?>, value: V) -> Setter where V.Datatype == Int64 {
+public func &=<V: Value>(column: Expression<V?>, value: V) -> Setter where V.Datatype == Int64 {
     return column <- column & value
 }
 
-public func |=<V : Value>(column: Expression<V>, value: Expression<V>) -> Setter where V.Datatype == Int64 {
+public func |=<V: Value>(column: Expression<V>, value: Expression<V>) -> Setter where V.Datatype == Int64 {
     return column <- column | value
 }
-public func |=<V : Value>(column: Expression<V>, value: V) -> Setter where V.Datatype == Int64 {
+public func |=<V: Value>(column: Expression<V>, value: V) -> Setter where V.Datatype == Int64 {
     return column <- column | value
 }
-public func |=<V : Value>(column: Expression<V?>, value: Expression<V>) -> Setter where V.Datatype == Int64 {
+public func |=<V: Value>(column: Expression<V?>, value: Expression<V>) -> Setter where V.Datatype == Int64 {
     return column <- column | value
 }
-public func |=<V : Value>(column: Expression<V?>, value: Expression<V?>) -> Setter where V.Datatype == Int64 {
+public func |=<V: Value>(column: Expression<V?>, value: Expression<V?>) -> Setter where V.Datatype == Int64 {
     return column <- column | value
 }
-public func |=<V : Value>(column: Expression<V?>, value: V) -> Setter where V.Datatype == Int64 {
+public func |=<V: Value>(column: Expression<V?>, value: V) -> Setter where V.Datatype == Int64 {
     return column <- column | value
 }
 
-public func ^=<V : Value>(column: Expression<V>, value: Expression<V>) -> Setter where V.Datatype == Int64 {
+public func ^=<V: Value>(column: Expression<V>, value: Expression<V>) -> Setter where V.Datatype == Int64 {
     return column <- column ^ value
 }
-public func ^=<V : Value>(column: Expression<V>, value: V) -> Setter where V.Datatype == Int64 {
+public func ^=<V: Value>(column: Expression<V>, value: V) -> Setter where V.Datatype == Int64 {
     return column <- column ^ value
 }
-public func ^=<V : Value>(column: Expression<V?>, value: Expression<V>) -> Setter where V.Datatype == Int64 {
+public func ^=<V: Value>(column: Expression<V?>, value: Expression<V>) -> Setter where V.Datatype == Int64 {
     return column <- column ^ value
 }
-public func ^=<V : Value>(column: Expression<V?>, value: Expression<V?>) -> Setter where V.Datatype == Int64 {
+public func ^=<V: Value>(column: Expression<V?>, value: Expression<V?>) -> Setter where V.Datatype == Int64 {
     return column <- column ^ value
 }
-public func ^=<V : Value>(column: Expression<V?>, value: V) -> Setter where V.Datatype == Int64 {
+public func ^=<V: Value>(column: Expression<V?>, value: V) -> Setter where V.Datatype == Int64 {
     return column <- column ^ value
 }
 
-public postfix func ++<V : Value>(column: Expression<V>) -> Setter where V.Datatype == Int64 {
+public postfix func ++<V: Value>(column: Expression<V>) -> Setter where V.Datatype == Int64 {
     return Expression<Int>(column) += 1
 }
-public postfix func ++<V : Value>(column: Expression<V?>) -> Setter where V.Datatype == Int64 {
+public postfix func ++<V: Value>(column: Expression<V?>) -> Setter where V.Datatype == Int64 {
     return Expression<Int>(column) += 1
 }
 
-public postfix func --<V : Value>(column: Expression<V>) -> Setter where V.Datatype == Int64 {
+public postfix func --<V: Value>(column: Expression<V>) -> Setter where V.Datatype == Int64 {
     return Expression<Int>(column) -= 1
 }
-public postfix func --<V : Value>(column: Expression<V?>) -> Setter where V.Datatype == Int64 {
+public postfix func --<V: Value>(column: Expression<V?>) -> Setter where V.Datatype == Int64 {
     return Expression<Int>(column) -= 1
 }
diff --git a/Tests/.swiftlint.yml b/Tests/.swiftlint.yml
new file mode 100644
index 00000000..3537085c
--- /dev/null
+++ b/Tests/.swiftlint.yml
@@ -0,0 +1,20 @@
+parent_config: ../.swiftlint.yml
+
+disabled_rules:
+  - force_cast
+  - force_try
+  - identifier_name
+
+type_body_length:
+  warning: 800
+  error: 800
+
+function_body_length:
+  warning: 200
+  error: 200
+
+file_length:
+  warning: 1000
+  error: 1000
+
+
diff --git a/Tests/SQLiteTests/AggregateFunctionsTests.swift b/Tests/SQLiteTests/AggregateFunctionsTests.swift
index 6b583ccf..71ac79fb 100644
--- a/Tests/SQLiteTests/AggregateFunctionsTests.swift
+++ b/Tests/SQLiteTests/AggregateFunctionsTests.swift
@@ -1,68 +1,68 @@
 import XCTest
 import SQLite
 
-class AggregateFunctionsTests : XCTestCase {
+class AggregateFunctionsTests: XCTestCase {
 
     func test_distinct_prependsExpressionsWithDistinctKeyword() {
-        AssertSQL("DISTINCT \"int\"", int.distinct)
-        AssertSQL("DISTINCT \"intOptional\"", intOptional.distinct)
-        AssertSQL("DISTINCT \"double\"", double.distinct)
-        AssertSQL("DISTINCT \"doubleOptional\"", doubleOptional.distinct)
-        AssertSQL("DISTINCT \"string\"", string.distinct)
-        AssertSQL("DISTINCT \"stringOptional\"", stringOptional.distinct)
+        assertSQL("DISTINCT \"int\"", int.distinct)
+        assertSQL("DISTINCT \"intOptional\"", intOptional.distinct)
+        assertSQL("DISTINCT \"double\"", double.distinct)
+        assertSQL("DISTINCT \"doubleOptional\"", doubleOptional.distinct)
+        assertSQL("DISTINCT \"string\"", string.distinct)
+        assertSQL("DISTINCT \"stringOptional\"", stringOptional.distinct)
     }
 
     func test_count_wrapsOptionalExpressionsWithCountFunction() {
-        AssertSQL("count(\"intOptional\")", intOptional.count)
-        AssertSQL("count(\"doubleOptional\")", doubleOptional.count)
-        AssertSQL("count(\"stringOptional\")", stringOptional.count)
+        assertSQL("count(\"intOptional\")", intOptional.count)
+        assertSQL("count(\"doubleOptional\")", doubleOptional.count)
+        assertSQL("count(\"stringOptional\")", stringOptional.count)
     }
 
     func test_max_wrapsComparableExpressionsWithMaxFunction() {
-        AssertSQL("max(\"int\")", int.max)
-        AssertSQL("max(\"intOptional\")", intOptional.max)
-        AssertSQL("max(\"double\")", double.max)
-        AssertSQL("max(\"doubleOptional\")", doubleOptional.max)
-        AssertSQL("max(\"string\")", string.max)
-        AssertSQL("max(\"stringOptional\")", stringOptional.max)
-        AssertSQL("max(\"date\")", date.max)
-        AssertSQL("max(\"dateOptional\")", dateOptional.max)
+        assertSQL("max(\"int\")", int.max)
+        assertSQL("max(\"intOptional\")", intOptional.max)
+        assertSQL("max(\"double\")", double.max)
+        assertSQL("max(\"doubleOptional\")", doubleOptional.max)
+        assertSQL("max(\"string\")", string.max)
+        assertSQL("max(\"stringOptional\")", stringOptional.max)
+        assertSQL("max(\"date\")", date.max)
+        assertSQL("max(\"dateOptional\")", dateOptional.max)
     }
 
     func test_min_wrapsComparableExpressionsWithMinFunction() {
-        AssertSQL("min(\"int\")", int.min)
-        AssertSQL("min(\"intOptional\")", intOptional.min)
-        AssertSQL("min(\"double\")", double.min)
-        AssertSQL("min(\"doubleOptional\")", doubleOptional.min)
-        AssertSQL("min(\"string\")", string.min)
-        AssertSQL("min(\"stringOptional\")", stringOptional.min)
-        AssertSQL("min(\"date\")", date.min)
-        AssertSQL("min(\"dateOptional\")", dateOptional.min)
+        assertSQL("min(\"int\")", int.min)
+        assertSQL("min(\"intOptional\")", intOptional.min)
+        assertSQL("min(\"double\")", double.min)
+        assertSQL("min(\"doubleOptional\")", doubleOptional.min)
+        assertSQL("min(\"string\")", string.min)
+        assertSQL("min(\"stringOptional\")", stringOptional.min)
+        assertSQL("min(\"date\")", date.min)
+        assertSQL("min(\"dateOptional\")", dateOptional.min)
     }
 
     func test_average_wrapsNumericExpressionsWithAvgFunction() {
-        AssertSQL("avg(\"int\")", int.average)
-        AssertSQL("avg(\"intOptional\")", intOptional.average)
-        AssertSQL("avg(\"double\")", double.average)
-        AssertSQL("avg(\"doubleOptional\")", doubleOptional.average)
+        assertSQL("avg(\"int\")", int.average)
+        assertSQL("avg(\"intOptional\")", intOptional.average)
+        assertSQL("avg(\"double\")", double.average)
+        assertSQL("avg(\"doubleOptional\")", doubleOptional.average)
     }
 
     func test_sum_wrapsNumericExpressionsWithSumFunction() {
-        AssertSQL("sum(\"int\")", int.sum)
-        AssertSQL("sum(\"intOptional\")", intOptional.sum)
-        AssertSQL("sum(\"double\")", double.sum)
-        AssertSQL("sum(\"doubleOptional\")", doubleOptional.sum)
+        assertSQL("sum(\"int\")", int.sum)
+        assertSQL("sum(\"intOptional\")", intOptional.sum)
+        assertSQL("sum(\"double\")", double.sum)
+        assertSQL("sum(\"doubleOptional\")", doubleOptional.sum)
     }
 
     func test_total_wrapsNumericExpressionsWithTotalFunction() {
-        AssertSQL("total(\"int\")", int.total)
-        AssertSQL("total(\"intOptional\")", intOptional.total)
-        AssertSQL("total(\"double\")", double.total)
-        AssertSQL("total(\"doubleOptional\")", doubleOptional.total)
+        assertSQL("total(\"int\")", int.total)
+        assertSQL("total(\"intOptional\")", intOptional.total)
+        assertSQL("total(\"double\")", double.total)
+        assertSQL("total(\"doubleOptional\")", doubleOptional.total)
     }
 
     func test_count_withStar_wrapsStarWithCountFunction() {
-        AssertSQL("count(*)", count(*))
+        assertSQL("count(*)", count(*))
     }
 
 }
diff --git a/Tests/SQLiteTests/BlobTests.swift b/Tests/SQLiteTests/BlobTests.swift
index 817205d6..87eb5709 100644
--- a/Tests/SQLiteTests/BlobTests.swift
+++ b/Tests/SQLiteTests/BlobTests.swift
@@ -1,7 +1,7 @@
 import XCTest
 import SQLite
 
-class BlobTests : XCTestCase {
+class BlobTests: XCTestCase {
 
     func test_toHex() {
         let blob = Blob(bytes: [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 150, 250, 255])
diff --git a/Tests/SQLiteTests/CipherTests.swift b/Tests/SQLiteTests/CipherTests.swift
index a27ac17d..77e8e7f0 100644
--- a/Tests/SQLiteTests/CipherTests.swift
+++ b/Tests/SQLiteTests/CipherTests.swift
@@ -77,7 +77,7 @@ class CipherTests: XCTestCase {
         // sqlite> pragma key = 'sqlcipher-test';
         // sqlite> CREATE TABLE foo (bar TEXT);
         // sqlite> INSERT INTO foo (bar) VALUES ('world');
-        guard let cipherVersion:String = db1.cipherVersion,
+        guard let cipherVersion: String = db1.cipherVersion,
             cipherVersion.starts(with: "3.") || cipherVersion.starts(with: "4.")
             else { return }
 
@@ -85,12 +85,12 @@ class CipherTests: XCTestCase {
             fixture("encrypted-3.x", withExtension: "sqlite") :
             fixture("encrypted-4.x", withExtension: "sqlite")
 
-        try! FileManager.default.setAttributes([FileAttributeKey.immutable : 1], ofItemAtPath: encryptedFile)
+        try! FileManager.default.setAttributes([FileAttributeKey.immutable: 1], ofItemAtPath: encryptedFile)
         XCTAssertFalse(FileManager.default.isWritableFile(atPath: encryptedFile))
 
         defer {
             // ensure file can be cleaned up afterwards
-            try! FileManager.default.setAttributes([FileAttributeKey.immutable : 0], ofItemAtPath: encryptedFile)
+            try! FileManager.default.setAttributes([FileAttributeKey.immutable: 0], ofItemAtPath: encryptedFile)
         }
 
         let conn = try! Connection(encryptedFile)
diff --git a/Tests/SQLiteTests/ConnectionTests.swift b/Tests/SQLiteTests/ConnectionTests.swift
index a05cceb5..00fe4f6b 100644
--- a/Tests/SQLiteTests/ConnectionTests.swift
+++ b/Tests/SQLiteTests/ConnectionTests.swift
@@ -13,12 +13,12 @@ import CSQLite
 import SQLite3
 #endif
 
-class ConnectionTests : SQLiteTestCase {
+class ConnectionTests: SQLiteTestCase {
 
     override func setUp() {
         super.setUp()
 
-        CreateUsersTable()
+        createUsersTable()
     }
 
     func test_init_withInMemory_returnsInMemoryConnection() {
@@ -62,13 +62,13 @@ class ConnectionTests : SQLiteTestCase {
     }
 
     func test_lastInsertRowid_returnsLastIdAfterInserts() {
-        try! InsertUser("alice")
+        try! insertUser("alice")
         XCTAssertEqual(1, db.lastInsertRowid)
     }
 
     func test_lastInsertRowid_doesNotResetAfterError() {
         XCTAssert(db.lastInsertRowid == 0)
-        try! InsertUser("alice")
+        try! insertUser("alice")
         XCTAssertEqual(1, db.lastInsertRowid)
         XCTAssertThrowsError(
             try db.run("INSERT INTO \"users\" (email, age, admin) values ('invalid@example.com', 12, 'invalid')")
@@ -83,17 +83,17 @@ class ConnectionTests : SQLiteTestCase {
     }
 
     func test_changes_returnsNumberOfChanges() {
-        try! InsertUser("alice")
+        try! insertUser("alice")
         XCTAssertEqual(1, db.changes)
-        try! InsertUser("betsy")
+        try! insertUser("betsy")
         XCTAssertEqual(1, db.changes)
     }
 
     func test_totalChanges_returnsTotalNumberOfChanges() {
         XCTAssertEqual(0, db.totalChanges)
-        try! InsertUser("alice")
+        try! insertUser("alice")
         XCTAssertEqual(1, db.totalChanges)
-        try! InsertUser("betsy")
+        try! insertUser("betsy")
         XCTAssertEqual(2, db.totalChanges)
     }
 
@@ -109,9 +109,9 @@ class ConnectionTests : SQLiteTestCase {
         try! db.run("SELECT * FROM users WHERE admin = ?", 0)
         try! db.run("SELECT * FROM users WHERE admin = ?", [0])
         try! db.run("SELECT * FROM users WHERE admin = $admin", ["$admin": 0])
-        AssertSQL("SELECT * FROM users WHERE admin = 0", 4)
+        assertSQL("SELECT * FROM users WHERE admin = 0", 4)
     }
-    
+
     func test_vacuum() {
         try! db.vacuum()
     }
@@ -121,31 +121,31 @@ class ConnectionTests : SQLiteTestCase {
         XCTAssertEqual(0, try! db.scalar("SELECT count(*) FROM users WHERE admin = ?", 0) as? Int64)
         XCTAssertEqual(0, try! db.scalar("SELECT count(*) FROM users WHERE admin = ?", [0]) as? Int64)
         XCTAssertEqual(0, try! db.scalar("SELECT count(*) FROM users WHERE admin = $admin", ["$admin": 0]) as? Int64)
-        AssertSQL("SELECT count(*) FROM users WHERE admin = 0", 4)
+        assertSQL("SELECT count(*) FROM users WHERE admin = 0", 4)
     }
 
     func test_execute_comment() {
         try! db.run("-- this is a comment\nSELECT 1")
-        AssertSQL("-- this is a comment", 0)
-        AssertSQL("SELECT 1", 0)
+        assertSQL("-- this is a comment", 0)
+        assertSQL("SELECT 1", 0)
     }
 
     func test_transaction_executesBeginDeferred() {
         try! db.transaction(.deferred) {}
 
-        AssertSQL("BEGIN DEFERRED TRANSACTION")
+        assertSQL("BEGIN DEFERRED TRANSACTION")
     }
 
     func test_transaction_executesBeginImmediate() {
         try! db.transaction(.immediate) {}
 
-        AssertSQL("BEGIN IMMEDIATE TRANSACTION")
+        assertSQL("BEGIN IMMEDIATE TRANSACTION")
     }
 
     func test_transaction_executesBeginExclusive() {
         try! db.transaction(.exclusive) {}
 
-        AssertSQL("BEGIN EXCLUSIVE TRANSACTION")
+        assertSQL("BEGIN EXCLUSIVE TRANSACTION")
     }
 
     func test_transaction_beginsAndCommitsTransactions() {
@@ -155,10 +155,10 @@ class ConnectionTests : SQLiteTestCase {
             try stmt.run()
         }
 
-        AssertSQL("BEGIN DEFERRED TRANSACTION")
-        AssertSQL("INSERT INTO users (email) VALUES ('alice@example.com')")
-        AssertSQL("COMMIT TRANSACTION")
-        AssertSQL("ROLLBACK TRANSACTION", 0)
+        assertSQL("BEGIN DEFERRED TRANSACTION")
+        assertSQL("INSERT INTO users (email) VALUES ('alice@example.com')")
+        assertSQL("COMMIT TRANSACTION")
+        assertSQL("ROLLBACK TRANSACTION", 0)
     }
 
     func test_transaction_rollsBackTransactionsIfCommitsFail() {
@@ -186,10 +186,10 @@ class ConnectionTests : SQLiteTestCase {
             XCTFail("unexpected error: \(error)")
         }
 
-        AssertSQL("BEGIN DEFERRED TRANSACTION")
-        AssertSQL("INSERT INTO users (email, manager_id) VALUES ('alice@example.com', 100)")
-        AssertSQL("COMMIT TRANSACTION")
-        AssertSQL("ROLLBACK TRANSACTION")
+        assertSQL("BEGIN DEFERRED TRANSACTION")
+        assertSQL("INSERT INTO users (email, manager_id) VALUES ('alice@example.com', 100)")
+        assertSQL("COMMIT TRANSACTION")
+        assertSQL("ROLLBACK TRANSACTION")
 
         // Run another transaction to ensure that a subsequent transaction does not fail with an "cannot start a
         // transaction within a transaction" error.
@@ -210,14 +210,14 @@ class ConnectionTests : SQLiteTestCase {
         } catch {
         }
 
-        AssertSQL("BEGIN DEFERRED TRANSACTION")
-        AssertSQL("INSERT INTO users (email) VALUES ('alice@example.com')", 2)
-        AssertSQL("ROLLBACK TRANSACTION")
-        AssertSQL("COMMIT TRANSACTION", 0)
+        assertSQL("BEGIN DEFERRED TRANSACTION")
+        assertSQL("INSERT INTO users (email) VALUES ('alice@example.com')", 2)
+        assertSQL("ROLLBACK TRANSACTION")
+        assertSQL("COMMIT TRANSACTION", 0)
     }
 
     func test_savepoint_beginsAndCommitsSavepoints() {
-        let db:Connection = self.db
+        let db: Connection = self.db
 
         try! db.savepoint("1") {
             try db.savepoint("2") {
@@ -225,17 +225,17 @@ class ConnectionTests : SQLiteTestCase {
             }
         }
 
-        AssertSQL("SAVEPOINT '1'")
-        AssertSQL("SAVEPOINT '2'")
-        AssertSQL("INSERT INTO users (email) VALUES ('alice@example.com')")
-        AssertSQL("RELEASE SAVEPOINT '2'")
-        AssertSQL("RELEASE SAVEPOINT '1'")
-        AssertSQL("ROLLBACK TO SAVEPOINT '2'", 0)
-        AssertSQL("ROLLBACK TO SAVEPOINT '1'", 0)
+        assertSQL("SAVEPOINT '1'")
+        assertSQL("SAVEPOINT '2'")
+        assertSQL("INSERT INTO users (email) VALUES ('alice@example.com')")
+        assertSQL("RELEASE SAVEPOINT '2'")
+        assertSQL("RELEASE SAVEPOINT '1'")
+        assertSQL("ROLLBACK TO SAVEPOINT '2'", 0)
+        assertSQL("ROLLBACK TO SAVEPOINT '1'", 0)
     }
 
     func test_savepoint_beginsAndRollsSavepointsBack() {
-        let db:Connection = self.db
+        let db: Connection = self.db
         let stmt = try! db.prepare("INSERT INTO users (email) VALUES (?)", "alice@example.com")
 
         do {
@@ -254,13 +254,13 @@ class ConnectionTests : SQLiteTestCase {
         } catch {
         }
 
-        AssertSQL("SAVEPOINT '1'")
-        AssertSQL("SAVEPOINT '2'")
-        AssertSQL("INSERT INTO users (email) VALUES ('alice@example.com')", 2)
-        AssertSQL("ROLLBACK TO SAVEPOINT '2'")
-        AssertSQL("ROLLBACK TO SAVEPOINT '1'")
-        AssertSQL("RELEASE SAVEPOINT '2'", 0)
-        AssertSQL("RELEASE SAVEPOINT '1'", 0)
+        assertSQL("SAVEPOINT '1'")
+        assertSQL("SAVEPOINT '2'")
+        assertSQL("INSERT INTO users (email) VALUES ('alice@example.com')", 2)
+        assertSQL("ROLLBACK TO SAVEPOINT '2'")
+        assertSQL("ROLLBACK TO SAVEPOINT '1'")
+        assertSQL("RELEASE SAVEPOINT '2'", 0)
+        assertSQL("RELEASE SAVEPOINT '1'", 0)
     }
 
     func test_updateHook_setsUpdateHook_withInsert() {
@@ -272,12 +272,12 @@ class ConnectionTests : SQLiteTestCase {
                 XCTAssertEqual(1, rowid)
                 done()
             }
-            try! InsertUser("alice")
+            try! insertUser("alice")
         }
     }
 
     func test_updateHook_setsUpdateHook_withUpdate() {
-        try! InsertUser("alice")
+        try! insertUser("alice")
         async { done in
             db.updateHook { operation, db, table, rowid in
                 XCTAssertEqual(Connection.Operation.update, operation)
@@ -291,7 +291,7 @@ class ConnectionTests : SQLiteTestCase {
     }
 
     func test_updateHook_setsUpdateHook_withDelete() {
-        try! InsertUser("alice")
+        try! insertUser("alice")
         async { done in
             db.updateHook { operation, db, table, rowid in
                 XCTAssertEqual(Connection.Operation.delete, operation)
@@ -310,7 +310,7 @@ class ConnectionTests : SQLiteTestCase {
                 done()
             }
             try! db.transaction {
-                try self.InsertUser("alice")
+                try self.insertUser("alice")
             }
             XCTAssertEqual(1, try! db.scalar("SELECT count(*) FROM users") as? Int64)
         }
@@ -321,8 +321,8 @@ class ConnectionTests : SQLiteTestCase {
             db.rollbackHook(done)
             do {
                 try db.transaction {
-                    try self.InsertUser("alice")
-                    try self.InsertUser("alice") // throw
+                    try self.insertUser("alice")
+                    try self.insertUser("alice") // throw
                 }
             } catch {
             }
@@ -338,7 +338,7 @@ class ConnectionTests : SQLiteTestCase {
             db.rollbackHook(done)
             do {
                 try db.transaction {
-                    try self.InsertUser("alice")
+                    try self.insertUser("alice")
                 }
             } catch {
             }
@@ -376,7 +376,7 @@ class ConnectionTests : SQLiteTestCase {
 
     func test_interrupt_interruptsLongRunningQuery() {
         let semaphore = DispatchSemaphore(value: 0)
-        db.createFunction("sleep") { args in
+        db.createFunction("sleep") { _ in
             DispatchQueue.global(qos: .background).async {
                 self.db.interrupt()
                 semaphore.signal()
@@ -396,7 +396,7 @@ class ConnectionTests : SQLiteTestCase {
 
     func test_concurrent_access_single_connection() {
         // test can fail on iOS/tvOS 9.x: SQLite compile-time differences?
-        guard #available(iOS 10.0, OSX 10.10, tvOS 10.0, watchOS 2.2, *) else  { return }
+        guard #available(iOS 10.0, OSX 10.10, tvOS 10.0, watchOS 2.2, *) else { return }
 
         let conn = try! Connection("\(NSTemporaryDirectory())/\(UUID().uuidString)")
         try! conn.execute("DROP TABLE IF EXISTS test; CREATE TABLE test(value);")
@@ -416,8 +416,7 @@ class ConnectionTests : SQLiteTestCase {
     }
 }
 
-
-class ResultTests : XCTestCase {
+class ResultTests: XCTestCase {
     let connection = try! Connection(.inMemory)
 
     func test_init_with_ok_code_returns_nil() {
@@ -434,13 +433,13 @@ class ResultTests : XCTestCase {
 
     func test_init_with_other_code_returns_error() {
         if case .some(.error(let message, let code, let statement)) =
-            Result(errorCode: SQLITE_MISUSE, connection: connection, statement: nil)  {
+            Result(errorCode: SQLITE_MISUSE, connection: connection, statement: nil) {
             XCTAssertEqual("not an error", message)
             XCTAssertEqual(SQLITE_MISUSE, code)
             XCTAssertNil(statement)
             XCTAssert(self.connection === connection)
         } else {
-            XCTFail()
+            XCTFail("no error")
         }
     }
 
diff --git a/Tests/SQLiteTests/CoreFunctionsTests.swift b/Tests/SQLiteTests/CoreFunctionsTests.swift
index e7402de3..e03e3769 100644
--- a/Tests/SQLiteTests/CoreFunctionsTests.swift
+++ b/Tests/SQLiteTests/CoreFunctionsTests.swift
@@ -1,145 +1,145 @@
 import XCTest
 @testable import SQLite
 
-class CoreFunctionsTests : XCTestCase {
+class CoreFunctionsTests: XCTestCase {
 
     func test_round_wrapsDoubleExpressionsWithRoundFunction() {
-        AssertSQL("round(\"double\")", double.round())
-        AssertSQL("round(\"doubleOptional\")", doubleOptional.round())
+        assertSQL("round(\"double\")", double.round())
+        assertSQL("round(\"doubleOptional\")", doubleOptional.round())
 
-        AssertSQL("round(\"double\", 1)", double.round(1))
-        AssertSQL("round(\"doubleOptional\", 2)", doubleOptional.round(2))
+        assertSQL("round(\"double\", 1)", double.round(1))
+        assertSQL("round(\"doubleOptional\", 2)", doubleOptional.round(2))
     }
 
     func test_random_generatesExpressionWithRandomFunction() {
-        AssertSQL("random()", Expression<Int64>.random())
-        AssertSQL("random()", Expression<Int>.random())
+        assertSQL("random()", Expression<Int64>.random())
+        assertSQL("random()", Expression<Int>.random())
     }
 
     func test_length_wrapsStringExpressionWithLengthFunction() {
-        AssertSQL("length(\"string\")", string.length)
-        AssertSQL("length(\"stringOptional\")", stringOptional.length)
+        assertSQL("length(\"string\")", string.length)
+        assertSQL("length(\"stringOptional\")", stringOptional.length)
     }
 
     func test_lowercaseString_wrapsStringExpressionWithLowerFunction() {
-        AssertSQL("lower(\"string\")", string.lowercaseString)
-        AssertSQL("lower(\"stringOptional\")", stringOptional.lowercaseString)
+        assertSQL("lower(\"string\")", string.lowercaseString)
+        assertSQL("lower(\"stringOptional\")", stringOptional.lowercaseString)
     }
 
     func test_uppercaseString_wrapsStringExpressionWithUpperFunction() {
-        AssertSQL("upper(\"string\")", string.uppercaseString)
-        AssertSQL("upper(\"stringOptional\")", stringOptional.uppercaseString)
+        assertSQL("upper(\"string\")", string.uppercaseString)
+        assertSQL("upper(\"stringOptional\")", stringOptional.uppercaseString)
     }
 
     func test_like_buildsExpressionWithLikeOperator() {
-        AssertSQL("(\"string\" LIKE 'a%')", string.like("a%"))
-        AssertSQL("(\"stringOptional\" LIKE 'b%')", stringOptional.like("b%"))
-
-        AssertSQL("(\"string\" LIKE '%\\%' ESCAPE '\\')", string.like("%\\%", escape: "\\"))
-        AssertSQL("(\"stringOptional\" LIKE '_\\_' ESCAPE '\\')", stringOptional.like("_\\_", escape: "\\"))
-        
-        AssertSQL("(\"string\" LIKE \"a\")", string.like(Expression<String>("a")))
-        AssertSQL("(\"stringOptional\" LIKE \"a\")", stringOptional.like(Expression<String>("a")))
-        
-        AssertSQL("(\"string\" LIKE \"a\" ESCAPE '\\')", string.like(Expression<String>("a"), escape: "\\"))
-        AssertSQL("(\"stringOptional\" LIKE \"a\" ESCAPE '\\')", stringOptional.like(Expression<String>("a"), escape: "\\"))
-        
-        AssertSQL("('string' LIKE \"a\")", "string".like(Expression<String>("a")))
-        AssertSQL("('string' LIKE \"a\" ESCAPE '\\')", "string".like(Expression<String>("a"), escape: "\\"))
+        assertSQL("(\"string\" LIKE 'a%')", string.like("a%"))
+        assertSQL("(\"stringOptional\" LIKE 'b%')", stringOptional.like("b%"))
+
+        assertSQL("(\"string\" LIKE '%\\%' ESCAPE '\\')", string.like("%\\%", escape: "\\"))
+        assertSQL("(\"stringOptional\" LIKE '_\\_' ESCAPE '\\')", stringOptional.like("_\\_", escape: "\\"))
+
+        assertSQL("(\"string\" LIKE \"a\")", string.like(Expression<String>("a")))
+        assertSQL("(\"stringOptional\" LIKE \"a\")", stringOptional.like(Expression<String>("a")))
+
+        assertSQL("(\"string\" LIKE \"a\" ESCAPE '\\')", string.like(Expression<String>("a"), escape: "\\"))
+        assertSQL("(\"stringOptional\" LIKE \"a\" ESCAPE '\\')", stringOptional.like(Expression<String>("a"), escape: "\\"))
+
+        assertSQL("('string' LIKE \"a\")", "string".like(Expression<String>("a")))
+        assertSQL("('string' LIKE \"a\" ESCAPE '\\')", "string".like(Expression<String>("a"), escape: "\\"))
     }
 
     func test_glob_buildsExpressionWithGlobOperator() {
-        AssertSQL("(\"string\" GLOB 'a*')", string.glob("a*"))
-        AssertSQL("(\"stringOptional\" GLOB 'b*')", stringOptional.glob("b*"))
+        assertSQL("(\"string\" GLOB 'a*')", string.glob("a*"))
+        assertSQL("(\"stringOptional\" GLOB 'b*')", stringOptional.glob("b*"))
     }
 
     func test_match_buildsExpressionWithMatchOperator() {
-        AssertSQL("(\"string\" MATCH 'a*')", string.match("a*"))
-        AssertSQL("(\"stringOptional\" MATCH 'b*')", stringOptional.match("b*"))
+        assertSQL("(\"string\" MATCH 'a*')", string.match("a*"))
+        assertSQL("(\"stringOptional\" MATCH 'b*')", stringOptional.match("b*"))
     }
 
     func test_regexp_buildsExpressionWithRegexpOperator() {
-        AssertSQL("(\"string\" REGEXP '^.+@.+\\.com$')", string.regexp("^.+@.+\\.com$"))
-        AssertSQL("(\"stringOptional\" REGEXP '^.+@.+\\.net$')", stringOptional.regexp("^.+@.+\\.net$"))
+        assertSQL("(\"string\" REGEXP '^.+@.+\\.com$')", string.regexp("^.+@.+\\.com$"))
+        assertSQL("(\"stringOptional\" REGEXP '^.+@.+\\.net$')", stringOptional.regexp("^.+@.+\\.net$"))
     }
 
     func test_collate_buildsExpressionWithCollateOperator() {
-        AssertSQL("(\"string\" COLLATE BINARY)", string.collate(.binary))
-        AssertSQL("(\"string\" COLLATE NOCASE)", string.collate(.nocase))
-        AssertSQL("(\"string\" COLLATE RTRIM)", string.collate(.rtrim))
-        AssertSQL("(\"string\" COLLATE \"CUSTOM\")", string.collate(.custom("CUSTOM")))
+        assertSQL("(\"string\" COLLATE BINARY)", string.collate(.binary))
+        assertSQL("(\"string\" COLLATE NOCASE)", string.collate(.nocase))
+        assertSQL("(\"string\" COLLATE RTRIM)", string.collate(.rtrim))
+        assertSQL("(\"string\" COLLATE \"CUSTOM\")", string.collate(.custom("CUSTOM")))
 
-        AssertSQL("(\"stringOptional\" COLLATE BINARY)", stringOptional.collate(.binary))
-        AssertSQL("(\"stringOptional\" COLLATE NOCASE)", stringOptional.collate(.nocase))
-        AssertSQL("(\"stringOptional\" COLLATE RTRIM)", stringOptional.collate(.rtrim))
-        AssertSQL("(\"stringOptional\" COLLATE \"CUSTOM\")", stringOptional.collate(.custom("CUSTOM")))
+        assertSQL("(\"stringOptional\" COLLATE BINARY)", stringOptional.collate(.binary))
+        assertSQL("(\"stringOptional\" COLLATE NOCASE)", stringOptional.collate(.nocase))
+        assertSQL("(\"stringOptional\" COLLATE RTRIM)", stringOptional.collate(.rtrim))
+        assertSQL("(\"stringOptional\" COLLATE \"CUSTOM\")", stringOptional.collate(.custom("CUSTOM")))
     }
 
     func test_ltrim_wrapsStringWithLtrimFunction() {
-        AssertSQL("ltrim(\"string\")", string.ltrim())
-        AssertSQL("ltrim(\"stringOptional\")", stringOptional.ltrim())
+        assertSQL("ltrim(\"string\")", string.ltrim())
+        assertSQL("ltrim(\"stringOptional\")", stringOptional.ltrim())
 
-        AssertSQL("ltrim(\"string\", ' ')", string.ltrim([" "]))
-        AssertSQL("ltrim(\"stringOptional\", ' ')", stringOptional.ltrim([" "]))
+        assertSQL("ltrim(\"string\", ' ')", string.ltrim([" "]))
+        assertSQL("ltrim(\"stringOptional\", ' ')", stringOptional.ltrim([" "]))
     }
 
     func test_ltrim_wrapsStringWithRtrimFunction() {
-        AssertSQL("rtrim(\"string\")", string.rtrim())
-        AssertSQL("rtrim(\"stringOptional\")", stringOptional.rtrim())
+        assertSQL("rtrim(\"string\")", string.rtrim())
+        assertSQL("rtrim(\"stringOptional\")", stringOptional.rtrim())
 
-        AssertSQL("rtrim(\"string\", ' ')", string.rtrim([" "]))
-        AssertSQL("rtrim(\"stringOptional\", ' ')", stringOptional.rtrim([" "]))
+        assertSQL("rtrim(\"string\", ' ')", string.rtrim([" "]))
+        assertSQL("rtrim(\"stringOptional\", ' ')", stringOptional.rtrim([" "]))
     }
 
     func test_ltrim_wrapsStringWithTrimFunction() {
-        AssertSQL("trim(\"string\")", string.trim())
-        AssertSQL("trim(\"stringOptional\")", stringOptional.trim())
+        assertSQL("trim(\"string\")", string.trim())
+        assertSQL("trim(\"stringOptional\")", stringOptional.trim())
 
-        AssertSQL("trim(\"string\", ' ')", string.trim([" "]))
-        AssertSQL("trim(\"stringOptional\", ' ')", stringOptional.trim([" "]))
+        assertSQL("trim(\"string\", ' ')", string.trim([" "]))
+        assertSQL("trim(\"stringOptional\", ' ')", stringOptional.trim([" "]))
     }
 
     func test_replace_wrapsStringWithReplaceFunction() {
-        AssertSQL("replace(\"string\", '@example.com', '@example.net')", string.replace("@example.com", with: "@example.net"))
-        AssertSQL("replace(\"stringOptional\", '@example.net', '@example.com')", stringOptional.replace("@example.net", with: "@example.com"))
+        assertSQL("replace(\"string\", '@example.com', '@example.net')", string.replace("@example.com", with: "@example.net"))
+        assertSQL("replace(\"stringOptional\", '@example.net', '@example.com')", stringOptional.replace("@example.net", with: "@example.com"))
     }
 
     func test_substring_wrapsStringWithSubstrFunction() {
-        AssertSQL("substr(\"string\", 1, 2)", string.substring(1, length: 2))
-        AssertSQL("substr(\"stringOptional\", 2, 1)", stringOptional.substring(2, length: 1))
+        assertSQL("substr(\"string\", 1, 2)", string.substring(1, length: 2))
+        assertSQL("substr(\"stringOptional\", 2, 1)", stringOptional.substring(2, length: 1))
     }
 
     func test_subscriptWithRange_wrapsStringWithSubstrFunction() {
-        AssertSQL("substr(\"string\", 1, 2)", string[1..<3])
-        AssertSQL("substr(\"stringOptional\", 2, 1)", stringOptional[2..<3])
+        assertSQL("substr(\"string\", 1, 2)", string[1..<3])
+        assertSQL("substr(\"stringOptional\", 2, 1)", stringOptional[2..<3])
     }
 
     func test_nilCoalescingOperator_wrapsOptionalsWithIfnullFunction() {
-        AssertSQL("ifnull(\"intOptional\", 1)", intOptional ?? 1)
+        assertSQL("ifnull(\"intOptional\", 1)", intOptional ?? 1)
         // AssertSQL("ifnull(\"doubleOptional\", 1.0)", doubleOptional ?? 1) // rdar://problem/21677256
         XCTAssertEqual("ifnull(\"doubleOptional\", 1.0)", (doubleOptional ?? 1).asSQL())
-        AssertSQL("ifnull(\"stringOptional\", 'literal')", stringOptional ?? "literal")
+        assertSQL("ifnull(\"stringOptional\", 'literal')", stringOptional ?? "literal")
 
-        AssertSQL("ifnull(\"intOptional\", \"int\")", intOptional ?? int)
-        AssertSQL("ifnull(\"doubleOptional\", \"double\")", doubleOptional ?? double)
-        AssertSQL("ifnull(\"stringOptional\", \"string\")", stringOptional ?? string)
+        assertSQL("ifnull(\"intOptional\", \"int\")", intOptional ?? int)
+        assertSQL("ifnull(\"doubleOptional\", \"double\")", doubleOptional ?? double)
+        assertSQL("ifnull(\"stringOptional\", \"string\")", stringOptional ?? string)
 
-        AssertSQL("ifnull(\"intOptional\", \"intOptional\")", intOptional ?? intOptional)
-        AssertSQL("ifnull(\"doubleOptional\", \"doubleOptional\")", doubleOptional ?? doubleOptional)
-        AssertSQL("ifnull(\"stringOptional\", \"stringOptional\")", stringOptional ?? stringOptional)
+        assertSQL("ifnull(\"intOptional\", \"intOptional\")", intOptional ?? intOptional)
+        assertSQL("ifnull(\"doubleOptional\", \"doubleOptional\")", doubleOptional ?? doubleOptional)
+        assertSQL("ifnull(\"stringOptional\", \"stringOptional\")", stringOptional ?? stringOptional)
     }
 
     func test_absoluteValue_wrapsNumberWithAbsFucntion() {
-        AssertSQL("abs(\"int\")", int.absoluteValue)
-        AssertSQL("abs(\"intOptional\")", intOptional.absoluteValue)
+        assertSQL("abs(\"int\")", int.absoluteValue)
+        assertSQL("abs(\"intOptional\")", intOptional.absoluteValue)
 
-        AssertSQL("abs(\"double\")", double.absoluteValue)
-        AssertSQL("abs(\"doubleOptional\")", doubleOptional.absoluteValue)
+        assertSQL("abs(\"double\")", double.absoluteValue)
+        assertSQL("abs(\"doubleOptional\")", doubleOptional.absoluteValue)
     }
 
     func test_contains_buildsExpressionWithInOperator() {
-        AssertSQL("(\"string\" IN ('hello', 'world'))", ["hello", "world"].contains(string))
-        AssertSQL("(\"stringOptional\" IN ('hello', 'world'))", ["hello", "world"].contains(stringOptional))
+        assertSQL("(\"string\" IN ('hello', 'world'))", ["hello", "world"].contains(string))
+        assertSQL("(\"stringOptional\" IN ('hello', 'world'))", ["hello", "world"].contains(stringOptional))
     }
 
 }
diff --git a/Tests/SQLiteTests/CustomFunctionsTests.swift b/Tests/SQLiteTests/CustomFunctionsTests.swift
index 919986b6..0ebcd13c 100644
--- a/Tests/SQLiteTests/CustomFunctionsTests.swift
+++ b/Tests/SQLiteTests/CustomFunctionsTests.swift
@@ -1,7 +1,7 @@
 import XCTest
 import SQLite
 
-class CustomFunctionNoArgsTests : SQLiteTestCase {
+class CustomFunctionNoArgsTests: SQLiteTestCase {
     typealias FunctionNoOptional              = ()  -> Expression<String>
     typealias FunctionResultOptional          = ()  -> Expression<String?>
 
@@ -22,7 +22,7 @@ class CustomFunctionNoArgsTests : SQLiteTestCase {
     }
 }
 
-class CustomFunctionWithOneArgTests : SQLiteTestCase {
+class CustomFunctionWithOneArgTests: SQLiteTestCase {
     typealias FunctionNoOptional              = (Expression<String>)  -> Expression<String>
     typealias FunctionLeftOptional            = (Expression<String?>) -> Expression<String>
     typealias FunctionResultOptional          = (Expression<String>)  -> Expression<String?>
@@ -53,7 +53,7 @@ class CustomFunctionWithOneArgTests : SQLiteTestCase {
     }
 
     func testFunctionLeftResultOptional() {
-        let _: FunctionLeftResultOptional = try! db.createFunction("test", deterministic: true) { (a:String?) -> String? in
+        let _: FunctionLeftResultOptional = try! db.createFunction("test", deterministic: true) { (a: String?) -> String? in
             return "b"+a!
         }
         let result = try! db.prepare("SELECT test(?)").scalar("a") as! String
@@ -61,14 +61,14 @@ class CustomFunctionWithOneArgTests : SQLiteTestCase {
     }
 }
 
-class CustomFunctionWithTwoArgsTests : SQLiteTestCase {
-    typealias FunctionNoOptional              = (Expression<String>,  Expression<String>)  -> Expression<String>
+class CustomFunctionWithTwoArgsTests: SQLiteTestCase {
+    typealias FunctionNoOptional              = (Expression<String>, Expression<String>)  -> Expression<String>
     typealias FunctionLeftOptional            = (Expression<String?>, Expression<String>)  -> Expression<String>
-    typealias FunctionRightOptional           = (Expression<String>,  Expression<String?>) -> Expression<String>
-    typealias FunctionResultOptional          = (Expression<String>,  Expression<String>)  -> Expression<String?>
+    typealias FunctionRightOptional           = (Expression<String>, Expression<String?>) -> Expression<String>
+    typealias FunctionResultOptional          = (Expression<String>, Expression<String>)  -> Expression<String?>
     typealias FunctionLeftRightOptional       = (Expression<String?>, Expression<String?>) -> Expression<String>
     typealias FunctionLeftResultOptional      = (Expression<String?>, Expression<String>)  -> Expression<String?>
-    typealias FunctionRightResultOptional     = (Expression<String>,  Expression<String?>) -> Expression<String?>
+    typealias FunctionRightResultOptional     = (Expression<String>, Expression<String?>) -> Expression<String?>
     typealias FunctionLeftRightResultOptional = (Expression<String?>, Expression<String?>) -> Expression<String?>
 
     func testNoOptional() {
diff --git a/Tests/SQLiteTests/DateAndTimeFunctionTests.swift b/Tests/SQLiteTests/DateAndTimeFunctionTests.swift
index 628b5910..393e9c7c 100644
--- a/Tests/SQLiteTests/DateAndTimeFunctionTests.swift
+++ b/Tests/SQLiteTests/DateAndTimeFunctionTests.swift
@@ -1,66 +1,66 @@
 import XCTest
 @testable import SQLite
 
-class DateAndTimeFunctionsTests : XCTestCase {
+class DateAndTimeFunctionsTests: XCTestCase {
 
     func test_date() {
-        AssertSQL("date('now')", DateFunctions.date("now"))
-        AssertSQL("date('now', 'localtime')", DateFunctions.date("now", "localtime"))
+        assertSQL("date('now')", DateFunctions.date("now"))
+        assertSQL("date('now', 'localtime')", DateFunctions.date("now", "localtime"))
     }
 
     func test_time() {
-        AssertSQL("time('now')", DateFunctions.time("now"))
-        AssertSQL("time('now', 'localtime')", DateFunctions.time("now", "localtime"))
+        assertSQL("time('now')", DateFunctions.time("now"))
+        assertSQL("time('now', 'localtime')", DateFunctions.time("now", "localtime"))
     }
 
     func test_datetime() {
-        AssertSQL("datetime('now')", DateFunctions.datetime("now"))
-        AssertSQL("datetime('now', 'localtime')", DateFunctions.datetime("now", "localtime"))
+        assertSQL("datetime('now')", DateFunctions.datetime("now"))
+        assertSQL("datetime('now', 'localtime')", DateFunctions.datetime("now", "localtime"))
     }
 
     func test_julianday() {
-        AssertSQL("julianday('now')", DateFunctions.julianday("now"))
-        AssertSQL("julianday('now', 'localtime')", DateFunctions.julianday("now", "localtime"))
+        assertSQL("julianday('now')", DateFunctions.julianday("now"))
+        assertSQL("julianday('now', 'localtime')", DateFunctions.julianday("now", "localtime"))
     }
 
     func test_strftime() {
-        AssertSQL("strftime('%Y-%m-%d', 'now')", DateFunctions.strftime("%Y-%m-%d", "now"))
-        AssertSQL("strftime('%Y-%m-%d', 'now', 'localtime')", DateFunctions.strftime("%Y-%m-%d", "now", "localtime"))
+        assertSQL("strftime('%Y-%m-%d', 'now')", DateFunctions.strftime("%Y-%m-%d", "now"))
+        assertSQL("strftime('%Y-%m-%d', 'now', 'localtime')", DateFunctions.strftime("%Y-%m-%d", "now", "localtime"))
     }
 }
 
-class DateExtensionTests : XCTestCase {
+class DateExtensionTests: XCTestCase {
     func test_time() {
-        AssertSQL("time('1970-01-01T00:00:00.000')", Date(timeIntervalSince1970: 0).time)
+        assertSQL("time('1970-01-01T00:00:00.000')", Date(timeIntervalSince1970: 0).time)
     }
 
     func test_date() {
-        AssertSQL("date('1970-01-01T00:00:00.000')", Date(timeIntervalSince1970: 0).date)
+        assertSQL("date('1970-01-01T00:00:00.000')", Date(timeIntervalSince1970: 0).date)
     }
 
     func test_datetime() {
-        AssertSQL("datetime('1970-01-01T00:00:00.000')", Date(timeIntervalSince1970: 0).datetime)
+        assertSQL("datetime('1970-01-01T00:00:00.000')", Date(timeIntervalSince1970: 0).datetime)
     }
 
     func test_julianday() {
-        AssertSQL("julianday('1970-01-01T00:00:00.000')", Date(timeIntervalSince1970: 0).julianday)
+        assertSQL("julianday('1970-01-01T00:00:00.000')", Date(timeIntervalSince1970: 0).julianday)
     }
 }
 
-class DateExpressionTests : XCTestCase {
+class DateExpressionTests: XCTestCase {
     func test_date() {
-        AssertSQL("date(\"date\")", date.date)
+        assertSQL("date(\"date\")", date.date)
     }
 
     func test_time() {
-        AssertSQL("time(\"date\")", date.time)
+        assertSQL("time(\"date\")", date.time)
     }
 
     func test_datetime() {
-        AssertSQL("datetime(\"date\")", date.datetime)
+        assertSQL("datetime(\"date\")", date.datetime)
     }
 
     func test_julianday() {
-        AssertSQL("julianday(\"date\")", date.julianday)
+        assertSQL("julianday(\"date\")", date.julianday)
     }
 }
diff --git a/Tests/SQLiteTests/ExpressionTests.swift b/Tests/SQLiteTests/ExpressionTests.swift
index 036e10ce..32100d4d 100644
--- a/Tests/SQLiteTests/ExpressionTests.swift
+++ b/Tests/SQLiteTests/ExpressionTests.swift
@@ -1,6 +1,5 @@
 import XCTest
 import SQLite
 
-class ExpressionTests : XCTestCase {
-
+class ExpressionTests: XCTestCase {
 }
diff --git a/Tests/SQLiteTests/FTS4Tests.swift b/Tests/SQLiteTests/FTS4Tests.swift
index 79f0a8e2..b637955b 100644
--- a/Tests/SQLiteTests/FTS4Tests.swift
+++ b/Tests/SQLiteTests/FTS4Tests.swift
@@ -1,7 +1,7 @@
 import XCTest
 import SQLite
 
-class FTS4Tests : XCTestCase {
+class FTS4Tests: XCTestCase {
 
     func test_create_onVirtualTable_withFTS4_compilesCreateVirtualTableExpression() {
         XCTAssertEqual(
@@ -25,26 +25,33 @@ class FTS4Tests : XCTestCase {
             virtualTable.create(.FTS4(tokenize: .Unicode61(removeDiacritics: false)))
         )
         XCTAssertEqual(
-            "CREATE VIRTUAL TABLE \"virtual_table\" USING fts4(tokenize=unicode61 \"removeDiacritics=1\" \"tokenchars=.\" \"separators=X\")",
-            virtualTable.create(.FTS4(tokenize: .Unicode61(removeDiacritics: true, tokenchars: ["."], separators: ["X"])))
+            """
+            CREATE VIRTUAL TABLE \"virtual_table\" USING fts4(tokenize=unicode61 \"removeDiacritics=1\"
+             \"tokenchars=.\" \"separators=X\")
+            """.replacingOccurrences(of: "\n", with: ""),
+            virtualTable.create(.FTS4(tokenize: .Unicode61(removeDiacritics: true,
+                                                           tokenchars: ["."], separators: ["X"])))
         )
     }
 
     func test_match_onVirtualTableAsExpression_compilesMatchExpression() {
-        AssertSQL("(\"virtual_table\" MATCH 'string')", virtualTable.match("string") as Expression<Bool>)
-        AssertSQL("(\"virtual_table\" MATCH \"string\")", virtualTable.match(string) as Expression<Bool>)
-        AssertSQL("(\"virtual_table\" MATCH \"stringOptional\")", virtualTable.match(stringOptional) as Expression<Bool?>)
+        assertSQL("(\"virtual_table\" MATCH 'string')", virtualTable.match("string") as Expression<Bool>)
+        assertSQL("(\"virtual_table\" MATCH \"string\")", virtualTable.match(string) as Expression<Bool>)
+        assertSQL("(\"virtual_table\" MATCH \"stringOptional\")", virtualTable.match(stringOptional) as Expression<Bool?>)
     }
 
     func test_match_onVirtualTableAsQueryType_compilesMatchExpression() {
-        AssertSQL("SELECT * FROM \"virtual_table\" WHERE (\"virtual_table\" MATCH 'string')", virtualTable.match("string") as QueryType)
-        AssertSQL("SELECT * FROM \"virtual_table\" WHERE (\"virtual_table\" MATCH \"string\")", virtualTable.match(string) as QueryType)
-        AssertSQL("SELECT * FROM \"virtual_table\" WHERE (\"virtual_table\" MATCH \"stringOptional\")", virtualTable.match(stringOptional) as QueryType)
+        assertSQL("SELECT * FROM \"virtual_table\" WHERE (\"virtual_table\" MATCH 'string')",
+                  virtualTable.match("string") as QueryType)
+        assertSQL("SELECT * FROM \"virtual_table\" WHERE (\"virtual_table\" MATCH \"string\")",
+                  virtualTable.match(string) as QueryType)
+        assertSQL("SELECT * FROM \"virtual_table\" WHERE (\"virtual_table\" MATCH \"stringOptional\")",
+                  virtualTable.match(stringOptional) as QueryType)
     }
 
 }
 
-class FTS4ConfigTests : XCTestCase {
+class FTS4ConfigTests: XCTestCase {
     var config: FTS4Config!
 
     override func setUp() {
@@ -108,7 +115,10 @@ class FTS4ConfigTests : XCTestCase {
 
     func test_tokenizer_unicode61_with_options() {
         XCTAssertEqual(
-            "CREATE VIRTUAL TABLE \"virtual_table\" USING fts4(tokenize=unicode61 \"removeDiacritics=1\" \"tokenchars=.\" \"separators=X\")",
+            """
+            CREATE VIRTUAL TABLE \"virtual_table\" USING fts4(tokenize=unicode61 \"removeDiacritics=1\"
+             \"tokenchars=.\" \"separators=X\")
+            """.replacingOccurrences(of: "\n", with: ""),
             sql(config.tokenizer(.Unicode61(removeDiacritics: true, tokenchars: ["."], separators: ["X"]))))
     }
 
@@ -156,7 +166,11 @@ class FTS4ConfigTests : XCTestCase {
 
     func test_config_all() {
         XCTAssertEqual(
-            "CREATE VIRTUAL TABLE \"virtual_table\" USING fts4(\"int\", \"string\", \"date\", tokenize=porter, prefix=\"2,4\", content=\"table\", notindexed=\"string\", notindexed=\"date\", languageid=\"lid\", matchinfo=\"fts3\", order=\"desc\")",
+            """
+            CREATE VIRTUAL TABLE \"virtual_table\" USING fts4(\"int\", \"string\", \"date\",
+             tokenize=porter, prefix=\"2,4\", content=\"table\", notindexed=\"string\", notindexed=\"date\",
+             languageid=\"lid\", matchinfo=\"fts3\", order=\"desc\")
+            """.replacingOccurrences(of: "\n", with: ""),
             sql(config
                 .tokenizer(.Porter)
                 .column(int)
@@ -175,7 +189,7 @@ class FTS4ConfigTests : XCTestCase {
     }
 }
 
-class FTS4IntegrationTests : SQLiteTestCase {
+class FTS4IntegrationTests: SQLiteTestCase {
 #if !SQLITE_SWIFT_STANDALONE && !SQLITE_SWIFT_SQLCIPHER
     func test_registerTokenizer_registersTokenizer() {
         let emails = VirtualTable("emails")
@@ -184,9 +198,11 @@ class FTS4IntegrationTests : SQLiteTestCase {
 
         let locale = CFLocaleCopyCurrent()
         let tokenizerName = "tokenizer"
-        let tokenizer = CFStringTokenizerCreate(nil, "" as CFString, CFRangeMake(0, 0), UInt(kCFStringTokenizerUnitWord), locale)
+        let tokenizer = CFStringTokenizerCreate(nil, "" as CFString, CFRangeMake(0, 0),
+                                                UInt(kCFStringTokenizerUnitWord), locale)
         try! db.registerTokenizer(tokenizerName) { string in
-            CFStringTokenizerSetString(tokenizer, string as CFString, CFRangeMake(0, CFStringGetLength(string as CFString)))
+            CFStringTokenizerSetString(tokenizer, string as CFString,
+                                       CFRangeMake(0, CFStringGetLength(string as CFString)))
             if CFStringTokenizerAdvanceToNextToken(tokenizer).isEmpty {
                 return nil
             }
@@ -199,7 +215,10 @@ class FTS4IntegrationTests : SQLiteTestCase {
         }
 
         try! db.run(emails.create(.FTS4([subject, body], tokenize: .Custom(tokenizerName))))
-        AssertSQL("CREATE VIRTUAL TABLE \"emails\" USING fts4(\"subject\", \"body\", tokenize=\"SQLite.swift\" \"tokenizer\")")
+        assertSQL("""
+                  CREATE VIRTUAL TABLE \"emails\" USING fts4(\"subject\", \"body\",
+                   tokenize=\"SQLite.swift\" \"tokenizer\")
+                  """.replacingOccurrences(of: "\n", with: ""))
 
         try! _ = db.run(emails.insert(subject <- "Aún más cáfe!"))
         XCTAssertEqual(1, try! db.scalar(emails.filter(emails.match("aun")).count))
diff --git a/Tests/SQLiteTests/FTS5Tests.swift b/Tests/SQLiteTests/FTS5Tests.swift
index 63d8dc40..8079b28c 100644
--- a/Tests/SQLiteTests/FTS5Tests.swift
+++ b/Tests/SQLiteTests/FTS5Tests.swift
@@ -107,7 +107,10 @@ class FTS5Tests: XCTestCase {
 
     func test_fts5_config_all() {
         XCTAssertEqual(
-            "CREATE VIRTUAL TABLE \"virtual_table\" USING fts5(\"int\", \"string\" UNINDEXED, \"date\" UNINDEXED, tokenize=porter, prefix=\"2,4\", content=\"table\")",
+            """
+            CREATE VIRTUAL TABLE \"virtual_table\" USING fts5(\"int\", \"string\" UNINDEXED, \"date\" UNINDEXED,
+             tokenize=porter, prefix=\"2,4\", content=\"table\")
+            """.replacingOccurrences(of: "\n", with: ""),
             sql(config
                 .tokenizer(.Porter)
                 .column(int)
diff --git a/Tests/SQLiteTests/FoundationTests.swift b/Tests/SQLiteTests/FoundationTests.swift
index ba9685b7..cef485fc 100644
--- a/Tests/SQLiteTests/FoundationTests.swift
+++ b/Tests/SQLiteTests/FoundationTests.swift
@@ -1,7 +1,7 @@
 import XCTest
 import SQLite
 
-class FoundationTests : XCTestCase {
+class FoundationTests: XCTestCase {
     func testDataFromBlob() {
         let data = Data([1, 2, 3])
         let blob = data.datatypeValue
diff --git a/Tests/SQLiteTests/OperatorsTests.swift b/Tests/SQLiteTests/OperatorsTests.swift
index c9665671..370b910b 100644
--- a/Tests/SQLiteTests/OperatorsTests.swift
+++ b/Tests/SQLiteTests/OperatorsTests.swift
@@ -1,378 +1,378 @@
 import XCTest
 import SQLite
 
-class OperatorsTests : XCTestCase {
+class OperatorsTests: XCTestCase {
 
     func test_stringExpressionPlusStringExpression_buildsConcatenatingStringExpression() {
-        AssertSQL("(\"string\" || \"string\")", string + string)
-        AssertSQL("(\"string\" || \"stringOptional\")", string + stringOptional)
-        AssertSQL("(\"stringOptional\" || \"string\")", stringOptional + string)
-        AssertSQL("(\"stringOptional\" || \"stringOptional\")", stringOptional + stringOptional)
-        AssertSQL("(\"string\" || 'literal')", string + "literal")
-        AssertSQL("(\"stringOptional\" || 'literal')", stringOptional + "literal")
-        AssertSQL("('literal' || \"string\")", "literal" + string)
-        AssertSQL("('literal' || \"stringOptional\")", "literal" + stringOptional)
+        assertSQL("(\"string\" || \"string\")", string + string)
+        assertSQL("(\"string\" || \"stringOptional\")", string + stringOptional)
+        assertSQL("(\"stringOptional\" || \"string\")", stringOptional + string)
+        assertSQL("(\"stringOptional\" || \"stringOptional\")", stringOptional + stringOptional)
+        assertSQL("(\"string\" || 'literal')", string + "literal")
+        assertSQL("(\"stringOptional\" || 'literal')", stringOptional + "literal")
+        assertSQL("('literal' || \"string\")", "literal" + string)
+        assertSQL("('literal' || \"stringOptional\")", "literal" + stringOptional)
     }
 
     func test_numberExpression_plusNumberExpression_buildsAdditiveNumberExpression() {
-        AssertSQL("(\"int\" + \"int\")", int + int)
-        AssertSQL("(\"int\" + \"intOptional\")", int + intOptional)
-        AssertSQL("(\"intOptional\" + \"int\")", intOptional + int)
-        AssertSQL("(\"intOptional\" + \"intOptional\")", intOptional + intOptional)
-        AssertSQL("(\"int\" + 1)", int + 1)
-        AssertSQL("(\"intOptional\" + 1)", intOptional + 1)
-        AssertSQL("(1 + \"int\")", 1 + int)
-        AssertSQL("(1 + \"intOptional\")", 1 + intOptional)
-
-        AssertSQL("(\"double\" + \"double\")", double + double)
-        AssertSQL("(\"double\" + \"doubleOptional\")", double + doubleOptional)
-        AssertSQL("(\"doubleOptional\" + \"double\")", doubleOptional + double)
-        AssertSQL("(\"doubleOptional\" + \"doubleOptional\")", doubleOptional + doubleOptional)
-        AssertSQL("(\"double\" + 1.0)", double + 1)
-        AssertSQL("(\"doubleOptional\" + 1.0)", doubleOptional + 1)
-        AssertSQL("(1.0 + \"double\")", 1 + double)
-        AssertSQL("(1.0 + \"doubleOptional\")", 1 + doubleOptional)
+        assertSQL("(\"int\" + \"int\")", int + int)
+        assertSQL("(\"int\" + \"intOptional\")", int + intOptional)
+        assertSQL("(\"intOptional\" + \"int\")", intOptional + int)
+        assertSQL("(\"intOptional\" + \"intOptional\")", intOptional + intOptional)
+        assertSQL("(\"int\" + 1)", int + 1)
+        assertSQL("(\"intOptional\" + 1)", intOptional + 1)
+        assertSQL("(1 + \"int\")", 1 + int)
+        assertSQL("(1 + \"intOptional\")", 1 + intOptional)
+
+        assertSQL("(\"double\" + \"double\")", double + double)
+        assertSQL("(\"double\" + \"doubleOptional\")", double + doubleOptional)
+        assertSQL("(\"doubleOptional\" + \"double\")", doubleOptional + double)
+        assertSQL("(\"doubleOptional\" + \"doubleOptional\")", doubleOptional + doubleOptional)
+        assertSQL("(\"double\" + 1.0)", double + 1)
+        assertSQL("(\"doubleOptional\" + 1.0)", doubleOptional + 1)
+        assertSQL("(1.0 + \"double\")", 1 + double)
+        assertSQL("(1.0 + \"doubleOptional\")", 1 + doubleOptional)
     }
 
     func test_numberExpression_minusNumberExpression_buildsSubtractiveNumberExpression() {
-        AssertSQL("(\"int\" - \"int\")", int - int)
-        AssertSQL("(\"int\" - \"intOptional\")", int - intOptional)
-        AssertSQL("(\"intOptional\" - \"int\")", intOptional - int)
-        AssertSQL("(\"intOptional\" - \"intOptional\")", intOptional - intOptional)
-        AssertSQL("(\"int\" - 1)", int - 1)
-        AssertSQL("(\"intOptional\" - 1)", intOptional - 1)
-        AssertSQL("(1 - \"int\")", 1 - int)
-        AssertSQL("(1 - \"intOptional\")", 1 - intOptional)
-
-        AssertSQL("(\"double\" - \"double\")", double - double)
-        AssertSQL("(\"double\" - \"doubleOptional\")", double - doubleOptional)
-        AssertSQL("(\"doubleOptional\" - \"double\")", doubleOptional - double)
-        AssertSQL("(\"doubleOptional\" - \"doubleOptional\")", doubleOptional - doubleOptional)
-        AssertSQL("(\"double\" - 1.0)", double - 1)
-        AssertSQL("(\"doubleOptional\" - 1.0)", doubleOptional - 1)
-        AssertSQL("(1.0 - \"double\")", 1 - double)
-        AssertSQL("(1.0 - \"doubleOptional\")", 1 - doubleOptional)
+        assertSQL("(\"int\" - \"int\")", int - int)
+        assertSQL("(\"int\" - \"intOptional\")", int - intOptional)
+        assertSQL("(\"intOptional\" - \"int\")", intOptional - int)
+        assertSQL("(\"intOptional\" - \"intOptional\")", intOptional - intOptional)
+        assertSQL("(\"int\" - 1)", int - 1)
+        assertSQL("(\"intOptional\" - 1)", intOptional - 1)
+        assertSQL("(1 - \"int\")", 1 - int)
+        assertSQL("(1 - \"intOptional\")", 1 - intOptional)
+
+        assertSQL("(\"double\" - \"double\")", double - double)
+        assertSQL("(\"double\" - \"doubleOptional\")", double - doubleOptional)
+        assertSQL("(\"doubleOptional\" - \"double\")", doubleOptional - double)
+        assertSQL("(\"doubleOptional\" - \"doubleOptional\")", doubleOptional - doubleOptional)
+        assertSQL("(\"double\" - 1.0)", double - 1)
+        assertSQL("(\"doubleOptional\" - 1.0)", doubleOptional - 1)
+        assertSQL("(1.0 - \"double\")", 1 - double)
+        assertSQL("(1.0 - \"doubleOptional\")", 1 - doubleOptional)
     }
 
     func test_numberExpression_timesNumberExpression_buildsMultiplicativeNumberExpression() {
-        AssertSQL("(\"int\" * \"int\")", int * int)
-        AssertSQL("(\"int\" * \"intOptional\")", int * intOptional)
-        AssertSQL("(\"intOptional\" * \"int\")", intOptional * int)
-        AssertSQL("(\"intOptional\" * \"intOptional\")", intOptional * intOptional)
-        AssertSQL("(\"int\" * 1)", int * 1)
-        AssertSQL("(\"intOptional\" * 1)", intOptional * 1)
-        AssertSQL("(1 * \"int\")", 1 * int)
-        AssertSQL("(1 * \"intOptional\")", 1 * intOptional)
-
-        AssertSQL("(\"double\" * \"double\")", double * double)
-        AssertSQL("(\"double\" * \"doubleOptional\")", double * doubleOptional)
-        AssertSQL("(\"doubleOptional\" * \"double\")", doubleOptional * double)
-        AssertSQL("(\"doubleOptional\" * \"doubleOptional\")", doubleOptional * doubleOptional)
-        AssertSQL("(\"double\" * 1.0)", double * 1)
-        AssertSQL("(\"doubleOptional\" * 1.0)", doubleOptional * 1)
-        AssertSQL("(1.0 * \"double\")", 1 * double)
-        AssertSQL("(1.0 * \"doubleOptional\")", 1 * doubleOptional)
+        assertSQL("(\"int\" * \"int\")", int * int)
+        assertSQL("(\"int\" * \"intOptional\")", int * intOptional)
+        assertSQL("(\"intOptional\" * \"int\")", intOptional * int)
+        assertSQL("(\"intOptional\" * \"intOptional\")", intOptional * intOptional)
+        assertSQL("(\"int\" * 1)", int * 1)
+        assertSQL("(\"intOptional\" * 1)", intOptional * 1)
+        assertSQL("(1 * \"int\")", 1 * int)
+        assertSQL("(1 * \"intOptional\")", 1 * intOptional)
+
+        assertSQL("(\"double\" * \"double\")", double * double)
+        assertSQL("(\"double\" * \"doubleOptional\")", double * doubleOptional)
+        assertSQL("(\"doubleOptional\" * \"double\")", doubleOptional * double)
+        assertSQL("(\"doubleOptional\" * \"doubleOptional\")", doubleOptional * doubleOptional)
+        assertSQL("(\"double\" * 1.0)", double * 1)
+        assertSQL("(\"doubleOptional\" * 1.0)", doubleOptional * 1)
+        assertSQL("(1.0 * \"double\")", 1 * double)
+        assertSQL("(1.0 * \"doubleOptional\")", 1 * doubleOptional)
     }
 
     func test_numberExpression_dividedByNumberExpression_buildsDivisiveNumberExpression() {
-        AssertSQL("(\"int\" / \"int\")", int / int)
-        AssertSQL("(\"int\" / \"intOptional\")", int / intOptional)
-        AssertSQL("(\"intOptional\" / \"int\")", intOptional / int)
-        AssertSQL("(\"intOptional\" / \"intOptional\")", intOptional / intOptional)
-        AssertSQL("(\"int\" / 1)", int / 1)
-        AssertSQL("(\"intOptional\" / 1)", intOptional / 1)
-        AssertSQL("(1 / \"int\")", 1 / int)
-        AssertSQL("(1 / \"intOptional\")", 1 / intOptional)
-
-        AssertSQL("(\"double\" / \"double\")", double / double)
-        AssertSQL("(\"double\" / \"doubleOptional\")", double / doubleOptional)
-        AssertSQL("(\"doubleOptional\" / \"double\")", doubleOptional / double)
-        AssertSQL("(\"doubleOptional\" / \"doubleOptional\")", doubleOptional / doubleOptional)
-        AssertSQL("(\"double\" / 1.0)", double / 1)
-        AssertSQL("(\"doubleOptional\" / 1.0)", doubleOptional / 1)
-        AssertSQL("(1.0 / \"double\")", 1 / double)
-        AssertSQL("(1.0 / \"doubleOptional\")", 1 / doubleOptional)
+        assertSQL("(\"int\" / \"int\")", int / int)
+        assertSQL("(\"int\" / \"intOptional\")", int / intOptional)
+        assertSQL("(\"intOptional\" / \"int\")", intOptional / int)
+        assertSQL("(\"intOptional\" / \"intOptional\")", intOptional / intOptional)
+        assertSQL("(\"int\" / 1)", int / 1)
+        assertSQL("(\"intOptional\" / 1)", intOptional / 1)
+        assertSQL("(1 / \"int\")", 1 / int)
+        assertSQL("(1 / \"intOptional\")", 1 / intOptional)
+
+        assertSQL("(\"double\" / \"double\")", double / double)
+        assertSQL("(\"double\" / \"doubleOptional\")", double / doubleOptional)
+        assertSQL("(\"doubleOptional\" / \"double\")", doubleOptional / double)
+        assertSQL("(\"doubleOptional\" / \"doubleOptional\")", doubleOptional / doubleOptional)
+        assertSQL("(\"double\" / 1.0)", double / 1)
+        assertSQL("(\"doubleOptional\" / 1.0)", doubleOptional / 1)
+        assertSQL("(1.0 / \"double\")", 1 / double)
+        assertSQL("(1.0 / \"doubleOptional\")", 1 / doubleOptional)
     }
 
     func test_numberExpression_prefixedWithMinus_buildsInvertedNumberExpression() {
-        AssertSQL("-(\"int\")", -int)
-        AssertSQL("-(\"intOptional\")", -intOptional)
+        assertSQL("-(\"int\")", -int)
+        assertSQL("-(\"intOptional\")", -intOptional)
 
-        AssertSQL("-(\"double\")", -double)
-        AssertSQL("-(\"doubleOptional\")", -doubleOptional)
+        assertSQL("-(\"double\")", -double)
+        assertSQL("-(\"doubleOptional\")", -doubleOptional)
     }
 
     func test_integerExpression_moduloIntegerExpression_buildsModuloIntegerExpression() {
-        AssertSQL("(\"int\" % \"int\")", int % int)
-        AssertSQL("(\"int\" % \"intOptional\")", int % intOptional)
-        AssertSQL("(\"intOptional\" % \"int\")", intOptional % int)
-        AssertSQL("(\"intOptional\" % \"intOptional\")", intOptional % intOptional)
-        AssertSQL("(\"int\" % 1)", int % 1)
-        AssertSQL("(\"intOptional\" % 1)", intOptional % 1)
-        AssertSQL("(1 % \"int\")", 1 % int)
-        AssertSQL("(1 % \"intOptional\")", 1 % intOptional)
+        assertSQL("(\"int\" % \"int\")", int % int)
+        assertSQL("(\"int\" % \"intOptional\")", int % intOptional)
+        assertSQL("(\"intOptional\" % \"int\")", intOptional % int)
+        assertSQL("(\"intOptional\" % \"intOptional\")", intOptional % intOptional)
+        assertSQL("(\"int\" % 1)", int % 1)
+        assertSQL("(\"intOptional\" % 1)", intOptional % 1)
+        assertSQL("(1 % \"int\")", 1 % int)
+        assertSQL("(1 % \"intOptional\")", 1 % intOptional)
     }
 
     func test_integerExpression_bitShiftLeftIntegerExpression_buildsLeftShiftedIntegerExpression() {
-        AssertSQL("(\"int\" << \"int\")", int << int)
-        AssertSQL("(\"int\" << \"intOptional\")", int << intOptional)
-        AssertSQL("(\"intOptional\" << \"int\")", intOptional << int)
-        AssertSQL("(\"intOptional\" << \"intOptional\")", intOptional << intOptional)
-        AssertSQL("(\"int\" << 1)", int << 1)
-        AssertSQL("(\"intOptional\" << 1)", intOptional << 1)
-        AssertSQL("(1 << \"int\")", 1 << int)
-        AssertSQL("(1 << \"intOptional\")", 1 << intOptional)
+        assertSQL("(\"int\" << \"int\")", int << int)
+        assertSQL("(\"int\" << \"intOptional\")", int << intOptional)
+        assertSQL("(\"intOptional\" << \"int\")", intOptional << int)
+        assertSQL("(\"intOptional\" << \"intOptional\")", intOptional << intOptional)
+        assertSQL("(\"int\" << 1)", int << 1)
+        assertSQL("(\"intOptional\" << 1)", intOptional << 1)
+        assertSQL("(1 << \"int\")", 1 << int)
+        assertSQL("(1 << \"intOptional\")", 1 << intOptional)
     }
 
     func test_integerExpression_bitShiftRightIntegerExpression_buildsRightShiftedIntegerExpression() {
-        AssertSQL("(\"int\" >> \"int\")", int >> int)
-        AssertSQL("(\"int\" >> \"intOptional\")", int >> intOptional)
-        AssertSQL("(\"intOptional\" >> \"int\")", intOptional >> int)
-        AssertSQL("(\"intOptional\" >> \"intOptional\")", intOptional >> intOptional)
-        AssertSQL("(\"int\" >> 1)", int >> 1)
-        AssertSQL("(\"intOptional\" >> 1)", intOptional >> 1)
-        AssertSQL("(1 >> \"int\")", 1 >> int)
-        AssertSQL("(1 >> \"intOptional\")", 1 >> intOptional)
+        assertSQL("(\"int\" >> \"int\")", int >> int)
+        assertSQL("(\"int\" >> \"intOptional\")", int >> intOptional)
+        assertSQL("(\"intOptional\" >> \"int\")", intOptional >> int)
+        assertSQL("(\"intOptional\" >> \"intOptional\")", intOptional >> intOptional)
+        assertSQL("(\"int\" >> 1)", int >> 1)
+        assertSQL("(\"intOptional\" >> 1)", intOptional >> 1)
+        assertSQL("(1 >> \"int\")", 1 >> int)
+        assertSQL("(1 >> \"intOptional\")", 1 >> intOptional)
     }
 
     func test_integerExpression_bitwiseAndIntegerExpression_buildsAndedIntegerExpression() {
-        AssertSQL("(\"int\" & \"int\")", int & int)
-        AssertSQL("(\"int\" & \"intOptional\")", int & intOptional)
-        AssertSQL("(\"intOptional\" & \"int\")", intOptional & int)
-        AssertSQL("(\"intOptional\" & \"intOptional\")", intOptional & intOptional)
-        AssertSQL("(\"int\" & 1)", int & 1)
-        AssertSQL("(\"intOptional\" & 1)", intOptional & 1)
-        AssertSQL("(1 & \"int\")", 1 & int)
-        AssertSQL("(1 & \"intOptional\")", 1 & intOptional)
+        assertSQL("(\"int\" & \"int\")", int & int)
+        assertSQL("(\"int\" & \"intOptional\")", int & intOptional)
+        assertSQL("(\"intOptional\" & \"int\")", intOptional & int)
+        assertSQL("(\"intOptional\" & \"intOptional\")", intOptional & intOptional)
+        assertSQL("(\"int\" & 1)", int & 1)
+        assertSQL("(\"intOptional\" & 1)", intOptional & 1)
+        assertSQL("(1 & \"int\")", 1 & int)
+        assertSQL("(1 & \"intOptional\")", 1 & intOptional)
     }
 
     func test_integerExpression_bitwiseOrIntegerExpression_buildsOredIntegerExpression() {
-        AssertSQL("(\"int\" | \"int\")", int | int)
-        AssertSQL("(\"int\" | \"intOptional\")", int | intOptional)
-        AssertSQL("(\"intOptional\" | \"int\")", intOptional | int)
-        AssertSQL("(\"intOptional\" | \"intOptional\")", intOptional | intOptional)
-        AssertSQL("(\"int\" | 1)", int | 1)
-        AssertSQL("(\"intOptional\" | 1)", intOptional | 1)
-        AssertSQL("(1 | \"int\")", 1 | int)
-        AssertSQL("(1 | \"intOptional\")", 1 | intOptional)
+        assertSQL("(\"int\" | \"int\")", int | int)
+        assertSQL("(\"int\" | \"intOptional\")", int | intOptional)
+        assertSQL("(\"intOptional\" | \"int\")", intOptional | int)
+        assertSQL("(\"intOptional\" | \"intOptional\")", intOptional | intOptional)
+        assertSQL("(\"int\" | 1)", int | 1)
+        assertSQL("(\"intOptional\" | 1)", intOptional | 1)
+        assertSQL("(1 | \"int\")", 1 | int)
+        assertSQL("(1 | \"intOptional\")", 1 | intOptional)
     }
 
     func test_integerExpression_bitwiseExclusiveOrIntegerExpression_buildsOredIntegerExpression() {
-        AssertSQL("(~((\"int\" & \"int\")) & (\"int\" | \"int\"))", int ^ int)
-        AssertSQL("(~((\"int\" & \"intOptional\")) & (\"int\" | \"intOptional\"))", int ^ intOptional)
-        AssertSQL("(~((\"intOptional\" & \"int\")) & (\"intOptional\" | \"int\"))", intOptional ^ int)
-        AssertSQL("(~((\"intOptional\" & \"intOptional\")) & (\"intOptional\" | \"intOptional\"))", intOptional ^ intOptional)
-        AssertSQL("(~((\"int\" & 1)) & (\"int\" | 1))", int ^ 1)
-        AssertSQL("(~((\"intOptional\" & 1)) & (\"intOptional\" | 1))", intOptional ^ 1)
-        AssertSQL("(~((1 & \"int\")) & (1 | \"int\"))", 1 ^ int)
-        AssertSQL("(~((1 & \"intOptional\")) & (1 | \"intOptional\"))", 1 ^ intOptional)
+        assertSQL("(~((\"int\" & \"int\")) & (\"int\" | \"int\"))", int ^ int)
+        assertSQL("(~((\"int\" & \"intOptional\")) & (\"int\" | \"intOptional\"))", int ^ intOptional)
+        assertSQL("(~((\"intOptional\" & \"int\")) & (\"intOptional\" | \"int\"))", intOptional ^ int)
+        assertSQL("(~((\"intOptional\" & \"intOptional\")) & (\"intOptional\" | \"intOptional\"))", intOptional ^ intOptional)
+        assertSQL("(~((\"int\" & 1)) & (\"int\" | 1))", int ^ 1)
+        assertSQL("(~((\"intOptional\" & 1)) & (\"intOptional\" | 1))", intOptional ^ 1)
+        assertSQL("(~((1 & \"int\")) & (1 | \"int\"))", 1 ^ int)
+        assertSQL("(~((1 & \"intOptional\")) & (1 | \"intOptional\"))", 1 ^ intOptional)
     }
 
     func test_bitwiseNot_integerExpression_buildsComplementIntegerExpression() {
-        AssertSQL("~(\"int\")", ~int)
-        AssertSQL("~(\"intOptional\")", ~intOptional)
+        assertSQL("~(\"int\")", ~int)
+        assertSQL("~(\"intOptional\")", ~intOptional)
     }
 
     func test_equalityOperator_withEquatableExpressions_buildsBooleanExpression() {
-        AssertSQL("(\"bool\" = \"bool\")", bool == bool)
-        AssertSQL("(\"bool\" = \"boolOptional\")", bool == boolOptional)
-        AssertSQL("(\"boolOptional\" = \"bool\")", boolOptional == bool)
-        AssertSQL("(\"boolOptional\" = \"boolOptional\")", boolOptional == boolOptional)
-        AssertSQL("(\"bool\" = 1)", bool == true)
-        AssertSQL("(\"boolOptional\" = 1)", boolOptional == true)
-        AssertSQL("(1 = \"bool\")", true == bool)
-        AssertSQL("(1 = \"boolOptional\")", true == boolOptional)
+        assertSQL("(\"bool\" = \"bool\")", bool == bool)
+        assertSQL("(\"bool\" = \"boolOptional\")", bool == boolOptional)
+        assertSQL("(\"boolOptional\" = \"bool\")", boolOptional == bool)
+        assertSQL("(\"boolOptional\" = \"boolOptional\")", boolOptional == boolOptional)
+        assertSQL("(\"bool\" = 1)", bool == true)
+        assertSQL("(\"boolOptional\" = 1)", boolOptional == true)
+        assertSQL("(1 = \"bool\")", true == bool)
+        assertSQL("(1 = \"boolOptional\")", true == boolOptional)
 
-        AssertSQL("(\"boolOptional\" IS NULL)", boolOptional == nil)
-        AssertSQL("(NULL IS \"boolOptional\")", nil == boolOptional)
+        assertSQL("(\"boolOptional\" IS NULL)", boolOptional == nil)
+        assertSQL("(NULL IS \"boolOptional\")", nil == boolOptional)
     }
 
     func test_isOperator_withEquatableExpressions_buildsBooleanExpression() {
-       AssertSQL("(\"bool\" IS \"bool\")", bool === bool)
-       AssertSQL("(\"bool\" IS \"boolOptional\")", bool === boolOptional)
-       AssertSQL("(\"boolOptional\" IS \"bool\")", boolOptional === bool)
-       AssertSQL("(\"boolOptional\" IS \"boolOptional\")", boolOptional === boolOptional)
-       AssertSQL("(\"bool\" IS 1)", bool === true)
-       AssertSQL("(\"boolOptional\" IS 1)", boolOptional === true)
-       AssertSQL("(1 IS \"bool\")", true === bool)
-       AssertSQL("(1 IS \"boolOptional\")", true === boolOptional)
-
-       AssertSQL("(\"boolOptional\" IS NULL)", boolOptional === nil)
-       AssertSQL("(NULL IS \"boolOptional\")", nil === boolOptional)
-    }
-    
+       assertSQL("(\"bool\" IS \"bool\")", bool === bool)
+       assertSQL("(\"bool\" IS \"boolOptional\")", bool === boolOptional)
+       assertSQL("(\"boolOptional\" IS \"bool\")", boolOptional === bool)
+       assertSQL("(\"boolOptional\" IS \"boolOptional\")", boolOptional === boolOptional)
+       assertSQL("(\"bool\" IS 1)", bool === true)
+       assertSQL("(\"boolOptional\" IS 1)", boolOptional === true)
+       assertSQL("(1 IS \"bool\")", true === bool)
+       assertSQL("(1 IS \"boolOptional\")", true === boolOptional)
+
+       assertSQL("(\"boolOptional\" IS NULL)", boolOptional === nil)
+       assertSQL("(NULL IS \"boolOptional\")", nil === boolOptional)
+    }
+
     func test_isNotOperator_withEquatableExpressions_buildsBooleanExpression() {
-        AssertSQL("(\"bool\" IS NOT \"bool\")", bool !== bool)
-        AssertSQL("(\"bool\" IS NOT \"boolOptional\")", bool !== boolOptional)
-        AssertSQL("(\"boolOptional\" IS NOT \"bool\")", boolOptional !== bool)
-        AssertSQL("(\"boolOptional\" IS NOT \"boolOptional\")", boolOptional !== boolOptional)
-        AssertSQL("(\"bool\" IS NOT 1)", bool !== true)
-        AssertSQL("(\"boolOptional\" IS NOT 1)", boolOptional !== true)
-        AssertSQL("(1 IS NOT \"bool\")", true !== bool)
-        AssertSQL("(1 IS NOT \"boolOptional\")", true !== boolOptional)
-
-        AssertSQL("(\"boolOptional\" IS NOT NULL)", boolOptional !== nil)
-        AssertSQL("(NULL IS NOT \"boolOptional\")", nil !== boolOptional)
+        assertSQL("(\"bool\" IS NOT \"bool\")", bool !== bool)
+        assertSQL("(\"bool\" IS NOT \"boolOptional\")", bool !== boolOptional)
+        assertSQL("(\"boolOptional\" IS NOT \"bool\")", boolOptional !== bool)
+        assertSQL("(\"boolOptional\" IS NOT \"boolOptional\")", boolOptional !== boolOptional)
+        assertSQL("(\"bool\" IS NOT 1)", bool !== true)
+        assertSQL("(\"boolOptional\" IS NOT 1)", boolOptional !== true)
+        assertSQL("(1 IS NOT \"bool\")", true !== bool)
+        assertSQL("(1 IS NOT \"boolOptional\")", true !== boolOptional)
+
+        assertSQL("(\"boolOptional\" IS NOT NULL)", boolOptional !== nil)
+        assertSQL("(NULL IS NOT \"boolOptional\")", nil !== boolOptional)
      }
 
     func test_inequalityOperator_withEquatableExpressions_buildsBooleanExpression() {
-        AssertSQL("(\"bool\" != \"bool\")", bool != bool)
-        AssertSQL("(\"bool\" != \"boolOptional\")", bool != boolOptional)
-        AssertSQL("(\"boolOptional\" != \"bool\")", boolOptional != bool)
-        AssertSQL("(\"boolOptional\" != \"boolOptional\")", boolOptional != boolOptional)
-        AssertSQL("(\"bool\" != 1)", bool != true)
-        AssertSQL("(\"boolOptional\" != 1)", boolOptional != true)
-        AssertSQL("(1 != \"bool\")", true != bool)
-        AssertSQL("(1 != \"boolOptional\")", true != boolOptional)
+        assertSQL("(\"bool\" != \"bool\")", bool != bool)
+        assertSQL("(\"bool\" != \"boolOptional\")", bool != boolOptional)
+        assertSQL("(\"boolOptional\" != \"bool\")", boolOptional != bool)
+        assertSQL("(\"boolOptional\" != \"boolOptional\")", boolOptional != boolOptional)
+        assertSQL("(\"bool\" != 1)", bool != true)
+        assertSQL("(\"boolOptional\" != 1)", boolOptional != true)
+        assertSQL("(1 != \"bool\")", true != bool)
+        assertSQL("(1 != \"boolOptional\")", true != boolOptional)
 
-        AssertSQL("(\"boolOptional\" IS NOT NULL)", boolOptional != nil)
-        AssertSQL("(NULL IS NOT \"boolOptional\")", nil != boolOptional)
+        assertSQL("(\"boolOptional\" IS NOT NULL)", boolOptional != nil)
+        assertSQL("(NULL IS NOT \"boolOptional\")", nil != boolOptional)
     }
 
     func test_greaterThanOperator_withComparableExpressions_buildsBooleanExpression() {
-        AssertSQL("(\"bool\" > \"bool\")", bool > bool)
-        AssertSQL("(\"bool\" > \"boolOptional\")", bool > boolOptional)
-        AssertSQL("(\"boolOptional\" > \"bool\")", boolOptional > bool)
-        AssertSQL("(\"boolOptional\" > \"boolOptional\")", boolOptional > boolOptional)
-        AssertSQL("(\"bool\" > 1)", bool > true)
-        AssertSQL("(\"boolOptional\" > 1)", boolOptional > true)
-        AssertSQL("(1 > \"bool\")", true > bool)
-        AssertSQL("(1 > \"boolOptional\")", true > boolOptional)
+        assertSQL("(\"bool\" > \"bool\")", bool > bool)
+        assertSQL("(\"bool\" > \"boolOptional\")", bool > boolOptional)
+        assertSQL("(\"boolOptional\" > \"bool\")", boolOptional > bool)
+        assertSQL("(\"boolOptional\" > \"boolOptional\")", boolOptional > boolOptional)
+        assertSQL("(\"bool\" > 1)", bool > true)
+        assertSQL("(\"boolOptional\" > 1)", boolOptional > true)
+        assertSQL("(1 > \"bool\")", true > bool)
+        assertSQL("(1 > \"boolOptional\")", true > boolOptional)
     }
 
     func test_greaterThanOrEqualToOperator_withComparableExpressions_buildsBooleanExpression() {
-        AssertSQL("(\"bool\" >= \"bool\")", bool >= bool)
-        AssertSQL("(\"bool\" >= \"boolOptional\")", bool >= boolOptional)
-        AssertSQL("(\"boolOptional\" >= \"bool\")", boolOptional >= bool)
-        AssertSQL("(\"boolOptional\" >= \"boolOptional\")", boolOptional >= boolOptional)
-        AssertSQL("(\"bool\" >= 1)", bool >= true)
-        AssertSQL("(\"boolOptional\" >= 1)", boolOptional >= true)
-        AssertSQL("(1 >= \"bool\")", true >= bool)
-        AssertSQL("(1 >= \"boolOptional\")", true >= boolOptional)
+        assertSQL("(\"bool\" >= \"bool\")", bool >= bool)
+        assertSQL("(\"bool\" >= \"boolOptional\")", bool >= boolOptional)
+        assertSQL("(\"boolOptional\" >= \"bool\")", boolOptional >= bool)
+        assertSQL("(\"boolOptional\" >= \"boolOptional\")", boolOptional >= boolOptional)
+        assertSQL("(\"bool\" >= 1)", bool >= true)
+        assertSQL("(\"boolOptional\" >= 1)", boolOptional >= true)
+        assertSQL("(1 >= \"bool\")", true >= bool)
+        assertSQL("(1 >= \"boolOptional\")", true >= boolOptional)
     }
 
     func test_lessThanOperator_withComparableExpressions_buildsBooleanExpression() {
-        AssertSQL("(\"bool\" < \"bool\")", bool < bool)
-        AssertSQL("(\"bool\" < \"boolOptional\")", bool < boolOptional)
-        AssertSQL("(\"boolOptional\" < \"bool\")", boolOptional < bool)
-        AssertSQL("(\"boolOptional\" < \"boolOptional\")", boolOptional < boolOptional)
-        AssertSQL("(\"bool\" < 1)", bool < true)
-        AssertSQL("(\"boolOptional\" < 1)", boolOptional < true)
-        AssertSQL("(1 < \"bool\")", true < bool)
-        AssertSQL("(1 < \"boolOptional\")", true < boolOptional)
+        assertSQL("(\"bool\" < \"bool\")", bool < bool)
+        assertSQL("(\"bool\" < \"boolOptional\")", bool < boolOptional)
+        assertSQL("(\"boolOptional\" < \"bool\")", boolOptional < bool)
+        assertSQL("(\"boolOptional\" < \"boolOptional\")", boolOptional < boolOptional)
+        assertSQL("(\"bool\" < 1)", bool < true)
+        assertSQL("(\"boolOptional\" < 1)", boolOptional < true)
+        assertSQL("(1 < \"bool\")", true < bool)
+        assertSQL("(1 < \"boolOptional\")", true < boolOptional)
     }
 
     func test_lessThanOrEqualToOperator_withComparableExpressions_buildsBooleanExpression() {
-        AssertSQL("(\"bool\" <= \"bool\")", bool <= bool)
-        AssertSQL("(\"bool\" <= \"boolOptional\")", bool <= boolOptional)
-        AssertSQL("(\"boolOptional\" <= \"bool\")", boolOptional <= bool)
-        AssertSQL("(\"boolOptional\" <= \"boolOptional\")", boolOptional <= boolOptional)
-        AssertSQL("(\"bool\" <= 1)", bool <= true)
-        AssertSQL("(\"boolOptional\" <= 1)", boolOptional <= true)
-        AssertSQL("(1 <= \"bool\")", true <= bool)
-        AssertSQL("(1 <= \"boolOptional\")", true <= boolOptional)
+        assertSQL("(\"bool\" <= \"bool\")", bool <= bool)
+        assertSQL("(\"bool\" <= \"boolOptional\")", bool <= boolOptional)
+        assertSQL("(\"boolOptional\" <= \"bool\")", boolOptional <= bool)
+        assertSQL("(\"boolOptional\" <= \"boolOptional\")", boolOptional <= boolOptional)
+        assertSQL("(\"bool\" <= 1)", bool <= true)
+        assertSQL("(\"boolOptional\" <= 1)", boolOptional <= true)
+        assertSQL("(1 <= \"bool\")", true <= bool)
+        assertSQL("(1 <= \"boolOptional\")", true <= boolOptional)
     }
 
     func test_patternMatchingOperator_withComparableCountableClosedRange_buildsBetweenBooleanExpression() {
-        AssertSQL("\"int\" BETWEEN 0 AND 5", 0...5 ~= int)
-        AssertSQL("\"intOptional\" BETWEEN 0 AND 5", 0...5 ~= intOptional)
+        assertSQL("\"int\" BETWEEN 0 AND 5", 0...5 ~= int)
+        assertSQL("\"intOptional\" BETWEEN 0 AND 5", 0...5 ~= intOptional)
     }
 
     func test_patternMatchingOperator_withComparableClosedRange_buildsBetweenBooleanExpression() {
-        AssertSQL("\"double\" BETWEEN 1.2 AND 4.5", 1.2...4.5 ~= double)
-        AssertSQL("\"doubleOptional\" BETWEEN 1.2 AND 4.5", 1.2...4.5 ~= doubleOptional)
+        assertSQL("\"double\" BETWEEN 1.2 AND 4.5", 1.2...4.5 ~= double)
+        assertSQL("\"doubleOptional\" BETWEEN 1.2 AND 4.5", 1.2...4.5 ~= doubleOptional)
     }
 
     func test_patternMatchingOperator_withComparableRange_buildsBooleanExpression() {
-        AssertSQL("\"double\" >= 1.2 AND \"double\" < 4.5", 1.2..<4.5 ~= double)
-        AssertSQL("\"doubleOptional\" >= 1.2 AND \"doubleOptional\" < 4.5", 1.2..<4.5 ~= doubleOptional)
+        assertSQL("\"double\" >= 1.2 AND \"double\" < 4.5", 1.2..<4.5 ~= double)
+        assertSQL("\"doubleOptional\" >= 1.2 AND \"doubleOptional\" < 4.5", 1.2..<4.5 ~= doubleOptional)
     }
 
     func test_patternMatchingOperator_withComparablePartialRangeThrough_buildsBooleanExpression() {
-        AssertSQL("\"double\" <= 4.5", ...4.5 ~= double)
-        AssertSQL("\"doubleOptional\" <= 4.5", ...4.5 ~= doubleOptional)
+        assertSQL("\"double\" <= 4.5", ...4.5 ~= double)
+        assertSQL("\"doubleOptional\" <= 4.5", ...4.5 ~= doubleOptional)
     }
 
     func test_patternMatchingOperator_withComparablePartialRangeUpTo_buildsBooleanExpression() {
-        AssertSQL("\"double\" < 4.5", ..<4.5 ~= double)
-        AssertSQL("\"doubleOptional\" < 4.5", ..<4.5 ~= doubleOptional)
+        assertSQL("\"double\" < 4.5", ..<4.5 ~= double)
+        assertSQL("\"doubleOptional\" < 4.5", ..<4.5 ~= doubleOptional)
     }
 
     func test_patternMatchingOperator_withComparablePartialRangeFrom_buildsBooleanExpression() {
-        AssertSQL("\"double\" >= 4.5", 4.5... ~= double)
-        AssertSQL("\"doubleOptional\" >= 4.5", 4.5... ~= doubleOptional)
+        assertSQL("\"double\" >= 4.5", 4.5... ~= double)
+        assertSQL("\"doubleOptional\" >= 4.5", 4.5... ~= doubleOptional)
     }
 
     func test_patternMatchingOperator_withComparableClosedRangeString_buildsBetweenBooleanExpression() {
-        AssertSQL("\"string\" BETWEEN 'a' AND 'b'", "a"..."b" ~= string)
-        AssertSQL("\"stringOptional\" BETWEEN 'a' AND 'b'", "a"..."b" ~= stringOptional)
+        assertSQL("\"string\" BETWEEN 'a' AND 'b'", "a"..."b" ~= string)
+        assertSQL("\"stringOptional\" BETWEEN 'a' AND 'b'", "a"..."b" ~= stringOptional)
     }
 
     func test_doubleAndOperator_withBooleanExpressions_buildsCompoundExpression() {
-        AssertSQL("(\"bool\" AND \"bool\")", bool && bool)
-        AssertSQL("(\"bool\" AND \"boolOptional\")", bool && boolOptional)
-        AssertSQL("(\"boolOptional\" AND \"bool\")", boolOptional && bool)
-        AssertSQL("(\"boolOptional\" AND \"boolOptional\")", boolOptional && boolOptional)
-        AssertSQL("(\"bool\" AND 1)", bool && true)
-        AssertSQL("(\"boolOptional\" AND 1)", boolOptional && true)
-        AssertSQL("(1 AND \"bool\")", true && bool)
-        AssertSQL("(1 AND \"boolOptional\")", true && boolOptional)
-    }
-    
+        assertSQL("(\"bool\" AND \"bool\")", bool && bool)
+        assertSQL("(\"bool\" AND \"boolOptional\")", bool && boolOptional)
+        assertSQL("(\"boolOptional\" AND \"bool\")", boolOptional && bool)
+        assertSQL("(\"boolOptional\" AND \"boolOptional\")", boolOptional && boolOptional)
+        assertSQL("(\"bool\" AND 1)", bool && true)
+        assertSQL("(\"boolOptional\" AND 1)", boolOptional && true)
+        assertSQL("(1 AND \"bool\")", true && bool)
+        assertSQL("(1 AND \"boolOptional\")", true && boolOptional)
+    }
+
     func test_andFunction_withBooleanExpressions_buildsCompoundExpression() {
-        AssertSQL("(\"bool\" AND \"bool\" AND \"bool\")", and([bool, bool, bool]))
-        AssertSQL("(\"bool\" AND \"bool\")", and([bool, bool]))
-        AssertSQL("(\"bool\")", and([bool]))
-        
-        AssertSQL("(\"bool\" AND \"bool\" AND \"bool\")", and(bool, bool, bool))
-        AssertSQL("(\"bool\" AND \"bool\")", and(bool, bool))
-        AssertSQL("(\"bool\")", and(bool))
+        assertSQL("(\"bool\" AND \"bool\" AND \"bool\")", and([bool, bool, bool]))
+        assertSQL("(\"bool\" AND \"bool\")", and([bool, bool]))
+        assertSQL("(\"bool\")", and([bool]))
+
+        assertSQL("(\"bool\" AND \"bool\" AND \"bool\")", and(bool, bool, bool))
+        assertSQL("(\"bool\" AND \"bool\")", and(bool, bool))
+        assertSQL("(\"bool\")", and(bool))
     }
 
     func test_doubleOrOperator_withBooleanExpressions_buildsCompoundExpression() {
-        AssertSQL("(\"bool\" OR \"bool\")", bool || bool)
-        AssertSQL("(\"bool\" OR \"boolOptional\")", bool || boolOptional)
-        AssertSQL("(\"boolOptional\" OR \"bool\")", boolOptional || bool)
-        AssertSQL("(\"boolOptional\" OR \"boolOptional\")", boolOptional || boolOptional)
-        AssertSQL("(\"bool\" OR 1)", bool || true)
-        AssertSQL("(\"boolOptional\" OR 1)", boolOptional || true)
-        AssertSQL("(1 OR \"bool\")", true || bool)
-        AssertSQL("(1 OR \"boolOptional\")", true || boolOptional)
-    }
-    
+        assertSQL("(\"bool\" OR \"bool\")", bool || bool)
+        assertSQL("(\"bool\" OR \"boolOptional\")", bool || boolOptional)
+        assertSQL("(\"boolOptional\" OR \"bool\")", boolOptional || bool)
+        assertSQL("(\"boolOptional\" OR \"boolOptional\")", boolOptional || boolOptional)
+        assertSQL("(\"bool\" OR 1)", bool || true)
+        assertSQL("(\"boolOptional\" OR 1)", boolOptional || true)
+        assertSQL("(1 OR \"bool\")", true || bool)
+        assertSQL("(1 OR \"boolOptional\")", true || boolOptional)
+    }
+
     func test_orFunction_withBooleanExpressions_buildsCompoundExpression() {
-        AssertSQL("(\"bool\" OR \"bool\" OR \"bool\")", or([bool, bool, bool]))
-        AssertSQL("(\"bool\" OR \"bool\")", or([bool, bool]))
-        AssertSQL("(\"bool\")", or([bool]))
-        
-        AssertSQL("(\"bool\" OR \"bool\" OR \"bool\")", or(bool, bool, bool))
-        AssertSQL("(\"bool\" OR \"bool\")", or(bool, bool))
-        AssertSQL("(\"bool\")", or(bool))
+        assertSQL("(\"bool\" OR \"bool\" OR \"bool\")", or([bool, bool, bool]))
+        assertSQL("(\"bool\" OR \"bool\")", or([bool, bool]))
+        assertSQL("(\"bool\")", or([bool]))
+
+        assertSQL("(\"bool\" OR \"bool\" OR \"bool\")", or(bool, bool, bool))
+        assertSQL("(\"bool\" OR \"bool\")", or(bool, bool))
+        assertSQL("(\"bool\")", or(bool))
     }
 
     func test_unaryNotOperator_withBooleanExpressions_buildsNotExpression() {
-        AssertSQL("NOT (\"bool\")", !bool)
-        AssertSQL("NOT (\"boolOptional\")", !boolOptional)
+        assertSQL("NOT (\"bool\")", !bool)
+        assertSQL("NOT (\"boolOptional\")", !boolOptional)
     }
 
     func test_precedencePreserved() {
         let n = Expression<Int>(value: 1)
-        AssertSQL("(((1 = 1) AND (1 = 1)) OR (1 = 1))", (n == n && n == n) || n == n)
-        AssertSQL("((1 = 1) AND ((1 = 1) OR (1 = 1)))", n == n && (n == n || n == n))
+        assertSQL("(((1 = 1) AND (1 = 1)) OR (1 = 1))", (n == n && n == n) || n == n)
+        assertSQL("((1 = 1) AND ((1 = 1) OR (1 = 1)))", n == n && (n == n || n == n))
     }
 
     func test_dateExpressionLessGreater() {
         let begin = Date(timeIntervalSince1970: 0)
-        AssertSQL("(\"date\" < '1970-01-01T00:00:00.000')", date < begin)
-        AssertSQL("(\"date\" > '1970-01-01T00:00:00.000')", date > begin)
-        AssertSQL("(\"date\" >= '1970-01-01T00:00:00.000')", date >= begin)
-        AssertSQL("(\"date\" <= '1970-01-01T00:00:00.000')", date <= begin)
+        assertSQL("(\"date\" < '1970-01-01T00:00:00.000')", date < begin)
+        assertSQL("(\"date\" > '1970-01-01T00:00:00.000')", date > begin)
+        assertSQL("(\"date\" >= '1970-01-01T00:00:00.000')", date >= begin)
+        assertSQL("(\"date\" <= '1970-01-01T00:00:00.000')", date <= begin)
     }
 
     func test_dateExpressionRange() {
         let begin = Date(timeIntervalSince1970: 0)
         let end = Date(timeIntervalSince1970: 5000)
-        AssertSQL(
+        assertSQL(
             "\"date\" >= '1970-01-01T00:00:00.000' AND \"date\" < '1970-01-01T01:23:20.000'",
             (begin..<end) ~= date
         )
@@ -381,7 +381,7 @@ class OperatorsTests : XCTestCase {
     func test_dateExpressionClosedRange() {
         let begin = Date(timeIntervalSince1970: 0)
         let end = Date(timeIntervalSince1970: 5000)
-        AssertSQL(
+        assertSQL(
             "\"date\" BETWEEN '1970-01-01T00:00:00.000' AND '1970-01-01T01:23:20.000'",
             (begin...end) ~= date
         )
diff --git a/Tests/SQLiteTests/QueryTests.swift b/Tests/SQLiteTests/QueryTests.swift
index 041b7d0c..3a58e2fc 100644
--- a/Tests/SQLiteTests/QueryTests.swift
+++ b/Tests/SQLiteTests/QueryTests.swift
@@ -10,7 +10,7 @@ import SQLite3
 #endif
 @testable import SQLite
 
-class QueryTests : XCTestCase {
+class QueryTests: XCTestCase {
 
     let users = Table("users")
     let id = Expression<Int64>("id")
@@ -28,65 +28,65 @@ class QueryTests : XCTestCase {
     let tag = Expression<String>("tag")
 
     func test_select_withExpression_compilesSelectClause() {
-        AssertSQL("SELECT \"email\" FROM \"users\"", users.select(email))
+        assertSQL("SELECT \"email\" FROM \"users\"", users.select(email))
     }
 
     func test_select_withStarExpression_compilesSelectClause() {
-        AssertSQL("SELECT * FROM \"users\"", users.select(*))
+        assertSQL("SELECT * FROM \"users\"", users.select(*))
     }
 
     func test_select_withNamespacedStarExpression_compilesSelectClause() {
-        AssertSQL("SELECT \"users\".* FROM \"users\"", users.select(users[*]))
+        assertSQL("SELECT \"users\".* FROM \"users\"", users.select(users[*]))
     }
 
     func test_select_withVariadicExpressions_compilesSelectClause() {
-        AssertSQL("SELECT \"email\", count(*) FROM \"users\"", users.select(email, count(*)))
+        assertSQL("SELECT \"email\", count(*) FROM \"users\"", users.select(email, count(*)))
     }
 
     func test_select_withExpressions_compilesSelectClause() {
-        AssertSQL("SELECT \"email\", count(*) FROM \"users\"", users.select([email, count(*)]))
+        assertSQL("SELECT \"email\", count(*) FROM \"users\"", users.select([email, count(*)]))
     }
 
     func test_selectDistinct_withExpression_compilesSelectClause() {
-        AssertSQL("SELECT DISTINCT \"age\" FROM \"users\"", users.select(distinct: age))
+        assertSQL("SELECT DISTINCT \"age\" FROM \"users\"", users.select(distinct: age))
     }
 
     func test_selectDistinct_withExpressions_compilesSelectClause() {
-        AssertSQL("SELECT DISTINCT \"age\", \"admin\" FROM \"users\"", users.select(distinct: [age, admin]))
+        assertSQL("SELECT DISTINCT \"age\", \"admin\" FROM \"users\"", users.select(distinct: [age, admin]))
     }
 
     func test_selectDistinct_withStar_compilesSelectClause() {
-        AssertSQL("SELECT DISTINCT * FROM \"users\"", users.select(distinct: *))
+        assertSQL("SELECT DISTINCT * FROM \"users\"", users.select(distinct: *))
     }
 
     func test_join_compilesJoinClause() {
-        AssertSQL(
+        assertSQL(
             "SELECT * FROM \"users\" INNER JOIN \"posts\" ON (\"posts\".\"user_id\" = \"users\".\"id\")",
             users.join(posts, on: posts[userId] == users[id])
         )
     }
 
     func test_join_withExplicitType_compilesJoinClauseWithType() {
-        AssertSQL(
+        assertSQL(
             "SELECT * FROM \"users\" LEFT OUTER JOIN \"posts\" ON (\"posts\".\"user_id\" = \"users\".\"id\")",
             users.join(.leftOuter, posts, on: posts[userId] == users[id])
         )
 
-        AssertSQL(
+        assertSQL(
             "SELECT * FROM \"users\" CROSS JOIN \"posts\" ON (\"posts\".\"user_id\" = \"users\".\"id\")",
             users.join(.cross, posts, on: posts[userId] == users[id])
         )
     }
 
     func test_join_withTableCondition_compilesJoinClauseWithTableCondition() {
-        AssertSQL(
+        assertSQL(
             "SELECT * FROM \"users\" INNER JOIN \"posts\" ON ((\"posts\".\"user_id\" = \"users\".\"id\") AND \"published\")",
             users.join(posts.filter(published), on: posts[userId] == users[id])
         )
     }
 
     func test_join_whenChained_compilesAggregateJoinClause() {
-        AssertSQL(
+        assertSQL(
             "SELECT * FROM \"users\" " +
             "INNER JOIN \"posts\" ON (\"posts\".\"user_id\" = \"users\".\"id\") " +
             "INNER JOIN \"categories\" ON (\"categories\".\"id\" = \"posts\".\"category_id\")",
@@ -95,79 +95,79 @@ class QueryTests : XCTestCase {
     }
 
     func test_filter_compilesWhereClause() {
-        AssertSQL("SELECT * FROM \"users\" WHERE (\"admin\" = 1)", users.filter(admin == true))
+        assertSQL("SELECT * FROM \"users\" WHERE (\"admin\" = 1)", users.filter(admin == true))
     }
 
     func test_filter_compilesWhereClause_false() {
-        AssertSQL("SELECT * FROM \"users\" WHERE (\"admin\" = 0)", users.filter(admin == false))
+        assertSQL("SELECT * FROM \"users\" WHERE (\"admin\" = 0)", users.filter(admin == false))
     }
 
     func test_filter_compilesWhereClause_optional() {
-        AssertSQL("SELECT * FROM \"users\" WHERE (\"admin\" = 1)", users.filter(optionalAdmin == true))
+        assertSQL("SELECT * FROM \"users\" WHERE (\"admin\" = 1)", users.filter(optionalAdmin == true))
     }
 
     func test_filter_compilesWhereClause_optional_false() {
-        AssertSQL("SELECT * FROM \"users\" WHERE (\"admin\" = 0)", users.filter(optionalAdmin == false))
+        assertSQL("SELECT * FROM \"users\" WHERE (\"admin\" = 0)", users.filter(optionalAdmin == false))
     }
 
     func test_where_compilesWhereClause() {
-        AssertSQL("SELECT * FROM \"users\" WHERE (\"admin\" = 1)", users.where(admin == true))
+        assertSQL("SELECT * FROM \"users\" WHERE (\"admin\" = 1)", users.where(admin == true))
     }
 
     func test_where_compilesWhereClause_false() {
-        AssertSQL("SELECT * FROM \"users\" WHERE (\"admin\" = 0)", users.where(admin == false))
+        assertSQL("SELECT * FROM \"users\" WHERE (\"admin\" = 0)", users.where(admin == false))
     }
 
     func test_where_compilesWhereClause_optional() {
-        AssertSQL("SELECT * FROM \"users\" WHERE (\"admin\" = 1)", users.where(optionalAdmin == true))
+        assertSQL("SELECT * FROM \"users\" WHERE (\"admin\" = 1)", users.where(optionalAdmin == true))
     }
 
     func test_where_compilesWhereClause_optional_false() {
-        AssertSQL("SELECT * FROM \"users\" WHERE (\"admin\" = 0)", users.where(optionalAdmin == false))
+        assertSQL("SELECT * FROM \"users\" WHERE (\"admin\" = 0)", users.where(optionalAdmin == false))
     }
 
     func test_filter_whenChained_compilesAggregateWhereClause() {
-        AssertSQL(
+        assertSQL(
             "SELECT * FROM \"users\" WHERE ((\"age\" >= 35) AND \"admin\")",
             users.filter(age >= 35).filter(admin)
         )
     }
 
     func test_group_withSingleExpressionName_compilesGroupClause() {
-        AssertSQL("SELECT * FROM \"users\" GROUP BY \"age\"",
+        assertSQL("SELECT * FROM \"users\" GROUP BY \"age\"",
             users.group(age))
     }
 
     func test_group_withVariadicExpressionNames_compilesGroupClause() {
-        AssertSQL("SELECT * FROM \"users\" GROUP BY \"age\", \"admin\"", users.group(age, admin))
+        assertSQL("SELECT * FROM \"users\" GROUP BY \"age\", \"admin\"", users.group(age, admin))
     }
 
     func test_group_withExpressionNameAndHavingBindings_compilesGroupClause() {
-        AssertSQL("SELECT * FROM \"users\" GROUP BY \"age\" HAVING \"admin\"", users.group(age, having: admin))
-        AssertSQL("SELECT * FROM \"users\" GROUP BY \"age\" HAVING (\"age\" >= 30)", users.group(age, having: age >= 30))
+        assertSQL("SELECT * FROM \"users\" GROUP BY \"age\" HAVING \"admin\"", users.group(age, having: admin))
+        assertSQL("SELECT * FROM \"users\" GROUP BY \"age\" HAVING (\"age\" >= 30)", users.group(age, having: age >= 30))
     }
 
     func test_group_withExpressionNamesAndHavingBindings_compilesGroupClause() {
-        AssertSQL(
+        assertSQL(
             "SELECT * FROM \"users\" GROUP BY \"age\", \"admin\" HAVING \"admin\"",
             users.group([age, admin], having: admin)
         )
-        AssertSQL(
+        assertSQL(
             "SELECT * FROM \"users\" GROUP BY \"age\", \"admin\" HAVING (\"age\" >= 30)",
             users.group([age, admin], having: age >= 30)
         )
     }
 
     func test_order_withSingleExpressionName_compilesOrderClause() {
-        AssertSQL("SELECT * FROM \"users\" ORDER BY \"age\"", users.order(age))
+        assertSQL("SELECT * FROM \"users\" ORDER BY \"age\"", users.order(age))
     }
 
     func test_order_withVariadicExpressionNames_compilesOrderClause() {
-        AssertSQL("SELECT * FROM \"users\" ORDER BY \"age\", \"email\"", users.order(age, email))
+        assertSQL("SELECT * FROM \"users\" ORDER BY \"age\", \"email\"", users.order(age, email))
     }
 
     func test_order_withArrayExpressionNames_compilesOrderClause() {
-        AssertSQL("SELECT * FROM \"users\" ORDER BY \"age\", \"email\"", users.order([age, email]))
+        assertSQL("SELECT * FROM \"users\" ORDER BY \"age\", \"email\"", users.order([age, email]))
     }
 
     func test_order_withExpressionAndSortDirection_compilesOrderClause() {
@@ -175,7 +175,7 @@ class QueryTests : XCTestCase {
     }
 
     func test_order_whenChained_resetsOrderClause() {
-        AssertSQL("SELECT * FROM \"users\" ORDER BY \"age\"", users.order(email).order(age))
+        assertSQL("SELECT * FROM \"users\" ORDER BY \"age\"", users.order(email).order(age))
     }
 
     func test_reverse_withoutOrder_ordersByRowIdDescending() {
@@ -187,25 +187,25 @@ class QueryTests : XCTestCase {
     }
 
     func test_limit_compilesLimitClause() {
-        AssertSQL("SELECT * FROM \"users\" LIMIT 5", users.limit(5))
+        assertSQL("SELECT * FROM \"users\" LIMIT 5", users.limit(5))
     }
 
     func test_limit_withOffset_compilesOffsetClause() {
-        AssertSQL("SELECT * FROM \"users\" LIMIT 5 OFFSET 5", users.limit(5, offset: 5))
+        assertSQL("SELECT * FROM \"users\" LIMIT 5 OFFSET 5", users.limit(5, offset: 5))
     }
 
     func test_limit_whenChained_overridesLimit() {
         let query = users.limit(5)
 
-        AssertSQL("SELECT * FROM \"users\" LIMIT 10", query.limit(10))
-        AssertSQL("SELECT * FROM \"users\"", query.limit(nil))
+        assertSQL("SELECT * FROM \"users\" LIMIT 10", query.limit(10))
+        assertSQL("SELECT * FROM \"users\"", query.limit(nil))
     }
 
     func test_limit_whenChained_withOffset_overridesOffset() {
         let query = users.limit(5, offset: 5)
 
-        AssertSQL("SELECT * FROM \"users\" LIMIT 10 OFFSET 20", query.limit(10, offset: 20))
-        AssertSQL("SELECT * FROM \"users\"", query.limit(nil))
+        assertSQL("SELECT * FROM \"users\" LIMIT 10 OFFSET 20", query.limit(10, offset: 20))
+        assertSQL("SELECT * FROM \"users\"", query.limit(nil))
     }
 
     func test_alias_aliasesTable() {
@@ -213,7 +213,7 @@ class QueryTests : XCTestCase {
 
         let managers = users.alias("managers")
 
-        AssertSQL(
+        assertSQL(
             "SELECT * FROM \"users\" " +
             "INNER JOIN \"users\" AS \"managers\" ON (\"managers\".\"id\" = \"users\".\"manager_id\")",
             users.join(managers, on: managers[id] == users[managerId])
@@ -221,78 +221,99 @@ class QueryTests : XCTestCase {
     }
 
     func test_insert_compilesInsertExpression() {
-        AssertSQL(
+        assertSQL(
             "INSERT INTO \"users\" (\"email\", \"age\") VALUES ('alice@example.com', 30)",
             users.insert(email <- "alice@example.com", age <- 30)
         )
     }
 
     func test_insert_withOnConflict_compilesInsertOrOnConflictExpression() {
-        AssertSQL(
+        assertSQL(
             "INSERT OR REPLACE INTO \"users\" (\"email\", \"age\") VALUES ('alice@example.com', 30)",
             users.insert(or: .replace, email <- "alice@example.com", age <- 30)
         )
     }
 
     func test_insert_compilesInsertExpressionWithDefaultValues() {
-        AssertSQL("INSERT INTO \"users\" DEFAULT VALUES", users.insert())
+        assertSQL("INSERT INTO \"users\" DEFAULT VALUES", users.insert())
     }
 
     func test_insert_withQuery_compilesInsertExpressionWithSelectStatement() {
         let emails = Table("emails")
 
-        AssertSQL(
+        assertSQL(
             "INSERT INTO \"emails\" SELECT \"email\" FROM \"users\" WHERE \"admin\"",
             emails.insert(users.select(email).filter(admin))
         )
     }
 
     func test_insert_many_compilesInsertManyExpression() {
-        AssertSQL(
-            "INSERT INTO \"users\" (\"email\", \"age\") VALUES ('alice@example.com', 30), ('geoff@example.com', 32), ('alex@example.com', 83)",
-            users.insertMany([[email <- "alice@example.com", age <- 30], [email <- "geoff@example.com", age <- 32], [email <- "alex@example.com", age <- 83]])
+        assertSQL(
+            """
+            INSERT INTO \"users\" (\"email\", \"age\") VALUES ('alice@example.com', 30), ('geoff@example.com', 32),
+             ('alex@example.com', 83)
+            """.replacingOccurrences(of: "\n", with: ""),
+            users.insertMany([[email <- "alice@example.com", age <- 30],
+                              [email <- "geoff@example.com", age <- 32], [email <- "alex@example.com", age <- 83]])
         )
     }
     func test_insert_many_compilesInsertManyNoneExpression() {
-        AssertSQL(
+        assertSQL(
             "INSERT INTO \"users\" DEFAULT VALUES",
             users.insertMany([])
         )
     }
 
     func test_insert_many_withOnConflict_compilesInsertManyOrOnConflictExpression() {
-        AssertSQL(
-            "INSERT OR REPLACE INTO \"users\" (\"email\", \"age\") VALUES ('alice@example.com', 30), ('geoff@example.com', 32), ('alex@example.com', 83)",
-            users.insertMany(or: .replace, [[email <- "alice@example.com", age <- 30], [email <- "geoff@example.com", age <- 32], [email <- "alex@example.com", age <- 83]])
+        assertSQL(
+            """
+            INSERT OR REPLACE INTO \"users\" (\"email\", \"age\") VALUES ('alice@example.com', 30),
+             ('geoff@example.com', 32), ('alex@example.com', 83)
+            """.replacingOccurrences(of: "\n", with: ""),
+            users.insertMany(or: .replace, [[email <- "alice@example.com", age <- 30],
+                                            [email <- "geoff@example.com", age <- 32],
+                                            [email <- "alex@example.com", age <- 83]])
         )
     }
 
     func test_insert_encodable() throws {
         let emails = Table("emails")
-        let value = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4, date: Date(timeIntervalSince1970: 0), optional: nil, sub: nil)
+        let value = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4,
+                                date: Date(timeIntervalSince1970: 0), optional: nil, sub: nil)
         let insert = try emails.insert(value)
-        AssertSQL(
-            "INSERT INTO \"emails\" (\"int\", \"string\", \"bool\", \"float\", \"double\", \"date\") VALUES (1, '2', 1, 3.0, 4.0, '1970-01-01T00:00:00.000')",
+        assertSQL(
+            """
+            INSERT INTO \"emails\" (\"int\", \"string\", \"bool\", \"float\", \"double\", \"date\")
+             VALUES (1, '2', 1, 3.0, 4.0, '1970-01-01T00:00:00.000')
+            """.replacingOccurrences(of: "\n", with: ""),
             insert
         )
     }
 
     func test_insert_encodable_with_nested_encodable() throws {
         let emails = Table("emails")
-        let value1 = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4, date: Date(timeIntervalSince1970: 0), optional: nil, sub: nil)
-        let value = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4, date: Date(timeIntervalSince1970: 0), optional: "optional", sub: value1)
+        let value1 = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4,
+                                 date: Date(timeIntervalSince1970: 0), optional: nil, sub: nil)
+        let value = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4,
+                                date: Date(timeIntervalSince1970: 0), optional: "optional", sub: value1)
         let insert = try emails.insert(value)
         let encodedJSON = try JSONEncoder().encode(value1)
         let encodedJSONString = String(data: encodedJSON, encoding: .utf8)!
-        AssertSQL(
-            "INSERT INTO \"emails\" (\"int\", \"string\", \"bool\", \"float\", \"double\", \"date\", \"optional\", \"sub\") VALUES (1, '2', 1, 3.0, 4.0, '1970-01-01T00:00:00.000', 'optional', '\(encodedJSONString)')",
+        assertSQL(
+            """
+            INSERT INTO \"emails\" (\"int\", \"string\", \"bool\", \"float\", \"double\", \"date\", \"optional\",
+             \"sub\") VALUES (1, '2', 1, 3.0, 4.0, '1970-01-01T00:00:00.000', 'optional', '\(encodedJSONString)')
+            """.replacingOccurrences(of: "\n", with: ""),
             insert
         )
     }
 
     func test_upsert_withOnConflict_compilesInsertOrOnConflictExpression() {
-        AssertSQL(
-            "INSERT INTO \"users\" (\"email\", \"age\") VALUES ('alice@example.com', 30) ON CONFLICT (\"email\") DO UPDATE SET \"age\" = \"excluded\".\"age\"",
+        assertSQL(
+            """
+            INSERT INTO \"users\" (\"email\", \"age\") VALUES ('alice@example.com', 30) ON CONFLICT (\"email\")
+             DO UPDATE SET \"age\" = \"excluded\".\"age\"
+            """.replacingOccurrences(of: "\n", with: ""),
             users.upsert(email <- "alice@example.com", age <- 30, onConflictOf: email)
         )
     }
@@ -300,35 +321,48 @@ class QueryTests : XCTestCase {
     func test_upsert_encodable() throws {
         let emails = Table("emails")
         let string = Expression<String>("string")
-        let value = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4, date: Date(timeIntervalSince1970: 0), optional: nil, sub: nil)
+        let value = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4,
+                                date: Date(timeIntervalSince1970: 0), optional: nil, sub: nil)
         let insert = try emails.upsert(value, onConflictOf: string)
-        AssertSQL(
-            "INSERT INTO \"emails\" (\"int\", \"string\", \"bool\", \"float\", \"double\", \"date\") VALUES (1, '2', 1, 3.0, 4.0, '1970-01-01T00:00:00.000') ON CONFLICT (\"string\") DO UPDATE SET \"int\" = \"excluded\".\"int\", \"bool\" = \"excluded\".\"bool\", \"float\" = \"excluded\".\"float\", \"double\" = \"excluded\".\"double\", \"date\" = \"excluded\".\"date\"",
+        assertSQL(
+            """
+            INSERT INTO \"emails\" (\"int\", \"string\", \"bool\", \"float\", \"double\", \"date\")
+             VALUES (1, '2', 1, 3.0, 4.0, '1970-01-01T00:00:00.000') ON CONFLICT (\"string\")
+             DO UPDATE SET \"int\" = \"excluded\".\"int\", \"bool\" = \"excluded\".\"bool\",
+             \"float\" = \"excluded\".\"float\", \"double\" = \"excluded\".\"double\", \"date\" = \"excluded\".\"date\"
+            """.replacingOccurrences(of: "\n", with: ""),
             insert
         )
     }
 
     func test_insert_many_encodable() throws {
         let emails = Table("emails")
-        let value1 = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4, date: Date(timeIntervalSince1970: 0), optional: nil, sub: nil)
-        let value2 = TestCodable(int: 2, string: "3", bool: true, float: 3, double: 5, date: Date(timeIntervalSince1970: 0), optional: nil, sub: nil)
-        let value3 = TestCodable(int: 3, string: "4", bool: true, float: 3, double: 6, date: Date(timeIntervalSince1970: 0), optional: nil, sub: nil)
+        let value1 = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4,
+                                 date: Date(timeIntervalSince1970: 0), optional: nil, sub: nil)
+        let value2 = TestCodable(int: 2, string: "3", bool: true, float: 3, double: 5,
+                                 date: Date(timeIntervalSince1970: 0), optional: nil, sub: nil)
+        let value3 = TestCodable(int: 3, string: "4", bool: true, float: 3, double: 6,
+                                 date: Date(timeIntervalSince1970: 0), optional: nil, sub: nil)
         let insert = try emails.insertMany([value1, value2, value3])
-        AssertSQL(
-            "INSERT INTO \"emails\" (\"int\", \"string\", \"bool\", \"float\", \"double\", \"date\") VALUES (1, '2', 1, 3.0, 4.0, '1970-01-01T00:00:00.000'), (2, '3', 1, 3.0, 5.0, '1970-01-01T00:00:00.000'), (3, '4', 1, 3.0, 6.0, '1970-01-01T00:00:00.000')",
+        assertSQL(
+            """
+            INSERT INTO \"emails\" (\"int\", \"string\", \"bool\", \"float\", \"double\", \"date\")
+             VALUES (1, '2', 1, 3.0, 4.0, '1970-01-01T00:00:00.000'), (2, '3', 1, 3.0, 5.0, '1970-01-01T00:00:00.000'),
+             (3, '4', 1, 3.0, 6.0, '1970-01-01T00:00:00.000')
+            """.replacingOccurrences(of: "\n", with: ""),
             insert
         )
     }
 
     func test_update_compilesUpdateExpression() {
-        AssertSQL(
+        assertSQL(
             "UPDATE \"users\" SET \"age\" = 30, \"admin\" = 1 WHERE (\"id\" = 1)",
             users.filter(id == 1).update(age <- 30, admin <- true)
         )
     }
 
     func test_update_compilesUpdateLimitOrderExpression() {
-        AssertSQL(
+        assertSQL(
             "UPDATE \"users\" SET \"age\" = 30 ORDER BY \"id\" LIMIT 1",
             users.order(id).limit(1).update(age <- 30)
         )
@@ -336,95 +370,104 @@ class QueryTests : XCTestCase {
 
     func test_update_encodable() throws {
         let emails = Table("emails")
-        let value = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4, date: Date(timeIntervalSince1970: 0), optional: nil, sub: nil)
+        let value = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4,
+                                date: Date(timeIntervalSince1970: 0), optional: nil, sub: nil)
         let update = try emails.update(value)
-        AssertSQL(
-            "UPDATE \"emails\" SET \"int\" = 1, \"string\" = '2', \"bool\" = 1, \"float\" = 3.0, \"double\" = 4.0, \"date\" = '1970-01-01T00:00:00.000'",
+        assertSQL(
+            """
+            UPDATE \"emails\" SET \"int\" = 1, \"string\" = '2', \"bool\" = 1, \"float\" = 3.0, \"double\" = 4.0,
+             \"date\" = '1970-01-01T00:00:00.000'
+            """.replacingOccurrences(of: "\n", with: ""),
             update
         )
     }
 
     func test_update_encodable_with_nested_encodable() throws {
         let emails = Table("emails")
-        let value1 = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4, date: Date(timeIntervalSince1970: 0), optional: nil, sub: nil)
-        let value = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4, date: Date(timeIntervalSince1970: 0), optional: nil, sub: value1)
+        let value1 = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4,
+                                 date: Date(timeIntervalSince1970: 0), optional: nil, sub: nil)
+        let value = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4,
+                                date: Date(timeIntervalSince1970: 0), optional: nil, sub: value1)
         let update = try emails.update(value)
         let encodedJSON = try JSONEncoder().encode(value1)
         let encodedJSONString = String(data: encodedJSON, encoding: .utf8)!
-        AssertSQL(
-            "UPDATE \"emails\" SET \"int\" = 1, \"string\" = '2', \"bool\" = 1, \"float\" = 3.0, \"double\" = 4.0, \"date\" = '1970-01-01T00:00:00.000', \"sub\" = '\(encodedJSONString)'",
+        assertSQL(
+            """
+            UPDATE \"emails\" SET \"int\" = 1, \"string\" = '2', \"bool\" = 1, \"float\" = 3.0, \"double\" = 4.0,
+             \"date\" = '1970-01-01T00:00:00.000', \"sub\" = '\(encodedJSONString)'
+            """.replacingOccurrences(of: "\n", with: ""),
             update
         )
     }
 
     func test_delete_compilesDeleteExpression() {
-        AssertSQL(
+        assertSQL(
             "DELETE FROM \"users\" WHERE (\"id\" = 1)",
             users.filter(id == 1).delete()
         )
     }
 
     func test_delete_compilesDeleteLimitOrderExpression() {
-        AssertSQL(
+        assertSQL(
             "DELETE FROM \"users\" ORDER BY \"id\" LIMIT 1",
             users.order(id).limit(1).delete()
         )
     }
 
     func test_delete_compilesExistsExpression() {
-        AssertSQL(
+        assertSQL(
             "SELECT EXISTS (SELECT * FROM \"users\")",
             users.exists
         )
     }
 
     func test_count_returnsCountExpression() {
-        AssertSQL("SELECT count(*) FROM \"users\"", users.count)
+        assertSQL("SELECT count(*) FROM \"users\"", users.count)
     }
 
     func test_scalar_returnsScalarExpression() {
-        AssertSQL("SELECT \"int\" FROM \"table\"", table.select(int) as ScalarQuery<Int>)
-        AssertSQL("SELECT \"intOptional\" FROM \"table\"", table.select(intOptional) as ScalarQuery<Int?>)
-        AssertSQL("SELECT DISTINCT \"int\" FROM \"table\"", table.select(distinct: int) as ScalarQuery<Int>)
-        AssertSQL("SELECT DISTINCT \"intOptional\" FROM \"table\"", table.select(distinct: intOptional) as ScalarQuery<Int?>)
+        assertSQL("SELECT \"int\" FROM \"table\"", table.select(int) as ScalarQuery<Int>)
+        assertSQL("SELECT \"intOptional\" FROM \"table\"", table.select(intOptional) as ScalarQuery<Int?>)
+        assertSQL("SELECT DISTINCT \"int\" FROM \"table\"", table.select(distinct: int) as ScalarQuery<Int>)
+        assertSQL("SELECT DISTINCT \"intOptional\" FROM \"table\"", table.select(distinct: intOptional) as ScalarQuery<Int?>)
     }
 
     func test_subscript_withExpression_returnsNamespacedExpression() {
         let query = Table("query")
 
-        AssertSQL("\"query\".\"blob\"", query[data])
-        AssertSQL("\"query\".\"blobOptional\"", query[dataOptional])
+        assertSQL("\"query\".\"blob\"", query[data])
+        assertSQL("\"query\".\"blobOptional\"", query[dataOptional])
 
-        AssertSQL("\"query\".\"bool\"", query[bool])
-        AssertSQL("\"query\".\"boolOptional\"", query[boolOptional])
+        assertSQL("\"query\".\"bool\"", query[bool])
+        assertSQL("\"query\".\"boolOptional\"", query[boolOptional])
 
-        AssertSQL("\"query\".\"date\"", query[date])
-        AssertSQL("\"query\".\"dateOptional\"", query[dateOptional])
+        assertSQL("\"query\".\"date\"", query[date])
+        assertSQL("\"query\".\"dateOptional\"", query[dateOptional])
 
-        AssertSQL("\"query\".\"double\"", query[double])
-        AssertSQL("\"query\".\"doubleOptional\"", query[doubleOptional])
+        assertSQL("\"query\".\"double\"", query[double])
+        assertSQL("\"query\".\"doubleOptional\"", query[doubleOptional])
 
-        AssertSQL("\"query\".\"int\"", query[int])
-        AssertSQL("\"query\".\"intOptional\"", query[intOptional])
+        assertSQL("\"query\".\"int\"", query[int])
+        assertSQL("\"query\".\"intOptional\"", query[intOptional])
 
-        AssertSQL("\"query\".\"int64\"", query[int64])
-        AssertSQL("\"query\".\"int64Optional\"", query[int64Optional])
+        assertSQL("\"query\".\"int64\"", query[int64])
+        assertSQL("\"query\".\"int64Optional\"", query[int64Optional])
 
-        AssertSQL("\"query\".\"string\"", query[string])
-        AssertSQL("\"query\".\"stringOptional\"", query[stringOptional])
+        assertSQL("\"query\".\"string\"", query[string])
+        assertSQL("\"query\".\"stringOptional\"", query[stringOptional])
 
-        AssertSQL("\"query\".*", query[*])
+        assertSQL("\"query\".*", query[*])
     }
 
     func test_tableNamespacedByDatabase() {
         let table = Table("table", database: "attached")
 
-        AssertSQL("SELECT * FROM \"attached\".\"table\"", table)
+        assertSQL("SELECT * FROM \"attached\".\"table\"", table)
     }
 
 }
 
-class QueryIntegrationTests : SQLiteTestCase {
+class QueryIntegrationTests: SQLiteTestCase {
 
     let id = Expression<Int64>("id")
     let email = Expression<String>("email")
@@ -433,7 +476,7 @@ class QueryIntegrationTests : SQLiteTestCase {
     override func setUp() {
         super.setUp()
 
-        CreateUsersTable()
+        createUsersTable()
     }
 
     // MARK: -
@@ -452,7 +495,7 @@ class QueryIntegrationTests : SQLiteTestCase {
 
     func test_prepareRowIterator() {
         let names = ["a", "b", "c"]
-        try! InsertUsers(names)
+        try! insertUsers(names)
 
         let emailColumn = Expression<String>("email")
         let emails = try! db.prepareRowIterator(users).map { $0[emailColumn] }
@@ -462,7 +505,7 @@ class QueryIntegrationTests : SQLiteTestCase {
 
     func test_ambiguousMap() {
         let names = ["a", "b", "c"]
-        try! InsertUsers(names)
+        try! insertUsers(names)
 
         let emails = try! db.prepare("select email from users", []).map { $0[0] as! String  }
 
@@ -494,8 +537,10 @@ class QueryIntegrationTests : SQLiteTestCase {
             builder.column(Expression<Data>("sub"))
         })
 
-        let value1 = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4, date: Date(timeIntervalSince1970: 0), optional: nil, sub: nil)
-        let value = TestCodable(int: 5, string: "6", bool: true, float: 7, double: 8, date: Date(timeIntervalSince1970: 5000), optional: "optional", sub: value1)
+        let value1 = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4,
+                                 date: Date(timeIntervalSince1970: 0), optional: nil, sub: nil)
+        let value = TestCodable(int: 5, string: "6", bool: true, float: 7, double: 8,
+                                date: Date(timeIntervalSince1970: 5000), optional: "optional", sub: value1)
 
         try db.run(table.insert(value))
 
@@ -523,7 +568,7 @@ class QueryIntegrationTests : SQLiteTestCase {
         XCTAssertEqual(0, try! db.scalar(users.count))
         XCTAssertEqual(false, try! db.scalar(users.exists))
 
-        try! InsertUsers("alice")
+        try! insertUsers("alice")
         XCTAssertEqual(1, try! db.scalar(users.select(id.average)))
     }
 
@@ -590,7 +635,7 @@ class QueryIntegrationTests : SQLiteTestCase {
 
     func test_no_such_column() throws {
         let doesNotExist = Expression<String>("doesNotExist")
-        try! InsertUser("alice")
+        try! insertUser("alice")
         let row = try! db.pluck(users.filter(email == "alice@example.com"))!
 
         XCTAssertThrowsError(try row.get(doesNotExist)) { error in
diff --git a/Tests/SQLiteTests/RTreeTests.swift b/Tests/SQLiteTests/RTreeTests.swift
index 7147533e..5525da26 100644
--- a/Tests/SQLiteTests/RTreeTests.swift
+++ b/Tests/SQLiteTests/RTreeTests.swift
@@ -1,7 +1,7 @@
 import XCTest
 import SQLite
 
-class RTreeTests : XCTestCase {
+class RTreeTests: XCTestCase {
 
     func test_create_onVirtualTable_withRTree_createVirtualTableExpression() {
         XCTAssertEqual(
@@ -14,4 +14,4 @@ class RTreeTests : XCTestCase {
         )
     }
 
-}
\ No newline at end of file
+}
diff --git a/Tests/SQLiteTests/RowTests.swift b/Tests/SQLiteTests/RowTests.swift
index 17873e71..36721f80 100644
--- a/Tests/SQLiteTests/RowTests.swift
+++ b/Tests/SQLiteTests/RowTests.swift
@@ -1,7 +1,7 @@
 import XCTest
 @testable import SQLite
 
-class RowTests : XCTestCase {
+class RowTests: XCTestCase {
 
     public func test_get_value() {
         let row = Row(["\"foo\"": 0], ["value"])
diff --git a/Tests/SQLiteTests/SchemaTests.swift b/Tests/SQLiteTests/SchemaTests.swift
index 30646b98..495a5e51 100644
--- a/Tests/SQLiteTests/SchemaTests.swift
+++ b/Tests/SQLiteTests/SchemaTests.swift
@@ -1,7 +1,7 @@
 import XCTest
 import SQLite
 
-class SchemaTests : XCTestCase {
+class SchemaTests: XCTestCase {
 
     func test_drop_compilesDropTableExpression() {
         XCTAssertEqual("DROP TABLE \"table\"", table.drop())
@@ -330,7 +330,10 @@ class SchemaTests : XCTestCase {
             table.create { t in t.column(int64, unique: true, check: int64 > 0, references: table, int64) }
         )
         XCTAssertEqual(
-            "CREATE TABLE \"table\" (\"int64\" INTEGER NOT NULL UNIQUE CHECK (\"int64Optional\" > 0) REFERENCES \"table\" (\"int64\"))",
+            """
+            CREATE TABLE \"table\" (\"int64\" INTEGER NOT NULL UNIQUE CHECK (\"int64Optional\" > 0) REFERENCES
+             \"table\" (\"int64\"))
+            """.replacingOccurrences(of: "\n", with: ""),
             table.create { t in t.column(int64, unique: true, check: int64Optional > 0, references: table, int64) }
         )
 
@@ -487,48 +490,95 @@ class SchemaTests : XCTestCase {
             table.create { t in t.column(stringOptional, unique: true, check: string != "", defaultValue: string, collate: .rtrim) }
         )
         XCTAssertEqual(
-            "CREATE TABLE \"table\" (\"stringOptional\" TEXT UNIQUE CHECK (\"string\" != '') DEFAULT (\"stringOptional\") COLLATE RTRIM)",
-            table.create { t in t.column(stringOptional, unique: true, check: string != "", defaultValue: stringOptional, collate: .rtrim) }
+            """
+            CREATE TABLE \"table\" (\"stringOptional\" TEXT UNIQUE CHECK (\"string\" != '')
+             DEFAULT (\"stringOptional\") COLLATE RTRIM)
+            """.replacingOccurrences(of: "\n", with: ""),
+            table.create { t in t.column(stringOptional,
+                                         unique: true,
+                                         check: string != "",
+                                         defaultValue: stringOptional,
+                                         collate: .rtrim) }
         )
         XCTAssertEqual(
-            "CREATE TABLE \"table\" (\"stringOptional\" TEXT UNIQUE CHECK (\"stringOptional\" != '') DEFAULT (\"string\") COLLATE RTRIM)",
-            table.create { t in t.column(stringOptional, unique: true, check: stringOptional != "", defaultValue: string, collate: .rtrim) }
+            """
+            CREATE TABLE \"table\" (\"stringOptional\" TEXT UNIQUE CHECK (\"stringOptional\" != '')
+             DEFAULT (\"string\") COLLATE RTRIM)
+            """.replacingOccurrences(of: "\n", with: ""),
+            table.create { t in t.column(stringOptional, unique: true, check: stringOptional != "",
+                                         defaultValue: string, collate: .rtrim) }
         )
         XCTAssertEqual(
-            "CREATE TABLE \"table\" (\"stringOptional\" TEXT UNIQUE CHECK (\"stringOptional\" != '') DEFAULT (\"stringOptional\") COLLATE RTRIM)",
-            table.create { t in t.column(stringOptional, unique: true, check: stringOptional != "", defaultValue: stringOptional, collate: .rtrim) }
+            """
+            CREATE TABLE \"table\" (\"stringOptional\" TEXT UNIQUE CHECK (\"stringOptional\" != '')
+             DEFAULT (\"stringOptional\") COLLATE RTRIM)
+            """.replacingOccurrences(of: "\n", with: ""),
+            table.create { t in t.column(stringOptional, unique: true, check: stringOptional != "",
+                                         defaultValue: stringOptional, collate: .rtrim) }
         )
         XCTAssertEqual(
-            "CREATE TABLE \"table\" (\"stringOptional\" TEXT UNIQUE CHECK (\"string\" != '') DEFAULT ('string') COLLATE RTRIM)",
-            table.create { t in t.column(stringOptional, unique: true, check: string != "", defaultValue: "string", collate: .rtrim) }
+            """
+            CREATE TABLE \"table\" (\"stringOptional\" TEXT UNIQUE CHECK (\"string\" != '')
+             DEFAULT ('string') COLLATE RTRIM)
+            """.replacingOccurrences(of: "\n", with: ""),
+            table.create { t in t.column(stringOptional, unique: true, check: string != "",
+                                         defaultValue: "string", collate: .rtrim) }
         )
         XCTAssertEqual(
-            "CREATE TABLE \"table\" (\"stringOptional\" TEXT UNIQUE CHECK (\"stringOptional\" != '') DEFAULT ('string') COLLATE RTRIM)",
-            table.create { t in t.column(stringOptional, unique: true, check: stringOptional != "", defaultValue: "string", collate: .rtrim) }
+            """
+            CREATE TABLE \"table\" (\"stringOptional\" TEXT UNIQUE CHECK (\"stringOptional\" != '')
+             DEFAULT ('string') COLLATE RTRIM)
+            """.replacingOccurrences(of: "\n", with: ""),
+            table.create { t in t.column(stringOptional, unique: true, check: stringOptional != "",
+                                         defaultValue: "string", collate: .rtrim) }
         )
         XCTAssertEqual(
-            "CREATE TABLE \"table\" (\"stringOptional\" TEXT CHECK (\"string\" != '') DEFAULT (\"string\") COLLATE RTRIM)",
-            table.create { t in t.column(stringOptional, check: string != "", defaultValue: string, collate: .rtrim) }
+            """
+            CREATE TABLE \"table\" (\"stringOptional\" TEXT CHECK (\"string\" != '')
+             DEFAULT (\"string\") COLLATE RTRIM)
+            """.replacingOccurrences(of: "\n", with: ""),
+            table.create { t in t.column(stringOptional, check: string != "",
+                                         defaultValue: string, collate: .rtrim) }
         )
         XCTAssertEqual(
-            "CREATE TABLE \"table\" (\"stringOptional\" TEXT CHECK (\"stringOptional\" != '') DEFAULT (\"string\") COLLATE RTRIM)",
-            table.create { t in t.column(stringOptional, check: stringOptional != "", defaultValue: string, collate: .rtrim) }
+            """
+            CREATE TABLE \"table\" (\"stringOptional\" TEXT CHECK (\"stringOptional\" != '')
+             DEFAULT (\"string\") COLLATE RTRIM)
+            """.replacingOccurrences(of: "\n", with: ""),
+            table.create { t in t.column(stringOptional, check: stringOptional != "",
+                                         defaultValue: string, collate: .rtrim) }
         )
         XCTAssertEqual(
-            "CREATE TABLE \"table\" (\"stringOptional\" TEXT CHECK (\"string\" != '') DEFAULT (\"stringOptional\") COLLATE RTRIM)",
-            table.create { t in t.column(stringOptional, check: string != "", defaultValue: stringOptional, collate: .rtrim) }
+            """
+            CREATE TABLE \"table\" (\"stringOptional\" TEXT CHECK (\"string\" != '')
+             DEFAULT (\"stringOptional\") COLLATE RTRIM)
+            """.replacingOccurrences(of: "\n", with: ""),
+            table.create { t in t.column(stringOptional, check: string != "",
+                                         defaultValue: stringOptional, collate: .rtrim) }
         )
         XCTAssertEqual(
-            "CREATE TABLE \"table\" (\"stringOptional\" TEXT CHECK (\"stringOptional\" != '') DEFAULT (\"stringOptional\") COLLATE RTRIM)",
-            table.create { t in t.column(stringOptional, check: stringOptional != "", defaultValue: stringOptional, collate: .rtrim) }
+            """
+            CREATE TABLE \"table\" (\"stringOptional\" TEXT CHECK (\"stringOptional\" != '')
+             DEFAULT (\"stringOptional\") COLLATE RTRIM)
+            """.replacingOccurrences(of: "\n", with: ""),
+            table.create { t in t.column(stringOptional, check: stringOptional != "",
+                                         defaultValue: stringOptional, collate: .rtrim) }
         )
         XCTAssertEqual(
-            "CREATE TABLE \"table\" (\"stringOptional\" TEXT CHECK (\"string\" != '') DEFAULT ('string') COLLATE RTRIM)",
-            table.create { t in t.column(stringOptional, check: string != "", defaultValue: "string", collate: .rtrim) }
+            """
+            CREATE TABLE \"table\" (\"stringOptional\" TEXT CHECK (\"string\" != '')
+             DEFAULT ('string') COLLATE RTRIM)
+            """.replacingOccurrences(of: "\n", with: ""),
+            table.create { t in t.column(stringOptional, check: string != "",
+                                         defaultValue: "string", collate: .rtrim) }
         )
         XCTAssertEqual(
-            "CREATE TABLE \"table\" (\"stringOptional\" TEXT CHECK (\"stringOptional\" != '') DEFAULT ('string') COLLATE RTRIM)",
-            table.create { t in t.column(stringOptional, check: stringOptional != "", defaultValue: "string", collate: .rtrim) }
+            """
+            CREATE TABLE \"table\" (\"stringOptional\" TEXT CHECK (\"stringOptional\" != '')
+             DEFAULT ('string') COLLATE RTRIM)
+            """.replacingOccurrences(of: "\n", with: ""),
+            table.create { t in t.column(stringOptional, check: stringOptional != "",
+                                         defaultValue: "string", collate: .rtrim) }
         )
     }
 
diff --git a/Tests/SQLiteTests/SelectTests.swift b/Tests/SQLiteTests/SelectTests.swift
index bca01092..c66d401d 100644
--- a/Tests/SQLiteTests/SelectTests.swift
+++ b/Tests/SQLiteTests/SelectTests.swift
@@ -2,14 +2,14 @@ import XCTest
 @testable import SQLite
 
 class SelectTests: SQLiteTestCase {
-    
+
     override func setUp() {
         super.setUp()
-        CreateUsersTable()
-        CreateUsersDataTable()
+        createUsersTable()
+        createUsersDataTable()
     }
-    
-    func CreateUsersDataTable() {
+
+    func createUsersDataTable() {
         try! db.execute("""
             CREATE TABLE users_name (
                 id INTEGER,
@@ -19,27 +19,26 @@ class SelectTests: SQLiteTestCase {
             """
         )
     }
-    
+
     func test_select_columns_from_multiple_tables() {
         let usersData = Table("users_name")
         let users = Table("users")
-        
+
         let name = Expression<String>("name")
         let id = Expression<Int64>("id")
         let userID = Expression<Int64>("user_id")
         let email = Expression<String>("email")
-        
-        try! InsertUser("Joey")
+
+        try! insertUser("Joey")
         try! db.run(usersData.insert(
             id <- 1,
             userID <- 1,
             name <- "Joey"
         ))
-        
+
         try! db.prepare(users.select(name, email).join(usersData, on: userID == users[id])).forEach {
             XCTAssertEqual($0[name], "Joey")
             XCTAssertEqual($0[email], "Joey@example.com")
         }
     }
-    
 }
diff --git a/Tests/SQLiteTests/SetterTests.swift b/Tests/SQLiteTests/SetterTests.swift
index d4f189d7..938dd013 100644
--- a/Tests/SQLiteTests/SetterTests.swift
+++ b/Tests/SQLiteTests/SetterTests.swift
@@ -1,137 +1,137 @@
 import XCTest
 import SQLite
 
-class SetterTests : XCTestCase {
+class SetterTests: XCTestCase {
 
     func test_setterAssignmentOperator_buildsSetter() {
-        AssertSQL("\"int\" = \"int\"", int <- int)
-        AssertSQL("\"int\" = 1", int <- 1)
-        AssertSQL("\"intOptional\" = \"int\"", intOptional <- int)
-        AssertSQL("\"intOptional\" = \"intOptional\"", intOptional <- intOptional)
-        AssertSQL("\"intOptional\" = 1", intOptional <- 1)
-        AssertSQL("\"intOptional\" = NULL", intOptional <- nil)
+        assertSQL("\"int\" = \"int\"", int <- int)
+        assertSQL("\"int\" = 1", int <- 1)
+        assertSQL("\"intOptional\" = \"int\"", intOptional <- int)
+        assertSQL("\"intOptional\" = \"intOptional\"", intOptional <- intOptional)
+        assertSQL("\"intOptional\" = 1", intOptional <- 1)
+        assertSQL("\"intOptional\" = NULL", intOptional <- nil)
     }
 
     func test_plusEquals_withStringExpression_buildsSetter() {
-        AssertSQL("\"string\" = (\"string\" || \"string\")", string += string)
-        AssertSQL("\"string\" = (\"string\" || 'literal')", string += "literal")
-        AssertSQL("\"stringOptional\" = (\"stringOptional\" || \"string\")", stringOptional += string)
-        AssertSQL("\"stringOptional\" = (\"stringOptional\" || \"stringOptional\")", stringOptional += stringOptional)
-        AssertSQL("\"stringOptional\" = (\"stringOptional\" || 'literal')", stringOptional += "literal")
+        assertSQL("\"string\" = (\"string\" || \"string\")", string += string)
+        assertSQL("\"string\" = (\"string\" || 'literal')", string += "literal")
+        assertSQL("\"stringOptional\" = (\"stringOptional\" || \"string\")", stringOptional += string)
+        assertSQL("\"stringOptional\" = (\"stringOptional\" || \"stringOptional\")", stringOptional += stringOptional)
+        assertSQL("\"stringOptional\" = (\"stringOptional\" || 'literal')", stringOptional += "literal")
     }
 
     func test_plusEquals_withNumberExpression_buildsSetter() {
-        AssertSQL("\"int\" = (\"int\" + \"int\")", int += int)
-        AssertSQL("\"int\" = (\"int\" + 1)", int += 1)
-        AssertSQL("\"intOptional\" = (\"intOptional\" + \"int\")", intOptional += int)
-        AssertSQL("\"intOptional\" = (\"intOptional\" + \"intOptional\")", intOptional += intOptional)
-        AssertSQL("\"intOptional\" = (\"intOptional\" + 1)", intOptional += 1)
-
-        AssertSQL("\"double\" = (\"double\" + \"double\")", double += double)
-        AssertSQL("\"double\" = (\"double\" + 1.0)", double += 1)
-        AssertSQL("\"doubleOptional\" = (\"doubleOptional\" + \"double\")", doubleOptional += double)
-        AssertSQL("\"doubleOptional\" = (\"doubleOptional\" + \"doubleOptional\")", doubleOptional += doubleOptional)
-        AssertSQL("\"doubleOptional\" = (\"doubleOptional\" + 1.0)", doubleOptional += 1)
+        assertSQL("\"int\" = (\"int\" + \"int\")", int += int)
+        assertSQL("\"int\" = (\"int\" + 1)", int += 1)
+        assertSQL("\"intOptional\" = (\"intOptional\" + \"int\")", intOptional += int)
+        assertSQL("\"intOptional\" = (\"intOptional\" + \"intOptional\")", intOptional += intOptional)
+        assertSQL("\"intOptional\" = (\"intOptional\" + 1)", intOptional += 1)
+
+        assertSQL("\"double\" = (\"double\" + \"double\")", double += double)
+        assertSQL("\"double\" = (\"double\" + 1.0)", double += 1)
+        assertSQL("\"doubleOptional\" = (\"doubleOptional\" + \"double\")", doubleOptional += double)
+        assertSQL("\"doubleOptional\" = (\"doubleOptional\" + \"doubleOptional\")", doubleOptional += doubleOptional)
+        assertSQL("\"doubleOptional\" = (\"doubleOptional\" + 1.0)", doubleOptional += 1)
     }
 
     func test_minusEquals_withNumberExpression_buildsSetter() {
-        AssertSQL("\"int\" = (\"int\" - \"int\")", int -= int)
-        AssertSQL("\"int\" = (\"int\" - 1)", int -= 1)
-        AssertSQL("\"intOptional\" = (\"intOptional\" - \"int\")", intOptional -= int)
-        AssertSQL("\"intOptional\" = (\"intOptional\" - \"intOptional\")", intOptional -= intOptional)
-        AssertSQL("\"intOptional\" = (\"intOptional\" - 1)", intOptional -= 1)
-
-        AssertSQL("\"double\" = (\"double\" - \"double\")", double -= double)
-        AssertSQL("\"double\" = (\"double\" - 1.0)", double -= 1)
-        AssertSQL("\"doubleOptional\" = (\"doubleOptional\" - \"double\")", doubleOptional -= double)
-        AssertSQL("\"doubleOptional\" = (\"doubleOptional\" - \"doubleOptional\")", doubleOptional -= doubleOptional)
-        AssertSQL("\"doubleOptional\" = (\"doubleOptional\" - 1.0)", doubleOptional -= 1)
+        assertSQL("\"int\" = (\"int\" - \"int\")", int -= int)
+        assertSQL("\"int\" = (\"int\" - 1)", int -= 1)
+        assertSQL("\"intOptional\" = (\"intOptional\" - \"int\")", intOptional -= int)
+        assertSQL("\"intOptional\" = (\"intOptional\" - \"intOptional\")", intOptional -= intOptional)
+        assertSQL("\"intOptional\" = (\"intOptional\" - 1)", intOptional -= 1)
+
+        assertSQL("\"double\" = (\"double\" - \"double\")", double -= double)
+        assertSQL("\"double\" = (\"double\" - 1.0)", double -= 1)
+        assertSQL("\"doubleOptional\" = (\"doubleOptional\" - \"double\")", doubleOptional -= double)
+        assertSQL("\"doubleOptional\" = (\"doubleOptional\" - \"doubleOptional\")", doubleOptional -= doubleOptional)
+        assertSQL("\"doubleOptional\" = (\"doubleOptional\" - 1.0)", doubleOptional -= 1)
     }
 
     func test_timesEquals_withNumberExpression_buildsSetter() {
-        AssertSQL("\"int\" = (\"int\" * \"int\")", int *= int)
-        AssertSQL("\"int\" = (\"int\" * 1)", int *= 1)
-        AssertSQL("\"intOptional\" = (\"intOptional\" * \"int\")", intOptional *= int)
-        AssertSQL("\"intOptional\" = (\"intOptional\" * \"intOptional\")", intOptional *= intOptional)
-        AssertSQL("\"intOptional\" = (\"intOptional\" * 1)", intOptional *= 1)
-
-        AssertSQL("\"double\" = (\"double\" * \"double\")", double *= double)
-        AssertSQL("\"double\" = (\"double\" * 1.0)", double *= 1)
-        AssertSQL("\"doubleOptional\" = (\"doubleOptional\" * \"double\")", doubleOptional *= double)
-        AssertSQL("\"doubleOptional\" = (\"doubleOptional\" * \"doubleOptional\")", doubleOptional *= doubleOptional)
-        AssertSQL("\"doubleOptional\" = (\"doubleOptional\" * 1.0)", doubleOptional *= 1)
+        assertSQL("\"int\" = (\"int\" * \"int\")", int *= int)
+        assertSQL("\"int\" = (\"int\" * 1)", int *= 1)
+        assertSQL("\"intOptional\" = (\"intOptional\" * \"int\")", intOptional *= int)
+        assertSQL("\"intOptional\" = (\"intOptional\" * \"intOptional\")", intOptional *= intOptional)
+        assertSQL("\"intOptional\" = (\"intOptional\" * 1)", intOptional *= 1)
+
+        assertSQL("\"double\" = (\"double\" * \"double\")", double *= double)
+        assertSQL("\"double\" = (\"double\" * 1.0)", double *= 1)
+        assertSQL("\"doubleOptional\" = (\"doubleOptional\" * \"double\")", doubleOptional *= double)
+        assertSQL("\"doubleOptional\" = (\"doubleOptional\" * \"doubleOptional\")", doubleOptional *= doubleOptional)
+        assertSQL("\"doubleOptional\" = (\"doubleOptional\" * 1.0)", doubleOptional *= 1)
     }
 
     func test_dividedByEquals_withNumberExpression_buildsSetter() {
-        AssertSQL("\"int\" = (\"int\" / \"int\")", int /= int)
-        AssertSQL("\"int\" = (\"int\" / 1)", int /= 1)
-        AssertSQL("\"intOptional\" = (\"intOptional\" / \"int\")", intOptional /= int)
-        AssertSQL("\"intOptional\" = (\"intOptional\" / \"intOptional\")", intOptional /= intOptional)
-        AssertSQL("\"intOptional\" = (\"intOptional\" / 1)", intOptional /= 1)
-
-        AssertSQL("\"double\" = (\"double\" / \"double\")", double /= double)
-        AssertSQL("\"double\" = (\"double\" / 1.0)", double /= 1)
-        AssertSQL("\"doubleOptional\" = (\"doubleOptional\" / \"double\")", doubleOptional /= double)
-        AssertSQL("\"doubleOptional\" = (\"doubleOptional\" / \"doubleOptional\")", doubleOptional /= doubleOptional)
-        AssertSQL("\"doubleOptional\" = (\"doubleOptional\" / 1.0)", doubleOptional /= 1)
+        assertSQL("\"int\" = (\"int\" / \"int\")", int /= int)
+        assertSQL("\"int\" = (\"int\" / 1)", int /= 1)
+        assertSQL("\"intOptional\" = (\"intOptional\" / \"int\")", intOptional /= int)
+        assertSQL("\"intOptional\" = (\"intOptional\" / \"intOptional\")", intOptional /= intOptional)
+        assertSQL("\"intOptional\" = (\"intOptional\" / 1)", intOptional /= 1)
+
+        assertSQL("\"double\" = (\"double\" / \"double\")", double /= double)
+        assertSQL("\"double\" = (\"double\" / 1.0)", double /= 1)
+        assertSQL("\"doubleOptional\" = (\"doubleOptional\" / \"double\")", doubleOptional /= double)
+        assertSQL("\"doubleOptional\" = (\"doubleOptional\" / \"doubleOptional\")", doubleOptional /= doubleOptional)
+        assertSQL("\"doubleOptional\" = (\"doubleOptional\" / 1.0)", doubleOptional /= 1)
     }
 
     func test_moduloEquals_withIntegerExpression_buildsSetter() {
-        AssertSQL("\"int\" = (\"int\" % \"int\")", int %= int)
-        AssertSQL("\"int\" = (\"int\" % 1)", int %= 1)
-        AssertSQL("\"intOptional\" = (\"intOptional\" % \"int\")", intOptional %= int)
-        AssertSQL("\"intOptional\" = (\"intOptional\" % \"intOptional\")", intOptional %= intOptional)
-        AssertSQL("\"intOptional\" = (\"intOptional\" % 1)", intOptional %= 1)
+        assertSQL("\"int\" = (\"int\" % \"int\")", int %= int)
+        assertSQL("\"int\" = (\"int\" % 1)", int %= 1)
+        assertSQL("\"intOptional\" = (\"intOptional\" % \"int\")", intOptional %= int)
+        assertSQL("\"intOptional\" = (\"intOptional\" % \"intOptional\")", intOptional %= intOptional)
+        assertSQL("\"intOptional\" = (\"intOptional\" % 1)", intOptional %= 1)
     }
 
     func test_leftShiftEquals_withIntegerExpression_buildsSetter() {
-        AssertSQL("\"int\" = (\"int\" << \"int\")", int <<= int)
-        AssertSQL("\"int\" = (\"int\" << 1)", int <<= 1)
-        AssertSQL("\"intOptional\" = (\"intOptional\" << \"int\")", intOptional <<= int)
-        AssertSQL("\"intOptional\" = (\"intOptional\" << \"intOptional\")", intOptional <<= intOptional)
-        AssertSQL("\"intOptional\" = (\"intOptional\" << 1)", intOptional <<= 1)
+        assertSQL("\"int\" = (\"int\" << \"int\")", int <<= int)
+        assertSQL("\"int\" = (\"int\" << 1)", int <<= 1)
+        assertSQL("\"intOptional\" = (\"intOptional\" << \"int\")", intOptional <<= int)
+        assertSQL("\"intOptional\" = (\"intOptional\" << \"intOptional\")", intOptional <<= intOptional)
+        assertSQL("\"intOptional\" = (\"intOptional\" << 1)", intOptional <<= 1)
     }
 
     func test_rightShiftEquals_withIntegerExpression_buildsSetter() {
-        AssertSQL("\"int\" = (\"int\" >> \"int\")", int >>= int)
-        AssertSQL("\"int\" = (\"int\" >> 1)", int >>= 1)
-        AssertSQL("\"intOptional\" = (\"intOptional\" >> \"int\")", intOptional >>= int)
-        AssertSQL("\"intOptional\" = (\"intOptional\" >> \"intOptional\")", intOptional >>= intOptional)
-        AssertSQL("\"intOptional\" = (\"intOptional\" >> 1)", intOptional >>= 1)
+        assertSQL("\"int\" = (\"int\" >> \"int\")", int >>= int)
+        assertSQL("\"int\" = (\"int\" >> 1)", int >>= 1)
+        assertSQL("\"intOptional\" = (\"intOptional\" >> \"int\")", intOptional >>= int)
+        assertSQL("\"intOptional\" = (\"intOptional\" >> \"intOptional\")", intOptional >>= intOptional)
+        assertSQL("\"intOptional\" = (\"intOptional\" >> 1)", intOptional >>= 1)
     }
 
     func test_bitwiseAndEquals_withIntegerExpression_buildsSetter() {
-        AssertSQL("\"int\" = (\"int\" & \"int\")", int &= int)
-        AssertSQL("\"int\" = (\"int\" & 1)", int &= 1)
-        AssertSQL("\"intOptional\" = (\"intOptional\" & \"int\")", intOptional &= int)
-        AssertSQL("\"intOptional\" = (\"intOptional\" & \"intOptional\")", intOptional &= intOptional)
-        AssertSQL("\"intOptional\" = (\"intOptional\" & 1)", intOptional &= 1)
+        assertSQL("\"int\" = (\"int\" & \"int\")", int &= int)
+        assertSQL("\"int\" = (\"int\" & 1)", int &= 1)
+        assertSQL("\"intOptional\" = (\"intOptional\" & \"int\")", intOptional &= int)
+        assertSQL("\"intOptional\" = (\"intOptional\" & \"intOptional\")", intOptional &= intOptional)
+        assertSQL("\"intOptional\" = (\"intOptional\" & 1)", intOptional &= 1)
     }
 
     func test_bitwiseOrEquals_withIntegerExpression_buildsSetter() {
-        AssertSQL("\"int\" = (\"int\" | \"int\")", int |= int)
-        AssertSQL("\"int\" = (\"int\" | 1)", int |= 1)
-        AssertSQL("\"intOptional\" = (\"intOptional\" | \"int\")", intOptional |= int)
-        AssertSQL("\"intOptional\" = (\"intOptional\" | \"intOptional\")", intOptional |= intOptional)
-        AssertSQL("\"intOptional\" = (\"intOptional\" | 1)", intOptional |= 1)
+        assertSQL("\"int\" = (\"int\" | \"int\")", int |= int)
+        assertSQL("\"int\" = (\"int\" | 1)", int |= 1)
+        assertSQL("\"intOptional\" = (\"intOptional\" | \"int\")", intOptional |= int)
+        assertSQL("\"intOptional\" = (\"intOptional\" | \"intOptional\")", intOptional |= intOptional)
+        assertSQL("\"intOptional\" = (\"intOptional\" | 1)", intOptional |= 1)
     }
 
     func test_bitwiseExclusiveOrEquals_withIntegerExpression_buildsSetter() {
-        AssertSQL("\"int\" = (~((\"int\" & \"int\")) & (\"int\" | \"int\"))", int ^= int)
-        AssertSQL("\"int\" = (~((\"int\" & 1)) & (\"int\" | 1))", int ^= 1)
-        AssertSQL("\"intOptional\" = (~((\"intOptional\" & \"int\")) & (\"intOptional\" | \"int\"))", intOptional ^= int)
-        AssertSQL("\"intOptional\" = (~((\"intOptional\" & \"intOptional\")) & (\"intOptional\" | \"intOptional\"))", intOptional ^= intOptional)
-        AssertSQL("\"intOptional\" = (~((\"intOptional\" & 1)) & (\"intOptional\" | 1))", intOptional ^= 1)
+        assertSQL("\"int\" = (~((\"int\" & \"int\")) & (\"int\" | \"int\"))", int ^= int)
+        assertSQL("\"int\" = (~((\"int\" & 1)) & (\"int\" | 1))", int ^= 1)
+        assertSQL("\"intOptional\" = (~((\"intOptional\" & \"int\")) & (\"intOptional\" | \"int\"))", intOptional ^= int)
+        assertSQL("\"intOptional\" = (~((\"intOptional\" & \"intOptional\")) & (\"intOptional\" | \"intOptional\"))", intOptional ^= intOptional)
+        assertSQL("\"intOptional\" = (~((\"intOptional\" & 1)) & (\"intOptional\" | 1))", intOptional ^= 1)
     }
 
     func test_postfixPlus_withIntegerValue_buildsSetter() {
-        AssertSQL("\"int\" = (\"int\" + 1)", int++)
-        AssertSQL("\"intOptional\" = (\"intOptional\" + 1)", intOptional++)
+        assertSQL("\"int\" = (\"int\" + 1)", int++)
+        assertSQL("\"intOptional\" = (\"intOptional\" + 1)", intOptional++)
     }
 
     func test_postfixMinus_withIntegerValue_buildsSetter() {
-        AssertSQL("\"int\" = (\"int\" - 1)", int--)
-        AssertSQL("\"intOptional\" = (\"intOptional\" - 1)", intOptional--)
+        assertSQL("\"int\" = (\"int\" - 1)", int--)
+        assertSQL("\"intOptional\" = (\"intOptional\" - 1)", intOptional--)
     }
 
 }
diff --git a/Tests/SQLiteTests/StatementTests.swift b/Tests/SQLiteTests/StatementTests.swift
index 326259b2..5a05675d 100644
--- a/Tests/SQLiteTests/StatementTests.swift
+++ b/Tests/SQLiteTests/StatementTests.swift
@@ -1,14 +1,14 @@
 import XCTest
 import SQLite
 
-class StatementTests : SQLiteTestCase {
+class StatementTests: SQLiteTestCase {
     override func setUp() {
         super.setUp()
-        CreateUsersTable()
+        createUsersTable()
     }
 
     func test_cursor_to_blob() {
-        try! InsertUsers("alice")
+        try! insertUsers("alice")
         let statement = try! db.prepare("SELECT email FROM users")
         XCTAssert(try! statement.step())
         let blob = statement.row[0] as Blob
diff --git a/Tests/SQLiteTests/TestHelpers.swift b/Tests/SQLiteTests/TestHelpers.swift
index 247013b4..0994dd97 100644
--- a/Tests/SQLiteTests/TestHelpers.swift
+++ b/Tests/SQLiteTests/TestHelpers.swift
@@ -1,15 +1,15 @@
 import XCTest
 @testable import SQLite
 
-class SQLiteTestCase : XCTestCase {
-    private var trace:[String: Int]!
-    var db:Connection!
+class SQLiteTestCase: XCTestCase {
+    private var trace: [String: Int]!
+    var db: Connection!
     let users = Table("users")
 
     override func setUp() {
         super.setUp()
         db = try! Connection()
-        trace = [String:Int]()
+        trace = [String: Int]()
 
         db.trace { SQL in
             print(SQL)
@@ -17,7 +17,7 @@ class SQLiteTestCase : XCTestCase {
         }
     }
 
-    func CreateUsersTable() {
+    func createUsersTable() {
         try! db.execute("""
             CREATE TABLE users (
                 id INTEGER PRIMARY KEY,
@@ -33,22 +33,22 @@ class SQLiteTestCase : XCTestCase {
         )
     }
 
-    func InsertUsers(_ names: String...) throws {
-        try InsertUsers(names)
+    func insertUsers(_ names: String...) throws {
+        try insertUsers(names)
     }
 
-    func InsertUsers(_ names: [String]) throws {
-        for name in names { try InsertUser(name) }
+    func insertUsers(_ names: [String]) throws {
+        for name in names { try insertUser(name) }
     }
 
-    @discardableResult func InsertUser(_ name: String, age: Int? = nil, admin: Bool = false) throws -> Statement {
+    @discardableResult func insertUser(_ name: String, age: Int? = nil, admin: Bool = false) throws -> Statement {
         return try db.run(
             "INSERT INTO \"users\" (email, age, admin) values (?, ?, ?)",
             "\(name)@example.com", age?.datatypeValue, admin.datatypeValue
         )
     }
 
-    func AssertSQL(_ SQL: String, _ executions: Int = 1, _ message: String? = nil, file: StaticString = #file, line: UInt = #line) {
+    func assertSQL(_ SQL: String, _ executions: Int = 1, _ message: String? = nil, file: StaticString = #file, line: UInt = #line) {
         XCTAssertEqual(
             executions, trace[SQL] ?? 0,
             message ?? SQL,
@@ -56,9 +56,9 @@ class SQLiteTestCase : XCTestCase {
         )
     }
 
-    func AssertSQL(_ SQL: String, _ statement: Statement, _ message: String? = nil, file: StaticString = #file, line: UInt = #line) {
+    func assertSQL(_ SQL: String, _ statement: Statement, _ message: String? = nil, file: StaticString = #file, line: UInt = #line) {
         try! statement.run()
-        AssertSQL(SQL, 1, message, file: file, line: line)
+        assertSQL(SQL, 1, message, file: file, line: line)
         if let count = trace[SQL] { trace[SQL] = count - 1 }
     }
 
@@ -97,7 +97,8 @@ let int64Optional = Expression<Int64?>("int64Optional")
 let string = Expression<String>("string")
 let stringOptional = Expression<String?>("stringOptional")
 
-func AssertSQL(_ expression1: @autoclosure () -> String, _ expression2: @autoclosure () -> Expressible, file: StaticString = #file, line: UInt = #line) {
+func assertSQL(_ expression1: @autoclosure () -> String, _ expression2: @autoclosure () -> Expressible,
+               file: StaticString = #file, line: UInt = #line) {
     XCTAssertEqual(expression1(), expression2().asSQL(), file: file, line: line)
 }
 
diff --git a/Tests/SQLiteTests/ValueTests.swift b/Tests/SQLiteTests/ValueTests.swift
index bda2b4b3..f880cb34 100644
--- a/Tests/SQLiteTests/ValueTests.swift
+++ b/Tests/SQLiteTests/ValueTests.swift
@@ -1,6 +1,6 @@
 import XCTest
 import SQLite
 
-class ValueTests : XCTestCase {
+class ValueTests: XCTestCase {
 
 }

From ae42398825c0e391dbbb8393dc8380979e3a9004 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Mon, 23 Aug 2021 01:20:49 +0200
Subject: [PATCH 046/326] brew

---
 .github/workflows/build.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 0b9d0cf1..1119284a 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -14,7 +14,7 @@ jobs:
         gem install xcpretty --no-document
         brew update
         brew outdated carthage || brew upgrade carthage
-        brew install swiftlint
+        brew outdated swiftlint || brew upgrade swiftlint
     - name: "Lint"
       run: make lint
     - name: "Run tests (BUILD_SCHEME: SQLite iOS)"

From a8f5079a2e0676b74baffe49a3226c65cb7af6e4 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Mon, 23 Aug 2021 01:38:03 +0200
Subject: [PATCH 047/326] Fix warnings

---
 .swiftlint.yml                           | 12 +++++++++---
 Makefile                                 |  2 +-
 Sources/SQLite/Core/Connection.swift     | 10 +++++-----
 Sources/SQLite/Extensions/FTS4.swift     |  1 +
 Sources/SQLite/Helpers.swift             | 10 +++++-----
 Sources/SQLite/Typed/CoreFunctions.swift |  2 +-
 Sources/SQLite/Typed/Query.swift         | 20 +++++++++++---------
 7 files changed, 33 insertions(+), 24 deletions(-)

diff --git a/.swiftlint.yml b/.swiftlint.yml
index e18c09a1..3bfc5fa5 100644
--- a/.swiftlint.yml
+++ b/.swiftlint.yml
@@ -11,6 +11,7 @@ excluded: # paths to ignore during linting. overridden by `included`.
 identifier_name:
   excluded:
     - db
+    - in
     - to
     - by
     - or
@@ -20,13 +21,18 @@ identifier_name:
     - fn
     - a
     - b
+    - q
+    - SQLITE_TRANSIENT
+
+type_body_length:
+  warning: 260
+  error: 260
 
 line_length:
   warning: 150
   error: 150
   ignores_comments: true
 
-
 file_length:
-  warning: 700
-  error: 700
+  warning: 760
+  error: 760
diff --git a/Makefile b/Makefile
index 5e4ff388..a9d29d58 100644
--- a/Makefile
+++ b/Makefile
@@ -19,7 +19,7 @@ build:
 	$(BUILD_TOOL) $(BUILD_ARGUMENTS)
 
 lint:
-	swiftlint
+	swiftlint --strict
 
 test:
 ifdef XCPRETTY
diff --git a/Sources/SQLite/Core/Connection.swift b/Sources/SQLite/Core/Connection.swift
index a4dad4a6..61c5f94a 100644
--- a/Sources/SQLite/Core/Connection.swift
+++ b/Sources/SQLite/Core/Connection.swift
@@ -473,10 +473,10 @@ public final class Connection {
                  // The T argument is one of the SQLITE_TRACE constants to indicate why the
                  // callback was invoked. The C argument is a copy of the context pointer.
                  // The P and X arguments are pointers whose meanings depend on T.
-                 (_: UInt32, C: UnsafeMutableRawPointer?, P: UnsafeMutableRawPointer?, _: UnsafeMutableRawPointer?) in
-                 if let P = P,
-                    let expandedSQL = sqlite3_expanded_sql(OpaquePointer(P)) {
-                     unsafeBitCast(C, to: Trace.self)(expandedSQL)
+                 (_: UInt32, context: UnsafeMutableRawPointer?, pointer: UnsafeMutableRawPointer?, _: UnsafeMutableRawPointer?) in
+                 if let pointer = pointer,
+                    let expandedSQL = sqlite3_expanded_sql(OpaquePointer(pointer)) {
+                     unsafeBitCast(context, to: Trace.self)(expandedSQL)
                      sqlite3_free(expandedSQL)
                  }
                  return Int32(0) // currently ignored
@@ -585,7 +585,7 @@ public final class Connection {
     ///   - block: A block of code to run when the function is called. The block
     ///     is called with an array of raw SQL values mapped to the function’s
     ///     parameters and should return a raw SQL value (or nil).
-    // swiftlint:disable:next cyclomatic_complexity
+    // swiftlint:disable:next cyclomatic_complexity function_body_length
     public func createFunction(_ function: String, argumentCount: UInt? = nil, deterministic: Bool = false,
                                _ block: @escaping (_ args: [Binding?]) -> Binding?) {
         let argc = argumentCount.map { Int($0) } ?? -1
diff --git a/Sources/SQLite/Extensions/FTS4.swift b/Sources/SQLite/Extensions/FTS4.swift
index a3c253f2..bdb89e0e 100644
--- a/Sources/SQLite/Extensions/FTS4.swift
+++ b/Sources/SQLite/Extensions/FTS4.swift
@@ -91,6 +91,7 @@ extension VirtualTable {
 
 }
 
+// swiftlint:disable identifier_name
 public struct Tokenizer {
 
     public static let Simple = Tokenizer("simple")
diff --git a/Sources/SQLite/Helpers.swift b/Sources/SQLite/Helpers.swift
index 0e3385e3..f5c67e36 100644
--- a/Sources/SQLite/Helpers.swift
+++ b/Sources/SQLite/Helpers.swift
@@ -115,11 +115,11 @@ func transcode(_ literal: Binding?) -> String {
     }
 }
 
-//swiftlint:disable force_cast
-func value<A: Value>(_ v: Binding) -> A {
-    A.fromDatatypeValue(v as! A.Datatype) as! A
+// swiftlint:disable force_cast
+func value<A: Value>(_ binding: Binding) -> A {
+    A.fromDatatypeValue(binding as! A.Datatype) as! A
 }
 
-func value<A: Value>(_ v: Binding?) -> A {
-    value(v!)
+func value<A: Value>(_ binding: Binding?) -> A {
+    value(binding!)
 }
diff --git a/Sources/SQLite/Typed/CoreFunctions.swift b/Sources/SQLite/Typed/CoreFunctions.swift
index 053f17e5..42689578 100644
--- a/Sources/SQLite/Typed/CoreFunctions.swift
+++ b/Sources/SQLite/Typed/CoreFunctions.swift
@@ -21,7 +21,7 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 //
-
+// swiftlint:disable file_length
 import Foundation
 
 private enum Function: String {
diff --git a/Sources/SQLite/Typed/Query.swift b/Sources/SQLite/Typed/Query.swift
index 8d3ded52..6b1f6b8e 100644
--- a/Sources/SQLite/Typed/Query.swift
+++ b/Sources/SQLite/Typed/Query.swift
@@ -1015,13 +1015,13 @@ extension Connection {
             let column = names.removeLast()
             let namespace = names.joined(separator: ".")
 
-            func expandGlob(_ namespace: Bool) -> ((QueryType) throws -> Void) {
-                return { (query: QueryType) throws -> Void in
-                    var q = type(of: query).init(query.clauses.from.name, database: query.clauses.from.database)
-                    q.clauses.select = query.clauses.select
-                    let e = q.expression
-                    var names = try self.prepare(e.template, e.bindings).columnNames.map { $0.quote() }
-                    if namespace { names = names.map { "\(query.tableName().expression.template).\($0)" } }
+            func expandGlob(_ namespace: Bool) -> (QueryType) throws -> Void {
+                return { (queryType: QueryType) throws -> Void in
+                    var query = type(of: queryType).init(queryType.clauses.from.name, database: queryType.clauses.from.database)
+                    query.clauses.select = queryType.clauses.select
+                    let expression = query.expression
+                    var names = try self.prepare(expression.template, expression.bindings).columnNames.map { $0.quote() }
+                    if namespace { names = names.map { "\(queryType.tableName().expression.template).\($0)" } }
                     for name in names { columnNames[name] = idx; idx += 1 }
                 }
             }
@@ -1184,11 +1184,13 @@ public struct Row {
     }
 
     public subscript<T: Value>(column: Expression<T>) -> T {
-        return try! get(column)
+        // swiftlint:disable:next force_try
+        try! get(column)
     }
 
     public subscript<T: Value>(column: Expression<T?>) -> T? {
-        return try! get(column)
+        // swiftlint:disable:next force_try
+        try! get(column)
     }
 }
 

From 7a8269be26e128fe4731cc39a624470edcc11764 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Mon, 23 Aug 2021 10:41:51 +0200
Subject: [PATCH 048/326] Remove unnecessary self

---
 Sources/SQLite/Core/Connection.swift    |  4 +-
 Sources/SQLite/Core/Statement.swift     | 10 +--
 Sources/SQLite/Extensions/FTS4.swift    | 14 ++--
 Sources/SQLite/Extensions/FTS5.swift    |  4 +-
 Sources/SQLite/Foundation.swift         |  2 +-
 Sources/SQLite/Typed/Coding.swift       | 96 ++++++++++++-------------
 Sources/SQLite/Typed/Query.swift        |  8 +--
 Sources/SQLite/Typed/Setter.swift       |  2 +-
 Tests/SQLiteTests/ConnectionTests.swift | 14 ++--
 9 files changed, 77 insertions(+), 77 deletions(-)

diff --git a/Sources/SQLite/Core/Connection.swift b/Sources/SQLite/Core/Connection.swift
index 61c5f94a..2ba51d80 100644
--- a/Sources/SQLite/Core/Connection.swift
+++ b/Sources/SQLite/Core/Connection.swift
@@ -161,7 +161,7 @@ public final class Connection {
     ///
     /// - Throws: `Result.Error` if query execution fails.
     public func execute(_ SQL: String) throws {
-        _ = try sync { try self.check(sqlite3_exec(self.handle, SQL, nil, nil, nil)) }
+        _ = try sync { try check(sqlite3_exec(handle, SQL, nil, nil, nil)) }
     }
 
     // MARK: - Prepare
@@ -633,7 +633,7 @@ public final class Connection {
             let function = unsafeBitCast(sqlite3_user_data(context), to: Function.self)
             function(context, argc, value)
         }, nil, nil, nil)
-        if functions[function] == nil { self.functions[function] = [:] }
+        if functions[function] == nil { functions[function] = [:] }
         functions[function]?[argc] = box
     }
     
diff --git a/Sources/SQLite/Core/Statement.swift b/Sources/SQLite/Core/Statement.swift
index dfb67d22..4332cb49 100644
--- a/Sources/SQLite/Core/Statement.swift
+++ b/Sources/SQLite/Core/Statement.swift
@@ -48,10 +48,10 @@ public final class Statement {
         sqlite3_finalize(handle)
     }
 
-    public lazy var columnCount: Int = Int(sqlite3_column_count(self.handle))
+    public lazy var columnCount: Int = Int(sqlite3_column_count(handle))
 
-    public lazy var columnNames: [String] = (0..<Int32(self.columnCount)).map {
-        String(cString: sqlite3_column_name(self.handle, $0))
+    public lazy var columnNames: [String] = (0..<Int32(columnCount)).map {
+        String(cString: sqlite3_column_name(handle, $0))
     }
 
     /// A cursor pointing to the current row.
@@ -182,7 +182,7 @@ public final class Statement {
     }
 
     public func step() throws -> Bool {
-        return try connection.sync { try self.connection.check(sqlite3_step(self.handle)) == SQLITE_ROW }
+        return try connection.sync { try connection.check(sqlite3_step(handle)) == SQLITE_ROW }
     }
 
     fileprivate func reset(clearBindings shouldClear: Bool = true) {
@@ -304,7 +304,7 @@ extension Cursor: Sequence {
     public func makeIterator() -> AnyIterator<Binding?> {
         var idx = 0
         return AnyIterator {
-            if idx >= self.columnCount {
+            if idx >= columnCount {
                 return Binding??.none
             } else {
                 idx += 1
diff --git a/Sources/SQLite/Extensions/FTS4.swift b/Sources/SQLite/Extensions/FTS4.swift
index bdb89e0e..1de03ff2 100644
--- a/Sources/SQLite/Extensions/FTS4.swift
+++ b/Sources/SQLite/Extensions/FTS4.swift
@@ -184,7 +184,7 @@ open class FTSConfig {
 
     /// Adds a column definition
     @discardableResult open func column(_ column: Expressible, _ options: [ColumnOption] = []) -> Self {
-        self.columnDefinitions.append((column, options))
+        columnDefinitions.append((column, options))
         return self
     }
 
@@ -203,19 +203,19 @@ open class FTSConfig {
 
     /// [The prefix= option](https://www.sqlite.org/fts3.html#section_6_6)
     @discardableResult open func prefix(_ prefix: [Int]) -> Self {
-        self.prefixes += prefix
+        prefixes += prefix
         return self
     }
 
     /// [The content= option](https://www.sqlite.org/fts3.html#section_6_2)
     @discardableResult open func externalContent(_ schema: SchemaType) -> Self {
-        self.externalContentSchema = schema
+        externalContentSchema = schema
         return self
     }
 
     /// [Contentless FTS4 Tables](https://www.sqlite.org/fts3.html#section_6_2_1)
     @discardableResult open func contentless() -> Self {
-        self.isContentless = true
+        isContentless = true
         return self
     }
 
@@ -311,19 +311,19 @@ open class FTS4Config: FTSConfig {
 
     /// [The compress= and uncompress= options](https://www.sqlite.org/fts3.html#section_6_1)
     @discardableResult open func compress(_ functionName: String) -> Self {
-        self.compressFunction = functionName
+        compressFunction = functionName
         return self
     }
 
     /// [The compress= and uncompress= options](https://www.sqlite.org/fts3.html#section_6_1)
     @discardableResult open func uncompress(_ functionName: String) -> Self {
-        self.uncompressFunction = functionName
+        uncompressFunction = functionName
         return self
     }
 
     /// [The languageid= option](https://www.sqlite.org/fts3.html#section_6_3)
     @discardableResult open func languageId(_ columnName: String) -> Self {
-        self.languageId = columnName
+        languageId = columnName
         return self
     }
 
diff --git a/Sources/SQLite/Extensions/FTS5.swift b/Sources/SQLite/Extensions/FTS5.swift
index 07f49ffc..a5d04dbf 100644
--- a/Sources/SQLite/Extensions/FTS5.swift
+++ b/Sources/SQLite/Extensions/FTS5.swift
@@ -59,13 +59,13 @@ open class FTS5Config: FTSConfig {
 
     /// [External Content Tables](https://www.sqlite.org/fts5.html#section_4_4_2)
     @discardableResult open func contentRowId(_ column: Expressible) -> Self {
-        self.contentRowId = column
+        contentRowId = column
         return self
     }
 
     /// [The Columnsize Option](https://www.sqlite.org/fts5.html#section_4_5)
     @discardableResult open func columnSize(_ size: Int) -> Self {
-        self.columnSize = size
+        columnSize = size
         return self
     }
 
diff --git a/Sources/SQLite/Foundation.swift b/Sources/SQLite/Foundation.swift
index d837d8ba..81e89d48 100644
--- a/Sources/SQLite/Foundation.swift
+++ b/Sources/SQLite/Foundation.swift
@@ -80,7 +80,7 @@ extension UUID: Value {
     }
 
     public var datatypeValue: String {
-        return self.uuidString
+        return uuidString
     }
 
 }
diff --git a/Sources/SQLite/Typed/Coding.swift b/Sources/SQLite/Typed/Coding.swift
index 5a672883..d2df2fe6 100644
--- a/Sources/SQLite/Typed/Coding.swift
+++ b/Sources/SQLite/Typed/Coding.swift
@@ -149,7 +149,7 @@ extension Row {
     ///
     /// - Returns: a decoded object from this row
     public func decode<V: Decodable>(userInfo: [CodingUserInfoKey: Any] = [:]) throws -> V {
-        return try V(from: self.decoder(userInfo: userInfo))
+        return try V(from: decoder(userInfo: userInfo))
     }
 
     public func decoder(userInfo: [CodingUserInfoKey: Any] = [:]) -> Decoder {
@@ -179,82 +179,82 @@ private class SQLiteEncoder: Encoder {
         }
 
         func encodeNil(forKey key: SQLiteEncoder.SQLiteKeyedEncodingContainer<Key>.Key) throws {
-            self.encoder.setters.append(Expression<String?>(key.stringValue) <- nil)
+            encoder.setters.append(Expression<String?>(key.stringValue) <- nil)
         }
 
         func encode(_ value: Int, forKey key: SQLiteEncoder.SQLiteKeyedEncodingContainer<Key>.Key) throws {
-            self.encoder.setters.append(Expression(key.stringValue) <- value)
+            encoder.setters.append(Expression(key.stringValue) <- value)
         }
 
         func encode(_ value: Bool, forKey key: Key) throws {
-            self.encoder.setters.append(Expression(key.stringValue) <- value)
+            encoder.setters.append(Expression(key.stringValue) <- value)
         }
 
         func encode(_ value: Float, forKey key: Key) throws {
-            self.encoder.setters.append(Expression(key.stringValue) <- Double(value))
+            encoder.setters.append(Expression(key.stringValue) <- Double(value))
         }
 
         func encode(_ value: Double, forKey key: Key) throws {
-            self.encoder.setters.append(Expression(key.stringValue) <- value)
+            encoder.setters.append(Expression(key.stringValue) <- value)
         }
 
         func encode(_ value: String, forKey key: Key) throws {
-            self.encoder.setters.append(Expression(key.stringValue) <- value)
+            encoder.setters.append(Expression(key.stringValue) <- value)
         }
 
         func encode<T>(_ value: T, forKey key: Key) throws where T: Swift.Encodable {
             if let data = value as? Data {
-                self.encoder.setters.append(Expression(key.stringValue) <- data)
+                encoder.setters.append(Expression(key.stringValue) <- data)
             } else if let date = value as? Date {
-                self.encoder.setters.append(Expression(key.stringValue) <- date.datatypeValue)
+                encoder.setters.append(Expression(key.stringValue) <- date.datatypeValue)
             } else {
                 let encoded = try JSONEncoder().encode(value)
                 let string = String(data: encoded, encoding: .utf8)
-                self.encoder.setters.append(Expression(key.stringValue) <- string)
+                encoder.setters.append(Expression(key.stringValue) <- string)
             }
         }
 
         func encode(_ value: Int8, forKey key: Key) throws {
-            throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: self.codingPath,
+            throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: codingPath,
                                                                           debugDescription: "encoding an Int8 is not supported"))
         }
 
         func encode(_ value: Int16, forKey key: Key) throws {
-            throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: self.codingPath,
+            throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: codingPath,
                                                                           debugDescription: "encoding an Int16 is not supported"))
         }
 
         func encode(_ value: Int32, forKey key: Key) throws {
-            throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: self.codingPath,
+            throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: codingPath,
                                                                           debugDescription: "encoding an Int32 is not supported"))
         }
 
         func encode(_ value: Int64, forKey key: Key) throws {
-            self.encoder.setters.append(Expression(key.stringValue) <- value)
+            encoder.setters.append(Expression(key.stringValue) <- value)
         }
 
         func encode(_ value: UInt, forKey key: Key) throws {
-            throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: self.codingPath,
+            throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: codingPath,
                                                                           debugDescription: "encoding an UInt is not supported"))
         }
 
         func encode(_ value: UInt8, forKey key: Key) throws {
-            throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: self.codingPath,
+            throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: codingPath,
                                                                           debugDescription: "encoding an UInt8 is not supported"))
         }
 
         func encode(_ value: UInt16, forKey key: Key) throws {
-            throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: self.codingPath,
+            throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: codingPath,
                                                                           debugDescription: "encoding an UInt16 is not supported"))
         }
 
         func encode(_ value: UInt32, forKey key: Key) throws {
-            throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: self.codingPath,
+            throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: codingPath,
                                                                           debugDescription: "encoding an UInt32 is not supported"))
         }
 
         func encode(_ value: UInt64, forKey key: Key) throws {
-            throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: self.codingPath,
+            throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: codingPath,
                                                                           debugDescription: "encoding an UInt64 is not supported"))
         }
 
@@ -301,98 +301,98 @@ private class SQLiteDecoder: Decoder {
         }
 
         var allKeys: [Key] {
-            return self.row.columnNames.keys.compactMap({Key(stringValue: $0)})
+            return row.columnNames.keys.compactMap({Key(stringValue: $0)})
         }
 
         func contains(_ key: Key) -> Bool {
-            return self.row.hasValue(for: key.stringValue)
+            return row.hasValue(for: key.stringValue)
         }
 
         func decodeNil(forKey key: Key) throws -> Bool {
-            return !self.contains(key)
+            return !contains(key)
         }
 
         func decode(_ type: Bool.Type, forKey key: Key) throws -> Bool {
-            return try self.row.get(Expression(key.stringValue))
+            return try row.get(Expression(key.stringValue))
         }
 
         func decode(_ type: Int.Type, forKey key: Key) throws -> Int {
-            return try self.row.get(Expression(key.stringValue))
+            return try row.get(Expression(key.stringValue))
         }
 
         func decode(_ type: Int8.Type, forKey key: Key) throws -> Int8 {
-            throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: self.codingPath,
+            throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: codingPath,
                                                                          debugDescription: "decoding an Int8 is not supported"))
         }
 
         func decode(_ type: Int16.Type, forKey key: Key) throws -> Int16 {
-            throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: self.codingPath,
+            throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: codingPath,
                                                                          debugDescription: "decoding an Int16 is not supported"))
         }
 
         func decode(_ type: Int32.Type, forKey key: Key) throws -> Int32 {
-            throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: self.codingPath,
+            throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: codingPath,
                                                                          debugDescription: "decoding an Int32 is not supported"))
         }
 
         func decode(_ type: Int64.Type, forKey key: Key) throws -> Int64 {
-            return try self.row.get(Expression(key.stringValue))
+            return try row.get(Expression(key.stringValue))
         }
 
         func decode(_ type: UInt.Type, forKey key: Key) throws -> UInt {
-            throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: self.codingPath,
+            throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: codingPath,
                                                                          debugDescription: "decoding an UInt is not supported"))
 
         }
 
         func decode(_ type: UInt8.Type, forKey key: Key) throws -> UInt8 {
-            throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: self.codingPath,
+            throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: codingPath,
                                                                          debugDescription: "decoding an UInt8 is not supported"))
         }
 
         func decode(_ type: UInt16.Type, forKey key: Key) throws -> UInt16 {
-            throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: self.codingPath,
+            throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: codingPath,
                                                                          debugDescription: "decoding an UInt16 is not supported"))
         }
 
         func decode(_ type: UInt32.Type, forKey key: Key) throws -> UInt32 {
-            throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: self.codingPath,
+            throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: codingPath,
                                                                          debugDescription: "decoding an UInt32 is not supported"))
         }
 
         func decode(_ type: UInt64.Type, forKey key: Key) throws -> UInt64 {
-            throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: self.codingPath,
+            throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: codingPath,
                                                                          debugDescription: "decoding an UInt64 is not supported"))
         }
 
         func decode(_ type: Float.Type, forKey key: Key) throws -> Float {
-            return Float(try self.row.get(Expression<Double>(key.stringValue)))
+            return Float(try row.get(Expression<Double>(key.stringValue)))
         }
 
         func decode(_ type: Double.Type, forKey key: Key) throws -> Double {
-            return try self.row.get(Expression(key.stringValue))
+            return try row.get(Expression(key.stringValue))
         }
 
         func decode(_ type: String.Type, forKey key: Key) throws -> String {
-            return try self.row.get(Expression(key.stringValue))
+            return try row.get(Expression(key.stringValue))
         }
 
         func decode<T>(_ type: T.Type, forKey key: Key) throws -> T where T: Swift.Decodable {
             // swiftlint:disable force_cast
             if type == Data.self {
-                let data = try self.row.get(Expression<Data>(key.stringValue))
+                let data = try row.get(Expression<Data>(key.stringValue))
                 return data as! T
             } else if type == Date.self {
-                let date = try self.row.get(Expression<Date>(key.stringValue))
+                let date = try row.get(Expression<Date>(key.stringValue))
                 return date as! T
             }
             // swiftlint:enable force_cast
-            guard let JSONString = try self.row.get(Expression<String?>(key.stringValue)) else {
-                throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: self.codingPath,
+            guard let JSONString = try row.get(Expression<String?>(key.stringValue)) else {
+                throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: codingPath,
                                                                              debugDescription: "an unsupported type was found"))
             }
             guard let data = JSONString.data(using: .utf8) else {
-                throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath,
+                throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: codingPath,
                                                                         debugDescription: "invalid utf8 data found"))
             }
             return try JSONDecoder().decode(type, from: data)
@@ -400,22 +400,22 @@ private class SQLiteDecoder: Decoder {
 
         func nestedContainer<NestedKey>(keyedBy type: NestedKey.Type, forKey key: Key) throws
                         -> KeyedDecodingContainer<NestedKey> where NestedKey: CodingKey {
-            throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath,
+            throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: codingPath,
                                                                     debugDescription: "decoding nested containers is not supported"))
         }
 
         func nestedUnkeyedContainer(forKey key: Key) throws -> UnkeyedDecodingContainer {
-            throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath,
+            throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: codingPath,
                                                                     debugDescription: "decoding unkeyed containers is not supported"))
         }
 
         func superDecoder() throws -> Swift.Decoder {
-            throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath,
+            throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: codingPath,
                                                                     debugDescription: "decoding super encoders containers is not supported"))
         }
 
         func superDecoder(forKey key: Key) throws -> Swift.Decoder {
-            throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath,
+            throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: codingPath,
                                                                     debugDescription: "decoding super decoders is not supported"))
         }
     }
@@ -430,16 +430,16 @@ private class SQLiteDecoder: Decoder {
     }
 
     func container<Key>(keyedBy type: Key.Type) throws -> KeyedDecodingContainer<Key> where Key: CodingKey {
-        return KeyedDecodingContainer(SQLiteKeyedDecodingContainer(row: self.row))
+        return KeyedDecodingContainer(SQLiteKeyedDecodingContainer(row: row))
     }
 
     func unkeyedContainer() throws -> UnkeyedDecodingContainer {
-        throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath,
+        throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: codingPath,
                                                                 debugDescription: "decoding an unkeyed container is not supported"))
     }
 
     func singleValueContainer() throws -> SingleValueDecodingContainer {
-        throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: self.codingPath,
+        throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: codingPath,
                                                                 debugDescription: "decoding a single value container is not supported"))
     }
 }
diff --git a/Sources/SQLite/Typed/Query.swift b/Sources/SQLite/Typed/Query.swift
index 6b1f6b8e..22742809 100644
--- a/Sources/SQLite/Typed/Query.swift
+++ b/Sources/SQLite/Typed/Query.swift
@@ -1093,7 +1093,7 @@ extension Connection {
         let expression = query.expression
         return try sync {
             try self.run(expression.template, expression.bindings)
-            return self.lastInsertRowid
+            return lastInsertRowid
         }
     }
 
@@ -1109,7 +1109,7 @@ extension Connection {
         let expression = query.expression
         return try sync {
             try self.run(expression.template, expression.bindings)
-            return self.changes
+            return changes
         }
     }
 
@@ -1124,7 +1124,7 @@ extension Connection {
         let expression = query.expression
         return try sync {
             try self.run(expression.template, expression.bindings)
-            return self.changes
+            return changes
         }
     }
 
@@ -1244,7 +1244,7 @@ public struct QueryClauses {
     var union = [QueryType]()
 
     fileprivate init(_ name: String, alias: String?, database: String?) {
-        self.from = (name, alias, database)
+        from = (name, alias, database)
     }
 
 }
diff --git a/Sources/SQLite/Typed/Setter.swift b/Sources/SQLite/Typed/Setter.swift
index 25e88342..08fb1748 100644
--- a/Sources/SQLite/Typed/Setter.swift
+++ b/Sources/SQLite/Typed/Setter.swift
@@ -63,7 +63,7 @@ public struct Setter {
     init(excluded column: Expressible) {
         let excluded = Expression<Void>("excluded")
         self.column = column
-        self.value = ".".join([excluded, column.expression])
+        value = ".".join([excluded, column.expression])
     }
 }
 
diff --git a/Tests/SQLiteTests/ConnectionTests.swift b/Tests/SQLiteTests/ConnectionTests.swift
index 00fe4f6b..44c2c302 100644
--- a/Tests/SQLiteTests/ConnectionTests.swift
+++ b/Tests/SQLiteTests/ConnectionTests.swift
@@ -217,7 +217,7 @@ class ConnectionTests: SQLiteTestCase {
     }
 
     func test_savepoint_beginsAndCommitsSavepoints() {
-        let db: Connection = self.db
+        let db: Connection = db
 
         try! db.savepoint("1") {
             try db.savepoint("2") {
@@ -235,7 +235,7 @@ class ConnectionTests: SQLiteTestCase {
     }
 
     func test_savepoint_beginsAndRollsSavepointsBack() {
-        let db: Connection = self.db
+        let db: Connection = db
         let stmt = try! db.prepare("INSERT INTO users (email) VALUES (?)", "alice@example.com")
 
         do {
@@ -310,7 +310,7 @@ class ConnectionTests: SQLiteTestCase {
                 done()
             }
             try! db.transaction {
-                try self.insertUser("alice")
+                try insertUser("alice")
             }
             XCTAssertEqual(1, try! db.scalar("SELECT count(*) FROM users") as? Int64)
         }
@@ -321,8 +321,8 @@ class ConnectionTests: SQLiteTestCase {
             db.rollbackHook(done)
             do {
                 try db.transaction {
-                    try self.insertUser("alice")
-                    try self.insertUser("alice") // throw
+                    try insertUser("alice")
+                    try insertUser("alice") // throw
                 }
             } catch {
             }
@@ -338,7 +338,7 @@ class ConnectionTests: SQLiteTestCase {
             db.rollbackHook(done)
             do {
                 try db.transaction {
-                    try self.insertUser("alice")
+                    try insertUser("alice")
                 }
             } catch {
             }
@@ -437,7 +437,7 @@ class ResultTests: XCTestCase {
             XCTAssertEqual("not an error", message)
             XCTAssertEqual(SQLITE_MISUSE, code)
             XCTAssertNil(statement)
-            XCTAssert(self.connection === connection)
+            XCTAssert(connection === connection)
         } else {
             XCTFail("no error")
         }

From e2afa8b6bf00728ab275af6be155a4f7b3b16a6e Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Mon, 23 Aug 2021 10:48:13 +0200
Subject: [PATCH 049/326] Simplify

---
 Sources/SQLite/Core/Statement.swift | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Sources/SQLite/Core/Statement.swift b/Sources/SQLite/Core/Statement.swift
index 4332cb49..1f9afbc6 100644
--- a/Sources/SQLite/Core/Statement.swift
+++ b/Sources/SQLite/Core/Statement.swift
@@ -305,7 +305,7 @@ extension Cursor: Sequence {
         var idx = 0
         return AnyIterator {
             if idx >= columnCount {
-                return Binding??.none
+                return .none
             } else {
                 idx += 1
                 return self[idx - 1]

From aebb9c590a469f714b334a9fd2ce84c45475f464 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Mon, 23 Aug 2021 11:01:21 +0200
Subject: [PATCH 050/326] Remove unnecessary returns

---
 Sources/SQLite/Core/Blob.swift                |   6 +-
 Sources/SQLite/Core/Connection.swift          |  30 +-
 Sources/SQLite/Core/Statement.swift           |  28 +-
 Sources/SQLite/Core/Value.swift               |  24 +-
 Sources/SQLite/Extensions/Cipher.swift        |   2 +-
 Sources/SQLite/Extensions/FTS4.swift          |  32 +-
 Sources/SQLite/Extensions/FTS5.swift          |   4 +-
 Sources/SQLite/Foundation.swift               |  20 +-
 Sources/SQLite/Helpers.swift                  |  12 +-
 Sources/SQLite/Typed/AggregateFunctions.swift |  34 +-
 Sources/SQLite/Typed/Coding.swift             |  26 +-
 Sources/SQLite/Typed/Collation.swift          |   2 +-
 Sources/SQLite/Typed/CoreFunctions.swift      |  62 +--
 .../SQLite/Typed/DateAndTimeFunctions.swift   |  24 +-
 Sources/SQLite/Typed/Expression.swift         |  14 +-
 Sources/SQLite/Typed/Operators.swift          | 368 +++++++++---------
 Sources/SQLite/Typed/Query.swift              | 110 +++---
 Sources/SQLite/Typed/Schema.swift             |  42 +-
 Sources/SQLite/Typed/Setter.swift             | 130 +++----
 Tests/SQLiteTests/ConnectionTests.swift       |   4 +-
 Tests/SQLiteTests/CustomFunctionsTests.swift  |  28 +-
 Tests/SQLiteTests/FTS4Tests.swift             |   2 +-
 Tests/SQLiteTests/FTS5Tests.swift             |   2 +-
 Tests/SQLiteTests/TestHelpers.swift           |   6 +-
 24 files changed, 506 insertions(+), 506 deletions(-)

diff --git a/Sources/SQLite/Core/Blob.swift b/Sources/SQLite/Core/Blob.swift
index a49ef23b..cd31483b 100644
--- a/Sources/SQLite/Core/Blob.swift
+++ b/Sources/SQLite/Core/Blob.swift
@@ -36,7 +36,7 @@ public struct Blob {
     }
 
     public func toHex() -> String {
-        return bytes.map {
+        bytes.map {
             ($0 < 16 ? "0" : "") + String($0, radix: 16, uppercase: false)
         }.joined(separator: "")
     }
@@ -46,7 +46,7 @@ public struct Blob {
 extension Blob: CustomStringConvertible {
 
     public var description: String {
-        return "x'\(toHex())'"
+        "x'\(toHex())'"
     }
 
 }
@@ -56,5 +56,5 @@ extension Blob: Equatable {
 }
 
 public func ==(lhs: Blob, rhs: Blob) -> Bool {
-    return lhs.bytes == rhs.bytes
+    lhs.bytes == rhs.bytes
 }
diff --git a/Sources/SQLite/Core/Connection.swift b/Sources/SQLite/Core/Connection.swift
index 2ba51d80..07c85373 100644
--- a/Sources/SQLite/Core/Connection.swift
+++ b/Sources/SQLite/Core/Connection.swift
@@ -84,7 +84,7 @@ public final class Connection {
         }
     }
 
-    public var handle: OpaquePointer { return _handle! }
+    public var handle: OpaquePointer { _handle! }
 
     fileprivate var _handle: OpaquePointer?
 
@@ -133,23 +133,23 @@ public final class Connection {
     // MARK: -
 
     /// Whether or not the database was opened in a read-only state.
-    public var readonly: Bool { return sqlite3_db_readonly(handle, nil) == 1 }
+    public var readonly: Bool { sqlite3_db_readonly(handle, nil) == 1 }
 
     /// The last rowid inserted into the database via this connection.
     public var lastInsertRowid: Int64 {
-        return sqlite3_last_insert_rowid(handle)
+        sqlite3_last_insert_rowid(handle)
     }
 
     /// The last number of changes (inserts, updates, or deletes) made to the
     /// database via this connection.
     public var changes: Int {
-        return Int(sqlite3_changes(handle))
+        Int(sqlite3_changes(handle))
     }
 
     /// The total number of changes (inserts, updates, or deletes) made to the
     /// database via this connection.
     public var totalChanges: Int {
-        return Int(sqlite3_total_changes(handle))
+        Int(sqlite3_total_changes(handle))
     }
 
     // MARK: - Execute
@@ -190,7 +190,7 @@ public final class Connection {
     ///
     /// - Returns: A prepared statement.
     public func prepare(_ statement: String, _ bindings: [Binding?]) throws -> Statement {
-        return try prepare(statement).bind(bindings)
+        try prepare(statement).bind(bindings)
     }
 
     /// Prepares a single SQL statement and binds parameters to it.
@@ -203,7 +203,7 @@ public final class Connection {
     ///
     /// - Returns: A prepared statement.
     public func prepare(_ statement: String, _ bindings: [String: Binding?]) throws -> Statement {
-        return try prepare(statement).bind(bindings)
+        try prepare(statement).bind(bindings)
     }
 
     // MARK: - Run
@@ -220,7 +220,7 @@ public final class Connection {
     ///
     /// - Returns: The statement.
     @discardableResult public func run(_ statement: String, _ bindings: Binding?...) throws -> Statement {
-        return try run(statement, bindings)
+        try run(statement, bindings)
     }
 
     /// Prepares, binds, and runs a single SQL statement.
@@ -235,7 +235,7 @@ public final class Connection {
     ///
     /// - Returns: The statement.
     @discardableResult public func run(_ statement: String, _ bindings: [Binding?]) throws -> Statement {
-        return try prepare(statement).run(bindings)
+        try prepare(statement).run(bindings)
     }
 
     /// Prepares, binds, and runs a single SQL statement.
@@ -250,7 +250,7 @@ public final class Connection {
     ///
     /// - Returns: The statement.
     @discardableResult public func run(_ statement: String, _ bindings: [String: Binding?]) throws -> Statement {
-        return try prepare(statement).run(bindings)
+        try prepare(statement).run(bindings)
     }
 
     // MARK: - VACUUM
@@ -261,7 +261,7 @@ public final class Connection {
     ///
     /// - Returns: The statement.
     @discardableResult public func vacuum() throws -> Statement {
-        return try run("VACUUM")
+        try run("VACUUM")
     }
 
     // MARK: - Scalar
@@ -277,7 +277,7 @@ public final class Connection {
     ///
     /// - Returns: The first value of the first row returned.
     public func scalar(_ statement: String, _ bindings: Binding?...) throws -> Binding? {
-        return try scalar(statement, bindings)
+        try scalar(statement, bindings)
     }
 
     /// Runs a single SQL statement (with optional parameter bindings),
@@ -291,7 +291,7 @@ public final class Connection {
     ///
     /// - Returns: The first value of the first row returned.
     public func scalar(_ statement: String, _ bindings: [Binding?]) throws -> Binding? {
-        return try prepare(statement).scalar(bindings)
+        try prepare(statement).scalar(bindings)
     }
 
     /// Runs a single SQL statement (with optional parameter bindings),
@@ -305,7 +305,7 @@ public final class Connection {
     ///
     /// - Returns: The first value of the first row returned.
     public func scalar(_ statement: String, _ bindings: [String: Binding?]) throws -> Binding? {
-        return try prepare(statement).scalar(bindings)
+        try prepare(statement).scalar(bindings)
     }
 
     // MARK: - Transactions
@@ -810,7 +810,7 @@ public final class Connection {
 extension Connection: CustomStringConvertible {
 
     public var description: String {
-        return String(cString: sqlite3_db_filename(handle, nil))
+        String(cString: sqlite3_db_filename(handle, nil))
     }
 
 }
diff --git a/Sources/SQLite/Core/Statement.swift b/Sources/SQLite/Core/Statement.swift
index 1f9afbc6..48347c31 100644
--- a/Sources/SQLite/Core/Statement.swift
+++ b/Sources/SQLite/Core/Statement.swift
@@ -63,7 +63,7 @@ public final class Statement {
     ///
     /// - Returns: The statement object (useful for chaining).
     public func bind(_ values: Binding?...) -> Statement {
-        return bind(values)
+        bind(values)
     }
 
     /// Binds a list of parameters to a statement.
@@ -140,7 +140,7 @@ public final class Statement {
     ///
     /// - Returns: The statement object (useful for chaining).
     @discardableResult public func run(_ bindings: [Binding?]) throws -> Statement {
-        return try bind(bindings).run()
+        try bind(bindings).run()
     }
 
     /// - Parameter bindings: A dictionary of named parameters to bind to the
@@ -150,7 +150,7 @@ public final class Statement {
     ///
     /// - Returns: The statement object (useful for chaining).
     @discardableResult public func run(_ bindings: [String: Binding?]) throws -> Statement {
-        return try bind(bindings).run()
+        try bind(bindings).run()
     }
 
     /// - Parameter bindings: A list of parameters to bind to the statement.
@@ -170,7 +170,7 @@ public final class Statement {
     ///
     /// - Returns: The first value of the first row returned.
     public func scalar(_ bindings: [Binding?]) throws -> Binding? {
-        return try bind(bindings).scalar()
+        try bind(bindings).scalar()
     }
 
     /// - Parameter bindings: A dictionary of named parameters to bind to the
@@ -178,11 +178,11 @@ public final class Statement {
     ///
     /// - Returns: The first value of the first row returned.
     public func scalar(_ bindings: [String: Binding?]) throws -> Binding? {
-        return try bind(bindings).scalar()
+        try bind(bindings).scalar()
     }
 
     public func step() throws -> Bool {
-        return try connection.sync { try connection.check(sqlite3_step(handle)) == SQLITE_ROW }
+        try connection.sync { try connection.check(sqlite3_step(handle)) == SQLITE_ROW }
     }
 
     fileprivate func reset(clearBindings shouldClear: Bool = true) {
@@ -207,7 +207,7 @@ public protocol FailableIterator: IteratorProtocol {
 
 extension FailableIterator {
     public func next() -> Element? {
-        return try? failableNext()
+        try? failableNext()
     }
 }
 
@@ -223,14 +223,14 @@ extension Array {
 extension Statement: FailableIterator {
     public typealias Element = [Binding?]
     public func failableNext() throws -> [Binding?]? {
-        return try step() ? Array(row) : nil
+        try step() ? Array(row) : nil
     }
 }
 
 extension Statement: CustomStringConvertible {
 
     public var description: String {
-        return String(cString: sqlite3_sql(handle))
+        String(cString: sqlite3_sql(handle))
     }
 
 }
@@ -247,15 +247,15 @@ public struct Cursor {
     }
 
     public subscript(idx: Int) -> Double {
-        return sqlite3_column_double(handle, Int32(idx))
+        sqlite3_column_double(handle, Int32(idx))
     }
 
     public subscript(idx: Int) -> Int64 {
-        return sqlite3_column_int64(handle, Int32(idx))
+        sqlite3_column_int64(handle, Int32(idx))
     }
 
     public subscript(idx: Int) -> String {
-        return String(cString: UnsafePointer(sqlite3_column_text(handle, Int32(idx))))
+        String(cString: UnsafePointer(sqlite3_column_text(handle, Int32(idx))))
     }
 
     public subscript(idx: Int) -> Blob {
@@ -272,11 +272,11 @@ public struct Cursor {
     // MARK: -
 
     public subscript(idx: Int) -> Bool {
-        return Bool.fromDatatypeValue(self[idx])
+        Bool.fromDatatypeValue(self[idx])
     }
 
     public subscript(idx: Int) -> Int {
-        return Int.fromDatatypeValue(self[idx])
+        Int.fromDatatypeValue(self[idx])
     }
 
 }
diff --git a/Sources/SQLite/Core/Value.swift b/Sources/SQLite/Core/Value.swift
index b8686eab..9c463f0c 100644
--- a/Sources/SQLite/Core/Value.swift
+++ b/Sources/SQLite/Core/Value.swift
@@ -50,11 +50,11 @@ extension Double: Number, Value {
     public static let declaredDatatype = "REAL"
 
     public static func fromDatatypeValue(_ datatypeValue: Double) -> Double {
-        return datatypeValue
+        datatypeValue
     }
 
     public var datatypeValue: Double {
-        return self
+        self
     }
 
 }
@@ -64,11 +64,11 @@ extension Int64: Number, Value {
     public static let declaredDatatype = "INTEGER"
 
     public static func fromDatatypeValue(_ datatypeValue: Int64) -> Int64 {
-        return datatypeValue
+        datatypeValue
     }
 
     public var datatypeValue: Int64 {
-        return self
+        self
     }
 
 }
@@ -78,11 +78,11 @@ extension String: Binding, Value {
     public static let declaredDatatype = "TEXT"
 
     public static func fromDatatypeValue(_ datatypeValue: String) -> String {
-        return datatypeValue
+        datatypeValue
     }
 
     public var datatypeValue: String {
-        return self
+        self
     }
 
 }
@@ -92,11 +92,11 @@ extension Blob: Binding, Value {
     public static let declaredDatatype = "BLOB"
 
     public static func fromDatatypeValue(_ datatypeValue: Blob) -> Blob {
-        return datatypeValue
+        datatypeValue
     }
 
     public var datatypeValue: Blob {
-        return self
+        self
     }
 
 }
@@ -108,11 +108,11 @@ extension Bool: Binding, Value {
     public static var declaredDatatype = Int64.declaredDatatype
 
     public static func fromDatatypeValue(_ datatypeValue: Int64) -> Bool {
-        return datatypeValue != 0
+        datatypeValue != 0
     }
 
     public var datatypeValue: Int64 {
-        return self ? 1 : 0
+        self ? 1 : 0
     }
 
 }
@@ -122,11 +122,11 @@ extension Int: Number, Value {
     public static var declaredDatatype = Int64.declaredDatatype
 
     public static func fromDatatypeValue(_ datatypeValue: Int64) -> Int {
-        return Int(datatypeValue)
+        Int(datatypeValue)
     }
 
     public var datatypeValue: Int64 {
-        return Int64(self)
+        Int64(self)
     }
 
 }
diff --git a/Sources/SQLite/Extensions/Cipher.swift b/Sources/SQLite/Extensions/Cipher.swift
index 9fce42f7..25d20158 100644
--- a/Sources/SQLite/Extensions/Cipher.swift
+++ b/Sources/SQLite/Extensions/Cipher.swift
@@ -7,7 +7,7 @@ extension Connection {
 
     /// - Returns: the SQLCipher version
     public var cipherVersion: String? {
-        return (try? scalar("PRAGMA cipher_version")) as? String
+        (try? scalar("PRAGMA cipher_version")) as? String
     }
 
     /// Specify the key for an encrypted database.  This routine should be
diff --git a/Sources/SQLite/Extensions/FTS4.swift b/Sources/SQLite/Extensions/FTS4.swift
index 1de03ff2..115b146a 100644
--- a/Sources/SQLite/Extensions/FTS4.swift
+++ b/Sources/SQLite/Extensions/FTS4.swift
@@ -29,15 +29,15 @@ import SQLiteObjc
 extension Module {
 
     public static func FTS4(_ column: Expressible, _ more: Expressible...) -> Module {
-        return FTS4([column] + more)
+        FTS4([column] + more)
     }
 
     public static func FTS4(_ columns: [Expressible] = [], tokenize tokenizer: Tokenizer? = nil) -> Module {
-        return FTS4(FTS4Config().columns(columns).tokenizer(tokenizer))
+        FTS4(FTS4Config().columns(columns).tokenizer(tokenizer))
     }
 
     public static func FTS4(_ config: FTS4Config) -> Module {
-        return Module(name: "fts4", arguments: config.arguments())
+        Module(name: "fts4", arguments: config.arguments())
     }
 }
 
@@ -56,15 +56,15 @@ extension VirtualTable {
     /// - Returns: An expression appended with a `MATCH` query against the given
     ///   pattern.
     public func match(_ pattern: String) -> Expression<Bool> {
-        return "MATCH".infix(tableName(), pattern)
+        "MATCH".infix(tableName(), pattern)
     }
 
     public func match(_ pattern: Expression<String>) -> Expression<Bool> {
-        return "MATCH".infix(tableName(), pattern)
+        "MATCH".infix(tableName(), pattern)
     }
 
     public func match(_ pattern: Expression<String?>) -> Expression<Bool?> {
-        return "MATCH".infix(tableName(), pattern)
+        "MATCH".infix(tableName(), pattern)
     }
 
     /// Builds a copy of the query with a `WHERE … MATCH` clause.
@@ -78,15 +78,15 @@ extension VirtualTable {
     ///
     /// - Returns: A query with the given `WHERE … MATCH` clause applied.
     public func match(_ pattern: String) -> QueryType {
-        return filter(match(pattern))
+        filter(match(pattern))
     }
 
     public func match(_ pattern: Expression<String>) -> QueryType {
-        return filter(match(pattern))
+        filter(match(pattern))
     }
 
     public func match(_ pattern: Expression<String?>) -> QueryType {
-        return filter(match(pattern))
+        filter(match(pattern))
     }
 
 }
@@ -120,7 +120,7 @@ public struct Tokenizer {
     }
 
     public static func Custom(_ name: String) -> Tokenizer {
-        return Tokenizer(Tokenizer.moduleName.quote(), [name.quote()])
+        Tokenizer(Tokenizer.moduleName.quote(), [name.quote()])
     }
 
     public let name: String
@@ -139,7 +139,7 @@ public struct Tokenizer {
 extension Tokenizer: CustomStringConvertible {
 
     public var description: String {
-        return ([name] + arguments).joined(separator: " ")
+        ([name] + arguments).joined(separator: " ")
     }
 
 }
@@ -220,11 +220,11 @@ open class FTSConfig {
     }
 
     func formatColumnDefinitions() -> [Expressible] {
-        return columnDefinitions.map { $0.0 }
+        columnDefinitions.map { $0.0 }
     }
 
     func arguments() -> [Expressible] {
-        return options().arguments
+        options().arguments
     }
 
     func options() -> Options {
@@ -259,11 +259,11 @@ open class FTSConfig {
         }
 
         @discardableResult mutating func append(_ key: String, value: CustomStringConvertible?) -> Options {
-            return append(key, value: value?.description)
+            append(key, value: value?.description)
         }
 
         @discardableResult mutating func append(_ key: String, value: String?) -> Options {
-            return append(key, value: value.map { Expression<String>($0) })
+            append(key, value: value.map { Expression<String>($0) })
         }
 
         @discardableResult mutating func append(_ key: String, value: Expressible?) -> Options {
@@ -281,7 +281,7 @@ open class FTS4Config: FTSConfig {
     public enum MatchInfo: CustomStringConvertible {
         case fts3
         public var description: String {
-            return "fts3"
+            "fts3"
         }
     }
 
diff --git a/Sources/SQLite/Extensions/FTS5.swift b/Sources/SQLite/Extensions/FTS5.swift
index a5d04dbf..f108bbec 100644
--- a/Sources/SQLite/Extensions/FTS5.swift
+++ b/Sources/SQLite/Extensions/FTS5.swift
@@ -24,7 +24,7 @@
 
 extension Module {
     public static func FTS5(_ config: FTS5Config) -> Module {
-        return Module(name: "fts5", arguments: config.arguments())
+        Module(name: "fts5", arguments: config.arguments())
     }
 }
 
@@ -86,7 +86,7 @@ open class FTS5Config: FTSConfig {
     }
 
     override func formatColumnDefinitions() -> [Expressible] {
-        return columnDefinitions.map { definition in
+        columnDefinitions.map { definition in
             if definition.options.contains(.unindexed) {
                 return " ".join([definition.0, Expression<Void>(literal: "UNINDEXED")])
             } else {
diff --git a/Sources/SQLite/Foundation.swift b/Sources/SQLite/Foundation.swift
index 81e89d48..2acbc00e 100644
--- a/Sources/SQLite/Foundation.swift
+++ b/Sources/SQLite/Foundation.swift
@@ -27,16 +27,16 @@ import Foundation
 extension Data: Value {
 
     public static var declaredDatatype: String {
-        return Blob.declaredDatatype
+        Blob.declaredDatatype
     }
 
     public static func fromDatatypeValue(_ dataValue: Blob) -> Data {
-        return Data(dataValue.bytes)
+        Data(dataValue.bytes)
     }
 
     public var datatypeValue: Blob {
-        return withUnsafeBytes { (pointer: UnsafeRawBufferPointer) -> Blob in
-            return Blob(bytes: pointer.baseAddress!, length: count)
+        withUnsafeBytes { (pointer: UnsafeRawBufferPointer) -> Blob in
+            Blob(bytes: pointer.baseAddress!, length: count)
         }
     }
 
@@ -45,15 +45,15 @@ extension Data: Value {
 extension Date: Value {
 
     public static var declaredDatatype: String {
-        return String.declaredDatatype
+        String.declaredDatatype
     }
 
     public static func fromDatatypeValue(_ stringValue: String) -> Date {
-        return dateFormatter.date(from: stringValue)!
+        dateFormatter.date(from: stringValue)!
     }
 
     public var datatypeValue: String {
-        return dateFormatter.string(from: self)
+        dateFormatter.string(from: self)
     }
 
 }
@@ -72,15 +72,15 @@ public var dateFormatter: DateFormatter = {
 extension UUID: Value {
 
     public static var declaredDatatype: String {
-        return String.declaredDatatype
+        String.declaredDatatype
     }
 
     public static func fromDatatypeValue(_ stringValue: String) -> UUID {
-        return UUID(uuidString: stringValue)!
+        UUID(uuidString: stringValue)!
     }
 
     public var datatypeValue: String {
-        return uuidString
+        uuidString
     }
 
 }
diff --git a/Sources/SQLite/Helpers.swift b/Sources/SQLite/Helpers.swift
index f5c67e36..d4c6828e 100644
--- a/Sources/SQLite/Helpers.swift
+++ b/Sources/SQLite/Helpers.swift
@@ -35,7 +35,7 @@ import SQLite3
 public typealias Star = (Expression<Binding>?, Expression<Binding>?) -> Expression<Void>
 
 public func *(_: Expression<Binding>?, _: Expression<Binding>?) -> Expression<Void> {
-    return Expression(literal: "*")
+    Expression(literal: "*")
 }
 
 public protocol _OptionalType {
@@ -73,7 +73,7 @@ extension String {
     }
 
     func infix<T>(_ lhs: Expressible, _ rhs: Expressible, wrap: Bool = true) -> Expression<T> {
-        return infix([lhs, rhs], wrap: wrap)
+        infix([lhs, rhs], wrap: wrap)
     }
 
     func infix<T>(_ terms: [Expressible], wrap: Bool = true) -> Expression<T> {
@@ -85,19 +85,19 @@ extension String {
     }
 
     func prefix(_ expressions: Expressible) -> Expressible {
-        return "\(self) ".wrap(expressions) as Expression<Void>
+        "\(self) ".wrap(expressions) as Expression<Void>
     }
 
     func prefix(_ expressions: [Expressible]) -> Expressible {
-        return "\(self) ".wrap(expressions) as Expression<Void>
+        "\(self) ".wrap(expressions) as Expression<Void>
     }
 
     func wrap<T>(_ expression: Expressible) -> Expression<T> {
-        return Expression("\(self)(\(expression.expression.template))", expression.expression.bindings)
+        Expression("\(self)(\(expression.expression.template))", expression.expression.bindings)
     }
 
     func wrap<T>(_ expressions: [Expressible]) -> Expression<T> {
-        return wrap(", ".join(expressions))
+        wrap(", ".join(expressions))
     }
 
 }
diff --git a/Sources/SQLite/Typed/AggregateFunctions.swift b/Sources/SQLite/Typed/AggregateFunctions.swift
index 325b0277..bf4fb8fc 100644
--- a/Sources/SQLite/Typed/AggregateFunctions.swift
+++ b/Sources/SQLite/Typed/AggregateFunctions.swift
@@ -31,7 +31,7 @@ private enum Function: String {
     case total
 
     func wrap<T>(_ expression: Expressible) -> Expression<T> {
-        return self.rawValue.wrap(expression)
+        self.rawValue.wrap(expression)
     }
 }
 
@@ -46,7 +46,7 @@ extension ExpressionType where UnderlyingType: Value {
     /// - Returns: A copy of the expression prefixed with the `DISTINCT`
     ///   keyword.
     public var distinct: Expression<UnderlyingType> {
-        return Expression("DISTINCT \(template)", bindings)
+        Expression("DISTINCT \(template)", bindings)
     }
 
     /// Builds a copy of the expression wrapped with the `count` aggregate
@@ -61,7 +61,7 @@ extension ExpressionType where UnderlyingType: Value {
     /// - Returns: A copy of the expression wrapped with the `count` aggregate
     ///   function.
     public var count: Expression<Int> {
-        return Function.count.wrap(self)
+        Function.count.wrap(self)
     }
 
 }
@@ -77,7 +77,7 @@ extension ExpressionType where UnderlyingType: _OptionalType, UnderlyingType.Wra
     /// - Returns: A copy of the expression prefixed with the `DISTINCT`
     ///   keyword.
     public var distinct: Expression<UnderlyingType> {
-        return Expression("DISTINCT \(template)", bindings)
+        Expression("DISTINCT \(template)", bindings)
     }
 
     /// Builds a copy of the expression wrapped with the `count` aggregate
@@ -92,7 +92,7 @@ extension ExpressionType where UnderlyingType: _OptionalType, UnderlyingType.Wra
     /// - Returns: A copy of the expression wrapped with the `count` aggregate
     ///   function.
     public var count: Expression<Int> {
-        return Function.count.wrap(self)
+        Function.count.wrap(self)
     }
 
 }
@@ -109,7 +109,7 @@ extension ExpressionType where UnderlyingType: Value, UnderlyingType.Datatype: C
     /// - Returns: A copy of the expression wrapped with the `max` aggregate
     ///   function.
     public var max: Expression<UnderlyingType?> {
-        return Function.max.wrap(self)
+        Function.max.wrap(self)
     }
 
     /// Builds a copy of the expression wrapped with the `min` aggregate
@@ -122,7 +122,7 @@ extension ExpressionType where UnderlyingType: Value, UnderlyingType.Datatype: C
     /// - Returns: A copy of the expression wrapped with the `min` aggregate
     ///   function.
     public var min: Expression<UnderlyingType?> {
-        return Function.min.wrap(self)
+        Function.min.wrap(self)
     }
 
 }
@@ -139,7 +139,7 @@ extension ExpressionType where UnderlyingType: _OptionalType, UnderlyingType.Wra
     /// - Returns: A copy of the expression wrapped with the `max` aggregate
     ///   function.
     public var max: Expression<UnderlyingType> {
-        return Function.max.wrap(self)
+        Function.max.wrap(self)
     }
 
     /// Builds a copy of the expression wrapped with the `min` aggregate
@@ -152,7 +152,7 @@ extension ExpressionType where UnderlyingType: _OptionalType, UnderlyingType.Wra
     /// - Returns: A copy of the expression wrapped with the `min` aggregate
     ///   function.
     public var min: Expression<UnderlyingType> {
-        return Function.min.wrap(self)
+        Function.min.wrap(self)
     }
 
 }
@@ -169,7 +169,7 @@ extension ExpressionType where UnderlyingType: Value, UnderlyingType.Datatype: N
     /// - Returns: A copy of the expression wrapped with the `min` aggregate
     ///   function.
     public var average: Expression<Double?> {
-        return Function.avg.wrap(self)
+        Function.avg.wrap(self)
     }
 
     /// Builds a copy of the expression wrapped with the `sum` aggregate
@@ -182,7 +182,7 @@ extension ExpressionType where UnderlyingType: Value, UnderlyingType.Datatype: N
     /// - Returns: A copy of the expression wrapped with the `min` aggregate
     ///   function.
     public var sum: Expression<UnderlyingType?> {
-        return Function.sum.wrap(self)
+        Function.sum.wrap(self)
     }
 
     /// Builds a copy of the expression wrapped with the `total` aggregate
@@ -195,7 +195,7 @@ extension ExpressionType where UnderlyingType: Value, UnderlyingType.Datatype: N
     /// - Returns: A copy of the expression wrapped with the `min` aggregate
     ///   function.
     public var total: Expression<Double> {
-        return Function.total.wrap(self)
+        Function.total.wrap(self)
     }
 
 }
@@ -212,7 +212,7 @@ extension ExpressionType where UnderlyingType: _OptionalType, UnderlyingType.Wra
     /// - Returns: A copy of the expression wrapped with the `min` aggregate
     ///   function.
     public var average: Expression<Double?> {
-        return Function.avg.wrap(self)
+        Function.avg.wrap(self)
     }
 
     /// Builds a copy of the expression wrapped with the `sum` aggregate
@@ -225,7 +225,7 @@ extension ExpressionType where UnderlyingType: _OptionalType, UnderlyingType.Wra
     /// - Returns: A copy of the expression wrapped with the `min` aggregate
     ///   function.
     public var sum: Expression<UnderlyingType> {
-        return Function.sum.wrap(self)
+        Function.sum.wrap(self)
     }
 
     /// Builds a copy of the expression wrapped with the `total` aggregate
@@ -238,7 +238,7 @@ extension ExpressionType where UnderlyingType: _OptionalType, UnderlyingType.Wra
     /// - Returns: A copy of the expression wrapped with the `min` aggregate
     ///   function.
     public var total: Expression<Double> {
-        return Function.total.wrap(self)
+        Function.total.wrap(self)
     }
 
 }
@@ -246,7 +246,7 @@ extension ExpressionType where UnderlyingType: _OptionalType, UnderlyingType.Wra
 extension ExpressionType where UnderlyingType == Int {
 
     static func count(_ star: Star) -> Expression<UnderlyingType> {
-        return Function.count.wrap(star(nil, nil))
+        Function.count.wrap(star(nil, nil))
     }
 
 }
@@ -260,5 +260,5 @@ extension ExpressionType where UnderlyingType == Int {
 /// - Returns: An expression returning `count(*)` (when called with the `*`
 ///   function literal).
 public func count(_ star: Star) -> Expression<Int> {
-    return Expression.count(star)
+    Expression.count(star)
 }
diff --git a/Sources/SQLite/Typed/Coding.swift b/Sources/SQLite/Typed/Coding.swift
index d2df2fe6..3dc1e6cf 100644
--- a/Sources/SQLite/Typed/Coding.swift
+++ b/Sources/SQLite/Typed/Coding.swift
@@ -149,11 +149,11 @@ extension Row {
     ///
     /// - Returns: a decoded object from this row
     public func decode<V: Decodable>(userInfo: [CodingUserInfoKey: Any] = [:]) throws -> V {
-        return try V(from: decoder(userInfo: userInfo))
+        try V(from: decoder(userInfo: userInfo))
     }
 
     public func decoder(userInfo: [CodingUserInfoKey: Any] = [:]) -> Decoder {
-        return SQLiteDecoder(row: self, userInfo: userInfo)
+        SQLiteDecoder(row: self, userInfo: userInfo)
     }
 }
 
@@ -285,7 +285,7 @@ private class SQLiteEncoder: Encoder {
     }
 
     func container<Key>(keyedBy type: Key.Type) -> KeyedEncodingContainer<Key> where Key: CodingKey {
-        return KeyedEncodingContainer(SQLiteKeyedEncodingContainer(encoder: self))
+        KeyedEncodingContainer(SQLiteKeyedEncodingContainer(encoder: self))
     }
 }
 
@@ -301,23 +301,23 @@ private class SQLiteDecoder: Decoder {
         }
 
         var allKeys: [Key] {
-            return row.columnNames.keys.compactMap({Key(stringValue: $0)})
+            row.columnNames.keys.compactMap({ Key(stringValue: $0) })
         }
 
         func contains(_ key: Key) -> Bool {
-            return row.hasValue(for: key.stringValue)
+            row.hasValue(for: key.stringValue)
         }
 
         func decodeNil(forKey key: Key) throws -> Bool {
-            return !contains(key)
+            !contains(key)
         }
 
         func decode(_ type: Bool.Type, forKey key: Key) throws -> Bool {
-            return try row.get(Expression(key.stringValue))
+            try row.get(Expression(key.stringValue))
         }
 
         func decode(_ type: Int.Type, forKey key: Key) throws -> Int {
-            return try row.get(Expression(key.stringValue))
+            try row.get(Expression(key.stringValue))
         }
 
         func decode(_ type: Int8.Type, forKey key: Key) throws -> Int8 {
@@ -336,7 +336,7 @@ private class SQLiteDecoder: Decoder {
         }
 
         func decode(_ type: Int64.Type, forKey key: Key) throws -> Int64 {
-            return try row.get(Expression(key.stringValue))
+            try row.get(Expression(key.stringValue))
         }
 
         func decode(_ type: UInt.Type, forKey key: Key) throws -> UInt {
@@ -366,15 +366,15 @@ private class SQLiteDecoder: Decoder {
         }
 
         func decode(_ type: Float.Type, forKey key: Key) throws -> Float {
-            return Float(try row.get(Expression<Double>(key.stringValue)))
+            Float(try row.get(Expression<Double>(key.stringValue)))
         }
 
         func decode(_ type: Double.Type, forKey key: Key) throws -> Double {
-            return try row.get(Expression(key.stringValue))
+            try row.get(Expression(key.stringValue))
         }
 
         func decode(_ type: String.Type, forKey key: Key) throws -> String {
-            return try row.get(Expression(key.stringValue))
+            try row.get(Expression(key.stringValue))
         }
 
         func decode<T>(_ type: T.Type, forKey key: Key) throws -> T where T: Swift.Decodable {
@@ -430,7 +430,7 @@ private class SQLiteDecoder: Decoder {
     }
 
     func container<Key>(keyedBy type: Key.Type) throws -> KeyedDecodingContainer<Key> where Key: CodingKey {
-        return KeyedDecodingContainer(SQLiteKeyedDecodingContainer(row: row))
+        KeyedDecodingContainer(SQLiteKeyedDecodingContainer(row: row))
     }
 
     func unkeyedContainer() throws -> UnkeyedDecodingContainer {
diff --git a/Sources/SQLite/Typed/Collation.swift b/Sources/SQLite/Typed/Collation.swift
index 3b268ce0..fec66129 100644
--- a/Sources/SQLite/Typed/Collation.swift
+++ b/Sources/SQLite/Typed/Collation.swift
@@ -46,7 +46,7 @@ public enum Collation {
 extension Collation: Expressible {
 
     public var expression: Expression<Void> {
-        return Expression(literal: description)
+        Expression(literal: description)
     }
 
 }
diff --git a/Sources/SQLite/Typed/CoreFunctions.swift b/Sources/SQLite/Typed/CoreFunctions.swift
index 42689578..be38d97e 100644
--- a/Sources/SQLite/Typed/CoreFunctions.swift
+++ b/Sources/SQLite/Typed/CoreFunctions.swift
@@ -47,15 +47,15 @@ private enum Function: String {
     case ifnull
 
     func infix<T>(_ lhs: Expressible, _ rhs: Expressible, wrap: Bool = true) -> Expression<T> {
-        return self.rawValue.infix(lhs, rhs, wrap: wrap)
+        self.rawValue.infix(lhs, rhs, wrap: wrap)
     }
 
     func wrap<T>(_ expression: Expressible) -> Expression<T> {
-        return self.rawValue.wrap(expression)
+        self.rawValue.wrap(expression)
     }
 
     func wrap<T>(_ expressions: [Expressible]) -> Expression<T> {
-        return self.rawValue.wrap(", ".join(expressions))
+        self.rawValue.wrap(", ".join(expressions))
     }
 }
 
@@ -69,7 +69,7 @@ extension ExpressionType where UnderlyingType: Number {
     ///
     /// - Returns: A copy of the expression wrapped with the `abs` function.
     public var absoluteValue: Expression<UnderlyingType> {
-        return Function.abs.wrap(self)
+        Function.abs.wrap(self)
     }
 
 }
@@ -84,7 +84,7 @@ extension ExpressionType where UnderlyingType: _OptionalType, UnderlyingType.Wra
     ///
     /// - Returns: A copy of the expression wrapped with the `abs` function.
     public var absoluteValue: Expression<UnderlyingType> {
-        return Function.abs.wrap(self)
+        Function.abs.wrap(self)
     }
 
 }
@@ -138,7 +138,7 @@ extension ExpressionType where UnderlyingType: Value, UnderlyingType.Datatype ==
     ///
     /// - Returns: An expression calling the `random` function.
     public static func random() -> Expression<UnderlyingType> {
-        return Function.random.wrap([])
+        Function.random.wrap([])
     }
 
 }
@@ -154,7 +154,7 @@ extension ExpressionType where UnderlyingType == Data {
     ///
     /// - Returns: An expression calling the `randomblob` function.
     public static func random(_ length: Int) -> Expression<UnderlyingType> {
-        return Function.randomblob.wrap([])
+        Function.randomblob.wrap([])
     }
 
     /// Builds an expression representing the `zeroblob` function.
@@ -166,7 +166,7 @@ extension ExpressionType where UnderlyingType == Data {
     ///
     /// - Returns: An expression calling the `zeroblob` function.
     public static func allZeros(_ length: Int) -> Expression<UnderlyingType> {
-        return Function.zeroblob.wrap([])
+        Function.zeroblob.wrap([])
     }
 
     /// Builds a copy of the expression wrapped with the `length` function.
@@ -177,7 +177,7 @@ extension ExpressionType where UnderlyingType == Data {
     ///
     /// - Returns: A copy of the expression wrapped with the `length` function.
     public var length: Expression<Int> {
-        return Function.length.wrap(self)
+        Function.length.wrap(self)
     }
 
 }
@@ -192,7 +192,7 @@ extension ExpressionType where UnderlyingType == Data? {
     ///
     /// - Returns: A copy of the expression wrapped with the `length` function.
     public var length: Expression<Int?> {
-        return Function.length.wrap(self)
+        Function.length.wrap(self)
     }
 
 }
@@ -207,7 +207,7 @@ extension ExpressionType where UnderlyingType == String {
     ///
     /// - Returns: A copy of the expression wrapped with the `length` function.
     public var length: Expression<Int> {
-        return Function.length.wrap(self)
+        Function.length.wrap(self)
     }
 
     /// Builds a copy of the expression wrapped with the `lower` function.
@@ -218,7 +218,7 @@ extension ExpressionType where UnderlyingType == String {
     ///
     /// - Returns: A copy of the expression wrapped with the `lower` function.
     public var lowercaseString: Expression<UnderlyingType> {
-        return Function.lower.wrap(self)
+        Function.lower.wrap(self)
     }
 
     /// Builds a copy of the expression wrapped with the `upper` function.
@@ -229,7 +229,7 @@ extension ExpressionType where UnderlyingType == String {
     ///
     /// - Returns: A copy of the expression wrapped with the `upper` function.
     public var uppercaseString: Expression<UnderlyingType> {
-        return Function.upper.wrap(self)
+        Function.upper.wrap(self)
     }
 
     /// Builds a copy of the expression appended with a `LIKE` query against the
@@ -294,7 +294,7 @@ extension ExpressionType where UnderlyingType == String {
     /// - Returns: A copy of the expression appended with a `GLOB` query against
     ///   the given pattern.
     public func glob(_ pattern: String) -> Expression<Bool> {
-        return Function.glob.infix(self, pattern)
+        Function.glob.infix(self, pattern)
     }
 
     /// Builds a copy of the expression appended with a `MATCH` query against
@@ -309,7 +309,7 @@ extension ExpressionType where UnderlyingType == String {
     /// - Returns: A copy of the expression appended with a `MATCH` query
     ///   against the given pattern.
     public func match(_ pattern: String) -> Expression<Bool> {
-        return Function.match.infix(self, pattern)
+        Function.match.infix(self, pattern)
     }
 
     /// Builds a copy of the expression appended with a `REGEXP` query against
@@ -320,7 +320,7 @@ extension ExpressionType where UnderlyingType == String {
     /// - Returns: A copy of the expression appended with a `REGEXP` query
     ///   against the given pattern.
     public func regexp(_ pattern: String) -> Expression<Bool> {
-        return Function.regexp.infix(self, pattern)
+        Function.regexp.infix(self, pattern)
     }
 
     /// Builds a copy of the expression appended with a `COLLATE` clause with
@@ -335,7 +335,7 @@ extension ExpressionType where UnderlyingType == String {
     /// - Returns: A copy of the expression appended with a `COLLATE` clause
     ///   with the given sequence.
     public func collate(_ collation: Collation) -> Expression<UnderlyingType> {
-        return Function.collate.infix(self, collation)
+        Function.collate.infix(self, collation)
     }
 
     /// Builds a copy of the expression wrapped with the `ltrim` function.
@@ -406,7 +406,7 @@ extension ExpressionType where UnderlyingType == String {
     ///
     /// - Returns: A copy of the expression wrapped with the `replace` function.
     public func replace(_ pattern: String, with replacement: String) -> Expression<UnderlyingType> {
-        return Function.replace.wrap([self, pattern, replacement])
+        Function.replace.wrap([self, pattern, replacement])
     }
 
     public func substring(_ location: Int, length: Int? = nil) -> Expression<UnderlyingType> {
@@ -417,7 +417,7 @@ extension ExpressionType where UnderlyingType == String {
     }
 
     public subscript(range: Range<Int>) -> Expression<UnderlyingType> {
-        return substring(range.lowerBound, length: range.upperBound - range.lowerBound)
+        substring(range.lowerBound, length: range.upperBound - range.lowerBound)
     }
 
 }
@@ -432,7 +432,7 @@ extension ExpressionType where UnderlyingType == String? {
     ///
     /// - Returns: A copy of the expression wrapped with the `length` function.
     public var length: Expression<Int?> {
-        return Function.length.wrap(self)
+        Function.length.wrap(self)
     }
 
     /// Builds a copy of the expression wrapped with the `lower` function.
@@ -443,7 +443,7 @@ extension ExpressionType where UnderlyingType == String? {
     ///
     /// - Returns: A copy of the expression wrapped with the `lower` function.
     public var lowercaseString: Expression<UnderlyingType> {
-        return Function.lower.wrap(self)
+        Function.lower.wrap(self)
     }
 
     /// Builds a copy of the expression wrapped with the `upper` function.
@@ -454,7 +454,7 @@ extension ExpressionType where UnderlyingType == String? {
     ///
     /// - Returns: A copy of the expression wrapped with the `upper` function.
     public var uppercaseString: Expression<UnderlyingType> {
-        return Function.upper.wrap(self)
+        Function.upper.wrap(self)
     }
 
     /// Builds a copy of the expression appended with a `LIKE` query against the
@@ -519,7 +519,7 @@ extension ExpressionType where UnderlyingType == String? {
     /// - Returns: A copy of the expression appended with a `GLOB` query against
     ///   the given pattern.
     public func glob(_ pattern: String) -> Expression<Bool?> {
-        return Function.glob.infix(self, pattern)
+        Function.glob.infix(self, pattern)
     }
 
     /// Builds a copy of the expression appended with a `MATCH` query against
@@ -534,7 +534,7 @@ extension ExpressionType where UnderlyingType == String? {
     /// - Returns: A copy of the expression appended with a `MATCH` query
     ///   against the given pattern.
     public func match(_ pattern: String) -> Expression<Bool> {
-        return Function.match.infix(self, pattern)
+        Function.match.infix(self, pattern)
     }
 
     /// Builds a copy of the expression appended with a `REGEXP` query against
@@ -545,7 +545,7 @@ extension ExpressionType where UnderlyingType == String? {
     /// - Returns: A copy of the expression appended with a `REGEXP` query
     ///   against the given pattern.
     public func regexp(_ pattern: String) -> Expression<Bool?> {
-        return Function.regexp.infix(self, pattern)
+        Function.regexp.infix(self, pattern)
     }
 
     /// Builds a copy of the expression appended with a `COLLATE` clause with
@@ -560,7 +560,7 @@ extension ExpressionType where UnderlyingType == String? {
     /// - Returns: A copy of the expression appended with a `COLLATE` clause
     ///   with the given sequence.
     public func collate(_ collation: Collation) -> Expression<UnderlyingType> {
-        return Function.collate.infix(self, collation)
+        Function.collate.infix(self, collation)
     }
 
     /// Builds a copy of the expression wrapped with the `ltrim` function.
@@ -631,7 +631,7 @@ extension ExpressionType where UnderlyingType == String? {
     ///
     /// - Returns: A copy of the expression wrapped with the `replace` function.
     public func replace(_ pattern: String, with replacement: String) -> Expression<UnderlyingType> {
-        return Function.replace.wrap([self, pattern, replacement])
+        Function.replace.wrap([self, pattern, replacement])
     }
 
     /// Builds a copy of the expression wrapped with the `substr` function.
@@ -666,7 +666,7 @@ extension ExpressionType where UnderlyingType == String? {
     ///
     /// - Returns: A copy of the expression wrapped with the `substr` function.
     public subscript(range: Range<Int>) -> Expression<UnderlyingType> {
-        return substring(range.lowerBound, length: range.upperBound - range.lowerBound)
+        substring(range.lowerBound, length: range.upperBound - range.lowerBound)
     }
 
 }
@@ -752,7 +752,7 @@ extension String {
 /// - Returns: A copy of the given expressions wrapped with the `ifnull`
 ///   function.
 public func ??<V: Value>(optional: Expression<V?>, defaultValue: V) -> Expression<V> {
-    return Function.ifnull.wrap([optional, defaultValue])
+    Function.ifnull.wrap([optional, defaultValue])
 }
 
 /// Builds a copy of the given expressions wrapped with the `ifnull` function.
@@ -772,7 +772,7 @@ public func ??<V: Value>(optional: Expression<V?>, defaultValue: V) -> Expressio
 /// - Returns: A copy of the given expressions wrapped with the `ifnull`
 ///   function.
 public func ??<V: Value>(optional: Expression<V?>, defaultValue: Expression<V>) -> Expression<V> {
-    return Function.ifnull.wrap([optional, defaultValue])
+    Function.ifnull.wrap([optional, defaultValue])
 }
 
 /// Builds a copy of the given expressions wrapped with the `ifnull` function.
@@ -792,5 +792,5 @@ public func ??<V: Value>(optional: Expression<V?>, defaultValue: Expression<V>)
 /// - Returns: A copy of the given expressions wrapped with the `ifnull`
 ///   function.
 public func ??<V: Value>(optional: Expression<V?>, defaultValue: Expression<V?>) -> Expression<V> {
-    return Function.ifnull.wrap([optional, defaultValue])
+    Function.ifnull.wrap([optional, defaultValue])
 }
diff --git a/Sources/SQLite/Typed/DateAndTimeFunctions.swift b/Sources/SQLite/Typed/DateAndTimeFunctions.swift
index 0b9a497f..b4382194 100644
--- a/Sources/SQLite/Typed/DateAndTimeFunctions.swift
+++ b/Sources/SQLite/Typed/DateAndTimeFunctions.swift
@@ -32,23 +32,23 @@ import Foundation
 public class DateFunctions {
     /// The date() function returns the date in this format: YYYY-MM-DD.
     public static func date(_ timestring: String, _ modifiers: String...) -> Expression<Date?> {
-        return timefunction("date", timestring: timestring, modifiers: modifiers)
+        timefunction("date", timestring: timestring, modifiers: modifiers)
     }
 
     /// The time() function returns the time as HH:MM:SS.
     public static func time(_ timestring: String, _ modifiers: String...) -> Expression<Date?> {
-        return timefunction("time", timestring: timestring, modifiers: modifiers)
+        timefunction("time", timestring: timestring, modifiers: modifiers)
     }
 
     /// The datetime() function returns "YYYY-MM-DD HH:MM:SS".
     public static func datetime(_ timestring: String, _ modifiers: String...) -> Expression<Date?> {
-        return timefunction("datetime", timestring: timestring, modifiers: modifiers)
+        timefunction("datetime", timestring: timestring, modifiers: modifiers)
     }
 
     /// The julianday() function returns the Julian day -
     /// the number of days since noon in Greenwich on November 24, 4714 B.C.
     public static func julianday(_ timestring: String, _ modifiers: String...) -> Expression<Date?> {
-        return timefunction("julianday", timestring: timestring, modifiers: modifiers)
+        timefunction("julianday", timestring: timestring, modifiers: modifiers)
     }
 
     ///  The strftime() routine returns the date formatted according to the format string specified as the first argument.
@@ -71,36 +71,36 @@ public class DateFunctions {
 
 extension Date {
     public var date: Expression<Date?> {
-        return DateFunctions.date(dateFormatter.string(from: self))
+        DateFunctions.date(dateFormatter.string(from: self))
     }
 
     public var time: Expression<Date?> {
-        return DateFunctions.time(dateFormatter.string(from: self))
+        DateFunctions.time(dateFormatter.string(from: self))
     }
 
     public var datetime: Expression<Date?> {
-        return DateFunctions.datetime(dateFormatter.string(from: self))
+        DateFunctions.datetime(dateFormatter.string(from: self))
     }
 
     public var julianday: Expression<Date?> {
-        return DateFunctions.julianday(dateFormatter.string(from: self))
+        DateFunctions.julianday(dateFormatter.string(from: self))
     }
 }
 
 extension Expression where UnderlyingType == Date {
     public var date: Expression<Date> {
-        return Expression<Date>("date(\(template))", bindings)
+        Expression<Date>("date(\(template))", bindings)
     }
 
     public var time: Expression<Date> {
-        return Expression<Date>("time(\(template))", bindings)
+        Expression<Date>("time(\(template))", bindings)
     }
 
     public var datetime: Expression<Date> {
-        return Expression<Date>("datetime(\(template))", bindings)
+        Expression<Date>("datetime(\(template))", bindings)
     }
 
     public var julianday: Expression<Date> {
-        return Expression<Date>("julianday(\(template))", bindings)
+        Expression<Date>("julianday(\(template))", bindings)
     }
 }
diff --git a/Sources/SQLite/Typed/Expression.swift b/Sources/SQLite/Typed/Expression.swift
index 39a3c62f..95cdd3b9 100644
--- a/Sources/SQLite/Typed/Expression.swift
+++ b/Sources/SQLite/Typed/Expression.swift
@@ -95,15 +95,15 @@ extension Expressible {
 extension ExpressionType {
 
     public var expression: Expression<Void> {
-        return Expression(template, bindings)
+        Expression(template, bindings)
     }
 
     public var asc: Expressible {
-        return " ".join([self, Expression<Void>(literal: "ASC")])
+        " ".join([self, Expression<Void>(literal: "ASC")])
     }
 
     public var desc: Expressible {
-        return " ".join([self, Expression<Void>(literal: "DESC")])
+        " ".join([self, Expression<Void>(literal: "DESC")])
     }
 
 }
@@ -119,7 +119,7 @@ extension ExpressionType where UnderlyingType: Value {
 extension ExpressionType where UnderlyingType: _OptionalType, UnderlyingType.WrappedType: Value {
 
     public static var null: Self {
-        return self.init(value: nil)
+        self.init(value: nil)
     }
 
     public init(value: UnderlyingType.WrappedType?) {
@@ -131,7 +131,7 @@ extension ExpressionType where UnderlyingType: _OptionalType, UnderlyingType.Wra
 extension Value {
 
     public var expression: Expression<Void> {
-        return Expression(value: self).expression
+        Expression(value: self).expression
     }
 
 }
@@ -139,9 +139,9 @@ extension Value {
 public let rowid = Expression<Int64>("ROWID")
 
 public func cast<T: Value, U: Value>(_ expression: Expression<T>) -> Expression<U> {
-    return Expression("CAST (\(expression.template) AS \(U.declaredDatatype))", expression.bindings)
+    Expression("CAST (\(expression.template) AS \(U.declaredDatatype))", expression.bindings)
 }
 
 public func cast<T: Value, U: Value>(_ expression: Expression<T?>) -> Expression<U?> {
-    return Expression("CAST (\(expression.template) AS \(U.declaredDatatype))", expression.bindings)
+    Expression("CAST (\(expression.template) AS \(U.declaredDatatype))", expression.bindings)
 }
diff --git a/Sources/SQLite/Typed/Operators.swift b/Sources/SQLite/Typed/Operators.swift
index 32d39fa7..5ffbbceb 100644
--- a/Sources/SQLite/Typed/Operators.swift
+++ b/Sources/SQLite/Typed/Operators.swift
@@ -47,331 +47,331 @@ private enum Operator: String {
     case concatenate = "||"
 
     func infix<T>(_ lhs: Expressible, _ rhs: Expressible, wrap: Bool = true) -> Expression<T> {
-        return self.rawValue.infix(lhs, rhs, wrap: wrap)
+        self.rawValue.infix(lhs, rhs, wrap: wrap)
     }
 
     func wrap<T>(_ expression: Expressible) -> Expression<T> {
-        return self.rawValue.wrap(expression)
+        self.rawValue.wrap(expression)
     }
 }
 
 public func +(lhs: Expression<String>, rhs: Expression<String>) -> Expression<String> {
-    return Operator.concatenate.infix(lhs, rhs)
+    Operator.concatenate.infix(lhs, rhs)
 }
 
 public func +(lhs: Expression<String>, rhs: Expression<String?>) -> Expression<String?> {
-    return Operator.concatenate.infix(lhs, rhs)
+    Operator.concatenate.infix(lhs, rhs)
 }
 public func +(lhs: Expression<String?>, rhs: Expression<String>) -> Expression<String?> {
-    return Operator.concatenate.infix(lhs, rhs)
+    Operator.concatenate.infix(lhs, rhs)
 }
 public func +(lhs: Expression<String?>, rhs: Expression<String?>) -> Expression<String?> {
-    return Operator.concatenate.infix(lhs, rhs)
+    Operator.concatenate.infix(lhs, rhs)
 }
 public func +(lhs: Expression<String>, rhs: String) -> Expression<String> {
-    return Operator.concatenate.infix(lhs, rhs)
+    Operator.concatenate.infix(lhs, rhs)
 }
 public func +(lhs: Expression<String?>, rhs: String) -> Expression<String?> {
-    return Operator.concatenate.infix(lhs, rhs)
+    Operator.concatenate.infix(lhs, rhs)
 }
 public func +(lhs: String, rhs: Expression<String>) -> Expression<String> {
-    return Operator.concatenate.infix(lhs, rhs)
+    Operator.concatenate.infix(lhs, rhs)
 }
 public func +(lhs: String, rhs: Expression<String?>) -> Expression<String?> {
-    return Operator.concatenate.infix(lhs, rhs)
+    Operator.concatenate.infix(lhs, rhs)
 }
 
 // MARK: -
 
 public func +<V: Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<V> where V.Datatype: Number {
-    return Operator.plus.infix(lhs, rhs)
+    Operator.plus.infix(lhs, rhs)
 }
 public func +<V: Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype: Number {
-    return Operator.plus.infix(lhs, rhs)
+    Operator.plus.infix(lhs, rhs)
 }
 public func +<V: Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<V?> where V.Datatype: Number {
-    return Operator.plus.infix(lhs, rhs)
+    Operator.plus.infix(lhs, rhs)
 }
 public func +<V: Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype: Number {
-    return Operator.plus.infix(lhs, rhs)
+    Operator.plus.infix(lhs, rhs)
 }
 public func +<V: Value>(lhs: Expression<V>, rhs: V) -> Expression<V> where V.Datatype: Number {
-    return Operator.plus.infix(lhs, rhs)
+    Operator.plus.infix(lhs, rhs)
 }
 public func +<V: Value>(lhs: Expression<V?>, rhs: V) -> Expression<V?> where V.Datatype: Number {
-    return Operator.plus.infix(lhs, rhs)
+    Operator.plus.infix(lhs, rhs)
 }
 public func +<V: Value>(lhs: V, rhs: Expression<V>) -> Expression<V> where V.Datatype: Number {
-    return Operator.plus.infix(lhs, rhs)
+    Operator.plus.infix(lhs, rhs)
 }
 public func +<V: Value>(lhs: V, rhs: Expression<V?>) -> Expression<V?> where V.Datatype: Number {
-    return Operator.plus.infix(lhs, rhs)
+    Operator.plus.infix(lhs, rhs)
 }
 
 public func -<V: Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<V> where V.Datatype: Number {
-    return Operator.minus.infix(lhs, rhs)
+    Operator.minus.infix(lhs, rhs)
 }
 public func -<V: Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype: Number {
-    return Operator.minus.infix(lhs, rhs)
+    Operator.minus.infix(lhs, rhs)
 }
 public func -<V: Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<V?> where V.Datatype: Number {
-    return Operator.minus.infix(lhs, rhs)
+    Operator.minus.infix(lhs, rhs)
 }
 public func -<V: Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype: Number {
-    return Operator.minus.infix(lhs, rhs)
+    Operator.minus.infix(lhs, rhs)
 }
 public func -<V: Value>(lhs: Expression<V>, rhs: V) -> Expression<V> where V.Datatype: Number {
-    return Operator.minus.infix(lhs, rhs)
+    Operator.minus.infix(lhs, rhs)
 }
 public func -<V: Value>(lhs: Expression<V?>, rhs: V) -> Expression<V?> where V.Datatype: Number {
-    return Operator.minus.infix(lhs, rhs)
+    Operator.minus.infix(lhs, rhs)
 }
 public func -<V: Value>(lhs: V, rhs: Expression<V>) -> Expression<V> where V.Datatype: Number {
-    return Operator.minus.infix(lhs, rhs)
+    Operator.minus.infix(lhs, rhs)
 }
 public func -<V: Value>(lhs: V, rhs: Expression<V?>) -> Expression<V?> where V.Datatype: Number {
-    return Operator.minus.infix(lhs, rhs)
+    Operator.minus.infix(lhs, rhs)
 }
 
 public func *<V: Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<V> where V.Datatype: Number {
-    return Operator.mul.infix(lhs, rhs)
+    Operator.mul.infix(lhs, rhs)
 }
 public func *<V: Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype: Number {
-    return Operator.mul.infix(lhs, rhs)
+    Operator.mul.infix(lhs, rhs)
 }
 public func *<V: Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<V?> where V.Datatype: Number {
-    return Operator.mul.infix(lhs, rhs)
+    Operator.mul.infix(lhs, rhs)
 }
 public func *<V: Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype: Number {
-    return Operator.mul.infix(lhs, rhs)
+    Operator.mul.infix(lhs, rhs)
 }
 public func *<V: Value>(lhs: Expression<V>, rhs: V) -> Expression<V> where V.Datatype: Number {
-    return Operator.mul.infix(lhs, rhs)
+    Operator.mul.infix(lhs, rhs)
 }
 public func *<V: Value>(lhs: Expression<V?>, rhs: V) -> Expression<V?> where V.Datatype: Number {
-    return Operator.mul.infix(lhs, rhs)
+    Operator.mul.infix(lhs, rhs)
 }
 public func *<V: Value>(lhs: V, rhs: Expression<V>) -> Expression<V> where V.Datatype: Number {
-    return Operator.mul.infix(lhs, rhs)
+    Operator.mul.infix(lhs, rhs)
 }
 public func *<V: Value>(lhs: V, rhs: Expression<V?>) -> Expression<V?> where V.Datatype: Number {
-    return Operator.mul.infix(lhs, rhs)
+    Operator.mul.infix(lhs, rhs)
 }
 
 public func /<V: Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<V> where V.Datatype: Number {
-    return Operator.div.infix(lhs, rhs)
+    Operator.div.infix(lhs, rhs)
 }
 public func /<V: Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype: Number {
-    return Operator.div.infix(lhs, rhs)
+    Operator.div.infix(lhs, rhs)
 }
 public func /<V: Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<V?> where V.Datatype: Number {
-    return Operator.div.infix(lhs, rhs)
+    Operator.div.infix(lhs, rhs)
 }
 public func /<V: Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype: Number {
-    return Operator.div.infix(lhs, rhs)
+    Operator.div.infix(lhs, rhs)
 }
 public func /<V: Value>(lhs: Expression<V>, rhs: V) -> Expression<V> where V.Datatype: Number {
-    return Operator.div.infix(lhs, rhs)
+    Operator.div.infix(lhs, rhs)
 }
 public func /<V: Value>(lhs: Expression<V?>, rhs: V) -> Expression<V?> where V.Datatype: Number {
-    return Operator.div.infix(lhs, rhs)
+    Operator.div.infix(lhs, rhs)
 }
 public func /<V: Value>(lhs: V, rhs: Expression<V>) -> Expression<V> where V.Datatype: Number {
-    return Operator.div.infix(lhs, rhs)
+    Operator.div.infix(lhs, rhs)
 }
 public func /<V: Value>(lhs: V, rhs: Expression<V?>) -> Expression<V?> where V.Datatype: Number {
-    return Operator.div.infix(lhs, rhs)
+    Operator.div.infix(lhs, rhs)
 }
 
 public prefix func -<V: Value>(rhs: Expression<V>) -> Expression<V> where V.Datatype: Number {
-    return Operator.minus.wrap(rhs)
+    Operator.minus.wrap(rhs)
 }
 public prefix func -<V: Value>(rhs: Expression<V?>) -> Expression<V?> where V.Datatype: Number {
-    return Operator.minus.wrap(rhs)
+    Operator.minus.wrap(rhs)
 }
 
 // MARK: -
 
 public func %<V: Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<V> where V.Datatype == Int64 {
-    return Operator.mod.infix(lhs, rhs)
+    Operator.mod.infix(lhs, rhs)
 }
 public func %<V: Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
-    return Operator.mod.infix(lhs, rhs)
+    Operator.mod.infix(lhs, rhs)
 }
 public func %<V: Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<V?> where V.Datatype == Int64 {
-    return Operator.mod.infix(lhs, rhs)
+    Operator.mod.infix(lhs, rhs)
 }
 public func %<V: Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
-    return Operator.mod.infix(lhs, rhs)
+    Operator.mod.infix(lhs, rhs)
 }
 public func %<V: Value>(lhs: Expression<V>, rhs: V) -> Expression<V> where V.Datatype == Int64 {
-    return Operator.mod.infix(lhs, rhs)
+    Operator.mod.infix(lhs, rhs)
 }
 public func %<V: Value>(lhs: Expression<V?>, rhs: V) -> Expression<V?> where V.Datatype == Int64 {
-    return Operator.mod.infix(lhs, rhs)
+    Operator.mod.infix(lhs, rhs)
 }
 public func %<V: Value>(lhs: V, rhs: Expression<V>) -> Expression<V> where V.Datatype == Int64 {
-    return Operator.mod.infix(lhs, rhs)
+    Operator.mod.infix(lhs, rhs)
 }
 public func %<V: Value>(lhs: V, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
-    return Operator.mod.infix(lhs, rhs)
+    Operator.mod.infix(lhs, rhs)
 }
 
 public func <<<V: Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<V> where V.Datatype == Int64 {
-    return Operator.bitwiseLeft.infix(lhs, rhs)
+    Operator.bitwiseLeft.infix(lhs, rhs)
 }
 public func <<<V: Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
-    return Operator.bitwiseLeft.infix(lhs, rhs)
+    Operator.bitwiseLeft.infix(lhs, rhs)
 }
 public func <<<V: Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<V?> where V.Datatype == Int64 {
-    return Operator.bitwiseLeft.infix(lhs, rhs)
+    Operator.bitwiseLeft.infix(lhs, rhs)
 }
 public func <<<V: Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
-    return Operator.bitwiseLeft.infix(lhs, rhs)
+    Operator.bitwiseLeft.infix(lhs, rhs)
 }
 public func <<<V: Value>(lhs: Expression<V>, rhs: V) -> Expression<V> where V.Datatype == Int64 {
-    return Operator.bitwiseLeft.infix(lhs, rhs)
+    Operator.bitwiseLeft.infix(lhs, rhs)
 }
 public func <<<V: Value>(lhs: Expression<V?>, rhs: V) -> Expression<V?> where V.Datatype == Int64 {
-    return Operator.bitwiseLeft.infix(lhs, rhs)
+    Operator.bitwiseLeft.infix(lhs, rhs)
 }
 public func <<<V: Value>(lhs: V, rhs: Expression<V>) -> Expression<V> where V.Datatype == Int64 {
-    return Operator.bitwiseLeft.infix(lhs, rhs)
+    Operator.bitwiseLeft.infix(lhs, rhs)
 }
 public func <<<V: Value>(lhs: V, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
-    return Operator.bitwiseLeft.infix(lhs, rhs)
+    Operator.bitwiseLeft.infix(lhs, rhs)
 }
 
 public func >><V: Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<V> where V.Datatype == Int64 {
-    return Operator.bitwiseRight.infix(lhs, rhs)
+    Operator.bitwiseRight.infix(lhs, rhs)
 }
 public func >><V: Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
-    return Operator.bitwiseRight.infix(lhs, rhs)
+    Operator.bitwiseRight.infix(lhs, rhs)
 }
 public func >><V: Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<V?> where V.Datatype == Int64 {
-    return Operator.bitwiseRight.infix(lhs, rhs)
+    Operator.bitwiseRight.infix(lhs, rhs)
 }
 public func >><V: Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
-    return Operator.bitwiseRight.infix(lhs, rhs)
+    Operator.bitwiseRight.infix(lhs, rhs)
 }
 public func >><V: Value>(lhs: Expression<V>, rhs: V) -> Expression<V> where V.Datatype == Int64 {
-    return Operator.bitwiseRight.infix(lhs, rhs)
+    Operator.bitwiseRight.infix(lhs, rhs)
 }
 public func >><V: Value>(lhs: Expression<V?>, rhs: V) -> Expression<V?> where V.Datatype == Int64 {
-    return Operator.bitwiseRight.infix(lhs, rhs)
+    Operator.bitwiseRight.infix(lhs, rhs)
 }
 public func >><V: Value>(lhs: V, rhs: Expression<V>) -> Expression<V> where V.Datatype == Int64 {
-    return Operator.bitwiseRight.infix(lhs, rhs)
+    Operator.bitwiseRight.infix(lhs, rhs)
 }
 public func >><V: Value>(lhs: V, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
-    return Operator.bitwiseRight.infix(lhs, rhs)
+    Operator.bitwiseRight.infix(lhs, rhs)
 }
 
 public func &<V: Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<V> where V.Datatype == Int64 {
-    return Operator.bitwiseAnd.infix(lhs, rhs)
+    Operator.bitwiseAnd.infix(lhs, rhs)
 }
 public func &<V: Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
-    return Operator.bitwiseAnd.infix(lhs, rhs)
+    Operator.bitwiseAnd.infix(lhs, rhs)
 }
 public func &<V: Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<V?> where V.Datatype == Int64 {
-    return Operator.bitwiseAnd.infix(lhs, rhs)
+    Operator.bitwiseAnd.infix(lhs, rhs)
 }
 public func &<V: Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
-    return Operator.bitwiseAnd.infix(lhs, rhs)
+    Operator.bitwiseAnd.infix(lhs, rhs)
 }
 public func &<V: Value>(lhs: Expression<V>, rhs: V) -> Expression<V> where V.Datatype == Int64 {
-    return Operator.bitwiseAnd.infix(lhs, rhs)
+    Operator.bitwiseAnd.infix(lhs, rhs)
 }
 public func &<V: Value>(lhs: Expression<V?>, rhs: V) -> Expression<V?> where V.Datatype == Int64 {
-    return Operator.bitwiseAnd.infix(lhs, rhs)
+    Operator.bitwiseAnd.infix(lhs, rhs)
 }
 public func &<V: Value>(lhs: V, rhs: Expression<V>) -> Expression<V> where V.Datatype == Int64 {
-    return Operator.bitwiseAnd.infix(lhs, rhs)
+    Operator.bitwiseAnd.infix(lhs, rhs)
 }
 public func &<V: Value>(lhs: V, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
-    return Operator.bitwiseAnd.infix(lhs, rhs)
+    Operator.bitwiseAnd.infix(lhs, rhs)
 }
 
 public func |<V: Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<V> where V.Datatype == Int64 {
-    return Operator.bitwiseOr.infix(lhs, rhs)
+    Operator.bitwiseOr.infix(lhs, rhs)
 }
 public func |<V: Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
-    return Operator.bitwiseOr.infix(lhs, rhs)
+    Operator.bitwiseOr.infix(lhs, rhs)
 }
 public func |<V: Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<V?> where V.Datatype == Int64 {
-    return Operator.bitwiseOr.infix(lhs, rhs)
+    Operator.bitwiseOr.infix(lhs, rhs)
 }
 public func |<V: Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
-    return Operator.bitwiseOr.infix(lhs, rhs)
+    Operator.bitwiseOr.infix(lhs, rhs)
 }
 public func |<V: Value>(lhs: Expression<V>, rhs: V) -> Expression<V> where V.Datatype == Int64 {
-    return Operator.bitwiseOr.infix(lhs, rhs)
+    Operator.bitwiseOr.infix(lhs, rhs)
 }
 public func |<V: Value>(lhs: Expression<V?>, rhs: V) -> Expression<V?> where V.Datatype == Int64 {
-    return Operator.bitwiseOr.infix(lhs, rhs)
+    Operator.bitwiseOr.infix(lhs, rhs)
 }
 public func |<V: Value>(lhs: V, rhs: Expression<V>) -> Expression<V> where V.Datatype == Int64 {
-    return Operator.bitwiseOr.infix(lhs, rhs)
+    Operator.bitwiseOr.infix(lhs, rhs)
 }
 public func |<V: Value>(lhs: V, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
-    return Operator.bitwiseOr.infix(lhs, rhs)
+    Operator.bitwiseOr.infix(lhs, rhs)
 }
 
 public func ^<V: Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<V> where V.Datatype == Int64 {
-    return (~(lhs & rhs)) & (lhs | rhs)
+    (~(lhs & rhs)) & (lhs | rhs)
 }
 public func ^<V: Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
-    return (~(lhs & rhs)) & (lhs | rhs)
+    (~(lhs & rhs)) & (lhs | rhs)
 }
 public func ^<V: Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<V?> where V.Datatype == Int64 {
-    return (~(lhs & rhs)) & (lhs | rhs)
+    (~(lhs & rhs)) & (lhs | rhs)
 }
 public func ^<V: Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
-    return (~(lhs & rhs)) & (lhs | rhs)
+    (~(lhs & rhs)) & (lhs | rhs)
 }
 public func ^<V: Value>(lhs: Expression<V>, rhs: V) -> Expression<V> where V.Datatype == Int64 {
-    return (~(lhs & rhs)) & (lhs | rhs)
+    (~(lhs & rhs)) & (lhs | rhs)
 }
 public func ^<V: Value>(lhs: Expression<V?>, rhs: V) -> Expression<V?> where V.Datatype == Int64 {
-    return (~(lhs & rhs)) & (lhs | rhs)
+    (~(lhs & rhs)) & (lhs | rhs)
 }
 public func ^<V: Value>(lhs: V, rhs: Expression<V>) -> Expression<V> where V.Datatype == Int64 {
-    return (~(lhs & rhs)) & (lhs | rhs)
+    (~(lhs & rhs)) & (lhs | rhs)
 }
 public func ^<V: Value>(lhs: V, rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
-    return (~(lhs & rhs)) & (lhs | rhs)
+    (~(lhs & rhs)) & (lhs | rhs)
 }
 
 public prefix func ~<V: Value>(rhs: Expression<V>) -> Expression<V> where V.Datatype == Int64 {
-    return Operator.bitwiseXor.wrap(rhs)
+    Operator.bitwiseXor.wrap(rhs)
 }
 public prefix func ~<V: Value>(rhs: Expression<V?>) -> Expression<V?> where V.Datatype == Int64 {
-    return Operator.bitwiseXor.wrap(rhs)
+    Operator.bitwiseXor.wrap(rhs)
 }
 
 // MARK: -
 
 public func ==<V: Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<Bool> where V.Datatype: Equatable {
-    return Operator.eq.infix(lhs, rhs)
+    Operator.eq.infix(lhs, rhs)
 }
 public func ==<V: Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype: Equatable {
-    return Operator.eq.infix(lhs, rhs)
+    Operator.eq.infix(lhs, rhs)
 }
 public func ==<V: Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<Bool?> where V.Datatype: Equatable {
-    return Operator.eq.infix(lhs, rhs)
+    Operator.eq.infix(lhs, rhs)
 }
 public func ==<V: Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype: Equatable {
-    return Operator.eq.infix(lhs, rhs)
+    Operator.eq.infix(lhs, rhs)
 }
 public func ==<V: Value>(lhs: Expression<V>, rhs: V) -> Expression<Bool> where V.Datatype: Equatable {
-    return Operator.eq.infix(lhs, rhs)
+    Operator.eq.infix(lhs, rhs)
 }
 public func ==<V: Value>(lhs: Expression<V?>, rhs: V?) -> Expression<Bool?> where V.Datatype: Equatable {
     guard let rhs = rhs else { return "IS".infix(lhs, Expression<V?>(value: nil)) }
     return Operator.eq.infix(lhs, rhs)
 }
 public func ==<V: Value>(lhs: V, rhs: Expression<V>) -> Expression<Bool> where V.Datatype: Equatable {
-    return Operator.eq.infix(lhs, rhs)
+    Operator.eq.infix(lhs, rhs)
 }
 public func ==<V: Value>(lhs: V?, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype: Equatable {
     guard let lhs = lhs else { return "IS".infix(Expression<V?>(value: nil), rhs) }
@@ -379,26 +379,26 @@ public func ==<V: Value>(lhs: V?, rhs: Expression<V?>) -> Expression<Bool?> wher
 }
 
 public func ===<V: Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<Bool> where V.Datatype: Equatable {
-    return "IS".infix(lhs, rhs)
+    "IS".infix(lhs, rhs)
 }
 public func ===<V: Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype: Equatable {
-    return "IS".infix(lhs, rhs)
+    "IS".infix(lhs, rhs)
 }
 public func ===<V: Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<Bool?> where V.Datatype: Equatable {
-    return "IS".infix(lhs, rhs)
+    "IS".infix(lhs, rhs)
 }
 public func ===<V: Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype: Equatable {
-    return "IS".infix(lhs, rhs)
+    "IS".infix(lhs, rhs)
 }
 public func ===<V: Value>(lhs: Expression<V>, rhs: V) -> Expression<Bool> where V.Datatype: Equatable {
-    return "IS".infix(lhs, rhs)
+    "IS".infix(lhs, rhs)
 }
 public func ===<V: Value>(lhs: Expression<V?>, rhs: V?) -> Expression<Bool?> where V.Datatype: Equatable {
     guard let rhs = rhs else { return "IS".infix(lhs, Expression<V?>(value: nil)) }
     return "IS".infix(lhs, rhs)
 }
 public func ===<V: Value>(lhs: V, rhs: Expression<V>) -> Expression<Bool> where V.Datatype: Equatable {
-    return "IS".infix(lhs, rhs)
+    "IS".infix(lhs, rhs)
 }
 public func ===<V: Value>(lhs: V?, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype: Equatable {
     guard let lhs = lhs else { return "IS".infix(Expression<V?>(value: nil), rhs) }
@@ -406,26 +406,26 @@ public func ===<V: Value>(lhs: V?, rhs: Expression<V?>) -> Expression<Bool?> whe
 }
 
 public func !=<V: Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<Bool> where V.Datatype: Equatable {
-    return Operator.neq.infix(lhs, rhs)
+    Operator.neq.infix(lhs, rhs)
 }
 public func !=<V: Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype: Equatable {
-    return Operator.neq.infix(lhs, rhs)
+    Operator.neq.infix(lhs, rhs)
 }
 public func !=<V: Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<Bool?> where V.Datatype: Equatable {
-    return Operator.neq.infix(lhs, rhs)
+    Operator.neq.infix(lhs, rhs)
 }
 public func !=<V: Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype: Equatable {
-    return Operator.neq.infix(lhs, rhs)
+    Operator.neq.infix(lhs, rhs)
 }
 public func !=<V: Value>(lhs: Expression<V>, rhs: V) -> Expression<Bool> where V.Datatype: Equatable {
-    return Operator.neq.infix(lhs, rhs)
+    Operator.neq.infix(lhs, rhs)
 }
 public func !=<V: Value>(lhs: Expression<V?>, rhs: V?) -> Expression<Bool?> where V.Datatype: Equatable {
     guard let rhs = rhs else { return "IS NOT".infix(lhs, Expression<V?>(value: nil)) }
     return Operator.neq.infix(lhs, rhs)
 }
 public func !=<V: Value>(lhs: V, rhs: Expression<V>) -> Expression<Bool> where V.Datatype: Equatable {
-    return Operator.neq.infix(lhs, rhs)
+    Operator.neq.infix(lhs, rhs)
 }
 public func !=<V: Value>(lhs: V?, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype: Equatable {
     guard let lhs = lhs else { return "IS NOT".infix(Expression<V?>(value: nil), rhs) }
@@ -433,26 +433,26 @@ public func !=<V: Value>(lhs: V?, rhs: Expression<V?>) -> Expression<Bool?> wher
 }
 
 public func !==<V: Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<Bool> where V.Datatype: Equatable {
-    return "IS NOT".infix(lhs, rhs)
+    "IS NOT".infix(lhs, rhs)
 }
 public func !==<V: Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype: Equatable {
-    return "IS NOT".infix(lhs, rhs)
+    "IS NOT".infix(lhs, rhs)
 }
 public func !==<V: Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<Bool?> where V.Datatype: Equatable {
-    return "IS NOT".infix(lhs, rhs)
+    "IS NOT".infix(lhs, rhs)
 }
 public func !==<V: Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype: Equatable {
-    return "IS NOT".infix(lhs, rhs)
+    "IS NOT".infix(lhs, rhs)
 }
 public func !==<V: Value>(lhs: Expression<V>, rhs: V) -> Expression<Bool> where V.Datatype: Equatable {
-    return "IS NOT".infix(lhs, rhs)
+    "IS NOT".infix(lhs, rhs)
 }
 public func !==<V: Value>(lhs: Expression<V?>, rhs: V?) -> Expression<Bool?> where V.Datatype: Equatable {
     guard let rhs = rhs else { return "IS NOT".infix(lhs, Expression<V?>(value: nil)) }
     return "IS NOT".infix(lhs, rhs)
 }
 public func !==<V: Value>(lhs: V, rhs: Expression<V>) -> Expression<Bool> where V.Datatype: Equatable {
-    return "IS NOT".infix(lhs, rhs)
+    "IS NOT".infix(lhs, rhs)
 }
 public func !==<V: Value>(lhs: V?, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype: Equatable {
     guard let lhs = lhs else { return "IS NOT".infix(Expression<V?>(value: nil), rhs) }
@@ -460,215 +460,215 @@ public func !==<V: Value>(lhs: V?, rhs: Expression<V?>) -> Expression<Bool?> whe
 }
 
 public func ><V: Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<Bool> where V.Datatype: Comparable {
-    return Operator.gt.infix(lhs, rhs)
+    Operator.gt.infix(lhs, rhs)
 }
 public func ><V: Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype: Comparable {
-    return Operator.gt.infix(lhs, rhs)
+    Operator.gt.infix(lhs, rhs)
 }
 public func ><V: Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<Bool?> where V.Datatype: Comparable {
-    return Operator.gt.infix(lhs, rhs)
+    Operator.gt.infix(lhs, rhs)
 }
 public func ><V: Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype: Comparable {
-    return Operator.gt.infix(lhs, rhs)
+    Operator.gt.infix(lhs, rhs)
 }
 public func ><V: Value>(lhs: Expression<V>, rhs: V) -> Expression<Bool> where V.Datatype: Comparable {
-    return Operator.gt.infix(lhs, rhs)
+    Operator.gt.infix(lhs, rhs)
 }
 public func ><V: Value>(lhs: Expression<V?>, rhs: V) -> Expression<Bool?> where V.Datatype: Comparable {
-    return Operator.gt.infix(lhs, rhs)
+    Operator.gt.infix(lhs, rhs)
 }
 public func ><V: Value>(lhs: V, rhs: Expression<V>) -> Expression<Bool> where V.Datatype: Comparable {
-    return Operator.gt.infix(lhs, rhs)
+    Operator.gt.infix(lhs, rhs)
 }
 public func ><V: Value>(lhs: V, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype: Comparable {
-    return Operator.gt.infix(lhs, rhs)
+    Operator.gt.infix(lhs, rhs)
 }
 
 public func >=<V: Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<Bool> where V.Datatype: Comparable {
-    return Operator.gte.infix(lhs, rhs)
+    Operator.gte.infix(lhs, rhs)
 }
 public func >=<V: Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype: Comparable {
-    return Operator.gte.infix(lhs, rhs)
+    Operator.gte.infix(lhs, rhs)
 }
 public func >=<V: Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<Bool?> where V.Datatype: Comparable {
-    return Operator.gte.infix(lhs, rhs)
+    Operator.gte.infix(lhs, rhs)
 }
 public func >=<V: Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype: Comparable {
-    return Operator.gte.infix(lhs, rhs)
+    Operator.gte.infix(lhs, rhs)
 }
 public func >=<V: Value>(lhs: Expression<V>, rhs: V) -> Expression<Bool> where V.Datatype: Comparable {
-    return Operator.gte.infix(lhs, rhs)
+    Operator.gte.infix(lhs, rhs)
 }
 public func >=<V: Value>(lhs: Expression<V?>, rhs: V) -> Expression<Bool?> where V.Datatype: Comparable {
-    return Operator.gte.infix(lhs, rhs)
+    Operator.gte.infix(lhs, rhs)
 }
 public func >=<V: Value>(lhs: V, rhs: Expression<V>) -> Expression<Bool> where V.Datatype: Comparable {
-    return Operator.gte.infix(lhs, rhs)
+    Operator.gte.infix(lhs, rhs)
 }
 public func >=<V: Value>(lhs: V, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype: Comparable {
-    return Operator.gte.infix(lhs, rhs)
+    Operator.gte.infix(lhs, rhs)
 }
 
 public func <<V: Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<Bool> where V.Datatype: Comparable {
-    return Operator.lt.infix(lhs, rhs)
+    Operator.lt.infix(lhs, rhs)
 }
 public func <<V: Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype: Comparable {
-    return Operator.lt.infix(lhs, rhs)
+    Operator.lt.infix(lhs, rhs)
 }
 public func <<V: Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<Bool?> where V.Datatype: Comparable {
-    return Operator.lt.infix(lhs, rhs)
+    Operator.lt.infix(lhs, rhs)
 }
 public func <<V: Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype: Comparable {
-    return Operator.lt.infix(lhs, rhs)
+    Operator.lt.infix(lhs, rhs)
 }
 public func <<V: Value>(lhs: Expression<V>, rhs: V) -> Expression<Bool> where V.Datatype: Comparable {
-    return Operator.lt.infix(lhs, rhs)
+    Operator.lt.infix(lhs, rhs)
 }
 public func <<V: Value>(lhs: Expression<V?>, rhs: V) -> Expression<Bool?> where V.Datatype: Comparable {
-    return Operator.lt.infix(lhs, rhs)
+    Operator.lt.infix(lhs, rhs)
 }
 public func <<V: Value>(lhs: V, rhs: Expression<V>) -> Expression<Bool> where V.Datatype: Comparable {
-    return Operator.lt.infix(lhs, rhs)
+    Operator.lt.infix(lhs, rhs)
 }
 public func <<V: Value>(lhs: V, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype: Comparable {
-    return Operator.lt.infix(lhs, rhs)
+    Operator.lt.infix(lhs, rhs)
 }
 
 public func <=<V: Value>(lhs: Expression<V>, rhs: Expression<V>) -> Expression<Bool> where V.Datatype: Comparable {
-    return Operator.lte.infix(lhs, rhs)
+    Operator.lte.infix(lhs, rhs)
 }
 public func <=<V: Value>(lhs: Expression<V>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype: Comparable {
-    return Operator.lte.infix(lhs, rhs)
+    Operator.lte.infix(lhs, rhs)
 }
 public func <=<V: Value>(lhs: Expression<V?>, rhs: Expression<V>) -> Expression<Bool?> where V.Datatype: Comparable {
-    return Operator.lte.infix(lhs, rhs)
+    Operator.lte.infix(lhs, rhs)
 }
 public func <=<V: Value>(lhs: Expression<V?>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype: Comparable {
-    return Operator.lte.infix(lhs, rhs)
+    Operator.lte.infix(lhs, rhs)
 }
 public func <=<V: Value>(lhs: Expression<V>, rhs: V) -> Expression<Bool> where V.Datatype: Comparable {
-    return Operator.lte.infix(lhs, rhs)
+    Operator.lte.infix(lhs, rhs)
 }
 public func <=<V: Value>(lhs: Expression<V?>, rhs: V) -> Expression<Bool?> where V.Datatype: Comparable {
-    return Operator.lte.infix(lhs, rhs)
+    Operator.lte.infix(lhs, rhs)
 }
 public func <=<V: Value>(lhs: V, rhs: Expression<V>) -> Expression<Bool> where V.Datatype: Comparable {
-    return Operator.lte.infix(lhs, rhs)
+    Operator.lte.infix(lhs, rhs)
 }
 public func <=<V: Value>(lhs: V, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype: Comparable {
-    return Operator.lte.infix(lhs, rhs)
+    Operator.lte.infix(lhs, rhs)
 }
 
 public func ~=<V: Value>(lhs: ClosedRange<V>, rhs: Expression<V>) -> Expression<Bool> where V.Datatype: Comparable & Value {
-    return Expression("\(rhs.template) BETWEEN ? AND ?", rhs.bindings + [lhs.lowerBound.datatypeValue, lhs.upperBound.datatypeValue])
+    Expression("\(rhs.template) BETWEEN ? AND ?", rhs.bindings + [lhs.lowerBound.datatypeValue, lhs.upperBound.datatypeValue])
 }
 
 public func ~=<V: Value>(lhs: ClosedRange<V>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype: Comparable & Value {
-    return Expression("\(rhs.template) BETWEEN ? AND ?", rhs.bindings + [lhs.lowerBound.datatypeValue, lhs.upperBound.datatypeValue])
+    Expression("\(rhs.template) BETWEEN ? AND ?", rhs.bindings + [lhs.lowerBound.datatypeValue, lhs.upperBound.datatypeValue])
 }
 
 public func ~=<V: Value>(lhs: Range<V>, rhs: Expression<V>) -> Expression<Bool> where V.Datatype: Comparable & Value {
-    return Expression("\(rhs.template) >= ? AND \(rhs.template) < ?", rhs.bindings + [lhs.lowerBound.datatypeValue] +
-            rhs.bindings + [lhs.upperBound.datatypeValue])
+    Expression("\(rhs.template) >= ? AND \(rhs.template) < ?",
+               rhs.bindings + [lhs.lowerBound.datatypeValue] + rhs.bindings + [lhs.upperBound.datatypeValue])
 }
 
 public func ~=<V: Value>(lhs: Range<V>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype: Comparable & Value {
-    return Expression("\(rhs.template) >= ? AND \(rhs.template) < ?", rhs.bindings + [lhs.lowerBound.datatypeValue] +
-            rhs.bindings + [lhs.upperBound.datatypeValue])
+    Expression("\(rhs.template) >= ? AND \(rhs.template) < ?",
+               rhs.bindings + [lhs.lowerBound.datatypeValue] + rhs.bindings + [lhs.upperBound.datatypeValue])
 }
 
 public func ~=<V: Value>(lhs: PartialRangeThrough<V>, rhs: Expression<V>) -> Expression<Bool> where V.Datatype: Comparable & Value {
-    return Expression("\(rhs.template) <= ?", rhs.bindings + [lhs.upperBound.datatypeValue])
+    Expression("\(rhs.template) <= ?", rhs.bindings + [lhs.upperBound.datatypeValue])
 }
 
 public func ~=<V: Value>(lhs: PartialRangeThrough<V>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype: Comparable & Value {
-    return Expression("\(rhs.template) <= ?", rhs.bindings + [lhs.upperBound.datatypeValue])
+    Expression("\(rhs.template) <= ?", rhs.bindings + [lhs.upperBound.datatypeValue])
 }
 
 public func ~=<V: Value>(lhs: PartialRangeUpTo<V>, rhs: Expression<V>) -> Expression<Bool> where V.Datatype: Comparable & Value {
-    return Expression("\(rhs.template) < ?", rhs.bindings + [lhs.upperBound.datatypeValue])
+    Expression("\(rhs.template) < ?", rhs.bindings + [lhs.upperBound.datatypeValue])
 }
 
 public func ~=<V: Value>(lhs: PartialRangeUpTo<V>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype: Comparable & Value {
-    return Expression("\(rhs.template) < ?", rhs.bindings + [lhs.upperBound.datatypeValue])
+    Expression("\(rhs.template) < ?", rhs.bindings + [lhs.upperBound.datatypeValue])
 }
 
 public func ~=<V: Value>(lhs: PartialRangeFrom<V>, rhs: Expression<V>) -> Expression<Bool> where V.Datatype: Comparable & Value {
-    return Expression("\(rhs.template) >= ?", rhs.bindings + [lhs.lowerBound.datatypeValue])
+    Expression("\(rhs.template) >= ?", rhs.bindings + [lhs.lowerBound.datatypeValue])
 }
 
 public func ~=<V: Value>(lhs: PartialRangeFrom<V>, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype: Comparable & Value {
-    return Expression("\(rhs.template) >= ?", rhs.bindings + [lhs.lowerBound.datatypeValue])
+    Expression("\(rhs.template) >= ?", rhs.bindings + [lhs.lowerBound.datatypeValue])
 }
 
 // MARK: -
 
 public func and(_ terms: Expression<Bool>...) -> Expression<Bool> {
-    return "AND".infix(terms)
+    "AND".infix(terms)
 }
 public func and(_ terms: [Expression<Bool>]) -> Expression<Bool> {
-    return "AND".infix(terms)
+    "AND".infix(terms)
 }
 public func &&(lhs: Expression<Bool>, rhs: Expression<Bool>) -> Expression<Bool> {
-    return Operator.and.infix(lhs, rhs)
+    Operator.and.infix(lhs, rhs)
 }
 public func &&(lhs: Expression<Bool>, rhs: Expression<Bool?>) -> Expression<Bool?> {
-    return Operator.and.infix(lhs, rhs)
+    Operator.and.infix(lhs, rhs)
 }
 public func &&(lhs: Expression<Bool?>, rhs: Expression<Bool>) -> Expression<Bool?> {
-    return Operator.and.infix(lhs, rhs)
+    Operator.and.infix(lhs, rhs)
 }
 public func &&(lhs: Expression<Bool?>, rhs: Expression<Bool?>) -> Expression<Bool?> {
-    return Operator.and.infix(lhs, rhs)
+    Operator.and.infix(lhs, rhs)
 }
 public func &&(lhs: Expression<Bool>, rhs: Bool) -> Expression<Bool> {
-    return Operator.and.infix(lhs, rhs)
+    Operator.and.infix(lhs, rhs)
 }
 public func &&(lhs: Expression<Bool?>, rhs: Bool) -> Expression<Bool?> {
-    return Operator.and.infix(lhs, rhs)
+    Operator.and.infix(lhs, rhs)
 }
 public func &&(lhs: Bool, rhs: Expression<Bool>) -> Expression<Bool> {
-    return Operator.and.infix(lhs, rhs)
+    Operator.and.infix(lhs, rhs)
 }
 public func &&(lhs: Bool, rhs: Expression<Bool?>) -> Expression<Bool?> {
-    return Operator.and.infix(lhs, rhs)
+    Operator.and.infix(lhs, rhs)
 }
 
 public func or(_ terms: Expression<Bool>...) -> Expression<Bool> {
-    return "OR".infix(terms)
+    "OR".infix(terms)
 }
 public func or(_ terms: [Expression<Bool>]) -> Expression<Bool> {
-    return "OR".infix(terms)
+    "OR".infix(terms)
 }
 public func ||(lhs: Expression<Bool>, rhs: Expression<Bool>) -> Expression<Bool> {
-    return Operator.or.infix(lhs, rhs)
+    Operator.or.infix(lhs, rhs)
 }
 public func ||(lhs: Expression<Bool>, rhs: Expression<Bool?>) -> Expression<Bool?> {
-    return Operator.or.infix(lhs, rhs)
+    Operator.or.infix(lhs, rhs)
 }
 public func ||(lhs: Expression<Bool?>, rhs: Expression<Bool>) -> Expression<Bool?> {
-    return Operator.or.infix(lhs, rhs)
+    Operator.or.infix(lhs, rhs)
 }
 public func ||(lhs: Expression<Bool?>, rhs: Expression<Bool?>) -> Expression<Bool?> {
-    return Operator.or.infix(lhs, rhs)
+    Operator.or.infix(lhs, rhs)
 }
 public func ||(lhs: Expression<Bool>, rhs: Bool) -> Expression<Bool> {
-    return Operator.or.infix(lhs, rhs)
+    Operator.or.infix(lhs, rhs)
 }
 public func ||(lhs: Expression<Bool?>, rhs: Bool) -> Expression<Bool?> {
-    return Operator.or.infix(lhs, rhs)
+    Operator.or.infix(lhs, rhs)
 }
 public func ||(lhs: Bool, rhs: Expression<Bool>) -> Expression<Bool> {
-    return Operator.or.infix(lhs, rhs)
+    Operator.or.infix(lhs, rhs)
 }
 public func ||(lhs: Bool, rhs: Expression<Bool?>) -> Expression<Bool?> {
-    return Operator.or.infix(lhs, rhs)
+    Operator.or.infix(lhs, rhs)
 }
 
 public prefix func !(rhs: Expression<Bool>) -> Expression<Bool> {
-    return Operator.not.wrap(rhs)
+    Operator.not.wrap(rhs)
 }
 
 public prefix func !(rhs: Expression<Bool?>) -> Expression<Bool?> {
-    return Operator.not.wrap(rhs)
+    Operator.not.wrap(rhs)
 }
diff --git a/Sources/SQLite/Typed/Query.swift b/Sources/SQLite/Typed/Query.swift
index 22742809..59b24a8d 100644
--- a/Sources/SQLite/Typed/Query.swift
+++ b/Sources/SQLite/Typed/Query.swift
@@ -53,7 +53,7 @@ extension SchemaType {
     ///
     /// - Returns: A query with the given `SELECT` clause applied.
     public func select(_ column1: Expressible, _ more: Expressible...) -> Self {
-        return select(false, [column1] + more)
+        select(false, [column1] + more)
     }
 
     /// Builds a copy of the query with the `SELECT DISTINCT` clause applied.
@@ -68,7 +68,7 @@ extension SchemaType {
     ///
     /// - Returns: A query with the given `SELECT DISTINCT` clause applied.
     public func select(distinct column1: Expressible, _ more: Expressible...) -> Self {
-        return select(true, [column1] + more)
+        select(true, [column1] + more)
     }
 
     /// Builds a copy of the query with the `SELECT` clause applied.
@@ -84,7 +84,7 @@ extension SchemaType {
     ///
     /// - Returns: A query with the given `SELECT` clause applied.
     public func select(_ all: [Expressible]) -> Self {
-        return select(false, all)
+        select(false, all)
     }
 
     /// Builds a copy of the query with the `SELECT DISTINCT` clause applied.
@@ -99,7 +99,7 @@ extension SchemaType {
     ///
     /// - Returns: A query with the given `SELECT DISTINCT` clause applied.
     public func select(distinct columns: [Expressible]) -> Self {
-        return select(true, columns)
+        select(true, columns)
     }
 
     /// Builds a copy of the query with the `SELECT *` clause applied.
@@ -113,7 +113,7 @@ extension SchemaType {
     ///
     /// - Returns: A query with the given `SELECT *` clause applied.
     public func select(_ star: Star) -> Self {
-        return select([star(nil, nil)])
+        select([star(nil, nil)])
     }
 
     /// Builds a copy of the query with the `SELECT DISTINCT *` clause applied.
@@ -127,7 +127,7 @@ extension SchemaType {
     ///
     /// - Returns: A query with the given `SELECT DISTINCT *` clause applied.
     public func select(distinct star: Star) -> Self {
-        return select(distinct: [star(nil, nil)])
+        select(distinct: [star(nil, nil)])
     }
 
     /// Builds a scalar copy of the query with the `SELECT` clause applied.
@@ -142,10 +142,10 @@ extension SchemaType {
     ///
     /// - Returns: A query with the given `SELECT` clause applied.
     public func select<V: Value>(_ column: Expression<V>) -> ScalarQuery<V> {
-        return select(false, [column])
+        select(false, [column])
     }
     public func select<V: Value>(_ column: Expression<V?>) -> ScalarQuery<V?> {
-        return select(false, [column])
+        select(false, [column])
     }
 
     /// Builds a scalar copy of the query with the `SELECT DISTINCT` clause
@@ -161,14 +161,14 @@ extension SchemaType {
     ///
     /// - Returns: A query with the given `SELECT DISTINCT` clause applied.
     public func select<V: Value>(distinct column: Expression<V>) -> ScalarQuery<V> {
-        return select(true, [column])
+        select(true, [column])
     }
     public func select<V: Value>(distinct column: Expression<V?>) -> ScalarQuery<V?> {
-        return select(true, [column])
+        select(true, [column])
     }
 
     public var count: ScalarQuery<Int> {
-        return select(Expression.count(*))
+        select(Expression.count(*))
     }
 
 }
@@ -223,7 +223,7 @@ extension QueryType {
     ///
     /// - Returns: A query with the given `JOIN` clause applied.
     public func join(_ table: QueryType, on condition: Expression<Bool>) -> Self {
-        return join(table, on: Expression<Bool?>(condition))
+        join(table, on: Expression<Bool?>(condition))
     }
 
     /// Adds a `JOIN` clause to the query.
@@ -244,7 +244,7 @@ extension QueryType {
     ///
     /// - Returns: A query with the given `JOIN` clause applied.
     public func join(_ table: QueryType, on condition: Expression<Bool?>) -> Self {
-        return join(.inner, table, on: condition)
+        join(.inner, table, on: condition)
     }
 
     /// Adds a `JOIN` clause to the query.
@@ -267,7 +267,7 @@ extension QueryType {
     ///
     /// - Returns: A query with the given `JOIN` clause applied.
     public func join(_ type: JoinType, _ table: QueryType, on condition: Expression<Bool>) -> Self {
-        return join(type, table, on: Expression<Bool?>(condition))
+        join(type, table, on: Expression<Bool?>(condition))
     }
 
     /// Adds a `JOIN` clause to the query.
@@ -291,7 +291,8 @@ extension QueryType {
     /// - Returns: A query with the given `JOIN` clause applied.
     public func join(_ type: JoinType, _ table: QueryType, on condition: Expression<Bool?>) -> Self {
         var query = self
-        query.clauses.join.append((type: type, query: table, condition: table.clauses.filters.map { condition && $0 } ?? condition as Expressible))
+        query.clauses.join.append((type: type, query: table,
+                                          condition: table.clauses.filters.map { condition && $0 } ?? condition as Expressible))
         return query
     }
 
@@ -309,7 +310,7 @@ extension QueryType {
     ///
     /// - Returns: A query with the given `WHERE` clause applied.
     public func filter(_ predicate: Expression<Bool>) -> Self {
-        return filter(Expression<Bool?>(predicate))
+        filter(Expression<Bool?>(predicate))
     }
 
     /// Adds a condition to the query’s `WHERE` clause.
@@ -332,13 +333,13 @@ extension QueryType {
     /// Adds a condition to the query’s `WHERE` clause.
     /// This is an alias for `filter(predicate)`
     public func `where`(_ predicate: Expression<Bool>) -> Self {
-        return `where`(Expression<Bool?>(predicate))
+        `where`(Expression<Bool?>(predicate))
     }
 
     /// Adds a condition to the query’s `WHERE` clause.
     /// This is an alias for `filter(predicate)`
     public func `where`(_ predicate: Expression<Bool?>) -> Self {
-        return filter(predicate)
+        filter(predicate)
     }
 
     // MARK: GROUP BY
@@ -349,7 +350,7 @@ extension QueryType {
     ///
     /// - Returns: A query with the given `GROUP BY` clause applied.
     public func group(_ by: Expressible...) -> Self {
-        return group(by)
+        group(by)
     }
 
     /// Sets a `GROUP BY` clause on the query.
@@ -358,7 +359,7 @@ extension QueryType {
     ///
     /// - Returns: A query with the given `GROUP BY` clause applied.
     public func group(_ by: [Expressible]) -> Self {
-        return group(by, nil)
+        group(by, nil)
     }
 
     /// Sets a `GROUP BY`-`HAVING` clause on the query.
@@ -371,7 +372,7 @@ extension QueryType {
     ///
     /// - Returns: A query with the given `GROUP BY`–`HAVING` clause applied.
     public func group(_ by: Expressible, having: Expression<Bool>) -> Self {
-        return group([by], having: having)
+        group([by], having: having)
     }
 
     /// Sets a `GROUP BY`-`HAVING` clause on the query.
@@ -384,7 +385,7 @@ extension QueryType {
     ///
     /// - Returns: A query with the given `GROUP BY`–`HAVING` clause applied.
     public func group(_ by: Expressible, having: Expression<Bool?>) -> Self {
-        return group([by], having: having)
+        group([by], having: having)
     }
 
     /// Sets a `GROUP BY`-`HAVING` clause on the query.
@@ -397,7 +398,7 @@ extension QueryType {
     ///
     /// - Returns: A query with the given `GROUP BY`–`HAVING` clause applied.
     public func group(_ by: [Expressible], having: Expression<Bool>) -> Self {
-        return group(by, Expression<Bool?>(having))
+        group(by, Expression<Bool?>(having))
     }
 
     /// Sets a `GROUP BY`-`HAVING` clause on the query.
@@ -410,7 +411,7 @@ extension QueryType {
     ///
     /// - Returns: A query with the given `GROUP BY`–`HAVING` clause applied.
     public func group(_ by: [Expressible], having: Expression<Bool?>) -> Self {
-        return group(by, having)
+        group(by, having)
     }
 
     fileprivate func group(_ by: [Expressible], _ having: Expression<Bool?>?) -> Self {
@@ -434,7 +435,7 @@ extension QueryType {
     ///
     /// - Returns: A query with the given `ORDER BY` clause applied.
     public func order(_ by: Expressible...) -> Self {
-        return order(by)
+        order(by)
     }
 
     /// Sets an `ORDER BY` clause on the query.
@@ -469,7 +470,7 @@ extension QueryType {
     ///
     /// - Returns: A query with the given LIMIT clause applied.
     public func limit(_ length: Int?) -> Self {
-        return limit(length, nil)
+        limit(length, nil)
     }
 
     /// Sets LIMIT and OFFSET clauses on the query.
@@ -487,7 +488,7 @@ extension QueryType {
     ///
     /// - Returns: A query with the given LIMIT and OFFSET clauses applied.
     public func limit(_ length: Int, offset: Int) -> Self {
-        return limit(length, offset)
+        limit(length, offset)
     }
 
     // prevents limit(nil, offset: 5)
@@ -504,12 +505,13 @@ extension QueryType {
     // MARK: -
 
     fileprivate var selectClause: Expressible {
-        return " ".join([
-            Expression<Void>(literal: clauses.select.distinct ? "SELECT DISTINCT" : "SELECT"),
-            ", ".join(clauses.select.columns),
-            Expression<Void>(literal: "FROM"),
-            tableName(alias: true)
-        ])
+        " ".join([
+           Expression<Void>(literal:
+                            clauses.select.distinct ? "SELECT DISTINCT" : "SELECT"),
+           ", ".join(clauses.select.columns),
+           Expression<Void>(literal: "FROM"),
+           tableName(alias: true)
+       ])
     }
 
     fileprivate var joinClause: Expressible? {
@@ -616,31 +618,31 @@ extension QueryType {
     // MARK: INSERT
 
     public func insert(_ value: Setter, _ more: Setter...) -> Insert {
-        return insert([value] + more)
+        insert([value] + more)
     }
 
     public func insert(_ values: [Setter]) -> Insert {
-        return insert(nil, values)
+        insert(nil, values)
     }
 
     public func insert(or onConflict: OnConflict, _ values: Setter...) -> Insert {
-        return insert(or: onConflict, values)
+        insert(or: onConflict, values)
     }
 
     public func insert(or onConflict: OnConflict, _ values: [Setter]) -> Insert {
-        return insert(onConflict, values)
+        insert(onConflict, values)
     }
 
     public func insertMany( _ values: [[Setter]]) -> Insert {
-        return insertMany(nil, values)
+        insertMany(nil, values)
     }
 
     public func insertMany(or onConflict: OnConflict, _ values: [[Setter]]) -> Insert {
-        return insertMany(onConflict, values)
+        insertMany(onConflict, values)
     }
 
     public func insertMany(or onConflict: OnConflict, _ values: [Setter]...) -> Insert {
-        return insertMany(onConflict, values)
+        insertMany(onConflict, values)
     }
 
     fileprivate func insert(_ or: OnConflict?, _ values: [Setter]) -> Insert {
@@ -689,7 +691,7 @@ extension QueryType {
 
     /// Runs an `INSERT` statement against the query with `DEFAULT VALUES`.
     public func insert() -> Insert {
-        return Insert(" ".join([
+        Insert(" ".join([
             Expression<Void>(literal: "INSERT INTO"),
             tableName(),
             Expression<Void>(literal: "DEFAULT VALUES")
@@ -703,17 +705,17 @@ extension QueryType {
     ///
     /// - Returns: The number of updated rows and statement.
     public func insert(_ query: QueryType) -> Update {
-        return Update(" ".join([
+        Update(" ".join([
             Expression<Void>(literal: "INSERT INTO"),
             tableName(),
             query.expression
-        ]).expression)
+       ]).expression)
     }
 
     // MARK: UPSERT
 
     public func upsert(_ insertValues: Setter..., onConflictOf conflicting: Expressible) -> Insert {
-        return upsert(insertValues, onConflictOf: conflicting)
+        upsert(insertValues, onConflictOf: conflicting)
     }
 
     public func upsert(_ insertValues: [Setter], onConflictOf conflicting: Expressible) -> Insert {
@@ -723,7 +725,7 @@ extension QueryType {
     }
 
     public func upsert(_ insertValues: Setter..., onConflictOf conflicting: Expressible, set setValues: [Setter]) -> Insert {
-        return upsert(insertValues, onConflictOf: conflicting, set: setValues)
+        upsert(insertValues, onConflictOf: conflicting, set: setValues)
     }
 
     public func upsert(_ insertValues: [Setter], onConflictOf conflicting: Expressible, set setValues: [Setter]) -> Insert {
@@ -751,7 +753,7 @@ extension QueryType {
     // MARK: UPDATE
 
     public func update(_ values: Setter...) -> Update {
-        return update(values)
+        update(values)
     }
 
     public func update(_ values: [Setter]) -> Update {
@@ -785,7 +787,7 @@ extension QueryType {
     // MARK: EXISTS
 
     public var exists: Select<Bool> {
-        return Select(" ".join([
+        Select(" ".join([
             Expression<Void>(literal: "SELECT EXISTS"),
             "".wrap(expression) as Expression<Void>
         ]).expression)
@@ -800,15 +802,15 @@ extension QueryType {
     /// - Returns: A column expression namespaced with the query’s table name or
     ///   alias.
     public func namespace<V>(_ column: Expression<V>) -> Expression<V> {
-        return Expression(".".join([tableName(), column]).expression)
+        Expression(".".join([tableName(), column]).expression)
     }
 
     public subscript<T>(column: Expression<T>) -> Expression<T> {
-        return namespace(column)
+        namespace(column)
     }
 
     public subscript<T>(column: Expression<T?>) -> Expression<T?> {
-        return namespace(column)
+        namespace(column)
     }
 
     /// Prefixes a star with the query’s table name or alias.
@@ -818,7 +820,7 @@ extension QueryType {
     /// - Returns: A `*` expression namespaced with the query’s table name or
     ///   alias.
     public subscript(star: Star) -> Expression<Void> {
-        return namespace(star(nil, nil))
+        namespace(star(nil, nil))
     }
 
     // MARK: -
@@ -977,7 +979,7 @@ public struct RowIterator: FailableIterator {
     let columnNames: [String: Int]
 
     public func failableNext() throws -> Row? {
-        return try statement.failableNext().flatMap { Row(columnNames, $0) }
+        try statement.failableNext().flatMap { Row(columnNames, $0) }
     }
 
     public func map<T>(_ transform: (Element) throws -> T) throws -> [T] {
@@ -1016,7 +1018,7 @@ extension Connection {
             let namespace = names.joined(separator: ".")
 
             func expandGlob(_ namespace: Bool) -> (QueryType) throws -> Void {
-                return { (queryType: QueryType) throws -> Void in
+                { (queryType: QueryType) throws -> Void in
                     var query = type(of: queryType).init(queryType.clauses.from.name, database: queryType.clauses.from.database)
                     query.clauses.select = queryType.clauses.select
                     let expression = query.expression
@@ -1074,7 +1076,7 @@ extension Connection {
     }
 
     public func pluck(_ query: QueryType) throws -> Row? {
-        return try prepareRowIterator(query.limit(1, query.clauses.limit?.offset)).failableNext()
+        try prepareRowIterator(query.limit(1, query.clauses.limit?.offset)).failableNext()
     }
 
     /// Runs an `Insert` query.
diff --git a/Sources/SQLite/Typed/Schema.swift b/Sources/SQLite/Typed/Schema.swift
index d8d45e41..726f3e27 100644
--- a/Sources/SQLite/Typed/Schema.swift
+++ b/Sources/SQLite/Typed/Schema.swift
@@ -27,7 +27,7 @@ extension SchemaType {
     // MARK: - DROP TABLE / VIEW / VIRTUAL TABLE
 
     public func drop(ifExists: Bool = false) -> String {
-        return drop("TABLE", tableName(), ifExists)
+        drop("TABLE", tableName(), ifExists)
     }
 
 }
@@ -64,63 +64,63 @@ extension Table {
     // MARK: - ALTER TABLE … ADD COLUMN
 
     public func addColumn<V: Value>(_ name: Expression<V>, check: Expression<Bool>? = nil, defaultValue: V) -> String {
-        return addColumn(definition(name, V.declaredDatatype, nil, false, false, check, defaultValue, nil, nil))
+        addColumn(definition(name, V.declaredDatatype, nil, false, false, check, defaultValue, nil, nil))
     }
 
     public func addColumn<V: Value>(_ name: Expression<V>, check: Expression<Bool?>, defaultValue: V) -> String {
-        return addColumn(definition(name, V.declaredDatatype, nil, false, false, check, defaultValue, nil, nil))
+        addColumn(definition(name, V.declaredDatatype, nil, false, false, check, defaultValue, nil, nil))
     }
 
     public func addColumn<V: Value>(_ name: Expression<V?>, check: Expression<Bool>? = nil, defaultValue: V? = nil) -> String {
-        return addColumn(definition(name, V.declaredDatatype, nil, true, false, check, defaultValue, nil, nil))
+        addColumn(definition(name, V.declaredDatatype, nil, true, false, check, defaultValue, nil, nil))
     }
 
     public func addColumn<V: Value>(_ name: Expression<V?>, check: Expression<Bool?>, defaultValue: V? = nil) -> String {
-        return addColumn(definition(name, V.declaredDatatype, nil, true, false, check, defaultValue, nil, nil))
+        addColumn(definition(name, V.declaredDatatype, nil, true, false, check, defaultValue, nil, nil))
     }
 
     public func addColumn<V: Value>(_ name: Expression<V>, unique: Bool = false, check: Expression<Bool>? = nil,
                                     references table: QueryType, _ other: Expression<V>) -> String where V.Datatype == Int64 {
-        return addColumn(definition(name, V.declaredDatatype, nil, false, unique, check, nil, (table, other), nil))
+        addColumn(definition(name, V.declaredDatatype, nil, false, unique, check, nil, (table, other), nil))
     }
 
     public func addColumn<V: Value>(_ name: Expression<V>, unique: Bool = false, check: Expression<Bool?>,
                                     references table: QueryType, _ other: Expression<V>) -> String where V.Datatype == Int64 {
-        return addColumn(definition(name, V.declaredDatatype, nil, false, unique, check, nil, (table, other), nil))
+        addColumn(definition(name, V.declaredDatatype, nil, false, unique, check, nil, (table, other), nil))
     }
 
     public func addColumn<V: Value>(_ name: Expression<V?>, unique: Bool = false, check: Expression<Bool>? = nil,
                                     references table: QueryType, _ other: Expression<V>) -> String where V.Datatype == Int64 {
-        return addColumn(definition(name, V.declaredDatatype, nil, true, unique, check, nil, (table, other), nil))
+        addColumn(definition(name, V.declaredDatatype, nil, true, unique, check, nil, (table, other), nil))
     }
 
     public func addColumn<V: Value>(_ name: Expression<V?>, unique: Bool = false, check: Expression<Bool?>,
                                     references table: QueryType, _ other: Expression<V>) -> String where V.Datatype == Int64 {
-        return addColumn(definition(name, V.declaredDatatype, nil, true, unique, check, nil, (table, other), nil))
+        addColumn(definition(name, V.declaredDatatype, nil, true, unique, check, nil, (table, other), nil))
     }
 
     public func addColumn<V: Value>(_ name: Expression<V>, check: Expression<Bool>? = nil, defaultValue: V,
                                     collate: Collation) -> String where V.Datatype == String {
-        return addColumn(definition(name, V.declaredDatatype, nil, false, false, check, defaultValue, nil, collate))
+        addColumn(definition(name, V.declaredDatatype, nil, false, false, check, defaultValue, nil, collate))
     }
 
     public func addColumn<V: Value>(_ name: Expression<V>, check: Expression<Bool?>, defaultValue: V,
                                     collate: Collation) -> String where V.Datatype == String {
-        return addColumn(definition(name, V.declaredDatatype, nil, false, false, check, defaultValue, nil, collate))
+        addColumn(definition(name, V.declaredDatatype, nil, false, false, check, defaultValue, nil, collate))
     }
 
     public func addColumn<V: Value>(_ name: Expression<V?>, check: Expression<Bool>? = nil, defaultValue: V? = nil,
                                     collate: Collation) -> String where V.Datatype == String {
-        return addColumn(definition(name, V.declaredDatatype, nil, true, false, check, defaultValue, nil, collate))
+        addColumn(definition(name, V.declaredDatatype, nil, true, false, check, defaultValue, nil, collate))
     }
 
     public func addColumn<V: Value>(_ name: Expression<V?>, check: Expression<Bool?>, defaultValue: V? = nil,
                                     collate: Collation) -> String where V.Datatype == String {
-        return addColumn(definition(name, V.declaredDatatype, nil, true, false, check, defaultValue, nil, collate))
+        addColumn(definition(name, V.declaredDatatype, nil, true, false, check, defaultValue, nil, collate))
     }
 
     fileprivate func addColumn(_ expression: Expressible) -> String {
-        return " ".join([
+        " ".join([
             Expression<Void>(literal: "ALTER TABLE"),
             tableName(),
             Expression<Void>(literal: "ADD COLUMN"),
@@ -131,7 +131,7 @@ extension Table {
     // MARK: - ALTER TABLE … RENAME TO
 
     public func rename(_ to: Table) -> String {
-        return rename(to: to)
+        rename(to: to)
     }
 
     // MARK: - CREATE INDEX
@@ -150,7 +150,7 @@ extension Table {
     // MARK: - DROP INDEX
 
     public func dropIndex(_ columns: Expressible..., ifExists: Bool = false) -> String {
-        return drop("INDEX", indexName(columns), ifExists)
+        drop("INDEX", indexName(columns), ifExists)
     }
 
     fileprivate func indexName(_ columns: [Expressible]) -> Expressible {
@@ -188,7 +188,7 @@ extension View {
     // MARK: - DROP VIEW
 
     public func drop(ifExists: Bool = false) -> String {
-        return drop("VIEW", tableName(), ifExists)
+        drop("VIEW", tableName(), ifExists)
     }
 
 }
@@ -210,7 +210,7 @@ extension VirtualTable {
     // MARK: - ALTER TABLE … RENAME TO
 
     public func rename(_ to: VirtualTable) -> String {
-        return rename(to: to)
+        rename(to: to)
     }
 
 }
@@ -495,7 +495,7 @@ public struct Module {
 extension Module: Expressible {
 
     public var expression: Expression<Void> {
-        return name.wrap(arguments)
+        name.wrap(arguments)
     }
 
 }
@@ -517,7 +517,7 @@ private extension QueryType {
     }
 
     func rename(to: Self) -> String {
-        return " ".join([
+        " ".join([
             Expression<Void>(literal: "ALTER TABLE"),
             tableName(),
             Expression<Void>(literal: "RENAME TO"),
@@ -557,7 +557,7 @@ private func definition(_ column: Expressible, _ datatype: String, _ primaryKey:
 }
 
 private func reference(_ primary: (QueryType, Expressible)) -> Expressible {
-    return " ".join([
+    " ".join([
         Expression<Void>(literal: "REFERENCES"),
         primary.0.tableName(qualified: false),
         "".wrap(primary.1) as Expression<Void>
diff --git a/Sources/SQLite/Typed/Setter.swift b/Sources/SQLite/Typed/Setter.swift
index 08fb1748..7910cab8 100644
--- a/Sources/SQLite/Typed/Setter.swift
+++ b/Sources/SQLite/Typed/Setter.swift
@@ -70,213 +70,213 @@ public struct Setter {
 extension Setter: Expressible {
 
     public var expression: Expression<Void> {
-        return "=".infix(column, value, wrap: false)
+        "=".infix(column, value, wrap: false)
     }
 
 }
 
 public func <-<V: Value>(column: Expression<V>, value: Expression<V>) -> Setter {
-    return Setter(column: column, value: value)
+    Setter(column: column, value: value)
 }
 public func <-<V: Value>(column: Expression<V>, value: V) -> Setter {
-    return Setter(column: column, value: value)
+    Setter(column: column, value: value)
 }
 public func <-<V: Value>(column: Expression<V?>, value: Expression<V>) -> Setter {
-    return Setter(column: column, value: value)
+    Setter(column: column, value: value)
 }
 public func <-<V: Value>(column: Expression<V?>, value: Expression<V?>) -> Setter {
-    return Setter(column: column, value: value)
+    Setter(column: column, value: value)
 }
 public func <-<V: Value>(column: Expression<V?>, value: V?) -> Setter {
-    return Setter(column: column, value: value)
+    Setter(column: column, value: value)
 }
 
 public func +=(column: Expression<String>, value: Expression<String>) -> Setter {
-    return column <- column + value
+    column <- column + value
 }
 public func +=(column: Expression<String>, value: String) -> Setter {
-    return column <- column + value
+    column <- column + value
 }
 public func +=(column: Expression<String?>, value: Expression<String>) -> Setter {
-    return column <- column + value
+    column <- column + value
 }
 public func +=(column: Expression<String?>, value: Expression<String?>) -> Setter {
-    return column <- column + value
+    column <- column + value
 }
 public func +=(column: Expression<String?>, value: String) -> Setter {
-    return column <- column + value
+    column <- column + value
 }
 
 public func +=<V: Value>(column: Expression<V>, value: Expression<V>) -> Setter where V.Datatype: Number {
-    return column <- column + value
+    column <- column + value
 }
 public func +=<V: Value>(column: Expression<V>, value: V) -> Setter where V.Datatype: Number {
-    return column <- column + value
+    column <- column + value
 }
 public func +=<V: Value>(column: Expression<V?>, value: Expression<V>) -> Setter where V.Datatype: Number {
-    return column <- column + value
+    column <- column + value
 }
 public func +=<V: Value>(column: Expression<V?>, value: Expression<V?>) -> Setter where V.Datatype: Number {
-    return column <- column + value
+    column <- column + value
 }
 public func +=<V: Value>(column: Expression<V?>, value: V) -> Setter where V.Datatype: Number {
-    return column <- column + value
+    column <- column + value
 }
 
 public func -=<V: Value>(column: Expression<V>, value: Expression<V>) -> Setter where V.Datatype: Number {
-    return column <- column - value
+    column <- column - value
 }
 public func -=<V: Value>(column: Expression<V>, value: V) -> Setter where V.Datatype: Number {
-    return column <- column - value
+    column <- column - value
 }
 public func -=<V: Value>(column: Expression<V?>, value: Expression<V>) -> Setter where V.Datatype: Number {
-    return column <- column - value
+    column <- column - value
 }
 public func -=<V: Value>(column: Expression<V?>, value: Expression<V?>) -> Setter where V.Datatype: Number {
-    return column <- column - value
+    column <- column - value
 }
 public func -=<V: Value>(column: Expression<V?>, value: V) -> Setter where V.Datatype: Number {
-    return column <- column - value
+    column <- column - value
 }
 
 public func *=<V: Value>(column: Expression<V>, value: Expression<V>) -> Setter where V.Datatype: Number {
-    return column <- column * value
+    column <- column * value
 }
 public func *=<V: Value>(column: Expression<V>, value: V) -> Setter where V.Datatype: Number {
-    return column <- column * value
+    column <- column * value
 }
 public func *=<V: Value>(column: Expression<V?>, value: Expression<V>) -> Setter where V.Datatype: Number {
-    return column <- column * value
+    column <- column * value
 }
 public func *=<V: Value>(column: Expression<V?>, value: Expression<V?>) -> Setter where V.Datatype: Number {
-    return column <- column * value
+    column <- column * value
 }
 public func *=<V: Value>(column: Expression<V?>, value: V) -> Setter where V.Datatype: Number {
-    return column <- column * value
+    column <- column * value
 }
 
 public func /=<V: Value>(column: Expression<V>, value: Expression<V>) -> Setter where V.Datatype: Number {
-    return column <- column / value
+    column <- column / value
 }
 public func /=<V: Value>(column: Expression<V>, value: V) -> Setter where V.Datatype: Number {
-    return column <- column / value
+    column <- column / value
 }
 public func /=<V: Value>(column: Expression<V?>, value: Expression<V>) -> Setter where V.Datatype: Number {
-    return column <- column / value
+    column <- column / value
 }
 public func /=<V: Value>(column: Expression<V?>, value: Expression<V?>) -> Setter where V.Datatype: Number {
-    return column <- column / value
+    column <- column / value
 }
 public func /=<V: Value>(column: Expression<V?>, value: V) -> Setter where V.Datatype: Number {
-    return column <- column / value
+    column <- column / value
 }
 
 public func %=<V: Value>(column: Expression<V>, value: Expression<V>) -> Setter where V.Datatype == Int64 {
-    return column <- column % value
+    column <- column % value
 }
 public func %=<V: Value>(column: Expression<V>, value: V) -> Setter where V.Datatype == Int64 {
-    return column <- column % value
+    column <- column % value
 }
 public func %=<V: Value>(column: Expression<V?>, value: Expression<V>) -> Setter where V.Datatype == Int64 {
-    return column <- column % value
+    column <- column % value
 }
 public func %=<V: Value>(column: Expression<V?>, value: Expression<V?>) -> Setter where V.Datatype == Int64 {
-    return column <- column % value
+    column <- column % value
 }
 public func %=<V: Value>(column: Expression<V?>, value: V) -> Setter where V.Datatype == Int64 {
-    return column <- column % value
+    column <- column % value
 }
 
 public func <<=<V: Value>(column: Expression<V>, value: Expression<V>) -> Setter where V.Datatype == Int64 {
-    return column <- column << value
+    column <- column << value
 }
 public func <<=<V: Value>(column: Expression<V>, value: V) -> Setter where V.Datatype == Int64 {
-    return column <- column << value
+    column <- column << value
 }
 public func <<=<V: Value>(column: Expression<V?>, value: Expression<V>) -> Setter where V.Datatype == Int64 {
-    return column <- column << value
+    column <- column << value
 }
 public func <<=<V: Value>(column: Expression<V?>, value: Expression<V?>) -> Setter where V.Datatype == Int64 {
-    return column <- column << value
+    column <- column << value
 }
 public func <<=<V: Value>(column: Expression<V?>, value: V) -> Setter where V.Datatype == Int64 {
-    return column <- column << value
+    column <- column << value
 }
 
 public func >>=<V: Value>(column: Expression<V>, value: Expression<V>) -> Setter where V.Datatype == Int64 {
-    return column <- column >> value
+    column <- column >> value
 }
 public func >>=<V: Value>(column: Expression<V>, value: V) -> Setter where V.Datatype == Int64 {
-    return column <- column >> value
+    column <- column >> value
 }
 public func >>=<V: Value>(column: Expression<V?>, value: Expression<V>) -> Setter where V.Datatype == Int64 {
-    return column <- column >> value
+    column <- column >> value
 }
 public func >>=<V: Value>(column: Expression<V?>, value: Expression<V?>) -> Setter where V.Datatype == Int64 {
-    return column <- column >> value
+    column <- column >> value
 }
 public func >>=<V: Value>(column: Expression<V?>, value: V) -> Setter where V.Datatype == Int64 {
-    return column <- column >> value
+    column <- column >> value
 }
 
 public func &=<V: Value>(column: Expression<V>, value: Expression<V>) -> Setter where V.Datatype == Int64 {
-    return column <- column & value
+    column <- column & value
 }
 public func &=<V: Value>(column: Expression<V>, value: V) -> Setter where V.Datatype == Int64 {
-    return column <- column & value
+    column <- column & value
 }
 public func &=<V: Value>(column: Expression<V?>, value: Expression<V>) -> Setter where V.Datatype == Int64 {
-    return column <- column & value
+    column <- column & value
 }
 public func &=<V: Value>(column: Expression<V?>, value: Expression<V?>) -> Setter where V.Datatype == Int64 {
-    return column <- column & value
+    column <- column & value
 }
 public func &=<V: Value>(column: Expression<V?>, value: V) -> Setter where V.Datatype == Int64 {
-    return column <- column & value
+    column <- column & value
 }
 
 public func |=<V: Value>(column: Expression<V>, value: Expression<V>) -> Setter where V.Datatype == Int64 {
-    return column <- column | value
+    column <- column | value
 }
 public func |=<V: Value>(column: Expression<V>, value: V) -> Setter where V.Datatype == Int64 {
-    return column <- column | value
+    column <- column | value
 }
 public func |=<V: Value>(column: Expression<V?>, value: Expression<V>) -> Setter where V.Datatype == Int64 {
-    return column <- column | value
+    column <- column | value
 }
 public func |=<V: Value>(column: Expression<V?>, value: Expression<V?>) -> Setter where V.Datatype == Int64 {
-    return column <- column | value
+    column <- column | value
 }
 public func |=<V: Value>(column: Expression<V?>, value: V) -> Setter where V.Datatype == Int64 {
-    return column <- column | value
+    column <- column | value
 }
 
 public func ^=<V: Value>(column: Expression<V>, value: Expression<V>) -> Setter where V.Datatype == Int64 {
-    return column <- column ^ value
+    column <- column ^ value
 }
 public func ^=<V: Value>(column: Expression<V>, value: V) -> Setter where V.Datatype == Int64 {
-    return column <- column ^ value
+    column <- column ^ value
 }
 public func ^=<V: Value>(column: Expression<V?>, value: Expression<V>) -> Setter where V.Datatype == Int64 {
-    return column <- column ^ value
+    column <- column ^ value
 }
 public func ^=<V: Value>(column: Expression<V?>, value: Expression<V?>) -> Setter where V.Datatype == Int64 {
-    return column <- column ^ value
+    column <- column ^ value
 }
 public func ^=<V: Value>(column: Expression<V?>, value: V) -> Setter where V.Datatype == Int64 {
-    return column <- column ^ value
+    column <- column ^ value
 }
 
 public postfix func ++<V: Value>(column: Expression<V>) -> Setter where V.Datatype == Int64 {
-    return Expression<Int>(column) += 1
+    Expression<Int>(column) += 1
 }
 public postfix func ++<V: Value>(column: Expression<V?>) -> Setter where V.Datatype == Int64 {
-    return Expression<Int>(column) += 1
+    Expression<Int>(column) += 1
 }
 
 public postfix func --<V: Value>(column: Expression<V>) -> Setter where V.Datatype == Int64 {
-    return Expression<Int>(column) -= 1
+    Expression<Int>(column) -= 1
 }
 public postfix func --<V: Value>(column: Expression<V?>) -> Setter where V.Datatype == Int64 {
-    return Expression<Int>(column) -= 1
+    Expression<Int>(column) -= 1
 }
diff --git a/Tests/SQLiteTests/ConnectionTests.swift b/Tests/SQLiteTests/ConnectionTests.swift
index 44c2c302..045a8274 100644
--- a/Tests/SQLiteTests/ConnectionTests.swift
+++ b/Tests/SQLiteTests/ConnectionTests.swift
@@ -362,14 +362,14 @@ class ConnectionTests: SQLiteTestCase {
 
     func test_createCollation_createsCollation() {
         try! db.createCollation("NODIACRITIC") { lhs, rhs in
-            return lhs.compare(rhs, options: .diacriticInsensitive)
+            lhs.compare(rhs, options: .diacriticInsensitive)
         }
         XCTAssertEqual(1, try! db.scalar("SELECT ? = ? COLLATE NODIACRITIC", "cafe", "café") as? Int64)
     }
 
     func test_createCollation_createsQuotableCollation() {
         try! db.createCollation("NO DIACRITIC") { lhs, rhs in
-            return lhs.compare(rhs, options: .diacriticInsensitive)
+            lhs.compare(rhs, options: .diacriticInsensitive)
         }
         XCTAssertEqual(1, try! db.scalar("SELECT ? = ? COLLATE \"NO DIACRITIC\"", "cafe", "café") as? Int64)
     }
diff --git a/Tests/SQLiteTests/CustomFunctionsTests.swift b/Tests/SQLiteTests/CustomFunctionsTests.swift
index 0ebcd13c..af1ef110 100644
--- a/Tests/SQLiteTests/CustomFunctionsTests.swift
+++ b/Tests/SQLiteTests/CustomFunctionsTests.swift
@@ -7,7 +7,7 @@ class CustomFunctionNoArgsTests: SQLiteTestCase {
 
     func testFunctionNoOptional() {
         let _: FunctionNoOptional = try! db.createFunction("test", deterministic: true) {
-            return "a"
+            "a"
         }
         let result = try! db.prepare("SELECT test()").scalar() as! String
         XCTAssertEqual("a", result)
@@ -15,7 +15,7 @@ class CustomFunctionNoArgsTests: SQLiteTestCase {
 
     func testFunctionResultOptional() {
         let _: FunctionResultOptional = try! db.createFunction("test", deterministic: true) {
-            return "a"
+            "a"
         }
         let result = try! db.prepare("SELECT test()").scalar() as! String?
         XCTAssertEqual("a", result)
@@ -30,7 +30,7 @@ class CustomFunctionWithOneArgTests: SQLiteTestCase {
 
     func testFunctionNoOptional() {
         let _: FunctionNoOptional = try! db.createFunction("test", deterministic: true) { a in
-            return "b"+a
+            "b" + a
         }
         let result = try! db.prepare("SELECT test(?)").scalar("a") as! String
         XCTAssertEqual("ba", result)
@@ -38,7 +38,7 @@ class CustomFunctionWithOneArgTests: SQLiteTestCase {
 
     func testFunctionLeftOptional() {
         let _: FunctionLeftOptional = try! db.createFunction("test", deterministic: true) { a in
-            return "b"+a!
+            "b" + a!
         }
         let result = try! db.prepare("SELECT test(?)").scalar("a") as! String
         XCTAssertEqual("ba", result)
@@ -46,7 +46,7 @@ class CustomFunctionWithOneArgTests: SQLiteTestCase {
 
     func testFunctionResultOptional() {
         let _: FunctionResultOptional = try! db.createFunction("test", deterministic: true) { a in
-            return "b"+a
+            "b" + a
         }
         let result = try! db.prepare("SELECT test(?)").scalar("a") as! String
         XCTAssertEqual("ba", result)
@@ -54,7 +54,7 @@ class CustomFunctionWithOneArgTests: SQLiteTestCase {
 
     func testFunctionLeftResultOptional() {
         let _: FunctionLeftResultOptional = try! db.createFunction("test", deterministic: true) { (a: String?) -> String? in
-            return "b"+a!
+            "b" + a!
         }
         let result = try! db.prepare("SELECT test(?)").scalar("a") as! String
         XCTAssertEqual("ba", result)
@@ -73,7 +73,7 @@ class CustomFunctionWithTwoArgsTests: SQLiteTestCase {
 
     func testNoOptional() {
         let _: FunctionNoOptional = try! db.createFunction("test", deterministic: true) { a, b in
-            return a+b
+            a + b
         }
         let result = try! db.prepare("SELECT test(?, ?)").scalar("a", "b") as! String
         XCTAssertEqual("ab", result)
@@ -81,7 +81,7 @@ class CustomFunctionWithTwoArgsTests: SQLiteTestCase {
 
     func testLeftOptional() {
         let _: FunctionLeftOptional = try! db.createFunction("test", deterministic: true) { a, b in
-            return a!+b
+            a! + b
         }
         let result = try! db.prepare("SELECT test(?, ?)").scalar("a", "b") as! String
         XCTAssertEqual("ab", result)
@@ -89,7 +89,7 @@ class CustomFunctionWithTwoArgsTests: SQLiteTestCase {
 
     func testRightOptional() {
         let _: FunctionRightOptional = try! db.createFunction("test", deterministic: true) { a, b in
-            return a+b!
+            a + b!
         }
         let result = try! db.prepare("SELECT test(?, ?)").scalar("a", "b") as! String
         XCTAssertEqual("ab", result)
@@ -97,7 +97,7 @@ class CustomFunctionWithTwoArgsTests: SQLiteTestCase {
 
     func testResultOptional() {
         let _: FunctionResultOptional = try! db.createFunction("test", deterministic: true) { a, b in
-            return a+b
+            a + b
         }
         let result = try! db.prepare("SELECT test(?, ?)").scalar("a", "b") as! String?
         XCTAssertEqual("ab", result)
@@ -105,7 +105,7 @@ class CustomFunctionWithTwoArgsTests: SQLiteTestCase {
 
     func testFunctionLeftRightOptional() {
         let _: FunctionLeftRightOptional = try! db.createFunction("test", deterministic: true) { a, b in
-            return a!+b!
+            a! + b!
         }
         let result = try! db.prepare("SELECT test(?, ?)").scalar("a", "b") as! String
         XCTAssertEqual("ab", result)
@@ -113,7 +113,7 @@ class CustomFunctionWithTwoArgsTests: SQLiteTestCase {
 
     func testFunctionLeftResultOptional() {
         let _: FunctionLeftResultOptional = try! db.createFunction("test", deterministic: true) { a, b in
-            return a!+b
+            a! + b
         }
         let result = try! db.prepare("SELECT test(?, ?)").scalar("a", "b") as! String?
         XCTAssertEqual("ab", result)
@@ -121,7 +121,7 @@ class CustomFunctionWithTwoArgsTests: SQLiteTestCase {
 
     func testFunctionRightResultOptional() {
         let _: FunctionRightResultOptional = try! db.createFunction("test", deterministic: true) { a, b in
-            return a+b!
+            a + b!
         }
         let result = try! db.prepare("SELECT test(?, ?)").scalar("a", "b") as! String?
         XCTAssertEqual("ab", result)
@@ -129,7 +129,7 @@ class CustomFunctionWithTwoArgsTests: SQLiteTestCase {
 
     func testFunctionLeftRightResultOptional() {
         let _: FunctionLeftRightResultOptional = try! db.createFunction("test", deterministic: true) { a, b in
-            return a!+b!
+            a! + b!
         }
         let result = try! db.prepare("SELECT test(?, ?)").scalar("a", "b") as! String?
         XCTAssertEqual("ab", result)
diff --git a/Tests/SQLiteTests/FTS4Tests.swift b/Tests/SQLiteTests/FTS4Tests.swift
index b637955b..33be422c 100644
--- a/Tests/SQLiteTests/FTS4Tests.swift
+++ b/Tests/SQLiteTests/FTS4Tests.swift
@@ -185,7 +185,7 @@ class FTS4ConfigTests: XCTestCase {
     }
 
     func sql(_ config: FTS4Config) -> String {
-        return virtualTable.create(.FTS4(config))
+        virtualTable.create(.FTS4(config))
     }
 }
 
diff --git a/Tests/SQLiteTests/FTS5Tests.swift b/Tests/SQLiteTests/FTS5Tests.swift
index 8079b28c..4cce4523 100644
--- a/Tests/SQLiteTests/FTS5Tests.swift
+++ b/Tests/SQLiteTests/FTS5Tests.swift
@@ -122,6 +122,6 @@ class FTS5Tests: XCTestCase {
     }
 
     func sql(_ config: FTS5Config) -> String {
-        return virtualTable.create(.FTS5(config))
+        virtualTable.create(.FTS5(config))
     }
 }
diff --git a/Tests/SQLiteTests/TestHelpers.swift b/Tests/SQLiteTests/TestHelpers.swift
index 0994dd97..0aa918f0 100644
--- a/Tests/SQLiteTests/TestHelpers.swift
+++ b/Tests/SQLiteTests/TestHelpers.swift
@@ -42,10 +42,8 @@ class SQLiteTestCase: XCTestCase {
     }
 
     @discardableResult func insertUser(_ name: String, age: Int? = nil, admin: Bool = false) throws -> Statement {
-        return try db.run(
-            "INSERT INTO \"users\" (email, age, admin) values (?, ?, ?)",
-            "\(name)@example.com", age?.datatypeValue, admin.datatypeValue
-        )
+        try db.run("INSERT INTO \"users\" (email, age, admin) values (?, ?, ?)",
+                   "\(name)@example.com", age?.datatypeValue, admin.datatypeValue)
     }
 
     func assertSQL(_ SQL: String, _ executions: Int = 1, _ message: String? = nil, file: StaticString = #file, line: UInt = #line) {

From 9b3781c66197ed1d30894fbc831112b54786f21e Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Mon, 23 Aug 2021 11:06:05 +0200
Subject: [PATCH 051/326] Unneeded

---
 Tests/SQLiteTests/ConnectionTests.swift | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/Tests/SQLiteTests/ConnectionTests.swift b/Tests/SQLiteTests/ConnectionTests.swift
index 045a8274..37b765ab 100644
--- a/Tests/SQLiteTests/ConnectionTests.swift
+++ b/Tests/SQLiteTests/ConnectionTests.swift
@@ -217,8 +217,6 @@ class ConnectionTests: SQLiteTestCase {
     }
 
     func test_savepoint_beginsAndCommitsSavepoints() {
-        let db: Connection = db
-
         try! db.savepoint("1") {
             try db.savepoint("2") {
                 try db.run("INSERT INTO users (email) VALUES (?)", "alice@example.com")
@@ -235,7 +233,6 @@ class ConnectionTests: SQLiteTestCase {
     }
 
     func test_savepoint_beginsAndRollsSavepointsBack() {
-        let db: Connection = db
         let stmt = try! db.prepare("INSERT INTO users (email) VALUES (?)", "alice@example.com")
 
         do {

From 6a1b8f355a808255a850c58d65ca56b1b68df9bf Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Mon, 23 Aug 2021 11:32:10 +0200
Subject: [PATCH 052/326] Add a release checklist

---
 .cocoadocs.yml                                |  2 --
 Documentation/Release.md                      | 10 ++++++
 SQLite.xcodeproj/project.pbxproj              |  2 ++
 Sources/SQLite/Typed/CustomFunctions.swift    | 24 ++++++-------
 .../SQLiteTests/CustomAggregationTests.swift  | 34 +++++++++----------
 5 files changed, 41 insertions(+), 31 deletions(-)
 delete mode 100644 .cocoadocs.yml
 create mode 100644 Documentation/Release.md

diff --git a/.cocoadocs.yml b/.cocoadocs.yml
deleted file mode 100644
index 27840032..00000000
--- a/.cocoadocs.yml
+++ /dev/null
@@ -1,2 +0,0 @@
-additional_guides:
-  - Documentation/Index.md
diff --git a/Documentation/Release.md b/Documentation/Release.md
new file mode 100644
index 00000000..ef174683
--- /dev/null
+++ b/Documentation/Release.md
@@ -0,0 +1,10 @@
+# SQLite.swift Release checklist
+
+* [ ] Make sure current master branch has a green build
+* [ ] Make sure `CHANGELOG.md` is up-to-date
+* [ ] Run `pod lib lint` locally
+* [ ] Update the version numbers mentioned in `README.md`, `Documentation/Index.md`
+* [ ] Update `MARKETING_VERSION` in `SQLite.xcodeproj/project.pbxproj`
+* [ ] Create a tag with the version number (`x.y.z`)
+* [ ] Publish to CocoaPods: `pod trunk push`
+* [ ] Update the release information on GitHub
diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj
index 03d8b326..3b5428d0 100644
--- a/SQLite.xcodeproj/project.pbxproj
+++ b/SQLite.xcodeproj/project.pbxproj
@@ -232,6 +232,7 @@
 		19A17BA55DABB480F9020C8A /* DateAndTimeFunctions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DateAndTimeFunctions.swift; sourceTree = "<group>"; };
 		19A17E2695737FAB5D6086E3 /* fixtures */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = folder; path = fixtures; sourceTree = "<group>"; };
 		3717F907221F5D7C00B9BD3D /* CustomAggregationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomAggregationTests.swift; sourceTree = "<group>"; };
+		19A17EA3A313F129011B3FA0 /* Release.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = Release.md; sourceTree = "<group>"; };
 		3D67B3E51DB2469200A4F4C6 /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = Platforms/WatchOS.platform/Developer/SDKs/WatchOS3.0.sdk/usr/lib/libsqlite3.tbd; sourceTree = DEVELOPER_DIR; };
 		3DDC112E26CDBA0200CE369F /* SQLiteObjc.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SQLiteObjc.h; path = ../SQLiteObjc/include/SQLiteObjc.h; sourceTree = "<group>"; };
 		49EB68C31F7B3CB400D89D40 /* Coding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Coding.swift; sourceTree = "<group>"; };
@@ -496,6 +497,7 @@
 			children = (
 				EE247B8F1C3F822500AE3E12 /* Index.md */,
 				EE247B901C3F822500AE3E12 /* Resources */,
+				19A17EA3A313F129011B3FA0 /* Release.md */,
 			);
 			path = Documentation;
 			sourceTree = "<group>";
diff --git a/Sources/SQLite/Typed/CustomFunctions.swift b/Sources/SQLite/Typed/CustomFunctions.swift
index 88fb5aaa..f2d90b0f 100644
--- a/Sources/SQLite/Typed/CustomFunctions.swift
+++ b/Sources/SQLite/Typed/CustomFunctions.swift
@@ -160,8 +160,8 @@ public extension Connection {
     }
 
     // MARK: -
-    
-    public func createAggregation<T: AnyObject>(
+
+    func createAggregation<T: AnyObject>(
         _ aggregate: String,
         argumentCount: UInt? = nil,
         deterministic: Bool = false,
@@ -169,14 +169,14 @@ public extension Connection {
         reduce: @escaping (T, [Binding?]) -> T,
         result: @escaping (T) -> Binding?
         ) {
-        
+
         let step: ([Binding?], UnsafeMutablePointer<UnsafeMutableRawPointer>) -> () = { (bindings, ptr) in
             let p = ptr.pointee.assumingMemoryBound(to: T.self)
             let current = Unmanaged<T>.fromOpaque(p).takeRetainedValue()
             let next = reduce(current, bindings)
             ptr.pointee = Unmanaged.passRetained(next).toOpaque()
         }
-        
+
         let final: (UnsafeMutablePointer<UnsafeMutableRawPointer>) -> Binding? = { (ptr) in
             let p = ptr.pointee.assumingMemoryBound(to: T.self)
             let obj = Unmanaged<T>.fromOpaque(p).takeRetainedValue()
@@ -184,17 +184,17 @@ public extension Connection {
             ptr.deallocate()
             return value
         }
-        
+
         let state: () -> UnsafeMutablePointer<UnsafeMutableRawPointer> = {
             let p = UnsafeMutablePointer<UnsafeMutableRawPointer>.allocate(capacity: 1)
             p.pointee = Unmanaged.passRetained(initialValue).toOpaque()
             return p
         }
-        
+
         createAggregation(aggregate, step: step, final: final, state: state)
     }
-    
-    public func createAggregation<T>(
+
+    func createAggregation<T>(
         _ aggregate: String,
         argumentCount: UInt? = nil,
         deterministic: Bool = false,
@@ -202,25 +202,25 @@ public extension Connection {
         reduce: @escaping (T, [Binding?]) -> T,
         result: @escaping (T) -> Binding?
         ) {
-        
+
         let step: ([Binding?], UnsafeMutablePointer<T>) -> () = { (bindings, p) in
             let current = p.pointee
             let next = reduce(current, bindings)
             p.pointee = next
         }
-        
+
         let final: (UnsafeMutablePointer<T>) -> Binding? = { (p) in
             let v = result(p.pointee)
             p.deallocate()
             return v
         }
-        
+
         let state: () -> UnsafeMutablePointer<T> = {
             let p = UnsafeMutablePointer<T>.allocate(capacity: 1)
             p.pointee = initialValue
             return p
         }
-        
+
         createAggregation(aggregate, step: step, final: final, state: state)
     }
 
diff --git a/Tests/SQLiteTests/CustomAggregationTests.swift b/Tests/SQLiteTests/CustomAggregationTests.swift
index f8efc7e4..05ed619a 100644
--- a/Tests/SQLiteTests/CustomAggregationTests.swift
+++ b/Tests/SQLiteTests/CustomAggregationTests.swift
@@ -16,19 +16,19 @@ import SQLite3
 class CustomAggregationTests : SQLiteTestCase {
     override func setUp() {
         super.setUp()
-        CreateUsersTable()
-        try! InsertUser("Alice", age: 30, admin: true)
-        try! InsertUser("Bob", age: 25, admin: true)
-        try! InsertUser("Eve", age: 28, admin: false)
+        createUsersTable()
+        try! insertUser("Alice", age: 30, admin: true)
+        try! insertUser("Bob", age: 25, admin: true)
+        try! insertUser("Eve", age: 28, admin: false)
     }
-    
+
     func testUnsafeCustomSum() {
         let step = { (bindings: [Binding?], state: UnsafeMutablePointer<Int64>) in
             if let v = bindings[0] as? Int64 {
                 state.pointee += v
             }
         }
-        
+
         let final = { (state: UnsafeMutablePointer<Int64>) -> Binding? in
             let v = state.pointee
             let p = UnsafeMutableBufferPointer(start: state, count: 1)
@@ -41,13 +41,13 @@ class CustomAggregationTests : SQLiteTestCase {
             return v.baseAddress!
         }
         let result = try! db.prepare("SELECT mySUM1(age) AS s FROM users")
-        let i = result.columnNames.index(of: "s")!
+        let i = result.columnNames.firstIndex(of: "s")!
         for row in result {
             let value = row[i] as? Int64
             XCTAssertEqual(83, value)
         }
     }
-    
+
     func testUnsafeCustomSumGrouping() {
         let step = { (bindings: [Binding?], state: UnsafeMutablePointer<Int64>) in
             if let v = bindings[0] as? Int64 {
@@ -66,11 +66,11 @@ class CustomAggregationTests : SQLiteTestCase {
             return v.baseAddress!
         }
         let result = try! db.prepare("SELECT mySUM2(age) AS s FROM users GROUP BY admin ORDER BY s")
-        let i = result.columnNames.index(of: "s")!
+        let i = result.columnNames.firstIndex(of: "s")!
         let values = result.compactMap { $0[i] as? Int64 }
         XCTAssertTrue(values.elementsEqual([28, 55]))
     }
-    
+
     func testCustomSum() {
         let reduce : (Int64, [Binding?]) -> Int64 = { (last, bindings) in
             let v = (bindings[0] as? Int64) ?? 0
@@ -78,7 +78,7 @@ class CustomAggregationTests : SQLiteTestCase {
         }
         let _ = db.createAggregation("myReduceSUM1", initialValue: Int64(2000), reduce: reduce, result: { $0 })
         let result = try! db.prepare("SELECT myReduceSUM1(age) AS s FROM users")
-        let i = result.columnNames.index(of: "s")!
+        let i = result.columnNames.firstIndex(of: "s")!
         for row in result {
             let value = row[i] as? Int64
             XCTAssertEqual(2083, value)
@@ -92,11 +92,11 @@ class CustomAggregationTests : SQLiteTestCase {
         }
         let _ = db.createAggregation("myReduceSUM2", initialValue: Int64(3000), reduce: reduce, result: { $0 })
         let result = try! db.prepare("SELECT myReduceSUM2(age) AS s FROM users GROUP BY admin ORDER BY s")
-        let i = result.columnNames.index(of: "s")!
+        let i = result.columnNames.firstIndex(of: "s")!
         let values = result.compactMap { $0[i] as? Int64 }
         XCTAssertTrue(values.elementsEqual([3028, 3055]))
     }
-    
+
     func testCustomStringAgg() {
         let initial = String(repeating: " ", count: 64)
         let reduce : (String, [Binding?]) -> String = { (last, bindings) in
@@ -105,13 +105,13 @@ class CustomAggregationTests : SQLiteTestCase {
         }
         let _ = db.createAggregation("myReduceSUM3", initialValue: initial, reduce: reduce, result: { $0 })
         let result = try! db.prepare("SELECT myReduceSUM3(email) AS s FROM users")
-        let i = result.columnNames.index(of: "s")!
+        let i = result.columnNames.firstIndex(of: "s")!
         for row in result {
             let value = row[i] as? String
             XCTAssertEqual("\(initial)Alice@example.comBob@example.comEve@example.com", value)
         }
     }
-    
+
     func testCustomObjectSum() {
         {
             let initial = TestObject(value: 1000)
@@ -126,7 +126,7 @@ class CustomAggregationTests : SQLiteTestCase {
         {
             XCTAssertEqual(TestObject.inits, 1)
             let result = try! db.prepare("SELECT myReduceSUMX(age) AS s FROM users")
-            let i = result.columnNames.index(of: "s")!
+            let i = result.columnNames.firstIndex(of: "s")!
             for row in result {
                 let value = row[i] as? Int64
                 XCTAssertEqual(1083, value)
@@ -143,7 +143,7 @@ class CustomAggregationTests : SQLiteTestCase {
 class TestObject {
     static var inits = 0
     static var deinits = 0
-    
+
     var value: Int64
     init(value: Int64) {
         self.value = value

From 75ef800901784da018a4d9eed1942e49e0a82ae1 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Mon, 23 Aug 2021 11:37:02 +0200
Subject: [PATCH 053/326] Need to update podspec

---
 Documentation/Release.md | 1 +
 SQLite.swift.podspec     | 7 +++----
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/Documentation/Release.md b/Documentation/Release.md
index ef174683..0177fc8f 100644
--- a/Documentation/Release.md
+++ b/Documentation/Release.md
@@ -2,6 +2,7 @@
 
 * [ ] Make sure current master branch has a green build
 * [ ] Make sure `CHANGELOG.md` is up-to-date
+* [ ] Update the version number in `SQLite.swift.podspec`
 * [ ] Run `pod lib lint` locally
 * [ ] Update the version numbers mentioned in `README.md`, `Documentation/Index.md`
 * [ ] Update `MARKETING_VERSION` in `SQLite.xcodeproj/project.pbxproj`
diff --git a/SQLite.swift.podspec b/SQLite.swift.podspec
index 499012c1..f273ffd8 100644
--- a/SQLite.swift.podspec
+++ b/SQLite.swift.podspec
@@ -1,7 +1,7 @@
 Pod::Spec.new do |s|
   s.name             = "SQLite.swift"
   s.version          = "0.13.0"
-  s.summary          = "A type-safe, Swift-language layer over SQLite3 for iOS and macOS."
+  s.summary          = "A type-safe, Swift-language layer over SQLite3."
 
   s.description      = <<-DESC
     SQLite.swift provides compile-time confidence in SQL statement syntax and
@@ -17,13 +17,12 @@ Pod::Spec.new do |s|
   s.module_name      = 'SQLite'
   s.default_subspec  = 'standard'
   s.swift_versions = ['5']
-  
-  
+
   ios_deployment_target = '9.0'
   tvos_deployment_target = '9.1'
   osx_deployment_target = '10.15'
   watchos_deployment_target = '3.0'
-  
+
   s.ios.deployment_target = ios_deployment_target
   s.tvos.deployment_target = tvos_deployment_target
   s.osx.deployment_target = osx_deployment_target

From 8ba6c0e6ea34e3b85f71a47511420fe2ad34bddd Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Mon, 23 Aug 2021 11:45:50 +0200
Subject: [PATCH 054/326] https links, cocoadocs is dead

---
 CONTRIBUTING.md |  2 +-
 README.md       | 14 +++++++-------
 2 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 6c367b95..6969a705 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -19,7 +19,7 @@ addresses everything. If it doesn’t, continue the conversation there.
 If your searches return empty, see the [bug](#bugs) or [feature
 request](#feature-requests) guidelines below.
 
-[Ask on Stack Overflow]: http://stackoverflow.com/questions/tagged/sqlite.swift
+[Ask on Stack Overflow]: https://stackoverflow.com/questions/tagged/sqlite.swift
 [Search]: https://github.com/stephencelis/SQLite.swift/search?type=Issues
 
 
diff --git a/README.md b/README.md
index 98a625c4..ac044dc0 100644
--- a/README.md
+++ b/README.md
@@ -20,7 +20,7 @@ syntax _and_ intent.
  - Extensively tested
  - [SQLCipher][] support via CocoaPods
  - Active support at
-   [StackOverflow](http://stackoverflow.com/questions/tagged/sqlite.swift),
+   [StackOverflow](https://stackoverflow.com/questions/tagged/sqlite.swift),
    and [Gitter Chat Room](https://gitter.im/stephencelis/SQLite.swift)
    (_experimental_)
 
@@ -119,7 +119,7 @@ For a more comprehensive example, see
 and the [companion repository][SQLiteDataAccessLayer2].
 
 
-[Create a Data Access Layer with SQLite.swift and Swift 2]: http://masteringswift.blogspot.com/2015/09/create-data-access-layer-with.html
+[Create a Data Access Layer with SQLite.swift and Swift 2]: https://masteringswift.blogspot.com/2015/09/create-data-access-layer-with.html
 [SQLiteDataAccessLayer2]: https://github.com/hoffmanjon/SQLiteDataAccessLayer2/tree/master
 
 ## Installation
@@ -226,7 +226,7 @@ device:
 
 
 [Xcode]: https://developer.apple.com/xcode/downloads/
-[Submodule]: http://git-scm.com/book/en/Git-Tools-Submodules
+[Submodule]: https://git-scm.com/book/en/Git-Tools-Submodules
 [download]: https://github.com/stephencelis/SQLite.swift/archive/master.zip
 
 
@@ -243,7 +243,7 @@ device:
 
 [See the planning document]: /Documentation/Planning.md
 [Read the contributing guidelines]: ./CONTRIBUTING.md#contributing
-[Ask on Stack Overflow]: http://stackoverflow.com/questions/tagged/sqlite.swift
+[Ask on Stack Overflow]: https://stackoverflow.com/questions/tagged/sqlite.swift
 [Open an issue]: https://github.com/stephencelis/SQLite.swift/issues/new
 [Submit a pull request]: https://github.com/stephencelis/SQLite.swift/fork
 
@@ -280,16 +280,16 @@ Looking for something else? Try another Swift wrapper (or [FMDB][]):
  - [SwiftSQLite](https://github.com/chrismsimpson/SwiftSQLite)
 
 [Swift]: https://swift.org/
-[SQLite3]: http://www.sqlite.org
+[SQLite3]: https://www.sqlite.org
 [SQLite.swift]: https://github.com/stephencelis/SQLite.swift
 
 [GitHubActionBadge]: https://img.shields.io/github/workflow/status/stephencelis/SQLite.swift/Build%20and%20test
 
 [CocoaPodsVersionBadge]: https://cocoapod-badges.herokuapp.com/v/SQLite.swift/badge.png
-[CocoaPodsVersionLink]: http://cocoadocs.org/docsets/SQLite.swift
+[CocoaPodsVersionLink]: https://cocoapods.org/pods/SQLite.swift
 
 [PlatformBadge]: https://cocoapod-badges.herokuapp.com/p/SQLite.swift/badge.png
-[PlatformLink]: http://cocoadocs.org/docsets/SQLite.swift
+[PlatformLink]: https://cocoapods.org/pods/SQLite.swift
 
 [CartagheBadge]: https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat
 [CarthageLink]: https://github.com/Carthage/Carthage

From 66e01e2191707158dff6127ce1055fb084f8f2b5 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Mon, 23 Aug 2021 12:03:19 +0200
Subject: [PATCH 055/326] Fix lint post merge

---
 .swiftlint.yml                                |  8 +++--
 Sources/SQLite/Core/Connection.swift          | 32 ++++++++---------
 Sources/SQLite/Typed/CoreFunctions.swift      |  1 -
 Sources/SQLite/Typed/CustomFunctions.swift    | 36 +++++++++----------
 .../SQLiteTests/CustomAggregationTests.swift  | 24 +++++++------
 5 files changed, 53 insertions(+), 48 deletions(-)

diff --git a/.swiftlint.yml b/.swiftlint.yml
index 3bfc5fa5..b5e53d56 100644
--- a/.swiftlint.yml
+++ b/.swiftlint.yml
@@ -28,11 +28,15 @@ type_body_length:
   warning: 260
   error: 260
 
+function_body_length:
+  warning: 60
+  error: 60
+
 line_length:
   warning: 150
   error: 150
   ignores_comments: true
 
 file_length:
-  warning: 760
-  error: 760
+  warning: 900
+  error: 900
diff --git a/Sources/SQLite/Core/Connection.swift b/Sources/SQLite/Core/Connection.swift
index 07c85373..2bea05c8 100644
--- a/Sources/SQLite/Core/Connection.swift
+++ b/Sources/SQLite/Core/Connection.swift
@@ -35,6 +35,7 @@ import SQLite3
 #endif
 
 /// A connection to SQLite.
+// swiftlint:disable:next type_body_length
 public final class Connection {
 
     /// The location of a SQLite database.
@@ -585,7 +586,7 @@ public final class Connection {
     ///   - block: A block of code to run when the function is called. The block
     ///     is called with an array of raw SQL values mapped to the function’s
     ///     parameters and should return a raw SQL value (or nil).
-    // swiftlint:disable:next cyclomatic_complexity function_body_length
+    // swiftlint:disable:next cyclomatic_complexity
     public func createFunction(_ function: String, argumentCount: UInt? = nil, deterministic: Bool = false,
                                _ block: @escaping (_ args: [Binding?]) -> Binding?) {
         let argc = argumentCount.map { Int($0) } ?? -1
@@ -636,7 +637,7 @@ public final class Connection {
         if functions[function] == nil { functions[function] = [:] }
         functions[function]?[argc] = box
     }
-    
+
     /// Creates or redefines a custom SQL aggregate.
     ///
     /// - Parameters:
@@ -665,19 +666,19 @@ public final class Connection {
     ///   - state: A block of code to run to produce a fresh state variable for
     ///     each aggregation group. The block should return an
     ///     UnsafeMutablePointer to the fresh state variable.
+    // swiftlint:disable:next cyclomatic_complexity
     public func createAggregation<T>(
         _ aggregate: String,
         argumentCount: UInt? = nil,
         deterministic: Bool = false,
-        step: @escaping ([Binding?], UnsafeMutablePointer<T>) -> (),
+        step: @escaping ([Binding?], UnsafeMutablePointer<T>) -> Void,
         final: @escaping (UnsafeMutablePointer<T>) -> Binding?,
         state: @escaping () -> UnsafeMutablePointer<T>) {
-        
-        
+
         let argc = argumentCount.map { Int($0) } ?? -1
-        let box : Aggregate = { (stepFlag: Int, context: OpaquePointer?, argc: Int32, argv: UnsafeMutablePointer<OpaquePointer?>?) in
+        let box: Aggregate = { (stepFlag: Int, context: OpaquePointer?, argc: Int32, argv: UnsafeMutablePointer<OpaquePointer?>?) in
             let ptr = sqlite3_aggregate_context(context, 64)! // needs to be at least as large as uintptr_t; better way to do this?
-            let p = ptr.assumingMemoryBound(to: UnsafeMutableRawPointer.self)
+            let mutablePointer = ptr.assumingMemoryBound(to: UnsafeMutableRawPointer.self)
             if stepFlag > 0 {
                 let arguments: [Binding?] = (0..<Int(argc)).map { idx in
                     let value = argv![idx]
@@ -696,14 +697,14 @@ public final class Connection {
                         fatalError("unsupported value type: \(type)")
                     }
                 }
-                
+
                 if ptr.assumingMemoryBound(to: Int64.self).pointee == 0 {
-                    let v = state()
-                    p.pointee = UnsafeMutableRawPointer(mutating: v)
+                    let value = state()
+                    mutablePointer.pointee = UnsafeMutableRawPointer(mutating: value)
                 }
-                step(arguments, p.pointee.assumingMemoryBound(to: T.self))
+                step(arguments, mutablePointer.pointee.assumingMemoryBound(to: T.self))
             } else {
-                let result = final(p.pointee.assumingMemoryBound(to: T.self))
+                let result = final(mutablePointer.pointee.assumingMemoryBound(to: T.self))
                 if let result = result as? Blob {
                     sqlite3_result_blob(context, result.bytes, Int32(result.bytes.count), nil)
                 } else if let result = result as? Double {
@@ -719,14 +720,14 @@ public final class Connection {
                 }
             }
         }
-        
+
         var flags = SQLITE_UTF8
         #if !os(Linux)
         if deterministic {
             flags |= SQLITE_DETERMINISTIC
         }
         #endif
-        
+
         sqlite3_create_function_v2(
             handle,
             aggregate,
@@ -737,8 +738,7 @@ public final class Connection {
             { context, argc, value in
                 let function = unsafeBitCast(sqlite3_user_data(context), to: Aggregate.self)
                 function(1, context, argc, value)
-        },
-            { context in
+        }, { context in
                 let function = unsafeBitCast(sqlite3_user_data(context), to: Aggregate.self)
                 function(0, context, 0, nil)
         },
diff --git a/Sources/SQLite/Typed/CoreFunctions.swift b/Sources/SQLite/Typed/CoreFunctions.swift
index be38d97e..dc6a1044 100644
--- a/Sources/SQLite/Typed/CoreFunctions.swift
+++ b/Sources/SQLite/Typed/CoreFunctions.swift
@@ -21,7 +21,6 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 //
-// swiftlint:disable file_length
 import Foundation
 
 private enum Function: String {
diff --git a/Sources/SQLite/Typed/CustomFunctions.swift b/Sources/SQLite/Typed/CustomFunctions.swift
index f2d90b0f..1d66d075 100644
--- a/Sources/SQLite/Typed/CustomFunctions.swift
+++ b/Sources/SQLite/Typed/CustomFunctions.swift
@@ -170,25 +170,25 @@ public extension Connection {
         result: @escaping (T) -> Binding?
         ) {
 
-        let step: ([Binding?], UnsafeMutablePointer<UnsafeMutableRawPointer>) -> () = { (bindings, ptr) in
-            let p = ptr.pointee.assumingMemoryBound(to: T.self)
-            let current = Unmanaged<T>.fromOpaque(p).takeRetainedValue()
+        let step: ([Binding?], UnsafeMutablePointer<UnsafeMutableRawPointer>) -> Void = { (bindings, ptr) in
+            let pointer = ptr.pointee.assumingMemoryBound(to: T.self)
+            let current = Unmanaged<T>.fromOpaque(pointer).takeRetainedValue()
             let next = reduce(current, bindings)
             ptr.pointee = Unmanaged.passRetained(next).toOpaque()
         }
 
         let final: (UnsafeMutablePointer<UnsafeMutableRawPointer>) -> Binding? = { (ptr) in
-            let p = ptr.pointee.assumingMemoryBound(to: T.self)
-            let obj = Unmanaged<T>.fromOpaque(p).takeRetainedValue()
+            let pointer = ptr.pointee.assumingMemoryBound(to: T.self)
+            let obj = Unmanaged<T>.fromOpaque(pointer).takeRetainedValue()
             let value = result(obj)
             ptr.deallocate()
             return value
         }
 
         let state: () -> UnsafeMutablePointer<UnsafeMutableRawPointer> = {
-            let p = UnsafeMutablePointer<UnsafeMutableRawPointer>.allocate(capacity: 1)
-            p.pointee = Unmanaged.passRetained(initialValue).toOpaque()
-            return p
+            let pointer = UnsafeMutablePointer<UnsafeMutableRawPointer>.allocate(capacity: 1)
+            pointer.pointee = Unmanaged.passRetained(initialValue).toOpaque()
+            return pointer
         }
 
         createAggregation(aggregate, step: step, final: final, state: state)
@@ -203,22 +203,22 @@ public extension Connection {
         result: @escaping (T) -> Binding?
         ) {
 
-        let step: ([Binding?], UnsafeMutablePointer<T>) -> () = { (bindings, p) in
-            let current = p.pointee
+        let step: ([Binding?], UnsafeMutablePointer<T>) -> Void = { (bindings, pointer) in
+            let current = pointer.pointee
             let next = reduce(current, bindings)
-            p.pointee = next
+            pointer.pointee = next
         }
 
-        let final: (UnsafeMutablePointer<T>) -> Binding? = { (p) in
-            let v = result(p.pointee)
-            p.deallocate()
-            return v
+        let final: (UnsafeMutablePointer<T>) -> Binding? = { pointer in
+            let value = result(pointer.pointee)
+            pointer.deallocate()
+            return value
         }
 
         let state: () -> UnsafeMutablePointer<T> = {
-            let p = UnsafeMutablePointer<T>.allocate(capacity: 1)
-            p.pointee = initialValue
-            return p
+            let pointer = UnsafeMutablePointer<T>.allocate(capacity: 1)
+            pointer.pointee = initialValue
+            return pointer
         }
 
         createAggregation(aggregate, step: step, final: final, state: state)
diff --git a/Tests/SQLiteTests/CustomAggregationTests.swift b/Tests/SQLiteTests/CustomAggregationTests.swift
index 05ed619a..99cbb00d 100644
--- a/Tests/SQLiteTests/CustomAggregationTests.swift
+++ b/Tests/SQLiteTests/CustomAggregationTests.swift
@@ -13,7 +13,7 @@ import CSQLite
 import SQLite3
 #endif
 
-class CustomAggregationTests : SQLiteTestCase {
+class CustomAggregationTests: SQLiteTestCase {
     override func setUp() {
         super.setUp()
         createUsersTable()
@@ -35,7 +35,7 @@ class CustomAggregationTests : SQLiteTestCase {
             p.deallocate()
             return v
         }
-        let _ = db.createAggregation("mySUM1", step: step, final: final) {
+        _ = db.createAggregation("mySUM1", step: step, final: final) {
             let v = UnsafeMutableBufferPointer<Int64>.allocate(capacity: 1)
             v[0] = 0
             return v.baseAddress!
@@ -60,7 +60,7 @@ class CustomAggregationTests : SQLiteTestCase {
             p.deallocate()
             return v
         }
-        let _ = db.createAggregation("mySUM2", step: step, final: final) {
+        _ = db.createAggregation("mySUM2", step: step, final: final) {
             let v = UnsafeMutableBufferPointer<Int64>.allocate(capacity: 1)
             v[0] = 0
             return v.baseAddress!
@@ -72,11 +72,11 @@ class CustomAggregationTests : SQLiteTestCase {
     }
 
     func testCustomSum() {
-        let reduce : (Int64, [Binding?]) -> Int64 = { (last, bindings) in
+        let reduce: (Int64, [Binding?]) -> Int64 = { (last, bindings) in
             let v = (bindings[0] as? Int64) ?? 0
             return last + v
         }
-        let _ = db.createAggregation("myReduceSUM1", initialValue: Int64(2000), reduce: reduce, result: { $0 })
+        _ = db.createAggregation("myReduceSUM1", initialValue: Int64(2000), reduce: reduce, result: { $0 })
         let result = try! db.prepare("SELECT myReduceSUM1(age) AS s FROM users")
         let i = result.columnNames.firstIndex(of: "s")!
         for row in result {
@@ -86,11 +86,11 @@ class CustomAggregationTests : SQLiteTestCase {
     }
 
     func testCustomSumGrouping() {
-        let reduce : (Int64, [Binding?]) -> Int64 = { (last, bindings) in
+        let reduce: (Int64, [Binding?]) -> Int64 = { (last, bindings) in
             let v = (bindings[0] as? Int64) ?? 0
             return last + v
         }
-        let _ = db.createAggregation("myReduceSUM2", initialValue: Int64(3000), reduce: reduce, result: { $0 })
+        _ = db.createAggregation("myReduceSUM2", initialValue: Int64(3000), reduce: reduce, result: { $0 })
         let result = try! db.prepare("SELECT myReduceSUM2(age) AS s FROM users GROUP BY admin ORDER BY s")
         let i = result.columnNames.firstIndex(of: "s")!
         let values = result.compactMap { $0[i] as? Int64 }
@@ -99,11 +99,11 @@ class CustomAggregationTests : SQLiteTestCase {
 
     func testCustomStringAgg() {
         let initial = String(repeating: " ", count: 64)
-        let reduce : (String, [Binding?]) -> String = { (last, bindings) in
+        let reduce: (String, [Binding?]) -> String = { (last, bindings) in
             let v = (bindings[0] as? String) ?? ""
             return last + v
         }
-        let _ = db.createAggregation("myReduceSUM3", initialValue: initial, reduce: reduce, result: { $0 })
+        _ = db.createAggregation("myReduceSUM3", initialValue: initial, reduce: reduce, result: { $0 })
         let result = try! db.prepare("SELECT myReduceSUM3(email) AS s FROM users")
         let i = result.columnNames.firstIndex(of: "s")!
         for row in result {
@@ -115,14 +115,16 @@ class CustomAggregationTests : SQLiteTestCase {
     func testCustomObjectSum() {
         {
             let initial = TestObject(value: 1000)
-            let reduce : (TestObject, [Binding?]) -> TestObject = { (last, bindings) in
+            let reduce: (TestObject, [Binding?]) -> TestObject = { (last, bindings) in
                 let v = (bindings[0] as? Int64) ?? 0
                 return TestObject(value: last.value + v)
             }
-            let _ = db.createAggregation("myReduceSUMX", initialValue: initial, reduce: reduce, result: { $0.value })
+            db.createAggregation("myReduceSUMX", initialValue: initial, reduce: reduce, result: { $0.value })
             // end this scope to ensure that the initial value is retained
             // by the createAggregation call.
+            // swiftlint:disable:next trailing_semicolon
         }();
+
         {
             XCTAssertEqual(TestObject.inits, 1)
             let result = try! db.prepare("SELECT myReduceSUMX(age) AS s FROM users")

From 985b2e4d8c21f94879a084c5d5becafee4821dfe Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Mon, 23 Aug 2021 12:04:41 +0200
Subject: [PATCH 056/326] Fix warnings

---
 Tests/SQLiteTests/CustomAggregationTests.swift | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/Tests/SQLiteTests/CustomAggregationTests.swift b/Tests/SQLiteTests/CustomAggregationTests.swift
index 99cbb00d..337a5aa8 100644
--- a/Tests/SQLiteTests/CustomAggregationTests.swift
+++ b/Tests/SQLiteTests/CustomAggregationTests.swift
@@ -35,7 +35,7 @@ class CustomAggregationTests: SQLiteTestCase {
             p.deallocate()
             return v
         }
-        _ = db.createAggregation("mySUM1", step: step, final: final) {
+        db.createAggregation("mySUM1", step: step, final: final) {
             let v = UnsafeMutableBufferPointer<Int64>.allocate(capacity: 1)
             v[0] = 0
             return v.baseAddress!
@@ -60,7 +60,7 @@ class CustomAggregationTests: SQLiteTestCase {
             p.deallocate()
             return v
         }
-        _ = db.createAggregation("mySUM2", step: step, final: final) {
+        db.createAggregation("mySUM2", step: step, final: final) {
             let v = UnsafeMutableBufferPointer<Int64>.allocate(capacity: 1)
             v[0] = 0
             return v.baseAddress!
@@ -76,7 +76,7 @@ class CustomAggregationTests: SQLiteTestCase {
             let v = (bindings[0] as? Int64) ?? 0
             return last + v
         }
-        _ = db.createAggregation("myReduceSUM1", initialValue: Int64(2000), reduce: reduce, result: { $0 })
+        db.createAggregation("myReduceSUM1", initialValue: Int64(2000), reduce: reduce, result: { $0 })
         let result = try! db.prepare("SELECT myReduceSUM1(age) AS s FROM users")
         let i = result.columnNames.firstIndex(of: "s")!
         for row in result {
@@ -90,7 +90,7 @@ class CustomAggregationTests: SQLiteTestCase {
             let v = (bindings[0] as? Int64) ?? 0
             return last + v
         }
-        _ = db.createAggregation("myReduceSUM2", initialValue: Int64(3000), reduce: reduce, result: { $0 })
+        db.createAggregation("myReduceSUM2", initialValue: Int64(3000), reduce: reduce, result: { $0 })
         let result = try! db.prepare("SELECT myReduceSUM2(age) AS s FROM users GROUP BY admin ORDER BY s")
         let i = result.columnNames.firstIndex(of: "s")!
         let values = result.compactMap { $0[i] as? Int64 }
@@ -103,7 +103,7 @@ class CustomAggregationTests: SQLiteTestCase {
             let v = (bindings[0] as? String) ?? ""
             return last + v
         }
-        _ = db.createAggregation("myReduceSUM3", initialValue: initial, reduce: reduce, result: { $0 })
+        db.createAggregation("myReduceSUM3", initialValue: initial, reduce: reduce, result: { $0 })
         let result = try! db.prepare("SELECT myReduceSUM3(email) AS s FROM users")
         let i = result.columnNames.firstIndex(of: "s")!
         for row in result {

From 896d2978548a336bcade3c8e1b3076f7b2205c55 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Mon, 23 Aug 2021 13:16:20 +0200
Subject: [PATCH 057/326] Fail build if there are compiler warnings

Closes #1065
---
 .github/workflows/build.yml          | 8 ++++----
 Sources/SQLite/Core/Connection.swift | 2 +-
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 1119284a..af6666ba 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -17,6 +17,10 @@ jobs:
         brew outdated swiftlint || brew upgrade swiftlint
     - name: "Lint"
       run: make lint
+    - name: "Run tests (PACKAGE_MANAGER_COMMAND: test)"
+      env:
+        PACKAGE_MANAGER_COMMAND: test -Xswiftc -warnings-as-errors
+      run: ./run-tests.sh
     - name: "Run tests (BUILD_SCHEME: SQLite iOS)"
       env:
         BUILD_SCHEME: SQLite iOS
@@ -57,7 +61,3 @@ jobs:
       env:
         CARTHAGE_PLATFORM: tvOS
       run: ./run-tests.sh
-    - name: "Run tests (PACKAGE_MANAGER_COMMAND: test)"
-      env:
-        PACKAGE_MANAGER_COMMAND: test
-      run: ./run-tests.sh
diff --git a/Sources/SQLite/Core/Connection.swift b/Sources/SQLite/Core/Connection.swift
index 2bea05c8..fa086181 100644
--- a/Sources/SQLite/Core/Connection.swift
+++ b/Sources/SQLite/Core/Connection.swift
@@ -434,7 +434,7 @@ public final class Connection {
         }
     }
 
-    @available(OSX, deprecated: 10.2)
+    @available(OSX, deprecated: 10.12)
     @available(iOS, deprecated: 10.0)
     @available(watchOS, deprecated: 3.0)
     @available(tvOS, deprecated: 10.0)

From 559b796187ab1b71148ac68f37c4b2e23db3b49a Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Mon, 23 Aug 2021 13:42:34 +0200
Subject: [PATCH 058/326] Simplify CocoaPods linting

Closes #1066
---
 Tests/CocoaPods/.gitignore          |  1 -
 Tests/CocoaPods/Gemfile             |  4 --
 Tests/CocoaPods/Gemfile.lock        | 96 -----------------------------
 Tests/CocoaPods/Makefile            | 15 -----
 Tests/CocoaPods/integration_test.rb | 35 -----------
 run-tests.sh                        |  8 ++-
 6 files changed, 6 insertions(+), 153 deletions(-)
 delete mode 100644 Tests/CocoaPods/.gitignore
 delete mode 100644 Tests/CocoaPods/Gemfile
 delete mode 100644 Tests/CocoaPods/Gemfile.lock
 delete mode 100644 Tests/CocoaPods/Makefile
 delete mode 100755 Tests/CocoaPods/integration_test.rb

diff --git a/Tests/CocoaPods/.gitignore b/Tests/CocoaPods/.gitignore
deleted file mode 100644
index 4cf24de2..00000000
--- a/Tests/CocoaPods/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-gems/
diff --git a/Tests/CocoaPods/Gemfile b/Tests/CocoaPods/Gemfile
deleted file mode 100644
index da52ec89..00000000
--- a/Tests/CocoaPods/Gemfile
+++ /dev/null
@@ -1,4 +0,0 @@
-source 'https://rubygems.org'
-
-gem 'cocoapods', '~> 1.10.2'
-gem 'minitest'
diff --git a/Tests/CocoaPods/Gemfile.lock b/Tests/CocoaPods/Gemfile.lock
deleted file mode 100644
index e0383a67..00000000
--- a/Tests/CocoaPods/Gemfile.lock
+++ /dev/null
@@ -1,96 +0,0 @@
-GEM
-  remote: https://rubygems.org/
-  specs:
-    CFPropertyList (3.0.3)
-    activesupport (5.2.6)
-      concurrent-ruby (~> 1.0, >= 1.0.2)
-      i18n (>= 0.7, < 2)
-      minitest (~> 5.1)
-      tzinfo (~> 1.1)
-    addressable (2.8.0)
-      public_suffix (>= 2.0.2, < 5.0)
-    algoliasearch (1.27.5)
-      httpclient (~> 2.8, >= 2.8.3)
-      json (>= 1.5.1)
-    atomos (0.1.3)
-    claide (1.0.3)
-    cocoapods (1.10.2)
-      addressable (~> 2.6)
-      claide (>= 1.0.2, < 2.0)
-      cocoapods-core (= 1.10.2)
-      cocoapods-deintegrate (>= 1.0.3, < 2.0)
-      cocoapods-downloader (>= 1.4.0, < 2.0)
-      cocoapods-plugins (>= 1.0.0, < 2.0)
-      cocoapods-search (>= 1.0.0, < 2.0)
-      cocoapods-trunk (>= 1.4.0, < 2.0)
-      cocoapods-try (>= 1.1.0, < 2.0)
-      colored2 (~> 3.1)
-      escape (~> 0.0.4)
-      fourflusher (>= 2.3.0, < 3.0)
-      gh_inspector (~> 1.0)
-      molinillo (~> 0.6.6)
-      nap (~> 1.0)
-      ruby-macho (~> 1.4)
-      xcodeproj (>= 1.19.0, < 2.0)
-    cocoapods-core (1.10.2)
-      activesupport (> 5.0, < 6)
-      addressable (~> 2.6)
-      algoliasearch (~> 1.0)
-      concurrent-ruby (~> 1.1)
-      fuzzy_match (~> 2.0.4)
-      nap (~> 1.0)
-      netrc (~> 0.11)
-      public_suffix
-      typhoeus (~> 1.0)
-    cocoapods-deintegrate (1.0.5)
-    cocoapods-downloader (1.4.0)
-    cocoapods-plugins (1.0.0)
-      nap
-    cocoapods-search (1.0.1)
-    cocoapods-trunk (1.5.0)
-      nap (>= 0.8, < 2.0)
-      netrc (~> 0.11)
-    cocoapods-try (1.2.0)
-    colored2 (3.1.2)
-    concurrent-ruby (1.1.9)
-    escape (0.0.4)
-    ethon (0.14.0)
-      ffi (>= 1.15.0)
-    ffi (1.15.3)
-    fourflusher (2.3.1)
-    fuzzy_match (2.0.4)
-    gh_inspector (1.1.3)
-    httpclient (2.8.3)
-    i18n (1.8.10)
-      concurrent-ruby (~> 1.0)
-    json (2.5.1)
-    minitest (5.11.3)
-    molinillo (0.6.6)
-    nanaimo (0.3.0)
-    nap (1.1.0)
-    netrc (0.11.0)
-    public_suffix (4.0.6)
-    rexml (3.2.5)
-    ruby-macho (1.4.0)
-    thread_safe (0.3.6)
-    typhoeus (1.4.0)
-      ethon (>= 0.9.0)
-    tzinfo (1.2.9)
-      thread_safe (~> 0.1)
-    xcodeproj (1.21.0)
-      CFPropertyList (>= 2.3.3, < 4.0)
-      atomos (~> 0.1.3)
-      claide (>= 1.0.2, < 2.0)
-      colored2 (~> 3.1)
-      nanaimo (~> 0.3.0)
-      rexml (~> 3.2.4)
-
-PLATFORMS
-  ruby
-
-DEPENDENCIES
-  cocoapods (~> 1.10.2)
-  minitest
-
-BUNDLED WITH
-   2.2.22
diff --git a/Tests/CocoaPods/Makefile b/Tests/CocoaPods/Makefile
deleted file mode 100644
index 532dcbff..00000000
--- a/Tests/CocoaPods/Makefile
+++ /dev/null
@@ -1,15 +0,0 @@
-XCPRETTY := $(shell command -v xcpretty)
-
-test: install repo_update
-	@set -e; \
-	for test in *_test.rb; do \
-		bundle exec ./$$test | $(XCPRETTY) -c; \
-	done
-
-repo_update:
-	@bundle exec pod repo update --silent
-
-install:
-	@bundle install --path gems
-
-.PHONY: test install
diff --git a/Tests/CocoaPods/integration_test.rb b/Tests/CocoaPods/integration_test.rb
deleted file mode 100755
index 67b13385..00000000
--- a/Tests/CocoaPods/integration_test.rb
+++ /dev/null
@@ -1,35 +0,0 @@
-#!/usr/bin/env ruby
-
-require 'cocoapods'
-require 'cocoapods/validator'
-require 'minitest/autorun'
-
-class IntegrationTest < Minitest::Test
-
-  def test_validate_project
-    assert validator.validate, "validation failed: #{validator.failure_reason}"
-  end
-
-  private
-
-  def validator
-    @validator ||= Pod::Validator.new(podspec, ['https://github.com/CocoaPods/Specs.git']).tap do |validator|
-        validator.config.verbose = true
-        validator.no_clean = true
-        validator.use_frameworks = true
-        validator.fail_fast = true
-        validator.local = true
-        validator.allow_warnings = true
-        subspec = ENV['VALIDATOR_SUBSPEC']
-        if subspec == 'none'
-          validator.no_subspecs = true
-        else
-          validator.only_subspec = subspec
-        end
-    end
-  end
-
-  def podspec
-    File.expand_path(File.dirname(__FILE__) + '/../../SQLite.swift.podspec')
-  end
-end
diff --git a/run-tests.sh b/run-tests.sh
index 0a105c41..edc60987 100755
--- a/run-tests.sh
+++ b/run-tests.sh
@@ -7,9 +7,13 @@ if [ -n "$BUILD_SCHEME" ]; then
         make test BUILD_SCHEME="$BUILD_SCHEME"
     fi
 elif [ -n "$VALIDATOR_SUBSPEC" ]; then
-    cd Tests/CocoaPods && make test
+    if [ "$VALIDATOR_SUBSPEC" == "none" ]; then
+      pod lib lint --no-subspecs
+    else
+      pod lib lint --subspec="${VALIDATOR_SUBSPEC}"
+    fi
 elif [ -n "$CARTHAGE_PLATFORM" ]; then
     cd Tests/Carthage && make test CARTHAGE_PLATFORM="$CARTHAGE_PLATFORM"
 elif [ -n "${PACKAGE_MANAGER_COMMAND}" ]; then
-    swift ${PACKAGE_MANAGER_COMMAND}
+    swift "${PACKAGE_MANAGER_COMMAND}"
 fi

From 00b7d3f2eea7a5d98941f88f05c4f9c38eb04654 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Mon, 23 Aug 2021 13:51:00 +0200
Subject: [PATCH 059/326] rm obsolete swiftcov

---
 Makefile | 28 ++--------------------------
 1 file changed, 2 insertions(+), 26 deletions(-)

diff --git a/Makefile b/Makefile
index a9d29d58..0b9a1606 100644
--- a/Makefile
+++ b/Makefile
@@ -9,8 +9,6 @@ else
 endif
 
 XCPRETTY := $(shell command -v xcpretty)
-SWIFTCOV := $(shell command -v swiftcov)
-GCOVR := $(shell command -v gcovr)
 TEST_ACTIONS := clean build build-for-testing test-without-building
 
 default: test
@@ -28,36 +26,14 @@ else
 	$(BUILD_TOOL) $(BUILD_ARGUMENTS) $(TEST_ACTIONS)
 endif
 
-coverage:
-ifdef SWIFTCOV
-	$(SWIFTCOV) generate --output coverage \
-		$(BUILD_TOOL) $(BUILD_ARGUMENTS) -configuration Release test \
-		-- ./SQLite/*.swift
-ifdef GCOVR
-	$(GCOVR) \
-		--root . \
-		--use-gcov-files \
-		--html \
-		--html-details \
-		--output coverage/index.html \
-		--keep
-else
-	@echo gcovr must be installed for HTML output: https://github.com/gcovr/gcovr
-endif
-else
-	@echo swiftcov must be installed for coverage: https://github.com/realm/SwiftCov
-	@exit 1
-endif
-
 clean:
 	$(BUILD_TOOL) $(BUILD_ARGUMENTS) clean
-	rm -r coverage
 
 repl:
 	@$(BUILD_TOOL) $(BUILD_ARGUMENTS) -derivedDataPath $(TMPDIR)/SQLite.swift > /dev/null && \
 		swift -F '$(TMPDIR)/SQLite.swift/Build/Products/Debug'
 
 sloc:
-	@zsh -c "grep -vE '^ *//|^$$' SQLite/*/*.{swift,h,m} | wc -l"
+	@zsh -c "grep -vE '^ *//|^$$' Sources/**/*.{swift,h,m} | wc -l"
 
-.PHONY: test coverage clean repl sloc
+.PHONY: test clean repl sloc

From 325cfd9480f59a280f4bfe64eb05b1b9288a6402 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Mon, 23 Aug 2021 14:00:42 +0200
Subject: [PATCH 060/326] Unquote

---
 run-tests.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/run-tests.sh b/run-tests.sh
index edc60987..1ae7a5ca 100755
--- a/run-tests.sh
+++ b/run-tests.sh
@@ -15,5 +15,5 @@ elif [ -n "$VALIDATOR_SUBSPEC" ]; then
 elif [ -n "$CARTHAGE_PLATFORM" ]; then
     cd Tests/Carthage && make test CARTHAGE_PLATFORM="$CARTHAGE_PLATFORM"
 elif [ -n "${PACKAGE_MANAGER_COMMAND}" ]; then
-    swift "${PACKAGE_MANAGER_COMMAND}"
+    swift ${PACKAGE_MANAGER_COMMAND}
 fi

From 05c84149a49ff2c41b3f15c1f971b736985c9553 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Mon, 23 Aug 2021 14:12:21 +0200
Subject: [PATCH 061/326] Treat warnings as errors

---
 SQLite.xcodeproj/project.pbxproj | 30 +++++++++++++++++++++++++++++-
 1 file changed, 29 insertions(+), 1 deletion(-)

diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj
index 3b5428d0..ea7ab871 100644
--- a/SQLite.xcodeproj/project.pbxproj
+++ b/SQLite.xcodeproj/project.pbxproj
@@ -231,8 +231,8 @@
 		19A17B93B48B5560E6E51791 /* Fixtures.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Fixtures.swift; sourceTree = "<group>"; };
 		19A17BA55DABB480F9020C8A /* DateAndTimeFunctions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DateAndTimeFunctions.swift; sourceTree = "<group>"; };
 		19A17E2695737FAB5D6086E3 /* fixtures */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = folder; path = fixtures; sourceTree = "<group>"; };
-		3717F907221F5D7C00B9BD3D /* CustomAggregationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomAggregationTests.swift; sourceTree = "<group>"; };
 		19A17EA3A313F129011B3FA0 /* Release.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = Release.md; sourceTree = "<group>"; };
+		3717F907221F5D7C00B9BD3D /* CustomAggregationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomAggregationTests.swift; sourceTree = "<group>"; };
 		3D67B3E51DB2469200A4F4C6 /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = Platforms/WatchOS.platform/Developer/SDKs/WatchOS3.0.sdk/usr/lib/libsqlite3.tbd; sourceTree = DEVELOPER_DIR; };
 		3DDC112E26CDBA0200CE369F /* SQLiteObjc.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SQLiteObjc.h; path = ../SQLiteObjc/include/SQLiteObjc.h; sourceTree = "<group>"; };
 		49EB68C31F7B3CB400D89D40 /* Coding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Coding.swift; sourceTree = "<group>"; };
@@ -1042,6 +1042,7 @@
 				DYLIB_COMPATIBILITY_VERSION = 1;
 				DYLIB_CURRENT_VERSION = 1;
 				DYLIB_INSTALL_NAME_BASE = "@rpath";
+				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
 				INFOPLIST_FILE = "$(SRCROOT)/Sources/SQLite/Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
@@ -1049,6 +1050,7 @@
 				PRODUCT_NAME = SQLite;
 				SDKROOT = appletvos;
 				SKIP_INSTALL = YES;
+				SWIFT_TREAT_WARNINGS_AS_ERRORS = YES;
 				TVOS_DEPLOYMENT_TARGET = 12.0;
 			};
 			name = Debug;
@@ -1062,6 +1064,7 @@
 				DYLIB_COMPATIBILITY_VERSION = 1;
 				DYLIB_CURRENT_VERSION = 1;
 				DYLIB_INSTALL_NAME_BASE = "@rpath";
+				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
 				INFOPLIST_FILE = "$(SRCROOT)/Sources/SQLite/Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
@@ -1069,6 +1072,7 @@
 				PRODUCT_NAME = SQLite;
 				SDKROOT = appletvos;
 				SKIP_INSTALL = YES;
+				SWIFT_TREAT_WARNINGS_AS_ERRORS = YES;
 				TVOS_DEPLOYMENT_TARGET = 12.0;
 			};
 			name = Release;
@@ -1076,11 +1080,13 @@
 		03A65E6D1C6BB0F60062603F /* Debug */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
+				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
 				INFOPLIST_FILE = Tests/SQLiteTests/Info.plist;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLiteTests;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SDKROOT = appletvos;
+				SWIFT_TREAT_WARNINGS_AS_ERRORS = YES;
 				TVOS_DEPLOYMENT_TARGET = 12.0;
 			};
 			name = Debug;
@@ -1088,11 +1094,13 @@
 		03A65E6E1C6BB0F60062603F /* Release */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
+				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
 				INFOPLIST_FILE = Tests/SQLiteTests/Info.plist;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLiteTests;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SDKROOT = appletvos;
+				SWIFT_TREAT_WARNINGS_AS_ERRORS = YES;
 				TVOS_DEPLOYMENT_TARGET = 12.0;
 			};
 			name = Release;
@@ -1107,6 +1115,7 @@
 				DYLIB_COMPATIBILITY_VERSION = 1;
 				DYLIB_CURRENT_VERSION = 1;
 				DYLIB_INSTALL_NAME_BASE = "@rpath";
+				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
 				INFOPLIST_FILE = "$(SRCROOT)/Sources/SQLite/Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
@@ -1114,6 +1123,7 @@
 				PRODUCT_NAME = SQLite;
 				SDKROOT = watchos;
 				SKIP_INSTALL = YES;
+				SWIFT_TREAT_WARNINGS_AS_ERRORS = YES;
 				TARGETED_DEVICE_FAMILY = 4;
 				WATCHOS_DEPLOYMENT_TARGET = 3.0;
 			};
@@ -1129,6 +1139,7 @@
 				DYLIB_COMPATIBILITY_VERSION = 1;
 				DYLIB_CURRENT_VERSION = 1;
 				DYLIB_INSTALL_NAME_BASE = "@rpath";
+				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
 				INFOPLIST_FILE = "$(SRCROOT)/Sources/SQLite/Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
@@ -1136,6 +1147,7 @@
 				PRODUCT_NAME = SQLite;
 				SDKROOT = watchos;
 				SKIP_INSTALL = YES;
+				SWIFT_TREAT_WARNINGS_AS_ERRORS = YES;
 				TARGETED_DEVICE_FAMILY = 4;
 				WATCHOS_DEPLOYMENT_TARGET = 3.0;
 			};
@@ -1271,6 +1283,7 @@
 				DYLIB_COMPATIBILITY_VERSION = 1;
 				DYLIB_CURRENT_VERSION = 1;
 				DYLIB_INSTALL_NAME_BASE = "@rpath";
+				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
 				INFOPLIST_FILE = "$(SRCROOT)/Sources/SQLite/Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
 				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
@@ -1280,6 +1293,7 @@
 				PRODUCT_NAME = SQLite;
 				SKIP_INSTALL = YES;
 				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+				SWIFT_TREAT_WARNINGS_AS_ERRORS = YES;
 			};
 			name = Debug;
 		};
@@ -1293,6 +1307,7 @@
 				DYLIB_COMPATIBILITY_VERSION = 1;
 				DYLIB_CURRENT_VERSION = 1;
 				DYLIB_INSTALL_NAME_BASE = "@rpath";
+				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
 				INFOPLIST_FILE = "$(SRCROOT)/Sources/SQLite/Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
 				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
@@ -1301,26 +1316,31 @@
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
 				PRODUCT_NAME = SQLite;
 				SKIP_INSTALL = YES;
+				SWIFT_TREAT_WARNINGS_AS_ERRORS = YES;
 			};
 			name = Release;
 		};
 		EE247AEB1C3F04ED00AE3E12 /* Debug */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
+				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
 				INFOPLIST_FILE = Tests/SQLiteTests/Info.plist;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLiteTests;
 				PRODUCT_NAME = "$(TARGET_NAME)";
+				SWIFT_TREAT_WARNINGS_AS_ERRORS = YES;
 			};
 			name = Debug;
 		};
 		EE247AEC1C3F04ED00AE3E12 /* Release */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
+				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
 				INFOPLIST_FILE = Tests/SQLiteTests/Info.plist;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLiteTests;
 				PRODUCT_NAME = "$(TARGET_NAME)";
+				SWIFT_TREAT_WARNINGS_AS_ERRORS = YES;
 			};
 			name = Release;
 		};
@@ -1335,6 +1355,7 @@
 				DYLIB_CURRENT_VERSION = 1;
 				DYLIB_INSTALL_NAME_BASE = "@rpath";
 				FRAMEWORK_VERSION = A;
+				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
 				INFOPLIST_FILE = "$(SRCROOT)/Sources/SQLite/Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
@@ -1343,6 +1364,7 @@
 				SDKROOT = macosx;
 				SKIP_INSTALL = YES;
 				SWIFT_INCLUDE_PATHS = "";
+				SWIFT_TREAT_WARNINGS_AS_ERRORS = YES;
 			};
 			name = Debug;
 		};
@@ -1357,6 +1379,7 @@
 				DYLIB_CURRENT_VERSION = 1;
 				DYLIB_INSTALL_NAME_BASE = "@rpath";
 				FRAMEWORK_VERSION = A;
+				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
 				INFOPLIST_FILE = "$(SRCROOT)/Sources/SQLite/Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
@@ -1365,6 +1388,7 @@
 				SDKROOT = macosx;
 				SKIP_INSTALL = YES;
 				SWIFT_INCLUDE_PATHS = "";
+				SWIFT_TREAT_WARNINGS_AS_ERRORS = YES;
 			};
 			name = Release;
 		};
@@ -1373,11 +1397,13 @@
 			buildSettings = {
 				CODE_SIGN_IDENTITY = "-";
 				COMBINE_HIDPI_IMAGES = YES;
+				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
 				INFOPLIST_FILE = Tests/SQLiteTests/Info.plist;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLiteTests;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SDKROOT = macosx;
+				SWIFT_TREAT_WARNINGS_AS_ERRORS = YES;
 			};
 			name = Debug;
 		};
@@ -1386,11 +1412,13 @@
 			buildSettings = {
 				CODE_SIGN_IDENTITY = "-";
 				COMBINE_HIDPI_IMAGES = YES;
+				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
 				INFOPLIST_FILE = Tests/SQLiteTests/Info.plist;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLiteTests;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SDKROOT = macosx;
+				SWIFT_TREAT_WARNINGS_AS_ERRORS = YES;
 			};
 			name = Release;
 		};

From fdac6551b06350181904eab08a0be0a4be4a2160 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Mon, 23 Aug 2021 14:13:58 +0200
Subject: [PATCH 062/326] fail fast

---
 run-tests.sh | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/run-tests.sh b/run-tests.sh
index 1ae7a5ca..32465388 100755
--- a/run-tests.sh
+++ b/run-tests.sh
@@ -8,9 +8,9 @@ if [ -n "$BUILD_SCHEME" ]; then
     fi
 elif [ -n "$VALIDATOR_SUBSPEC" ]; then
     if [ "$VALIDATOR_SUBSPEC" == "none" ]; then
-      pod lib lint --no-subspecs
+      pod lib lint --no-subspecs --fail-fast
     else
-      pod lib lint --subspec="${VALIDATOR_SUBSPEC}"
+      pod lib lint --subspec="${VALIDATOR_SUBSPEC}" --fail-fast
     fi
 elif [ -n "$CARTHAGE_PLATFORM" ]; then
     cd Tests/Carthage && make test CARTHAGE_PLATFORM="$CARTHAGE_PLATFORM"

From 6d5bb0ca1d561589cbcf5ffbacd154e5c05659cf Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Mon, 23 Aug 2021 14:52:47 +0200
Subject: [PATCH 063/326] Initialize pointer

---
 Sources/SQLite/Typed/CustomFunctions.swift     | 2 +-
 Tests/SQLiteTests/CustomAggregationTests.swift | 1 +
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/Sources/SQLite/Typed/CustomFunctions.swift b/Sources/SQLite/Typed/CustomFunctions.swift
index 1d66d075..c38113d1 100644
--- a/Sources/SQLite/Typed/CustomFunctions.swift
+++ b/Sources/SQLite/Typed/CustomFunctions.swift
@@ -217,7 +217,7 @@ public extension Connection {
 
         let state: () -> UnsafeMutablePointer<T> = {
             let pointer = UnsafeMutablePointer<T>.allocate(capacity: 1)
-            pointer.pointee = initialValue
+            pointer.initialize(to: initialValue)
             return pointer
         }
 
diff --git a/Tests/SQLiteTests/CustomAggregationTests.swift b/Tests/SQLiteTests/CustomAggregationTests.swift
index 337a5aa8..d4569eef 100644
--- a/Tests/SQLiteTests/CustomAggregationTests.swift
+++ b/Tests/SQLiteTests/CustomAggregationTests.swift
@@ -105,6 +105,7 @@ class CustomAggregationTests: SQLiteTestCase {
         }
         db.createAggregation("myReduceSUM3", initialValue: initial, reduce: reduce, result: { $0 })
         let result = try! db.prepare("SELECT myReduceSUM3(email) AS s FROM users")
+
         let i = result.columnNames.firstIndex(of: "s")!
         for row in result {
             let value = row[i] as? String

From 9bfc767ce660df89eaacd35e2ff23c8465c3ac3b Mon Sep 17 00:00:00 2001
From: Nathan <contact@nathanfallet.me>
Date: Tue, 24 Aug 2021 10:36:16 +0200
Subject: [PATCH 064/326] Triggering GitHub Actions workflow


From 7ec7a266bf3cc071db3a6dbdb2c1628ebf3e212c Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Tue, 24 Aug 2021 17:49:52 +0200
Subject: [PATCH 065/326] Fix truncated strings in function

Closes #468
---
 Sources/SQLite/Core/Connection.swift         | 2 +-
 Tests/SQLiteTests/CustomFunctionsTests.swift | 9 +++++++++
 2 files changed, 10 insertions(+), 1 deletion(-)

diff --git a/Sources/SQLite/Core/Connection.swift b/Sources/SQLite/Core/Connection.swift
index fa086181..b2bc2cbd 100644
--- a/Sources/SQLite/Core/Connection.swift
+++ b/Sources/SQLite/Core/Connection.swift
@@ -616,7 +616,7 @@ public final class Connection {
             } else if let result = result as? Int64 {
                 sqlite3_result_int64(context, result)
             } else if let result = result as? String {
-                sqlite3_result_text(context, result, Int32(result.count), SQLITE_TRANSIENT)
+                sqlite3_result_text(context, result, Int32(result.lengthOfBytes(using: .utf8)), SQLITE_TRANSIENT)
             } else if result == nil {
                 sqlite3_result_null(context)
             } else {
diff --git a/Tests/SQLiteTests/CustomFunctionsTests.swift b/Tests/SQLiteTests/CustomFunctionsTests.swift
index af1ef110..8e702e9c 100644
--- a/Tests/SQLiteTests/CustomFunctionsTests.swift
+++ b/Tests/SQLiteTests/CustomFunctionsTests.swift
@@ -135,3 +135,12 @@ class CustomFunctionWithTwoArgsTests: SQLiteTestCase {
         XCTAssertEqual("ab", result)
     }
 }
+
+class CustomFunctionTruncation: SQLiteTestCase {
+    // https://github.com/stephencelis/SQLite.swift/issues/468
+    func testStringTruncation() {
+        _ = try! db.createFunction("customLower") { (value: String) in value.lowercased() }
+        let result = try! db.prepare("SELECT customLower(?)").scalar("TÖL-AA 12") as? String
+        XCTAssertEqual("töl-aa 12", result)
+    }
+}

From 4b4dc65997c1fce4735178c7563285b988133c6a Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Wed, 25 Aug 2021 10:53:04 +0200
Subject: [PATCH 066/326] Make reference to libsqlite3.tbd non-version specific
 on macOS

https://github.com/stephencelis/SQLite.swift/pull/846
---
 SQLite.xcodeproj/project.pbxproj | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj
index ea7ab871..fcdb521c 100644
--- a/SQLite.xcodeproj/project.pbxproj
+++ b/SQLite.xcodeproj/project.pbxproj
@@ -286,7 +286,7 @@
 		EE247B911C3F822500AE3E12 /* installation@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "installation@2x.png"; sourceTree = "<group>"; };
 		EE247B921C3F822600AE3E12 /* playground@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "playground@2x.png"; sourceTree = "<group>"; };
 		EE247B931C3F826100AE3E12 /* SQLite.swift.podspec */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; path = SQLite.swift.podspec; sourceTree = "<group>"; };
-		EE9180911C46E9D30038162A /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/usr/lib/libsqlite3.tbd; sourceTree = DEVELOPER_DIR; };
+		EE9180911C46E9D30038162A /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/lib/libsqlite3.tbd; sourceTree = DEVELOPER_DIR; };
 		EE9180931C46EA210038162A /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = usr/lib/libsqlite3.tbd; sourceTree = SDKROOT; };
 /* End PBXFileReference section */
 

From be6bed351703b1e4c054e7ef403a50ca8de53e91 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Wed, 25 Aug 2021 11:06:07 +0200
Subject: [PATCH 067/326] Renamed

---
 Documentation/Index.md | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/Documentation/Index.md b/Documentation/Index.md
index 5cc678a6..fce068a2 100644
--- a/Documentation/Index.md
+++ b/Documentation/Index.md
@@ -262,10 +262,8 @@ If you have bundled it in your application, you can use FileManager to copy it t
 
 ```swift
 func copyDatabaseIfNeeded(sourcePath: String) -> Bool {
-    let path = NSSearchPathForDirectoriesInDomains(
-        .documentDirectory, .userDomainMask, true
-    ).first!
-    let destinationPath = destinationPath + "/db.sqlite3"
+    let documents = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first!
+    let destinationPath = documents + "/db.sqlite3"
     let exists = FileManager.default.fileExists(atPath: destinationPath)
     guard !exists else { return false }
     do {

From 03823fb0ccf57628db29d8644b1e09d0bb6cc2f8 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Wed, 25 Aug 2021 12:44:22 +0200
Subject: [PATCH 068/326] Add Linux tests

Closes #1064
---
 .github/workflows/build.yml                   | 10 ++++++++
 Documentation/Linux.md                        | 24 +++++++++++++++++++
 SQLite.xcodeproj/project.pbxproj              |  2 ++
 Sources/SQLite/Core/Connection.swift          | 16 ++++++++++---
 Tests/LinuxMain.swift                         |  6 -----
 Tests/SQLiteTests/ConnectionTests.swift       |  3 +++
 .../SQLiteTests/CustomAggregationTests.swift  |  4 ++++
 Tests/SQLiteTests/CustomFunctionsTests.swift  |  5 ++++
 Tests/SQLiteTests/QueryTests.swift            |  2 ++
 9 files changed, 63 insertions(+), 9 deletions(-)
 create mode 100644 Documentation/Linux.md
 delete mode 100644 Tests/LinuxMain.swift

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index af6666ba..51654fba 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -61,3 +61,13 @@ jobs:
       env:
         CARTHAGE_PLATFORM: tvOS
       run: ./run-tests.sh
+  build-linux:
+    runs-on: ubuntu-latest
+    steps:
+    - uses: actions/checkout@v2
+    - name: Install
+      run: |
+        sudo apt-get update -qq
+        sudo apt-get install -y libsqlite3-dev
+    - name: Test
+      run: swift test
diff --git a/Documentation/Linux.md b/Documentation/Linux.md
new file mode 100644
index 00000000..19189c53
--- /dev/null
+++ b/Documentation/Linux.md
@@ -0,0 +1,24 @@
+# Linux
+
+## Debugging
+
+### Create and launch docker container
+
+```shell
+$ docker container create swift:focal
+$ docker run --cap-add=SYS_PTRACE \
+  --security-opt seccomp=unconfined \
+  --security-opt apparmor=unconfined \
+  -i -t swift:focal bash
+```
+
+### Compile and run tests in debugger
+
+```shell
+$ apt-get update && apt-get install libsqlite3-dev
+$ git clone https://github.com/stephencelis/SQLite.swift.git
+$ swift test
+$ lldb .build/x86_64-unknown-linux-gnu/debug/SQLite.swiftPackageTests.xctest
+(lldb) target create ".build/x86_64-unknown-linux-gnu/debug/SQLite.swiftPackageTests.xctest"
+(lldb) run
+```
diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj
index fcdb521c..6aff8045 100644
--- a/SQLite.xcodeproj/project.pbxproj
+++ b/SQLite.xcodeproj/project.pbxproj
@@ -227,6 +227,7 @@
 		19A17399EA9E61235D5D77BF /* CipherTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CipherTests.swift; sourceTree = "<group>"; };
 		19A175C1F9CB3BBAB8FCEC7B /* RowTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RowTests.swift; sourceTree = "<group>"; };
 		19A178A39ACA9667A62663CC /* Cipher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Cipher.swift; sourceTree = "<group>"; };
+		19A1794B7972D14330A65BBD /* Linux.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = Linux.md; sourceTree = "<group>"; };
 		19A1794CC4D7827E997E32A7 /* FoundationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FoundationTests.swift; sourceTree = "<group>"; };
 		19A17B93B48B5560E6E51791 /* Fixtures.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Fixtures.swift; sourceTree = "<group>"; };
 		19A17BA55DABB480F9020C8A /* DateAndTimeFunctions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DateAndTimeFunctions.swift; sourceTree = "<group>"; };
@@ -498,6 +499,7 @@
 				EE247B8F1C3F822500AE3E12 /* Index.md */,
 				EE247B901C3F822500AE3E12 /* Resources */,
 				19A17EA3A313F129011B3FA0 /* Release.md */,
+				19A1794B7972D14330A65BBD /* Linux.md */,
 			);
 			path = Documentation;
 			sourceTree = "<group>";
diff --git a/Sources/SQLite/Core/Connection.swift b/Sources/SQLite/Core/Connection.swift
index b2bc2cbd..39f19a5c 100644
--- a/Sources/SQLite/Core/Connection.swift
+++ b/Sources/SQLite/Core/Connection.swift
@@ -629,12 +629,22 @@ public final class Connection {
             flags |= SQLITE_DETERMINISTIC
         }
         #endif
-        sqlite3_create_function_v2(handle, function, Int32(argc), flags,
-                                   unsafeBitCast(box, to: UnsafeMutableRawPointer.self), { context, argc, value in
+        let resultCode = sqlite3_create_function_v2(handle,
+                                                    function,
+                                                    Int32(argc),
+                                                    flags,
+                                                    unsafeBitCast(box, to: UnsafeMutableRawPointer.self), { context, argc, value in
             let function = unsafeBitCast(sqlite3_user_data(context), to: Function.self)
             function(context, argc, value)
         }, nil, nil, nil)
-        if functions[function] == nil { functions[function] = [:] }
+
+        if let result = Result(errorCode: resultCode, connection: self, statement: nil) {
+            fatalError("Error creating function: \(result)")
+        }
+
+        if functions[function] == nil {
+            functions[function] = [:] // fails on Linux, https://bugs.swift.org/browse/SR-4429
+        }
         functions[function]?[argc] = box
     }
 
diff --git a/Tests/LinuxMain.swift b/Tests/LinuxMain.swift
deleted file mode 100644
index 59796fde..00000000
--- a/Tests/LinuxMain.swift
+++ /dev/null
@@ -1,6 +0,0 @@
-import XCTest
-@testable import SQLiteTests
-
-XCTMain([
-testCase([
-])])
diff --git a/Tests/SQLiteTests/ConnectionTests.swift b/Tests/SQLiteTests/ConnectionTests.swift
index 37b765ab..6c4d170a 100644
--- a/Tests/SQLiteTests/ConnectionTests.swift
+++ b/Tests/SQLiteTests/ConnectionTests.swift
@@ -343,6 +343,8 @@ class ConnectionTests: SQLiteTestCase {
         }
     }
 
+    // https://bugs.swift.org/browse/SR-4429
+    #if !os(Linux)
     func test_createFunction_withArrayArguments() {
         db.createFunction("hello") { $0[0].map { "Hello, \($0)!" } }
 
@@ -390,6 +392,7 @@ class ConnectionTests: SQLiteTestCase {
             }
         }
     }
+    #endif
 
     func test_concurrent_access_single_connection() {
         // test can fail on iOS/tvOS 9.x: SQLite compile-time differences?
diff --git a/Tests/SQLiteTests/CustomAggregationTests.swift b/Tests/SQLiteTests/CustomAggregationTests.swift
index d4569eef..142d5d6e 100644
--- a/Tests/SQLiteTests/CustomAggregationTests.swift
+++ b/Tests/SQLiteTests/CustomAggregationTests.swift
@@ -13,6 +13,9 @@ import CSQLite
 import SQLite3
 #endif
 
+// https://bugs.swift.org/browse/SR-4429
+#if !os(Linux)
+
 class CustomAggregationTests: SQLiteTestCase {
     override func setUp() {
         super.setUp()
@@ -139,6 +142,7 @@ class CustomAggregationTests: SQLiteTestCase {
         XCTAssertEqual(TestObject.deinits, 3) // the initial value is still retained by the aggregate's state block, so deinits is one less than inits
     }
 }
+#endif
 
 /// This class is used to test that aggregation state variables
 /// can be reference types and are properly memory managed when
diff --git a/Tests/SQLiteTests/CustomFunctionsTests.swift b/Tests/SQLiteTests/CustomFunctionsTests.swift
index 8e702e9c..caa48acd 100644
--- a/Tests/SQLiteTests/CustomFunctionsTests.swift
+++ b/Tests/SQLiteTests/CustomFunctionsTests.swift
@@ -1,6 +1,9 @@
 import XCTest
 import SQLite
 
+// https://bugs.swift.org/browse/SR-4429
+#if !os(Linux)
+
 class CustomFunctionNoArgsTests: SQLiteTestCase {
     typealias FunctionNoOptional              = ()  -> Expression<String>
     typealias FunctionResultOptional          = ()  -> Expression<String?>
@@ -144,3 +147,5 @@ class CustomFunctionTruncation: SQLiteTestCase {
         XCTAssertEqual("töl-aa 12", result)
     }
 }
+
+#endif
diff --git a/Tests/SQLiteTests/QueryTests.swift b/Tests/SQLiteTests/QueryTests.swift
index 3a58e2fc..c80dd0c7 100644
--- a/Tests/SQLiteTests/QueryTests.swift
+++ b/Tests/SQLiteTests/QueryTests.swift
@@ -290,6 +290,7 @@ class QueryTests: XCTestCase {
         )
     }
 
+    #if !os(Linux) // depends on exact JSON serialization
     func test_insert_encodable_with_nested_encodable() throws {
         let emails = Table("emails")
         let value1 = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4,
@@ -307,6 +308,7 @@ class QueryTests: XCTestCase {
             insert
         )
     }
+    #endif
 
     func test_upsert_withOnConflict_compilesInsertOrOnConflictExpression() {
         assertSQL(

From 7d95523caa831ac0c59c98dd5cbe49675dc74a25 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Wed, 25 Aug 2021 15:50:03 +0200
Subject: [PATCH 069/326] Link to GH issue

---
 Sources/SQLite/Core/Connection.swift           | 2 +-
 Tests/SQLiteTests/ConnectionTests.swift        | 2 +-
 Tests/SQLiteTests/CustomAggregationTests.swift | 2 +-
 Tests/SQLiteTests/CustomFunctionsTests.swift   | 2 +-
 4 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/Sources/SQLite/Core/Connection.swift b/Sources/SQLite/Core/Connection.swift
index 39f19a5c..2c8c408c 100644
--- a/Sources/SQLite/Core/Connection.swift
+++ b/Sources/SQLite/Core/Connection.swift
@@ -643,7 +643,7 @@ public final class Connection {
         }
 
         if functions[function] == nil {
-            functions[function] = [:] // fails on Linux, https://bugs.swift.org/browse/SR-4429
+            functions[function] = [:] // fails on Linux, https://github.com/stephencelis/SQLite.swift/issues/1071
         }
         functions[function]?[argc] = box
     }
diff --git a/Tests/SQLiteTests/ConnectionTests.swift b/Tests/SQLiteTests/ConnectionTests.swift
index 6c4d170a..5bc4d42f 100644
--- a/Tests/SQLiteTests/ConnectionTests.swift
+++ b/Tests/SQLiteTests/ConnectionTests.swift
@@ -343,7 +343,7 @@ class ConnectionTests: SQLiteTestCase {
         }
     }
 
-    // https://bugs.swift.org/browse/SR-4429
+    // https://github.com/stephencelis/SQLite.swift/issues/1071
     #if !os(Linux)
     func test_createFunction_withArrayArguments() {
         db.createFunction("hello") { $0[0].map { "Hello, \($0)!" } }
diff --git a/Tests/SQLiteTests/CustomAggregationTests.swift b/Tests/SQLiteTests/CustomAggregationTests.swift
index 142d5d6e..d5232260 100644
--- a/Tests/SQLiteTests/CustomAggregationTests.swift
+++ b/Tests/SQLiteTests/CustomAggregationTests.swift
@@ -13,7 +13,7 @@ import CSQLite
 import SQLite3
 #endif
 
-// https://bugs.swift.org/browse/SR-4429
+// https://github.com/stephencelis/SQLite.swift/issues/1071
 #if !os(Linux)
 
 class CustomAggregationTests: SQLiteTestCase {
diff --git a/Tests/SQLiteTests/CustomFunctionsTests.swift b/Tests/SQLiteTests/CustomFunctionsTests.swift
index caa48acd..3d897f8e 100644
--- a/Tests/SQLiteTests/CustomFunctionsTests.swift
+++ b/Tests/SQLiteTests/CustomFunctionsTests.swift
@@ -1,7 +1,7 @@
 import XCTest
 import SQLite
 
-// https://bugs.swift.org/browse/SR-4429
+// https://github.com/stephencelis/SQLite.swift/issues/1071
 #if !os(Linux)
 
 class CustomFunctionNoArgsTests: SQLiteTestCase {

From 5e5ba530c064f93cff583923ae2455f0aaa67d15 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Wed, 25 Aug 2021 16:00:59 +0200
Subject: [PATCH 070/326] Link to Linux doc

---
 Documentation/Linux.md | 5 +++++
 README.md              | 1 +
 2 files changed, 6 insertions(+)

diff --git a/Documentation/Linux.md b/Documentation/Linux.md
index 19189c53..c9e85254 100644
--- a/Documentation/Linux.md
+++ b/Documentation/Linux.md
@@ -1,5 +1,10 @@
 # Linux
 
+## Limitations
+
+* Custom functions are currently not supported and crash, caused by a bug in Swift.
+See [#1071](https://github.com/stephencelis/SQLite.swift/issues/1071).
+
 ## Debugging
 
 ### Create and launch docker container
diff --git a/README.md b/README.md
index ac044dc0..9242b200 100644
--- a/README.md
+++ b/README.md
@@ -19,6 +19,7 @@ syntax _and_ intent.
  - [Well-documented][See Documentation]
  - Extensively tested
  - [SQLCipher][] support via CocoaPods
+ - Works on [Linux](Documentation/Linux.md) (with some limitations)
  - Active support at
    [StackOverflow](https://stackoverflow.com/questions/tagged/sqlite.swift),
    and [Gitter Chat Room](https://gitter.im/stephencelis/SQLite.swift)

From 0dd440cfefa79f9088547a0b5ee440736de87bf7 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Wed, 25 Aug 2021 16:04:47 +0200
Subject: [PATCH 071/326] FTS5

---
 Documentation/Linux.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Documentation/Linux.md b/Documentation/Linux.md
index c9e85254..640ced6f 100644
--- a/Documentation/Linux.md
+++ b/Documentation/Linux.md
@@ -4,6 +4,7 @@
 
 * Custom functions are currently not supported and crash, caused by a bug in Swift.
 See [#1071](https://github.com/stephencelis/SQLite.swift/issues/1071).
+* FTS5 might not work, see [#1007](https://github.com/stephencelis/SQLite.swift/issues/1007)
 
 ## Debugging
 

From 8583eb66469d69cc8e003d18ac5d8a5391bc14c6 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Wed, 25 Aug 2021 18:37:19 +0200
Subject: [PATCH 072/326] Closes #285

---
 SQLite.xcodeproj/project.pbxproj              |   8 +
 Tests/SQLiteTests/QueryIntegrationTests.swift | 222 ++++++++++++++++++
 Tests/SQLiteTests/QueryTests.swift            | 203 ----------------
 3 files changed, 230 insertions(+), 203 deletions(-)
 create mode 100644 Tests/SQLiteTests/QueryIntegrationTests.swift

diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj
index 6aff8045..6efd3a47 100644
--- a/SQLite.xcodeproj/project.pbxproj
+++ b/SQLite.xcodeproj/project.pbxproj
@@ -52,6 +52,7 @@
 		19A171F12AB8B07F2FD7201A /* Cipher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A178A39ACA9667A62663CC /* Cipher.swift */; };
 		19A1720B67ED13E6150C6A3D /* RowTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A175C1F9CB3BBAB8FCEC7B /* RowTests.swift */; };
 		19A17254FBA7894891F7297B /* FTS5Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1721B8984686B9963B45D /* FTS5Tests.swift */; };
+		19A172A9B536D02D4A98AAAD /* QueryIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17BA6B4E282C1315A115C /* QueryIntegrationTests.swift */; };
 		19A172EB202970561E5C4245 /* DateAndTimeFunctionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1729B75C33F9A0B9A89C1 /* DateAndTimeFunctionTests.swift */; };
 		19A173668D948AD4DF1F5352 /* DateAndTimeFunctions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17BA55DABB480F9020C8A /* DateAndTimeFunctions.swift */; };
 		19A1737286A74F3CF7412906 /* DateAndTimeFunctions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17BA55DABB480F9020C8A /* DateAndTimeFunctions.swift */; };
@@ -60,6 +61,7 @@
 		19A174D78559CD30679BCCCB /* FTS5Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1721B8984686B9963B45D /* FTS5Tests.swift */; };
 		19A1750CEE9B05267995CF3D /* FTS5.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1730E4390C775C25677D1 /* FTS5.swift */; };
 		19A175DFF47B84757E547C62 /* fixtures in Resources */ = {isa = PBXBuildFile; fileRef = 19A17E2695737FAB5D6086E3 /* fixtures */; };
+		19A176406BDE9D9C80CC9FA3 /* QueryIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17BA6B4E282C1315A115C /* QueryIntegrationTests.swift */; };
 		19A1769C1F3A7542BECF50FF /* DateAndTimeFunctionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1729B75C33F9A0B9A89C1 /* DateAndTimeFunctionTests.swift */; };
 		19A177CC33F2E6A24AF90B02 /* CipherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17399EA9E61235D5D77BF /* CipherTests.swift */; };
 		19A178072B371489E6A1E839 /* FoundationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1794CC4D7827E997E32A7 /* FoundationTests.swift */; };
@@ -71,6 +73,7 @@
 		19A179E76EA6207669B60C1B /* Cipher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A178A39ACA9667A62663CC /* Cipher.swift */; };
 		19A17C4B951CB054EE48AB1C /* CipherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17399EA9E61235D5D77BF /* CipherTests.swift */; };
 		19A17C80076860CF7751A056 /* DateAndTimeFunctionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1729B75C33F9A0B9A89C1 /* DateAndTimeFunctionTests.swift */; };
+		19A17CB808ACF606726E77A8 /* QueryIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17BA6B4E282C1315A115C /* QueryIntegrationTests.swift */; };
 		19A17DC282E36C4F41AA440B /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1710E73A46D5AC721CDA9 /* Errors.swift */; };
 		19A17E04C4C0956715C5676A /* FoundationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1794CC4D7827E997E32A7 /* FoundationTests.swift */; };
 		19A17E29278A12BC4F542506 /* DateAndTimeFunctions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17BA55DABB480F9020C8A /* DateAndTimeFunctions.swift */; };
@@ -231,6 +234,7 @@
 		19A1794CC4D7827E997E32A7 /* FoundationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FoundationTests.swift; sourceTree = "<group>"; };
 		19A17B93B48B5560E6E51791 /* Fixtures.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Fixtures.swift; sourceTree = "<group>"; };
 		19A17BA55DABB480F9020C8A /* DateAndTimeFunctions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DateAndTimeFunctions.swift; sourceTree = "<group>"; };
+		19A17BA6B4E282C1315A115C /* QueryIntegrationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QueryIntegrationTests.swift; sourceTree = "<group>"; };
 		19A17E2695737FAB5D6086E3 /* fixtures */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = folder; path = fixtures; sourceTree = "<group>"; };
 		19A17EA3A313F129011B3FA0 /* Release.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = Release.md; sourceTree = "<group>"; };
 		3717F907221F5D7C00B9BD3D /* CustomAggregationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomAggregationTests.swift; sourceTree = "<group>"; };
@@ -430,6 +434,7 @@
 				19A175C1F9CB3BBAB8FCEC7B /* RowTests.swift */,
 				19A1729B75C33F9A0B9A89C1 /* DateAndTimeFunctionTests.swift */,
 				D4DB368A20C09C9B00D5A58E /* SelectTests.swift */,
+				19A17BA6B4E282C1315A115C /* QueryIntegrationTests.swift */,
 			);
 			name = SQLiteTests;
 			path = Tests/SQLiteTests;
@@ -861,6 +866,7 @@
 				19A1785195182AF8731A8BDA /* RowTests.swift in Sources */,
 				19A1769C1F3A7542BECF50FF /* DateAndTimeFunctionTests.swift in Sources */,
 				D4DB368E20C09CFD00D5A58E /* SelectTests.swift in Sources */,
+				19A17CB808ACF606726E77A8 /* QueryIntegrationTests.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -951,6 +957,7 @@
 				19A1720B67ED13E6150C6A3D /* RowTests.swift in Sources */,
 				19A17C80076860CF7751A056 /* DateAndTimeFunctionTests.swift in Sources */,
 				D4DB368C20C09CFB00D5A58E /* SelectTests.swift in Sources */,
+				19A172A9B536D02D4A98AAAD /* QueryIntegrationTests.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -1011,6 +1018,7 @@
 				19A171967CC511C4F6F773C9 /* RowTests.swift in Sources */,
 				19A172EB202970561E5C4245 /* DateAndTimeFunctionTests.swift in Sources */,
 				D4DB368D20C09CFC00D5A58E /* SelectTests.swift in Sources */,
+				19A176406BDE9D9C80CC9FA3 /* QueryIntegrationTests.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
diff --git a/Tests/SQLiteTests/QueryIntegrationTests.swift b/Tests/SQLiteTests/QueryIntegrationTests.swift
new file mode 100644
index 00000000..9f5dc5aa
--- /dev/null
+++ b/Tests/SQLiteTests/QueryIntegrationTests.swift
@@ -0,0 +1,222 @@
+import XCTest
+#if SQLITE_SWIFT_STANDALONE
+import sqlite3
+#elseif SQLITE_SWIFT_SQLCIPHER
+import SQLCipher
+#elseif os(Linux)
+import CSQLite
+#else
+import SQLite3
+#endif
+@testable import SQLite
+
+class QueryIntegrationTests: SQLiteTestCase {
+
+    let id = Expression<Int64>("id")
+    let email = Expression<String>("email")
+    let age = Expression<Int>("age")
+
+    override func setUp() {
+        super.setUp()
+
+        createUsersTable()
+    }
+
+    // MARK: -
+
+    func test_select() {
+        let managerId = Expression<Int64>("manager_id")
+        let managers = users.alias("managers")
+
+        let alice = try! db.run(users.insert(email <- "alice@example.com"))
+        _ = try! db.run(users.insert(email <- "betsy@example.com", managerId <- alice))
+
+        for user in try! db.prepare(users.join(managers, on: managers[id] == users[managerId])) {
+            _ = user[users[managerId]]
+        }
+    }
+
+    func test_prepareRowIterator() {
+        let names = ["a", "b", "c"]
+        try! insertUsers(names)
+
+        let emailColumn = Expression<String>("email")
+        let emails = try! db.prepareRowIterator(users).map { $0[emailColumn] }
+
+        XCTAssertEqual(names.map({ "\($0)@example.com" }), emails.sorted())
+    }
+
+    func test_ambiguousMap() {
+        let names = ["a", "b", "c"]
+        try! insertUsers(names)
+
+        let emails = try! db.prepare("select email from users", []).map { $0[0] as! String  }
+
+        XCTAssertEqual(names.map({ "\($0)@example.com" }), emails.sorted())
+    }
+
+    func test_select_optional() {
+        let managerId = Expression<Int64?>("manager_id")
+        let managers = users.alias("managers")
+
+        let alice = try! db.run(users.insert(email <- "alice@example.com"))
+        _ = try! db.run(users.insert(email <- "betsy@example.com", managerId <- alice))
+
+        for user in try! db.prepare(users.join(managers, on: managers[id] == users[managerId])) {
+            _ = user[users[managerId]]
+        }
+    }
+
+    func test_select_codable() throws {
+        let table = Table("codable")
+        try db.run(table.create { builder in
+            builder.column(Expression<Int>("int"))
+            builder.column(Expression<String>("string"))
+            builder.column(Expression<Bool>("bool"))
+            builder.column(Expression<Double>("float"))
+            builder.column(Expression<Double>("double"))
+            builder.column(Expression<Date>("date"))
+            builder.column(Expression<String?>("optional"))
+            builder.column(Expression<Data>("sub"))
+        })
+
+        let value1 = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4,
+                                 date: Date(timeIntervalSince1970: 0), optional: nil, sub: nil)
+        let value = TestCodable(int: 5, string: "6", bool: true, float: 7, double: 8,
+                                date: Date(timeIntervalSince1970: 5000), optional: "optional", sub: value1)
+
+        try db.run(table.insert(value))
+
+        let rows = try db.prepare(table)
+        let values: [TestCodable] = try rows.map({ try $0.decode() })
+        XCTAssertEqual(values.count, 1)
+        XCTAssertEqual(values[0].int, 5)
+        XCTAssertEqual(values[0].string, "6")
+        XCTAssertEqual(values[0].bool, true)
+        XCTAssertEqual(values[0].float, 7)
+        XCTAssertEqual(values[0].double, 8)
+        XCTAssertEqual(values[0].date, Date(timeIntervalSince1970: 5000))
+        XCTAssertEqual(values[0].optional, "optional")
+        XCTAssertEqual(values[0].sub?.int, 1)
+        XCTAssertEqual(values[0].sub?.string, "2")
+        XCTAssertEqual(values[0].sub?.bool, true)
+        XCTAssertEqual(values[0].sub?.float, 3)
+        XCTAssertEqual(values[0].sub?.double, 4)
+        XCTAssertEqual(values[0].sub?.date, Date(timeIntervalSince1970: 0))
+        XCTAssertNil(values[0].sub?.optional)
+        XCTAssertNil(values[0].sub?.sub)
+    }
+
+    func test_scalar() {
+        XCTAssertEqual(0, try! db.scalar(users.count))
+        XCTAssertEqual(false, try! db.scalar(users.exists))
+
+        try! insertUsers("alice")
+        XCTAssertEqual(1, try! db.scalar(users.select(id.average)))
+    }
+
+    func test_pluck() {
+        let rowid = try! db.run(users.insert(email <- "alice@example.com"))
+        XCTAssertEqual(rowid, try! db.pluck(users)![id])
+    }
+
+    func test_insert() {
+        let id = try! db.run(users.insert(email <- "alice@example.com"))
+        XCTAssertEqual(1, id)
+    }
+
+    func test_insert_many() {
+        let id = try! db.run(users.insertMany([[email <- "alice@example.com"], [email <- "geoff@example.com"]]))
+        XCTAssertEqual(2, id)
+    }
+
+    func test_upsert() throws {
+        guard db.satisfiesMinimumVersion(minor: 24) else { return }
+        let fetchAge = { () throws -> Int? in
+            try self.db.pluck(self.users.filter(self.email == "alice@example.com")).flatMap { $0[self.age] }
+        }
+
+        let id = try db.run(users.upsert(email <- "alice@example.com", age <- 30, onConflictOf: email))
+        XCTAssertEqual(1, id)
+        XCTAssertEqual(30, try fetchAge())
+
+        let nextId = try db.run(users.upsert(email <- "alice@example.com", age <- 42, onConflictOf: email))
+        XCTAssertEqual(1, nextId)
+        XCTAssertEqual(42, try fetchAge())
+    }
+
+    func test_update() {
+        let changes = try! db.run(users.update(email <- "alice@example.com"))
+        XCTAssertEqual(0, changes)
+    }
+
+    func test_delete() {
+        let changes = try! db.run(users.delete())
+        XCTAssertEqual(0, changes)
+    }
+
+    func test_union() throws {
+        let expectedIDs = [
+            try db.run(users.insert(email <- "alice@example.com")),
+            try db.run(users.insert(email <- "sally@example.com"))
+        ]
+
+        let query1 = users.filter(email == "alice@example.com")
+        let query2 = users.filter(email == "sally@example.com")
+
+        let actualIDs = try db.prepare(query1.union(query2)).map { $0[id] }
+        XCTAssertEqual(expectedIDs, actualIDs)
+
+        let query3 = users.select(users[*], Expression<Int>(literal: "1 AS weight")).filter(email == "sally@example.com")
+        let query4 = users.select(users[*], Expression<Int>(literal: "2 AS weight")).filter(email == "alice@example.com")
+
+        print(query3.union(query4).order(Expression<Int>(literal: "weight")).asSQL())
+
+        let orderedIDs = try db.prepare(query3.union(query4).order(Expression<Int>(literal: "weight"), email)).map { $0[id] }
+        XCTAssertEqual(Array(expectedIDs.reversed()), orderedIDs)
+    }
+
+    func test_no_such_column() throws {
+        let doesNotExist = Expression<String>("doesNotExist")
+        try! insertUser("alice")
+        let row = try! db.pluck(users.filter(email == "alice@example.com"))!
+
+        XCTAssertThrowsError(try row.get(doesNotExist)) { error in
+            if case QueryError.noSuchColumn(let name, _) = error {
+                XCTAssertEqual("\"doesNotExist\"", name)
+            } else {
+                XCTFail("unexpected error: \(error)")
+            }
+        }
+    }
+
+    func test_catchConstraintError() {
+        try! db.run(users.insert(email <- "alice@example.com"))
+        do {
+            try db.run(users.insert(email <- "alice@example.com"))
+            XCTFail("expected error")
+        } catch let Result.error(_, code, _) where code == SQLITE_CONSTRAINT {
+            // expected
+        } catch let error {
+            XCTFail("unexpected error: \(error)")
+        }
+    }
+
+    // https://github.com/stephencelis/SQLite.swift/issues/285
+    func test_order_by_random() throws {
+        try insertUsers(["a", "b", "c'"])
+        let result = Array(try db.prepare(users.select(email).order(Expression<Int>.random()).limit(1)))
+        XCTAssertEqual(1, result.count)
+    }
+}
+
+
+private extension Connection {
+    func satisfiesMinimumVersion(minor: Int, patch: Int = 0) -> Bool {
+        guard let version = try? scalar("SELECT sqlite_version()") as? String else { return false }
+        let components = version.split(separator: ".", maxSplits: 3).compactMap { Int($0) }
+        guard components.count == 3 else { return false }
+
+        return components[1] >= minor && components[2] >= patch
+    }
+}
diff --git a/Tests/SQLiteTests/QueryTests.swift b/Tests/SQLiteTests/QueryTests.swift
index c80dd0c7..e0749f2c 100644
--- a/Tests/SQLiteTests/QueryTests.swift
+++ b/Tests/SQLiteTests/QueryTests.swift
@@ -468,206 +468,3 @@ class QueryTests: XCTestCase {
     }
 
 }
-
-class QueryIntegrationTests: SQLiteTestCase {
-
-    let id = Expression<Int64>("id")
-    let email = Expression<String>("email")
-    let age = Expression<Int>("age")
-
-    override func setUp() {
-        super.setUp()
-
-        createUsersTable()
-    }
-
-    // MARK: -
-
-    func test_select() {
-        let managerId = Expression<Int64>("manager_id")
-        let managers = users.alias("managers")
-
-        let alice = try! db.run(users.insert(email <- "alice@example.com"))
-        _ = try! db.run(users.insert(email <- "betsy@example.com", managerId <- alice))
-
-        for user in try! db.prepare(users.join(managers, on: managers[id] == users[managerId])) {
-            _ = user[users[managerId]]
-        }
-    }
-
-    func test_prepareRowIterator() {
-        let names = ["a", "b", "c"]
-        try! insertUsers(names)
-
-        let emailColumn = Expression<String>("email")
-        let emails = try! db.prepareRowIterator(users).map { $0[emailColumn] }
-
-        XCTAssertEqual(names.map({ "\($0)@example.com" }), emails.sorted())
-    }
-
-    func test_ambiguousMap() {
-        let names = ["a", "b", "c"]
-        try! insertUsers(names)
-
-        let emails = try! db.prepare("select email from users", []).map { $0[0] as! String  }
-
-        XCTAssertEqual(names.map({ "\($0)@example.com" }), emails.sorted())
-    }
-
-    func test_select_optional() {
-        let managerId = Expression<Int64?>("manager_id")
-        let managers = users.alias("managers")
-
-        let alice = try! db.run(users.insert(email <- "alice@example.com"))
-        _ = try! db.run(users.insert(email <- "betsy@example.com", managerId <- alice))
-
-        for user in try! db.prepare(users.join(managers, on: managers[id] == users[managerId])) {
-            _ = user[users[managerId]]
-        }
-    }
-
-    func test_select_codable() throws {
-        let table = Table("codable")
-        try db.run(table.create { builder in
-            builder.column(Expression<Int>("int"))
-            builder.column(Expression<String>("string"))
-            builder.column(Expression<Bool>("bool"))
-            builder.column(Expression<Double>("float"))
-            builder.column(Expression<Double>("double"))
-            builder.column(Expression<Date>("date"))
-            builder.column(Expression<String?>("optional"))
-            builder.column(Expression<Data>("sub"))
-        })
-
-        let value1 = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4,
-                                 date: Date(timeIntervalSince1970: 0), optional: nil, sub: nil)
-        let value = TestCodable(int: 5, string: "6", bool: true, float: 7, double: 8,
-                                date: Date(timeIntervalSince1970: 5000), optional: "optional", sub: value1)
-
-        try db.run(table.insert(value))
-
-        let rows = try db.prepare(table)
-        let values: [TestCodable] = try rows.map({ try $0.decode() })
-        XCTAssertEqual(values.count, 1)
-        XCTAssertEqual(values[0].int, 5)
-        XCTAssertEqual(values[0].string, "6")
-        XCTAssertEqual(values[0].bool, true)
-        XCTAssertEqual(values[0].float, 7)
-        XCTAssertEqual(values[0].double, 8)
-        XCTAssertEqual(values[0].date, Date(timeIntervalSince1970: 5000))
-        XCTAssertEqual(values[0].optional, "optional")
-        XCTAssertEqual(values[0].sub?.int, 1)
-        XCTAssertEqual(values[0].sub?.string, "2")
-        XCTAssertEqual(values[0].sub?.bool, true)
-        XCTAssertEqual(values[0].sub?.float, 3)
-        XCTAssertEqual(values[0].sub?.double, 4)
-        XCTAssertEqual(values[0].sub?.date, Date(timeIntervalSince1970: 0))
-        XCTAssertNil(values[0].sub?.optional)
-        XCTAssertNil(values[0].sub?.sub)
-    }
-
-    func test_scalar() {
-        XCTAssertEqual(0, try! db.scalar(users.count))
-        XCTAssertEqual(false, try! db.scalar(users.exists))
-
-        try! insertUsers("alice")
-        XCTAssertEqual(1, try! db.scalar(users.select(id.average)))
-    }
-
-    func test_pluck() {
-        let rowid = try! db.run(users.insert(email <- "alice@example.com"))
-        XCTAssertEqual(rowid, try! db.pluck(users)![id])
-    }
-
-    func test_insert() {
-        let id = try! db.run(users.insert(email <- "alice@example.com"))
-        XCTAssertEqual(1, id)
-    }
-
-    func test_insert_many() {
-        let id = try! db.run(users.insertMany([[email <- "alice@example.com"], [email <- "geoff@example.com"]]))
-        XCTAssertEqual(2, id)
-    }
-
-    func test_upsert() throws {
-        guard db.satisfiesMinimumVersion(minor: 24) else { return }
-        let fetchAge = { () throws -> Int? in
-            try self.db.pluck(self.users.filter(self.email == "alice@example.com")).flatMap { $0[self.age] }
-        }
-
-        let id = try db.run(users.upsert(email <- "alice@example.com", age <- 30, onConflictOf: email))
-        XCTAssertEqual(1, id)
-        XCTAssertEqual(30, try fetchAge())
-
-        let nextId = try db.run(users.upsert(email <- "alice@example.com", age <- 42, onConflictOf: email))
-        XCTAssertEqual(1, nextId)
-        XCTAssertEqual(42, try fetchAge())
-    }
-
-    func test_update() {
-        let changes = try! db.run(users.update(email <- "alice@example.com"))
-        XCTAssertEqual(0, changes)
-    }
-
-    func test_delete() {
-        let changes = try! db.run(users.delete())
-        XCTAssertEqual(0, changes)
-    }
-
-    func test_union() throws {
-        let expectedIDs = [
-            try db.run(users.insert(email <- "alice@example.com")),
-            try db.run(users.insert(email <- "sally@example.com"))
-        ]
-
-        let query1 = users.filter(email == "alice@example.com")
-        let query2 = users.filter(email == "sally@example.com")
-
-        let actualIDs = try db.prepare(query1.union(query2)).map { $0[id] }
-        XCTAssertEqual(expectedIDs, actualIDs)
-
-        let query3 = users.select(users[*], Expression<Int>(literal: "1 AS weight")).filter(email == "sally@example.com")
-        let query4 = users.select(users[*], Expression<Int>(literal: "2 AS weight")).filter(email == "alice@example.com")
-
-        print(query3.union(query4).order(Expression<Int>(literal: "weight")).asSQL())
-
-        let orderedIDs = try db.prepare(query3.union(query4).order(Expression<Int>(literal: "weight"), email)).map { $0[id] }
-        XCTAssertEqual(Array(expectedIDs.reversed()), orderedIDs)
-    }
-
-    func test_no_such_column() throws {
-        let doesNotExist = Expression<String>("doesNotExist")
-        try! insertUser("alice")
-        let row = try! db.pluck(users.filter(email == "alice@example.com"))!
-
-        XCTAssertThrowsError(try row.get(doesNotExist)) { error in
-            if case QueryError.noSuchColumn(let name, _) = error {
-                XCTAssertEqual("\"doesNotExist\"", name)
-            } else {
-                XCTFail("unexpected error: \(error)")
-            }
-        }
-    }
-
-    func test_catchConstraintError() {
-        try! db.run(users.insert(email <- "alice@example.com"))
-        do {
-            try db.run(users.insert(email <- "alice@example.com"))
-            XCTFail("expected error")
-        } catch let Result.error(_, code, _) where code == SQLITE_CONSTRAINT {
-            // expected
-        } catch let error {
-            XCTFail("unexpected error: \(error)")
-        }
-    }
-}
-
-private extension Connection {
-    func satisfiesMinimumVersion(minor: Int, patch: Int = 0) -> Bool {
-        guard let version = try? scalar("SELECT sqlite_version()") as? String else { return false }
-        let components = version.split(separator: ".", maxSplits: 3).compactMap { Int($0) }
-        guard components.count == 3 else { return false }
-
-        return components[1] >= minor && components[2] >= patch
-    }
-}

From b94e7a9e453b07c8eea8486d266744e3a8c6751d Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Wed, 25 Aug 2021 18:42:45 +0200
Subject: [PATCH 073/326] Lint

---
 Tests/SQLiteTests/QueryIntegrationTests.swift | 1 -
 1 file changed, 1 deletion(-)

diff --git a/Tests/SQLiteTests/QueryIntegrationTests.swift b/Tests/SQLiteTests/QueryIntegrationTests.swift
index 9f5dc5aa..c98b1601 100644
--- a/Tests/SQLiteTests/QueryIntegrationTests.swift
+++ b/Tests/SQLiteTests/QueryIntegrationTests.swift
@@ -210,7 +210,6 @@ class QueryIntegrationTests: SQLiteTestCase {
     }
 }
 
-
 private extension Connection {
     func satisfiesMinimumVersion(minor: Int, patch: Int = 0) -> Bool {
         guard let version = try? scalar("SELECT sqlite_version()") as? String else { return false }

From 723cdc8456cc87ce5dadbc6a25b3f4bbf46275c9 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Wed, 25 Aug 2021 18:52:21 +0200
Subject: [PATCH 074/326] Lint fixes, swap param order

---
 SQLite.xcodeproj/project.pbxproj        |  8 +++
 Sources/SQLite/Core/Backup.swift        | 69 ++++++++++++-------------
 Sources/SQLite/Core/Connection.swift    | 50 +++---------------
 Sources/SQLite/Core/Result.swift        | 45 ++++++++++++++++
 Tests/SQLiteTests/ConnectionTests.swift | 10 ++--
 5 files changed, 97 insertions(+), 85 deletions(-)
 create mode 100644 Sources/SQLite/Core/Result.swift

diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj
index fb40791a..b5929023 100644
--- a/SQLite.xcodeproj/project.pbxproj
+++ b/SQLite.xcodeproj/project.pbxproj
@@ -48,6 +48,7 @@
 		03A65E941C6BB3030062603F /* ValueTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B331C3F142E00AE3E12 /* ValueTests.swift */; };
 		03A65E951C6BB3030062603F /* TestHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B161C3F127200AE3E12 /* TestHelpers.swift */; };
 		03A65E971C6BB3210062603F /* libsqlite3.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 03A65E961C6BB3210062603F /* libsqlite3.tbd */; };
+		19A17073552293CA063BEA66 /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17E723300E5ED3771DCB5 /* Result.swift */; };
 		19A1709C3E7A406E62293B2A /* Fixtures.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17B93B48B5560E6E51791 /* Fixtures.swift */; };
 		19A17152E32A9585831E3FE0 /* DateAndTimeFunctions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17BA55DABB480F9020C8A /* DateAndTimeFunctions.swift */; };
 		19A1717B10CC941ACB5533D6 /* FTS5.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1730E4390C775C25677D1 /* FTS5.swift */; };
@@ -60,6 +61,7 @@
 		19A172EB202970561E5C4245 /* DateAndTimeFunctionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1729B75C33F9A0B9A89C1 /* DateAndTimeFunctionTests.swift */; };
 		19A173668D948AD4DF1F5352 /* DateAndTimeFunctions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17BA55DABB480F9020C8A /* DateAndTimeFunctions.swift */; };
 		19A1737286A74F3CF7412906 /* DateAndTimeFunctions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17BA55DABB480F9020C8A /* DateAndTimeFunctions.swift */; };
+		19A173EFEF0B3BD0B3ED406C /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17E723300E5ED3771DCB5 /* Result.swift */; };
 		19A17408007B182F884E3A53 /* Fixtures.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17B93B48B5560E6E51791 /* Fixtures.swift */; };
 		19A17490543609FCED53CACC /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1710E73A46D5AC721CDA9 /* Errors.swift */; };
 		19A174D78559CD30679BCCCB /* FTS5Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1721B8984686B9963B45D /* FTS5Tests.swift */; };
@@ -82,6 +84,7 @@
 		19A17E04C4C0956715C5676A /* FoundationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1794CC4D7827E997E32A7 /* FoundationTests.swift */; };
 		19A17E29278A12BC4F542506 /* DateAndTimeFunctions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17BA55DABB480F9020C8A /* DateAndTimeFunctions.swift */; };
 		19A17EC0D68BA8C03288ADF7 /* FTS5.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1730E4390C775C25677D1 /* FTS5.swift */; };
+		19A17F1B3F0A3C96B5ED6D64 /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17E723300E5ED3771DCB5 /* Result.swift */; };
 		19A17F3E1F7ACA33BD43E138 /* fixtures in Resources */ = {isa = PBXBuildFile; fileRef = 19A17E2695737FAB5D6086E3 /* fixtures */; };
 		19A17F60B685636D1F83C2DD /* Fixtures.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17B93B48B5560E6E51791 /* Fixtures.swift */; };
 		19A17FB80B94E882050AA908 /* FoundationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1794CC4D7827E997E32A7 /* FoundationTests.swift */; };
@@ -241,6 +244,7 @@
 		19A17BA55DABB480F9020C8A /* DateAndTimeFunctions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DateAndTimeFunctions.swift; sourceTree = "<group>"; };
 		19A17BA6B4E282C1315A115C /* QueryIntegrationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QueryIntegrationTests.swift; sourceTree = "<group>"; };
 		19A17E2695737FAB5D6086E3 /* fixtures */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = folder; path = fixtures; sourceTree = "<group>"; };
+		19A17E723300E5ED3771DCB5 /* Result.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Result.swift; sourceTree = "<group>"; };
 		19A17EA3A313F129011B3FA0 /* Release.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = Release.md; sourceTree = "<group>"; };
 		3717F907221F5D7C00B9BD3D /* CustomAggregationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomAggregationTests.swift; sourceTree = "<group>"; };
 		3D67B3E51DB2469200A4F4C6 /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = Platforms/WatchOS.platform/Developer/SDKs/WatchOS3.0.sdk/usr/lib/libsqlite3.tbd; sourceTree = DEVELOPER_DIR; };
@@ -456,6 +460,7 @@
 				EE247AF31C3F06E900AE3E12 /* Value.swift */,
 				19A1710E73A46D5AC721CDA9 /* Errors.swift */,
 				02A43A9722738CF100FEC494 /* Backup.swift */,
+				19A17E723300E5ED3771DCB5 /* Result.swift */,
 			);
 			path = Core;
 			sourceTree = "<group>";
@@ -843,6 +848,7 @@
 				02A43A9A22738CF100FEC494 /* Backup.swift in Sources */,
 				19A17FF4A10B44D3937C8CAC /* Errors.swift in Sources */,
 				19A1737286A74F3CF7412906 /* DateAndTimeFunctions.swift in Sources */,
+				19A17073552293CA063BEA66 /* Result.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -936,6 +942,7 @@
 				02A43A9822738CF100FEC494 /* Backup.swift in Sources */,
 				19A1792C0520D4E83C2EB075 /* Errors.swift in Sources */,
 				19A17E29278A12BC4F542506 /* DateAndTimeFunctions.swift in Sources */,
+				19A173EFEF0B3BD0B3ED406C /* Result.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -998,6 +1005,7 @@
 				02A43A9922738CF100FEC494 /* Backup.swift in Sources */,
 				19A17490543609FCED53CACC /* Errors.swift in Sources */,
 				19A17152E32A9585831E3FE0 /* DateAndTimeFunctions.swift in Sources */,
+				19A17F1B3F0A3C96B5ED6D64 /* Result.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
diff --git a/Sources/SQLite/Core/Backup.swift b/Sources/SQLite/Core/Backup.swift
index 1ea9637d..84ebf1e0 100644
--- a/Sources/SQLite/Core/Backup.swift
+++ b/Sources/SQLite/Core/Backup.swift
@@ -38,19 +38,19 @@ import SQLite3
 ///
 /// See: <https://www.sqlite.org/backup.html>
 public final class Backup {
-    
+
     /// The name of the database to backup
     public enum DatabaseName {
-        
+
         /// The main database
         case main
-        
+
         /// The temporary database
         case temp
-        
+
         /// A database added to the connection with ATTACH statement
         case attached(name: String)
-        
+
         var name: String {
             switch self {
             case .main:
@@ -62,16 +62,16 @@ public final class Backup {
             }
         }
     }
-    
+
     /// Number of pages to copy while performing a backup step
     public enum Pages {
-        
+
         /// Indicates all remaining pages should be copied
         case all
-        
+
         /// Indicates the maximal number of pages to be copied in single step
         case limited(number: Int32)
-        
+
         var number: Int32 {
             switch self {
             case .all:
@@ -81,63 +81,60 @@ public final class Backup {
             }
         }
     }
-    
+
     /// Total number of pages to copy
     ///
     /// See: <https://www.sqlite.org/c3ref/backup_finish.html#sqlite3backuppagecount>
     public var pageCount: Int32 {
         return handle.map { sqlite3_backup_pagecount($0) } ?? 0
     }
-    
+
     /// Number of remaining pages to copy.
     ///
     /// See: <https://www.sqlite.org/c3ref/backup_finish.html#sqlite3backupremaining>
     public var remainingPages: Int32 {
         return handle.map { sqlite3_backup_remaining($0) } ?? 0
     }
-    
+
     private let targetConnection: Connection
     private let sourceConnection: Connection
-    
+
     private var handle: OpaquePointer?
-    
+
     /// Initializes a new SQLite backup.
     ///
     /// - Parameters:
     ///
-    ///   - targetConnection: The connection to the database to save backup into.
-    ///
-    ///   - targetName: The name of the database to save backup into.
-    ///
-    ///     Default: `.main`.
-    ///
     ///   - sourceConnection: The connection to the database to backup.
-    ///
     ///   - sourceName: The name of the database to backup.
-    /// 
+    ///     Default: `.main`.
+    ///
+    ///   - targetConnection: The connection to the database to save backup into.
+    ///   - targetName: The name of the database to save backup into.
     ///     Default: `.main`.
     ///
     /// - Returns: A new database backup.
     ///
     /// See: <https://www.sqlite.org/c3ref/backup_finish.html#sqlite3backupinit>
-    public init(targetConnection: Connection,
-                targetName: DatabaseName = .main,
-                sourceConnection: Connection,
-                sourceName: DatabaseName = .main) throws {
-        
+    public init(sourceConnection: Connection,
+                sourceName: DatabaseName = .main,
+                targetConnection: Connection,
+                targetName: DatabaseName = .main) throws {
+
         self.targetConnection = targetConnection
         self.sourceConnection = sourceConnection
-        
+
         self.handle = sqlite3_backup_init(targetConnection.handle,
                                           targetName.name,
                                           sourceConnection.handle,
                                           sourceName.name)
-        
-        if self.handle == nil, let error = Result(errorCode: sqlite3_errcode(targetConnection.handle), connection: targetConnection) {
+
+        if handle == nil, let error = Result(errorCode: sqlite3_errcode(targetConnection.handle),
+                                             connection: targetConnection) {
             throw error
         }
     }
-    
+
     /// Performs a backup step.
     ///
     /// - Parameter pagesToCopy: The maximal number of pages to copy in one step
@@ -147,17 +144,17 @@ public final class Backup {
     /// See: <https://www.sqlite.org/c3ref/backup_finish.html#sqlite3backupstep>
     public func step(pagesToCopy pages: Pages = .all) throws {
         let status = sqlite3_backup_step(handle, pages.number)
-        
+
         guard status != SQLITE_DONE else {
             finish()
             return
         }
-        
+
         if let error = Result(errorCode: status, connection: targetConnection) {
             throw error
         }
     }
-    
+
     /// Finalizes backup.
     ///
     /// See: <https://www.sqlite.org/c3ref/backup_finish.html#sqlite3backupfinish>
@@ -165,11 +162,11 @@ public final class Backup {
         guard let handle = self.handle else {
             return
         }
-        
+
         sqlite3_backup_finish(handle)
         self.handle = nil
     }
-    
+
     deinit {
         finish()
     }
diff --git a/Sources/SQLite/Core/Connection.swift b/Sources/SQLite/Core/Connection.swift
index 5e34457c..a9012462 100644
--- a/Sources/SQLite/Core/Connection.swift
+++ b/Sources/SQLite/Core/Connection.swift
@@ -790,9 +790,9 @@ public final class Connection {
     }
     fileprivate typealias Collation = @convention(block) (UnsafeRawPointer, UnsafeRawPointer) -> Int32
     fileprivate var collations = [String: Collation]()
-    
+
     // MARK: - Backup
-    
+
     /// Prepares a new backup for current connection.
     ///
     /// - Parameters:
@@ -808,16 +808,14 @@ public final class Connection {
     ///     Default: `.main`.
     ///
     /// - Returns: A new database backup.
-    
+
     public func backup(databaseName: Backup.DatabaseName = .main,
                        usingConnection targetConnection: Connection,
                        andDatabaseName targetDatabaseName: Backup.DatabaseName = .main) throws -> Backup {
-        return try Backup(targetConnection: targetConnection,
-                          targetName: targetDatabaseName,
-                          sourceConnection: self,
-                          sourceName: databaseName)
+        try Backup(sourceConnection: self, sourceName: databaseName, targetConnection: targetConnection,
+                   targetName: targetDatabaseName)
     }
-    
+
     // MARK: - Error Handling
 
     func sync<T>(_ block: () throws -> T) rethrows -> T {
@@ -866,39 +864,3 @@ extension Connection.Location: CustomStringConvertible {
     }
 
 }
-
-public enum Result: Error {
-
-    fileprivate static let successCodes: Set = [SQLITE_OK, SQLITE_ROW, SQLITE_DONE]
-
-    /// Represents a SQLite specific [error code](https://sqlite.org/rescode.html)
-    ///
-    /// - message: English-language text that describes the error
-    ///
-    /// - code: SQLite [error code](https://sqlite.org/rescode.html#primary_result_code_list)
-    ///
-    /// - statement: the statement which produced the error
-    case error(message: String, code: Int32, statement: Statement?)
-
-    init?(errorCode: Int32, connection: Connection, statement: Statement? = nil) {
-        guard !Result.successCodes.contains(errorCode) else { return nil }
-
-        let message = String(cString: sqlite3_errmsg(connection.handle))
-        self = .error(message: message, code: errorCode, statement: statement)
-    }
-
-}
-
-extension Result: CustomStringConvertible {
-
-    public var description: String {
-        switch self {
-        case let .error(message, errorCode, statement):
-            if let statement = statement {
-                return "\(message) (\(statement)) (code: \(errorCode))"
-            } else {
-                return "\(message) (code: \(errorCode))"
-            }
-        }
-    }
-}
diff --git a/Sources/SQLite/Core/Result.swift b/Sources/SQLite/Core/Result.swift
new file mode 100644
index 00000000..3de4d25d
--- /dev/null
+++ b/Sources/SQLite/Core/Result.swift
@@ -0,0 +1,45 @@
+#if SQLITE_SWIFT_STANDALONE
+import sqlite3
+#elseif SQLITE_SWIFT_SQLCIPHER
+import SQLCipher
+#elseif os(Linux)
+import CSQLite
+#else
+import SQLite3
+#endif
+
+public enum Result: Error {
+
+    fileprivate static let successCodes: Set = [SQLITE_OK, SQLITE_ROW, SQLITE_DONE]
+
+    /// Represents a SQLite specific [error code](https://sqlite.org/rescode.html)
+    ///
+    /// - message: English-language text that describes the error
+    ///
+    /// - code: SQLite [error code](https://sqlite.org/rescode.html#primary_result_code_list)
+    ///
+    /// - statement: the statement which produced the error
+    case error(message: String, code: Int32, statement: Statement?)
+
+    init?(errorCode: Int32, connection: Connection, statement: Statement? = nil) {
+        guard !Result.successCodes.contains(errorCode) else { return nil }
+
+        let message = String(cString: sqlite3_errmsg(connection.handle))
+        self = .error(message: message, code: errorCode, statement: statement)
+    }
+
+}
+
+extension Result: CustomStringConvertible {
+
+    public var description: String {
+        switch self {
+        case let .error(message, errorCode, statement):
+            if let statement = statement {
+                return "\(message) (\(statement)) (code: \(errorCode))"
+            } else {
+                return "\(message) (code: \(errorCode))"
+            }
+        }
+    }
+}
diff --git a/Tests/SQLiteTests/ConnectionTests.swift b/Tests/SQLiteTests/ConnectionTests.swift
index 1cb70732..ae881289 100644
--- a/Tests/SQLiteTests/ConnectionTests.swift
+++ b/Tests/SQLiteTests/ConnectionTests.swift
@@ -147,15 +147,15 @@ class ConnectionTests: SQLiteTestCase {
 
         assertSQL("BEGIN EXCLUSIVE TRANSACTION")
     }
-    
+
     func test_backup_copiesDatabase() throws {
         let target = try Connection()
-        
-        try InsertUsers("alice", "betsy")
-        
+
+        try insertUsers("alice", "betsy")
+
         let backup = try db.backup(usingConnection: target)
         try backup.step()
-        
+
         let users = try target.prepare("SELECT email FROM users ORDER BY email")
         XCTAssertEqual(users.map { $0[0] as? String }, ["alice@example.com", "betsy@example.com"])
     }

From bcbfe980c789f73de1fe8486d78f82fda67b95b5 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Wed, 25 Aug 2021 18:56:02 +0200
Subject: [PATCH 075/326] Update changelog

---
 CHANGELOG.md | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 465438bc..fc09fa40 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,9 @@
+0.13.1 (tba)
+========================================
+
+* Support for database backup ([#919][])
+* Support for custom SQL aggregates ([#881][])
+
 0.13.0 (22-08-2021), [diff][diff-0.13.0]
 ========================================
 
@@ -116,3 +122,5 @@
 [#726]: https://github.com/stephencelis/SQLite.swift/pull/726
 [#797]: https://github.com/stephencelis/SQLite.swift/pull/797
 [#866]: https://github.com/stephencelis/SQLite.swift/pull/866
+[#881]: https://github.com/stephencelis/SQLite.swift/pull/881
+[#919]: https://github.com/stephencelis/SQLite.swift/pull/919

From 2ae7b9b61dfdc7e593108f8048ba19da5b51d656 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Wed, 25 Aug 2021 19:46:58 +0200
Subject: [PATCH 076/326] Missing target

---
 SQLite.xcodeproj/project.pbxproj | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj
index b5929023..b30c18d2 100644
--- a/SQLite.xcodeproj/project.pbxproj
+++ b/SQLite.xcodeproj/project.pbxproj
@@ -81,6 +81,7 @@
 		19A17C80076860CF7751A056 /* DateAndTimeFunctionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1729B75C33F9A0B9A89C1 /* DateAndTimeFunctionTests.swift */; };
 		19A17CB808ACF606726E77A8 /* QueryIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17BA6B4E282C1315A115C /* QueryIntegrationTests.swift */; };
 		19A17DC282E36C4F41AA440B /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1710E73A46D5AC721CDA9 /* Errors.swift */; };
+		19A17DF8D4F13A20F5D2269E /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17E723300E5ED3771DCB5 /* Result.swift */; };
 		19A17E04C4C0956715C5676A /* FoundationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1794CC4D7827E997E32A7 /* FoundationTests.swift */; };
 		19A17E29278A12BC4F542506 /* DateAndTimeFunctions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17BA55DABB480F9020C8A /* DateAndTimeFunctions.swift */; };
 		19A17EC0D68BA8C03288ADF7 /* FTS5.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1730E4390C775C25677D1 /* FTS5.swift */; };
@@ -911,6 +912,7 @@
 				02A43A9B22738CF100FEC494 /* Backup.swift in Sources */,
 				19A17DC282E36C4F41AA440B /* Errors.swift in Sources */,
 				19A173668D948AD4DF1F5352 /* DateAndTimeFunctions.swift in Sources */,
+				19A17DF8D4F13A20F5D2269E /* Result.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};

From 5e64bb587b8b45a478634eed54df5f26d84b0b5e Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Wed, 25 Aug 2021 19:25:53 +0200
Subject: [PATCH 077/326] Split and simplify code, remove duplication

---
 .swiftlint.yml                                |   5 +-
 SQLite.xcodeproj/project.pbxproj              |   8 +
 .../SQLite/Core/Connection+Aggregation.swift  | 155 ++++++++++++
 Sources/SQLite/Core/Connection.swift          | 225 +++++-------------
 Sources/SQLite/Typed/CustomFunctions.swift    |  65 -----
 Sources/SQLite/Typed/Query.swift              |   1 -
 6 files changed, 226 insertions(+), 233 deletions(-)
 create mode 100644 Sources/SQLite/Core/Connection+Aggregation.swift

diff --git a/.swiftlint.yml b/.swiftlint.yml
index b5e53d56..34bb253b 100644
--- a/.swiftlint.yml
+++ b/.swiftlint.yml
@@ -38,5 +38,6 @@ line_length:
   ignores_comments: true
 
 file_length:
-  warning: 900
-  error: 900
+  warning: 500
+  error: 500
+  ignore_comment_only_lines: true
diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj
index b30c18d2..5bb52f4e 100644
--- a/SQLite.xcodeproj/project.pbxproj
+++ b/SQLite.xcodeproj/project.pbxproj
@@ -50,6 +50,7 @@
 		03A65E971C6BB3210062603F /* libsqlite3.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 03A65E961C6BB3210062603F /* libsqlite3.tbd */; };
 		19A17073552293CA063BEA66 /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17E723300E5ED3771DCB5 /* Result.swift */; };
 		19A1709C3E7A406E62293B2A /* Fixtures.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17B93B48B5560E6E51791 /* Fixtures.swift */; };
+		19A170ACC97B19730FB7BA4D /* Connection+Aggregation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A175A9CB446640AE6F2200 /* Connection+Aggregation.swift */; };
 		19A17152E32A9585831E3FE0 /* DateAndTimeFunctions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17BA55DABB480F9020C8A /* DateAndTimeFunctions.swift */; };
 		19A1717B10CC941ACB5533D6 /* FTS5.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1730E4390C775C25677D1 /* FTS5.swift */; };
 		19A171967CC511C4F6F773C9 /* RowTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A175C1F9CB3BBAB8FCEC7B /* RowTests.swift */; };
@@ -67,6 +68,7 @@
 		19A174D78559CD30679BCCCB /* FTS5Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1721B8984686B9963B45D /* FTS5Tests.swift */; };
 		19A1750CEE9B05267995CF3D /* FTS5.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1730E4390C775C25677D1 /* FTS5.swift */; };
 		19A175DFF47B84757E547C62 /* fixtures in Resources */ = {isa = PBXBuildFile; fileRef = 19A17E2695737FAB5D6086E3 /* fixtures */; };
+		19A176376CB6A94759F7980A /* Connection+Aggregation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A175A9CB446640AE6F2200 /* Connection+Aggregation.swift */; };
 		19A176406BDE9D9C80CC9FA3 /* QueryIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17BA6B4E282C1315A115C /* QueryIntegrationTests.swift */; };
 		19A1769C1F3A7542BECF50FF /* DateAndTimeFunctionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1729B75C33F9A0B9A89C1 /* DateAndTimeFunctionTests.swift */; };
 		19A177CC33F2E6A24AF90B02 /* CipherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17399EA9E61235D5D77BF /* CipherTests.swift */; };
@@ -75,6 +77,7 @@
 		19A1785195182AF8731A8BDA /* RowTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A175C1F9CB3BBAB8FCEC7B /* RowTests.swift */; };
 		19A1792C0520D4E83C2EB075 /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1710E73A46D5AC721CDA9 /* Errors.swift */; };
 		19A179A0C45377CB09BB358C /* CipherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17399EA9E61235D5D77BF /* CipherTests.swift */; };
+		19A179B59450FE7C4811AB8A /* Connection+Aggregation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A175A9CB446640AE6F2200 /* Connection+Aggregation.swift */; };
 		19A179CCF9671E345E5A9811 /* Cipher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A178A39ACA9667A62663CC /* Cipher.swift */; };
 		19A179E76EA6207669B60C1B /* Cipher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A178A39ACA9667A62663CC /* Cipher.swift */; };
 		19A17C4B951CB054EE48AB1C /* CipherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17399EA9E61235D5D77BF /* CipherTests.swift */; };
@@ -237,6 +240,7 @@
 		19A1729B75C33F9A0B9A89C1 /* DateAndTimeFunctionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DateAndTimeFunctionTests.swift; sourceTree = "<group>"; };
 		19A1730E4390C775C25677D1 /* FTS5.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FTS5.swift; sourceTree = "<group>"; };
 		19A17399EA9E61235D5D77BF /* CipherTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CipherTests.swift; sourceTree = "<group>"; };
+		19A175A9CB446640AE6F2200 /* Connection+Aggregation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Connection+Aggregation.swift"; sourceTree = "<group>"; };
 		19A175C1F9CB3BBAB8FCEC7B /* RowTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RowTests.swift; sourceTree = "<group>"; };
 		19A178A39ACA9667A62663CC /* Cipher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Cipher.swift; sourceTree = "<group>"; };
 		19A1794B7972D14330A65BBD /* Linux.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = Linux.md; sourceTree = "<group>"; };
@@ -462,6 +466,7 @@
 				19A1710E73A46D5AC721CDA9 /* Errors.swift */,
 				02A43A9722738CF100FEC494 /* Backup.swift */,
 				19A17E723300E5ED3771DCB5 /* Result.swift */,
+				19A175A9CB446640AE6F2200 /* Connection+Aggregation.swift */,
 			);
 			path = Core;
 			sourceTree = "<group>";
@@ -850,6 +855,7 @@
 				19A17FF4A10B44D3937C8CAC /* Errors.swift in Sources */,
 				19A1737286A74F3CF7412906 /* DateAndTimeFunctions.swift in Sources */,
 				19A17073552293CA063BEA66 /* Result.swift in Sources */,
+				19A179B59450FE7C4811AB8A /* Connection+Aggregation.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -945,6 +951,7 @@
 				19A1792C0520D4E83C2EB075 /* Errors.swift in Sources */,
 				19A17E29278A12BC4F542506 /* DateAndTimeFunctions.swift in Sources */,
 				19A173EFEF0B3BD0B3ED406C /* Result.swift in Sources */,
+				19A176376CB6A94759F7980A /* Connection+Aggregation.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -1008,6 +1015,7 @@
 				19A17490543609FCED53CACC /* Errors.swift in Sources */,
 				19A17152E32A9585831E3FE0 /* DateAndTimeFunctions.swift in Sources */,
 				19A17F1B3F0A3C96B5ED6D64 /* Result.swift in Sources */,
+				19A170ACC97B19730FB7BA4D /* Connection+Aggregation.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
diff --git a/Sources/SQLite/Core/Connection+Aggregation.swift b/Sources/SQLite/Core/Connection+Aggregation.swift
new file mode 100644
index 00000000..257d48ed
--- /dev/null
+++ b/Sources/SQLite/Core/Connection+Aggregation.swift
@@ -0,0 +1,155 @@
+import Foundation
+#if SQLITE_SWIFT_STANDALONE
+import sqlite3
+#elseif SQLITE_SWIFT_SQLCIPHER
+import SQLCipher
+#elseif os(Linux)
+import CSQLite
+#else
+import SQLite3
+#endif
+
+extension Connection {
+    private typealias Aggregate = @convention(block) (Int, OpaquePointer?, Int32, UnsafeMutablePointer<OpaquePointer?>?) -> Void
+
+    /// Creates or redefines a custom SQL aggregate.
+    ///
+    /// - Parameters:
+    ///
+    ///   - aggregate: The name of the aggregate to create or redefine.
+    ///
+    ///   - argumentCount: The number of arguments that the aggregate takes. If
+    ///     `nil`, the aggregate may take any number of arguments.
+    ///
+    ///     Default: `nil`
+    ///
+    ///   - deterministic: Whether or not the aggregate is deterministic (_i.e._
+    ///     the aggregate always returns the same result for a given input).
+    ///
+    ///     Default: `false`
+    ///
+    ///   - step: A block of code to run for each row of an aggregation group.
+    ///     The block is called with an array of raw SQL values mapped to the
+    ///     aggregate’s parameters, and an UnsafeMutablePointer to a state
+    ///     variable.
+    ///
+    ///   - final: A block of code to run after each row of an aggregation group
+    ///     is processed. The block is called with an UnsafeMutablePointer to a
+    ///     state variable, and should return a raw SQL value (or nil).
+    ///
+    ///   - state: A block of code to run to produce a fresh state variable for
+    ///     each aggregation group. The block should return an
+    ///     UnsafeMutablePointer to the fresh state variable.
+    public func createAggregation<T>(
+            _ functionName: String,
+            argumentCount: UInt? = nil,
+            deterministic: Bool = false,
+            step: @escaping ([Binding?], UnsafeMutablePointer<T>) -> Void,
+            final: @escaping (UnsafeMutablePointer<T>) -> Binding?,
+            state: @escaping () -> UnsafeMutablePointer<T>) {
+
+        let argc = argumentCount.map { Int($0) } ?? -1
+        let box: Aggregate = { (stepFlag: Int, context: OpaquePointer?, argc: Int32, argv: UnsafeMutablePointer<OpaquePointer?>?) in
+            let nBytes = Int32(MemoryLayout<UnsafeMutablePointer<Int64>>.size)
+            guard let aggregateContext = sqlite3_aggregate_context(context, nBytes) else {
+                fatalError("Could not get aggregate context")
+            }
+            let mutablePointer = aggregateContext.assumingMemoryBound(to: UnsafeMutableRawPointer.self)
+            if stepFlag > 0 {
+                let arguments = getArguments(argc: argc, argv: argv)
+                if aggregateContext.assumingMemoryBound(to: Int64.self).pointee == 0 {
+                    mutablePointer.pointee = UnsafeMutableRawPointer(mutating: state())
+                }
+                step(arguments, mutablePointer.pointee.assumingMemoryBound(to: T.self))
+            } else {
+                let result = final(mutablePointer.pointee.assumingMemoryBound(to: T.self))
+                set(result: result, on: context)
+            }
+        }
+
+        func xStep(context: OpaquePointer?, argc: Int32, value: UnsafeMutablePointer<OpaquePointer?>?) {
+            unsafeBitCast(sqlite3_user_data(context), to: Aggregate.self)(1, context, argc, value)
+        }
+
+        func xFinal(context: OpaquePointer?) {
+            unsafeBitCast(sqlite3_user_data(context), to: Aggregate.self)(0, context, 0, nil)
+        }
+
+        let flags = SQLITE_UTF8 | (deterministic ? SQLITE_DETERMINISTIC : 0)
+        let resultCode = sqlite3_create_function_v2(
+            handle,
+            functionName,
+            Int32(argc),
+            flags,
+            /* pApp */ unsafeBitCast(box, to: UnsafeMutableRawPointer.self),
+            /* xFunc */ nil, xStep, xFinal, /* xDestroy */ nil
+        )
+        if let result = Result(errorCode: resultCode, connection: self) {
+            fatalError("Error creating function: \(result)")
+        }
+        register(functionName, argc: argc, value: box)
+    }
+
+    func createAggregation<T: AnyObject>(
+            _ aggregate: String,
+            argumentCount: UInt? = nil,
+            deterministic: Bool = false,
+            initialValue: T,
+            reduce: @escaping (T, [Binding?]) -> T,
+            result: @escaping (T) -> Binding?
+    ) {
+        let step: ([Binding?], UnsafeMutablePointer<UnsafeMutableRawPointer>) -> Void = { (bindings, ptr) in
+            let pointer = ptr.pointee.assumingMemoryBound(to: T.self)
+            let current = Unmanaged<T>.fromOpaque(pointer).takeRetainedValue()
+            let next = reduce(current, bindings)
+            ptr.pointee = Unmanaged.passRetained(next).toOpaque()
+        }
+
+        let final: (UnsafeMutablePointer<UnsafeMutableRawPointer>) -> Binding? = { ptr in
+            let pointer = ptr.pointee.assumingMemoryBound(to: T.self)
+            let obj = Unmanaged<T>.fromOpaque(pointer).takeRetainedValue()
+            let value = result(obj)
+            ptr.deallocate()
+            return value
+        }
+
+        let state: () -> UnsafeMutablePointer<UnsafeMutableRawPointer> = {
+            let pointer = UnsafeMutablePointer<UnsafeMutableRawPointer>.allocate(capacity: 1)
+            pointer.pointee = Unmanaged.passRetained(initialValue).toOpaque()
+            return pointer
+        }
+
+        createAggregation(aggregate, step: step, final: final, state: state)
+    }
+
+    func createAggregation<T>(
+            _ aggregate: String,
+            argumentCount: UInt? = nil,
+            deterministic: Bool = false,
+            initialValue: T,
+            reduce: @escaping (T, [Binding?]) -> T,
+            result: @escaping (T) -> Binding?
+    ) {
+
+        let step: ([Binding?], UnsafeMutablePointer<T>) -> Void = { (bindings, pointer) in
+            let current = pointer.pointee
+            let next = reduce(current, bindings)
+            pointer.pointee = next
+        }
+
+        let final: (UnsafeMutablePointer<T>) -> Binding? = { pointer in
+            let value = result(pointer.pointee)
+            pointer.deallocate()
+            return value
+        }
+
+        let state: () -> UnsafeMutablePointer<T> = {
+            let pointer = UnsafeMutablePointer<T>.allocate(capacity: 1)
+            pointer.initialize(to: initialValue)
+            return pointer
+        }
+
+        createAggregation(aggregate, step: step, final: final, state: state)
+    }
+
+}
diff --git a/Sources/SQLite/Core/Connection.swift b/Sources/SQLite/Core/Connection.swift
index a9012462..35996b29 100644
--- a/Sources/SQLite/Core/Connection.swift
+++ b/Sources/SQLite/Core/Connection.swift
@@ -35,7 +35,6 @@ import SQLite3
 #endif
 
 /// A connection to SQLite.
-// swiftlint:disable:next type_body_length
 public final class Connection {
 
     /// The location of a SQLite database.
@@ -586,182 +585,42 @@ public final class Connection {
     ///   - block: A block of code to run when the function is called. The block
     ///     is called with an array of raw SQL values mapped to the function’s
     ///     parameters and should return a raw SQL value (or nil).
-    // swiftlint:disable:next cyclomatic_complexity
-    public func createFunction(_ function: String, argumentCount: UInt? = nil, deterministic: Bool = false,
+    public func createFunction(_ functionName: String,
+                               argumentCount: UInt? = nil,
+                               deterministic: Bool = false,
                                _ block: @escaping (_ args: [Binding?]) -> Binding?) {
         let argc = argumentCount.map { Int($0) } ?? -1
         let box: Function = { context, argc, argv in
-            let arguments: [Binding?] = (0..<Int(argc)).map { idx in
-                let value = argv![idx]
-                switch sqlite3_value_type(value) {
-                case SQLITE_BLOB:
-                    return Blob(bytes: sqlite3_value_blob(value), length: Int(sqlite3_value_bytes(value)))
-                case SQLITE_FLOAT:
-                    return sqlite3_value_double(value)
-                case SQLITE_INTEGER:
-                    return sqlite3_value_int64(value)
-                case SQLITE_NULL:
-                    return nil
-                case SQLITE_TEXT:
-                    return String(cString: UnsafePointer(sqlite3_value_text(value)))
-                case let type:
-                    fatalError("unsupported value type: \(type)")
-                }
-            }
-            let result = block(arguments)
-            if let result = result as? Blob {
-                sqlite3_result_blob(context, result.bytes, Int32(result.bytes.count), nil)
-            } else if let result = result as? Double {
-                sqlite3_result_double(context, result)
-            } else if let result = result as? Int64 {
-                sqlite3_result_int64(context, result)
-            } else if let result = result as? String {
-                sqlite3_result_text(context, result, Int32(result.lengthOfBytes(using: .utf8)), SQLITE_TRANSIENT)
-            } else if result == nil {
-                sqlite3_result_null(context)
-            } else {
-                fatalError("unsupported result type: \(String(describing: result))")
-            }
+            set(result: block(getArguments(argc: argc, argv: argv)), on: context)
         }
-        var flags = SQLITE_UTF8
-        #if !os(Linux)
-        if deterministic {
-            flags |= SQLITE_DETERMINISTIC
-        }
-        #endif
-        let resultCode = sqlite3_create_function_v2(handle,
-                                                    function,
-                                                    Int32(argc),
-                                                    flags,
-                                                    unsafeBitCast(box, to: UnsafeMutableRawPointer.self), { context, argc, value in
-            let function = unsafeBitCast(sqlite3_user_data(context), to: Function.self)
-            function(context, argc, value)
-        }, nil, nil, nil)
-
-        if let result = Result(errorCode: resultCode, connection: self, statement: nil) {
-            fatalError("Error creating function: \(result)")
+        func xFunc(context: OpaquePointer?, argc: Int32, value: UnsafeMutablePointer<OpaquePointer?>?) {
+            unsafeBitCast(sqlite3_user_data(context), to: Function.self)(context, argc, value)
         }
+        let flags = SQLITE_UTF8 | (deterministic ? SQLITE_DETERMINISTIC : 0)
+        let resultCode = sqlite3_create_function_v2(
+            handle,
+            functionName,
+            Int32(argc),
+            flags,
+            /* pApp */ unsafeBitCast(box, to: UnsafeMutableRawPointer.self),
+            xFunc, /*xStep*/ nil, /*xFinal*/ nil, /*xDestroy*/ nil
+        )
 
-        if functions[function] == nil {
-            functions[function] = [:] // fails on Linux, https://github.com/stephencelis/SQLite.swift/issues/1071
+        if let result = Result(errorCode: resultCode, connection: self) {
+            fatalError("Error creating function: \(result)")
         }
-        functions[function]?[argc] = box
+        register(functionName, argc: argc, value: box)
     }
 
-    /// Creates or redefines a custom SQL aggregate.
-    ///
-    /// - Parameters:
-    ///
-    ///   - aggregate: The name of the aggregate to create or redefine.
-    ///
-    ///   - argumentCount: The number of arguments that the aggregate takes. If
-    ///     `nil`, the aggregate may take any number of arguments.
-    ///
-    ///     Default: `nil`
-    ///
-    ///   - deterministic: Whether or not the aggregate is deterministic (_i.e._
-    ///     the aggregate always returns the same result for a given input).
-    ///
-    ///     Default: `false`
-    ///
-    ///   - step: A block of code to run for each row of an aggregation group.
-    ///     The block is called with an array of raw SQL values mapped to the
-    ///     aggregate’s parameters, and an UnsafeMutablePointer to a state
-    ///     variable.
-    ///
-    ///   - final: A block of code to run after each row of an aggregation group
-    ///     is processed. The block is called with an UnsafeMutablePointer to a
-    ///     state variable, and should return a raw SQL value (or nil).
-    ///
-    ///   - state: A block of code to run to produce a fresh state variable for
-    ///     each aggregation group. The block should return an
-    ///     UnsafeMutablePointer to the fresh state variable.
-    // swiftlint:disable:next cyclomatic_complexity
-    public func createAggregation<T>(
-        _ aggregate: String,
-        argumentCount: UInt? = nil,
-        deterministic: Bool = false,
-        step: @escaping ([Binding?], UnsafeMutablePointer<T>) -> Void,
-        final: @escaping (UnsafeMutablePointer<T>) -> Binding?,
-        state: @escaping () -> UnsafeMutablePointer<T>) {
-
-        let argc = argumentCount.map { Int($0) } ?? -1
-        let box: Aggregate = { (stepFlag: Int, context: OpaquePointer?, argc: Int32, argv: UnsafeMutablePointer<OpaquePointer?>?) in
-            let ptr = sqlite3_aggregate_context(context, 64)! // needs to be at least as large as uintptr_t; better way to do this?
-            let mutablePointer = ptr.assumingMemoryBound(to: UnsafeMutableRawPointer.self)
-            if stepFlag > 0 {
-                let arguments: [Binding?] = (0..<Int(argc)).map { idx in
-                    let value = argv![idx]
-                    switch sqlite3_value_type(value) {
-                    case SQLITE_BLOB:
-                        return Blob(bytes: sqlite3_value_blob(value), length: Int(sqlite3_value_bytes(value)))
-                    case SQLITE_FLOAT:
-                        return sqlite3_value_double(value)
-                    case SQLITE_INTEGER:
-                        return sqlite3_value_int64(value)
-                    case SQLITE_NULL:
-                        return nil
-                    case SQLITE_TEXT:
-                        return String(cString: UnsafePointer(sqlite3_value_text(value)))
-                    case let type:
-                        fatalError("unsupported value type: \(type)")
-                    }
-                }
-
-                if ptr.assumingMemoryBound(to: Int64.self).pointee == 0 {
-                    let value = state()
-                    mutablePointer.pointee = UnsafeMutableRawPointer(mutating: value)
-                }
-                step(arguments, mutablePointer.pointee.assumingMemoryBound(to: T.self))
-            } else {
-                let result = final(mutablePointer.pointee.assumingMemoryBound(to: T.self))
-                if let result = result as? Blob {
-                    sqlite3_result_blob(context, result.bytes, Int32(result.bytes.count), nil)
-                } else if let result = result as? Double {
-                    sqlite3_result_double(context, result)
-                } else if let result = result as? Int64 {
-                    sqlite3_result_int64(context, result)
-                } else if let result = result as? String {
-                    sqlite3_result_text(context, result, Int32(result.count), SQLITE_TRANSIENT)
-                } else if result == nil {
-                    sqlite3_result_null(context)
-                } else {
-                    fatalError("unsupported result type: \(String(describing: result))")
-                }
-            }
+    func register(_ functionName: String, argc: Int, value: Any) {
+        if functions[functionName] == nil {
+            functions[functionName] = [:] // fails on Linux, https://github.com/stephencelis/SQLite.swift/issues/1071
         }
-
-        var flags = SQLITE_UTF8
-        #if !os(Linux)
-        if deterministic {
-            flags |= SQLITE_DETERMINISTIC
-        }
-        #endif
-
-        sqlite3_create_function_v2(
-            handle,
-            aggregate,
-            Int32(argc),
-            flags,
-            unsafeBitCast(box, to: UnsafeMutableRawPointer.self),
-            nil,
-            { context, argc, value in
-                let function = unsafeBitCast(sqlite3_user_data(context), to: Aggregate.self)
-                function(1, context, argc, value)
-        }, { context in
-                let function = unsafeBitCast(sqlite3_user_data(context), to: Aggregate.self)
-                function(0, context, 0, nil)
-        },
-            nil
-        )
-        if aggregations[aggregate] == nil { self.aggregations[aggregate] = [:] }
-        aggregations[aggregate]?[argc] = box
+        functions[functionName]?[argc] = value
     }
 
-    fileprivate typealias Aggregate = @convention(block) (Int, OpaquePointer?, Int32, UnsafeMutablePointer<OpaquePointer?>?) -> Void
     fileprivate typealias Function = @convention(block) (OpaquePointer?, Int32, UnsafeMutablePointer<OpaquePointer?>?) -> Void
-    fileprivate var functions = [String: [Int: Function]]()
-    fileprivate var aggregations = [String: [Int: Aggregate]]()
+    fileprivate var functions = [String: [Int: Any]]()
 
     /// Defines a new collating sequence.
     ///
@@ -808,7 +667,6 @@ public final class Connection {
     ///     Default: `.main`.
     ///
     /// - Returns: A new database backup.
-
     public func backup(databaseName: Backup.DatabaseName = .main,
                        usingConnection targetConnection: Connection,
                        andDatabaseName targetDatabaseName: Backup.DatabaseName = .main) throws -> Backup {
@@ -864,3 +722,40 @@ extension Connection.Location: CustomStringConvertible {
     }
 
 }
+
+func getArguments(argc: Int32, argv: UnsafeMutablePointer<OpaquePointer?>?) -> [Binding?] {
+    (0..<Int(argc)).map { idx in
+        let value = argv![idx]
+        switch sqlite3_value_type(value) {
+        case SQLITE_BLOB:
+            return Blob(bytes: sqlite3_value_blob(value), length: Int(sqlite3_value_bytes(value)))
+        case SQLITE_FLOAT:
+            return sqlite3_value_double(value)
+        case SQLITE_INTEGER:
+            return sqlite3_value_int64(value)
+        case SQLITE_NULL:
+            return nil
+        case SQLITE_TEXT:
+            return String(cString: UnsafePointer(sqlite3_value_text(value)))
+        case let type:
+            fatalError("unsupported value type: \(type)")
+        }
+    }
+}
+
+func set(result: Binding?, on context: OpaquePointer?) {
+    switch result {
+    case let blob as Blob:
+        sqlite3_result_blob(context, blob.bytes, Int32(blob.bytes.count), nil)
+    case let double as Double:
+        sqlite3_result_double(context, double)
+    case let int as Int64:
+        sqlite3_result_int64(context, int)
+    case let string as String:
+        sqlite3_result_text(context, string, Int32(string.lengthOfBytes(using: .utf8)), SQLITE_TRANSIENT)
+    case .none:
+        sqlite3_result_null(context)
+    default:
+        fatalError("unsupported result type: \(String(describing: result))")
+    }
+}
diff --git a/Sources/SQLite/Typed/CustomFunctions.swift b/Sources/SQLite/Typed/CustomFunctions.swift
index c38113d1..0eaa8cf3 100644
--- a/Sources/SQLite/Typed/CustomFunctions.swift
+++ b/Sources/SQLite/Typed/CustomFunctions.swift
@@ -159,69 +159,4 @@ public extension Connection {
         }
     }
 
-    // MARK: -
-
-    func createAggregation<T: AnyObject>(
-        _ aggregate: String,
-        argumentCount: UInt? = nil,
-        deterministic: Bool = false,
-        initialValue: T,
-        reduce: @escaping (T, [Binding?]) -> T,
-        result: @escaping (T) -> Binding?
-        ) {
-
-        let step: ([Binding?], UnsafeMutablePointer<UnsafeMutableRawPointer>) -> Void = { (bindings, ptr) in
-            let pointer = ptr.pointee.assumingMemoryBound(to: T.self)
-            let current = Unmanaged<T>.fromOpaque(pointer).takeRetainedValue()
-            let next = reduce(current, bindings)
-            ptr.pointee = Unmanaged.passRetained(next).toOpaque()
-        }
-
-        let final: (UnsafeMutablePointer<UnsafeMutableRawPointer>) -> Binding? = { (ptr) in
-            let pointer = ptr.pointee.assumingMemoryBound(to: T.self)
-            let obj = Unmanaged<T>.fromOpaque(pointer).takeRetainedValue()
-            let value = result(obj)
-            ptr.deallocate()
-            return value
-        }
-
-        let state: () -> UnsafeMutablePointer<UnsafeMutableRawPointer> = {
-            let pointer = UnsafeMutablePointer<UnsafeMutableRawPointer>.allocate(capacity: 1)
-            pointer.pointee = Unmanaged.passRetained(initialValue).toOpaque()
-            return pointer
-        }
-
-        createAggregation(aggregate, step: step, final: final, state: state)
-    }
-
-    func createAggregation<T>(
-        _ aggregate: String,
-        argumentCount: UInt? = nil,
-        deterministic: Bool = false,
-        initialValue: T,
-        reduce: @escaping (T, [Binding?]) -> T,
-        result: @escaping (T) -> Binding?
-        ) {
-
-        let step: ([Binding?], UnsafeMutablePointer<T>) -> Void = { (bindings, pointer) in
-            let current = pointer.pointee
-            let next = reduce(current, bindings)
-            pointer.pointee = next
-        }
-
-        let final: (UnsafeMutablePointer<T>) -> Binding? = { pointer in
-            let value = result(pointer.pointee)
-            pointer.deallocate()
-            return value
-        }
-
-        let state: () -> UnsafeMutablePointer<T> = {
-            let pointer = UnsafeMutablePointer<T>.allocate(capacity: 1)
-            pointer.initialize(to: initialValue)
-            return pointer
-        }
-
-        createAggregation(aggregate, step: step, final: final, state: state)
-    }
-
 }
diff --git a/Sources/SQLite/Typed/Query.swift b/Sources/SQLite/Typed/Query.swift
index 59b24a8d..2d88db63 100644
--- a/Sources/SQLite/Typed/Query.swift
+++ b/Sources/SQLite/Typed/Query.swift
@@ -21,7 +21,6 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 //
-// swiftlint:disable file_length
 import Foundation
 
 public protocol QueryType: Expressible {

From e5a10f5bcad9f9c25f948a9c90c5861e1bb83163 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Wed, 25 Aug 2021 22:23:36 +0200
Subject: [PATCH 078/326] Use typealiases

---
 .../SQLite/Core/Connection+Aggregation.swift  | 12 +--
 Sources/SQLite/Core/Connection.swift          | 74 ++++++++++---------
 2 files changed, 46 insertions(+), 40 deletions(-)

diff --git a/Sources/SQLite/Core/Connection+Aggregation.swift b/Sources/SQLite/Core/Connection+Aggregation.swift
index 257d48ed..106f292c 100644
--- a/Sources/SQLite/Core/Connection+Aggregation.swift
+++ b/Sources/SQLite/Core/Connection+Aggregation.swift
@@ -10,7 +10,7 @@ import SQLite3
 #endif
 
 extension Connection {
-    private typealias Aggregate = @convention(block) (Int, OpaquePointer?, Int32, UnsafeMutablePointer<OpaquePointer?>?) -> Void
+    private typealias Aggregate = @convention(block) (Int, Context, Int32, Argv) -> Void
 
     /// Creates or redefines a custom SQL aggregate.
     ///
@@ -49,29 +49,29 @@ extension Connection {
             state: @escaping () -> UnsafeMutablePointer<T>) {
 
         let argc = argumentCount.map { Int($0) } ?? -1
-        let box: Aggregate = { (stepFlag: Int, context: OpaquePointer?, argc: Int32, argv: UnsafeMutablePointer<OpaquePointer?>?) in
+        let box: Aggregate = { (stepFlag: Int, context: Context, argc: Int32, argv: Argv) in
             let nBytes = Int32(MemoryLayout<UnsafeMutablePointer<Int64>>.size)
             guard let aggregateContext = sqlite3_aggregate_context(context, nBytes) else {
                 fatalError("Could not get aggregate context")
             }
             let mutablePointer = aggregateContext.assumingMemoryBound(to: UnsafeMutableRawPointer.self)
             if stepFlag > 0 {
-                let arguments = getArguments(argc: argc, argv: argv)
+                let arguments = argv.getBindings(argc: argc)
                 if aggregateContext.assumingMemoryBound(to: Int64.self).pointee == 0 {
                     mutablePointer.pointee = UnsafeMutableRawPointer(mutating: state())
                 }
                 step(arguments, mutablePointer.pointee.assumingMemoryBound(to: T.self))
             } else {
                 let result = final(mutablePointer.pointee.assumingMemoryBound(to: T.self))
-                set(result: result, on: context)
+                context.set(result: result)
             }
         }
 
-        func xStep(context: OpaquePointer?, argc: Int32, value: UnsafeMutablePointer<OpaquePointer?>?) {
+        func xStep(context: Context, argc: Int32, value: Argv) {
             unsafeBitCast(sqlite3_user_data(context), to: Aggregate.self)(1, context, argc, value)
         }
 
-        func xFinal(context: OpaquePointer?) {
+        func xFinal(context: Context) {
             unsafeBitCast(sqlite3_user_data(context), to: Aggregate.self)(0, context, 0, nil)
         }
 
diff --git a/Sources/SQLite/Core/Connection.swift b/Sources/SQLite/Core/Connection.swift
index 35996b29..6b5481e3 100644
--- a/Sources/SQLite/Core/Connection.swift
+++ b/Sources/SQLite/Core/Connection.swift
@@ -590,10 +590,10 @@ public final class Connection {
                                deterministic: Bool = false,
                                _ block: @escaping (_ args: [Binding?]) -> Binding?) {
         let argc = argumentCount.map { Int($0) } ?? -1
-        let box: Function = { context, argc, argv in
-            set(result: block(getArguments(argc: argc, argv: argv)), on: context)
+        let box: Function = { (context: Context, argc, argv: Argv) in
+            context.set(result: block(argv.getBindings(argc: argc)))
         }
-        func xFunc(context: OpaquePointer?, argc: Int32, value: UnsafeMutablePointer<OpaquePointer?>?) {
+        func xFunc(context: Context, argc: Int32, value: Argv) {
             unsafeBitCast(sqlite3_user_data(context), to: Function.self)(context, argc, value)
         }
         let flags = SQLITE_UTF8 | (deterministic ? SQLITE_DETERMINISTIC : 0)
@@ -619,7 +619,7 @@ public final class Connection {
         functions[functionName]?[argc] = value
     }
 
-    fileprivate typealias Function = @convention(block) (OpaquePointer?, Int32, UnsafeMutablePointer<OpaquePointer?>?) -> Void
+    fileprivate typealias Function = @convention(block) (Context, Int32, Argv) -> Void
     fileprivate var functions = [String: [Int: Any]]()
 
     /// Defines a new collating sequence.
@@ -723,39 +723,45 @@ extension Connection.Location: CustomStringConvertible {
 
 }
 
-func getArguments(argc: Int32, argv: UnsafeMutablePointer<OpaquePointer?>?) -> [Binding?] {
-    (0..<Int(argc)).map { idx in
-        let value = argv![idx]
-        switch sqlite3_value_type(value) {
-        case SQLITE_BLOB:
-            return Blob(bytes: sqlite3_value_blob(value), length: Int(sqlite3_value_bytes(value)))
-        case SQLITE_FLOAT:
-            return sqlite3_value_double(value)
-        case SQLITE_INTEGER:
-            return sqlite3_value_int64(value)
-        case SQLITE_NULL:
-            return nil
-        case SQLITE_TEXT:
-            return String(cString: UnsafePointer(sqlite3_value_text(value)))
-        case let type:
-            fatalError("unsupported value type: \(type)")
+typealias Context = OpaquePointer?
+extension Context {
+    func set(result: Binding?) {
+        switch result {
+        case let blob as Blob:
+            sqlite3_result_blob(self, blob.bytes, Int32(blob.bytes.count), nil)
+        case let double as Double:
+            sqlite3_result_double(self, double)
+        case let int as Int64:
+            sqlite3_result_int64(self, int)
+        case let string as String:
+            sqlite3_result_text(self, string, Int32(string.lengthOfBytes(using: .utf8)), SQLITE_TRANSIENT)
+        case .none:
+            sqlite3_result_null(self)
+        default:
+            fatalError("unsupported result type: \(String(describing: result))")
         }
     }
 }
 
-func set(result: Binding?, on context: OpaquePointer?) {
-    switch result {
-    case let blob as Blob:
-        sqlite3_result_blob(context, blob.bytes, Int32(blob.bytes.count), nil)
-    case let double as Double:
-        sqlite3_result_double(context, double)
-    case let int as Int64:
-        sqlite3_result_int64(context, int)
-    case let string as String:
-        sqlite3_result_text(context, string, Int32(string.lengthOfBytes(using: .utf8)), SQLITE_TRANSIENT)
-    case .none:
-        sqlite3_result_null(context)
-    default:
-        fatalError("unsupported result type: \(String(describing: result))")
+typealias Argv = UnsafeMutablePointer<OpaquePointer?>?
+extension Argv {
+    func getBindings(argc: Int32) -> [Binding?] {
+        (0..<Int(argc)).map { idx in
+            let value = self![idx]
+            switch sqlite3_value_type(value) {
+            case SQLITE_BLOB:
+                return Blob(bytes: sqlite3_value_blob(value), length: Int(sqlite3_value_bytes(value)))
+            case SQLITE_FLOAT:
+                return sqlite3_value_double(value)
+            case SQLITE_INTEGER:
+                return sqlite3_value_int64(value)
+            case SQLITE_NULL:
+                return nil
+            case SQLITE_TEXT:
+                return String(cString: UnsafePointer(sqlite3_value_text(value)))
+            case let type:
+                fatalError("unsupported value type: \(type)")
+            }
+        }
     }
 }

From 7d12428fd912be63c072bea5b1670dd2d54d4089 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Mon, 23 Aug 2021 00:14:23 +0200
Subject: [PATCH 079/326] Add trigram tokenizer

---
 Sources/SQLite/Extensions/FTS4.swift |  9 +++++++--
 Tests/SQLiteTests/FTS5Tests.swift    | 10 ++++++++++
 2 files changed, 17 insertions(+), 2 deletions(-)

diff --git a/Sources/SQLite/Extensions/FTS4.swift b/Sources/SQLite/Extensions/FTS4.swift
index 115b146a..0e48943a 100644
--- a/Sources/SQLite/Extensions/FTS4.swift
+++ b/Sources/SQLite/Extensions/FTS4.swift
@@ -95,10 +95,10 @@ extension VirtualTable {
 public struct Tokenizer {
 
     public static let Simple = Tokenizer("simple")
-
     public static let Porter = Tokenizer("porter")
 
-    public static func Unicode61(removeDiacritics: Bool? = nil, tokenchars: Set<Character> = [],
+    public static func Unicode61(removeDiacritics: Bool? = nil,
+                                 tokenchars: Set<Character> = [],
                                  separators: Set<Character> = []) -> Tokenizer {
         var arguments = [String]()
 
@@ -119,6 +119,11 @@ public struct Tokenizer {
         return Tokenizer("unicode61", arguments)
     }
 
+    // https://sqlite.org/fts5.html#the_experimental_trigram_tokenizer
+    public static func Trigram(caseSensitive: Bool = false) -> Tokenizer {
+        return Tokenizer("trigram", ["case_sensitive", caseSensitive ? "1" : "0"])
+    }
+
     public static func Custom(_ name: String) -> Tokenizer {
         Tokenizer(Tokenizer.moduleName.quote(), [name.quote()])
     }
diff --git a/Tests/SQLiteTests/FTS5Tests.swift b/Tests/SQLiteTests/FTS5Tests.swift
index 4cce4523..c271be9d 100644
--- a/Tests/SQLiteTests/FTS5Tests.swift
+++ b/Tests/SQLiteTests/FTS5Tests.swift
@@ -81,6 +81,16 @@ class FTS5Tests: XCTestCase {
             sql(config.tokenizer(.Unicode61(removeDiacritics: true, tokenchars: ["."], separators: ["X"]))))
     }
 
+    func test_tokenizer_trigram() {
+        XCTAssertEqual(
+                "CREATE VIRTUAL TABLE \"virtual_table\" USING fts5(tokenize=trigram case_sensitive 0)",
+                sql(config.tokenizer(.Trigram())))
+
+        XCTAssertEqual(
+                "CREATE VIRTUAL TABLE \"virtual_table\" USING fts5(tokenize=trigram case_sensitive 1)",
+                sql(config.tokenizer(.Trigram(caseSensitive: true))))
+    }
+
     func test_column_size() {
         XCTAssertEqual(
             "CREATE VIRTUAL TABLE \"virtual_table\" USING fts5(columnsize=1)",

From 3dc24496883090888fdbd658a766eb622b815aec Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Thu, 26 Aug 2021 00:19:07 +0200
Subject: [PATCH 080/326] Add integration tests

---
 SQLite.xcodeproj/project.pbxproj              |  8 +++
 Tests/SQLiteTests/CipherTests.swift           | 16 ++---
 Tests/SQLiteTests/ConnectionTests.swift       |  7 +-
 .../SQLiteTests/CustomAggregationTests.swift  | 12 ++--
 Tests/SQLiteTests/FTSIntegrationTests.swift   | 64 +++++++++++++++++++
 Tests/SQLiteTests/QueryIntegrationTests.swift | 11 ++--
 Tests/SQLiteTests/SelectTests.swift           | 12 ++--
 Tests/SQLiteTests/StatementTests.swift        |  6 +-
 Tests/SQLiteTests/TestHelpers.swift           | 10 +--
 9 files changed, 108 insertions(+), 38 deletions(-)
 create mode 100644 Tests/SQLiteTests/FTSIntegrationTests.swift

diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj
index 5bb52f4e..92400180 100644
--- a/SQLite.xcodeproj/project.pbxproj
+++ b/SQLite.xcodeproj/project.pbxproj
@@ -54,6 +54,7 @@
 		19A17152E32A9585831E3FE0 /* DateAndTimeFunctions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17BA55DABB480F9020C8A /* DateAndTimeFunctions.swift */; };
 		19A1717B10CC941ACB5533D6 /* FTS5.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1730E4390C775C25677D1 /* FTS5.swift */; };
 		19A171967CC511C4F6F773C9 /* RowTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A175C1F9CB3BBAB8FCEC7B /* RowTests.swift */; };
+		19A171BE056457F13BFBC4C3 /* FTSIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A171FDE4D67879B14ACBDF /* FTSIntegrationTests.swift */; };
 		19A171E6FA242F72A308C594 /* FTS5Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1721B8984686B9963B45D /* FTS5Tests.swift */; };
 		19A171F12AB8B07F2FD7201A /* Cipher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A178A39ACA9667A62663CC /* Cipher.swift */; };
 		19A1720B67ED13E6150C6A3D /* RowTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A175C1F9CB3BBAB8FCEC7B /* RowTests.swift */; };
@@ -80,8 +81,10 @@
 		19A179B59450FE7C4811AB8A /* Connection+Aggregation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A175A9CB446640AE6F2200 /* Connection+Aggregation.swift */; };
 		19A179CCF9671E345E5A9811 /* Cipher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A178A39ACA9667A62663CC /* Cipher.swift */; };
 		19A179E76EA6207669B60C1B /* Cipher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A178A39ACA9667A62663CC /* Cipher.swift */; };
+		19A17C013B00682B70D53DB8 /* FTSIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A171FDE4D67879B14ACBDF /* FTSIntegrationTests.swift */; };
 		19A17C4B951CB054EE48AB1C /* CipherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17399EA9E61235D5D77BF /* CipherTests.swift */; };
 		19A17C80076860CF7751A056 /* DateAndTimeFunctionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1729B75C33F9A0B9A89C1 /* DateAndTimeFunctionTests.swift */; };
+		19A17C9407AC0EE104E5CC85 /* FTSIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A171FDE4D67879B14ACBDF /* FTSIntegrationTests.swift */; };
 		19A17CB808ACF606726E77A8 /* QueryIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17BA6B4E282C1315A115C /* QueryIntegrationTests.swift */; };
 		19A17DC282E36C4F41AA440B /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1710E73A46D5AC721CDA9 /* Errors.swift */; };
 		19A17DF8D4F13A20F5D2269E /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17E723300E5ED3771DCB5 /* Result.swift */; };
@@ -236,6 +239,7 @@
 		03A65E631C6BB0F60062603F /* SQLiteTests tvOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "SQLiteTests tvOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
 		03A65E961C6BB3210062603F /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS.sdk/usr/lib/libsqlite3.tbd; sourceTree = DEVELOPER_DIR; };
 		19A1710E73A46D5AC721CDA9 /* Errors.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Errors.swift; sourceTree = "<group>"; };
+		19A171FDE4D67879B14ACBDF /* FTSIntegrationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FTSIntegrationTests.swift; sourceTree = "<group>"; };
 		19A1721B8984686B9963B45D /* FTS5Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FTS5Tests.swift; sourceTree = "<group>"; };
 		19A1729B75C33F9A0B9A89C1 /* DateAndTimeFunctionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DateAndTimeFunctionTests.swift; sourceTree = "<group>"; };
 		19A1730E4390C775C25677D1 /* FTS5.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FTS5.swift; sourceTree = "<group>"; };
@@ -449,6 +453,7 @@
 				19A1729B75C33F9A0B9A89C1 /* DateAndTimeFunctionTests.swift */,
 				D4DB368A20C09C9B00D5A58E /* SelectTests.swift */,
 				19A17BA6B4E282C1315A115C /* QueryIntegrationTests.swift */,
+				19A171FDE4D67879B14ACBDF /* FTSIntegrationTests.swift */,
 			);
 			name = SQLiteTests;
 			path = Tests/SQLiteTests;
@@ -887,6 +892,7 @@
 				19A1769C1F3A7542BECF50FF /* DateAndTimeFunctionTests.swift in Sources */,
 				D4DB368E20C09CFD00D5A58E /* SelectTests.swift in Sources */,
 				19A17CB808ACF606726E77A8 /* QueryIntegrationTests.swift in Sources */,
+				19A17C9407AC0EE104E5CC85 /* FTSIntegrationTests.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -983,6 +989,7 @@
 				19A17C80076860CF7751A056 /* DateAndTimeFunctionTests.swift in Sources */,
 				D4DB368C20C09CFB00D5A58E /* SelectTests.swift in Sources */,
 				19A172A9B536D02D4A98AAAD /* QueryIntegrationTests.swift in Sources */,
+				19A17C013B00682B70D53DB8 /* FTSIntegrationTests.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -1047,6 +1054,7 @@
 				19A172EB202970561E5C4245 /* DateAndTimeFunctionTests.swift in Sources */,
 				D4DB368D20C09CFC00D5A58E /* SelectTests.swift in Sources */,
 				19A176406BDE9D9C80CC9FA3 /* QueryIntegrationTests.swift in Sources */,
+				19A171BE056457F13BFBC4C3 /* FTSIntegrationTests.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
diff --git a/Tests/SQLiteTests/CipherTests.swift b/Tests/SQLiteTests/CipherTests.swift
index 77e8e7f0..0995fca0 100644
--- a/Tests/SQLiteTests/CipherTests.swift
+++ b/Tests/SQLiteTests/CipherTests.swift
@@ -8,22 +8,22 @@ class CipherTests: XCTestCase {
     let db1 = try! Connection()
     let db2 = try! Connection()
 
-    override func setUp() {
+    override func setUpWithError() throws {
         // db
 
-        try! db1.key("hello")
+        try db1.key("hello")
 
-        try! db1.run("CREATE TABLE foo (bar TEXT)")
-        try! db1.run("INSERT INTO foo (bar) VALUES ('world')")
+        try db1.run("CREATE TABLE foo (bar TEXT)")
+        try db1.run("INSERT INTO foo (bar) VALUES ('world')")
 
         // db2
         let key2 = keyData()
-        try! db2.key(Blob(bytes: key2.bytes, length: key2.length))
+        try db2.key(Blob(bytes: key2.bytes, length: key2.length))
 
-        try! db2.run("CREATE TABLE foo (bar TEXT)")
-        try! db2.run("INSERT INTO foo (bar) VALUES ('world')")
+        try db2.run("CREATE TABLE foo (bar TEXT)")
+        try db2.run("INSERT INTO foo (bar) VALUES ('world')")
 
-        super.setUp()
+        try super.setUpWithError()
     }
 
     func test_key() {
diff --git a/Tests/SQLiteTests/ConnectionTests.swift b/Tests/SQLiteTests/ConnectionTests.swift
index ae881289..ae9d4dfd 100644
--- a/Tests/SQLiteTests/ConnectionTests.swift
+++ b/Tests/SQLiteTests/ConnectionTests.swift
@@ -15,10 +15,9 @@ import SQLite3
 
 class ConnectionTests: SQLiteTestCase {
 
-    override func setUp() {
-        super.setUp()
-
-        createUsersTable()
+    override func setUpWithError() throws {
+        try super.setUpWithError()
+        try createUsersTable()
     }
 
     func test_init_withInMemory_returnsInMemoryConnection() {
diff --git a/Tests/SQLiteTests/CustomAggregationTests.swift b/Tests/SQLiteTests/CustomAggregationTests.swift
index d5232260..5cbfbbef 100644
--- a/Tests/SQLiteTests/CustomAggregationTests.swift
+++ b/Tests/SQLiteTests/CustomAggregationTests.swift
@@ -17,12 +17,12 @@ import SQLite3
 #if !os(Linux)
 
 class CustomAggregationTests: SQLiteTestCase {
-    override func setUp() {
-        super.setUp()
-        createUsersTable()
-        try! insertUser("Alice", age: 30, admin: true)
-        try! insertUser("Bob", age: 25, admin: true)
-        try! insertUser("Eve", age: 28, admin: false)
+    override func setUpWithError() throws {
+        try super.setUpWithError()
+        try createUsersTable()
+        try insertUser("Alice", age: 30, admin: true)
+        try insertUser("Bob", age: 25, admin: true)
+        try insertUser("Eve", age: 28, admin: false)
     }
 
     func testUnsafeCustomSum() {
diff --git a/Tests/SQLiteTests/FTSIntegrationTests.swift b/Tests/SQLiteTests/FTSIntegrationTests.swift
new file mode 100644
index 00000000..561c756f
--- /dev/null
+++ b/Tests/SQLiteTests/FTSIntegrationTests.swift
@@ -0,0 +1,64 @@
+import XCTest
+#if SQLITE_SWIFT_STANDALONE
+import sqlite3
+#elseif SQLITE_SWIFT_SQLCIPHER
+import SQLCipher
+#elseif os(Linux)
+import CSQLite
+#else
+import SQLite3
+#endif
+@testable import SQLite
+
+class FTSIntegrationTests: SQLiteTestCase {
+    let email = Expression<String>("email")
+    let index = VirtualTable("index")
+
+    private func createIndex() throws {
+        try db.run(index.create(.FTS5(FTS5Config()
+            .column(email)
+            .tokenizer(.Unicode61())
+        )))
+
+        for user in try db.prepare(users) {
+            try db.run(index.insert(email <- user[email]))
+        }
+    }
+
+    private func createTrigramIndex() throws {
+        try XCTSkipUnless(db.satisfiesMinimumVersion(minor: 34))
+        try db.run(index.create(.FTS5(FTS5Config()
+            .column(email)
+            .tokenizer(.Trigram(caseSensitive: false))
+        )))
+
+        for user in try db.prepare(users) {
+            try db.run(index.insert(email <- user[email]))
+        }
+    }
+
+    override func setUpWithError() throws {
+        try super.setUpWithError()
+        try createUsersTable()
+        try insertUsers("John", "Paul", "George", "Ringo")
+    }
+
+    func testMatch() throws {
+        try createIndex()
+        let matches = Array(try db.prepare(index.match("Paul")))
+        XCTAssertEqual(matches.map { $0[email ]}, ["Paul@example.com"])
+    }
+
+    func testMatchPartial() throws {
+        try insertUsers("Paula")
+        try createIndex()
+        let matches = Array(try db.prepare(index.match("Pa*")))
+        XCTAssertEqual(matches.map { $0[email ]}, ["Paul@example.com", "Paula@example.com"])
+    }
+
+    func testTrigramIndex() throws {
+        try createTrigramIndex()
+        let matches = Array(try db.prepare(index.match("Paul")))
+        XCTAssertEqual(1, matches.count)
+    }
+}
diff --git a/Tests/SQLiteTests/QueryIntegrationTests.swift b/Tests/SQLiteTests/QueryIntegrationTests.swift
index c98b1601..4b9c4bbf 100644
--- a/Tests/SQLiteTests/QueryIntegrationTests.swift
+++ b/Tests/SQLiteTests/QueryIntegrationTests.swift
@@ -16,10 +16,9 @@ class QueryIntegrationTests: SQLiteTestCase {
     let email = Expression<String>("email")
     let age = Expression<Int>("age")
 
-    override func setUp() {
-        super.setUp()
-
-        createUsersTable()
+    override func setUpWithError() throws {
+        try super.setUpWithError()
+        try createUsersTable()
     }
 
     // MARK: -
@@ -131,7 +130,7 @@ class QueryIntegrationTests: SQLiteTestCase {
     }
 
     func test_upsert() throws {
-        guard db.satisfiesMinimumVersion(minor: 24) else { return }
+        try XCTSkipUnless(db.satisfiesMinimumVersion(minor: 24))
         let fetchAge = { () throws -> Int? in
             try self.db.pluck(self.users.filter(self.email == "alice@example.com")).flatMap { $0[self.age] }
         }
@@ -210,7 +209,7 @@ class QueryIntegrationTests: SQLiteTestCase {
     }
 }
 
-private extension Connection {
+extension Connection {
     func satisfiesMinimumVersion(minor: Int, patch: Int = 0) -> Bool {
         guard let version = try? scalar("SELECT sqlite_version()") as? String else { return false }
         let components = version.split(separator: ".", maxSplits: 3).compactMap { Int($0) }
diff --git a/Tests/SQLiteTests/SelectTests.swift b/Tests/SQLiteTests/SelectTests.swift
index c66d401d..d1126b66 100644
--- a/Tests/SQLiteTests/SelectTests.swift
+++ b/Tests/SQLiteTests/SelectTests.swift
@@ -3,14 +3,14 @@ import XCTest
 
 class SelectTests: SQLiteTestCase {
 
-    override func setUp() {
-        super.setUp()
-        createUsersTable()
-        createUsersDataTable()
+    override func setUpWithError() throws {
+        try super.setUpWithError()
+        try createUsersTable()
+        try createUsersDataTable()
     }
 
-    func createUsersDataTable() {
-        try! db.execute("""
+    func createUsersDataTable() throws {
+        try db.execute("""
             CREATE TABLE users_name (
                 id INTEGER,
                 user_id INTEGER REFERENCES users(id),
diff --git a/Tests/SQLiteTests/StatementTests.swift b/Tests/SQLiteTests/StatementTests.swift
index 5a05675d..b4ad7c28 100644
--- a/Tests/SQLiteTests/StatementTests.swift
+++ b/Tests/SQLiteTests/StatementTests.swift
@@ -2,9 +2,9 @@ import XCTest
 import SQLite
 
 class StatementTests: SQLiteTestCase {
-    override func setUp() {
-        super.setUp()
-        createUsersTable()
+    override func setUpWithError() throws {
+        try super.setUpWithError()
+        try createUsersTable()
     }
 
     func test_cursor_to_blob() {
diff --git a/Tests/SQLiteTests/TestHelpers.swift b/Tests/SQLiteTests/TestHelpers.swift
index 0aa918f0..07cc6f06 100644
--- a/Tests/SQLiteTests/TestHelpers.swift
+++ b/Tests/SQLiteTests/TestHelpers.swift
@@ -6,9 +6,9 @@ class SQLiteTestCase: XCTestCase {
     var db: Connection!
     let users = Table("users")
 
-    override func setUp() {
-        super.setUp()
-        db = try! Connection()
+    override func setUpWithError() throws {
+        try super.setUpWithError()
+        db = try Connection()
         trace = [String: Int]()
 
         db.trace { SQL in
@@ -17,8 +17,8 @@ class SQLiteTestCase: XCTestCase {
         }
     }
 
-    func createUsersTable() {
-        try! db.execute("""
+    func createUsersTable() throws {
+        try db.execute("""
             CREATE TABLE users (
                 id INTEGER PRIMARY KEY,
                 email TEXT NOT NULL UNIQUE,

From 7aaccafedd157148a5a69e3e4c1a767b6d704ef9 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Thu, 26 Aug 2021 00:21:03 +0200
Subject: [PATCH 081/326] Skip on Linux

---
 Package.swift | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Package.swift b/Package.swift
index 7522fa45..c1373ad8 100644
--- a/Package.swift
+++ b/Package.swift
@@ -46,6 +46,7 @@ package.dependencies = [.package(url: "https://github.com/stephencelis/CSQLite.g
 package.targets = [
     .target(name: "SQLite", exclude: ["Extensions/FTS4.swift", "Extensions/FTS5.swift"]),
     .testTarget(name: "SQLiteTests", dependencies: ["SQLite"], path: "Tests/SQLiteTests", exclude: [
+        "FTSIntegrationTests.swift",
         "FTS4Tests.swift",
         "FTS5Tests.swift"
     ])

From b1031e33bc8c08898858c3d2873e2985f8ff19cf Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Thu, 26 Aug 2021 00:49:30 +0200
Subject: [PATCH 082/326] Skip tests if not supported

---
 Tests/SQLiteTests/FTSIntegrationTests.swift | 30 ++++++++++++++-------
 1 file changed, 21 insertions(+), 9 deletions(-)

diff --git a/Tests/SQLiteTests/FTSIntegrationTests.swift b/Tests/SQLiteTests/FTSIntegrationTests.swift
index 561c756f..d629ca8b 100644
--- a/Tests/SQLiteTests/FTSIntegrationTests.swift
+++ b/Tests/SQLiteTests/FTSIntegrationTests.swift
@@ -15,10 +15,16 @@ class FTSIntegrationTests: SQLiteTestCase {
     let index = VirtualTable("index")
 
     private func createIndex() throws {
-        try db.run(index.create(.FTS5(FTS5Config()
-            .column(email)
-            .tokenizer(.Unicode61())
-        )))
+        do {
+            try db.run(index.create(.FTS5(
+                FTS5Config()
+                    .column(email)
+                    .tokenizer(.Unicode61()))
+            ))
+        } catch let error as Result {
+            try XCTSkipIf(error.description.starts(with: "no such model"))
+            throw error
+        }
 
         for user in try db.prepare(users) {
             try db.run(index.insert(email <- user[email]))
@@ -26,11 +32,17 @@ class FTSIntegrationTests: SQLiteTestCase {
     }
 
     private func createTrigramIndex() throws {
-        try XCTSkipUnless(db.satisfiesMinimumVersion(minor: 34))
-        try db.run(index.create(.FTS5(FTS5Config()
-            .column(email)
-            .tokenizer(.Trigram(caseSensitive: false))
-        )))
+        do {
+            try db.run(index.create(.FTS5(
+                FTS5Config()
+                  .column(email)
+                  .tokenizer(.Trigram(caseSensitive: false)))
+            ))
+        } catch let error as Result {
+            try XCTSkipIf(error.description.starts(with: "no such model") ||
+                          error.description.starts(with: "parse error in"))
+            throw error
+        }
 
         for user in try db.prepare(users) {
             try db.run(index.insert(email <- user[email]))

From 7f98a7b34b362a8776a429fcbc2359ae11b78aee Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Thu, 26 Aug 2021 01:12:21 +0200
Subject: [PATCH 083/326] Move into method

---
 Tests/SQLiteTests/FTSIntegrationTests.swift | 22 ++++++++++++---------
 1 file changed, 13 insertions(+), 9 deletions(-)

diff --git a/Tests/SQLiteTests/FTSIntegrationTests.swift b/Tests/SQLiteTests/FTSIntegrationTests.swift
index d629ca8b..da9ba8dc 100644
--- a/Tests/SQLiteTests/FTSIntegrationTests.swift
+++ b/Tests/SQLiteTests/FTSIntegrationTests.swift
@@ -15,15 +15,12 @@ class FTSIntegrationTests: SQLiteTestCase {
     let index = VirtualTable("index")
 
     private func createIndex() throws {
-        do {
+        try createOrSkip { db in
             try db.run(index.create(.FTS5(
                 FTS5Config()
                     .column(email)
                     .tokenizer(.Unicode61()))
             ))
-        } catch let error as Result {
-            try XCTSkipIf(error.description.starts(with: "no such model"))
-            throw error
         }
 
         for user in try db.prepare(users) {
@@ -32,16 +29,12 @@ class FTSIntegrationTests: SQLiteTestCase {
     }
 
     private func createTrigramIndex() throws {
-        do {
+        try createOrSkip { db in
             try db.run(index.create(.FTS5(
                 FTS5Config()
                   .column(email)
                   .tokenizer(.Trigram(caseSensitive: false)))
             ))
-        } catch let error as Result {
-            try XCTSkipIf(error.description.starts(with: "no such model") ||
-                          error.description.starts(with: "parse error in"))
-            throw error
         }
 
         for user in try db.prepare(users) {
@@ -73,4 +66,15 @@ class FTSIntegrationTests: SQLiteTestCase {
         let matches = Array(try db.prepare(index.match("Paul")))
         XCTAssertEqual(1, matches.count)
     }
+
+    private func createOrSkip(_ createIndex: (Connection) throws -> Void) throws {
+        do {
+            try createIndex(db)
+        } catch let error as Result {
+            try XCTSkipIf(error.description.starts(with: "no such module:") ||
+                          error.description.starts(with: "parse error")
+            )
+            throw error
+        }
+    }
 }

From 7e6d8b98d47c3cf7cfcb60d63fff17868eefd8c0 Mon Sep 17 00:00:00 2001
From: Nathan Fallet <contact@nathanfallet.me>
Date: Thu, 26 Aug 2021 14:24:52 +0200
Subject: [PATCH 084/326] Reverting a change in `FailableIterator`

---
 Sources/SQLite/Core/Statement.swift | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Sources/SQLite/Core/Statement.swift b/Sources/SQLite/Core/Statement.swift
index 48347c31..abeee24b 100644
--- a/Sources/SQLite/Core/Statement.swift
+++ b/Sources/SQLite/Core/Statement.swift
@@ -207,7 +207,7 @@ public protocol FailableIterator: IteratorProtocol {
 
 extension FailableIterator {
     public func next() -> Element? {
-        try? failableNext()
+        try! failableNext()
     }
 }
 

From 62fe865ae8b80bab15f373dfeefae04df3b555ea Mon Sep 17 00:00:00 2001
From: Nathan Fallet <contact@nathanfallet.me>
Date: Thu, 26 Aug 2021 14:33:12 +0200
Subject: [PATCH 085/326] Fix linting

---
 Sources/SQLite/Core/Statement.swift | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Sources/SQLite/Core/Statement.swift b/Sources/SQLite/Core/Statement.swift
index abeee24b..29d6938d 100644
--- a/Sources/SQLite/Core/Statement.swift
+++ b/Sources/SQLite/Core/Statement.swift
@@ -207,6 +207,7 @@ public protocol FailableIterator: IteratorProtocol {
 
 extension FailableIterator {
     public func next() -> Element? {
+        // swiftlint:disable:next force_cast
         try! failableNext()
     }
 }

From 870cf7b2cebe315a5e05176ca05a111cd8ab39f6 Mon Sep 17 00:00:00 2001
From: Nathan Fallet <contact@nathanfallet.me>
Date: Thu, 26 Aug 2021 15:04:55 +0200
Subject: [PATCH 086/326] Fix linting

---
 Sources/SQLite/Core/Statement.swift | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Sources/SQLite/Core/Statement.swift b/Sources/SQLite/Core/Statement.swift
index 29d6938d..1e2489b5 100644
--- a/Sources/SQLite/Core/Statement.swift
+++ b/Sources/SQLite/Core/Statement.swift
@@ -207,7 +207,7 @@ public protocol FailableIterator: IteratorProtocol {
 
 extension FailableIterator {
     public func next() -> Element? {
-        // swiftlint:disable:next force_cast
+        // swiftlint:disable:next force_try
         try! failableNext()
     }
 }

From 86c3f6a580fcc335694f12c90515d15671a35636 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Sun, 5 Sep 2021 17:23:39 +0200
Subject: [PATCH 087/326] Add SPM integration test

---
 .github/workflows/build.yml       |   4 ++++
 Package.swift                     |   6 +++++-
 Tests/SPM/.gitignore              |   7 +++++++
 Tests/SPM/Package.swift           |  19 +++++++++++++++++++
 Tests/SPM/Sources/test/main.swift |  11 +++++++++++
 Tests/SPM/db.sqlite               | Bin 0 -> 8192 bytes
 run-tests.sh                      |   2 ++
 7 files changed, 48 insertions(+), 1 deletion(-)
 create mode 100644 Tests/SPM/.gitignore
 create mode 100644 Tests/SPM/Package.swift
 create mode 100644 Tests/SPM/Sources/test/main.swift
 create mode 100644 Tests/SPM/db.sqlite

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 51654fba..60d487bd 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -21,6 +21,10 @@ jobs:
       env:
         PACKAGE_MANAGER_COMMAND: test -Xswiftc -warnings-as-errors
       run: ./run-tests.sh
+    - name: "Run tests (SPM integration test)"
+      env:
+        SPM: run
+      run: ./run-tests.sh
     - name: "Run tests (BUILD_SCHEME: SQLite iOS)"
       env:
         BUILD_SCHEME: SQLite iOS
diff --git a/Package.swift b/Package.swift
index c1373ad8..f4938678 100644
--- a/Package.swift
+++ b/Package.swift
@@ -44,7 +44,11 @@ let package = Package(
 #if os(Linux)
 package.dependencies = [.package(url: "https://github.com/stephencelis/CSQLite.git", from: "0.0.3")]
 package.targets = [
-    .target(name: "SQLite", exclude: ["Extensions/FTS4.swift", "Extensions/FTS5.swift"]),
+    .target(
+        name: "SQLite",
+        dependencies: [.product(name: "CSQLite", package: "CSQLite")],
+        exclude: ["Extensions/FTS4.swift", "Extensions/FTS5.swift"]
+    ),
     .testTarget(name: "SQLiteTests", dependencies: ["SQLite"], path: "Tests/SQLiteTests", exclude: [
         "FTSIntegrationTests.swift",
         "FTS4Tests.swift",
diff --git a/Tests/SPM/.gitignore b/Tests/SPM/.gitignore
new file mode 100644
index 00000000..bb460e7b
--- /dev/null
+++ b/Tests/SPM/.gitignore
@@ -0,0 +1,7 @@
+.DS_Store
+/.build
+/Packages
+/*.xcodeproj
+xcuserdata/
+DerivedData/
+.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
diff --git a/Tests/SPM/Package.swift b/Tests/SPM/Package.swift
new file mode 100644
index 00000000..810026ce
--- /dev/null
+++ b/Tests/SPM/Package.swift
@@ -0,0 +1,19 @@
+// swift-tools-version:5.3
+// The swift-tools-version declares the minimum version of Swift required to build this package.
+
+import PackageDescription
+
+let package = Package(
+    name: "test",
+    dependencies: [
+        .package(path: "../..")
+    ],
+    targets: [
+        // Targets are the basic building blocks of a package. A target can define a module or a test suite.
+        // Targets can depend on other targets in this package, and on products in packages this package depends on.
+        .target(
+            name: "test",
+            dependencies: [.product(name: "SQLite", package: "SQLite.swift")]
+	)
+    ]
+)
diff --git a/Tests/SPM/Sources/test/main.swift b/Tests/SPM/Sources/test/main.swift
new file mode 100644
index 00000000..94f12af8
--- /dev/null
+++ b/Tests/SPM/Sources/test/main.swift
@@ -0,0 +1,11 @@
+import SQLite 
+
+let table = Table("test")
+let name = Expression<String>("name")
+
+let db = try Connection("db.sqlite", readonly: true)
+
+for row in try db.prepare(table) {
+    print(row[name])
+}
+
diff --git a/Tests/SPM/db.sqlite b/Tests/SPM/db.sqlite
new file mode 100644
index 0000000000000000000000000000000000000000..f7b2e84c051df3ffdddb463bdd0dd990358789cb
GIT binary patch
diff --git a/run-tests.sh b/run-tests.sh
index 32465388..5f397553 100755
--- a/run-tests.sh
+++ b/run-tests.sh
@@ -14,6 +14,8 @@ elif [ -n "$VALIDATOR_SUBSPEC" ]; then
     fi
 elif [ -n "$CARTHAGE_PLATFORM" ]; then
     cd Tests/Carthage && make test CARTHAGE_PLATFORM="$CARTHAGE_PLATFORM"
+elif [ -n "$SPM" ]; then
+    cd Tests/SPM && swift run
 elif [ -n "${PACKAGE_MANAGER_COMMAND}" ]; then
     swift ${PACKAGE_MANAGER_COMMAND}
 fi

From 79eee35ca996dd0653bc06a823f4394c61b0a3d8 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Sun, 5 Sep 2021 17:31:38 +0200
Subject: [PATCH 088/326] Lint fix

---
 .github/workflows/build.yml       | 4 ++++
 Tests/SPM/Sources/test/main.swift | 3 +--
 run-tests.sh                      | 2 +-
 3 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 60d487bd..88c5803f 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -75,3 +75,7 @@ jobs:
         sudo apt-get install -y libsqlite3-dev
     - name: Test
       run: swift test
+    - name: "Run tests (SPM integration test)"
+      env:
+        SPM: run
+      run: ./run-tests.sh
diff --git a/Tests/SPM/Sources/test/main.swift b/Tests/SPM/Sources/test/main.swift
index 94f12af8..939b3391 100644
--- a/Tests/SPM/Sources/test/main.swift
+++ b/Tests/SPM/Sources/test/main.swift
@@ -1,4 +1,4 @@
-import SQLite 
+import SQLite
 
 let table = Table("test")
 let name = Expression<String>("name")
@@ -8,4 +8,3 @@ let db = try Connection("db.sqlite", readonly: true)
 for row in try db.prepare(table) {
     print(row[name])
 }
-
diff --git a/run-tests.sh b/run-tests.sh
index 5f397553..49330c12 100755
--- a/run-tests.sh
+++ b/run-tests.sh
@@ -15,7 +15,7 @@ elif [ -n "$VALIDATOR_SUBSPEC" ]; then
 elif [ -n "$CARTHAGE_PLATFORM" ]; then
     cd Tests/Carthage && make test CARTHAGE_PLATFORM="$CARTHAGE_PLATFORM"
 elif [ -n "$SPM" ]; then
-    cd Tests/SPM && swift run
+    cd Tests/SPM && swift ${SPM}
 elif [ -n "${PACKAGE_MANAGER_COMMAND}" ]; then
     swift ${PACKAGE_MANAGER_COMMAND}
 fi

From 09f730eaa8848ff3387bccaddbf573751694f876 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Mon, 6 Sep 2021 00:43:50 +0200
Subject: [PATCH 089/326] Update documentation

- Closes #1076
- Clarify BusyHandler usage (Closes #786)
- Integrate playground into project
---
 CHANGELOG.md                                  |  4 +
 Documentation/Index.md                        | 90 ++++++++++++++++---
 Documentation/Planning.md                     |  2 +-
 Documentation/Release.md                      |  1 +
 README.md                                     |  9 +-
 SQLite.playground/Contents.swift              | 78 +++++++++++++---
 SQLite.playground/contents.xcplayground       |  2 +-
 .../contents.xcworkspacedata                  |  7 ++
 SQLite.xcodeproj/project.pbxproj              |  2 +
 .../SQLite/Core/Connection+Aggregation.swift  |  4 +-
 Tests/SPM/Package.swift                       |  5 +-
 11 files changed, 167 insertions(+), 37 deletions(-)
 create mode 100644 SQLite.playground/playground.xcworkspace/contents.xcworkspacedata

diff --git a/CHANGELOG.md b/CHANGELOG.md
index fc09fa40..c9d09c12 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,8 @@
 
 * Support for database backup ([#919][])
 * Support for custom SQL aggregates ([#881][])
+* Restore previous iteration behavior ([#1075][])
+* Fix compilation on Linux ([#1077][])
 
 0.13.0 (22-08-2021), [diff][diff-0.13.0]
 ========================================
@@ -124,3 +126,5 @@
 [#866]: https://github.com/stephencelis/SQLite.swift/pull/866
 [#881]: https://github.com/stephencelis/SQLite.swift/pull/881
 [#919]: https://github.com/stephencelis/SQLite.swift/pull/919
+[#1075]: https://github.com/stephencelis/SQLite.swift/pull/1075
+[#1077]: https://github.com/stephencelis/SQLite.swift/issues/1077
diff --git a/Documentation/Index.md b/Documentation/Index.md
index fce068a2..1e08f6c4 100644
--- a/Documentation/Index.md
+++ b/Documentation/Index.md
@@ -77,9 +77,6 @@ The [Swift Package Manager][] is a tool for managing the distribution of
 Swift code. It’s integrated with the Swift build system to automate the
 process of downloading, compiling, and linking dependencies.
 
-It is the recommended approach for using SQLite.swift in OSX CLI
-applications.
-
  1. Add the following to your `Package.swift` file:
 
   ```swift
@@ -342,16 +339,15 @@ execution and can be safely accessed across threads. Threads that open
 transactions and savepoints will block other threads from executing
 statements while the transaction is open.
 
-If you maintain multiple connections for a single database, consider setting a timeout (in seconds) and/or a busy handler:
+If you maintain multiple connections for a single database, consider setting a timeout
+(in seconds) *or* a busy handler. There can only be one active at a time, so setting a busy
+handler will effectively override `busyTimeout`.
 
 ```swift
-db.busyTimeout = 5
+db.busyTimeout = 5 // error after 5 seconds (does multiple retries)
 
 db.busyHandler({ tries in
-    if tries >= 3 {
-        return false
-    }
-    return true
+    tries < 3  // error after 3 tries
 })
 ```
 
@@ -656,12 +652,13 @@ do {
 }
 ```
 
-Multiple rows can be inserted at once by similarily calling `insertMany` with an array of per-row [setters](#setters).
+Multiple rows can be inserted at once by similarily calling `insertMany` with an array of
+per-row [setters](#setters).
 
 ```swift
 do {
-    let rowid = try db.run(users.insertMany([mail <- "alice@mac.com"], [email <- "geoff@mac.com"]))
-    print("inserted id: \(rowid)")
+    let lastRowid = try db.run(users.insertMany([mail <- "alice@mac.com"], [email <- "geoff@mac.com"]))
+    print("last inserted id: \(lastRowid)")
 } catch {
     print("insertion failed: \(error)")
 }
@@ -799,6 +796,33 @@ for user in try db.prepare(users) {
 }
 ```
 
+Note that the iterator can throw *undeclared* database errors at any point during
+iteration:
+
+```swift
+let query = try db.prepare(users)
+for user in query {
+    // 💥 can throw an error here
+}
+````
+
+#### Failable iteration
+
+It is therefore recommended using the `RowIterator` API instead,
+which has explicit error handling:
+
+```swift
+let rowIterator = try db.prepareRowIterator(users)
+for user in try Array(rowIterator) {
+    print("id: \(user[id]), email: \(user[email])")
+}
+
+/// or using `map()`
+let mapRowIterator = try db.prepareRowIterator(users)
+let userIds = try mapRowIterator.map { $0[id] }
+
+```
+
 ### Plucking Rows
 
 We can pluck the first row by passing a query to the `pluck` function on a
@@ -1285,7 +1309,6 @@ try db.run(users.addColumn(suffix))
 // ALTER TABLE "users" ADD COLUMN "suffix" TEXT
 ```
 
-
 #### Added Column Constraints
 
 The `addColumn` function shares several of the same [`column` function
@@ -1337,6 +1360,13 @@ tables](#creating-a-table).
     // ALTER TABLE "posts" ADD COLUMN "user_id" INTEGER REFERENCES "users" ("id")
     ```
 
+### Renaming Columns
+
+Added in SQLite 3.25.0, not exposed yet. [#1073](https://github.com/stephencelis/SQLite.swift/issues/1073)
+
+### Dropping Columns
+
+Added in SQLite 3.35.0, not exposed yet. [#1073](https://github.com/stephencelis/SQLite.swift/issues/1073)
 
 ### Indexes
 
@@ -1762,6 +1792,19 @@ for row in stmt.bind(kUTTypeImage) { /* ... */ }
 
 [UTTypeConformsTo]: https://developer.apple.com/documentation/coreservices/1444079-uttypeconformsto
 
+## Custom Aggregations
+
+We can create custom aggregation functions by calling `createAggregation`:
+
+```swift
+let reduce: (String, [Binding?]) -> String = { (last, bindings) in
+    last + " " + (bindings.first as? String ?? "")
+}
+
+db.createAggregation("customConcat", initialValue: "", reduce: reduce, result: { $0 })
+let result = db.prepare("SELECT customConcat(email) FROM users").scalar() as! String
+```
+
 ## Custom Collations
 
 We can create custom collating sequences by calling `createCollation` on a
@@ -1944,6 +1987,19 @@ using the following functions.
     let count = try stmt.scalar() as! Int64
     ```
 
+## Online Database Backup
+
+To copy a database to another using the
+[SQLite Online Backup API](https://sqlite.org/backup.html):
+
+```swift
+// creates an in-memory copy of db.sqlite
+let db = try Connection("db.sqlite")
+let target = try Connection(.inMemory)
+
+let backup = try db.backup(usingConnection: target)
+try backup.step()
+```
 
 ## Logging
 
@@ -1955,6 +2011,14 @@ We can log SQL using the database’s `trace` function.
 #endif
 ```
 
+## Vacuum
+
+To run the [vacuum](https://www.sqlite.org/lang_vacuum.html) command:
+
+```swift
+try db.vacuum()
+```
+
 
 [ROWID]: https://sqlite.org/lang_createtable.html#rowid
 [SQLiteMigrationManager.swift]: https://github.com/garriguv/SQLiteMigrationManager.swift
diff --git a/Documentation/Planning.md b/Documentation/Planning.md
index 62df1f24..44e02c7a 100644
--- a/Documentation/Planning.md
+++ b/Documentation/Planning.md
@@ -6,7 +6,7 @@ additions and Pull Requests, as well as to keep the Issues list clear of
 enhancement requests so that bugs are more visible.
 
 > ⚠ This document is currently not actively maintained. See
-> the [0.13.0 milestone](https://github.com/stephencelis/SQLite.swift/issues?q=is%3Aopen+is%3Aissue+milestone%3A0.13.0)
+> the [0.13.1 milestone](https://github.com/stephencelis/SQLite.swift/issues?q=is%3Aopen+is%3Aissue+milestone%3A0.13.1)
 > on Github for additional information about planned features for the next release.
 
 ## Roadmap
diff --git a/Documentation/Release.md b/Documentation/Release.md
index 0177fc8f..87d715f6 100644
--- a/Documentation/Release.md
+++ b/Documentation/Release.md
@@ -1,6 +1,7 @@
 # SQLite.swift Release checklist
 
 * [ ] Make sure current master branch has a green build
+* [ ] Make sure `SQLite.playground` runs without errors
 * [ ] Make sure `CHANGELOG.md` is up-to-date
 * [ ] Update the version number in `SQLite.swift.podspec`
 * [ ] Run `pod lib lint` locally
diff --git a/README.md b/README.md
index 9242b200..ec92729f 100644
--- a/README.md
+++ b/README.md
@@ -146,6 +146,8 @@ Swift code.
   $ swift build
   ```
 
+See the [Tests/SPM](https://github.com/stephencelis/SQLite.swift/tree/master/Tests/SPM) folder for a small demo project which uses SPM.
+
 [Swift Package Manager]: https://swift.org/package-manager
 
 ### Carthage
@@ -175,8 +177,7 @@ install SQLite.swift with Carthage:
 [CocoaPods][] is a dependency manager for Cocoa projects. To install
 SQLite.swift with CocoaPods:
 
- 1. Make sure CocoaPods is [installed][CocoaPods Installation]. (SQLite.swift
-    requires version 1.6.1 or greater.)
+ 1. Make sure CocoaPods is [installed][CocoaPods Installation].
 
     ```sh
     # Using the default Ruby install will require you to use sudo when
@@ -266,7 +267,8 @@ These projects enhance or use SQLite.swift:
 
  - [SQLiteMigrationManager.swift][] (inspired by
    [FMDBMigrationManager][])
- - [Delta: Math helper](https://apps.apple.com/app/delta-math-helper/id1436506800) (see [Delta/Utils/Database.swift](https://github.com/GroupeMINASTE/Delta-iOS/blob/master/Delta/Utils/Database.swift) for production implementation example)
+ - [Delta: Math helper](https://apps.apple.com/app/delta-math-helper/id1436506800)
+   (see [Delta/Utils/Database.swift](https://github.com/GroupeMINASTE/Delta-iOS/blob/master/Delta/Utils/Database.swift) for production implementation example)
 
 
 ## Alternatives
@@ -278,7 +280,6 @@ Looking for something else? Try another Swift wrapper (or [FMDB][]):
  - [SQLiteDB](https://github.com/FahimF/SQLiteDB)
  - [Squeal](https://github.com/nerdyc/Squeal)
  - [SwiftData](https://github.com/ryanfowler/SwiftData)
- - [SwiftSQLite](https://github.com/chrismsimpson/SwiftSQLite)
 
 [Swift]: https://swift.org/
 [SQLite3]: https://www.sqlite.org
diff --git a/SQLite.playground/Contents.swift b/SQLite.playground/Contents.swift
index 11e13139..ebd5b020 100644
--- a/SQLite.playground/Contents.swift
+++ b/SQLite.playground/Contents.swift
@@ -1,43 +1,93 @@
 import SQLite
 
-let db = try! Connection()
+/// Create an in-memory database
+let db = try Connection(.inMemory)
 
+/// enable statement logging
 db.trace { print($0) }
 
+/// define a "users" table with some fields
 let users = Table("users")
 
 let id = Expression<Int64>("id")
-let email = Expression<String>("email")
-let name = Expression<String?>("name")
+let email = Expression<String>("email") // non-null
+let name = Expression<String?>("name")  // nullable
 
-try! db.run(users.create { t in
+/// prepare the query
+let statement = users.create { t in
     t.column(id, primaryKey: true)
     t.column(email, unique: true, check: email.like("%@%"))
     t.column(name)
-})
+}
+
+/// …and run it
+try db.run(statement)
+
+/// insert "alice"
+let rowid = try db.run(users.insert(email <- "alice@mac.com"))
+
+/// insert multiple rows using `insertMany`
+let lastRowid = try db.run(users.insertMany([
+  [email <- "bob@mac.com"],
+  [email <- "mallory@evil.com"]
+]))
+
+
+let query = try db.prepare(users)
+for user in query {
+    print("id: \(user[id]), email: \(user[email])")
+}
 
-let rowid = try! db.run(users.insert(email <- "alice@mac.com"))
-let alice = users.filter(id == rowid)
+// re-requery just rowid of Alice
+let alice = try db.prepare(users.filter(id == rowid))
+for user in alice {
+    print("id: \(user[id]), email: \(user[email])")
+}
 
-for user in try! db.prepare(users) {
+/// using the `RowIterator` API
+let rowIterator = try db.prepareRowIterator(users)
+for user in try Array(rowIterator) {
     print("id: \(user[id]), email: \(user[email])")
 }
 
+/// also with `map()`
+let mapRowIterator = try db.prepareRowIterator(users)
+let userIds = try mapRowIterator.map { $0[id] }
+
+/// define a virtual tabe for the FTS index
 let emails = VirtualTable("emails")
 
-let subject = Expression<String?>("subject")
+let subject = Expression<String>("subject")
 let body = Expression<String?>("body")
 
-try! db.run(emails.create(.FTS4(subject, body)))
+/// create the index
+try db.run(emails.create(.FTS5(
+    FTS5Config()
+      .column(subject)
+      .column(body)
+)))
 
-try! db.run(emails.insert(
+/// populate with data
+try db.run(emails.insert(
     subject <- "Hello, world!",
     body <- "This is a hello world message."
 ))
 
-let row = try! db.pluck(emails.match("hello"))
+/// run a query
+let ftsQuery = try db.prepare(emails.match("hello"))
 
-let query = try! db.prepare(emails.match("hello"))
-for row in query {
+for row in ftsQuery {
     print(row[subject])
 }
+
+/// custom aggregations
+let reduce: (String, [Binding?]) -> String = { (last, bindings) in
+    last + " " + (bindings.first as? String ?? "")
+}
+
+db.createAggregation("customConcat",
+                     initialValue: "users:",
+                     reduce: reduce,
+                     result: { $0 })
+let result = db.prepare("SELECT customConcat(email) FROM users").scalar() as! String
+print(result)
diff --git a/SQLite.playground/contents.xcplayground b/SQLite.playground/contents.xcplayground
index fd676d5b..441c60ef 100644
--- a/SQLite.playground/contents.xcplayground
+++ b/SQLite.playground/contents.xcplayground
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<playground version='5.0' target-platform='osx' display-mode='raw'>
+<playground version='5.0' target-platform='macos' display-mode='rendered' buildActiveScheme='true' importAppTypes='true'>
     <timeline fileName='timeline.xctimeline'/>
 </playground>
\ No newline at end of file
diff --git a/SQLite.playground/playground.xcworkspace/contents.xcworkspacedata b/SQLite.playground/playground.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 00000000..919434a6
--- /dev/null
+++ b/SQLite.playground/playground.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+   version = "1.0">
+   <FileRef
+      location = "self:">
+   </FileRef>
+</Workspace>
diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj
index 92400180..ba14e9ae 100644
--- a/SQLite.xcodeproj/project.pbxproj
+++ b/SQLite.xcodeproj/project.pbxproj
@@ -256,6 +256,7 @@
 		19A17E723300E5ED3771DCB5 /* Result.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Result.swift; sourceTree = "<group>"; };
 		19A17EA3A313F129011B3FA0 /* Release.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = Release.md; sourceTree = "<group>"; };
 		3717F907221F5D7C00B9BD3D /* CustomAggregationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomAggregationTests.swift; sourceTree = "<group>"; };
+		3D3C3CCB26E5568800759140 /* SQLite.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = SQLite.playground; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
 		3D67B3E51DB2469200A4F4C6 /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = Platforms/WatchOS.platform/Developer/SDKs/WatchOS3.0.sdk/usr/lib/libsqlite3.tbd; sourceTree = DEVELOPER_DIR; };
 		3DDC112E26CDBA0200CE369F /* SQLiteObjc.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SQLiteObjc.h; path = ../SQLiteObjc/include/SQLiteObjc.h; sourceTree = "<group>"; };
 		49EB68C31F7B3CB400D89D40 /* Coding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Coding.swift; sourceTree = "<group>"; };
@@ -386,6 +387,7 @@
 			children = (
 				EE247AD51C3F04ED00AE3E12 /* SQLite */,
 				EE247AE11C3F04ED00AE3E12 /* SQLiteTests */,
+				3D3C3CCB26E5568800759140 /* SQLite.playground */,
 				EE247B8A1C3F81D000AE3E12 /* Metadata */,
 				EE247AD41C3F04ED00AE3E12 /* Products */,
 				3D67B3E41DB2469200A4F4C6 /* Frameworks */,
diff --git a/Sources/SQLite/Core/Connection+Aggregation.swift b/Sources/SQLite/Core/Connection+Aggregation.swift
index 106f292c..4eea76c3 100644
--- a/Sources/SQLite/Core/Connection+Aggregation.swift
+++ b/Sources/SQLite/Core/Connection+Aggregation.swift
@@ -90,7 +90,7 @@ extension Connection {
         register(functionName, argc: argc, value: box)
     }
 
-    func createAggregation<T: AnyObject>(
+    public func createAggregation<T: AnyObject>(
             _ aggregate: String,
             argumentCount: UInt? = nil,
             deterministic: Bool = false,
@@ -122,7 +122,7 @@ extension Connection {
         createAggregation(aggregate, step: step, final: final, state: state)
     }
 
-    func createAggregation<T>(
+    public func createAggregation<T>(
             _ aggregate: String,
             argumentCount: UInt? = nil,
             deterministic: Bool = false,
diff --git a/Tests/SPM/Package.swift b/Tests/SPM/Package.swift
index 810026ce..265021e5 100644
--- a/Tests/SPM/Package.swift
+++ b/Tests/SPM/Package.swift
@@ -6,11 +6,12 @@ import PackageDescription
 let package = Package(
     name: "test",
     dependencies: [
+        // for testing from same repository
         .package(path: "../..")
+        // normally this would be:
+        // .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.13.0")
     ],
     targets: [
-        // Targets are the basic building blocks of a package. A target can define a module or a test suite.
-        // Targets can depend on other targets in this package, and on products in packages this package depends on.
         .target(
             name: "test",
             dependencies: [.product(name: "SQLite", package: "SQLite.swift")]

From ab67978f154c404e05a6a634cca30fa0206d1dd0 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Mon, 6 Sep 2021 01:00:54 +0200
Subject: [PATCH 090/326] Update screenshot

---
 Documentation/Resources/playground@2x.png | Bin 124729 -> 409028 bytes
 1 file changed, 0 insertions(+), 0 deletions(-)

diff --git a/Documentation/Resources/playground@2x.png b/Documentation/Resources/playground@2x.png
index 32646d6aa411b7b5a7163fc692c7252fe8ee8cce..da132718cf4bc7d2cafdff82eb5020abcb46ec93 100644
GIT binary patch

From 84a878807af8e6f317a2451937567ec82a6f80d9 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Tue, 7 Sep 2021 12:57:15 +0200
Subject: [PATCH 091/326] Clarify usage

---
 Documentation/Index.md | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/Documentation/Index.md b/Documentation/Index.md
index 1e08f6c4..1f702653 100644
--- a/Documentation/Index.md
+++ b/Documentation/Index.md
@@ -812,15 +812,24 @@ It is therefore recommended using the `RowIterator` API instead,
 which has explicit error handling:
 
 ```swift
+// option 1: convert results into an Array of rows
 let rowIterator = try db.prepareRowIterator(users)
 for user in try Array(rowIterator) {
     print("id: \(user[id]), email: \(user[email])")
 }
 
-/// or using `map()`
+/// option 2: transform results using `map()`
 let mapRowIterator = try db.prepareRowIterator(users)
 let userIds = try mapRowIterator.map { $0[id] }
 
+/// option 3: handle each row individually with `failableNext()`
+do {
+    while let row = try rowIterator.failableNext() {
+        // Handle row
+    }
+} catch {
+    // Handle error
+}
 ```
 
 ### Plucking Rows

From e191c7d04583a1fd4e0f95e73bec2161a30e4857 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Tue, 7 Sep 2021 13:04:24 +0200
Subject: [PATCH 092/326] Add failableNext() example to playground

---
 SQLite.playground/Contents.swift | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/SQLite.playground/Contents.swift b/SQLite.playground/Contents.swift
index ebd5b020..c089076d 100644
--- a/SQLite.playground/Contents.swift
+++ b/SQLite.playground/Contents.swift
@@ -54,6 +54,16 @@ for user in try Array(rowIterator) {
 let mapRowIterator = try db.prepareRowIterator(users)
 let userIds = try mapRowIterator.map { $0[id] }
 
+/// using `failableNext()` on `RowIterator`
+let iterator = try db.prepareRowIterator(users)
+do {
+    while let row = try rowIterator.failableNext() {
+        print(row)
+    }
+} catch {
+    // Handle error
+}
+
 /// define a virtual tabe for the FTS index
 let emails = VirtualTable("emails")
 

From 818af0882507b0e468c9ec40fa2149a52a4ad171 Mon Sep 17 00:00:00 2001
From: Calvin Cestari <calvin.cestari@gmail.com>
Date: Wed, 3 Nov 2021 12:59:38 -0700
Subject: [PATCH 093/326] Add platform versions to the SPM manifest

---
 Package.swift | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/Package.swift b/Package.swift
index f4938678..47c00b37 100644
--- a/Package.swift
+++ b/Package.swift
@@ -3,6 +3,12 @@ import PackageDescription
 
 let package = Package(
     name: "SQLite.swift",
+    platforms: [
+        .iOS(.v9),
+        .macOS(.v10_15),
+        .watchOS(.v3),
+        .tvOS(.v9)
+    ],
     products: [
         .library(
             name: "SQLite",
@@ -46,6 +52,12 @@ package.dependencies = [.package(url: "https://github.com/stephencelis/CSQLite.g
 package.targets = [
     .target(
         name: "SQLite",
+        platforms: [
+            .iOS(.v9),
+            .macOS(.v10_15),
+            .watchOS(.v3),
+            .tvOS(.v9)
+        ],
         dependencies: [.product(name: "CSQLite", package: "CSQLite")],
         exclude: ["Extensions/FTS4.swift", "Extensions/FTS5.swift"]
     ),

From 9af505317fff1d9129a4c905fa2e8757c3484fc0 Mon Sep 17 00:00:00 2001
From: Calvin Cestari <calvin.cestari@gmail.com>
Date: Wed, 3 Nov 2021 13:00:23 -0700
Subject: [PATCH 094/326] Move XCode deployment version from targets to project

---
 SQLite.xcodeproj/project.pbxproj | 12 ++++--------
 1 file changed, 4 insertions(+), 8 deletions(-)

diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj
index ba14e9ae..aecb424a 100644
--- a/SQLite.xcodeproj/project.pbxproj
+++ b/SQLite.xcodeproj/project.pbxproj
@@ -1099,7 +1099,6 @@
 				SDKROOT = appletvos;
 				SKIP_INSTALL = YES;
 				SWIFT_TREAT_WARNINGS_AS_ERRORS = YES;
-				TVOS_DEPLOYMENT_TARGET = 12.0;
 			};
 			name = Debug;
 		};
@@ -1121,7 +1120,6 @@
 				SDKROOT = appletvos;
 				SKIP_INSTALL = YES;
 				SWIFT_TREAT_WARNINGS_AS_ERRORS = YES;
-				TVOS_DEPLOYMENT_TARGET = 12.0;
 			};
 			name = Release;
 		};
@@ -1135,7 +1133,6 @@
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SDKROOT = appletvos;
 				SWIFT_TREAT_WARNINGS_AS_ERRORS = YES;
-				TVOS_DEPLOYMENT_TARGET = 12.0;
 			};
 			name = Debug;
 		};
@@ -1149,7 +1146,6 @@
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SDKROOT = appletvos;
 				SWIFT_TREAT_WARNINGS_AS_ERRORS = YES;
-				TVOS_DEPLOYMENT_TARGET = 12.0;
 			};
 			name = Release;
 		};
@@ -1173,7 +1169,6 @@
 				SKIP_INSTALL = YES;
 				SWIFT_TREAT_WARNINGS_AS_ERRORS = YES;
 				TARGETED_DEVICE_FAMILY = 4;
-				WATCHOS_DEPLOYMENT_TARGET = 3.0;
 			};
 			name = Debug;
 		};
@@ -1197,7 +1192,6 @@
 				SKIP_INSTALL = YES;
 				SWIFT_TREAT_WARNINGS_AS_ERRORS = YES;
 				TARGETED_DEVICE_FAMILY = 4;
-				WATCHOS_DEPLOYMENT_TARGET = 3.0;
 			};
 			name = Release;
 		};
@@ -1259,8 +1253,10 @@
 				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
 				SWIFT_VERSION = 5.0;
 				TARGETED_DEVICE_FAMILY = "1,2,3";
+				TVOS_DEPLOYMENT_TARGET = 9.1;
 				VERSIONING_SYSTEM = "apple-generic";
 				VERSION_INFO_PREFIX = "";
+				WATCHOS_DEPLOYMENT_TARGET = 3.0;
 			};
 			name = Debug;
 		};
@@ -1315,9 +1311,11 @@
 				SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
 				SWIFT_VERSION = 5.0;
 				TARGETED_DEVICE_FAMILY = "1,2,3";
+				TVOS_DEPLOYMENT_TARGET = 9.1;
 				VALIDATE_PRODUCT = YES;
 				VERSIONING_SYSTEM = "apple-generic";
 				VERSION_INFO_PREFIX = "";
+				WATCHOS_DEPLOYMENT_TARGET = 3.0;
 			};
 			name = Release;
 		};
@@ -1334,7 +1332,6 @@
 				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
 				INFOPLIST_FILE = "$(SRCROOT)/Sources/SQLite/Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
-				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
 				MARKETING_VERSION = 0.13.0;
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
@@ -1358,7 +1355,6 @@
 				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
 				INFOPLIST_FILE = "$(SRCROOT)/Sources/SQLite/Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
-				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
 				MARKETING_VERSION = 0.13.0;
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;

From d6eb85b381da062ffe2db30739f868607dc1c4c9 Mon Sep 17 00:00:00 2001
From: Calvin Cestari <calvin.cestari@gmail.com>
Date: Fri, 5 Nov 2021 12:13:00 -0700
Subject: [PATCH 095/326] Fix test platform versions

---
 Package.swift           | 6 ------
 Tests/SPM/Package.swift | 6 ++++++
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/Package.swift b/Package.swift
index 47c00b37..abce8395 100644
--- a/Package.swift
+++ b/Package.swift
@@ -52,12 +52,6 @@ package.dependencies = [.package(url: "https://github.com/stephencelis/CSQLite.g
 package.targets = [
     .target(
         name: "SQLite",
-        platforms: [
-            .iOS(.v9),
-            .macOS(.v10_15),
-            .watchOS(.v3),
-            .tvOS(.v9)
-        ],
         dependencies: [.product(name: "CSQLite", package: "CSQLite")],
         exclude: ["Extensions/FTS4.swift", "Extensions/FTS5.swift"]
     ),
diff --git a/Tests/SPM/Package.swift b/Tests/SPM/Package.swift
index 265021e5..91c0c6d1 100644
--- a/Tests/SPM/Package.swift
+++ b/Tests/SPM/Package.swift
@@ -5,6 +5,12 @@ import PackageDescription
 
 let package = Package(
     name: "test",
+    platforms: [
+        .iOS(.v9),
+        .macOS(.v10_15),
+        .watchOS(.v3),
+        .tvOS(.v9)
+    ],
     dependencies: [
         // for testing from same repository
         .package(path: "../..")

From d02d0b4090f855acca24e105f2f684c7f71dc9a1 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Fri, 5 Nov 2021 21:20:10 +0100
Subject: [PATCH 096/326] Move OSX deployment target back to 10.10

---
 Package.swift                    | 2 +-
 SQLite.swift.podspec             | 2 +-
 SQLite.xcodeproj/project.pbxproj | 4 ++--
 Tests/SPM/Package.swift          | 2 +-
 4 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/Package.swift b/Package.swift
index abce8395..24898bca 100644
--- a/Package.swift
+++ b/Package.swift
@@ -5,7 +5,7 @@ let package = Package(
     name: "SQLite.swift",
     platforms: [
         .iOS(.v9),
-        .macOS(.v10_15),
+        .macOS(.v10_10),
         .watchOS(.v3),
         .tvOS(.v9)
     ],
diff --git a/SQLite.swift.podspec b/SQLite.swift.podspec
index f273ffd8..80871b0b 100644
--- a/SQLite.swift.podspec
+++ b/SQLite.swift.podspec
@@ -20,7 +20,7 @@ Pod::Spec.new do |s|
 
   ios_deployment_target = '9.0'
   tvos_deployment_target = '9.1'
-  osx_deployment_target = '10.15'
+  osx_deployment_target = '10.10'
   watchos_deployment_target = '3.0'
 
   s.ios.deployment_target = ios_deployment_target
diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj
index aecb424a..ecc1195c 100644
--- a/SQLite.xcodeproj/project.pbxproj
+++ b/SQLite.xcodeproj/project.pbxproj
@@ -1245,7 +1245,7 @@
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
 				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
-				MACOSX_DEPLOYMENT_TARGET = 10.15;
+				MACOSX_DEPLOYMENT_TARGET = 10.10;
 				MTL_ENABLE_DEBUG_INFO = YES;
 				ONLY_ACTIVE_ARCH = YES;
 				PRODUCT_NAME = "";
@@ -1304,7 +1304,7 @@
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
 				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
-				MACOSX_DEPLOYMENT_TARGET = 10.15;
+				MACOSX_DEPLOYMENT_TARGET = 10.10;
 				MTL_ENABLE_DEBUG_INFO = NO;
 				PRODUCT_NAME = "";
 				SDKROOT = iphoneos;
diff --git a/Tests/SPM/Package.swift b/Tests/SPM/Package.swift
index 91c0c6d1..dd1cd45c 100644
--- a/Tests/SPM/Package.swift
+++ b/Tests/SPM/Package.swift
@@ -7,7 +7,7 @@ let package = Package(
     name: "test",
     platforms: [
         .iOS(.v9),
-        .macOS(.v10_15),
+        .macOS(.v10_10),
         .watchOS(.v3),
         .tvOS(.v9)
     ],

From 34798be5eb2cc057040903839ba732b5cab7c02c Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Fri, 5 Nov 2021 21:32:17 +0100
Subject: [PATCH 097/326] Update changelog

---
 CHANGELOG.md | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index c9d09c12..a36c6468 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,8 @@
 * Support for custom SQL aggregates ([#881][])
 * Restore previous iteration behavior ([#1075][])
 * Fix compilation on Linux ([#1077][])
+* Align platform versions in SPM manifest and Xcode ([#1094][])
+* Revert OSX deployment target back to 10.10 ([#1095][])
 
 0.13.0 (22-08-2021), [diff][diff-0.13.0]
 ========================================
@@ -128,3 +130,5 @@
 [#919]: https://github.com/stephencelis/SQLite.swift/pull/919
 [#1075]: https://github.com/stephencelis/SQLite.swift/pull/1075
 [#1077]: https://github.com/stephencelis/SQLite.swift/issues/1077
+[#1094]: https://github.com/stephencelis/SQLite.swift/pull/1094
+[#1095]: https://github.com/stephencelis/SQLite.swift/pull/1095
\ No newline at end of file

From 883117cbc847508192d2961f7833e9da5a4c5f6d Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Fri, 5 Nov 2021 21:42:45 +0100
Subject: [PATCH 098/326] "macos-latest" is not the latest :(

https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners#supported-runners-and-hardware-resources
---
 .github/workflows/build.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 88c5803f..a633f184 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -5,7 +5,7 @@ env:
   IOS_VERSION: 14.4
 jobs:
   build:
-    runs-on: macos-latest
+    runs-on: macos-11
     steps:
     - uses: actions/checkout@v2
     - name: Install

From 13d78942f39ab4254ac6856e7f88f5032946ef3b Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Fri, 5 Nov 2021 21:49:43 +0100
Subject: [PATCH 099/326] iOS 15.0

---
 Makefile | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Makefile b/Makefile
index 0b9a1606..c62da000 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
 BUILD_TOOL = xcodebuild
 BUILD_SCHEME = SQLite Mac
 IOS_SIMULATOR = iPhone 12
-IOS_VERSION = 14.4
+IOS_VERSION = 15.0
 ifeq ($(BUILD_SCHEME),SQLite iOS)
 	BUILD_ARGUMENTS = -scheme "$(BUILD_SCHEME)" -destination "platform=iOS Simulator,name=$(IOS_SIMULATOR),OS=$(IOS_VERSION)"
 else

From 7c0106255f742218b3d99d86ed08a31b42882cf7 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Fri, 5 Nov 2021 21:58:38 +0100
Subject: [PATCH 100/326] iOS version

---
 .github/workflows/build.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index a633f184..84dc9aba 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -2,7 +2,7 @@ name: Build and test
 on: [push, pull_request]
 env:
   IOS_SIMULATOR: iPhone 12
-  IOS_VERSION: 14.4
+  IOS_VERSION: 15.0
 jobs:
   build:
     runs-on: macos-11

From 47c70041ac7237b5c1ccdcf6114e6bb78c0d9dc6 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Fri, 5 Nov 2021 22:07:30 +0100
Subject: [PATCH 101/326] YML insanity

---
 .github/workflows/build.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 84dc9aba..baa8c2d8 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -2,7 +2,7 @@ name: Build and test
 on: [push, pull_request]
 env:
   IOS_SIMULATOR: iPhone 12
-  IOS_VERSION: 15.0
+  IOS_VERSION: "15.0"
 jobs:
   build:
     runs-on: macos-11

From db48bb0bf98568c0e29976cd5b95c898c013c277 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Wed, 17 Nov 2021 00:46:07 +0100
Subject: [PATCH 102/326] Update Index.md

Clarify SQLCipher integration (#1097)
---
 Documentation/Index.md | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/Documentation/Index.md b/Documentation/Index.md
index 1f702653..22c5fea5 100644
--- a/Documentation/Index.md
+++ b/Documentation/Index.md
@@ -170,6 +170,8 @@ If you want to use [SQLCipher][] with SQLite.swift you can require the
 
 ```ruby
 target 'YourAppTargetName' do
+  # Make sure you only require the subspec, otherwise you app might link against
+  # the system SQLite, which means the SQLCipher-specific methods won't work.
   pod 'SQLite.swift/SQLCipher', '~> 0.13.0'
 end
 ```

From 60a65015f6402b7c34b9a924f755ca0a73afeeaa Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Wed, 17 Nov 2021 00:55:18 +0100
Subject: [PATCH 103/326] Bump version

---
 CHANGELOG.md                     |  7 ++++---
 Documentation/Index.md           | 12 ++++++------
 Documentation/Planning.md        |  2 +-
 README.md                        |  6 +++---
 SQLite.swift.podspec             |  2 +-
 SQLite.xcodeproj/project.pbxproj |  4 ++--
 6 files changed, 17 insertions(+), 16 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index a36c6468..d89e33f4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,9 +1,9 @@
-0.13.1 (tba)
+0.13.1 (17-11-2021), [diff][diff-0.13.1]
 ========================================
 
 * Support for database backup ([#919][])
 * Support for custom SQL aggregates ([#881][])
-* Restore previous iteration behavior ([#1075][])
+* Restore previous behavior in `FailableIterator` ([#1075][])
 * Fix compilation on Linux ([#1077][])
 * Align platform versions in SPM manifest and Xcode ([#1094][])
 * Revert OSX deployment target back to 10.10 ([#1095][])
@@ -94,6 +94,7 @@
 [diff-0.12.0]: https://github.com/stephencelis/SQLite.swift/compare/0.11.6...0.12.0
 [diff-0.12.2]: https://github.com/stephencelis/SQLite.swift/compare/0.12.0...0.12.2
 [diff-0.13.0]: https://github.com/stephencelis/SQLite.swift/compare/0.12.2...0.13.0
+[diff-0.13.1]: https://github.com/stephencelis/SQLite.swift/compare/0.13.0...0.13.1
 
 [#142]: https://github.com/stephencelis/SQLite.swift/issues/142
 [#315]: https://github.com/stephencelis/SQLite.swift/issues/315
@@ -131,4 +132,4 @@
 [#1075]: https://github.com/stephencelis/SQLite.swift/pull/1075
 [#1077]: https://github.com/stephencelis/SQLite.swift/issues/1077
 [#1094]: https://github.com/stephencelis/SQLite.swift/pull/1094
-[#1095]: https://github.com/stephencelis/SQLite.swift/pull/1095
\ No newline at end of file
+[#1095]: https://github.com/stephencelis/SQLite.swift/pull/1095
diff --git a/Documentation/Index.md b/Documentation/Index.md
index 22c5fea5..96e5775c 100644
--- a/Documentation/Index.md
+++ b/Documentation/Index.md
@@ -81,7 +81,7 @@ process of downloading, compiling, and linking dependencies.
 
   ```swift
   dependencies: [
-    .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.13.0")
+    .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.13.1")
   ]
   ```
 
@@ -102,7 +102,7 @@ install SQLite.swift with Carthage:
  2. Update your Cartfile to include the following:
 
     ```ruby
-    github "stephencelis/SQLite.swift" ~> 0.13.0
+    github "stephencelis/SQLite.swift" ~> 0.13.1
     ```
 
  3. Run `carthage update` and [add the appropriate framework][Carthage Usage].
@@ -132,7 +132,7 @@ install SQLite.swift with Carthage:
     use_frameworks!
 
     target 'YourAppTargetName' do
-        pod 'SQLite.swift', '~> 0.13.0'
+        pod 'SQLite.swift', '~> 0.13.1'
     end
     ```
 
@@ -146,7 +146,7 @@ with the OS you can require the `standalone` subspec:
 
 ```ruby
 target 'YourAppTargetName' do
-  pod 'SQLite.swift/standalone', '~> 0.13.0'
+  pod 'SQLite.swift/standalone', '~> 0.13.1'
 end
 ```
 
@@ -156,7 +156,7 @@ dependency to sqlite3 or one of its subspecs:
 
 ```ruby
 target 'YourAppTargetName' do
-  pod 'SQLite.swift/standalone', '~> 0.13.0'
+  pod 'SQLite.swift/standalone', '~> 0.13.1'
   pod 'sqlite3/fts5', '= 3.15.0'  # SQLite 3.15.0 with FTS5 enabled
 end
 ```
@@ -172,7 +172,7 @@ If you want to use [SQLCipher][] with SQLite.swift you can require the
 target 'YourAppTargetName' do
   # Make sure you only require the subspec, otherwise you app might link against
   # the system SQLite, which means the SQLCipher-specific methods won't work.
-  pod 'SQLite.swift/SQLCipher', '~> 0.13.0'
+  pod 'SQLite.swift/SQLCipher', '~> 0.13.1'
 end
 ```
 
diff --git a/Documentation/Planning.md b/Documentation/Planning.md
index 44e02c7a..bebfb503 100644
--- a/Documentation/Planning.md
+++ b/Documentation/Planning.md
@@ -6,7 +6,7 @@ additions and Pull Requests, as well as to keep the Issues list clear of
 enhancement requests so that bugs are more visible.
 
 > ⚠ This document is currently not actively maintained. See
-> the [0.13.1 milestone](https://github.com/stephencelis/SQLite.swift/issues?q=is%3Aopen+is%3Aissue+milestone%3A0.13.1)
+> the [0.13.2 milestone](https://github.com/stephencelis/SQLite.swift/issues?q=is%3Aopen+is%3Aissue+milestone%3A0.13.2)
 > on Github for additional information about planned features for the next release.
 
 ## Roadmap
diff --git a/README.md b/README.md
index ec92729f..bc8eac31 100644
--- a/README.md
+++ b/README.md
@@ -136,7 +136,7 @@ Swift code.
 
   ```swift
   dependencies: [
-      .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.13.0")
+      .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.13.1")
   ]
   ```
 
@@ -160,7 +160,7 @@ install SQLite.swift with Carthage:
  2. Update your Cartfile to include the following:
 
     ```ruby
-    github "stephencelis/SQLite.swift" ~> 0.13.0
+    github "stephencelis/SQLite.swift" ~> 0.13.1
     ```
 
  3. Run `carthage update` and
@@ -191,7 +191,7 @@ SQLite.swift with CocoaPods:
     use_frameworks!
 
     target 'YourAppTargetName' do
-        pod 'SQLite.swift', '~> 0.13.0'
+        pod 'SQLite.swift', '~> 0.13.1'
     end
     ```
 
diff --git a/SQLite.swift.podspec b/SQLite.swift.podspec
index 80871b0b..90a8a348 100644
--- a/SQLite.swift.podspec
+++ b/SQLite.swift.podspec
@@ -1,6 +1,6 @@
 Pod::Spec.new do |s|
   s.name             = "SQLite.swift"
-  s.version          = "0.13.0"
+  s.version          = "0.13.1"
   s.summary          = "A type-safe, Swift-language layer over SQLite3."
 
   s.description      = <<-DESC
diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj
index ecc1195c..aee41218 100644
--- a/SQLite.xcodeproj/project.pbxproj
+++ b/SQLite.xcodeproj/project.pbxproj
@@ -1333,7 +1333,7 @@
 				INFOPLIST_FILE = "$(SRCROOT)/Sources/SQLite/Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
-				MARKETING_VERSION = 0.13.0;
+				MARKETING_VERSION = 0.13.1;
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
 				PRODUCT_NAME = SQLite;
 				SKIP_INSTALL = YES;
@@ -1356,7 +1356,7 @@
 				INFOPLIST_FILE = "$(SRCROOT)/Sources/SQLite/Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
-				MARKETING_VERSION = 0.13.0;
+				MARKETING_VERSION = 0.13.1;
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
 				PRODUCT_NAME = SQLite;
 				SKIP_INSTALL = YES;

From a2bfb9eb3b0641873c886384ed96fd44f70dba0e Mon Sep 17 00:00:00 2001
From: Dylan Nunns <dylan@gnarledrootsystems.com>
Date: Thu, 2 Dec 2021 19:05:01 -0400
Subject: [PATCH 104/326] Closing bracket position

Updated position of closing bracket where `onConflictOf` parameter. In this particular area it was closing `upsert()` and making `onConflictOf` a parameter of `.run()`
---
 Documentation/Index.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Documentation/Index.md b/Documentation/Index.md
index 96e5775c..061b448b 100644
--- a/Documentation/Index.md
+++ b/Documentation/Index.md
@@ -1174,7 +1174,7 @@ joined by the `<-` operator. Upserting is like inserting, except if there is a
 conflict on the specified column value, SQLite will perform an update on the row instead.
 
 ```swift
-try db.run(users.upsert(email <- "alice@mac.com", name <- "Alice"), onConflictOf: email)
+try db.run(users.upsert(email <- "alice@mac.com", name <- "Alice", onConflictOf: email))
 // INSERT INTO "users" ("email", "name") VALUES ('alice@mac.com', 'Alice') ON CONFLICT (\"email\") DO UPDATE SET \"name\" = \"excluded\".\"name\"
 ```
 

From b9489a7cc11f18a15e744193e29de1430ddd35b2 Mon Sep 17 00:00:00 2001
From: Nathan Fallet <contact@nathanfallet.me>
Date: Mon, 17 Jan 2022 17:08:36 +0100
Subject: [PATCH 105/326] Native user_version support in Connection

---
 Sources/SQLite/Core/Connection.swift | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/Sources/SQLite/Core/Connection.swift b/Sources/SQLite/Core/Connection.swift
index 6b5481e3..5b1fc50a 100644
--- a/Sources/SQLite/Core/Connection.swift
+++ b/Sources/SQLite/Core/Connection.swift
@@ -151,6 +151,13 @@ public final class Connection {
     public var totalChanges: Int {
         Int(sqlite3_total_changes(handle))
     }
+    
+    /// The user version of the database.
+    /// See SQLite [PRAGMA user_version](https://sqlite.org/pragma.html#pragma_user_version)
+    public var userVersion: Int32 {
+        get { return Int32(try! scalar("PRAGMA user_version") as! Int64)}
+        set { try! run("PRAGMA user_version = \(newValue)") }
+    }
 
     // MARK: - Execute
 

From 9e162a84c964562cf6248890722c77011f81eaa4 Mon Sep 17 00:00:00 2001
From: Nathan Fallet <contact@nathanfallet.me>
Date: Mon, 17 Jan 2022 17:30:00 +0100
Subject: [PATCH 106/326] Refactoring userVersion + adding tests

---
 Sources/SQLite/Core/Connection.swift    | 20 +++++++++++++++-----
 Tests/SQLiteTests/ConnectionTests.swift |  5 +++++
 2 files changed, 20 insertions(+), 5 deletions(-)

diff --git a/Sources/SQLite/Core/Connection.swift b/Sources/SQLite/Core/Connection.swift
index 5b1fc50a..4b1c712a 100644
--- a/Sources/SQLite/Core/Connection.swift
+++ b/Sources/SQLite/Core/Connection.swift
@@ -151,12 +151,22 @@ public final class Connection {
     public var totalChanges: Int {
         Int(sqlite3_total_changes(handle))
     }
-    
-    /// The user version of the database.
+
+    /// Gets the user version of the database.
+    /// See SQLite [PRAGMA user_version](https://sqlite.org/pragma.html#pragma_user_version)
+    /// - Returns: the user version of the database
+    public func getUserVersion() throws -> Int32? {
+        guard let userVersion = try scalar("PRAGMA user_version") as? Int64 else {
+            return nil
+        }
+        return Int32(userVersion)
+    }
+
+    /// Sets the user version of the database.
     /// See SQLite [PRAGMA user_version](https://sqlite.org/pragma.html#pragma_user_version)
-    public var userVersion: Int32 {
-        get { return Int32(try! scalar("PRAGMA user_version") as! Int64)}
-        set { try! run("PRAGMA user_version = \(newValue)") }
+    /// - Parameter userVersion: the new user version of the database
+    public func setUserVersion(to userVersion: Int32) throws {
+        try run("PRAGMA user_version = \(userVersion)")
     }
 
     // MARK: - Execute
diff --git a/Tests/SQLiteTests/ConnectionTests.swift b/Tests/SQLiteTests/ConnectionTests.swift
index ae9d4dfd..707cbc43 100644
--- a/Tests/SQLiteTests/ConnectionTests.swift
+++ b/Tests/SQLiteTests/ConnectionTests.swift
@@ -96,6 +96,11 @@ class ConnectionTests: SQLiteTestCase {
         XCTAssertEqual(2, db.totalChanges)
     }
 
+    func test_userVersion() {
+        try! db.setUserVersion(to: 2)
+        XCTAssertEqual(2, try! db.getUserVersion()!)
+    }
+
     func test_prepare_preparesAndReturnsStatements() {
         _ = try! db.prepare("SELECT * FROM users WHERE admin = 0")
         _ = try! db.prepare("SELECT * FROM users WHERE admin = ?", 0)

From d52eaaf1795e05dadb5c1c0e59cf56579c7f7112 Mon Sep 17 00:00:00 2001
From: Nathan Fallet <contact@nathanfallet.me>
Date: Tue, 18 Jan 2022 13:28:31 +0100
Subject: [PATCH 107/326] Set userVersion as a property

---
 Sources/SQLite/Core/Connection.swift    | 25 ++++++++++++-------------
 Tests/SQLiteTests/ConnectionTests.swift |  4 ++--
 2 files changed, 14 insertions(+), 15 deletions(-)

diff --git a/Sources/SQLite/Core/Connection.swift b/Sources/SQLite/Core/Connection.swift
index 4b1c712a..becc932e 100644
--- a/Sources/SQLite/Core/Connection.swift
+++ b/Sources/SQLite/Core/Connection.swift
@@ -152,21 +152,20 @@ public final class Connection {
         Int(sqlite3_total_changes(handle))
     }
 
-    /// Gets the user version of the database.
+    /// The user version of the database.
     /// See SQLite [PRAGMA user_version](https://sqlite.org/pragma.html#pragma_user_version)
-    /// - Returns: the user version of the database
-    public func getUserVersion() throws -> Int32? {
-        guard let userVersion = try scalar("PRAGMA user_version") as? Int64 else {
-            return nil
+    public var userVersion: Int32? {
+        get {
+            guard let userVersion = try? scalar("PRAGMA user_version") as? Int64 else {
+                return nil
+            }
+            return Int32(userVersion)
+        }
+        set {
+            if let userVersion = newValue {
+                _ = try? run("PRAGMA user_version = \(userVersion)")
+            }
         }
-        return Int32(userVersion)
-    }
-
-    /// Sets the user version of the database.
-    /// See SQLite [PRAGMA user_version](https://sqlite.org/pragma.html#pragma_user_version)
-    /// - Parameter userVersion: the new user version of the database
-    public func setUserVersion(to userVersion: Int32) throws {
-        try run("PRAGMA user_version = \(userVersion)")
     }
 
     // MARK: - Execute
diff --git a/Tests/SQLiteTests/ConnectionTests.swift b/Tests/SQLiteTests/ConnectionTests.swift
index 707cbc43..136271a4 100644
--- a/Tests/SQLiteTests/ConnectionTests.swift
+++ b/Tests/SQLiteTests/ConnectionTests.swift
@@ -97,8 +97,8 @@ class ConnectionTests: SQLiteTestCase {
     }
 
     func test_userVersion() {
-        try! db.setUserVersion(to: 2)
-        XCTAssertEqual(2, try! db.getUserVersion()!)
+        db.userVersion = 2
+        XCTAssertEqual(2, db.userVersion!)
     }
 
     func test_prepare_preparesAndReturnsStatements() {

From 80c8b30a4e372f92745e197047c9e8549fc4347d Mon Sep 17 00:00:00 2001
From: Nathan Fallet <contact@nathanfallet.me>
Date: Tue, 25 Jan 2022 13:14:00 +0100
Subject: [PATCH 108/326] More compact version

---
 Sources/SQLite/Core/Connection.swift | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/Sources/SQLite/Core/Connection.swift b/Sources/SQLite/Core/Connection.swift
index becc932e..043cfafc 100644
--- a/Sources/SQLite/Core/Connection.swift
+++ b/Sources/SQLite/Core/Connection.swift
@@ -156,10 +156,7 @@ public final class Connection {
     /// See SQLite [PRAGMA user_version](https://sqlite.org/pragma.html#pragma_user_version)
     public var userVersion: Int32? {
         get {
-            guard let userVersion = try? scalar("PRAGMA user_version") as? Int64 else {
-                return nil
-            }
-            return Int32(userVersion)
+            (try? scalar("PRAGMA user_version") as? Int64).map(Int32.init)
         }
         set {
             if let userVersion = newValue {

From d00473986112b4bce7bb7f15dc68483adf29c67e Mon Sep 17 00:00:00 2001
From: Nathan Fallet <contact@nathanfallet.me>
Date: Tue, 25 Jan 2022 14:09:11 +0100
Subject: [PATCH 109/326] Fixing tests for Linux

---
 Tests/SQLiteTests/QueryTests.swift  | 23 ++++++++++++++---------
 Tests/SQLiteTests/TestHelpers.swift | 13 ++++++++++++-
 2 files changed, 26 insertions(+), 10 deletions(-)

diff --git a/Tests/SQLiteTests/QueryTests.swift b/Tests/SQLiteTests/QueryTests.swift
index e0749f2c..f225adb4 100644
--- a/Tests/SQLiteTests/QueryTests.swift
+++ b/Tests/SQLiteTests/QueryTests.swift
@@ -391,15 +391,20 @@ class QueryTests: XCTestCase {
         let value = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4,
                                 date: Date(timeIntervalSince1970: 0), optional: nil, sub: value1)
         let update = try emails.update(value)
-        let encodedJSON = try JSONEncoder().encode(value1)
-        let encodedJSONString = String(data: encodedJSON, encoding: .utf8)!
-        assertSQL(
-            """
-            UPDATE \"emails\" SET \"int\" = 1, \"string\" = '2', \"bool\" = 1, \"float\" = 3.0, \"double\" = 4.0,
-             \"date\" = '1970-01-01T00:00:00.000', \"sub\" = '\(encodedJSONString)'
-            """.replacingOccurrences(of: "\n", with: ""),
-            update
-        )
+        
+        // NOTE: As Linux JSON decoding doesn't order keys the same way, we need to check prefix, suffix,
+        // and extract JSON to decode it and check the decoded object.
+        
+        let expectedPrefix = "UPDATE \"emails\" SET \"int\" = 1, \"string\" = '2', \"bool\" = 1, \"float\" = 3.0, \"double\" = 4.0, \"date\" = '1970-01-01T00:00:00.000', \"sub\" = '"
+        let expectedSuffix = "'"
+        
+        let sql = update.asSQL()
+        XCTAssert(sql.hasPrefix(expectedPrefix))
+        XCTAssert(sql.hasSuffix(expectedSuffix))
+        
+        let extractedJSON = String(sql[sql.index(sql.startIndex, offsetBy: expectedPrefix.count) ..< sql.index(sql.endIndex, offsetBy: -expectedSuffix.count)])
+        let decodedJSON = try JSONDecoder().decode(TestCodable.self, from: extractedJSON.data(using: .utf8)!)
+        XCTAssertEqual(decodedJSON, value1)
     }
 
     func test_delete_compilesDeleteExpression() {
diff --git a/Tests/SQLiteTests/TestHelpers.swift b/Tests/SQLiteTests/TestHelpers.swift
index 07cc6f06..53dabe79 100644
--- a/Tests/SQLiteTests/TestHelpers.swift
+++ b/Tests/SQLiteTests/TestHelpers.swift
@@ -105,7 +105,7 @@ let qualifiedTable = Table("table", database: "main")
 let virtualTable = VirtualTable("virtual_table")
 let _view = View("view") // avoid Mac XCTestCase collision
 
-class TestCodable: Codable {
+class TestCodable: Codable, Equatable {
     let int: Int
     let string: String
     let bool: Bool
@@ -125,4 +125,15 @@ class TestCodable: Codable {
         self.optional = optional
         self.sub = sub
     }
+    
+    static func == (lhs: TestCodable, rhs: TestCodable) -> Bool {
+        lhs.int == rhs.int &&
+        lhs.string == rhs.string &&
+        lhs.bool == rhs.bool &&
+        lhs.float == rhs.float &&
+        lhs.double == rhs.double &&
+        lhs.date == rhs.date &&
+        lhs.optional == rhs.optional &&
+        lhs.sub == rhs.sub
+    }
 }

From 34d3a9713e77d2a85b4140d505d3dbd3918117ff Mon Sep 17 00:00:00 2001
From: Nathan Fallet <contact@nathanfallet.me>
Date: Tue, 25 Jan 2022 14:50:37 +0100
Subject: [PATCH 110/326] Fixing Linting

---
 Tests/SQLiteTests/QueryTests.swift  | 19 +++++++++++++------
 Tests/SQLiteTests/TestHelpers.swift |  2 +-
 2 files changed, 14 insertions(+), 7 deletions(-)

diff --git a/Tests/SQLiteTests/QueryTests.swift b/Tests/SQLiteTests/QueryTests.swift
index f225adb4..ad0f3b25 100644
--- a/Tests/SQLiteTests/QueryTests.swift
+++ b/Tests/SQLiteTests/QueryTests.swift
@@ -391,18 +391,25 @@ class QueryTests: XCTestCase {
         let value = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4,
                                 date: Date(timeIntervalSince1970: 0), optional: nil, sub: value1)
         let update = try emails.update(value)
-        
+
         // NOTE: As Linux JSON decoding doesn't order keys the same way, we need to check prefix, suffix,
         // and extract JSON to decode it and check the decoded object.
-        
-        let expectedPrefix = "UPDATE \"emails\" SET \"int\" = 1, \"string\" = '2', \"bool\" = 1, \"float\" = 3.0, \"double\" = 4.0, \"date\" = '1970-01-01T00:00:00.000', \"sub\" = '"
+
+        let expectedPrefix =
+            """
+            UPDATE \"emails\" SET \"int\" = 1, \"string\" = '2', \"bool\" = 1, \"float\" = 3.0, \"double\" = 4.0,
+            \"date\" = '1970-01-01T00:00:00.000', \"sub\" = '
+            """.replacingOccurrences(of: "\n", with: "")
         let expectedSuffix = "'"
-        
+
         let sql = update.asSQL()
         XCTAssert(sql.hasPrefix(expectedPrefix))
         XCTAssert(sql.hasSuffix(expectedSuffix))
-        
-        let extractedJSON = String(sql[sql.index(sql.startIndex, offsetBy: expectedPrefix.count) ..< sql.index(sql.endIndex, offsetBy: -expectedSuffix.count)])
+
+        let extractedJSON = String(sql[
+            sql.index(sql.startIndex, offsetBy: expectedPrefix.count) ..<
+            sql.index(sql.endIndex, offsetBy: -expectedSuffix.count)
+        ])
         let decodedJSON = try JSONDecoder().decode(TestCodable.self, from: extractedJSON.data(using: .utf8)!)
         XCTAssertEqual(decodedJSON, value1)
     }
diff --git a/Tests/SQLiteTests/TestHelpers.swift b/Tests/SQLiteTests/TestHelpers.swift
index 53dabe79..4d8f78fa 100644
--- a/Tests/SQLiteTests/TestHelpers.swift
+++ b/Tests/SQLiteTests/TestHelpers.swift
@@ -125,7 +125,7 @@ class TestCodable: Codable, Equatable {
         self.optional = optional
         self.sub = sub
     }
-    
+
     static func == (lhs: TestCodable, rhs: TestCodable) -> Bool {
         lhs.int == rhs.int &&
         lhs.string == rhs.string &&

From 85921d83ce4921201a6d4106bc609f277253d4d8 Mon Sep 17 00:00:00 2001
From: Nathan Fallet <contact@nathanfallet.me>
Date: Tue, 25 Jan 2022 15:05:28 +0100
Subject: [PATCH 111/326] Oups

---
 Tests/SQLiteTests/QueryTests.swift | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Tests/SQLiteTests/QueryTests.swift b/Tests/SQLiteTests/QueryTests.swift
index ad0f3b25..f4942b88 100644
--- a/Tests/SQLiteTests/QueryTests.swift
+++ b/Tests/SQLiteTests/QueryTests.swift
@@ -398,7 +398,7 @@ class QueryTests: XCTestCase {
         let expectedPrefix =
             """
             UPDATE \"emails\" SET \"int\" = 1, \"string\" = '2', \"bool\" = 1, \"float\" = 3.0, \"double\" = 4.0,
-            \"date\" = '1970-01-01T00:00:00.000', \"sub\" = '
+             \"date\" = '1970-01-01T00:00:00.000', \"sub\" = '
             """.replacingOccurrences(of: "\n", with: "")
         let expectedSuffix = "'"
 

From 608684aeea5b6a5c5e8b87866600447c412d3b2e Mon Sep 17 00:00:00 2001
From: Nathan Fallet <contact@nathanfallet.me>
Date: Tue, 25 Jan 2022 15:19:40 +0100
Subject: [PATCH 112/326] Updating documentation

---
 Documentation/Index.md | 14 ++------------
 1 file changed, 2 insertions(+), 12 deletions(-)

diff --git a/Documentation/Index.md b/Documentation/Index.md
index 061b448b..4b4f45db 100644
--- a/Documentation/Index.md
+++ b/Documentation/Index.md
@@ -1457,21 +1457,11 @@ try db.run(users.drop(ifExists: true))
 
 ### Migrations and Schema Versioning
 
-You can add a convenience property on `Connection` to query and set the
+You can use the convenience property on `Connection` to query and set the
 [`PRAGMA user_version`](https://sqlite.org/pragma.html#pragma_user_version).
 
 This is a great way to manage your schema’s version over migrations.
-
-```swift
-extension Connection {
-    public var userVersion: Int32 {
-        get { return Int32(try! scalar("PRAGMA user_version") as! Int64)}
-        set { try! run("PRAGMA user_version = \(newValue)") }
-    }
-}
-```
-
-Then you can conditionally run your migrations along the lines of:
+You can conditionally run your migrations along the lines of:
 
 ```swift
 if db.userVersion == 0 {

From 946f69e79c2503d2dff58813d1abf78280205fa0 Mon Sep 17 00:00:00 2001
From: Nathan Fallet <contact@nathanfallet.me>
Date: Tue, 25 Jan 2022 15:31:17 +0100
Subject: [PATCH 113/326] Bump version number

---
 CHANGELOG.md                     |  9 +++++++++
 Documentation/Index.md           | 12 ++++++------
 Documentation/Planning.md        |  2 +-
 README.md                        |  6 +++---
 SQLite.swift.podspec             |  2 +-
 SQLite.xcodeproj/project.pbxproj |  4 ++--
 6 files changed, 22 insertions(+), 13 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index d89e33f4..11d0218e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,9 @@
+0.13.2 (25-01-2022), [diff][diff-0.13.2]
+========================================
+
+* Closing bracket position ([#1100][])
+* Native user_version support in Connection ([#1105][])
+
 0.13.1 (17-11-2021), [diff][diff-0.13.1]
 ========================================
 
@@ -95,6 +101,7 @@
 [diff-0.12.2]: https://github.com/stephencelis/SQLite.swift/compare/0.12.0...0.12.2
 [diff-0.13.0]: https://github.com/stephencelis/SQLite.swift/compare/0.12.2...0.13.0
 [diff-0.13.1]: https://github.com/stephencelis/SQLite.swift/compare/0.13.0...0.13.1
+[diff-0.13.2]: https://github.com/stephencelis/SQLite.swift/compare/0.13.1...0.13.2
 
 [#142]: https://github.com/stephencelis/SQLite.swift/issues/142
 [#315]: https://github.com/stephencelis/SQLite.swift/issues/315
@@ -133,3 +140,5 @@
 [#1077]: https://github.com/stephencelis/SQLite.swift/issues/1077
 [#1094]: https://github.com/stephencelis/SQLite.swift/pull/1094
 [#1095]: https://github.com/stephencelis/SQLite.swift/pull/1095
+[#1100]: https://github.com/stephencelis/SQLite.swift/pull/1100
+[#1105]: https://github.com/stephencelis/SQLite.swift/pull/1105
diff --git a/Documentation/Index.md b/Documentation/Index.md
index 4b4f45db..52affae4 100644
--- a/Documentation/Index.md
+++ b/Documentation/Index.md
@@ -81,7 +81,7 @@ process of downloading, compiling, and linking dependencies.
 
   ```swift
   dependencies: [
-    .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.13.1")
+    .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.13.2")
   ]
   ```
 
@@ -102,7 +102,7 @@ install SQLite.swift with Carthage:
  2. Update your Cartfile to include the following:
 
     ```ruby
-    github "stephencelis/SQLite.swift" ~> 0.13.1
+    github "stephencelis/SQLite.swift" ~> 0.13.2
     ```
 
  3. Run `carthage update` and [add the appropriate framework][Carthage Usage].
@@ -132,7 +132,7 @@ install SQLite.swift with Carthage:
     use_frameworks!
 
     target 'YourAppTargetName' do
-        pod 'SQLite.swift', '~> 0.13.1'
+        pod 'SQLite.swift', '~> 0.13.2'
     end
     ```
 
@@ -146,7 +146,7 @@ with the OS you can require the `standalone` subspec:
 
 ```ruby
 target 'YourAppTargetName' do
-  pod 'SQLite.swift/standalone', '~> 0.13.1'
+  pod 'SQLite.swift/standalone', '~> 0.13.2'
 end
 ```
 
@@ -156,7 +156,7 @@ dependency to sqlite3 or one of its subspecs:
 
 ```ruby
 target 'YourAppTargetName' do
-  pod 'SQLite.swift/standalone', '~> 0.13.1'
+  pod 'SQLite.swift/standalone', '~> 0.13.2'
   pod 'sqlite3/fts5', '= 3.15.0'  # SQLite 3.15.0 with FTS5 enabled
 end
 ```
@@ -172,7 +172,7 @@ If you want to use [SQLCipher][] with SQLite.swift you can require the
 target 'YourAppTargetName' do
   # Make sure you only require the subspec, otherwise you app might link against
   # the system SQLite, which means the SQLCipher-specific methods won't work.
-  pod 'SQLite.swift/SQLCipher', '~> 0.13.1'
+  pod 'SQLite.swift/SQLCipher', '~> 0.13.2'
 end
 ```
 
diff --git a/Documentation/Planning.md b/Documentation/Planning.md
index bebfb503..5b50219a 100644
--- a/Documentation/Planning.md
+++ b/Documentation/Planning.md
@@ -6,7 +6,7 @@ additions and Pull Requests, as well as to keep the Issues list clear of
 enhancement requests so that bugs are more visible.
 
 > ⚠ This document is currently not actively maintained. See
-> the [0.13.2 milestone](https://github.com/stephencelis/SQLite.swift/issues?q=is%3Aopen+is%3Aissue+milestone%3A0.13.2)
+> the [0.13.3 milestone](https://github.com/stephencelis/SQLite.swift/issues?q=is%3Aopen+is%3Aissue+milestone%3A0.13.3)
 > on Github for additional information about planned features for the next release.
 
 ## Roadmap
diff --git a/README.md b/README.md
index bc8eac31..a2b5e537 100644
--- a/README.md
+++ b/README.md
@@ -136,7 +136,7 @@ Swift code.
 
   ```swift
   dependencies: [
-      .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.13.1")
+      .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.13.2")
   ]
   ```
 
@@ -160,7 +160,7 @@ install SQLite.swift with Carthage:
  2. Update your Cartfile to include the following:
 
     ```ruby
-    github "stephencelis/SQLite.swift" ~> 0.13.1
+    github "stephencelis/SQLite.swift" ~> 0.13.2
     ```
 
  3. Run `carthage update` and
@@ -191,7 +191,7 @@ SQLite.swift with CocoaPods:
     use_frameworks!
 
     target 'YourAppTargetName' do
-        pod 'SQLite.swift', '~> 0.13.1'
+        pod 'SQLite.swift', '~> 0.13.2'
     end
     ```
 
diff --git a/SQLite.swift.podspec b/SQLite.swift.podspec
index 90a8a348..23146eee 100644
--- a/SQLite.swift.podspec
+++ b/SQLite.swift.podspec
@@ -1,6 +1,6 @@
 Pod::Spec.new do |s|
   s.name             = "SQLite.swift"
-  s.version          = "0.13.1"
+  s.version          = "0.13.2"
   s.summary          = "A type-safe, Swift-language layer over SQLite3."
 
   s.description      = <<-DESC
diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj
index aee41218..26c5e975 100644
--- a/SQLite.xcodeproj/project.pbxproj
+++ b/SQLite.xcodeproj/project.pbxproj
@@ -1333,7 +1333,7 @@
 				INFOPLIST_FILE = "$(SRCROOT)/Sources/SQLite/Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
-				MARKETING_VERSION = 0.13.1;
+				MARKETING_VERSION = 0.13.2;
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
 				PRODUCT_NAME = SQLite;
 				SKIP_INSTALL = YES;
@@ -1356,7 +1356,7 @@
 				INFOPLIST_FILE = "$(SRCROOT)/Sources/SQLite/Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
-				MARKETING_VERSION = 0.13.1;
+				MARKETING_VERSION = 0.13.2;
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
 				PRODUCT_NAME = SQLite;
 				SKIP_INSTALL = YES;

From 5f5ad81ac0d0a0f3e56e39e646e8423c617df523 Mon Sep 17 00:00:00 2001
From: Nathan Fallet <contact@nathanfallet.me>
Date: Tue, 25 Jan 2022 16:57:56 +0100
Subject: [PATCH 114/326] Reset userVersion if set to nil

---
 Sources/SQLite/Core/Connection.swift | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/Sources/SQLite/Core/Connection.swift b/Sources/SQLite/Core/Connection.swift
index 043cfafc..72e8d857 100644
--- a/Sources/SQLite/Core/Connection.swift
+++ b/Sources/SQLite/Core/Connection.swift
@@ -159,9 +159,7 @@ public final class Connection {
             (try? scalar("PRAGMA user_version") as? Int64).map(Int32.init)
         }
         set {
-            if let userVersion = newValue {
-                _ = try? run("PRAGMA user_version = \(userVersion)")
-            }
+            _ = try? run("PRAGMA user_version = \(newValue ?? 0)")
         }
     }
 

From 52e3ea84895004f81d647ab8582564d1e7ab3c73 Mon Sep 17 00:00:00 2001
From: Amy While <26681721+elihwyma@users.noreply.github.com>
Date: Sun, 6 Feb 2022 17:59:33 +0000
Subject: [PATCH 115/326] Add a Value conformance for Foundation NSURL

---
 Sources/SQLite/Foundation.swift | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/Sources/SQLite/Foundation.swift b/Sources/SQLite/Foundation.swift
index 2acbc00e..c1a4c501 100644
--- a/Sources/SQLite/Foundation.swift
+++ b/Sources/SQLite/Foundation.swift
@@ -84,3 +84,20 @@ extension UUID: Value {
     }
 
 }
+
+extension URL: Value {
+
+    public static var declaredDatatype: String {
+        String.declaredDatatype
+    }
+
+    public static func fromDatatypeValue(_ stringValue: String) -> URL {
+        URL(string: stringValue)!
+    }
+
+    public var datatypeValue: String {
+        absoluteString
+    }
+
+}
+

From 3eb1fdceda903ea3b7ba0f49a522258578a168d5 Mon Sep 17 00:00:00 2001
From: Amy While <26681721+elihwyma@users.noreply.github.com>
Date: Sat, 12 Feb 2022 13:13:42 +0000
Subject: [PATCH 116/326] Remove Extra Trailing New Line

---
 Sources/SQLite/Foundation.swift | 1 -
 1 file changed, 1 deletion(-)

diff --git a/Sources/SQLite/Foundation.swift b/Sources/SQLite/Foundation.swift
index c1a4c501..44a31736 100644
--- a/Sources/SQLite/Foundation.swift
+++ b/Sources/SQLite/Foundation.swift
@@ -100,4 +100,3 @@ extension URL: Value {
     }
 
 }
-

From da8d7b3100471349ac924e15b1351fcd17b83ade Mon Sep 17 00:00:00 2001
From: Benjamin Lewis <benrlewis@gmail.com>
Date: Fri, 18 Feb 2022 13:45:40 -0600
Subject: [PATCH 117/326] UUID Fix

---
 Sources/SQLite/Typed/Coding.swift | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/Sources/SQLite/Typed/Coding.swift b/Sources/SQLite/Typed/Coding.swift
index 3dc1e6cf..a8608579 100644
--- a/Sources/SQLite/Typed/Coding.swift
+++ b/Sources/SQLite/Typed/Coding.swift
@@ -207,6 +207,8 @@ private class SQLiteEncoder: Encoder {
                 encoder.setters.append(Expression(key.stringValue) <- data)
             } else if let date = value as? Date {
                 encoder.setters.append(Expression(key.stringValue) <- date.datatypeValue)
+            }else if let uuid = value as? UUID {
+                encoder.setters.append(Expression(key.stringValue) <- uuid.uuidString)
             } else {
                 let encoded = try JSONEncoder().encode(value)
                 let string = String(data: encoded, encoding: .utf8)

From b71549364be768397d09a91707867d2175f6261e Mon Sep 17 00:00:00 2001
From: Benjamin Lewis <benrlewis@gmail.com>
Date: Sat, 19 Feb 2022 10:52:13 -0600
Subject: [PATCH 118/326] Lint Fix and added test

---
 Sources/SQLite/Typed/Coding.swift       |  2 +-
 Tests/SQLiteTests/FoundationTests.swift | 26 +++++++++++++++++++++++++
 Tests/SQLiteTests/TestHelpers.swift     |  3 +++
 3 files changed, 30 insertions(+), 1 deletion(-)

diff --git a/Sources/SQLite/Typed/Coding.swift b/Sources/SQLite/Typed/Coding.swift
index a8608579..fa9ec02d 100644
--- a/Sources/SQLite/Typed/Coding.swift
+++ b/Sources/SQLite/Typed/Coding.swift
@@ -207,7 +207,7 @@ private class SQLiteEncoder: Encoder {
                 encoder.setters.append(Expression(key.stringValue) <- data)
             } else if let date = value as? Date {
                 encoder.setters.append(Expression(key.stringValue) <- date.datatypeValue)
-            }else if let uuid = value as? UUID {
+            } else if let uuid = value as? UUID {
                 encoder.setters.append(Expression(key.stringValue) <- uuid.uuidString)
             } else {
                 let encoded = try JSONEncoder().encode(value)
diff --git a/Tests/SQLiteTests/FoundationTests.swift b/Tests/SQLiteTests/FoundationTests.swift
index cef485fc..23ae1fe2 100644
--- a/Tests/SQLiteTests/FoundationTests.swift
+++ b/Tests/SQLiteTests/FoundationTests.swift
@@ -25,4 +25,30 @@ class FoundationTests: XCTestCase {
         let uuid = UUID.fromDatatypeValue(string)
         XCTAssertEqual(UUID(uuidString: "4ABE10C9-FF12-4CD4-90C1-4B429001BAD3"), uuid)
     }
+    
+    func testUUIDInsert() {
+        struct Test:Codable{
+            var uuid:UUID
+            var string:String
+        }
+        let testUUID = UUID()
+        let testValue = Test(uuid: testUUID, string: "value")
+        let db = try! Connection(.temporary)
+        try! db.run(table.create { t in
+            t.column(uuid)
+            t.column(string)
+        }
+        )
+        
+        let iQuery = try! table.insert(testValue)
+        try! db.run(iQuery)
+        
+        let fQuery = table.filter(uuid == testUUID)
+        if let result = try! db.pluck(fQuery){
+            let testValueReturned = Test(uuid: result[uuid], string: result[string])
+            XCTAssertEqual(testUUID, testValueReturned.uuid)
+        }else{
+            XCTFail()
+        }
+    }
 }
diff --git a/Tests/SQLiteTests/TestHelpers.swift b/Tests/SQLiteTests/TestHelpers.swift
index 4d8f78fa..a6efa2e7 100644
--- a/Tests/SQLiteTests/TestHelpers.swift
+++ b/Tests/SQLiteTests/TestHelpers.swift
@@ -95,6 +95,9 @@ let int64Optional = Expression<Int64?>("int64Optional")
 let string = Expression<String>("string")
 let stringOptional = Expression<String?>("stringOptional")
 
+let uuid = Expression<UUID>("uuid")
+let uuidOptional = Expression<UUID?>("uuidOptional")
+
 func assertSQL(_ expression1: @autoclosure () -> String, _ expression2: @autoclosure () -> Expressible,
                file: StaticString = #file, line: UInt = #line) {
     XCTAssertEqual(expression1(), expression2().asSQL(), file: file, line: line)

From 83f0f5de5bd978e5c41e45331154572a41204fe9 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Sun, 20 Feb 2022 23:46:24 +0100
Subject: [PATCH 119/326] Bump iOS version

---
 .github/workflows/build.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index baa8c2d8..d1659d9d 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -2,7 +2,7 @@ name: Build and test
 on: [push, pull_request]
 env:
   IOS_SIMULATOR: iPhone 12
-  IOS_VERSION: "15.0"
+  IOS_VERSION: "15.2"
 jobs:
   build:
     runs-on: macos-11

From c4483141f19a4ba796fd0475e279d4db1353000e Mon Sep 17 00:00:00 2001
From: Benjamin Lewis <benrlewis@gmail.com>
Date: Mon, 21 Feb 2022 19:33:15 -0600
Subject: [PATCH 120/326] Fixed linting issues.

---
 Tests/SQLiteTests/FoundationTests.swift | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/Tests/SQLiteTests/FoundationTests.swift b/Tests/SQLiteTests/FoundationTests.swift
index 23ae1fe2..02ab7f13 100644
--- a/Tests/SQLiteTests/FoundationTests.swift
+++ b/Tests/SQLiteTests/FoundationTests.swift
@@ -25,11 +25,11 @@ class FoundationTests: XCTestCase {
         let uuid = UUID.fromDatatypeValue(string)
         XCTAssertEqual(UUID(uuidString: "4ABE10C9-FF12-4CD4-90C1-4B429001BAD3"), uuid)
     }
-    
+
     func testUUIDInsert() {
-        struct Test:Codable{
-            var uuid:UUID
-            var string:String
+        struct Test: Codable {
+            var uuid: UUID
+            var string: String
         }
         let testUUID = UUID()
         let testValue = Test(uuid: testUUID, string: "value")
@@ -39,16 +39,16 @@ class FoundationTests: XCTestCase {
             t.column(string)
         }
         )
-        
+
         let iQuery = try! table.insert(testValue)
         try! db.run(iQuery)
-        
+
         let fQuery = table.filter(uuid == testUUID)
-        if let result = try! db.pluck(fQuery){
+        if let result = try! db.pluck(fQuery) {
             let testValueReturned = Test(uuid: result[uuid], string: result[string])
             XCTAssertEqual(testUUID, testValueReturned.uuid)
-        }else{
-            XCTFail()
+        } else {
+            XCTFail("Search for uuid failed")
         }
     }
 }

From 09e8424805b9ea02b96c6973fe2720c3caa4490f Mon Sep 17 00:00:00 2001
From: Benjamin Lewis <benrlewis@gmail.com>
Date: Tue, 22 Feb 2022 06:40:20 -0600
Subject: [PATCH 121/326] used UUID.datatypeValue and moved tests

---
 Sources/SQLite/Typed/Coding.swift       |  2 +-
 Tests/SQLiteTests/FoundationTests.swift | 25 ------------------------
 Tests/SQLiteTests/QueryTests.swift      | 26 +++++++++++++++++++++++++
 3 files changed, 27 insertions(+), 26 deletions(-)

diff --git a/Sources/SQLite/Typed/Coding.swift b/Sources/SQLite/Typed/Coding.swift
index fa9ec02d..cea2565a 100644
--- a/Sources/SQLite/Typed/Coding.swift
+++ b/Sources/SQLite/Typed/Coding.swift
@@ -208,7 +208,7 @@ private class SQLiteEncoder: Encoder {
             } else if let date = value as? Date {
                 encoder.setters.append(Expression(key.stringValue) <- date.datatypeValue)
             } else if let uuid = value as? UUID {
-                encoder.setters.append(Expression(key.stringValue) <- uuid.uuidString)
+                encoder.setters.append(Expression(key.stringValue) <- uuid.datatypeValue)
             } else {
                 let encoded = try JSONEncoder().encode(value)
                 let string = String(data: encoded, encoding: .utf8)
diff --git a/Tests/SQLiteTests/FoundationTests.swift b/Tests/SQLiteTests/FoundationTests.swift
index 02ab7f13..075e755b 100644
--- a/Tests/SQLiteTests/FoundationTests.swift
+++ b/Tests/SQLiteTests/FoundationTests.swift
@@ -26,29 +26,4 @@ class FoundationTests: XCTestCase {
         XCTAssertEqual(UUID(uuidString: "4ABE10C9-FF12-4CD4-90C1-4B429001BAD3"), uuid)
     }
 
-    func testUUIDInsert() {
-        struct Test: Codable {
-            var uuid: UUID
-            var string: String
-        }
-        let testUUID = UUID()
-        let testValue = Test(uuid: testUUID, string: "value")
-        let db = try! Connection(.temporary)
-        try! db.run(table.create { t in
-            t.column(uuid)
-            t.column(string)
-        }
-        )
-
-        let iQuery = try! table.insert(testValue)
-        try! db.run(iQuery)
-
-        let fQuery = table.filter(uuid == testUUID)
-        if let result = try! db.pluck(fQuery) {
-            let testValueReturned = Test(uuid: result[uuid], string: result[string])
-            XCTAssertEqual(testUUID, testValueReturned.uuid)
-        } else {
-            XCTFail("Search for uuid failed")
-        }
-    }
 }
diff --git a/Tests/SQLiteTests/QueryTests.swift b/Tests/SQLiteTests/QueryTests.swift
index f4942b88..efcbab94 100644
--- a/Tests/SQLiteTests/QueryTests.swift
+++ b/Tests/SQLiteTests/QueryTests.swift
@@ -310,6 +310,32 @@ class QueryTests: XCTestCase {
     }
     #endif
 
+    func test_insert_and_search_for_UUID() {
+        struct Test: Codable {
+            var uuid: UUID
+            var string: String
+        }
+        let testUUID = UUID()
+        let testValue = Test(uuid: testUUID, string: "value")
+        let db = try! Connection(.temporary)
+        try! db.run(table.create { t in
+            t.column(uuid)
+            t.column(string)
+        }
+        )
+
+        let iQuery = try! table.insert(testValue)
+        try! db.run(iQuery)
+
+        let fQuery = table.filter(uuid == testUUID)
+        if let result = try! db.pluck(fQuery) {
+            let testValueReturned = Test(uuid: result[uuid], string: result[string])
+            XCTAssertEqual(testUUID, testValueReturned.uuid)
+        } else {
+            XCTFail("Search for uuid failed")
+        }
+    }
+
     func test_upsert_withOnConflict_compilesInsertOrOnConflictExpression() {
         assertSQL(
             """

From f1ab605ad2056944eee80cb7cf55f5b94f17e74e Mon Sep 17 00:00:00 2001
From: Justin Meiners <justinm@passivelogic.com>
Date: Thu, 24 Feb 2022 09:49:20 -0700
Subject: [PATCH 122/326] improve performance of prefixed column resolution

- Previously this allocated arrays every time it was accessed.
It will now only allocate in one of the error cases.
- It also keeps track of the columnName index for both key and
value so it doesn't need to look it up twice.
---
 Sources/SQLite/Typed/Query.swift | 25 +++++++++++++++++--------
 1 file changed, 17 insertions(+), 8 deletions(-)

diff --git a/Sources/SQLite/Typed/Query.swift b/Sources/SQLite/Typed/Query.swift
index 2d88db63..ecb501af 100644
--- a/Sources/SQLite/Typed/Query.swift
+++ b/Sources/SQLite/Typed/Query.swift
@@ -1169,16 +1169,25 @@ public struct Row {
         }
 
         guard let idx = columnNames[column.template] else {
-            let similar = Array(columnNames.keys).filter { $0.hasSuffix(".\(column.template)") }
-
-            switch similar.count {
-            case 0:
+            func match(_ s: String) -> Bool {
+                return s.hasSuffix(".\(column.template)")
+            }
+            
+            guard let firstIndex = columnNames.firstIndex(where: { match($0.key) }) else {
                 throw QueryError.noSuchColumn(name: column.template, columns: columnNames.keys.sorted())
-            case 1:
-                return valueAtIndex(columnNames[similar[0]]!)
-            default:
-                throw QueryError.ambiguousColumn(name: column.template, similar: similar)
             }
+            
+            let secondIndex = columnNames
+                .suffix(from: columnNames.index(after: firstIndex))
+                .firstIndex(where: { match($0.key) })
+            
+            guard secondIndex == nil else {
+                throw QueryError.ambiguousColumn(
+                    name: column.template,
+                    similar: columnNames.keys.filter(match).sorted()
+                )
+            }
+            return valueAtIndex(columnNames[firstIndex].value)
         }
 
         return valueAtIndex(idx)

From 55bf2c10e10b84855ac3a26c10dc601271c6d171 Mon Sep 17 00:00:00 2001
From: Justin Meiners <justinm@passivelogic.com>
Date: Thu, 24 Feb 2022 09:52:56 -0700
Subject: [PATCH 123/326] rename match -> similar to follow convention

---
 Sources/SQLite/Typed/Query.swift | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/Sources/SQLite/Typed/Query.swift b/Sources/SQLite/Typed/Query.swift
index ecb501af..6dd07ac4 100644
--- a/Sources/SQLite/Typed/Query.swift
+++ b/Sources/SQLite/Typed/Query.swift
@@ -1169,22 +1169,22 @@ public struct Row {
         }
 
         guard let idx = columnNames[column.template] else {
-            func match(_ s: String) -> Bool {
+            func similar(_ s: String) -> Bool {
                 return s.hasSuffix(".\(column.template)")
             }
             
-            guard let firstIndex = columnNames.firstIndex(where: { match($0.key) }) else {
+            guard let firstIndex = columnNames.firstIndex(where: { similar($0.key) }) else {
                 throw QueryError.noSuchColumn(name: column.template, columns: columnNames.keys.sorted())
             }
             
             let secondIndex = columnNames
                 .suffix(from: columnNames.index(after: firstIndex))
-                .firstIndex(where: { match($0.key) })
+                .firstIndex(where: { similar($0.key) })
             
             guard secondIndex == nil else {
                 throw QueryError.ambiguousColumn(
                     name: column.template,
-                    similar: columnNames.keys.filter(match).sorted()
+                    similar: columnNames.keys.filter(similar).sorted()
                 )
             }
             return valueAtIndex(columnNames[firstIndex].value)

From eecb3cb4615534020bf96e32ad2b3a95c91504d6 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Thu, 24 Feb 2022 20:14:01 +0100
Subject: [PATCH 124/326] Adding PR template

---
 .github/PULL_REQUEST_TEMPLATE/template.md | 9 +++++++++
 1 file changed, 9 insertions(+)
 create mode 100644 .github/PULL_REQUEST_TEMPLATE/template.md

diff --git a/.github/PULL_REQUEST_TEMPLATE/template.md b/.github/PULL_REQUEST_TEMPLATE/template.md
new file mode 100644
index 00000000..b74ffd8d
--- /dev/null
+++ b/.github/PULL_REQUEST_TEMPLATE/template.md
@@ -0,0 +1,9 @@
+Thanks for taking the time to submit a pull request.
+
+Before submitting, please do the following:
+
+- Run `make lint` to check if there are any format errors (install [swiftlint](https://github.com/realm/SwiftLint#installation) first)
+- Run `swift test` to see if the tests pass.
+- Write new tests for new functionality.
+- Update documentation comments where applicable.
+

From 3614f4e317c5969c5fe102cee1f3b3f4e1294185 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Thu, 24 Feb 2022 20:24:24 +0100
Subject: [PATCH 125/326] Rename

---
 .../template.md => pull_request_template.md}                      | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 rename .github/{PULL_REQUEST_TEMPLATE/template.md => pull_request_template.md} (100%)

diff --git a/.github/PULL_REQUEST_TEMPLATE/template.md b/.github/pull_request_template.md
similarity index 100%
rename from .github/PULL_REQUEST_TEMPLATE/template.md
rename to .github/pull_request_template.md

From c897ce99454216dc33caae5d57a2cd021b672e03 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Thu, 24 Feb 2022 20:26:45 +0100
Subject: [PATCH 126/326] rename

---
 .github/{ISSUE_TEMPLATE/template.md => issue_template.md} | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
 rename .github/{ISSUE_TEMPLATE/template.md => issue_template.md} (87%)

diff --git a/.github/ISSUE_TEMPLATE/template.md b/.github/issue_template.md
similarity index 87%
rename from .github/ISSUE_TEMPLATE/template.md
rename to .github/issue_template.md
index 9304a672..f9622b9b 100644
--- a/.github/ISSUE_TEMPLATE/template.md
+++ b/.github/issue_template.md
@@ -1,5 +1,5 @@
 > Issues are used to track bugs and feature requests.
-> Need  help or have a general question? Ask on Stack Overflow (tag sqlite.swift).
+> Need help or have a general question? Ask on Stack Overflow (tag sqlite.swift).
 
 ## Build Information
 

From c659dc9256f1b80505d0bd5bbca026005cd43af2 Mon Sep 17 00:00:00 2001
From: Justin Meiners <justinm@passivelogic.com>
Date: Thu, 24 Feb 2022 13:46:25 -0700
Subject: [PATCH 127/326] Fix lint issues

---
 Sources/SQLite/Typed/Query.swift | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/Sources/SQLite/Typed/Query.swift b/Sources/SQLite/Typed/Query.swift
index 6dd07ac4..93dc2a73 100644
--- a/Sources/SQLite/Typed/Query.swift
+++ b/Sources/SQLite/Typed/Query.swift
@@ -1169,18 +1169,18 @@ public struct Row {
         }
 
         guard let idx = columnNames[column.template] else {
-            func similar(_ s: String) -> Bool {
-                return s.hasSuffix(".\(column.template)")
+            func similar(_ name: String) -> Bool {
+                return name.hasSuffix(".\(column.template)")
             }
-            
+
             guard let firstIndex = columnNames.firstIndex(where: { similar($0.key) }) else {
                 throw QueryError.noSuchColumn(name: column.template, columns: columnNames.keys.sorted())
             }
-            
+
             let secondIndex = columnNames
                 .suffix(from: columnNames.index(after: firstIndex))
                 .firstIndex(where: { similar($0.key) })
-            
+
             guard secondIndex == nil else {
                 throw QueryError.ambiguousColumn(
                     name: column.template,

From ef2257a885a875868177b2a004a839eb4f21a461 Mon Sep 17 00:00:00 2001
From: JIm Boyd <jimb@cabosoft.com>
Date: Thu, 17 Mar 2022 11:09:33 -0600
Subject: [PATCH 128/326] Add prepareRowIterator method to an extension of
 Statement.

---
 Sources/SQLite/Core/Statement.swift    | 15 +++++++++++++++
 Tests/SQLiteTests/StatementTests.swift | 11 +++++++++++
 2 files changed, 26 insertions(+)

diff --git a/Sources/SQLite/Core/Statement.swift b/Sources/SQLite/Core/Statement.swift
index 1e2489b5..8e9ed350 100644
--- a/Sources/SQLite/Core/Statement.swift
+++ b/Sources/SQLite/Core/Statement.swift
@@ -228,6 +228,21 @@ extension Statement: FailableIterator {
     }
 }
 
+extension Statement {
+    public func prepareRowIterator() -> RowIterator {
+        return RowIterator(statement: self, columnNames: self.columnNameMap)
+    }
+    
+    var columnNameMap: [String: Int] {
+        var result = [String: Int]()
+        for (index, name) in self.columnNames.enumerated() {
+            result[name.quote()] = index
+        }
+
+        return result
+    }
+}
+
 extension Statement: CustomStringConvertible {
 
     public var description: String {
diff --git a/Tests/SQLiteTests/StatementTests.swift b/Tests/SQLiteTests/StatementTests.swift
index b4ad7c28..7729a4a9 100644
--- a/Tests/SQLiteTests/StatementTests.swift
+++ b/Tests/SQLiteTests/StatementTests.swift
@@ -23,4 +23,15 @@ class StatementTests: SQLiteTestCase {
         let blobValue = try! db.scalar(blobs.select(blobColumn).limit(1, offset: 0))
         XCTAssertEqual([], blobValue.bytes)
     }
+    
+    func test_prepareRowIterator() {
+        let names = ["a", "b", "c"]
+        try! insertUsers(names)
+
+        let emailColumn = Expression<String>("email")
+        let statement = try! db.prepare("SELECT email FROM users")
+        let emails = try! statement.prepareRowIterator().map { $0[emailColumn] }
+
+        XCTAssertEqual(names.map({ "\($0)@example.com" }), emails.sorted())
+    }
 }

From 1a50a03fe0a216c75bb27cab7a4f2965669e5e30 Mon Sep 17 00:00:00 2001
From: JIm Boyd <jimb@cabosoft.com>
Date: Thu, 17 Mar 2022 11:22:55 -0600
Subject: [PATCH 129/326] Updates to documentation

---
 Documentation/Index.md | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/Documentation/Index.md b/Documentation/Index.md
index 52affae4..b58bf995 100644
--- a/Documentation/Index.md
+++ b/Documentation/Index.md
@@ -1963,6 +1963,14 @@ using the following functions.
         }
     }
     ```
+    Statements with results may be iterated over, using a `RowIterator` if
+    useful.
+    
+    ```swift
+    let emailColumn = Expression<String>("email")
+    let stmt = try db.prepare("SELECT id, email FROM users")
+    let emails = try! stmt.prepareRowIterator().map { $0[emailColumn] }
+    ```
 
   - `run` prepares a single `Statement` object from a SQL string, optionally
     binds values to it (using the statement’s `bind` function), executes,

From 4459338f778d49445bdfd182e40e9cd90220f8c7 Mon Sep 17 00:00:00 2001
From: JIm Boyd <jimb@cabosoft.com>
Date: Thu, 17 Mar 2022 13:43:06 -0600
Subject: [PATCH 130/326] Fix linting issues

---
 Sources/SQLite/Core/Statement.swift    | 2 +-
 Tests/SQLiteTests/StatementTests.swift | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/Sources/SQLite/Core/Statement.swift b/Sources/SQLite/Core/Statement.swift
index 8e9ed350..5ec430cf 100644
--- a/Sources/SQLite/Core/Statement.swift
+++ b/Sources/SQLite/Core/Statement.swift
@@ -232,7 +232,7 @@ extension Statement {
     public func prepareRowIterator() -> RowIterator {
         return RowIterator(statement: self, columnNames: self.columnNameMap)
     }
-    
+
     var columnNameMap: [String: Int] {
         var result = [String: Int]()
         for (index, name) in self.columnNames.enumerated() {
diff --git a/Tests/SQLiteTests/StatementTests.swift b/Tests/SQLiteTests/StatementTests.swift
index 7729a4a9..aa05de34 100644
--- a/Tests/SQLiteTests/StatementTests.swift
+++ b/Tests/SQLiteTests/StatementTests.swift
@@ -23,7 +23,7 @@ class StatementTests: SQLiteTestCase {
         let blobValue = try! db.scalar(blobs.select(blobColumn).limit(1, offset: 0))
         XCTAssertEqual([], blobValue.bytes)
     }
-    
+
     func test_prepareRowIterator() {
         let names = ["a", "b", "c"]
         try! insertUsers(names)

From 5af5c858fff85bf7a880bd01bd06b010c91c7436 Mon Sep 17 00:00:00 2001
From: Nathan Fallet <contact@nathanfallet.me>
Date: Sun, 27 Mar 2022 12:37:45 +0200
Subject: [PATCH 131/326] Adding primary key support to column with references

---
 CHANGELOG.md                        |  9 +++++++++
 Documentation/Index.md              | 12 ++++++------
 Documentation/Planning.md           |  2 +-
 README.md                           |  6 +++---
 SQLite.swift.podspec                |  2 +-
 SQLite.xcodeproj/project.pbxproj    |  4 ++--
 Sources/SQLite/Typed/Schema.swift   | 20 ++++++++++++++++++++
 Tests/SQLiteTests/SchemaTests.swift | 27 +++++++++++++++++++++++++++
 8 files changed, 69 insertions(+), 13 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 11d0218e..9bf5682b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,9 @@
+0.13.3 (25-01-2022), [diff][diff-0.13.3]
+========================================
+
+* UUID Fix ([#1112][])
+* Adding primary key support to column with references ([#1121][])
+
 0.13.2 (25-01-2022), [diff][diff-0.13.2]
 ========================================
 
@@ -102,6 +108,7 @@
 [diff-0.13.0]: https://github.com/stephencelis/SQLite.swift/compare/0.12.2...0.13.0
 [diff-0.13.1]: https://github.com/stephencelis/SQLite.swift/compare/0.13.0...0.13.1
 [diff-0.13.2]: https://github.com/stephencelis/SQLite.swift/compare/0.13.1...0.13.2
+[diff-0.13.3]: https://github.com/stephencelis/SQLite.swift/compare/0.13.2...0.13.3
 
 [#142]: https://github.com/stephencelis/SQLite.swift/issues/142
 [#315]: https://github.com/stephencelis/SQLite.swift/issues/315
@@ -142,3 +149,5 @@
 [#1095]: https://github.com/stephencelis/SQLite.swift/pull/1095
 [#1100]: https://github.com/stephencelis/SQLite.swift/pull/1100
 [#1105]: https://github.com/stephencelis/SQLite.swift/pull/1105
+[#1112]: https://github.com/stephencelis/SQLite.swift/pull/1112
+[#1121]: https://github.com/stephencelis/SQLite.swift/pull/1121
diff --git a/Documentation/Index.md b/Documentation/Index.md
index 52affae4..987b0814 100644
--- a/Documentation/Index.md
+++ b/Documentation/Index.md
@@ -81,7 +81,7 @@ process of downloading, compiling, and linking dependencies.
 
   ```swift
   dependencies: [
-    .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.13.2")
+    .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.13.3")
   ]
   ```
 
@@ -102,7 +102,7 @@ install SQLite.swift with Carthage:
  2. Update your Cartfile to include the following:
 
     ```ruby
-    github "stephencelis/SQLite.swift" ~> 0.13.2
+    github "stephencelis/SQLite.swift" ~> 0.13.3
     ```
 
  3. Run `carthage update` and [add the appropriate framework][Carthage Usage].
@@ -132,7 +132,7 @@ install SQLite.swift with Carthage:
     use_frameworks!
 
     target 'YourAppTargetName' do
-        pod 'SQLite.swift', '~> 0.13.2'
+        pod 'SQLite.swift', '~> 0.13.3'
     end
     ```
 
@@ -146,7 +146,7 @@ with the OS you can require the `standalone` subspec:
 
 ```ruby
 target 'YourAppTargetName' do
-  pod 'SQLite.swift/standalone', '~> 0.13.2'
+  pod 'SQLite.swift/standalone', '~> 0.13.3'
 end
 ```
 
@@ -156,7 +156,7 @@ dependency to sqlite3 or one of its subspecs:
 
 ```ruby
 target 'YourAppTargetName' do
-  pod 'SQLite.swift/standalone', '~> 0.13.2'
+  pod 'SQLite.swift/standalone', '~> 0.13.3'
   pod 'sqlite3/fts5', '= 3.15.0'  # SQLite 3.15.0 with FTS5 enabled
 end
 ```
@@ -172,7 +172,7 @@ If you want to use [SQLCipher][] with SQLite.swift you can require the
 target 'YourAppTargetName' do
   # Make sure you only require the subspec, otherwise you app might link against
   # the system SQLite, which means the SQLCipher-specific methods won't work.
-  pod 'SQLite.swift/SQLCipher', '~> 0.13.2'
+  pod 'SQLite.swift/SQLCipher', '~> 0.13.3'
 end
 ```
 
diff --git a/Documentation/Planning.md b/Documentation/Planning.md
index 5b50219a..0dea6d71 100644
--- a/Documentation/Planning.md
+++ b/Documentation/Planning.md
@@ -6,7 +6,7 @@ additions and Pull Requests, as well as to keep the Issues list clear of
 enhancement requests so that bugs are more visible.
 
 > ⚠ This document is currently not actively maintained. See
-> the [0.13.3 milestone](https://github.com/stephencelis/SQLite.swift/issues?q=is%3Aopen+is%3Aissue+milestone%3A0.13.3)
+> the [0.13.4 milestone](https://github.com/stephencelis/SQLite.swift/issues?q=is%3Aopen+is%3Aissue+milestone%3A0.13.4)
 > on Github for additional information about planned features for the next release.
 
 ## Roadmap
diff --git a/README.md b/README.md
index a2b5e537..1b04c7c8 100644
--- a/README.md
+++ b/README.md
@@ -136,7 +136,7 @@ Swift code.
 
   ```swift
   dependencies: [
-      .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.13.2")
+      .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.13.3")
   ]
   ```
 
@@ -160,7 +160,7 @@ install SQLite.swift with Carthage:
  2. Update your Cartfile to include the following:
 
     ```ruby
-    github "stephencelis/SQLite.swift" ~> 0.13.2
+    github "stephencelis/SQLite.swift" ~> 0.13.3
     ```
 
  3. Run `carthage update` and
@@ -191,7 +191,7 @@ SQLite.swift with CocoaPods:
     use_frameworks!
 
     target 'YourAppTargetName' do
-        pod 'SQLite.swift', '~> 0.13.2'
+        pod 'SQLite.swift', '~> 0.13.3'
     end
     ```
 
diff --git a/SQLite.swift.podspec b/SQLite.swift.podspec
index 23146eee..4a1787bb 100644
--- a/SQLite.swift.podspec
+++ b/SQLite.swift.podspec
@@ -1,6 +1,6 @@
 Pod::Spec.new do |s|
   s.name             = "SQLite.swift"
-  s.version          = "0.13.2"
+  s.version          = "0.13.3"
   s.summary          = "A type-safe, Swift-language layer over SQLite3."
 
   s.description      = <<-DESC
diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj
index 26c5e975..7387d0e9 100644
--- a/SQLite.xcodeproj/project.pbxproj
+++ b/SQLite.xcodeproj/project.pbxproj
@@ -1333,7 +1333,7 @@
 				INFOPLIST_FILE = "$(SRCROOT)/Sources/SQLite/Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
-				MARKETING_VERSION = 0.13.2;
+				MARKETING_VERSION = 0.13.3;
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
 				PRODUCT_NAME = SQLite;
 				SKIP_INSTALL = YES;
@@ -1356,7 +1356,7 @@
 				INFOPLIST_FILE = "$(SRCROOT)/Sources/SQLite/Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
-				MARKETING_VERSION = 0.13.2;
+				MARKETING_VERSION = 0.13.3;
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
 				PRODUCT_NAME = SQLite;
 				SKIP_INSTALL = YES;
diff --git a/Sources/SQLite/Typed/Schema.swift b/Sources/SQLite/Typed/Schema.swift
index 726f3e27..55e525bc 100644
--- a/Sources/SQLite/Typed/Schema.swift
+++ b/Sources/SQLite/Typed/Schema.swift
@@ -308,6 +308,26 @@ public final class TableBuilder {
                                  references table: QueryType, _ other: Expression<V>) where V.Datatype == Int64 {
         column(name, V.declaredDatatype, nil, true, unique, check, nil, (table, other), nil)
     }
+    
+    public func column<V: Value>(_ name: Expression<V>, primaryKey: Bool, check: Expression<Bool>? = nil,
+                                 references table: QueryType, _ other: Expression<V>) where V.Datatype == Int64 {
+        column(name, V.declaredDatatype, primaryKey ? .default : nil, false, false, check, nil, (table, other), nil)
+    }
+
+    public func column<V: Value>(_ name: Expression<V>, primaryKey: Bool, check: Expression<Bool?>,
+                                 references table: QueryType, _ other: Expression<V>) where V.Datatype == Int64 {
+        column(name, V.declaredDatatype, primaryKey ? .default : nil, false, false, check, nil, (table, other), nil)
+    }
+
+    public func column<V: Value>(_ name: Expression<V?>, primaryKey: Bool, check: Expression<Bool>? = nil,
+                                 references table: QueryType, _ other: Expression<V>) where V.Datatype == Int64 {
+        column(name, V.declaredDatatype, primaryKey ? .default : nil, true, false, check, nil, (table, other), nil)
+    }
+
+    public func column<V: Value>(_ name: Expression<V?>, primaryKey: Bool, check: Expression<Bool?>,
+                                 references table: QueryType, _ other: Expression<V>) where V.Datatype == Int64 {
+        column(name, V.declaredDatatype, primaryKey ? .default : nil, true, false, check, nil, (table, other), nil)
+    }
 
     public func column<V: Value>(_ name: Expression<V>, unique: Bool = false, check: Expression<Bool>? = nil,
                                  defaultValue: Expression<V>? = nil, collate: Collation) where V.Datatype == String {
diff --git a/Tests/SQLiteTests/SchemaTests.swift b/Tests/SQLiteTests/SchemaTests.swift
index 495a5e51..a7de8bcf 100644
--- a/Tests/SQLiteTests/SchemaTests.swift
+++ b/Tests/SQLiteTests/SchemaTests.swift
@@ -317,6 +317,10 @@ class SchemaTests: XCTestCase {
             "CREATE TABLE \"table\" (\"int64\" INTEGER NOT NULL UNIQUE REFERENCES \"table\" (\"int64\"))",
             table.create { t in t.column(int64, unique: true, references: table, int64) }
         )
+        XCTAssertEqual(
+            "CREATE TABLE \"table\" (\"int64\" INTEGER PRIMARY KEY NOT NULL REFERENCES \"table\" (\"int64\"))",
+            table.create { t in t.column(int64, primaryKey: true, references: table, int64) }
+        )
         XCTAssertEqual(
             "CREATE TABLE \"table\" (\"int64\" INTEGER NOT NULL CHECK (\"int64\" > 0) REFERENCES \"table\" (\"int64\"))",
             table.create { t in t.column(int64, check: int64 > 0, references: table, int64) }
@@ -329,6 +333,10 @@ class SchemaTests: XCTestCase {
             "CREATE TABLE \"table\" (\"int64\" INTEGER NOT NULL UNIQUE CHECK (\"int64\" > 0) REFERENCES \"table\" (\"int64\"))",
             table.create { t in t.column(int64, unique: true, check: int64 > 0, references: table, int64) }
         )
+        XCTAssertEqual(
+            "CREATE TABLE \"table\" (\"int64\" INTEGER PRIMARY KEY NOT NULL CHECK (\"int64\" > 0) REFERENCES \"table\" (\"int64\"))",
+            table.create { t in t.column(int64, primaryKey: true, check: int64 > 0, references: table, int64) }
+        )
         XCTAssertEqual(
             """
             CREATE TABLE \"table\" (\"int64\" INTEGER NOT NULL UNIQUE CHECK (\"int64Optional\" > 0) REFERENCES
@@ -336,6 +344,13 @@ class SchemaTests: XCTestCase {
             """.replacingOccurrences(of: "\n", with: ""),
             table.create { t in t.column(int64, unique: true, check: int64Optional > 0, references: table, int64) }
         )
+        XCTAssertEqual(
+            """
+            CREATE TABLE \"table\" (\"int64\" INTEGER PRIMARY KEY NOT NULL CHECK (\"int64Optional\" > 0) REFERENCES
+             \"table\" (\"int64\"))
+            """.replacingOccurrences(of: "\n", with: ""),
+            table.create { t in t.column(int64, primaryKey: true, check: int64Optional > 0, references: table, int64) }
+        )
 
         XCTAssertEqual(
             "CREATE TABLE \"table\" (\"int64Optional\" INTEGER REFERENCES \"table\" (\"int64\"))",
@@ -345,6 +360,10 @@ class SchemaTests: XCTestCase {
             "CREATE TABLE \"table\" (\"int64Optional\" INTEGER UNIQUE REFERENCES \"table\" (\"int64\"))",
             table.create { t in t.column(int64Optional, unique: true, references: table, int64) }
         )
+        XCTAssertEqual(
+            "CREATE TABLE \"table\" (\"int64Optional\" INTEGER PRIMARY KEY REFERENCES \"table\" (\"int64\"))",
+            table.create { t in t.column(int64Optional, primaryKey: true, references: table, int64) }
+        )
         XCTAssertEqual(
             "CREATE TABLE \"table\" (\"int64Optional\" INTEGER CHECK (\"int64\" > 0) REFERENCES \"table\" (\"int64\"))",
             table.create { t in t.column(int64Optional, check: int64 > 0, references: table, int64) }
@@ -357,10 +376,18 @@ class SchemaTests: XCTestCase {
             "CREATE TABLE \"table\" (\"int64Optional\" INTEGER UNIQUE CHECK (\"int64\" > 0) REFERENCES \"table\" (\"int64\"))",
             table.create { t in t.column(int64Optional, unique: true, check: int64 > 0, references: table, int64) }
         )
+        XCTAssertEqual(
+            "CREATE TABLE \"table\" (\"int64Optional\" INTEGER PRIMARY KEY CHECK (\"int64\" > 0) REFERENCES \"table\" (\"int64\"))",
+            table.create { t in t.column(int64Optional, primaryKey: true, check: int64 > 0, references: table, int64) }
+        )
         XCTAssertEqual(
             "CREATE TABLE \"table\" (\"int64Optional\" INTEGER UNIQUE CHECK (\"int64Optional\" > 0) REFERENCES \"table\" (\"int64\"))",
             table.create { t in t.column(int64Optional, unique: true, check: int64Optional > 0, references: table, int64) }
         )
+        XCTAssertEqual(
+            "CREATE TABLE \"table\" (\"int64Optional\" INTEGER PRIMARY KEY CHECK (\"int64Optional\" > 0) REFERENCES \"table\" (\"int64\"))",
+            table.create { t in t.column(int64Optional, primaryKey: true, check: int64Optional > 0, references: table, int64) }
+        )
     }
 
     func test_column_withStringExpression_compilesCollatedColumnDefinitionExpression() {

From 05fce4eeeb7b77aeaf75792276e502d9b88ecff1 Mon Sep 17 00:00:00 2001
From: Nathan Fallet <contact@nathanfallet.me>
Date: Sun, 27 Mar 2022 12:42:30 +0200
Subject: [PATCH 132/326] Fixing Trailing Whitespace Violation

---
 Sources/SQLite/Typed/Schema.swift | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Sources/SQLite/Typed/Schema.swift b/Sources/SQLite/Typed/Schema.swift
index 55e525bc..e162cf34 100644
--- a/Sources/SQLite/Typed/Schema.swift
+++ b/Sources/SQLite/Typed/Schema.swift
@@ -308,7 +308,7 @@ public final class TableBuilder {
                                  references table: QueryType, _ other: Expression<V>) where V.Datatype == Int64 {
         column(name, V.declaredDatatype, nil, true, unique, check, nil, (table, other), nil)
     }
-    
+
     public func column<V: Value>(_ name: Expression<V>, primaryKey: Bool, check: Expression<Bool>? = nil,
                                  references table: QueryType, _ other: Expression<V>) where V.Datatype == Int64 {
         column(name, V.declaredDatatype, primaryKey ? .default : nil, false, false, check, nil, (table, other), nil)

From b53434e2d2bf6c4457b25c212104fca94dcbba2d Mon Sep 17 00:00:00 2001
From: Nathan Fallet <contact@nathanfallet.me>
Date: Sun, 27 Mar 2022 12:47:24 +0200
Subject: [PATCH 133/326] Updated Changelog

---
 CHANGELOG.md | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9bf5682b..786084f8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,7 @@
 ========================================
 
 * UUID Fix ([#1112][])
+* Add prepareRowIterator method to an extension of Statement. ([#1119][])
 * Adding primary key support to column with references ([#1121][])
 
 0.13.2 (25-01-2022), [diff][diff-0.13.2]
@@ -150,4 +151,5 @@
 [#1100]: https://github.com/stephencelis/SQLite.swift/pull/1100
 [#1105]: https://github.com/stephencelis/SQLite.swift/pull/1105
 [#1112]: https://github.com/stephencelis/SQLite.swift/pull/1112
+[#1119]: https://github.com/stephencelis/SQLite.swift/pull/1119
 [#1121]: https://github.com/stephencelis/SQLite.swift/pull/1121

From c42167feb9e33dc97e9da05232fa96cf354efbbb Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Wed, 27 Apr 2022 09:37:46 +0200
Subject: [PATCH 134/326] removeDiacritics => remove_diacritics

https://www.sqlite.org/fts3.html#tokenizer

Closes #1128
---
 Sources/SQLite/Extensions/FTS4.swift | 2 +-
 Tests/SQLiteTests/FTS4Tests.swift    | 6 +++---
 Tests/SQLiteTests/FTS5Tests.swift    | 2 +-
 3 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/Sources/SQLite/Extensions/FTS4.swift b/Sources/SQLite/Extensions/FTS4.swift
index 0e48943a..52fec955 100644
--- a/Sources/SQLite/Extensions/FTS4.swift
+++ b/Sources/SQLite/Extensions/FTS4.swift
@@ -103,7 +103,7 @@ public struct Tokenizer {
         var arguments = [String]()
 
         if let removeDiacritics = removeDiacritics {
-            arguments.append("removeDiacritics=\(removeDiacritics ? 1 : 0)".quote())
+            arguments.append("remove_diacritics=\(removeDiacritics ? 1 : 0)".quote())
         }
 
         if !tokenchars.isEmpty {
diff --git a/Tests/SQLiteTests/FTS4Tests.swift b/Tests/SQLiteTests/FTS4Tests.swift
index 33be422c..d6385b0c 100644
--- a/Tests/SQLiteTests/FTS4Tests.swift
+++ b/Tests/SQLiteTests/FTS4Tests.swift
@@ -21,12 +21,12 @@ class FTS4Tests: XCTestCase {
             virtualTable.create(.FTS4([string], tokenize: .Porter))
         )
         XCTAssertEqual(
-            "CREATE VIRTUAL TABLE \"virtual_table\" USING fts4(tokenize=unicode61 \"removeDiacritics=0\")",
+            "CREATE VIRTUAL TABLE \"virtual_table\" USING fts4(tokenize=unicode61 \"remove_diacritics=0\")",
             virtualTable.create(.FTS4(tokenize: .Unicode61(removeDiacritics: false)))
         )
         XCTAssertEqual(
             """
-            CREATE VIRTUAL TABLE \"virtual_table\" USING fts4(tokenize=unicode61 \"removeDiacritics=1\"
+            CREATE VIRTUAL TABLE \"virtual_table\" USING fts4(tokenize=unicode61 \"remove_diacritics=1\"
              \"tokenchars=.\" \"separators=X\")
             """.replacingOccurrences(of: "\n", with: ""),
             virtualTable.create(.FTS4(tokenize: .Unicode61(removeDiacritics: true,
@@ -116,7 +116,7 @@ class FTS4ConfigTests: XCTestCase {
     func test_tokenizer_unicode61_with_options() {
         XCTAssertEqual(
             """
-            CREATE VIRTUAL TABLE \"virtual_table\" USING fts4(tokenize=unicode61 \"removeDiacritics=1\"
+            CREATE VIRTUAL TABLE \"virtual_table\" USING fts4(tokenize=unicode61 \"remove_diacritics=1\"
              \"tokenchars=.\" \"separators=X\")
             """.replacingOccurrences(of: "\n", with: ""),
             sql(config.tokenizer(.Unicode61(removeDiacritics: true, tokenchars: ["."], separators: ["X"]))))
diff --git a/Tests/SQLiteTests/FTS5Tests.swift b/Tests/SQLiteTests/FTS5Tests.swift
index c271be9d..199ec415 100644
--- a/Tests/SQLiteTests/FTS5Tests.swift
+++ b/Tests/SQLiteTests/FTS5Tests.swift
@@ -77,7 +77,7 @@ class FTS5Tests: XCTestCase {
 
     func test_tokenizer_unicode61_with_options() {
         XCTAssertEqual(
-            "CREATE VIRTUAL TABLE \"virtual_table\" USING fts5(tokenize=unicode61 \"removeDiacritics=1\" \"tokenchars=.\" \"separators=X\")",
+            "CREATE VIRTUAL TABLE \"virtual_table\" USING fts5(tokenize=unicode61 \"remove_diacritics=1\" \"tokenchars=.\" \"separators=X\")",
             sql(config.tokenizer(.Unicode61(removeDiacritics: true, tokenchars: ["."], separators: ["X"]))))
     }
 

From 626cc79344db350887b28016673b71645de5668b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Maciej=20=C5=9Awi=C4=87?= <maciej@swic.name>
Date: Fri, 29 Apr 2022 17:17:44 +0300
Subject: [PATCH 135/326] Fixed build order

---
 SQLite.xcodeproj/project.pbxproj | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj
index 7387d0e9..61c2773a 100644
--- a/SQLite.xcodeproj/project.pbxproj
+++ b/SQLite.xcodeproj/project.pbxproj
@@ -592,9 +592,9 @@
 			isa = PBXNativeTarget;
 			buildConfigurationList = 03A65E6F1C6BB0F60062603F /* Build configuration list for PBXNativeTarget "SQLite tvOS" */;
 			buildPhases = (
+				03A65E571C6BB0F50062603F /* Headers */,
 				03A65E551C6BB0F50062603F /* Sources */,
 				03A65E561C6BB0F50062603F /* Frameworks */,
-				03A65E571C6BB0F50062603F /* Headers */,
 				03A65E581C6BB0F50062603F /* Resources */,
 			);
 			buildRules = (
@@ -628,9 +628,9 @@
 			isa = PBXNativeTarget;
 			buildConfigurationList = A121AC4C1CA35C79005A31D1 /* Build configuration list for PBXNativeTarget "SQLite watchOS" */;
 			buildPhases = (
+				A121AC421CA35C79005A31D1 /* Headers */,
 				A121AC401CA35C79005A31D1 /* Sources */,
 				A121AC411CA35C79005A31D1 /* Frameworks */,
-				A121AC421CA35C79005A31D1 /* Headers */,
 				A121AC431CA35C79005A31D1 /* Resources */,
 			);
 			buildRules = (
@@ -646,9 +646,9 @@
 			isa = PBXNativeTarget;
 			buildConfigurationList = EE247AE71C3F04ED00AE3E12 /* Build configuration list for PBXNativeTarget "SQLite iOS" */;
 			buildPhases = (
+				EE247AD01C3F04ED00AE3E12 /* Headers */,
 				EE247ACE1C3F04ED00AE3E12 /* Sources */,
 				EE247ACF1C3F04ED00AE3E12 /* Frameworks */,
-				EE247AD01C3F04ED00AE3E12 /* Headers */,
 				EE247AD11C3F04ED00AE3E12 /* Resources */,
 			);
 			buildRules = (
@@ -682,9 +682,9 @@
 			isa = PBXNativeTarget;
 			buildConfigurationList = EE247B511C3F3ED000AE3E12 /* Build configuration list for PBXNativeTarget "SQLite Mac" */;
 			buildPhases = (
+				EE247B391C3F3ED000AE3E12 /* Headers */,
 				EE247B371C3F3ED000AE3E12 /* Sources */,
 				EE247B381C3F3ED000AE3E12 /* Frameworks */,
-				EE247B391C3F3ED000AE3E12 /* Headers */,
 				EE247B3A1C3F3ED000AE3E12 /* Resources */,
 			);
 			buildRules = (

From b45781a6659e186f85509a83b3678d4701221cc6 Mon Sep 17 00:00:00 2001
From: Justin Meiners <justinm@passivelogic.com>
Date: Mon, 9 May 2022 15:06:50 -0600
Subject: [PATCH 136/326] Improve string quote performance

---
 Sources/SQLite/Helpers.swift | 13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)

diff --git a/Sources/SQLite/Helpers.swift b/Sources/SQLite/Helpers.swift
index d4c6828e..e3d37e11 100644
--- a/Sources/SQLite/Helpers.swift
+++ b/Sources/SQLite/Helpers.swift
@@ -54,12 +54,17 @@ extension Optional: _OptionalType {
 let SQLITE_TRANSIENT = unsafeBitCast(-1, to: sqlite3_destructor_type.self)
 
 extension String {
-
     func quote(_ mark: Character = "\"") -> String {
-        let escaped = reduce("") { string, character in
-            string + (character == mark ? "\(mark)\(mark)" : "\(character)")
+        var quoted = ""
+        quoted.append(mark)
+        for character in self {
+            quoted.append(character)
+            if character == mark {
+                quoted.append(character)
+            }
         }
-        return "\(mark)\(escaped)\(mark)"
+        quoted.append(mark)
+        return quoted
     }
 
     func join(_ expressions: [Expressible]) -> Expressible {

From 75dfb4e4af53b572ed433588fece3950d2c52c35 Mon Sep 17 00:00:00 2001
From: Atulya <atultw@outlook.com>
Date: Thu, 9 Jun 2022 17:20:20 -0700
Subject: [PATCH 137/326] add decoding for UUID

---
 Sources/SQLite/Typed/Coding.swift | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/Sources/SQLite/Typed/Coding.swift b/Sources/SQLite/Typed/Coding.swift
index cea2565a..ca9ca055 100644
--- a/Sources/SQLite/Typed/Coding.swift
+++ b/Sources/SQLite/Typed/Coding.swift
@@ -387,7 +387,11 @@ private class SQLiteDecoder: Decoder {
             } else if type == Date.self {
                 let date = try row.get(Expression<Date>(key.stringValue))
                 return date as! T
+            } else if type == UUID.self {
+                let uuid = try row.get(Expression<UUID>(key.stringValue))
+                return uuid as! T
             }
+            
             // swiftlint:enable force_cast
             guard let JSONString = try row.get(Expression<String?>(key.stringValue)) else {
                 throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: codingPath,

From 84783d370c94b15ac23e4624027d56f0735d7b5b Mon Sep 17 00:00:00 2001
From: Atulya <atultw@outlook.com>
Date: Mon, 13 Jun 2022 18:20:22 -0700
Subject: [PATCH 138/326] remove trailing whitespace for swiftlint

---
 Sources/SQLite/Typed/Coding.swift | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Sources/SQLite/Typed/Coding.swift b/Sources/SQLite/Typed/Coding.swift
index ca9ca055..4033365f 100644
--- a/Sources/SQLite/Typed/Coding.swift
+++ b/Sources/SQLite/Typed/Coding.swift
@@ -391,7 +391,7 @@ private class SQLiteDecoder: Decoder {
                 let uuid = try row.get(Expression<UUID>(key.stringValue))
                 return uuid as! T
             }
-            
+
             // swiftlint:enable force_cast
             guard let JSONString = try row.get(Expression<String?>(key.stringValue)) else {
                 throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: codingPath,

From 7715f7d626261e152c5f718e0770a28d728ba632 Mon Sep 17 00:00:00 2001
From: Atulya <atultw@outlook.com>
Date: Tue, 14 Jun 2022 18:22:44 -0700
Subject: [PATCH 139/326] add tests for codable UUID

---
 Tests/SQLiteTests/QueryIntegrationTests.swift |  7 +--
 Tests/SQLiteTests/QueryTests.swift            | 44 +++++++++----------
 Tests/SQLiteTests/TestHelpers.swift           |  5 ++-
 3 files changed, 30 insertions(+), 26 deletions(-)

diff --git a/Tests/SQLiteTests/QueryIntegrationTests.swift b/Tests/SQLiteTests/QueryIntegrationTests.swift
index 4b9c4bbf..82ee50a3 100644
--- a/Tests/SQLiteTests/QueryIntegrationTests.swift
+++ b/Tests/SQLiteTests/QueryIntegrationTests.swift
@@ -75,15 +75,15 @@ class QueryIntegrationTests: SQLiteTestCase {
             builder.column(Expression<Double>("float"))
             builder.column(Expression<Double>("double"))
             builder.column(Expression<Date>("date"))
+            builder.column(Expression<UUID>("uuid"))
             builder.column(Expression<String?>("optional"))
             builder.column(Expression<Data>("sub"))
         })
 
         let value1 = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4,
-                                 date: Date(timeIntervalSince1970: 0), optional: nil, sub: nil)
+                                 date: Date(timeIntervalSince1970: 0), uuid: UUID(uuidString: "E621E1F8-C36C-495A-93FC-0C247A3E6E5F")!, optional: nil, sub: nil)
         let value = TestCodable(int: 5, string: "6", bool: true, float: 7, double: 8,
-                                date: Date(timeIntervalSince1970: 5000), optional: "optional", sub: value1)
-
+                                date: Date(timeIntervalSince1970: 5000), uuid: UUID(uuidString: "E621E1F8-C36C-495A-93FC-0C247A3E6E5F")!, optional: "optional", sub: value1)
         try db.run(table.insert(value))
 
         let rows = try db.prepare(table)
@@ -95,6 +95,7 @@ class QueryIntegrationTests: SQLiteTestCase {
         XCTAssertEqual(values[0].float, 7)
         XCTAssertEqual(values[0].double, 8)
         XCTAssertEqual(values[0].date, Date(timeIntervalSince1970: 5000))
+        XCTAssertEqual(values[0].uuid, UUID(uuidString: "E621E1F8-C36C-495A-93FC-0C247A3E6E5F")!)
         XCTAssertEqual(values[0].optional, "optional")
         XCTAssertEqual(values[0].sub?.int, 1)
         XCTAssertEqual(values[0].sub?.string, "2")
diff --git a/Tests/SQLiteTests/QueryTests.swift b/Tests/SQLiteTests/QueryTests.swift
index efcbab94..efaf7d15 100644
--- a/Tests/SQLiteTests/QueryTests.swift
+++ b/Tests/SQLiteTests/QueryTests.swift
@@ -279,12 +279,12 @@ class QueryTests: XCTestCase {
     func test_insert_encodable() throws {
         let emails = Table("emails")
         let value = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4,
-                                date: Date(timeIntervalSince1970: 0), optional: nil, sub: nil)
+                                date: Date(timeIntervalSince1970: 0), uuid: UUID(uuidString: "E621E1F8-C36C-495A-93FC-0C247A3E6E5F")!, optional: nil, sub: nil)
         let insert = try emails.insert(value)
         assertSQL(
             """
-            INSERT INTO \"emails\" (\"int\", \"string\", \"bool\", \"float\", \"double\", \"date\")
-             VALUES (1, '2', 1, 3.0, 4.0, '1970-01-01T00:00:00.000')
+            INSERT INTO \"emails\" (\"int\", \"string\", \"bool\", \"float\", \"double\", \"date\", \"uuid\")
+             VALUES (1, '2', 1, 3.0, 4.0, '1970-01-01T00:00:00.000', 'E621E1F8-C36C-495A-93FC-0C247A3E6E5F')
             """.replacingOccurrences(of: "\n", with: ""),
             insert
         )
@@ -294,16 +294,16 @@ class QueryTests: XCTestCase {
     func test_insert_encodable_with_nested_encodable() throws {
         let emails = Table("emails")
         let value1 = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4,
-                                 date: Date(timeIntervalSince1970: 0), optional: nil, sub: nil)
+                                 date: Date(timeIntervalSince1970: 0), uuid: UUID(uuidString: "E621E1F8-C36C-495A-93FC-0C247A3E6E5F")!, optional: nil, sub: nil)
         let value = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4,
-                                date: Date(timeIntervalSince1970: 0), optional: "optional", sub: value1)
+                                date: Date(timeIntervalSince1970: 0), uuid: UUID(uuidString: "E621E1F8-C36C-495A-93FC-0C247A3E6E5F")!, optional: "optional", sub: value1)
         let insert = try emails.insert(value)
         let encodedJSON = try JSONEncoder().encode(value1)
         let encodedJSONString = String(data: encodedJSON, encoding: .utf8)!
         assertSQL(
             """
-            INSERT INTO \"emails\" (\"int\", \"string\", \"bool\", \"float\", \"double\", \"date\", \"optional\",
-             \"sub\") VALUES (1, '2', 1, 3.0, 4.0, '1970-01-01T00:00:00.000', 'optional', '\(encodedJSONString)')
+            INSERT INTO \"emails\" (\"int\", \"string\", \"bool\", \"float\", \"double\", \"date\", \"uuid\", \"optional\",
+             \"sub\") VALUES (1, '2', 1, 3.0, 4.0, '1970-01-01T00:00:00.000', 'E621E1F8-C36C-495A-93FC-0C247A3E6E5F', 'optional', '\(encodedJSONString)')
             """.replacingOccurrences(of: "\n", with: ""),
             insert
         )
@@ -350,14 +350,14 @@ class QueryTests: XCTestCase {
         let emails = Table("emails")
         let string = Expression<String>("string")
         let value = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4,
-                                date: Date(timeIntervalSince1970: 0), optional: nil, sub: nil)
+                                date: Date(timeIntervalSince1970: 0), uuid: UUID(uuidString: "E621E1F8-C36C-495A-93FC-0C247A3E6E5F")!, optional: nil, sub: nil)
         let insert = try emails.upsert(value, onConflictOf: string)
         assertSQL(
             """
-            INSERT INTO \"emails\" (\"int\", \"string\", \"bool\", \"float\", \"double\", \"date\")
-             VALUES (1, '2', 1, 3.0, 4.0, '1970-01-01T00:00:00.000') ON CONFLICT (\"string\")
+            INSERT INTO \"emails\" (\"int\", \"string\", \"bool\", \"float\", \"double\", \"date\", \"uuid\")
+             VALUES (1, '2', 1, 3.0, 4.0, '1970-01-01T00:00:00.000', 'E621E1F8-C36C-495A-93FC-0C247A3E6E5F') ON CONFLICT (\"string\")
              DO UPDATE SET \"int\" = \"excluded\".\"int\", \"bool\" = \"excluded\".\"bool\",
-             \"float\" = \"excluded\".\"float\", \"double\" = \"excluded\".\"double\", \"date\" = \"excluded\".\"date\"
+             \"float\" = \"excluded\".\"float\", \"double\" = \"excluded\".\"double\", \"date\" = \"excluded\".\"date\", \"uuid\" = \"excluded\".\"uuid\"
             """.replacingOccurrences(of: "\n", with: ""),
             insert
         )
@@ -366,17 +366,17 @@ class QueryTests: XCTestCase {
     func test_insert_many_encodable() throws {
         let emails = Table("emails")
         let value1 = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4,
-                                 date: Date(timeIntervalSince1970: 0), optional: nil, sub: nil)
+                                 date: Date(timeIntervalSince1970: 0), uuid: UUID(uuidString: "E621E1F8-C36C-495A-93FC-0C247A3E6E5F")!, optional: nil, sub: nil)
         let value2 = TestCodable(int: 2, string: "3", bool: true, float: 3, double: 5,
-                                 date: Date(timeIntervalSince1970: 0), optional: nil, sub: nil)
+                                 date: Date(timeIntervalSince1970: 0), uuid: UUID(uuidString: "E621E1F8-C36C-495A-93FC-0C247A3E6E5F")!, optional: nil, sub: nil)
         let value3 = TestCodable(int: 3, string: "4", bool: true, float: 3, double: 6,
-                                 date: Date(timeIntervalSince1970: 0), optional: nil, sub: nil)
+                                 date: Date(timeIntervalSince1970: 0), uuid: UUID(uuidString: "E621E1F8-C36C-495A-93FC-0C247A3E6E5F")!, optional: nil, sub: nil)
         let insert = try emails.insertMany([value1, value2, value3])
         assertSQL(
             """
-            INSERT INTO \"emails\" (\"int\", \"string\", \"bool\", \"float\", \"double\", \"date\")
-             VALUES (1, '2', 1, 3.0, 4.0, '1970-01-01T00:00:00.000'), (2, '3', 1, 3.0, 5.0, '1970-01-01T00:00:00.000'),
-             (3, '4', 1, 3.0, 6.0, '1970-01-01T00:00:00.000')
+            INSERT INTO \"emails\" (\"int\", \"string\", \"bool\", \"float\", \"double\", \"date\", \"uuid\")
+             VALUES (1, '2', 1, 3.0, 4.0, '1970-01-01T00:00:00.000', 'E621E1F8-C36C-495A-93FC-0C247A3E6E5F'), (2, '3', 1, 3.0, 5.0, '1970-01-01T00:00:00.000', 'E621E1F8-C36C-495A-93FC-0C247A3E6E5F'),
+             (3, '4', 1, 3.0, 6.0, '1970-01-01T00:00:00.000', 'E621E1F8-C36C-495A-93FC-0C247A3E6E5F')
             """.replacingOccurrences(of: "\n", with: ""),
             insert
         )
@@ -399,12 +399,12 @@ class QueryTests: XCTestCase {
     func test_update_encodable() throws {
         let emails = Table("emails")
         let value = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4,
-                                date: Date(timeIntervalSince1970: 0), optional: nil, sub: nil)
+                                date: Date(timeIntervalSince1970: 0), uuid: UUID(uuidString: "E621E1F8-C36C-495A-93FC-0C247A3E6E5F")!, optional: nil, sub: nil)
         let update = try emails.update(value)
         assertSQL(
             """
             UPDATE \"emails\" SET \"int\" = 1, \"string\" = '2', \"bool\" = 1, \"float\" = 3.0, \"double\" = 4.0,
-             \"date\" = '1970-01-01T00:00:00.000'
+             \"date\" = '1970-01-01T00:00:00.000', \"uuid\" = 'E621E1F8-C36C-495A-93FC-0C247A3E6E5F'
             """.replacingOccurrences(of: "\n", with: ""),
             update
         )
@@ -413,9 +413,9 @@ class QueryTests: XCTestCase {
     func test_update_encodable_with_nested_encodable() throws {
         let emails = Table("emails")
         let value1 = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4,
-                                 date: Date(timeIntervalSince1970: 0), optional: nil, sub: nil)
+                                 date: Date(timeIntervalSince1970: 0), uuid: UUID(uuidString: "E621E1F8-C36C-495A-93FC-0C247A3E6E5F")!, optional: nil, sub: nil)
         let value = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4,
-                                date: Date(timeIntervalSince1970: 0), optional: nil, sub: value1)
+                                date: Date(timeIntervalSince1970: 0), uuid: UUID(uuidString: "E621E1F8-C36C-495A-93FC-0C247A3E6E5F")!, optional: nil, sub: value1)
         let update = try emails.update(value)
 
         // NOTE: As Linux JSON decoding doesn't order keys the same way, we need to check prefix, suffix,
@@ -424,7 +424,7 @@ class QueryTests: XCTestCase {
         let expectedPrefix =
             """
             UPDATE \"emails\" SET \"int\" = 1, \"string\" = '2', \"bool\" = 1, \"float\" = 3.0, \"double\" = 4.0,
-             \"date\" = '1970-01-01T00:00:00.000', \"sub\" = '
+             \"date\" = '1970-01-01T00:00:00.000', \"uuid\" = 'E621E1F8-C36C-495A-93FC-0C247A3E6E5F', \"sub\" = '
             """.replacingOccurrences(of: "\n", with: "")
         let expectedSuffix = "'"
 
diff --git a/Tests/SQLiteTests/TestHelpers.swift b/Tests/SQLiteTests/TestHelpers.swift
index a6efa2e7..bc538c5b 100644
--- a/Tests/SQLiteTests/TestHelpers.swift
+++ b/Tests/SQLiteTests/TestHelpers.swift
@@ -115,16 +115,18 @@ class TestCodable: Codable, Equatable {
     let float: Float
     let double: Double
     let date: Date
+    let uuid: UUID
     let optional: String?
     let sub: TestCodable?
 
-    init(int: Int, string: String, bool: Bool, float: Float, double: Double, date: Date, optional: String?, sub: TestCodable?) {
+    init(int: Int, string: String, bool: Bool, float: Float, double: Double, date: Date, uuid: UUID, optional: String?, sub: TestCodable?) {
         self.int = int
         self.string = string
         self.bool = bool
         self.float = float
         self.double = double
         self.date = date
+        self.uuid = uuid
         self.optional = optional
         self.sub = sub
     }
@@ -136,6 +138,7 @@ class TestCodable: Codable, Equatable {
         lhs.float == rhs.float &&
         lhs.double == rhs.double &&
         lhs.date == rhs.date &&
+        lhs.uuid == lhs.uuid &&
         lhs.optional == rhs.optional &&
         lhs.sub == rhs.sub
     }

From 8fece72b9be693a41d15ff20d7432bd30964610e Mon Sep 17 00:00:00 2001
From: Atulya <atultw@outlook.com>
Date: Mon, 27 Jun 2022 12:38:57 -0700
Subject: [PATCH 140/326] create a constant for the UUID used in tests

---
 Tests/SQLiteTests/QueryIntegrationTests.swift |  6 +++---
 Tests/SQLiteTests/QueryTests.swift            | 20 +++++++++----------
 Tests/SQLiteTests/TestHelpers.swift           |  2 ++
 3 files changed, 15 insertions(+), 13 deletions(-)

diff --git a/Tests/SQLiteTests/QueryIntegrationTests.swift b/Tests/SQLiteTests/QueryIntegrationTests.swift
index 82ee50a3..b3d9cf96 100644
--- a/Tests/SQLiteTests/QueryIntegrationTests.swift
+++ b/Tests/SQLiteTests/QueryIntegrationTests.swift
@@ -81,9 +81,9 @@ class QueryIntegrationTests: SQLiteTestCase {
         })
 
         let value1 = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4,
-                                 date: Date(timeIntervalSince1970: 0), uuid: UUID(uuidString: "E621E1F8-C36C-495A-93FC-0C247A3E6E5F")!, optional: nil, sub: nil)
+                                 date: Date(timeIntervalSince1970: 0), uuid: testUUIDValue, optional: nil, sub: nil)
         let value = TestCodable(int: 5, string: "6", bool: true, float: 7, double: 8,
-                                date: Date(timeIntervalSince1970: 5000), uuid: UUID(uuidString: "E621E1F8-C36C-495A-93FC-0C247A3E6E5F")!, optional: "optional", sub: value1)
+                                date: Date(timeIntervalSince1970: 5000), uuid: testUUIDValue, optional: "optional", sub: value1)
         try db.run(table.insert(value))
 
         let rows = try db.prepare(table)
@@ -95,7 +95,7 @@ class QueryIntegrationTests: SQLiteTestCase {
         XCTAssertEqual(values[0].float, 7)
         XCTAssertEqual(values[0].double, 8)
         XCTAssertEqual(values[0].date, Date(timeIntervalSince1970: 5000))
-        XCTAssertEqual(values[0].uuid, UUID(uuidString: "E621E1F8-C36C-495A-93FC-0C247A3E6E5F")!)
+        XCTAssertEqual(values[0].uuid, testUUIDValue)
         XCTAssertEqual(values[0].optional, "optional")
         XCTAssertEqual(values[0].sub?.int, 1)
         XCTAssertEqual(values[0].sub?.string, "2")
diff --git a/Tests/SQLiteTests/QueryTests.swift b/Tests/SQLiteTests/QueryTests.swift
index efaf7d15..e4353e52 100644
--- a/Tests/SQLiteTests/QueryTests.swift
+++ b/Tests/SQLiteTests/QueryTests.swift
@@ -279,7 +279,7 @@ class QueryTests: XCTestCase {
     func test_insert_encodable() throws {
         let emails = Table("emails")
         let value = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4,
-                                date: Date(timeIntervalSince1970: 0), uuid: UUID(uuidString: "E621E1F8-C36C-495A-93FC-0C247A3E6E5F")!, optional: nil, sub: nil)
+                                date: Date(timeIntervalSince1970: 0), uuid: testUUIDValue, optional: nil, sub: nil)
         let insert = try emails.insert(value)
         assertSQL(
             """
@@ -294,9 +294,9 @@ class QueryTests: XCTestCase {
     func test_insert_encodable_with_nested_encodable() throws {
         let emails = Table("emails")
         let value1 = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4,
-                                 date: Date(timeIntervalSince1970: 0), uuid: UUID(uuidString: "E621E1F8-C36C-495A-93FC-0C247A3E6E5F")!, optional: nil, sub: nil)
+                                 date: Date(timeIntervalSince1970: 0), uuid: testUUIDValue, optional: nil, sub: nil)
         let value = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4,
-                                date: Date(timeIntervalSince1970: 0), uuid: UUID(uuidString: "E621E1F8-C36C-495A-93FC-0C247A3E6E5F")!, optional: "optional", sub: value1)
+                                date: Date(timeIntervalSince1970: 0), uuid: testUUIDValue, optional: "optional", sub: value1)
         let insert = try emails.insert(value)
         let encodedJSON = try JSONEncoder().encode(value1)
         let encodedJSONString = String(data: encodedJSON, encoding: .utf8)!
@@ -350,7 +350,7 @@ class QueryTests: XCTestCase {
         let emails = Table("emails")
         let string = Expression<String>("string")
         let value = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4,
-                                date: Date(timeIntervalSince1970: 0), uuid: UUID(uuidString: "E621E1F8-C36C-495A-93FC-0C247A3E6E5F")!, optional: nil, sub: nil)
+                                date: Date(timeIntervalSince1970: 0), uuid: testUUIDValue, optional: nil, sub: nil)
         let insert = try emails.upsert(value, onConflictOf: string)
         assertSQL(
             """
@@ -366,11 +366,11 @@ class QueryTests: XCTestCase {
     func test_insert_many_encodable() throws {
         let emails = Table("emails")
         let value1 = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4,
-                                 date: Date(timeIntervalSince1970: 0), uuid: UUID(uuidString: "E621E1F8-C36C-495A-93FC-0C247A3E6E5F")!, optional: nil, sub: nil)
+                                 date: Date(timeIntervalSince1970: 0), uuid: testUUIDValue, optional: nil, sub: nil)
         let value2 = TestCodable(int: 2, string: "3", bool: true, float: 3, double: 5,
-                                 date: Date(timeIntervalSince1970: 0), uuid: UUID(uuidString: "E621E1F8-C36C-495A-93FC-0C247A3E6E5F")!, optional: nil, sub: nil)
+                                 date: Date(timeIntervalSince1970: 0), uuid: testUUIDValue, optional: nil, sub: nil)
         let value3 = TestCodable(int: 3, string: "4", bool: true, float: 3, double: 6,
-                                 date: Date(timeIntervalSince1970: 0), uuid: UUID(uuidString: "E621E1F8-C36C-495A-93FC-0C247A3E6E5F")!, optional: nil, sub: nil)
+                                 date: Date(timeIntervalSince1970: 0), uuid: testUUIDValue, optional: nil, sub: nil)
         let insert = try emails.insertMany([value1, value2, value3])
         assertSQL(
             """
@@ -399,7 +399,7 @@ class QueryTests: XCTestCase {
     func test_update_encodable() throws {
         let emails = Table("emails")
         let value = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4,
-                                date: Date(timeIntervalSince1970: 0), uuid: UUID(uuidString: "E621E1F8-C36C-495A-93FC-0C247A3E6E5F")!, optional: nil, sub: nil)
+                                date: Date(timeIntervalSince1970: 0), uuid: testUUIDValue, optional: nil, sub: nil)
         let update = try emails.update(value)
         assertSQL(
             """
@@ -413,9 +413,9 @@ class QueryTests: XCTestCase {
     func test_update_encodable_with_nested_encodable() throws {
         let emails = Table("emails")
         let value1 = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4,
-                                 date: Date(timeIntervalSince1970: 0), uuid: UUID(uuidString: "E621E1F8-C36C-495A-93FC-0C247A3E6E5F")!, optional: nil, sub: nil)
+                                 date: Date(timeIntervalSince1970: 0), uuid: testUUIDValue, optional: nil, sub: nil)
         let value = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4,
-                                date: Date(timeIntervalSince1970: 0), uuid: UUID(uuidString: "E621E1F8-C36C-495A-93FC-0C247A3E6E5F")!, optional: nil, sub: value1)
+                                date: Date(timeIntervalSince1970: 0), uuid: testUUIDValue, optional: nil, sub: value1)
         let update = try emails.update(value)
 
         // NOTE: As Linux JSON decoding doesn't order keys the same way, we need to check prefix, suffix,
diff --git a/Tests/SQLiteTests/TestHelpers.swift b/Tests/SQLiteTests/TestHelpers.swift
index bc538c5b..4a71fd40 100644
--- a/Tests/SQLiteTests/TestHelpers.swift
+++ b/Tests/SQLiteTests/TestHelpers.swift
@@ -98,6 +98,8 @@ let stringOptional = Expression<String?>("stringOptional")
 let uuid = Expression<UUID>("uuid")
 let uuidOptional = Expression<UUID?>("uuidOptional")
 
+let testUUIDValue = UUID(uuidString: "E621E1F8-C36C-495A-93FC-0C247A3E6E5F")!
+
 func assertSQL(_ expression1: @autoclosure () -> String, _ expression2: @autoclosure () -> Expressible,
                file: StaticString = #file, line: UInt = #line) {
     XCTAssertEqual(expression1(), expression2().asSQL(), file: file, line: line)

From 92760e03cc71643dbb51402eb21c2dbbb59405b3 Mon Sep 17 00:00:00 2001
From: Atulya <atultw@outlook.com>
Date: Mon, 27 Jun 2022 12:51:15 -0700
Subject: [PATCH 141/326] use a switch to accommodate more types

---
 Sources/SQLite/Typed/Coding.swift | 166 +++++++++++++++---------------
 1 file changed, 84 insertions(+), 82 deletions(-)

diff --git a/Sources/SQLite/Typed/Coding.swift b/Sources/SQLite/Typed/Coding.swift
index 4033365f..e7f5b7bb 100644
--- a/Sources/SQLite/Typed/Coding.swift
+++ b/Sources/SQLite/Typed/Coding.swift
@@ -44,7 +44,7 @@ extension QueryType {
         try encodable.encode(to: encoder)
         return self.insert(encoder.setters + otherSetters)
     }
-
+    
     /// Creates an `INSERT` statement by encoding the given object
     /// This method converts any custom nested types to JSON data and does not handle any sort
     /// of object relationships. If you want to support relationships between objects you will
@@ -69,7 +69,7 @@ extension QueryType {
         try encodable.encode(to: encoder)
         return self.insert(or: onConflict, encoder.setters + otherSetters)
     }
-
+    
     /// Creates a batch `INSERT` statement by encoding the array of given objects
     /// This method converts any custom nested types to JSON data and does not handle any sort
     /// of object relationships. If you want to support relationships between objects you will
@@ -93,7 +93,7 @@ extension QueryType {
         }
         return self.insertMany(combinedSetters)
     }
-
+    
     /// Creates an `INSERT ON CONFLICT DO UPDATE` statement, aka upsert, by encoding the given object
     /// This method converts any custom nested types to JSON data and does not handle any sort
     /// of object relationships. If you want to support relationships between objects you will
@@ -116,7 +116,7 @@ extension QueryType {
         try encodable.encode(to: encoder)
         return self.upsert(encoder.setters + otherSetters, onConflictOf: conflicting)
     }
-
+    
     /// Creates an `UPDATE` statement by encoding the given object
     /// This method converts any custom nested types to JSON data and does not handle any sort
     /// of object relationships. If you want to support relationships between objects you will
@@ -151,7 +151,7 @@ extension Row {
     public func decode<V: Decodable>(userInfo: [CodingUserInfoKey: Any] = [:]) throws -> V {
         try V(from: decoder(userInfo: userInfo))
     }
-
+    
     public func decoder(userInfo: [CodingUserInfoKey: Any] = [:]) -> Decoder {
         SQLiteDecoder(row: self, userInfo: userInfo)
     }
@@ -162,130 +162,131 @@ private class SQLiteEncoder: Encoder {
     class SQLiteKeyedEncodingContainer<MyKey: CodingKey>: KeyedEncodingContainerProtocol {
         // swiftlint:disable nesting
         typealias Key = MyKey
-
+        
         let encoder: SQLiteEncoder
         let codingPath: [CodingKey] = []
-
+        
         init(encoder: SQLiteEncoder) {
             self.encoder = encoder
         }
-
+        
         func superEncoder() -> Swift.Encoder {
             fatalError("SQLiteEncoding does not support super encoders")
         }
-
+        
         func superEncoder(forKey key: Key) -> Swift.Encoder {
             fatalError("SQLiteEncoding does not support super encoders")
         }
-
+        
         func encodeNil(forKey key: SQLiteEncoder.SQLiteKeyedEncodingContainer<Key>.Key) throws {
             encoder.setters.append(Expression<String?>(key.stringValue) <- nil)
         }
-
+        
         func encode(_ value: Int, forKey key: SQLiteEncoder.SQLiteKeyedEncodingContainer<Key>.Key) throws {
             encoder.setters.append(Expression(key.stringValue) <- value)
         }
-
+        
         func encode(_ value: Bool, forKey key: Key) throws {
             encoder.setters.append(Expression(key.stringValue) <- value)
         }
-
+        
         func encode(_ value: Float, forKey key: Key) throws {
             encoder.setters.append(Expression(key.stringValue) <- Double(value))
         }
-
+        
         func encode(_ value: Double, forKey key: Key) throws {
             encoder.setters.append(Expression(key.stringValue) <- value)
         }
-
+        
         func encode(_ value: String, forKey key: Key) throws {
             encoder.setters.append(Expression(key.stringValue) <- value)
         }
-
+        
         func encode<T>(_ value: T, forKey key: Key) throws where T: Swift.Encodable {
-            if let data = value as? Data {
+            switch value {
+            case let data as Data:
                 encoder.setters.append(Expression(key.stringValue) <- data)
-            } else if let date = value as? Date {
+            case let date as Date:
                 encoder.setters.append(Expression(key.stringValue) <- date.datatypeValue)
-            } else if let uuid = value as? UUID {
+            case let uuid as UUID:
                 encoder.setters.append(Expression(key.stringValue) <- uuid.datatypeValue)
-            } else {
+            default:
                 let encoded = try JSONEncoder().encode(value)
                 let string = String(data: encoded, encoding: .utf8)
                 encoder.setters.append(Expression(key.stringValue) <- string)
             }
         }
-
+        
         func encode(_ value: Int8, forKey key: Key) throws {
             throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: codingPath,
                                                                           debugDescription: "encoding an Int8 is not supported"))
         }
-
+        
         func encode(_ value: Int16, forKey key: Key) throws {
             throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: codingPath,
                                                                           debugDescription: "encoding an Int16 is not supported"))
         }
-
+        
         func encode(_ value: Int32, forKey key: Key) throws {
             throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: codingPath,
                                                                           debugDescription: "encoding an Int32 is not supported"))
         }
-
+        
         func encode(_ value: Int64, forKey key: Key) throws {
             encoder.setters.append(Expression(key.stringValue) <- value)
         }
-
+        
         func encode(_ value: UInt, forKey key: Key) throws {
             throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: codingPath,
                                                                           debugDescription: "encoding an UInt is not supported"))
         }
-
+        
         func encode(_ value: UInt8, forKey key: Key) throws {
             throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: codingPath,
                                                                           debugDescription: "encoding an UInt8 is not supported"))
         }
-
+        
         func encode(_ value: UInt16, forKey key: Key) throws {
             throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: codingPath,
                                                                           debugDescription: "encoding an UInt16 is not supported"))
         }
-
+        
         func encode(_ value: UInt32, forKey key: Key) throws {
             throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: codingPath,
                                                                           debugDescription: "encoding an UInt32 is not supported"))
         }
-
+        
         func encode(_ value: UInt64, forKey key: Key) throws {
             throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: codingPath,
                                                                           debugDescription: "encoding an UInt64 is not supported"))
         }
-
+        
         func nestedContainer<NestedKey>(keyedBy keyType: NestedKey.Type, forKey key: Key)
-                        -> KeyedEncodingContainer<NestedKey> where NestedKey: CodingKey {
+        -> KeyedEncodingContainer<NestedKey> where NestedKey: CodingKey {
             fatalError("encoding a nested container is not supported")
         }
-
+        
         func nestedUnkeyedContainer(forKey key: Key) -> UnkeyedEncodingContainer {
             fatalError("encoding nested values is not supported")
         }
     }
-
+    
     fileprivate var setters: [Setter] = []
     let codingPath: [CodingKey] = []
     let userInfo: [CodingUserInfoKey: Any]
-
+    
     init(userInfo: [CodingUserInfoKey: Any]) {
         self.userInfo = userInfo
     }
-
+    
     func singleValueContainer() -> SingleValueEncodingContainer {
         fatalError("not supported")
     }
-
+    
     func unkeyedContainer() -> UnkeyedEncodingContainer {
         fatalError("not supported")
     }
-
+    
     func container<Key>(keyedBy type: Key.Type) -> KeyedEncodingContainer<Key> where Key: CodingKey {
         KeyedEncodingContainer(SQLiteKeyedEncodingContainer(encoder: self))
     }
@@ -294,156 +295,157 @@ private class SQLiteEncoder: Encoder {
 private class SQLiteDecoder: Decoder {
     class SQLiteKeyedDecodingContainer<MyKey: CodingKey>: KeyedDecodingContainerProtocol {
         typealias Key = MyKey
-
+        
         let codingPath: [CodingKey] = []
         let row: Row
-
+        
         init(row: Row) {
             self.row = row
         }
-
+        
         var allKeys: [Key] {
             row.columnNames.keys.compactMap({ Key(stringValue: $0) })
         }
-
+        
         func contains(_ key: Key) -> Bool {
             row.hasValue(for: key.stringValue)
         }
-
+        
         func decodeNil(forKey key: Key) throws -> Bool {
             !contains(key)
         }
-
+        
         func decode(_ type: Bool.Type, forKey key: Key) throws -> Bool {
             try row.get(Expression(key.stringValue))
         }
-
+        
         func decode(_ type: Int.Type, forKey key: Key) throws -> Int {
             try row.get(Expression(key.stringValue))
         }
-
+        
         func decode(_ type: Int8.Type, forKey key: Key) throws -> Int8 {
             throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: codingPath,
                                                                          debugDescription: "decoding an Int8 is not supported"))
         }
-
+        
         func decode(_ type: Int16.Type, forKey key: Key) throws -> Int16 {
             throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: codingPath,
                                                                          debugDescription: "decoding an Int16 is not supported"))
         }
-
+        
         func decode(_ type: Int32.Type, forKey key: Key) throws -> Int32 {
             throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: codingPath,
                                                                          debugDescription: "decoding an Int32 is not supported"))
         }
-
+        
         func decode(_ type: Int64.Type, forKey key: Key) throws -> Int64 {
             try row.get(Expression(key.stringValue))
         }
-
+        
         func decode(_ type: UInt.Type, forKey key: Key) throws -> UInt {
             throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: codingPath,
                                                                          debugDescription: "decoding an UInt is not supported"))
-
+            
         }
-
+        
         func decode(_ type: UInt8.Type, forKey key: Key) throws -> UInt8 {
             throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: codingPath,
                                                                          debugDescription: "decoding an UInt8 is not supported"))
         }
-
+        
         func decode(_ type: UInt16.Type, forKey key: Key) throws -> UInt16 {
             throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: codingPath,
                                                                          debugDescription: "decoding an UInt16 is not supported"))
         }
-
+        
         func decode(_ type: UInt32.Type, forKey key: Key) throws -> UInt32 {
             throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: codingPath,
                                                                          debugDescription: "decoding an UInt32 is not supported"))
         }
-
+        
         func decode(_ type: UInt64.Type, forKey key: Key) throws -> UInt64 {
             throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: codingPath,
                                                                          debugDescription: "decoding an UInt64 is not supported"))
         }
-
+        
         func decode(_ type: Float.Type, forKey key: Key) throws -> Float {
             Float(try row.get(Expression<Double>(key.stringValue)))
         }
-
+        
         func decode(_ type: Double.Type, forKey key: Key) throws -> Double {
             try row.get(Expression(key.stringValue))
         }
-
+        
         func decode(_ type: String.Type, forKey key: Key) throws -> String {
             try row.get(Expression(key.stringValue))
         }
-
+        
         func decode<T>(_ type: T.Type, forKey key: Key) throws -> T where T: Swift.Decodable {
             // swiftlint:disable force_cast
-            if type == Data.self {
+            switch type {
+            case is Data.Type:
                 let data = try row.get(Expression<Data>(key.stringValue))
                 return data as! T
-            } else if type == Date.self {
+            case is Date.Type:
                 let date = try row.get(Expression<Date>(key.stringValue))
                 return date as! T
-            } else if type == UUID.self {
+            case is UUID.Type:
                 let uuid = try row.get(Expression<UUID>(key.stringValue))
                 return uuid as! T
+            default:
+                // swiftlint:enable force_cast
+                guard let JSONString = try row.get(Expression<String?>(key.stringValue)) else {
+                    throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: codingPath,
+                                                                                 debugDescription: "an unsupported type was found"))
+                }
+                guard let data = JSONString.data(using: .utf8) else {
+                    throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: codingPath,
+                                                                            debugDescription: "invalid utf8 data found"))
+                }
+                return try JSONDecoder().decode(type, from: data)
             }
-
-            // swiftlint:enable force_cast
-            guard let JSONString = try row.get(Expression<String?>(key.stringValue)) else {
-                throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: codingPath,
-                                                                             debugDescription: "an unsupported type was found"))
-            }
-            guard let data = JSONString.data(using: .utf8) else {
-                throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: codingPath,
-                                                                        debugDescription: "invalid utf8 data found"))
-            }
-            return try JSONDecoder().decode(type, from: data)
         }
-
+        
         func nestedContainer<NestedKey>(keyedBy type: NestedKey.Type, forKey key: Key) throws
-                        -> KeyedDecodingContainer<NestedKey> where NestedKey: CodingKey {
+        -> KeyedDecodingContainer<NestedKey> where NestedKey: CodingKey {
             throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: codingPath,
                                                                     debugDescription: "decoding nested containers is not supported"))
         }
-
+        
         func nestedUnkeyedContainer(forKey key: Key) throws -> UnkeyedDecodingContainer {
             throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: codingPath,
                                                                     debugDescription: "decoding unkeyed containers is not supported"))
         }
-
+        
         func superDecoder() throws -> Swift.Decoder {
             throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: codingPath,
                                                                     debugDescription: "decoding super encoders containers is not supported"))
         }
-
+        
         func superDecoder(forKey key: Key) throws -> Swift.Decoder {
             throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: codingPath,
                                                                     debugDescription: "decoding super decoders is not supported"))
         }
     }
-
+    
     let row: Row
     let codingPath: [CodingKey] = []
     let userInfo: [CodingUserInfoKey: Any]
-
+    
     init(row: Row, userInfo: [CodingUserInfoKey: Any]) {
         self.row = row
         self.userInfo = userInfo
     }
-
+    
     func container<Key>(keyedBy type: Key.Type) throws -> KeyedDecodingContainer<Key> where Key: CodingKey {
         KeyedDecodingContainer(SQLiteKeyedDecodingContainer(row: row))
     }
-
+    
     func unkeyedContainer() throws -> UnkeyedDecodingContainer {
         throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: codingPath,
                                                                 debugDescription: "decoding an unkeyed container is not supported"))
     }
-
+    
     func singleValueContainer() throws -> SingleValueDecodingContainer {
         throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: codingPath,
                                                                 debugDescription: "decoding a single value container is not supported"))

From bc63d3ab1a154d941ac67b8c75216b625cd0efdf Mon Sep 17 00:00:00 2001
From: Atulya <atultw@outlook.com>
Date: Mon, 27 Jun 2022 12:52:36 -0700
Subject: [PATCH 142/326] swiftlint fix

---
 Sources/SQLite/Typed/Coding.swift | 124 +++++++++++++++---------------
 1 file changed, 62 insertions(+), 62 deletions(-)

diff --git a/Sources/SQLite/Typed/Coding.swift b/Sources/SQLite/Typed/Coding.swift
index e7f5b7bb..ec2e0d6c 100644
--- a/Sources/SQLite/Typed/Coding.swift
+++ b/Sources/SQLite/Typed/Coding.swift
@@ -44,7 +44,7 @@ extension QueryType {
         try encodable.encode(to: encoder)
         return self.insert(encoder.setters + otherSetters)
     }
-    
+
     /// Creates an `INSERT` statement by encoding the given object
     /// This method converts any custom nested types to JSON data and does not handle any sort
     /// of object relationships. If you want to support relationships between objects you will
@@ -69,7 +69,7 @@ extension QueryType {
         try encodable.encode(to: encoder)
         return self.insert(or: onConflict, encoder.setters + otherSetters)
     }
-    
+
     /// Creates a batch `INSERT` statement by encoding the array of given objects
     /// This method converts any custom nested types to JSON data and does not handle any sort
     /// of object relationships. If you want to support relationships between objects you will
@@ -93,7 +93,7 @@ extension QueryType {
         }
         return self.insertMany(combinedSetters)
     }
-    
+
     /// Creates an `INSERT ON CONFLICT DO UPDATE` statement, aka upsert, by encoding the given object
     /// This method converts any custom nested types to JSON data and does not handle any sort
     /// of object relationships. If you want to support relationships between objects you will
@@ -116,7 +116,7 @@ extension QueryType {
         try encodable.encode(to: encoder)
         return self.upsert(encoder.setters + otherSetters, onConflictOf: conflicting)
     }
-    
+
     /// Creates an `UPDATE` statement by encoding the given object
     /// This method converts any custom nested types to JSON data and does not handle any sort
     /// of object relationships. If you want to support relationships between objects you will
@@ -151,7 +151,7 @@ extension Row {
     public func decode<V: Decodable>(userInfo: [CodingUserInfoKey: Any] = [:]) throws -> V {
         try V(from: decoder(userInfo: userInfo))
     }
-    
+
     public func decoder(userInfo: [CodingUserInfoKey: Any] = [:]) -> Decoder {
         SQLiteDecoder(row: self, userInfo: userInfo)
     }
@@ -162,46 +162,46 @@ private class SQLiteEncoder: Encoder {
     class SQLiteKeyedEncodingContainer<MyKey: CodingKey>: KeyedEncodingContainerProtocol {
         // swiftlint:disable nesting
         typealias Key = MyKey
-        
+
         let encoder: SQLiteEncoder
         let codingPath: [CodingKey] = []
-        
+
         init(encoder: SQLiteEncoder) {
             self.encoder = encoder
         }
-        
+
         func superEncoder() -> Swift.Encoder {
             fatalError("SQLiteEncoding does not support super encoders")
         }
-        
+
         func superEncoder(forKey key: Key) -> Swift.Encoder {
             fatalError("SQLiteEncoding does not support super encoders")
         }
-        
+
         func encodeNil(forKey key: SQLiteEncoder.SQLiteKeyedEncodingContainer<Key>.Key) throws {
             encoder.setters.append(Expression<String?>(key.stringValue) <- nil)
         }
-        
+
         func encode(_ value: Int, forKey key: SQLiteEncoder.SQLiteKeyedEncodingContainer<Key>.Key) throws {
             encoder.setters.append(Expression(key.stringValue) <- value)
         }
-        
+
         func encode(_ value: Bool, forKey key: Key) throws {
             encoder.setters.append(Expression(key.stringValue) <- value)
         }
-        
+
         func encode(_ value: Float, forKey key: Key) throws {
             encoder.setters.append(Expression(key.stringValue) <- Double(value))
         }
-        
+
         func encode(_ value: Double, forKey key: Key) throws {
             encoder.setters.append(Expression(key.stringValue) <- value)
         }
-        
+
         func encode(_ value: String, forKey key: Key) throws {
             encoder.setters.append(Expression(key.stringValue) <- value)
         }
-        
+
         func encode<T>(_ value: T, forKey key: Key) throws where T: Swift.Encodable {
             switch value {
             case let data as Data:
@@ -216,77 +216,77 @@ private class SQLiteEncoder: Encoder {
                 encoder.setters.append(Expression(key.stringValue) <- string)
             }
         }
-        
+
         func encode(_ value: Int8, forKey key: Key) throws {
             throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: codingPath,
                                                                           debugDescription: "encoding an Int8 is not supported"))
         }
-        
+
         func encode(_ value: Int16, forKey key: Key) throws {
             throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: codingPath,
                                                                           debugDescription: "encoding an Int16 is not supported"))
         }
-        
+
         func encode(_ value: Int32, forKey key: Key) throws {
             throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: codingPath,
                                                                           debugDescription: "encoding an Int32 is not supported"))
         }
-        
+
         func encode(_ value: Int64, forKey key: Key) throws {
             encoder.setters.append(Expression(key.stringValue) <- value)
         }
-        
+
         func encode(_ value: UInt, forKey key: Key) throws {
             throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: codingPath,
                                                                           debugDescription: "encoding an UInt is not supported"))
         }
-        
+
         func encode(_ value: UInt8, forKey key: Key) throws {
             throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: codingPath,
                                                                           debugDescription: "encoding an UInt8 is not supported"))
         }
-        
+
         func encode(_ value: UInt16, forKey key: Key) throws {
             throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: codingPath,
                                                                           debugDescription: "encoding an UInt16 is not supported"))
         }
-        
+
         func encode(_ value: UInt32, forKey key: Key) throws {
             throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: codingPath,
                                                                           debugDescription: "encoding an UInt32 is not supported"))
         }
-        
+
         func encode(_ value: UInt64, forKey key: Key) throws {
             throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: codingPath,
                                                                           debugDescription: "encoding an UInt64 is not supported"))
         }
-        
+
         func nestedContainer<NestedKey>(keyedBy keyType: NestedKey.Type, forKey key: Key)
         -> KeyedEncodingContainer<NestedKey> where NestedKey: CodingKey {
             fatalError("encoding a nested container is not supported")
         }
-        
+
         func nestedUnkeyedContainer(forKey key: Key) -> UnkeyedEncodingContainer {
             fatalError("encoding nested values is not supported")
         }
     }
-    
+
     fileprivate var setters: [Setter] = []
     let codingPath: [CodingKey] = []
     let userInfo: [CodingUserInfoKey: Any]
-    
+
     init(userInfo: [CodingUserInfoKey: Any]) {
         self.userInfo = userInfo
     }
-    
+
     func singleValueContainer() -> SingleValueEncodingContainer {
         fatalError("not supported")
     }
-    
+
     func unkeyedContainer() -> UnkeyedEncodingContainer {
         fatalError("not supported")
     }
-    
+
     func container<Key>(keyedBy type: Key.Type) -> KeyedEncodingContainer<Key> where Key: CodingKey {
         KeyedEncodingContainer(SQLiteKeyedEncodingContainer(encoder: self))
     }
@@ -295,91 +295,91 @@ private class SQLiteEncoder: Encoder {
 private class SQLiteDecoder: Decoder {
     class SQLiteKeyedDecodingContainer<MyKey: CodingKey>: KeyedDecodingContainerProtocol {
         typealias Key = MyKey
-        
+
         let codingPath: [CodingKey] = []
         let row: Row
-        
+
         init(row: Row) {
             self.row = row
         }
-        
+
         var allKeys: [Key] {
             row.columnNames.keys.compactMap({ Key(stringValue: $0) })
         }
-        
+
         func contains(_ key: Key) -> Bool {
             row.hasValue(for: key.stringValue)
         }
-        
+
         func decodeNil(forKey key: Key) throws -> Bool {
             !contains(key)
         }
-        
+
         func decode(_ type: Bool.Type, forKey key: Key) throws -> Bool {
             try row.get(Expression(key.stringValue))
         }
-        
+
         func decode(_ type: Int.Type, forKey key: Key) throws -> Int {
             try row.get(Expression(key.stringValue))
         }
-        
+
         func decode(_ type: Int8.Type, forKey key: Key) throws -> Int8 {
             throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: codingPath,
                                                                          debugDescription: "decoding an Int8 is not supported"))
         }
-        
+
         func decode(_ type: Int16.Type, forKey key: Key) throws -> Int16 {
             throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: codingPath,
                                                                          debugDescription: "decoding an Int16 is not supported"))
         }
-        
+
         func decode(_ type: Int32.Type, forKey key: Key) throws -> Int32 {
             throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: codingPath,
                                                                          debugDescription: "decoding an Int32 is not supported"))
         }
-        
+
         func decode(_ type: Int64.Type, forKey key: Key) throws -> Int64 {
             try row.get(Expression(key.stringValue))
         }
-        
+
         func decode(_ type: UInt.Type, forKey key: Key) throws -> UInt {
             throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: codingPath,
                                                                          debugDescription: "decoding an UInt is not supported"))
-            
+
         }
-        
+
         func decode(_ type: UInt8.Type, forKey key: Key) throws -> UInt8 {
             throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: codingPath,
                                                                          debugDescription: "decoding an UInt8 is not supported"))
         }
-        
+
         func decode(_ type: UInt16.Type, forKey key: Key) throws -> UInt16 {
             throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: codingPath,
                                                                          debugDescription: "decoding an UInt16 is not supported"))
         }
-        
+
         func decode(_ type: UInt32.Type, forKey key: Key) throws -> UInt32 {
             throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: codingPath,
                                                                          debugDescription: "decoding an UInt32 is not supported"))
         }
-        
+
         func decode(_ type: UInt64.Type, forKey key: Key) throws -> UInt64 {
             throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: codingPath,
                                                                          debugDescription: "decoding an UInt64 is not supported"))
         }
-        
+
         func decode(_ type: Float.Type, forKey key: Key) throws -> Float {
             Float(try row.get(Expression<Double>(key.stringValue)))
         }
-        
+
         func decode(_ type: Double.Type, forKey key: Key) throws -> Double {
             try row.get(Expression(key.stringValue))
         }
-        
+
         func decode(_ type: String.Type, forKey key: Key) throws -> String {
             try row.get(Expression(key.stringValue))
         }
-        
+
         func decode<T>(_ type: T.Type, forKey key: Key) throws -> T where T: Swift.Decodable {
             // swiftlint:disable force_cast
             switch type {
@@ -405,47 +405,47 @@ private class SQLiteDecoder: Decoder {
                 return try JSONDecoder().decode(type, from: data)
             }
         }
-        
+
         func nestedContainer<NestedKey>(keyedBy type: NestedKey.Type, forKey key: Key) throws
         -> KeyedDecodingContainer<NestedKey> where NestedKey: CodingKey {
             throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: codingPath,
                                                                     debugDescription: "decoding nested containers is not supported"))
         }
-        
+
         func nestedUnkeyedContainer(forKey key: Key) throws -> UnkeyedDecodingContainer {
             throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: codingPath,
                                                                     debugDescription: "decoding unkeyed containers is not supported"))
         }
-        
+
         func superDecoder() throws -> Swift.Decoder {
             throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: codingPath,
                                                                     debugDescription: "decoding super encoders containers is not supported"))
         }
-        
+
         func superDecoder(forKey key: Key) throws -> Swift.Decoder {
             throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: codingPath,
                                                                     debugDescription: "decoding super decoders is not supported"))
         }
     }
-    
+
     let row: Row
     let codingPath: [CodingKey] = []
     let userInfo: [CodingUserInfoKey: Any]
-    
+
     init(row: Row, userInfo: [CodingUserInfoKey: Any]) {
         self.row = row
         self.userInfo = userInfo
     }
-    
+
     func container<Key>(keyedBy type: Key.Type) throws -> KeyedDecodingContainer<Key> where Key: CodingKey {
         KeyedDecodingContainer(SQLiteKeyedDecodingContainer(row: row))
     }
-    
+
     func unkeyedContainer() throws -> UnkeyedDecodingContainer {
         throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: codingPath,
                                                                 debugDescription: "decoding an unkeyed container is not supported"))
     }
-    
+
     func singleValueContainer() throws -> SingleValueDecodingContainer {
         throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: codingPath,
                                                                 debugDescription: "decoding a single value container is not supported"))

From 3fac726365dc5813625aed56069c3b9feb446762 Mon Sep 17 00:00:00 2001
From: Atulya <atultw@outlook.com>
Date: Tue, 28 Jun 2022 12:27:52 -0700
Subject: [PATCH 143/326] silence swiftlint line_length for some sql strings

---
 Tests/SQLiteTests/QueryTests.swift | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/Tests/SQLiteTests/QueryTests.swift b/Tests/SQLiteTests/QueryTests.swift
index e4353e52..d9cce086 100644
--- a/Tests/SQLiteTests/QueryTests.swift
+++ b/Tests/SQLiteTests/QueryTests.swift
@@ -300,6 +300,7 @@ class QueryTests: XCTestCase {
         let insert = try emails.insert(value)
         let encodedJSON = try JSONEncoder().encode(value1)
         let encodedJSONString = String(data: encodedJSON, encoding: .utf8)!
+        // swiftlint:disable line_length
         assertSQL(
             """
             INSERT INTO \"emails\" (\"int\", \"string\", \"bool\", \"float\", \"double\", \"date\", \"uuid\", \"optional\",
@@ -307,6 +308,7 @@ class QueryTests: XCTestCase {
             """.replacingOccurrences(of: "\n", with: ""),
             insert
         )
+        // swiftlint:enable line_length
     }
     #endif
 
@@ -352,6 +354,7 @@ class QueryTests: XCTestCase {
         let value = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4,
                                 date: Date(timeIntervalSince1970: 0), uuid: testUUIDValue, optional: nil, sub: nil)
         let insert = try emails.upsert(value, onConflictOf: string)
+        // swiftlint:disable line_length
         assertSQL(
             """
             INSERT INTO \"emails\" (\"int\", \"string\", \"bool\", \"float\", \"double\", \"date\", \"uuid\")
@@ -361,6 +364,7 @@ class QueryTests: XCTestCase {
             """.replacingOccurrences(of: "\n", with: ""),
             insert
         )
+        // swiftlint:enable line_length
     }
 
     func test_insert_many_encodable() throws {
@@ -372,6 +376,7 @@ class QueryTests: XCTestCase {
         let value3 = TestCodable(int: 3, string: "4", bool: true, float: 3, double: 6,
                                  date: Date(timeIntervalSince1970: 0), uuid: testUUIDValue, optional: nil, sub: nil)
         let insert = try emails.insertMany([value1, value2, value3])
+        // swiftlint:disable line_length
         assertSQL(
             """
             INSERT INTO \"emails\" (\"int\", \"string\", \"bool\", \"float\", \"double\", \"date\", \"uuid\")
@@ -380,6 +385,7 @@ class QueryTests: XCTestCase {
             """.replacingOccurrences(of: "\n", with: ""),
             insert
         )
+        // swiftlint:enable line_length
     }
 
     func test_update_compilesUpdateExpression() {

From be3a5ced7e790619f225024db1ac93795b01cf81 Mon Sep 17 00:00:00 2001
From: Atulya <atultw@outlook.com>
Date: Wed, 29 Jun 2022 09:34:32 -0700
Subject: [PATCH 144/326] remove swiftlint line_length disables

---
 Tests/SQLiteTests/QueryTests.swift | 15 ++++++---------
 1 file changed, 6 insertions(+), 9 deletions(-)

diff --git a/Tests/SQLiteTests/QueryTests.swift b/Tests/SQLiteTests/QueryTests.swift
index d9cce086..2201caef 100644
--- a/Tests/SQLiteTests/QueryTests.swift
+++ b/Tests/SQLiteTests/QueryTests.swift
@@ -300,15 +300,14 @@ class QueryTests: XCTestCase {
         let insert = try emails.insert(value)
         let encodedJSON = try JSONEncoder().encode(value1)
         let encodedJSONString = String(data: encodedJSON, encoding: .utf8)!
-        // swiftlint:disable line_length
         assertSQL(
             """
             INSERT INTO \"emails\" (\"int\", \"string\", \"bool\", \"float\", \"double\", \"date\", \"uuid\", \"optional\",
-             \"sub\") VALUES (1, '2', 1, 3.0, 4.0, '1970-01-01T00:00:00.000', 'E621E1F8-C36C-495A-93FC-0C247A3E6E5F', 'optional', '\(encodedJSONString)')
+             \"sub\") VALUES (1, '2', 1, 3.0, 4.0, '1970-01-01T00:00:00.000', 'E621E1F8-C36C-495A-93FC-0C247A3E6E5F',
+             'optional', '\(encodedJSONString)')
             """.replacingOccurrences(of: "\n", with: ""),
             insert
         )
-        // swiftlint:enable line_length
     }
     #endif
 
@@ -354,17 +353,16 @@ class QueryTests: XCTestCase {
         let value = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4,
                                 date: Date(timeIntervalSince1970: 0), uuid: testUUIDValue, optional: nil, sub: nil)
         let insert = try emails.upsert(value, onConflictOf: string)
-        // swiftlint:disable line_length
         assertSQL(
             """
             INSERT INTO \"emails\" (\"int\", \"string\", \"bool\", \"float\", \"double\", \"date\", \"uuid\")
              VALUES (1, '2', 1, 3.0, 4.0, '1970-01-01T00:00:00.000', 'E621E1F8-C36C-495A-93FC-0C247A3E6E5F') ON CONFLICT (\"string\")
              DO UPDATE SET \"int\" = \"excluded\".\"int\", \"bool\" = \"excluded\".\"bool\",
-             \"float\" = \"excluded\".\"float\", \"double\" = \"excluded\".\"double\", \"date\" = \"excluded\".\"date\", \"uuid\" = \"excluded\".\"uuid\"
+             \"float\" = \"excluded\".\"float\", \"double\" = \"excluded\".\"double\", \"date\" = \"excluded\".\"date\",
+             \"uuid\" = \"excluded\".\"uuid\"
             """.replacingOccurrences(of: "\n", with: ""),
             insert
         )
-        // swiftlint:enable line_length
     }
 
     func test_insert_many_encodable() throws {
@@ -376,16 +374,15 @@ class QueryTests: XCTestCase {
         let value3 = TestCodable(int: 3, string: "4", bool: true, float: 3, double: 6,
                                  date: Date(timeIntervalSince1970: 0), uuid: testUUIDValue, optional: nil, sub: nil)
         let insert = try emails.insertMany([value1, value2, value3])
-        // swiftlint:disable line_length
         assertSQL(
             """
             INSERT INTO \"emails\" (\"int\", \"string\", \"bool\", \"float\", \"double\", \"date\", \"uuid\")
-             VALUES (1, '2', 1, 3.0, 4.0, '1970-01-01T00:00:00.000', 'E621E1F8-C36C-495A-93FC-0C247A3E6E5F'), (2, '3', 1, 3.0, 5.0, '1970-01-01T00:00:00.000', 'E621E1F8-C36C-495A-93FC-0C247A3E6E5F'),
+             VALUES (1, '2', 1, 3.0, 4.0, '1970-01-01T00:00:00.000', 'E621E1F8-C36C-495A-93FC-0C247A3E6E5F'),
+             (2, '3', 1, 3.0, 5.0, '1970-01-01T00:00:00.000', 'E621E1F8-C36C-495A-93FC-0C247A3E6E5F'),
              (3, '4', 1, 3.0, 6.0, '1970-01-01T00:00:00.000', 'E621E1F8-C36C-495A-93FC-0C247A3E6E5F')
             """.replacingOccurrences(of: "\n", with: ""),
             insert
         )
-        // swiftlint:enable line_length
     }
 
     func test_update_compilesUpdateExpression() {

From ba5165ac7aaddc3f240f04e54d3f978ae2208a5d Mon Sep 17 00:00:00 2001
From: Geoff MacDonald <geoffmacd@gmail.com>
Date: Fri, 1 Jul 2022 12:36:17 -0700
Subject: [PATCH 145/326] fix insertMany() failing with encodables which have
 assymmetric optional values

---
 Sources/SQLite/Typed/Coding.swift             | 88 +++++++++++++++++--
 Tests/SQLiteTests/QueryIntegrationTests.swift | 21 +++++
 Tests/SQLiteTests/QueryTests.swift            | 12 +--
 Tests/SQLiteTests/TestHelpers.swift           | 20 +++++
 4 files changed, 129 insertions(+), 12 deletions(-)

diff --git a/Sources/SQLite/Typed/Coding.swift b/Sources/SQLite/Typed/Coding.swift
index ec2e0d6c..14a469ab 100644
--- a/Sources/SQLite/Typed/Coding.swift
+++ b/Sources/SQLite/Typed/Coding.swift
@@ -86,12 +86,22 @@ extension QueryType {
     /// - Returns: An `INSERT` statement for the encodable objects
     public func insertMany(_ encodables: [Encodable], userInfo: [CodingUserInfoKey: Any] = [:],
                            otherSetters: [Setter] = []) throws -> Insert {
-        let combinedSetters = try encodables.map { encodable -> [Setter] in
-            let encoder = SQLiteEncoder(userInfo: userInfo)
+        let combinedSettersWithoutNils = try encodables.map { encodable -> [Setter] in
+            let encoder = SQLiteEncoder(userInfo: userInfo, forcingNilValueSetters: false)
             try encodable.encode(to: encoder)
             return encoder.setters + otherSetters
         }
-        return self.insertMany(combinedSetters)
+        // requires the same number of setters per encodable
+        guard Set(combinedSettersWithoutNils.map(\.count)).count == 1 else {
+            // asymmetric sets of value insertions (some nil, some not), requires NULL value to satisfy INSERT query
+            let combinedSymmetricSetters = try encodables.map { encodable -> [Setter] in
+                let encoder = SQLiteEncoder(userInfo: userInfo, forcingNilValueSetters: true)
+                try encodable.encode(to: encoder)
+                return encoder.setters + otherSetters
+            }
+            return self.insertMany(combinedSymmetricSetters)
+        }
+        return self.insertMany(combinedSettersWithoutNils)
     }
 
     /// Creates an `INSERT ON CONFLICT DO UPDATE` statement, aka upsert, by encoding the given object
@@ -165,9 +175,11 @@ private class SQLiteEncoder: Encoder {
 
         let encoder: SQLiteEncoder
         let codingPath: [CodingKey] = []
+        let forcingNilValueSetters: Bool
 
-        init(encoder: SQLiteEncoder) {
+        init(encoder: SQLiteEncoder, forcingNilValueSetters: Bool = false) {
             self.encoder = encoder
+            self.forcingNilValueSetters = forcingNilValueSetters
         }
 
         func superEncoder() -> Swift.Encoder {
@@ -202,6 +214,46 @@ private class SQLiteEncoder: Encoder {
             encoder.setters.append(Expression(key.stringValue) <- value)
         }
 
+        func encodeIfPresent(_ value: Int?, forKey key: SQLiteEncoder.SQLiteKeyedEncodingContainer<Key>.Key) throws {
+            if let value = value {
+                encoder.setters.append(Expression(key.stringValue) <- value)
+            } else if forcingNilValueSetters {
+                encoder.setters.append(Expression<Int?>(key.stringValue) <- nil)
+            }
+        }
+
+        func encodeIfPresent(_ value: Bool?, forKey key: Key) throws {
+            if let value = value {
+                encoder.setters.append(Expression(key.stringValue) <- value)
+            } else if forcingNilValueSetters {
+                encoder.setters.append(Expression<Bool?>(key.stringValue) <- nil)
+            }
+        }
+
+        func encodeIfPresent(_ value: Float?, forKey key: Key) throws {
+            if let value = value {
+                encoder.setters.append(Expression(key.stringValue) <- Double(value))
+            } else if forcingNilValueSetters{
+                encoder.setters.append(Expression<Double?>(key.stringValue) <- nil)
+            }
+        }
+
+        func encodeIfPresent(_ value: Double?, forKey key: Key) throws {
+            if let value = value {
+                encoder.setters.append(Expression(key.stringValue) <- value)
+            } else if forcingNilValueSetters {
+                encoder.setters.append(Expression<Double?>(key.stringValue) <- nil)
+            }
+        }
+
+        func encodeIfPresent(_ value: String?, forKey key: MyKey) throws {
+            if let value = value {
+                encoder.setters.append(Expression(key.stringValue) <- value)
+            } else if forcingNilValueSetters {
+                encoder.setters.append(Expression<String?>(key.stringValue) <- nil)
+            }
+        }
+
         func encode<T>(_ value: T, forKey key: Key) throws where T: Swift.Encodable {
             switch value {
             case let data as Data:
@@ -217,6 +269,28 @@ private class SQLiteEncoder: Encoder {
             }
         }
 
+        func encodeIfPresent<T>(_ value: T?, forKey key: Key) throws where T: Swift.Encodable {
+            guard let value = value else {
+                guard forcingNilValueSetters else {
+                    return
+                }
+                encoder.setters.append(Expression<String?>(key.stringValue) <- nil)
+                return
+            }
+            switch value {
+            case let data as Data:
+                encoder.setters.append(Expression(key.stringValue) <- data)
+            case let date as Date:
+                encoder.setters.append(Expression(key.stringValue) <- date.datatypeValue)
+            case let uuid as UUID:
+                encoder.setters.append(Expression(key.stringValue) <- uuid.datatypeValue)
+            default:
+                let encoded = try JSONEncoder().encode(value)
+                let string = String(data: encoded, encoding: .utf8)
+                encoder.setters.append(Expression(key.stringValue) <- string)
+            }
+        }
+
         func encode(_ value: Int8, forKey key: Key) throws {
             throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: codingPath,
                                                                           debugDescription: "encoding an Int8 is not supported"))
@@ -274,9 +348,11 @@ private class SQLiteEncoder: Encoder {
     fileprivate var setters: [Setter] = []
     let codingPath: [CodingKey] = []
     let userInfo: [CodingUserInfoKey: Any]
+    let forcingNilValueSetters: Bool
 
-    init(userInfo: [CodingUserInfoKey: Any]) {
+    init(userInfo: [CodingUserInfoKey: Any], forcingNilValueSetters: Bool = false) {
         self.userInfo = userInfo
+        self.forcingNilValueSetters = forcingNilValueSetters
     }
 
     func singleValueContainer() -> SingleValueEncodingContainer {
@@ -288,7 +364,7 @@ private class SQLiteEncoder: Encoder {
     }
 
     func container<Key>(keyedBy type: Key.Type) -> KeyedEncodingContainer<Key> where Key: CodingKey {
-        KeyedEncodingContainer(SQLiteKeyedEncodingContainer(encoder: self))
+        KeyedEncodingContainer(SQLiteKeyedEncodingContainer(encoder: self, forcingNilValueSetters: forcingNilValueSetters))
     }
 }
 
diff --git a/Tests/SQLiteTests/QueryIntegrationTests.swift b/Tests/SQLiteTests/QueryIntegrationTests.swift
index b3d9cf96..3ea06745 100644
--- a/Tests/SQLiteTests/QueryIntegrationTests.swift
+++ b/Tests/SQLiteTests/QueryIntegrationTests.swift
@@ -130,6 +130,27 @@ class QueryIntegrationTests: SQLiteTestCase {
         XCTAssertEqual(2, id)
     }
 
+    func test_insert_many_encodables() throws {
+        let table = Table("codable")
+        try db.run(table.create { builder in
+            builder.column(Expression<Int?>("int"))
+            builder.column(Expression<String?>("string"))
+            builder.column(Expression<Bool?>("bool"))
+            builder.column(Expression<Double?>("float"))
+            builder.column(Expression<Double?>("double"))
+            builder.column(Expression<Date?>("date"))
+            builder.column(Expression<UUID?>("uuid"))
+        })
+
+        let value1 = TestOptionalCodable(int: 5, string: "6", bool: true, float: 7, double: 8, date: Date(timeIntervalSince1970: 5000), uuid: testUUIDValue)
+        let valueWithNils = TestOptionalCodable(int: nil, string: nil, bool: nil, float: nil, double: nil, date: nil, uuid: nil)
+        try db.run(table.insertMany([value1, valueWithNils]))
+
+         let rows = try db.prepare(table)
+         let values: [TestOptionalCodable] = try rows.map({ try $0.decode() })
+         XCTAssertEqual(values.count, 2)
+    }
+
     func test_upsert() throws {
         try XCTSkipUnless(db.satisfiesMinimumVersion(minor: 24))
         let fetchAge = { () throws -> Int? in
diff --git a/Tests/SQLiteTests/QueryTests.swift b/Tests/SQLiteTests/QueryTests.swift
index 2201caef..b2f679e0 100644
--- a/Tests/SQLiteTests/QueryTests.swift
+++ b/Tests/SQLiteTests/QueryTests.swift
@@ -365,21 +365,21 @@ class QueryTests: XCTestCase {
         )
     }
 
-    func test_insert_many_encodable() throws {
+    func test_insert_many_encodables() throws {
         let emails = Table("emails")
         let value1 = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4,
                                  date: Date(timeIntervalSince1970: 0), uuid: testUUIDValue, optional: nil, sub: nil)
         let value2 = TestCodable(int: 2, string: "3", bool: true, float: 3, double: 5,
-                                 date: Date(timeIntervalSince1970: 0), uuid: testUUIDValue, optional: nil, sub: nil)
+                                 date: Date(timeIntervalSince1970: 0), uuid: testUUIDValue, optional: "optional", sub: nil)
         let value3 = TestCodable(int: 3, string: "4", bool: true, float: 3, double: 6,
                                  date: Date(timeIntervalSince1970: 0), uuid: testUUIDValue, optional: nil, sub: nil)
         let insert = try emails.insertMany([value1, value2, value3])
         assertSQL(
             """
-            INSERT INTO \"emails\" (\"int\", \"string\", \"bool\", \"float\", \"double\", \"date\", \"uuid\")
-             VALUES (1, '2', 1, 3.0, 4.0, '1970-01-01T00:00:00.000', 'E621E1F8-C36C-495A-93FC-0C247A3E6E5F'),
-             (2, '3', 1, 3.0, 5.0, '1970-01-01T00:00:00.000', 'E621E1F8-C36C-495A-93FC-0C247A3E6E5F'),
-             (3, '4', 1, 3.0, 6.0, '1970-01-01T00:00:00.000', 'E621E1F8-C36C-495A-93FC-0C247A3E6E5F')
+            INSERT INTO \"emails\" (\"int\", \"string\", \"bool\", \"float\", \"double\", \"date\", \"uuid\", \"optional\", \"sub\")
+             VALUES (1, '2', 1, 3.0, 4.0, '1970-01-01T00:00:00.000', 'E621E1F8-C36C-495A-93FC-0C247A3E6E5F', NULL, NULL),
+             (2, '3', 1, 3.0, 5.0, '1970-01-01T00:00:00.000', 'E621E1F8-C36C-495A-93FC-0C247A3E6E5F', 'optional', NULL),
+             (3, '4', 1, 3.0, 6.0, '1970-01-01T00:00:00.000', 'E621E1F8-C36C-495A-93FC-0C247A3E6E5F', NULL, NULL)
             """.replacingOccurrences(of: "\n", with: ""),
             insert
         )
diff --git a/Tests/SQLiteTests/TestHelpers.swift b/Tests/SQLiteTests/TestHelpers.swift
index 4a71fd40..f43310c9 100644
--- a/Tests/SQLiteTests/TestHelpers.swift
+++ b/Tests/SQLiteTests/TestHelpers.swift
@@ -145,3 +145,23 @@ class TestCodable: Codable, Equatable {
         lhs.sub == rhs.sub
     }
 }
+
+struct TestOptionalCodable: Codable, Equatable {
+    let int: Int?
+    let string: String?
+    let bool: Bool?
+    let float: Float?
+    let double: Double?
+    let date: Date?
+    let uuid: UUID?
+
+    init(int: Int?, string: String?, bool: Bool?, float: Float?, double: Double?, date: Date?, uuid: UUID?) {
+        self.int = int
+        self.string = string
+        self.bool = bool
+        self.float = float
+        self.double = double
+        self.date = date
+        self.uuid = uuid
+    }
+}

From c4bdad8f3c47a41d1902d32e988f148e4734075e Mon Sep 17 00:00:00 2001
From: Geoff MacDonald <geoffmacd@gmail.com>
Date: Fri, 1 Jul 2022 12:39:10 -0700
Subject: [PATCH 146/326] lint

---
 Sources/SQLite/Typed/Coding.swift             | 2 +-
 Tests/SQLiteTests/QueryIntegrationTests.swift | 3 ++-
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/Sources/SQLite/Typed/Coding.swift b/Sources/SQLite/Typed/Coding.swift
index 14a469ab..08b7131a 100644
--- a/Sources/SQLite/Typed/Coding.swift
+++ b/Sources/SQLite/Typed/Coding.swift
@@ -233,7 +233,7 @@ private class SQLiteEncoder: Encoder {
         func encodeIfPresent(_ value: Float?, forKey key: Key) throws {
             if let value = value {
                 encoder.setters.append(Expression(key.stringValue) <- Double(value))
-            } else if forcingNilValueSetters{
+            } else if forcingNilValueSetters {
                 encoder.setters.append(Expression<Double?>(key.stringValue) <- nil)
             }
         }
diff --git a/Tests/SQLiteTests/QueryIntegrationTests.swift b/Tests/SQLiteTests/QueryIntegrationTests.swift
index 3ea06745..f3b4bcd3 100644
--- a/Tests/SQLiteTests/QueryIntegrationTests.swift
+++ b/Tests/SQLiteTests/QueryIntegrationTests.swift
@@ -142,7 +142,8 @@ class QueryIntegrationTests: SQLiteTestCase {
             builder.column(Expression<UUID?>("uuid"))
         })
 
-        let value1 = TestOptionalCodable(int: 5, string: "6", bool: true, float: 7, double: 8, date: Date(timeIntervalSince1970: 5000), uuid: testUUIDValue)
+        let value1 = TestOptionalCodable(int: 5, string: "6", bool: true, float: 7, double: 8,
+                                         date: Date(timeIntervalSince1970: 5000), uuid: testUUIDValue)
         let valueWithNils = TestOptionalCodable(int: nil, string: nil, bool: nil, float: nil, double: nil, date: nil, uuid: nil)
         try db.run(table.insertMany([value1, valueWithNils]))
 

From abff512bb57f46cb155a8b9ccf744c72f72a886f Mon Sep 17 00:00:00 2001
From: Geoff MacDonald <geoffmacd@gmail.com>
Date: Sat, 2 Jul 2022 11:04:17 -0700
Subject: [PATCH 147/326] feedback

---
 Sources/SQLite/Typed/Coding.swift | 23 ++++++-----------------
 1 file changed, 6 insertions(+), 17 deletions(-)

diff --git a/Sources/SQLite/Typed/Coding.swift b/Sources/SQLite/Typed/Coding.swift
index 08b7131a..b96bc64e 100644
--- a/Sources/SQLite/Typed/Coding.swift
+++ b/Sources/SQLite/Typed/Coding.swift
@@ -216,7 +216,7 @@ private class SQLiteEncoder: Encoder {
 
         func encodeIfPresent(_ value: Int?, forKey key: SQLiteEncoder.SQLiteKeyedEncodingContainer<Key>.Key) throws {
             if let value = value {
-                encoder.setters.append(Expression(key.stringValue) <- value)
+                try encode(value, forKey: key)
             } else if forcingNilValueSetters {
                 encoder.setters.append(Expression<Int?>(key.stringValue) <- nil)
             }
@@ -224,7 +224,7 @@ private class SQLiteEncoder: Encoder {
 
         func encodeIfPresent(_ value: Bool?, forKey key: Key) throws {
             if let value = value {
-                encoder.setters.append(Expression(key.stringValue) <- value)
+                try encode(value, forKey: key)
             } else if forcingNilValueSetters {
                 encoder.setters.append(Expression<Bool?>(key.stringValue) <- nil)
             }
@@ -232,7 +232,7 @@ private class SQLiteEncoder: Encoder {
 
         func encodeIfPresent(_ value: Float?, forKey key: Key) throws {
             if let value = value {
-                encoder.setters.append(Expression(key.stringValue) <- Double(value))
+                try encode(value, forKey: key)
             } else if forcingNilValueSetters {
                 encoder.setters.append(Expression<Double?>(key.stringValue) <- nil)
             }
@@ -240,7 +240,7 @@ private class SQLiteEncoder: Encoder {
 
         func encodeIfPresent(_ value: Double?, forKey key: Key) throws {
             if let value = value {
-                encoder.setters.append(Expression(key.stringValue) <- value)
+                try encode(value, forKey: key)
             } else if forcingNilValueSetters {
                 encoder.setters.append(Expression<Double?>(key.stringValue) <- nil)
             }
@@ -248,7 +248,7 @@ private class SQLiteEncoder: Encoder {
 
         func encodeIfPresent(_ value: String?, forKey key: MyKey) throws {
             if let value = value {
-                encoder.setters.append(Expression(key.stringValue) <- value)
+                try encode(value, forKey: key)
             } else if forcingNilValueSetters {
                 encoder.setters.append(Expression<String?>(key.stringValue) <- nil)
             }
@@ -277,18 +277,7 @@ private class SQLiteEncoder: Encoder {
                 encoder.setters.append(Expression<String?>(key.stringValue) <- nil)
                 return
             }
-            switch value {
-            case let data as Data:
-                encoder.setters.append(Expression(key.stringValue) <- data)
-            case let date as Date:
-                encoder.setters.append(Expression(key.stringValue) <- date.datatypeValue)
-            case let uuid as UUID:
-                encoder.setters.append(Expression(key.stringValue) <- uuid.datatypeValue)
-            default:
-                let encoded = try JSONEncoder().encode(value)
-                let string = String(data: encoded, encoding: .utf8)
-                encoder.setters.append(Expression(key.stringValue) <- string)
-            }
+            try encode(value, forKey: key)
         }
 
         func encode(_ value: Int8, forKey key: Key) throws {

From 12e2166c90b24f900869b916d9d8b8a6ac24e839 Mon Sep 17 00:00:00 2001
From: Matthew Jee <matthew@marmphco.com>
Date: Thu, 7 Jul 2022 15:38:55 -0700
Subject: [PATCH 148/326] Add support for the WITH clause

The `WITH` clause provides the ability to do hierarchical or recursive queries of tree and graph-like data. See https://www.sqlite.org/lang_with.html.

**Details**

- Add `all` parameter to `QueryType.union` to allow `UNION ALL` to be used in a query. I opted to add the parameter to the start of the list so that it does not dangle at the end when the union's query is long:

```swift
users.union(all: true, posts.join(users, on: users[id] == posts[userId]))
// It's a little easier to read than:
users.union(posts.join(users, on: users[id] == posts[userId]), all: true)
```

- Add `with` function to `QueryType`. This function adds a `WITH` clause to a query. The function may be called multiple times to add multiple clauses to a query. If multiple clauses are added to the query with conflicting `recursive` parameters, the whole `WITH` clause will be considered recursive.

  Like the `union` function, I put the `subquery` parameter at the end so that the `recursive` and `materializationHint` options don't dangle at the end of a long query.

```swift
let users = Table("users")
let users = Table("posts")
let first = Table("first")
let second = Table("second")
first.with(first, recursive: true as: users).with(second, recursive: false, as: posts)
// WITH RECURSIVE "first" AS (SELECT * from users), "second" AS (SELECT * from posts) SELECT * from "first"
```
---
 Sources/SQLite/Typed/Query.swift              | 105 +++++++++++++++++-
 Tests/SQLiteTests/QueryIntegrationTests.swift |  41 +++++++
 Tests/SQLiteTests/QueryTests.swift            |  60 ++++++++++
 3 files changed, 201 insertions(+), 5 deletions(-)

diff --git a/Sources/SQLite/Typed/Query.swift b/Sources/SQLite/Typed/Query.swift
index 93dc2a73..c328aef6 100644
--- a/Sources/SQLite/Typed/Query.swift
+++ b/Sources/SQLite/Typed/Query.swift
@@ -193,12 +193,14 @@ extension QueryType {
     ///
     /// - Parameters:
     ///
+    ///   - all: If false, duplicate rows are removed from the result.
+    ///
     ///   - table: A query representing the other table.
     ///
     /// - Returns: A query with the given `UNION` clause applied.
-    public func union(_ table: QueryType) -> Self {
+    public func union(all: Bool = false, _ table: QueryType) -> Self {
         var query = self
-        query.clauses.union.append(table)
+        query.clauses.union.append((all, table))
         return query
     }
 
@@ -496,6 +498,37 @@ extension QueryType {
         query.clauses.limit = length.map { ($0, offset) }
         return query
     }
+    
+    // MARK: WITH
+    
+    /// Sets a `WITH` clause on the query.
+    ///
+    ///     let users = Table("users")
+    ///     let id = Expression<String>("email")
+    ///     let name = Expression<String?>("name")
+    ///
+    ///     let userNames = Table("user_names")
+    ///     userCategories.with(userNames, as: users.select(name))
+    ///     // WITH "user_names" as (SELECT "name" FROM "users") SELECT * FROM "user_names"
+    ///
+    /// - Parameters:
+    ///
+    ///   -  alias: A name to assign to the table expression.
+    ///
+    ///   -  recursive: Whether to evaluate the expression recursively.
+    ///
+    ///   -  materializationHint: Provides a hint to the query planner for how the expression should be implemented.
+    ///
+    ///   -  subquery: A query that generates the rows for the table expression.
+    ///
+    /// - Returns: A query with the given `ORDER BY` clause applied.
+    public func with(_ alias: Table, columns: [Expressible]? = nil, recursive: Bool = false, materializationHint: MaterializationHint? = nil, as subquery: QueryType) -> Self {
+        var query = self
+        let clause = WithClauses.Clause(alias: alias, columns: columns, materializationHint: materializationHint, query: subquery)
+        query.clauses.with.recursive = query.clauses.with.recursive || recursive
+        query.clauses.with.clauses.append(clause)
+        return query
+    }
 
     // MARK: - Clauses
     //
@@ -596,13 +629,50 @@ extension QueryType {
             return nil
         }
 
-        return " ".join(clauses.union.map { query in
+        return " ".join(clauses.union.map { (all, query) in
             " ".join([
-                Expression<Void>(literal: "UNION"),
+                Expression<Void>(literal: all ? "UNION ALL" : "UNION"),
                 query
             ])
         })
     }
+    
+    fileprivate var withClause: Expressible? {
+        guard !clauses.with.clauses.isEmpty else {
+            return nil
+        }
+
+        let innerClauses = ", ".join(clauses.with.clauses.map { (clause) in
+            let hintExpr: Expression<Void>?
+            if let hint = clause.materializationHint {
+                hintExpr = Expression<Void>(literal: hint.rawValue)
+            } else {
+                hintExpr = nil
+            }
+            
+            let columnExpr: Expression<Void>?
+            if let columns = clause.columns {
+                columnExpr = "".wrap(", ".join(columns))
+            } else {
+                columnExpr = nil
+            }
+            
+            let expressions: [Expressible?] = [
+                clause.alias.tableName(),
+                columnExpr,
+                Expression<Void>(literal: "AS"),
+                hintExpr,
+                "".wrap(clause.query) as Expression<Void>
+            ]
+            
+            return " ".join(expressions.compactMap { $0 })
+        })
+        
+        return " ".join([
+            Expression<Void>(literal: clauses.with.recursive ? "WITH RECURSIVE" : "WITH"),
+            innerClauses
+        ])
+    }
 
     // MARK: -
 
@@ -856,6 +926,7 @@ extension QueryType {
 
     public var expression: Expression<Void> {
         let clauses: [Expressible?] = [
+            withClause,
             selectClause,
             joinClause,
             whereClause,
@@ -1233,8 +1304,30 @@ public enum OnConflict: String {
 
 }
 
+/// Materialization hints for `WITH` clause
+public enum MaterializationHint: String {
+
+    case materialized = "MATERIALIZED"
+
+    case notMaterialized = "NOT MATERIALIZED"
+}
+
 // MARK: - Private
 
+struct WithClauses {
+    struct Clause {
+        var alias: Table
+        var columns: [Expressible]?
+        var materializationHint: MaterializationHint?
+        var query: QueryType
+    }
+    /// The `RECURSIVE` flag is applied to the entire `WITH` clause
+    var recursive: Bool = false
+    
+    /// Each `WITH` clause may have multiple subclauses
+    var clauses: [Clause] = []
+}
+
 public struct QueryClauses {
 
     var select = (distinct: false, columns: [Expression<Void>(literal: "*") as Expressible])
@@ -1251,7 +1344,9 @@ public struct QueryClauses {
 
     var limit: (length: Int, offset: Int?)?
 
-    var union = [QueryType]()
+    var union = [(all: Bool, table: QueryType)]()
+    
+    var with = WithClauses()
 
     fileprivate init(_ name: String, alias: String?, database: String?) {
         from = (name, alias, database)
diff --git a/Tests/SQLiteTests/QueryIntegrationTests.swift b/Tests/SQLiteTests/QueryIntegrationTests.swift
index f3b4bcd3..c1cd97a5 100644
--- a/Tests/SQLiteTests/QueryIntegrationTests.swift
+++ b/Tests/SQLiteTests/QueryIntegrationTests.swift
@@ -230,6 +230,47 @@ class QueryIntegrationTests: SQLiteTestCase {
         let result = Array(try db.prepare(users.select(email).order(Expression<Int>.random()).limit(1)))
         XCTAssertEqual(1, result.count)
     }
+    
+    func test_with_recursive() {
+        let nodes = Table("nodes")
+        let id = Expression<Int64>("id")
+        let parent = Expression<Int64?>("parent")
+        let value = Expression<Int64>("value")
+                
+        try! db.run(nodes.create { builder in
+            builder.column(id)
+            builder.column(parent)
+            builder.column(value)
+        })
+        
+        try! db.run(nodes.insertMany([
+            [id <- 0, parent <- nil, value <- 2],
+            [id <- 1, parent <- 0, value <- 4],
+            [id <- 2, parent <- 0, value <- 9],
+            [id <- 3, parent <- 2, value <- 8],
+            [id <- 4, parent <- 2, value <- 7],
+            [id <- 5, parent <- 4, value <- 3],
+        ]))
+        
+        // Compute the sum of the values of node 5 and its ancestors
+        let ancestors = Table("ancestors")
+        let sum = try! db.scalar(
+            ancestors
+                .select(value.sum)
+                .with(ancestors,
+                      columns: [id, parent, value],
+                      recursive: true,
+                      as: nodes
+                        .where(id == 5)
+                        .union(all: true,
+                               nodes.join(ancestors, on: nodes[id] == ancestors[parent])
+                                    .select(nodes[id], nodes[parent], nodes[value])
+                              )
+                     )
+        )
+        
+        XCTAssertEqual(21, sum)
+    }
 }
 
 extension Connection {
diff --git a/Tests/SQLiteTests/QueryTests.swift b/Tests/SQLiteTests/QueryTests.swift
index b2f679e0..6baf59b3 100644
--- a/Tests/SQLiteTests/QueryTests.swift
+++ b/Tests/SQLiteTests/QueryTests.swift
@@ -58,6 +58,11 @@ class QueryTests: XCTestCase {
     func test_selectDistinct_withStar_compilesSelectClause() {
         assertSQL("SELECT DISTINCT * FROM \"users\"", users.select(distinct: *))
     }
+    
+    func test_union_compilesUnionClause() {
+        assertSQL("SELECT * FROM \"users\" UNION SELECT * FROM \"posts\"", users.union(posts))
+        assertSQL("SELECT * FROM \"users\" UNION ALL SELECT * FROM \"posts\"", users.union(all: true, posts))
+    }
 
     func test_join_compilesJoinClause() {
         assertSQL(
@@ -219,6 +224,61 @@ class QueryTests: XCTestCase {
             users.join(managers, on: managers[id] == users[managerId])
         )
     }
+    
+    func test_with_compilesWithClause() {
+        let temp = Table("temp")
+        
+        assertSQL("WITH \"temp\" AS (SELECT * FROM \"users\") SELECT * FROM \"temp\"",
+                  temp.with(temp, as: users))
+    }
+    
+    func test_with_recursive_compilesWithClause() {
+        let temp = Table("temp")
+        
+        assertSQL("WITH RECURSIVE \"temp\" AS (SELECT * FROM \"users\") SELECT * FROM \"temp\"",
+                  temp.with(temp, recursive: true, as: users))
+        
+        assertSQL("WITH \"temp\" AS (SELECT * FROM \"users\") SELECT * FROM \"temp\"",
+                  temp.with(temp, recursive: false, as: users))
+    }
+    
+    func test_with_materialization_compilesWithClause() {
+        let temp = Table("temp")
+        
+        assertSQL("WITH \"temp\" AS MATERIALIZED (SELECT * FROM \"users\") SELECT * FROM \"temp\"",
+                  temp.with(temp, materializationHint: .materialized, as: users))
+        
+        assertSQL("WITH \"temp\" AS NOT MATERIALIZED (SELECT * FROM \"users\") SELECT * FROM \"temp\"",
+                  temp.with(temp, materializationHint: .notMaterialized, as: users))
+    }
+    
+    func test_with_columns_compilesWithClause() {
+        let temp = Table("temp")
+
+        assertSQL("WITH \"temp\" (\"id\", \"email\") AS (SELECT * FROM \"users\") SELECT * FROM \"temp\"",
+                  temp.with(temp, columns: [id, email], recursive: false, materializationHint: nil, as: users))
+    }
+    
+    func test_with_multiple_compilesWithClause() {
+        let temp = Table("temp")
+        let second = Table("second")
+        let third = Table("third")
+
+        let query = temp
+            .with(temp, recursive: true, as: users)
+            .with(second, recursive: true, as: posts)
+            .with(third, materializationHint: .materialized, as:categories)
+        
+        assertSQL(
+            """
+            WITH RECURSIVE \"temp\" AS (SELECT * FROM \"users\"),
+             \"second\" AS (SELECT * FROM \"posts\"),
+             \"third\" AS MATERIALIZED (SELECT * FROM \"categories\")
+             SELECT * FROM \"temp\"
+            """.replacingOccurrences(of: "\n", with: ""),
+            query
+        )
+    }
 
     func test_insert_compilesInsertExpression() {
         assertSQL(

From 03b56886891d1816bb03567584f7c6b00ae96e75 Mon Sep 17 00:00:00 2001
From: Matthew Jee <matthew@marmphco.com>
Date: Thu, 7 Jul 2022 17:03:31 -0700
Subject: [PATCH 149/326] documentation

---
 Documentation/Index.md | 57 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 57 insertions(+)

diff --git a/Documentation/Index.md b/Documentation/Index.md
index e28b24bb..7b5af41f 100644
--- a/Documentation/Index.md
+++ b/Documentation/Index.md
@@ -34,6 +34,7 @@
         - [Filter Operators and Functions](#filter-operators-and-functions)
       - [Sorting Rows](#sorting-rows)
       - [Limiting and Paging Results](#limiting-and-paging-results)
+      - [Recursive and Hierarchical Queries](#recursive-and-hierarchical-queries)
       - [Aggregation](#aggregation)
   - [Upserting Rows](#upserting-rows)
   - [Updating Rows](#updating-rows)
@@ -1086,6 +1087,62 @@ users.limit(5, offset: 5)
 ```
 
 
+#### Recursive and Hierarchical Queries
+
+We can perform a recursive or hierarchical query using a [query's](#queries) `with`
+function. Column names and a materialization hint can optionally be provided.
+
+```swift
+// Get the management chain for the manager with id == 8
+
+let chain = Table("chain")
+let id = Expression<Int64>("id")
+let managerId = Expression<Int64>("manager_id")
+
+let query = managers
+    .where(id == 8)
+    .union(chain.join(managers, on: chain[managerId] == managers[id])
+
+chain.with(chain, recursive: true, as: query)
+// WITH RECURSIVE
+//   "chain" AS (
+//     SELECT * FROM "managers" WHERE "id" = 8 
+//     UNION 
+//     SELECT * from "chain" 
+//     JOIN "managers" ON "chain"."manager_id" = "managers"."id"
+//   ) 
+// SELECT * FROM "chain"
+
+// Add a "level" column to the query representing manager's position in the chain
+
+let level = Expression<Int64>("level")
+
+let queryWithLevel = 
+    managers
+        .select(id, managerId, 0)
+        .where(id == 8)
+        .union(
+            chain
+                .select(managers[id], managers[manager_id], level + 1)
+                .join(managers, on: chain[managerId] == managers[id])
+        )
+
+chain.with(chain, 
+           columns: [id, managerId, level], 
+           recursive: true,
+           materializationHint: .materialize,
+           as: queryWithLevel)
+// WITH RECURSIVE
+//   "chain" ("id", "manager_id", "level") AS MATERIALIZED (
+//     SELECT ("id", "manager_id", 0) FROM "managers" WHERE "id" = 8 
+//     UNION 
+//     SELECT ("manager"."id", "manager"."manager_id", "level" + 1) FROM "chain" 
+//     JOIN "managers" ON "chain"."manager_id" = "managers"."id"
+//   ) 
+// SELECT * FROM "chain"
+```
+
+
 #### Aggregation
 
 [Queries](#queries) come with a number of functions that quickly return

From 76d34dd8ff668b58d6b5530528460dd6d2786c0e Mon Sep 17 00:00:00 2001
From: Matthew Jee <matthew@marmphco.com>
Date: Thu, 7 Jul 2022 17:36:18 -0700
Subject: [PATCH 150/326] Heed linter warnings

Note sure how to get the line length of Query back down below 500 without harming readability though.
---
 Documentation/Index.md                        |  2 +-
 Sources/SQLite/Typed/Query.swift              | 29 +++++++++--------
 Tests/SQLiteTests/QueryIntegrationTests.swift | 12 +++----
 Tests/SQLiteTests/QueryTests.swift            | 32 +++++++++----------
 4 files changed, 38 insertions(+), 37 deletions(-)

diff --git a/Documentation/Index.md b/Documentation/Index.md
index 7b5af41f..44304c1c 100644
--- a/Documentation/Index.md
+++ b/Documentation/Index.md
@@ -1130,7 +1130,7 @@ let queryWithLevel =
 chain.with(chain, 
            columns: [id, managerId, level], 
            recursive: true,
-           materializationHint: .materialize,
+           hint: .materialize,
            as: queryWithLevel)
 // WITH RECURSIVE
 //   "chain" ("id", "manager_id", "level") AS MATERIALIZED (
diff --git a/Sources/SQLite/Typed/Query.swift b/Sources/SQLite/Typed/Query.swift
index c328aef6..b34570a7 100644
--- a/Sources/SQLite/Typed/Query.swift
+++ b/Sources/SQLite/Typed/Query.swift
@@ -498,9 +498,9 @@ extension QueryType {
         query.clauses.limit = length.map { ($0, offset) }
         return query
     }
-    
+
     // MARK: WITH
-    
+
     /// Sets a `WITH` clause on the query.
     ///
     ///     let users = Table("users")
@@ -517,14 +517,15 @@ extension QueryType {
     ///
     ///   -  recursive: Whether to evaluate the expression recursively.
     ///
-    ///   -  materializationHint: Provides a hint to the query planner for how the expression should be implemented.
+    ///   -  hint: Provides a hint to the query planner for how the expression should be implemented.
     ///
     ///   -  subquery: A query that generates the rows for the table expression.
     ///
     /// - Returns: A query with the given `ORDER BY` clause applied.
-    public func with(_ alias: Table, columns: [Expressible]? = nil, recursive: Bool = false, materializationHint: MaterializationHint? = nil, as subquery: QueryType) -> Self {
+    public func with(_ alias: Table, columns: [Expressible]? = nil, recursive: Bool = false,
+                     hint: MaterializationHint? = nil, as subquery: QueryType) -> Self {
         var query = self
-        let clause = WithClauses.Clause(alias: alias, columns: columns, materializationHint: materializationHint, query: subquery)
+        let clause = WithClauses.Clause(alias: alias, columns: columns, hint: hint, query: subquery)
         query.clauses.with.recursive = query.clauses.with.recursive || recursive
         query.clauses.with.clauses.append(clause)
         return query
@@ -636,7 +637,7 @@ extension QueryType {
             ])
         })
     }
-    
+
     fileprivate var withClause: Expressible? {
         guard !clauses.with.clauses.isEmpty else {
             return nil
@@ -644,19 +645,19 @@ extension QueryType {
 
         let innerClauses = ", ".join(clauses.with.clauses.map { (clause) in
             let hintExpr: Expression<Void>?
-            if let hint = clause.materializationHint {
+            if let hint = clause.hint {
                 hintExpr = Expression<Void>(literal: hint.rawValue)
             } else {
                 hintExpr = nil
             }
-            
+
             let columnExpr: Expression<Void>?
             if let columns = clause.columns {
                 columnExpr = "".wrap(", ".join(columns))
             } else {
                 columnExpr = nil
             }
-            
+
             let expressions: [Expressible?] = [
                 clause.alias.tableName(),
                 columnExpr,
@@ -664,10 +665,10 @@ extension QueryType {
                 hintExpr,
                 "".wrap(clause.query) as Expression<Void>
             ]
-            
+
             return " ".join(expressions.compactMap { $0 })
         })
-        
+
         return " ".join([
             Expression<Void>(literal: clauses.with.recursive ? "WITH RECURSIVE" : "WITH"),
             innerClauses
@@ -1318,12 +1319,12 @@ struct WithClauses {
     struct Clause {
         var alias: Table
         var columns: [Expressible]?
-        var materializationHint: MaterializationHint?
+        var hint: MaterializationHint?
         var query: QueryType
     }
     /// The `RECURSIVE` flag is applied to the entire `WITH` clause
     var recursive: Bool = false
-    
+
     /// Each `WITH` clause may have multiple subclauses
     var clauses: [Clause] = []
 }
@@ -1345,7 +1346,7 @@ public struct QueryClauses {
     var limit: (length: Int, offset: Int?)?
 
     var union = [(all: Bool, table: QueryType)]()
-    
+
     var with = WithClauses()
 
     fileprivate init(_ name: String, alias: String?, database: String?) {
diff --git a/Tests/SQLiteTests/QueryIntegrationTests.swift b/Tests/SQLiteTests/QueryIntegrationTests.swift
index c1cd97a5..db4e2e4e 100644
--- a/Tests/SQLiteTests/QueryIntegrationTests.swift
+++ b/Tests/SQLiteTests/QueryIntegrationTests.swift
@@ -230,28 +230,28 @@ class QueryIntegrationTests: SQLiteTestCase {
         let result = Array(try db.prepare(users.select(email).order(Expression<Int>.random()).limit(1)))
         XCTAssertEqual(1, result.count)
     }
-    
+
     func test_with_recursive() {
         let nodes = Table("nodes")
         let id = Expression<Int64>("id")
         let parent = Expression<Int64?>("parent")
         let value = Expression<Int64>("value")
-                
+
         try! db.run(nodes.create { builder in
             builder.column(id)
             builder.column(parent)
             builder.column(value)
         })
-        
+
         try! db.run(nodes.insertMany([
             [id <- 0, parent <- nil, value <- 2],
             [id <- 1, parent <- 0, value <- 4],
             [id <- 2, parent <- 0, value <- 9],
             [id <- 3, parent <- 2, value <- 8],
             [id <- 4, parent <- 2, value <- 7],
-            [id <- 5, parent <- 4, value <- 3],
+            [id <- 5, parent <- 4, value <- 3]
         ]))
-        
+
         // Compute the sum of the values of node 5 and its ancestors
         let ancestors = Table("ancestors")
         let sum = try! db.scalar(
@@ -268,7 +268,7 @@ class QueryIntegrationTests: SQLiteTestCase {
                               )
                      )
         )
-        
+
         XCTAssertEqual(21, sum)
     }
 }
diff --git a/Tests/SQLiteTests/QueryTests.swift b/Tests/SQLiteTests/QueryTests.swift
index 6baf59b3..a96ff38c 100644
--- a/Tests/SQLiteTests/QueryTests.swift
+++ b/Tests/SQLiteTests/QueryTests.swift
@@ -58,7 +58,7 @@ class QueryTests: XCTestCase {
     func test_selectDistinct_withStar_compilesSelectClause() {
         assertSQL("SELECT DISTINCT * FROM \"users\"", users.select(distinct: *))
     }
-    
+
     func test_union_compilesUnionClause() {
         assertSQL("SELECT * FROM \"users\" UNION SELECT * FROM \"posts\"", users.union(posts))
         assertSQL("SELECT * FROM \"users\" UNION ALL SELECT * FROM \"posts\"", users.union(all: true, posts))
@@ -224,41 +224,41 @@ class QueryTests: XCTestCase {
             users.join(managers, on: managers[id] == users[managerId])
         )
     }
-    
+
     func test_with_compilesWithClause() {
         let temp = Table("temp")
-        
+
         assertSQL("WITH \"temp\" AS (SELECT * FROM \"users\") SELECT * FROM \"temp\"",
                   temp.with(temp, as: users))
     }
-    
+
     func test_with_recursive_compilesWithClause() {
         let temp = Table("temp")
-        
+
         assertSQL("WITH RECURSIVE \"temp\" AS (SELECT * FROM \"users\") SELECT * FROM \"temp\"",
                   temp.with(temp, recursive: true, as: users))
-        
+
         assertSQL("WITH \"temp\" AS (SELECT * FROM \"users\") SELECT * FROM \"temp\"",
                   temp.with(temp, recursive: false, as: users))
     }
-    
+
     func test_with_materialization_compilesWithClause() {
         let temp = Table("temp")
-        
+
         assertSQL("WITH \"temp\" AS MATERIALIZED (SELECT * FROM \"users\") SELECT * FROM \"temp\"",
-                  temp.with(temp, materializationHint: .materialized, as: users))
-        
+                  temp.with(temp, hint: .materialized, as: users))
+
         assertSQL("WITH \"temp\" AS NOT MATERIALIZED (SELECT * FROM \"users\") SELECT * FROM \"temp\"",
-                  temp.with(temp, materializationHint: .notMaterialized, as: users))
+                  temp.with(temp, hint: .notMaterialized, as: users))
     }
-    
+
     func test_with_columns_compilesWithClause() {
         let temp = Table("temp")
 
         assertSQL("WITH \"temp\" (\"id\", \"email\") AS (SELECT * FROM \"users\") SELECT * FROM \"temp\"",
-                  temp.with(temp, columns: [id, email], recursive: false, materializationHint: nil, as: users))
+                  temp.with(temp, columns: [id, email], recursive: false, hint: nil, as: users))
     }
-    
+
     func test_with_multiple_compilesWithClause() {
         let temp = Table("temp")
         let second = Table("second")
@@ -267,8 +267,8 @@ class QueryTests: XCTestCase {
         let query = temp
             .with(temp, recursive: true, as: users)
             .with(second, recursive: true, as: posts)
-            .with(third, materializationHint: .materialized, as:categories)
-        
+            .with(third, hint: .materialized, as: categories)
+
         assertSQL(
             """
             WITH RECURSIVE \"temp\" AS (SELECT * FROM \"users\"),

From bdacf4df697eb73cb1a0887a6b3db776b55b3b0d Mon Sep 17 00:00:00 2001
From: Matthew Jee <matthew@marmphco.com>
Date: Thu, 7 Jul 2022 17:39:50 -0700
Subject: [PATCH 151/326] Slight documentation tweak

---
 Documentation/Index.md | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/Documentation/Index.md b/Documentation/Index.md
index 44304c1c..7a9a390f 100644
--- a/Documentation/Index.md
+++ b/Documentation/Index.md
@@ -1090,7 +1090,7 @@ users.limit(5, offset: 5)
 #### Recursive and Hierarchical Queries
 
 We can perform a recursive or hierarchical query using a [query's](#queries) `with`
-function. Column names and a materialization hint can optionally be provided.
+function.
 
 ```swift
 // Get the management chain for the manager with id == 8
@@ -1112,9 +1112,12 @@ chain.with(chain, recursive: true, as: query)
 //     JOIN "managers" ON "chain"."manager_id" = "managers"."id"
 //   ) 
 // SELECT * FROM "chain"
+```
 
-// Add a "level" column to the query representing manager's position in the chain
+Column names and a materialization hint can optionally be provided.
 
+```swift
+// Add a "level" column to the query representing manager's position in the chain
 let level = Expression<Int64>("level")
 
 let queryWithLevel = 

From ced185132ea17b9ba7391322bc52982f930538bd Mon Sep 17 00:00:00 2001
From: Matthew Jee <matthew@marmphco.com>
Date: Wed, 13 Jul 2022 20:22:25 -0700
Subject: [PATCH 152/326] Move WITH support members to extension in
 Query+with.swift

---
 SQLite.xcodeproj/project.pbxproj      | 10 +++
 Sources/SQLite/Typed/Query+with.swift | 95 +++++++++++++++++++++++++++
 Sources/SQLite/Typed/Query.swift      | 69 -------------------
 3 files changed, 105 insertions(+), 69 deletions(-)
 create mode 100644 Sources/SQLite/Typed/Query+with.swift

diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj
index 61c2773a..fc38e5e9 100644
--- a/SQLite.xcodeproj/project.pbxproj
+++ b/SQLite.xcodeproj/project.pbxproj
@@ -130,6 +130,10 @@
 		49EB68C51F7B3CB400D89D40 /* Coding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49EB68C31F7B3CB400D89D40 /* Coding.swift */; };
 		49EB68C61F7B3CB400D89D40 /* Coding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49EB68C31F7B3CB400D89D40 /* Coding.swift */; };
 		49EB68C71F7B3CB400D89D40 /* Coding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49EB68C31F7B3CB400D89D40 /* Coding.swift */; };
+		997DF2AE287FC06D00F8DF95 /* Query+with.swift in Sources */ = {isa = PBXBuildFile; fileRef = 997DF2AD287FC06D00F8DF95 /* Query+with.swift */; };
+		997DF2AF287FC06D00F8DF95 /* Query+with.swift in Sources */ = {isa = PBXBuildFile; fileRef = 997DF2AD287FC06D00F8DF95 /* Query+with.swift */; };
+		997DF2B0287FC06D00F8DF95 /* Query+with.swift in Sources */ = {isa = PBXBuildFile; fileRef = 997DF2AD287FC06D00F8DF95 /* Query+with.swift */; };
+		997DF2B1287FC06D00F8DF95 /* Query+with.swift in Sources */ = {isa = PBXBuildFile; fileRef = 997DF2AD287FC06D00F8DF95 /* Query+with.swift */; };
 		D4DB368C20C09CFB00D5A58E /* SelectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4DB368A20C09C9B00D5A58E /* SelectTests.swift */; };
 		D4DB368D20C09CFC00D5A58E /* SelectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4DB368A20C09C9B00D5A58E /* SelectTests.swift */; };
 		D4DB368E20C09CFD00D5A58E /* SelectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4DB368A20C09C9B00D5A58E /* SelectTests.swift */; };
@@ -260,6 +264,7 @@
 		3D67B3E51DB2469200A4F4C6 /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = Platforms/WatchOS.platform/Developer/SDKs/WatchOS3.0.sdk/usr/lib/libsqlite3.tbd; sourceTree = DEVELOPER_DIR; };
 		3DDC112E26CDBA0200CE369F /* SQLiteObjc.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SQLiteObjc.h; path = ../SQLiteObjc/include/SQLiteObjc.h; sourceTree = "<group>"; };
 		49EB68C31F7B3CB400D89D40 /* Coding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Coding.swift; sourceTree = "<group>"; };
+		997DF2AD287FC06D00F8DF95 /* Query+with.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Query+with.swift"; sourceTree = "<group>"; };
 		A121AC451CA35C79005A31D1 /* SQLite.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SQLite.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		D4DB368A20C09C9B00D5A58E /* SelectTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectTests.swift; sourceTree = "<group>"; };
 		EE247AD31C3F04ED00AE3E12 /* SQLite.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SQLite.framework; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -499,6 +504,7 @@
 				EE247AFE1C3F06E900AE3E12 /* Expression.swift */,
 				EE247AFF1C3F06E900AE3E12 /* Operators.swift */,
 				EE247B001C3F06E900AE3E12 /* Query.swift */,
+				997DF2AD287FC06D00F8DF95 /* Query+with.swift */,
 				EE247B011C3F06E900AE3E12 /* Schema.swift */,
 				EE247B021C3F06E900AE3E12 /* Setter.swift */,
 				49EB68C31F7B3CB400D89D40 /* Coding.swift */,
@@ -862,6 +868,7 @@
 				19A17FF4A10B44D3937C8CAC /* Errors.swift in Sources */,
 				19A1737286A74F3CF7412906 /* DateAndTimeFunctions.swift in Sources */,
 				19A17073552293CA063BEA66 /* Result.swift in Sources */,
+				997DF2B0287FC06D00F8DF95 /* Query+with.swift in Sources */,
 				19A179B59450FE7C4811AB8A /* Connection+Aggregation.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
@@ -904,6 +911,7 @@
 			files = (
 				3D67B3F91DB246E700A4F4C6 /* SQLiteObjc.m in Sources */,
 				49EB68C71F7B3CB400D89D40 /* Coding.swift in Sources */,
+				997DF2B1287FC06D00F8DF95 /* Query+with.swift in Sources */,
 				3D67B3F71DB246D700A4F4C6 /* Foundation.swift in Sources */,
 				3D67B3F81DB246D700A4F4C6 /* Helpers.swift in Sources */,
 				3D67B3E91DB246D100A4F4C6 /* Statement.swift in Sources */,
@@ -959,6 +967,7 @@
 				19A1792C0520D4E83C2EB075 /* Errors.swift in Sources */,
 				19A17E29278A12BC4F542506 /* DateAndTimeFunctions.swift in Sources */,
 				19A173EFEF0B3BD0B3ED406C /* Result.swift in Sources */,
+				997DF2AE287FC06D00F8DF95 /* Query+with.swift in Sources */,
 				19A176376CB6A94759F7980A /* Connection+Aggregation.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
@@ -1024,6 +1033,7 @@
 				19A17490543609FCED53CACC /* Errors.swift in Sources */,
 				19A17152E32A9585831E3FE0 /* DateAndTimeFunctions.swift in Sources */,
 				19A17F1B3F0A3C96B5ED6D64 /* Result.swift in Sources */,
+				997DF2AF287FC06D00F8DF95 /* Query+with.swift in Sources */,
 				19A170ACC97B19730FB7BA4D /* Connection+Aggregation.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
diff --git a/Sources/SQLite/Typed/Query+with.swift b/Sources/SQLite/Typed/Query+with.swift
new file mode 100644
index 00000000..005f490c
--- /dev/null
+++ b/Sources/SQLite/Typed/Query+with.swift
@@ -0,0 +1,95 @@
+//
+// SQLite.swift
+// https://github.com/stephencelis/SQLite.swift
+// Copyright © 2014-2015 Stephen Celis.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+import Foundation
+
+extension QueryType {
+
+    /// Sets a `WITH` clause on the query.
+    ///
+    ///     let users = Table("users")
+    ///     let id = Expression<String>("email")
+    ///     let name = Expression<String?>("name")
+    ///
+    ///     let userNames = Table("user_names")
+    ///     userCategories.with(userNames, as: users.select(name))
+    ///     // WITH "user_names" as (SELECT "name" FROM "users") SELECT * FROM "user_names"
+    ///
+    /// - Parameters:
+    ///
+    ///   -  alias: A name to assign to the table expression.
+    ///
+    ///   -  recursive: Whether to evaluate the expression recursively.
+    ///
+    ///   -  hint: Provides a hint to the query planner for how the expression should be implemented.
+    ///
+    ///   -  subquery: A query that generates the rows for the table expression.
+    ///
+    /// - Returns: A query with the given `ORDER BY` clause applied.
+    public func with(_ alias: Table, columns: [Expressible]? = nil, recursive: Bool = false,
+                     hint: MaterializationHint? = nil, as subquery: QueryType) -> Self {
+        var query = self
+        let clause = WithClauses.Clause(alias: alias, columns: columns, hint: hint, query: subquery)
+        query.clauses.with.recursive = query.clauses.with.recursive || recursive
+        query.clauses.with.clauses.append(clause)
+        return query
+    }
+
+    /// self.clauses.with transformed to an Expressible
+    var withClause: Expressible? {
+        guard !clauses.with.clauses.isEmpty else {
+            return nil
+        }
+
+        let innerClauses = ", ".join(clauses.with.clauses.map { (clause) in
+            let hintExpr: Expression<Void>?
+            if let hint = clause.hint {
+                hintExpr = Expression<Void>(literal: hint.rawValue)
+            } else {
+                hintExpr = nil
+            }
+
+            let columnExpr: Expression<Void>?
+            if let columns = clause.columns {
+                columnExpr = "".wrap(", ".join(columns))
+            } else {
+                columnExpr = nil
+            }
+
+            let expressions: [Expressible?] = [
+                clause.alias.tableName(),
+                columnExpr,
+                Expression<Void>(literal: "AS"),
+                hintExpr,
+                "".wrap(clause.query) as Expression<Void>
+            ]
+
+            return " ".join(expressions.compactMap { $0 })
+        })
+
+        return " ".join([
+            Expression<Void>(literal: clauses.with.recursive ? "WITH RECURSIVE" : "WITH"),
+            innerClauses
+        ])
+    }
+}
diff --git a/Sources/SQLite/Typed/Query.swift b/Sources/SQLite/Typed/Query.swift
index b34570a7..f688cfe6 100644
--- a/Sources/SQLite/Typed/Query.swift
+++ b/Sources/SQLite/Typed/Query.swift
@@ -499,38 +499,6 @@ extension QueryType {
         return query
     }
 
-    // MARK: WITH
-
-    /// Sets a `WITH` clause on the query.
-    ///
-    ///     let users = Table("users")
-    ///     let id = Expression<String>("email")
-    ///     let name = Expression<String?>("name")
-    ///
-    ///     let userNames = Table("user_names")
-    ///     userCategories.with(userNames, as: users.select(name))
-    ///     // WITH "user_names" as (SELECT "name" FROM "users") SELECT * FROM "user_names"
-    ///
-    /// - Parameters:
-    ///
-    ///   -  alias: A name to assign to the table expression.
-    ///
-    ///   -  recursive: Whether to evaluate the expression recursively.
-    ///
-    ///   -  hint: Provides a hint to the query planner for how the expression should be implemented.
-    ///
-    ///   -  subquery: A query that generates the rows for the table expression.
-    ///
-    /// - Returns: A query with the given `ORDER BY` clause applied.
-    public func with(_ alias: Table, columns: [Expressible]? = nil, recursive: Bool = false,
-                     hint: MaterializationHint? = nil, as subquery: QueryType) -> Self {
-        var query = self
-        let clause = WithClauses.Clause(alias: alias, columns: columns, hint: hint, query: subquery)
-        query.clauses.with.recursive = query.clauses.with.recursive || recursive
-        query.clauses.with.clauses.append(clause)
-        return query
-    }
-
     // MARK: - Clauses
     //
     // MARK: SELECT
@@ -638,43 +606,6 @@ extension QueryType {
         })
     }
 
-    fileprivate var withClause: Expressible? {
-        guard !clauses.with.clauses.isEmpty else {
-            return nil
-        }
-
-        let innerClauses = ", ".join(clauses.with.clauses.map { (clause) in
-            let hintExpr: Expression<Void>?
-            if let hint = clause.hint {
-                hintExpr = Expression<Void>(literal: hint.rawValue)
-            } else {
-                hintExpr = nil
-            }
-
-            let columnExpr: Expression<Void>?
-            if let columns = clause.columns {
-                columnExpr = "".wrap(", ".join(columns))
-            } else {
-                columnExpr = nil
-            }
-
-            let expressions: [Expressible?] = [
-                clause.alias.tableName(),
-                columnExpr,
-                Expression<Void>(literal: "AS"),
-                hintExpr,
-                "".wrap(clause.query) as Expression<Void>
-            ]
-
-            return " ".join(expressions.compactMap { $0 })
-        })
-
-        return " ".join([
-            Expression<Void>(literal: clauses.with.recursive ? "WITH RECURSIVE" : "WITH"),
-            innerClauses
-        ])
-    }
-
     // MARK: -
 
     public func alias(_ aliasName: String) -> Self {

From ed43285b6e02a7244c86c8aa58d6069cf6ed803d Mon Sep 17 00:00:00 2001
From: Matthew Jee <matthew@marmphco.com>
Date: Thu, 14 Jul 2022 20:55:31 -0700
Subject: [PATCH 153/326] Move WithClauses to Query+with.swift

---
 Sources/SQLite/Typed/Query+with.swift | 14 ++++++++++++++
 Sources/SQLite/Typed/Query.swift      | 14 --------------
 2 files changed, 14 insertions(+), 14 deletions(-)

diff --git a/Sources/SQLite/Typed/Query+with.swift b/Sources/SQLite/Typed/Query+with.swift
index 005f490c..697e77fd 100644
--- a/Sources/SQLite/Typed/Query+with.swift
+++ b/Sources/SQLite/Typed/Query+with.swift
@@ -93,3 +93,17 @@ extension QueryType {
         ])
     }
 }
+
+struct WithClauses {
+    struct Clause {
+        var alias: Table
+        var columns: [Expressible]?
+        var hint: MaterializationHint?
+        var query: QueryType
+    }
+    /// The `RECURSIVE` flag is applied to the entire `WITH` clause
+    var recursive: Bool = false
+
+    /// Each `WITH` clause may have multiple subclauses
+    var clauses: [Clause] = []
+}
diff --git a/Sources/SQLite/Typed/Query.swift b/Sources/SQLite/Typed/Query.swift
index f688cfe6..b4f9f46f 100644
--- a/Sources/SQLite/Typed/Query.swift
+++ b/Sources/SQLite/Typed/Query.swift
@@ -1246,20 +1246,6 @@ public enum MaterializationHint: String {
 
 // MARK: - Private
 
-struct WithClauses {
-    struct Clause {
-        var alias: Table
-        var columns: [Expressible]?
-        var hint: MaterializationHint?
-        var query: QueryType
-    }
-    /// The `RECURSIVE` flag is applied to the entire `WITH` clause
-    var recursive: Bool = false
-
-    /// Each `WITH` clause may have multiple subclauses
-    var clauses: [Clause] = []
-}
-
 public struct QueryClauses {
 
     var select = (distinct: false, columns: [Expression<Void>(literal: "*") as Expressible])

From 0dfc7b0452455b5ef0805939ed35cccd66f46429 Mon Sep 17 00:00:00 2001
From: Matthew Jee <matthew@marmphco.com>
Date: Fri, 15 Jul 2022 08:01:00 -0700
Subject: [PATCH 154/326] MaterializationHint > Query+with

---
 Sources/SQLite/Typed/Query+with.swift | 8 ++++++++
 Sources/SQLite/Typed/Query.swift      | 8 --------
 2 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/Sources/SQLite/Typed/Query+with.swift b/Sources/SQLite/Typed/Query+with.swift
index 697e77fd..d06c8896 100644
--- a/Sources/SQLite/Typed/Query+with.swift
+++ b/Sources/SQLite/Typed/Query+with.swift
@@ -94,6 +94,14 @@ extension QueryType {
     }
 }
 
+/// Materialization hints for `WITH` clause
+public enum MaterializationHint: String {
+
+    case materialized = "MATERIALIZED"
+
+    case notMaterialized = "NOT MATERIALIZED"
+}
+
 struct WithClauses {
     struct Clause {
         var alias: Table
diff --git a/Sources/SQLite/Typed/Query.swift b/Sources/SQLite/Typed/Query.swift
index b4f9f46f..cfa7544e 100644
--- a/Sources/SQLite/Typed/Query.swift
+++ b/Sources/SQLite/Typed/Query.swift
@@ -1236,14 +1236,6 @@ public enum OnConflict: String {
 
 }
 
-/// Materialization hints for `WITH` clause
-public enum MaterializationHint: String {
-
-    case materialized = "MATERIALIZED"
-
-    case notMaterialized = "NOT MATERIALIZED"
-}
-
 // MARK: - Private
 
 public struct QueryClauses {

From cfa6010717800bd3ed4bc28bb019223871188853 Mon Sep 17 00:00:00 2001
From: Matthew Jee <matthew@marmphco.com>
Date: Fri, 15 Jul 2022 19:28:56 -0700
Subject: [PATCH 155/326] Split test cases

---
 Tests/SQLiteTests/QueryTests.swift | 14 +++++++++-----
 1 file changed, 9 insertions(+), 5 deletions(-)

diff --git a/Tests/SQLiteTests/QueryTests.swift b/Tests/SQLiteTests/QueryTests.swift
index a96ff38c..eae1d923 100644
--- a/Tests/SQLiteTests/QueryTests.swift
+++ b/Tests/SQLiteTests/QueryTests.swift
@@ -61,6 +61,9 @@ class QueryTests: XCTestCase {
 
     func test_union_compilesUnionClause() {
         assertSQL("SELECT * FROM \"users\" UNION SELECT * FROM \"posts\"", users.union(posts))
+    }
+
+    func test_union_compilesUnionAllClause() {
         assertSQL("SELECT * FROM \"users\" UNION ALL SELECT * FROM \"posts\"", users.union(all: true, posts))
     }
 
@@ -232,21 +235,22 @@ class QueryTests: XCTestCase {
                   temp.with(temp, as: users))
     }
 
-    func test_with_recursive_compilesWithClause() {
+    func test_with_compilesWithRecursiveClause() {
         let temp = Table("temp")
 
         assertSQL("WITH RECURSIVE \"temp\" AS (SELECT * FROM \"users\") SELECT * FROM \"temp\"",
                   temp.with(temp, recursive: true, as: users))
-
-        assertSQL("WITH \"temp\" AS (SELECT * FROM \"users\") SELECT * FROM \"temp\"",
-                  temp.with(temp, recursive: false, as: users))
     }
 
-    func test_with_materialization_compilesWithClause() {
+    func test_with_compilesWithMaterializedClause() {
         let temp = Table("temp")
 
         assertSQL("WITH \"temp\" AS MATERIALIZED (SELECT * FROM \"users\") SELECT * FROM \"temp\"",
                   temp.with(temp, hint: .materialized, as: users))
+    }
+
+    func test_with_compilesWithNotMaterializedClause() {
+        let temp = Table("temp")
 
         assertSQL("WITH \"temp\" AS NOT MATERIALIZED (SELECT * FROM \"users\") SELECT * FROM \"temp\"",
                   temp.with(temp, hint: .notMaterialized, as: users))

From ee905fe357157b6b48ed6641aa5abcf16a6ac354 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Sat, 16 Jul 2022 23:27:17 +0200
Subject: [PATCH 156/326] Add tests for NSURL conformance

---
 Tests/SQLiteTests/FoundationTests.swift | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/Tests/SQLiteTests/FoundationTests.swift b/Tests/SQLiteTests/FoundationTests.swift
index 075e755b..453febcd 100644
--- a/Tests/SQLiteTests/FoundationTests.swift
+++ b/Tests/SQLiteTests/FoundationTests.swift
@@ -26,4 +26,15 @@ class FoundationTests: XCTestCase {
         XCTAssertEqual(UUID(uuidString: "4ABE10C9-FF12-4CD4-90C1-4B429001BAD3"), uuid)
     }
 
+    func testURLFromString() {
+        let string = "http://foo.com"
+        let url = URL.fromDatatypeValue(string)
+        XCTAssertEqual(URL(string: string), url)
+    }
+
+    func testStringFromURL() {
+        let url = URL(string: "http://foo.com")!
+        let string = url.datatypeValue
+        XCTAssertEqual("http://foo.com", string)
+    }
 }

From 9d0c0b9f58c62099e767bf29a901879c3b8be668 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Sun, 17 Jul 2022 15:49:56 +0200
Subject: [PATCH 157/326] Add support for ATTACH/DETACH (#30)

---
 Package.swift                               |   3 +-
 SQLite.xcodeproj/project.pbxproj            |  28 ++
 Sources/SQLite/Core/Connection+Attach.swift |  23 ++
 Sources/SQLite/Core/Connection.swift        |  16 +-
 Sources/SQLite/Core/URIQueryParameter.swift |  52 ++++
 Tests/SQLiteTests/ConnectionTests.swift     | 280 +++++++++++---------
 Tests/SQLiteTests/Fixtures.swift            |  12 +-
 Tests/SQLiteTests/ResultTests.swift         |  52 ++++
 Tests/SQLiteTests/TestHelpers.swift         |   4 +-
 Tests/SQLiteTests/fixtures/test.sqlite      | Bin 0 -> 12288 bytes
 10 files changed, 330 insertions(+), 140 deletions(-)
 create mode 100644 Sources/SQLite/Core/Connection+Attach.swift
 create mode 100644 Sources/SQLite/Core/URIQueryParameter.swift
 create mode 100644 Tests/SQLiteTests/ResultTests.swift
 create mode 100644 Tests/SQLiteTests/fixtures/test.sqlite

diff --git a/Package.swift b/Package.swift
index 24898bca..1130d571 100644
--- a/Package.swift
+++ b/Package.swift
@@ -41,7 +41,8 @@ let package = Package(
             ],
             resources: [
                 .copy("fixtures/encrypted-3.x.sqlite"),
-                .copy("fixtures/encrypted-4.x.sqlite")
+                .copy("fixtures/encrypted-4.x.sqlite"),
+                .copy("fixtures/test.sqlite")
             ]
         )
     ]
diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj
index fc38e5e9..4e01067f 100644
--- a/SQLite.xcodeproj/project.pbxproj
+++ b/SQLite.xcodeproj/project.pbxproj
@@ -126,6 +126,17 @@
 		3DDC113626CDBE1900CE369F /* SQLiteObjc.h in Headers */ = {isa = PBXBuildFile; fileRef = 3DDC112E26CDBA0200CE369F /* SQLiteObjc.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		3DDC113726CDBE1900CE369F /* SQLiteObjc.h in Headers */ = {isa = PBXBuildFile; fileRef = 3DDC112E26CDBA0200CE369F /* SQLiteObjc.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		3DDC113826CDBE1C00CE369F /* SQLiteObjc.h in Headers */ = {isa = PBXBuildFile; fileRef = 3DDC112E26CDBA0200CE369F /* SQLiteObjc.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		3DF7B78828842972005DD8CA /* Connection+Attach.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DF7B78728842972005DD8CA /* Connection+Attach.swift */; };
+		3DF7B78928842972005DD8CA /* Connection+Attach.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DF7B78728842972005DD8CA /* Connection+Attach.swift */; };
+		3DF7B78A28842972005DD8CA /* Connection+Attach.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DF7B78728842972005DD8CA /* Connection+Attach.swift */; };
+		3DF7B78B28842972005DD8CA /* Connection+Attach.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DF7B78728842972005DD8CA /* Connection+Attach.swift */; };
+		3DF7B78D28842C23005DD8CA /* ResultTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DF7B78C28842C23005DD8CA /* ResultTests.swift */; };
+		3DF7B78E28842C23005DD8CA /* ResultTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DF7B78C28842C23005DD8CA /* ResultTests.swift */; };
+		3DF7B78F28842C23005DD8CA /* ResultTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DF7B78C28842C23005DD8CA /* ResultTests.swift */; };
+		3DF7B791288449BA005DD8CA /* URIQueryParameter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DF7B790288449BA005DD8CA /* URIQueryParameter.swift */; };
+		3DF7B792288449BA005DD8CA /* URIQueryParameter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DF7B790288449BA005DD8CA /* URIQueryParameter.swift */; };
+		3DF7B793288449BA005DD8CA /* URIQueryParameter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DF7B790288449BA005DD8CA /* URIQueryParameter.swift */; };
+		3DF7B794288449BA005DD8CA /* URIQueryParameter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DF7B790288449BA005DD8CA /* URIQueryParameter.swift */; };
 		49EB68C41F7B3CB400D89D40 /* Coding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49EB68C31F7B3CB400D89D40 /* Coding.swift */; };
 		49EB68C51F7B3CB400D89D40 /* Coding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49EB68C31F7B3CB400D89D40 /* Coding.swift */; };
 		49EB68C61F7B3CB400D89D40 /* Coding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49EB68C31F7B3CB400D89D40 /* Coding.swift */; };
@@ -263,6 +274,9 @@
 		3D3C3CCB26E5568800759140 /* SQLite.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = SQLite.playground; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
 		3D67B3E51DB2469200A4F4C6 /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = Platforms/WatchOS.platform/Developer/SDKs/WatchOS3.0.sdk/usr/lib/libsqlite3.tbd; sourceTree = DEVELOPER_DIR; };
 		3DDC112E26CDBA0200CE369F /* SQLiteObjc.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SQLiteObjc.h; path = ../SQLiteObjc/include/SQLiteObjc.h; sourceTree = "<group>"; };
+		3DF7B78728842972005DD8CA /* Connection+Attach.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Connection+Attach.swift"; sourceTree = "<group>"; };
+		3DF7B78C28842C23005DD8CA /* ResultTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResultTests.swift; sourceTree = "<group>"; };
+		3DF7B790288449BA005DD8CA /* URIQueryParameter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URIQueryParameter.swift; sourceTree = "<group>"; };
 		49EB68C31F7B3CB400D89D40 /* Coding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Coding.swift; sourceTree = "<group>"; };
 		997DF2AD287FC06D00F8DF95 /* Query+with.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Query+with.swift"; sourceTree = "<group>"; };
 		A121AC451CA35C79005A31D1 /* SQLite.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SQLite.framework; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -461,6 +475,7 @@
 				D4DB368A20C09C9B00D5A58E /* SelectTests.swift */,
 				19A17BA6B4E282C1315A115C /* QueryIntegrationTests.swift */,
 				19A171FDE4D67879B14ACBDF /* FTSIntegrationTests.swift */,
+				3DF7B78C28842C23005DD8CA /* ResultTests.swift */,
 			);
 			name = SQLiteTests;
 			path = Tests/SQLiteTests;
@@ -479,6 +494,8 @@
 				02A43A9722738CF100FEC494 /* Backup.swift */,
 				19A17E723300E5ED3771DCB5 /* Result.swift */,
 				19A175A9CB446640AE6F2200 /* Connection+Aggregation.swift */,
+				3DF7B78728842972005DD8CA /* Connection+Attach.swift */,
+				3DF7B790288449BA005DD8CA /* URIQueryParameter.swift */,
 			);
 			path = Core;
 			sourceTree = "<group>";
@@ -847,12 +864,14 @@
 				49EB68C61F7B3CB400D89D40 /* Coding.swift in Sources */,
 				03A65E761C6BB2E60062603F /* Blob.swift in Sources */,
 				03A65E7D1C6BB2F70062603F /* RTree.swift in Sources */,
+				3DF7B793288449BA005DD8CA /* URIQueryParameter.swift in Sources */,
 				03A65E791C6BB2EF0062603F /* SQLiteObjc.m in Sources */,
 				03A65E7B1C6BB2F70062603F /* Value.swift in Sources */,
 				03A65E821C6BB2FB0062603F /* Expression.swift in Sources */,
 				03A65E731C6BB2D80062603F /* Foundation.swift in Sources */,
 				03A65E7F1C6BB2FB0062603F /* Collation.swift in Sources */,
 				03A65E861C6BB2FB0062603F /* Setter.swift in Sources */,
+				3DF7B78A28842972005DD8CA /* Connection+Attach.swift in Sources */,
 				03A65E811C6BB2FB0062603F /* CustomFunctions.swift in Sources */,
 				03A65E7A1C6BB2F70062603F /* Statement.swift in Sources */,
 				03A65E741C6BB2DA0062603F /* Helpers.swift in Sources */,
@@ -877,6 +896,7 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				3DF7B78F28842C23005DD8CA /* ResultTests.swift in Sources */,
 				03A65E881C6BB3030062603F /* BlobTests.swift in Sources */,
 				03A65E901C6BB3030062603F /* RTreeTests.swift in Sources */,
 				03A65E941C6BB3030062603F /* ValueTests.swift in Sources */,
@@ -910,6 +930,7 @@
 			buildActionMask = 2147483647;
 			files = (
 				3D67B3F91DB246E700A4F4C6 /* SQLiteObjc.m in Sources */,
+				3DF7B78B28842972005DD8CA /* Connection+Attach.swift in Sources */,
 				49EB68C71F7B3CB400D89D40 /* Coding.swift in Sources */,
 				997DF2B1287FC06D00F8DF95 /* Query+with.swift in Sources */,
 				3D67B3F71DB246D700A4F4C6 /* Foundation.swift in Sources */,
@@ -925,6 +946,7 @@
 				3D67B3F11DB246D100A4F4C6 /* CustomFunctions.swift in Sources */,
 				3D67B3F21DB246D100A4F4C6 /* Expression.swift in Sources */,
 				3D67B3F31DB246D100A4F4C6 /* Operators.swift in Sources */,
+				3DF7B794288449BA005DD8CA /* URIQueryParameter.swift in Sources */,
 				3D67B3F41DB246D100A4F4C6 /* Query.swift in Sources */,
 				3D67B3F51DB246D100A4F4C6 /* Schema.swift in Sources */,
 				3D67B3F61DB246D100A4F4C6 /* Setter.swift in Sources */,
@@ -946,12 +968,14 @@
 				49EB68C41F7B3CB400D89D40 /* Coding.swift in Sources */,
 				EE247B0A1C3F06E900AE3E12 /* RTree.swift in Sources */,
 				EE247B031C3F06E900AE3E12 /* Blob.swift in Sources */,
+				3DF7B791288449BA005DD8CA /* URIQueryParameter.swift in Sources */,
 				EE247B0B1C3F06E900AE3E12 /* Foundation.swift in Sources */,
 				EE247B041C3F06E900AE3E12 /* Connection.swift in Sources */,
 				EE247B111C3F06E900AE3E12 /* Expression.swift in Sources */,
 				EE247B0C1C3F06E900AE3E12 /* Helpers.swift in Sources */,
 				EE247B0E1C3F06E900AE3E12 /* Collation.swift in Sources */,
 				EE247B151C3F06E900AE3E12 /* Setter.swift in Sources */,
+				3DF7B78828842972005DD8CA /* Connection+Attach.swift in Sources */,
 				EE247B101C3F06E900AE3E12 /* CustomFunctions.swift in Sources */,
 				EE247B091C3F06E900AE3E12 /* FTS4.swift in Sources */,
 				EE247B081C3F06E900AE3E12 /* Value.swift in Sources */,
@@ -976,6 +1000,7 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				3DF7B78D28842C23005DD8CA /* ResultTests.swift in Sources */,
 				EE247B261C3F137700AE3E12 /* CoreFunctionsTests.swift in Sources */,
 				EE247B291C3F137700AE3E12 /* FTS4Tests.swift in Sources */,
 				EE247B191C3F134A00AE3E12 /* SetterTests.swift in Sources */,
@@ -1012,12 +1037,14 @@
 				49EB68C51F7B3CB400D89D40 /* Coding.swift in Sources */,
 				EE247B651C3F3FEC00AE3E12 /* Blob.swift in Sources */,
 				EE247B6C1C3F3FEC00AE3E12 /* RTree.swift in Sources */,
+				3DF7B792288449BA005DD8CA /* URIQueryParameter.swift in Sources */,
 				EE247B681C3F3FEC00AE3E12 /* SQLiteObjc.m in Sources */,
 				EE247B6A1C3F3FEC00AE3E12 /* Value.swift in Sources */,
 				EE247B711C3F3FEC00AE3E12 /* Expression.swift in Sources */,
 				EE247B631C3F3FDB00AE3E12 /* Foundation.swift in Sources */,
 				EE247B6E1C3F3FEC00AE3E12 /* Collation.swift in Sources */,
 				EE247B751C3F3FEC00AE3E12 /* Setter.swift in Sources */,
+				3DF7B78928842972005DD8CA /* Connection+Attach.swift in Sources */,
 				EE247B701C3F3FEC00AE3E12 /* CustomFunctions.swift in Sources */,
 				EE247B691C3F3FEC00AE3E12 /* Statement.swift in Sources */,
 				EE247B641C3F3FDB00AE3E12 /* Helpers.swift in Sources */,
@@ -1042,6 +1069,7 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				3DF7B78E28842C23005DD8CA /* ResultTests.swift in Sources */,
 				EE247B561C3F3FC700AE3E12 /* CoreFunctionsTests.swift in Sources */,
 				EE247B5A1C3F3FC700AE3E12 /* OperatorsTests.swift in Sources */,
 				EE247B541C3F3FC700AE3E12 /* BlobTests.swift in Sources */,
diff --git a/Sources/SQLite/Core/Connection+Attach.swift b/Sources/SQLite/Core/Connection+Attach.swift
new file mode 100644
index 00000000..b9c08117
--- /dev/null
+++ b/Sources/SQLite/Core/Connection+Attach.swift
@@ -0,0 +1,23 @@
+import Foundation
+#if SQLITE_SWIFT_STANDALONE
+import sqlite3
+#elseif SQLITE_SWIFT_SQLCIPHER
+import SQLCipher
+#elseif os(Linux)
+import CSQLite
+#else
+import SQLite3
+#endif
+
+extension Connection {
+
+    /// See  https://www3.sqlite.org/lang_attach.html
+    public func attach(_ location: Location, as schemaName: String) throws {
+        try run("ATTACH DATABASE ? AS ?", location.description, schemaName)
+    }
+
+    /// See https://www3.sqlite.org/lang_detach.html
+    public func detach(_ schemaName: String) throws {
+        try run("DETACH DATABASE ?", schemaName)
+    }
+}
diff --git a/Sources/SQLite/Core/Connection.swift b/Sources/SQLite/Core/Connection.swift
index 72e8d857..60b9f1bd 100644
--- a/Sources/SQLite/Core/Connection.swift
+++ b/Sources/SQLite/Core/Connection.swift
@@ -55,7 +55,8 @@ public final class Connection {
         /// See: <https://www.sqlite.org/uri.html>
         ///
         /// - Parameter filename: A URI filename
-        case uri(String)
+        /// - Parameter parameters: optional query parameters
+        case uri(String, parameters: [URIQueryParameter] = [])
     }
 
     /// An SQL operation passed to update callbacks.
@@ -727,8 +728,17 @@ extension Connection.Location: CustomStringConvertible {
             return ":memory:"
         case .temporary:
             return ""
-        case .uri(let URI):
-            return URI
+        case let .uri(URI, parameters):
+            guard parameters.count > 0,
+                  var components = URLComponents(string: URI) else {
+                return URI
+            }
+            components.queryItems =
+                (components.queryItems ?? []) + parameters.map(\.queryItem)
+            if components.scheme == nil {
+                components.scheme = "file"
+            }
+            return components.description
         }
     }
 
diff --git a/Sources/SQLite/Core/URIQueryParameter.swift b/Sources/SQLite/Core/URIQueryParameter.swift
new file mode 100644
index 00000000..c82a7cf7
--- /dev/null
+++ b/Sources/SQLite/Core/URIQueryParameter.swift
@@ -0,0 +1,52 @@
+import Foundation
+
+public enum URIQueryParameter: CustomStringConvertible {
+    public enum FileMode: String {
+        case readOnly = "ro", readWrite = "rw", readWriteCreate = "rwc", memory
+    }
+
+    public enum CacheMode: String {
+        case shared, `private`
+    }
+
+    /// The cache query parameter determines if the new database is opened using shared cache mode or with a private cache.
+    case cache(CacheMode)
+
+    /// The immutable query parameter is a boolean that signals to SQLite that the underlying database file is held on read-only media
+    /// and cannot be modified, even by another process with elevated privileges.
+    case immutable(Bool)
+
+    /// When creating a new database file during `sqlite3_open_v2()` on unix systems, SQLite will try to set the permissions of the new database
+    /// file to match the existing file "filename".
+    case modeOf(String)
+
+    /// The mode query parameter determines if the new database is opened read-only, read-write, read-write and created if it does not exist,
+    /// or that the database is a pure in-memory database that never interacts with disk, respectively.
+    case mode(FileMode)
+
+    /// The nolock query parameter is a boolean that disables all calls to the `xLock`, ` xUnlock`, and `xCheckReservedLock` methods
+    /// of the VFS when true.
+    case nolock(Bool)
+
+    /// The psow query parameter overrides the `powersafe_overwrite` property of the database file being opened.
+    case psow(Bool)
+
+    /// The vfs query parameter causes the database connection to be opened using the VFS called NAME.
+    case vfs(String)
+
+    public var description: String {
+        queryItem.description
+    }
+
+    var queryItem: URLQueryItem {
+        switch self {
+        case .cache(let mode): return .init(name: "cache", value: mode.rawValue)
+        case .immutable(let bool): return .init(name: "immutable", value: NSNumber(value: bool).description)
+        case .modeOf(let filename): return .init(name: "modeOf", value: filename)
+        case .mode(let fileMode): return .init(name: "mode", value: fileMode.rawValue)
+        case .nolock(let bool): return .init(name: "nolock", value: NSNumber(value: bool).description)
+        case .psow(let bool): return .init(name: "psow", value: NSNumber(value: bool).description)
+        case .vfs(let name): return .init(name: "vfs", value: name)
+        }
+    }
+}
diff --git a/Tests/SQLiteTests/ConnectionTests.swift b/Tests/SQLiteTests/ConnectionTests.swift
index 136271a4..cf51c104 100644
--- a/Tests/SQLiteTests/ConnectionTests.swift
+++ b/Tests/SQLiteTests/ConnectionTests.swift
@@ -20,39 +20,49 @@ class ConnectionTests: SQLiteTestCase {
         try createUsersTable()
     }
 
-    func test_init_withInMemory_returnsInMemoryConnection() {
-        let db = try! Connection(.inMemory)
+    func test_init_withInMemory_returnsInMemoryConnection() throws {
+        let db = try Connection(.inMemory)
         XCTAssertEqual("", db.description)
     }
 
-    func test_init_returnsInMemoryByDefault() {
-        let db = try! Connection()
+    func test_init_returnsInMemoryByDefault() throws {
+        let db = try Connection()
         XCTAssertEqual("", db.description)
     }
 
-    func test_init_withTemporary_returnsTemporaryConnection() {
-        let db = try! Connection(.temporary)
+    func test_init_withTemporary_returnsTemporaryConnection() throws {
+        let db = try Connection(.temporary)
         XCTAssertEqual("", db.description)
     }
 
-    func test_init_withURI_returnsURIConnection() {
-        let db = try! Connection(.uri("\(NSTemporaryDirectory())/SQLite.swift Tests.sqlite3"))
+    func test_init_withURI_returnsURIConnection() throws {
+        let db = try Connection(.uri("\(NSTemporaryDirectory())/SQLite.swift Tests.sqlite3"))
         let url = URL(fileURLWithPath: db.description)
         XCTAssertEqual(url.lastPathComponent, "SQLite.swift Tests.sqlite3")
     }
 
-    func test_init_withString_returnsURIConnection() {
-        let db = try! Connection("\(NSTemporaryDirectory())/SQLite.swift Tests.sqlite3")
+    func test_init_withString_returnsURIConnection() throws {
+        let db = try Connection("\(NSTemporaryDirectory())/SQLite.swift Tests.sqlite3")
         let url = URL(fileURLWithPath: db.description)
         XCTAssertEqual(url.lastPathComponent, "SQLite.swift Tests.sqlite3")
     }
 
+    func testLocationWithoutUriParameters() {
+        let location: Connection.Location = .uri("foo")
+        XCTAssertEqual(location.description, "foo")
+    }
+
+    func testLocationWithUriParameters() {
+        let location: Connection.Location = .uri("foo", parameters: [.mode(.readOnly), .cache(.private)])
+        XCTAssertEqual(location.description, "file:foo?mode=ro&cache=private")
+    }
+
     func test_readonly_returnsFalseOnReadWriteConnections() {
         XCTAssertFalse(db.readonly)
     }
 
-    func test_readonly_returnsTrueOnReadOnlyConnections() {
-        let db = try! Connection(readonly: true)
+    func test_readonly_returnsTrueOnReadOnlyConnections() throws {
+        let db = try Connection(readonly: true)
         XCTAssertTrue(db.readonly)
     }
 
@@ -60,14 +70,14 @@ class ConnectionTests: SQLiteTestCase {
         XCTAssertEqual(0, db.changes)
     }
 
-    func test_lastInsertRowid_returnsLastIdAfterInserts() {
-        try! insertUser("alice")
+    func test_lastInsertRowid_returnsLastIdAfterInserts() throws {
+        try insertUser("alice")
         XCTAssertEqual(1, db.lastInsertRowid)
     }
 
-    func test_lastInsertRowid_doesNotResetAfterError() {
+    func test_lastInsertRowid_doesNotResetAfterError() throws {
         XCTAssert(db.lastInsertRowid == 0)
-        try! insertUser("alice")
+        try insertUser("alice")
         XCTAssertEqual(1, db.lastInsertRowid)
         XCTAssertThrowsError(
             try db.run("INSERT INTO \"users\" (email, age, admin) values ('invalid@example.com', 12, 'invalid')")
@@ -81,18 +91,18 @@ class ConnectionTests: SQLiteTestCase {
         XCTAssertEqual(1, db.lastInsertRowid)
     }
 
-    func test_changes_returnsNumberOfChanges() {
-        try! insertUser("alice")
+    func test_changes_returnsNumberOfChanges() throws {
+        try insertUser("alice")
         XCTAssertEqual(1, db.changes)
-        try! insertUser("betsy")
+        try insertUser("betsy")
         XCTAssertEqual(1, db.changes)
     }
 
-    func test_totalChanges_returnsTotalNumberOfChanges() {
+    func test_totalChanges_returnsTotalNumberOfChanges() throws {
         XCTAssertEqual(0, db.totalChanges)
-        try! insertUser("alice")
+        try insertUser("alice")
         XCTAssertEqual(1, db.totalChanges)
-        try! insertUser("betsy")
+        try insertUser("betsy")
         XCTAssertEqual(2, db.totalChanges)
     }
 
@@ -101,53 +111,53 @@ class ConnectionTests: SQLiteTestCase {
         XCTAssertEqual(2, db.userVersion!)
     }
 
-    func test_prepare_preparesAndReturnsStatements() {
-        _ = try! db.prepare("SELECT * FROM users WHERE admin = 0")
-        _ = try! db.prepare("SELECT * FROM users WHERE admin = ?", 0)
-        _ = try! db.prepare("SELECT * FROM users WHERE admin = ?", [0])
-        _ = try! db.prepare("SELECT * FROM users WHERE admin = $admin", ["$admin": 0])
+    func test_prepare_preparesAndReturnsStatements() throws {
+        _ = try db.prepare("SELECT * FROM users WHERE admin = 0")
+        _ = try db.prepare("SELECT * FROM users WHERE admin = ?", 0)
+        _ = try db.prepare("SELECT * FROM users WHERE admin = ?", [0])
+        _ = try db.prepare("SELECT * FROM users WHERE admin = $admin", ["$admin": 0])
     }
 
-    func test_run_preparesRunsAndReturnsStatements() {
-        try! db.run("SELECT * FROM users WHERE admin = 0")
-        try! db.run("SELECT * FROM users WHERE admin = ?", 0)
-        try! db.run("SELECT * FROM users WHERE admin = ?", [0])
-        try! db.run("SELECT * FROM users WHERE admin = $admin", ["$admin": 0])
+    func test_run_preparesRunsAndReturnsStatements() throws {
+        try db.run("SELECT * FROM users WHERE admin = 0")
+        try db.run("SELECT * FROM users WHERE admin = ?", 0)
+        try db.run("SELECT * FROM users WHERE admin = ?", [0])
+        try db.run("SELECT * FROM users WHERE admin = $admin", ["$admin": 0])
         assertSQL("SELECT * FROM users WHERE admin = 0", 4)
     }
 
-    func test_vacuum() {
-        try! db.vacuum()
+    func test_vacuum() throws {
+        try db.vacuum()
     }
 
-    func test_scalar_preparesRunsAndReturnsScalarValues() {
-        XCTAssertEqual(0, try! db.scalar("SELECT count(*) FROM users WHERE admin = 0") as? Int64)
-        XCTAssertEqual(0, try! db.scalar("SELECT count(*) FROM users WHERE admin = ?", 0) as? Int64)
-        XCTAssertEqual(0, try! db.scalar("SELECT count(*) FROM users WHERE admin = ?", [0]) as? Int64)
-        XCTAssertEqual(0, try! db.scalar("SELECT count(*) FROM users WHERE admin = $admin", ["$admin": 0]) as? Int64)
+    func test_scalar_preparesRunsAndReturnsScalarValues() throws {
+        XCTAssertEqual(0, try db.scalar("SELECT count(*) FROM users WHERE admin = 0") as? Int64)
+        XCTAssertEqual(0, try db.scalar("SELECT count(*) FROM users WHERE admin = ?", 0) as? Int64)
+        XCTAssertEqual(0, try db.scalar("SELECT count(*) FROM users WHERE admin = ?", [0]) as? Int64)
+        XCTAssertEqual(0, try db.scalar("SELECT count(*) FROM users WHERE admin = $admin", ["$admin": 0]) as? Int64)
         assertSQL("SELECT count(*) FROM users WHERE admin = 0", 4)
     }
 
-    func test_execute_comment() {
-        try! db.run("-- this is a comment\nSELECT 1")
+    func test_execute_comment() throws {
+        try db.run("-- this is a comment\nSELECT 1")
         assertSQL("-- this is a comment", 0)
         assertSQL("SELECT 1", 0)
     }
 
-    func test_transaction_executesBeginDeferred() {
-        try! db.transaction(.deferred) {}
+    func test_transaction_executesBeginDeferred() throws {
+        try db.transaction(.deferred) {}
 
         assertSQL("BEGIN DEFERRED TRANSACTION")
     }
 
-    func test_transaction_executesBeginImmediate() {
-        try! db.transaction(.immediate) {}
+    func test_transaction_executesBeginImmediate() throws {
+        try db.transaction(.immediate) {}
 
         assertSQL("BEGIN IMMEDIATE TRANSACTION")
     }
 
-    func test_transaction_executesBeginExclusive() {
-        try! db.transaction(.exclusive) {}
+    func test_transaction_executesBeginExclusive() throws {
+        try db.transaction(.exclusive) {}
 
         assertSQL("BEGIN EXCLUSIVE TRANSACTION")
     }
@@ -164,10 +174,10 @@ class ConnectionTests: SQLiteTestCase {
         XCTAssertEqual(users.map { $0[0] as? String }, ["alice@example.com", "betsy@example.com"])
     }
 
-    func test_transaction_beginsAndCommitsTransactions() {
-        let stmt = try! db.prepare("INSERT INTO users (email) VALUES (?)", "alice@example.com")
+    func test_transaction_beginsAndCommitsTransactions() throws {
+        let stmt = try db.prepare("INSERT INTO users (email) VALUES (?)", "alice@example.com")
 
-        try! db.transaction {
+        try db.transaction {
             try stmt.run()
         }
 
@@ -177,8 +187,8 @@ class ConnectionTests: SQLiteTestCase {
         assertSQL("ROLLBACK TRANSACTION", 0)
     }
 
-    func test_transaction_rollsBackTransactionsIfCommitsFail() {
-        let sqliteVersion = String(describing: try! db.scalar("SELECT sqlite_version()")!)
+    func test_transaction_rollsBackTransactionsIfCommitsFail() throws {
+        let sqliteVersion = String(describing: try db.scalar("SELECT sqlite_version()")!)
             .split(separator: ".").compactMap { Int($0) }
         // PRAGMA defer_foreign_keys only supported in SQLite >= 3.8.0
         guard sqliteVersion[0] == 3 && sqliteVersion[1] >= 8 else {
@@ -187,9 +197,9 @@ class ConnectionTests: SQLiteTestCase {
         }
         // This test case needs to emulate an environment where the individual statements succeed, but committing the
         // transaction fails. Using deferred foreign keys is one option to achieve this.
-        try! db.execute("PRAGMA foreign_keys = ON;")
-        try! db.execute("PRAGMA defer_foreign_keys = ON;")
-        let stmt = try! db.prepare("INSERT INTO users (email, manager_id) VALUES (?, ?)", "alice@example.com", 100)
+        try db.execute("PRAGMA foreign_keys = ON;")
+        try db.execute("PRAGMA defer_foreign_keys = ON;")
+        let stmt = try db.prepare("INSERT INTO users (email, manager_id) VALUES (?, ?)", "alice@example.com", 100)
 
         do {
             try db.transaction {
@@ -209,14 +219,14 @@ class ConnectionTests: SQLiteTestCase {
 
         // Run another transaction to ensure that a subsequent transaction does not fail with an "cannot start a
         // transaction within a transaction" error.
-        let stmt2 = try! db.prepare("INSERT INTO users (email) VALUES (?)", "alice@example.com")
-        try! db.transaction {
+        let stmt2 = try db.prepare("INSERT INTO users (email) VALUES (?)", "alice@example.com")
+        try db.transaction {
             try stmt2.run()
         }
     }
 
-    func test_transaction_beginsAndRollsTransactionsBack() {
-        let stmt = try! db.prepare("INSERT INTO users (email) VALUES (?)", "alice@example.com")
+    func test_transaction_beginsAndRollsTransactionsBack() throws {
+        let stmt = try db.prepare("INSERT INTO users (email) VALUES (?)", "alice@example.com")
 
         do {
             try db.transaction {
@@ -232,8 +242,8 @@ class ConnectionTests: SQLiteTestCase {
         assertSQL("COMMIT TRANSACTION", 0)
     }
 
-    func test_savepoint_beginsAndCommitsSavepoints() {
-        try! db.savepoint("1") {
+    func test_savepoint_beginsAndCommitsSavepoints() throws {
+        try db.savepoint("1") {
             try db.savepoint("2") {
                 try db.run("INSERT INTO users (email) VALUES (?)", "alice@example.com")
             }
@@ -248,8 +258,8 @@ class ConnectionTests: SQLiteTestCase {
         assertSQL("ROLLBACK TO SAVEPOINT '1'", 0)
     }
 
-    func test_savepoint_beginsAndRollsSavepointsBack() {
-        let stmt = try! db.prepare("INSERT INTO users (email) VALUES (?)", "alice@example.com")
+    func test_savepoint_beginsAndRollsSavepointsBack() throws {
+        let stmt = try db.prepare("INSERT INTO users (email) VALUES (?)", "alice@example.com")
 
         do {
             try db.savepoint("1") {
@@ -276,8 +286,8 @@ class ConnectionTests: SQLiteTestCase {
         assertSQL("RELEASE SAVEPOINT '1'", 0)
     }
 
-    func test_updateHook_setsUpdateHook_withInsert() {
-        async { done in
+    func test_updateHook_setsUpdateHook_withInsert() throws {
+        try async { done in
             db.updateHook { operation, db, table, rowid in
                 XCTAssertEqual(Connection.Operation.insert, operation)
                 XCTAssertEqual("main", db)
@@ -285,13 +295,13 @@ class ConnectionTests: SQLiteTestCase {
                 XCTAssertEqual(1, rowid)
                 done()
             }
-            try! insertUser("alice")
+            try insertUser("alice")
         }
     }
 
-    func test_updateHook_setsUpdateHook_withUpdate() {
-        try! insertUser("alice")
-        async { done in
+    func test_updateHook_setsUpdateHook_withUpdate() throws {
+        try insertUser("alice")
+        try async { done in
             db.updateHook { operation, db, table, rowid in
                 XCTAssertEqual(Connection.Operation.update, operation)
                 XCTAssertEqual("main", db)
@@ -299,13 +309,13 @@ class ConnectionTests: SQLiteTestCase {
                 XCTAssertEqual(1, rowid)
                 done()
             }
-            try! db.run("UPDATE users SET email = 'alice@example.com'")
+            try db.run("UPDATE users SET email = 'alice@example.com'")
         }
     }
 
-    func test_updateHook_setsUpdateHook_withDelete() {
-        try! insertUser("alice")
-        async { done in
+    func test_updateHook_setsUpdateHook_withDelete() throws {
+        try insertUser("alice")
+        try async { done in
             db.updateHook { operation, db, table, rowid in
                 XCTAssertEqual(Connection.Operation.delete, operation)
                 XCTAssertEqual("main", db)
@@ -313,24 +323,24 @@ class ConnectionTests: SQLiteTestCase {
                 XCTAssertEqual(1, rowid)
                 done()
             }
-            try! db.run("DELETE FROM users WHERE id = 1")
+            try db.run("DELETE FROM users WHERE id = 1")
         }
     }
 
-    func test_commitHook_setsCommitHook() {
-        async { done in
+    func test_commitHook_setsCommitHook() throws {
+        try async { done in
             db.commitHook {
                 done()
             }
-            try! db.transaction {
+            try db.transaction {
                 try insertUser("alice")
             }
-            XCTAssertEqual(1, try! db.scalar("SELECT count(*) FROM users") as? Int64)
+            XCTAssertEqual(1, try db.scalar("SELECT count(*) FROM users") as? Int64)
         }
     }
 
-    func test_rollbackHook_setsRollbackHook() {
-        async { done in
+    func test_rollbackHook_setsRollbackHook() throws {
+        try async { done in
             db.rollbackHook(done)
             do {
                 try db.transaction {
@@ -339,12 +349,12 @@ class ConnectionTests: SQLiteTestCase {
                 }
             } catch {
             }
-            XCTAssertEqual(0, try! db.scalar("SELECT count(*) FROM users") as? Int64)
+            XCTAssertEqual(0, try db.scalar("SELECT count(*) FROM users") as? Int64)
         }
     }
 
-    func test_commitHook_withRollback_rollsBack() {
-        async { done in
+    func test_commitHook_withRollback_rollsBack() throws {
+        try async { done in
             db.commitHook {
                 throw NSError(domain: "com.stephencelis.SQLiteTests", code: 1, userInfo: nil)
             }
@@ -355,41 +365,41 @@ class ConnectionTests: SQLiteTestCase {
                 }
             } catch {
             }
-            XCTAssertEqual(0, try! db.scalar("SELECT count(*) FROM users") as? Int64)
+            XCTAssertEqual(0, try db.scalar("SELECT count(*) FROM users") as? Int64)
         }
     }
 
     // https://github.com/stephencelis/SQLite.swift/issues/1071
     #if !os(Linux)
-    func test_createFunction_withArrayArguments() {
+    func test_createFunction_withArrayArguments() throws {
         db.createFunction("hello") { $0[0].map { "Hello, \($0)!" } }
 
-        XCTAssertEqual("Hello, world!", try! db.scalar("SELECT hello('world')") as? String)
-        XCTAssert(try! db.scalar("SELECT hello(NULL)") == nil)
+        XCTAssertEqual("Hello, world!", try db.scalar("SELECT hello('world')") as? String)
+        XCTAssert(try db.scalar("SELECT hello(NULL)") == nil)
     }
 
-    func test_createFunction_createsQuotableFunction() {
+    func test_createFunction_createsQuotableFunction() throws {
         db.createFunction("hello world") { $0[0].map { "Hello, \($0)!" } }
 
-        XCTAssertEqual("Hello, world!", try! db.scalar("SELECT \"hello world\"('world')") as? String)
-        XCTAssert(try! db.scalar("SELECT \"hello world\"(NULL)") == nil)
+        XCTAssertEqual("Hello, world!", try db.scalar("SELECT \"hello world\"('world')") as? String)
+        XCTAssert(try db.scalar("SELECT \"hello world\"(NULL)") == nil)
     }
 
-    func test_createCollation_createsCollation() {
-        try! db.createCollation("NODIACRITIC") { lhs, rhs in
+    func test_createCollation_createsCollation() throws {
+        try db.createCollation("NODIACRITIC") { lhs, rhs in
             lhs.compare(rhs, options: .diacriticInsensitive)
         }
-        XCTAssertEqual(1, try! db.scalar("SELECT ? = ? COLLATE NODIACRITIC", "cafe", "café") as? Int64)
+        XCTAssertEqual(1, try db.scalar("SELECT ? = ? COLLATE NODIACRITIC", "cafe", "café") as? Int64)
     }
 
-    func test_createCollation_createsQuotableCollation() {
-        try! db.createCollation("NO DIACRITIC") { lhs, rhs in
+    func test_createCollation_createsQuotableCollation() throws {
+        try db.createCollation("NO DIACRITIC") { lhs, rhs in
             lhs.compare(rhs, options: .diacriticInsensitive)
         }
-        XCTAssertEqual(1, try! db.scalar("SELECT ? = ? COLLATE \"NO DIACRITIC\"", "cafe", "café") as? Int64)
+        XCTAssertEqual(1, try db.scalar("SELECT ? = ? COLLATE \"NO DIACRITIC\"", "cafe", "café") as? Int64)
     }
 
-    func test_interrupt_interruptsLongRunningQuery() {
+    func test_interrupt_interruptsLongRunningQuery() throws {
         let semaphore = DispatchSemaphore(value: 0)
         db.createFunction("sleep") { _ in
             DispatchQueue.global(qos: .background).async {
@@ -399,7 +409,7 @@ class ConnectionTests: SQLiteTestCase {
             semaphore.wait()
             return nil
         }
-        let stmt = try! db.prepare("SELECT sleep()")
+        let stmt = try db.prepare("SELECT sleep()")
         XCTAssertThrowsError(try stmt.run()) { error in
             if case Result.error(_, let code, _) = error {
                 XCTAssertEqual(code, SQLITE_INTERRUPT)
@@ -410,13 +420,13 @@ class ConnectionTests: SQLiteTestCase {
     }
     #endif
 
-    func test_concurrent_access_single_connection() {
+    func test_concurrent_access_single_connection() throws {
         // test can fail on iOS/tvOS 9.x: SQLite compile-time differences?
         guard #available(iOS 10.0, OSX 10.10, tvOS 10.0, watchOS 2.2, *) else { return }
 
-        let conn = try! Connection("\(NSTemporaryDirectory())/\(UUID().uuidString)")
-        try! conn.execute("DROP TABLE IF EXISTS test; CREATE TABLE test(value);")
-        try! conn.run("INSERT INTO test(value) VALUES(?)", 0)
+        let conn = try Connection("\(NSTemporaryDirectory())/\(UUID().uuidString)")
+        try conn.execute("DROP TABLE IF EXISTS test; CREATE TABLE test(value);")
+        try conn.run("INSERT INTO test(value) VALUES(?)", 0)
         let queue = DispatchQueue(label: "Readers", attributes: [.concurrent])
 
         let nReaders = 5
@@ -430,43 +440,51 @@ class ConnectionTests: SQLiteTestCase {
         }
         semaphores.forEach { $0.wait() }
     }
-}
 
-class ResultTests: XCTestCase {
-    let connection = try! Connection(.inMemory)
+    func test_attach_detach_memory_database() throws {
+        let schemaName = "test"
 
-    func test_init_with_ok_code_returns_nil() {
-        XCTAssertNil(Result(errorCode: SQLITE_OK, connection: connection, statement: nil) as Result?)
-    }
+        try db.attach(.inMemory, as: schemaName)
 
-    func test_init_with_row_code_returns_nil() {
-        XCTAssertNil(Result(errorCode: SQLITE_ROW, connection: connection, statement: nil) as Result?)
-    }
+        let table = Table("attached_users", database: schemaName)
+        let name = Expression<String>("string")
 
-    func test_init_with_done_code_returns_nil() {
-        XCTAssertNil(Result(errorCode: SQLITE_DONE, connection: connection, statement: nil) as Result?)
-    }
+        // create a table, insert some data
+        try db.run(table.create { builder in
+            builder.column(name)
+        })
+        _ = try db.run(table.insert(name <- "test"))
 
-    func test_init_with_other_code_returns_error() {
-        if case .some(.error(let message, let code, let statement)) =
-            Result(errorCode: SQLITE_MISUSE, connection: connection, statement: nil) {
-            XCTAssertEqual("not an error", message)
-            XCTAssertEqual(SQLITE_MISUSE, code)
-            XCTAssertNil(statement)
-            XCTAssert(connection === connection)
-        } else {
-            XCTFail("no error")
-        }
+        // query data
+        let rows = try db.prepare(table.select(name)).map { $0[name] }
+        XCTAssertEqual(["test"], rows)
+
+        try db.detach(schemaName)
     }
 
-    func test_description_contains_error_code() {
-        XCTAssertEqual("not an error (code: 21)",
-            Result(errorCode: SQLITE_MISUSE, connection: connection, statement: nil)?.description)
+    func test_attach_detach_file_database() throws {
+        let schemaName = "test"
+        let testDb = fixture("test", withExtension: "sqlite")
+
+        try db.attach(.uri(testDb, parameters: [.mode(.readOnly)]), as: schemaName)
+
+        let table = Table("tests", database: schemaName)
+        let email = Expression<String>("email")
+
+        let rows = try db.prepare(table.select(email)).map { $0[email] }
+        XCTAssertEqual(["foo@bar.com"], rows)
+
+        try db.detach(schemaName)
     }
 
-    func test_description_contains_statement_and_error_code() {
-        let statement = try! Statement(connection, "SELECT 1")
-        XCTAssertEqual("not an error (SELECT 1) (code: 21)",
-            Result(errorCode: SQLITE_MISUSE, connection: connection, statement: statement)?.description)
+    func test_detach_invalid_schema_name_errors_with_no_such_database() throws {
+        XCTAssertThrowsError(try db.detach("no-exist")) { error in
+            if case let Result.error(message, code, _) = error {
+                XCTAssertEqual(code, SQLITE_ERROR)
+                XCTAssertEqual("no such database: no-exist", message)
+            } else {
+                XCTFail("unexpected error: \(error)")
+            }
+        }
     }
 }
diff --git a/Tests/SQLiteTests/Fixtures.swift b/Tests/SQLiteTests/Fixtures.swift
index d0683130..f3851cba 100644
--- a/Tests/SQLiteTests/Fixtures.swift
+++ b/Tests/SQLiteTests/Fixtures.swift
@@ -2,7 +2,13 @@ import Foundation
 
 func fixture(_ name: String, withExtension: String?) -> String {
     let testBundle = Bundle(for: SQLiteTestCase.self)
-    return testBundle.url(
-        forResource: name,
-        withExtension: withExtension)!.path
+
+    for resource in [name, "fixtures/\(name)"] {
+        if let url = testBundle.url(
+            forResource: resource,
+            withExtension: withExtension) {
+            return url.path
+        }
+    }
+    fatalError("Cannot find \(name).\(withExtension ?? "")")
 }
diff --git a/Tests/SQLiteTests/ResultTests.swift b/Tests/SQLiteTests/ResultTests.swift
new file mode 100644
index 00000000..ccedcfe2
--- /dev/null
+++ b/Tests/SQLiteTests/ResultTests.swift
@@ -0,0 +1,52 @@
+import XCTest
+import Foundation
+@testable import SQLite
+
+#if SQLITE_SWIFT_STANDALONE
+import sqlite3
+#elseif SQLITE_SWIFT_SQLCIPHER
+import SQLCipher
+#elseif os(Linux)
+import CSQLite
+#else
+import SQLite3
+#endif
+
+class ResultTests: XCTestCase {
+    let connection = try! Connection(.inMemory)
+
+    func test_init_with_ok_code_returns_nil() {
+        XCTAssertNil(Result(errorCode: SQLITE_OK, connection: connection, statement: nil) as Result?)
+    }
+
+    func test_init_with_row_code_returns_nil() {
+        XCTAssertNil(Result(errorCode: SQLITE_ROW, connection: connection, statement: nil) as Result?)
+    }
+
+    func test_init_with_done_code_returns_nil() {
+        XCTAssertNil(Result(errorCode: SQLITE_DONE, connection: connection, statement: nil) as Result?)
+    }
+
+    func test_init_with_other_code_returns_error() {
+        if case .some(.error(let message, let code, let statement)) =
+            Result(errorCode: SQLITE_MISUSE, connection: connection, statement: nil) {
+            XCTAssertEqual("not an error", message)
+            XCTAssertEqual(SQLITE_MISUSE, code)
+            XCTAssertNil(statement)
+            XCTAssert(connection === connection)
+        } else {
+            XCTFail("no error")
+        }
+    }
+
+    func test_description_contains_error_code() {
+        XCTAssertEqual("not an error (code: 21)",
+            Result(errorCode: SQLITE_MISUSE, connection: connection, statement: nil)?.description)
+    }
+
+    func test_description_contains_statement_and_error_code() {
+        let statement = try! Statement(connection, "SELECT 1")
+        XCTAssertEqual("not an error (SELECT 1) (code: 21)",
+            Result(errorCode: SQLITE_MISUSE, connection: connection, statement: statement)?.description)
+    }
+}
diff --git a/Tests/SQLiteTests/TestHelpers.swift b/Tests/SQLiteTests/TestHelpers.swift
index f43310c9..6e8e9ebb 100644
--- a/Tests/SQLiteTests/TestHelpers.swift
+++ b/Tests/SQLiteTests/TestHelpers.swift
@@ -66,9 +66,9 @@ class SQLiteTestCase: XCTestCase {
 //        if let count = trace[SQL] { trace[SQL] = count - 1 }
 //    }
 
-    func async(expect description: String = "async", timeout: Double = 5, block: (@escaping () -> Void) -> Void) {
+    func async(expect description: String = "async", timeout: Double = 5, block: (@escaping () -> Void) throws -> Void) throws {
         let expectation = self.expectation(description: description)
-        block({ expectation.fulfill() })
+        try block({ expectation.fulfill() })
         waitForExpectations(timeout: timeout, handler: nil)
     }
 
diff --git a/Tests/SQLiteTests/fixtures/test.sqlite b/Tests/SQLiteTests/fixtures/test.sqlite
new file mode 100644
index 0000000000000000000000000000000000000000..7b39b423d7afdd43d0d793bb0c87de0248b8910d
GIT binary patch

From 544e7b9c5c291bbe1973abd06eb88afa22f6f296 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Sun, 17 Jul 2022 16:07:32 +0200
Subject: [PATCH 158/326] Remove force tries in tests

---
 Tests/SQLiteTests/CipherTests.swift           | 52 +++++------
 .../SQLiteTests/CustomAggregationTests.swift  | 22 ++---
 Tests/SQLiteTests/CustomFunctionsTests.swift  | 90 +++++++++----------
 Tests/SQLiteTests/FTS4Tests.swift             | 10 +--
 Tests/SQLiteTests/QueryIntegrationTests.swift | 76 ++++++++--------
 Tests/SQLiteTests/QueryTests.swift            | 12 +--
 Tests/SQLiteTests/ResultTests.swift           | 10 ++-
 Tests/SQLiteTests/RowTests.swift              | 16 ++--
 Tests/SQLiteTests/SelectTests.swift           |  8 +-
 Tests/SQLiteTests/StatementTests.swift        | 24 ++---
 Tests/SQLiteTests/TestHelpers.swift           |  4 +-
 11 files changed, 164 insertions(+), 160 deletions(-)

diff --git a/Tests/SQLiteTests/CipherTests.swift b/Tests/SQLiteTests/CipherTests.swift
index 0995fca0..f25dbe5b 100644
--- a/Tests/SQLiteTests/CipherTests.swift
+++ b/Tests/SQLiteTests/CipherTests.swift
@@ -5,8 +5,8 @@ import SQLCipher
 
 class CipherTests: XCTestCase {
 
-    let db1 = try! Connection()
-    let db2 = try! Connection()
+    let db1 = try Connection()
+    let db2 = try Connection()
 
     override func setUpWithError() throws {
         // db
@@ -26,41 +26,41 @@ class CipherTests: XCTestCase {
         try super.setUpWithError()
     }
 
-    func test_key() {
-        XCTAssertEqual(1, try! db1.scalar("SELECT count(*) FROM foo") as? Int64)
+    func test_key() throws {
+        XCTAssertEqual(1, try db1.scalar("SELECT count(*) FROM foo") as? Int64)
     }
 
     func test_key_blob_literal() {
-        let db = try! Connection()
-        try! db.key("x'2DD29CA851E7B56E4697B0E1F08507293D761A05CE4D1B628663F411A8086D99'")
+        let db = try Connection()
+        try db.key("x'2DD29CA851E7B56E4697B0E1F08507293D761A05CE4D1B628663F411A8086D99'")
     }
 
-    func test_rekey() {
-        try! db1.rekey("goodbye")
-        XCTAssertEqual(1, try! db1.scalar("SELECT count(*) FROM foo") as? Int64)
+    func test_rekey() throws {
+        try db1.rekey("goodbye")
+        XCTAssertEqual(1, try db1.scalar("SELECT count(*) FROM foo") as? Int64)
     }
 
-    func test_data_key() {
-        XCTAssertEqual(1, try! db2.scalar("SELECT count(*) FROM foo") as? Int64)
+    func test_data_key() throws {
+        XCTAssertEqual(1, try db2.scalar("SELECT count(*) FROM foo") as? Int64)
     }
 
-    func test_data_rekey() {
+    func test_data_rekey() throws {
         let newKey = keyData()
-        try! db2.rekey(Blob(bytes: newKey.bytes, length: newKey.length))
-        XCTAssertEqual(1, try! db2.scalar("SELECT count(*) FROM foo") as? Int64)
+        try db2.rekey(Blob(bytes: newKey.bytes, length: newKey.length))
+        XCTAssertEqual(1, try db2.scalar("SELECT count(*) FROM foo") as? Int64)
     }
 
-    func test_keyFailure() {
+    func test_keyFailure() throws {
         let path = "\(NSTemporaryDirectory())/db.sqlite3"
         _ = try? FileManager.default.removeItem(atPath: path)
 
-        let connA = try! Connection(path)
-        defer { try! FileManager.default.removeItem(atPath: path) }
+        let connA = try Connection(path)
+        defer { try FileManager.default.removeItem(atPath: path) }
 
-        try! connA.key("hello")
-        try! connA.run("CREATE TABLE foo (bar TEXT)")
+        try connA.key("hello")
+        try connA.run("CREATE TABLE foo (bar TEXT)")
 
-        let connB = try! Connection(path, readonly: true)
+        let connB = try Connection(path, readonly: true)
 
         do {
             try connB.key("world")
@@ -72,7 +72,7 @@ class CipherTests: XCTestCase {
         }
     }
 
-    func test_open_db_encrypted_with_sqlcipher() {
+    func test_open_db_encrypted_with_sqlcipher() throws {
         // $ sqlcipher Tests/SQLiteTests/fixtures/encrypted-[version].x.sqlite
         // sqlite> pragma key = 'sqlcipher-test';
         // sqlite> CREATE TABLE foo (bar TEXT);
@@ -85,17 +85,17 @@ class CipherTests: XCTestCase {
             fixture("encrypted-3.x", withExtension: "sqlite") :
             fixture("encrypted-4.x", withExtension: "sqlite")
 
-        try! FileManager.default.setAttributes([FileAttributeKey.immutable: 1], ofItemAtPath: encryptedFile)
+        try FileManager.default.setAttributes([FileAttributeKey.immutable: 1], ofItemAtPath: encryptedFile)
         XCTAssertFalse(FileManager.default.isWritableFile(atPath: encryptedFile))
 
         defer {
             // ensure file can be cleaned up afterwards
-            try! FileManager.default.setAttributes([FileAttributeKey.immutable: 0], ofItemAtPath: encryptedFile)
+            try FileManager.default.setAttributes([FileAttributeKey.immutable: 0], ofItemAtPath: encryptedFile)
         }
 
-        let conn = try! Connection(encryptedFile)
-        try! conn.key("sqlcipher-test")
-        XCTAssertEqual(1, try! conn.scalar("SELECT count(*) FROM foo") as? Int64)
+        let conn = try Connection(encryptedFile)
+        try conn.key("sqlcipher-test")
+        XCTAssertEqual(1, try conn.scalar("SELECT count(*) FROM foo") as? Int64)
     }
 
     private func keyData(length: Int = 64) -> NSMutableData {
diff --git a/Tests/SQLiteTests/CustomAggregationTests.swift b/Tests/SQLiteTests/CustomAggregationTests.swift
index 5cbfbbef..71cbba9c 100644
--- a/Tests/SQLiteTests/CustomAggregationTests.swift
+++ b/Tests/SQLiteTests/CustomAggregationTests.swift
@@ -25,7 +25,7 @@ class CustomAggregationTests: SQLiteTestCase {
         try insertUser("Eve", age: 28, admin: false)
     }
 
-    func testUnsafeCustomSum() {
+    func testUnsafeCustomSum() throws {
         let step = { (bindings: [Binding?], state: UnsafeMutablePointer<Int64>) in
             if let v = bindings[0] as? Int64 {
                 state.pointee += v
@@ -43,7 +43,7 @@ class CustomAggregationTests: SQLiteTestCase {
             v[0] = 0
             return v.baseAddress!
         }
-        let result = try! db.prepare("SELECT mySUM1(age) AS s FROM users")
+        let result = try db.prepare("SELECT mySUM1(age) AS s FROM users")
         let i = result.columnNames.firstIndex(of: "s")!
         for row in result {
             let value = row[i] as? Int64
@@ -51,7 +51,7 @@ class CustomAggregationTests: SQLiteTestCase {
         }
     }
 
-    func testUnsafeCustomSumGrouping() {
+    func testUnsafeCustomSumGrouping() throws {
         let step = { (bindings: [Binding?], state: UnsafeMutablePointer<Int64>) in
             if let v = bindings[0] as? Int64 {
                 state.pointee += v
@@ -68,19 +68,19 @@ class CustomAggregationTests: SQLiteTestCase {
             v[0] = 0
             return v.baseAddress!
         }
-        let result = try! db.prepare("SELECT mySUM2(age) AS s FROM users GROUP BY admin ORDER BY s")
+        let result = try db.prepare("SELECT mySUM2(age) AS s FROM users GROUP BY admin ORDER BY s")
         let i = result.columnNames.firstIndex(of: "s")!
         let values = result.compactMap { $0[i] as? Int64 }
         XCTAssertTrue(values.elementsEqual([28, 55]))
     }
 
-    func testCustomSum() {
+    func testCustomSum() throws {
         let reduce: (Int64, [Binding?]) -> Int64 = { (last, bindings) in
             let v = (bindings[0] as? Int64) ?? 0
             return last + v
         }
         db.createAggregation("myReduceSUM1", initialValue: Int64(2000), reduce: reduce, result: { $0 })
-        let result = try! db.prepare("SELECT myReduceSUM1(age) AS s FROM users")
+        let result = try db.prepare("SELECT myReduceSUM1(age) AS s FROM users")
         let i = result.columnNames.firstIndex(of: "s")!
         for row in result {
             let value = row[i] as? Int64
@@ -88,26 +88,26 @@ class CustomAggregationTests: SQLiteTestCase {
         }
     }
 
-    func testCustomSumGrouping() {
+    func testCustomSumGrouping() throws {
         let reduce: (Int64, [Binding?]) -> Int64 = { (last, bindings) in
             let v = (bindings[0] as? Int64) ?? 0
             return last + v
         }
         db.createAggregation("myReduceSUM2", initialValue: Int64(3000), reduce: reduce, result: { $0 })
-        let result = try! db.prepare("SELECT myReduceSUM2(age) AS s FROM users GROUP BY admin ORDER BY s")
+        let result = try db.prepare("SELECT myReduceSUM2(age) AS s FROM users GROUP BY admin ORDER BY s")
         let i = result.columnNames.firstIndex(of: "s")!
         let values = result.compactMap { $0[i] as? Int64 }
         XCTAssertTrue(values.elementsEqual([3028, 3055]))
     }
 
-    func testCustomStringAgg() {
+    func testCustomStringAgg() throws {
         let initial = String(repeating: " ", count: 64)
         let reduce: (String, [Binding?]) -> String = { (last, bindings) in
             let v = (bindings[0] as? String) ?? ""
             return last + v
         }
         db.createAggregation("myReduceSUM3", initialValue: initial, reduce: reduce, result: { $0 })
-        let result = try! db.prepare("SELECT myReduceSUM3(email) AS s FROM users")
+        let result = try db.prepare("SELECT myReduceSUM3(email) AS s FROM users")
 
         let i = result.columnNames.firstIndex(of: "s")!
         for row in result {
@@ -116,7 +116,7 @@ class CustomAggregationTests: SQLiteTestCase {
         }
     }
 
-    func testCustomObjectSum() {
+    func testCustomObjectSum() throws {
         {
             let initial = TestObject(value: 1000)
             let reduce: (TestObject, [Binding?]) -> TestObject = { (last, bindings) in
diff --git a/Tests/SQLiteTests/CustomFunctionsTests.swift b/Tests/SQLiteTests/CustomFunctionsTests.swift
index 3d897f8e..c4eb51e6 100644
--- a/Tests/SQLiteTests/CustomFunctionsTests.swift
+++ b/Tests/SQLiteTests/CustomFunctionsTests.swift
@@ -8,19 +8,19 @@ class CustomFunctionNoArgsTests: SQLiteTestCase {
     typealias FunctionNoOptional              = ()  -> Expression<String>
     typealias FunctionResultOptional          = ()  -> Expression<String?>
 
-    func testFunctionNoOptional() {
-        let _: FunctionNoOptional = try! db.createFunction("test", deterministic: true) {
+    func testFunctionNoOptional() throws {
+        let _: FunctionNoOptional = try db.createFunction("test", deterministic: true) {
             "a"
         }
-        let result = try! db.prepare("SELECT test()").scalar() as! String
+        let result = try db.prepare("SELECT test()").scalar() as! String
         XCTAssertEqual("a", result)
     }
 
-    func testFunctionResultOptional() {
-        let _: FunctionResultOptional = try! db.createFunction("test", deterministic: true) {
+    func testFunctionResultOptional() throws {
+        let _: FunctionResultOptional = try db.createFunction("test", deterministic: true) {
             "a"
         }
-        let result = try! db.prepare("SELECT test()").scalar() as! String?
+        let result = try db.prepare("SELECT test()").scalar() as! String?
         XCTAssertEqual("a", result)
     }
 }
@@ -31,35 +31,35 @@ class CustomFunctionWithOneArgTests: SQLiteTestCase {
     typealias FunctionResultOptional          = (Expression<String>)  -> Expression<String?>
     typealias FunctionLeftResultOptional      = (Expression<String?>) -> Expression<String?>
 
-    func testFunctionNoOptional() {
-        let _: FunctionNoOptional = try! db.createFunction("test", deterministic: true) { a in
+    func testFunctionNoOptional() throws {
+        let _: FunctionNoOptional = try db.createFunction("test", deterministic: true) { a in
             "b" + a
         }
-        let result = try! db.prepare("SELECT test(?)").scalar("a") as! String
+        let result = try db.prepare("SELECT test(?)").scalar("a") as! String
         XCTAssertEqual("ba", result)
     }
 
-    func testFunctionLeftOptional() {
-        let _: FunctionLeftOptional = try! db.createFunction("test", deterministic: true) { a in
+    func testFunctionLeftOptional() throws {
+        let _: FunctionLeftOptional = try db.createFunction("test", deterministic: true) { a in
             "b" + a!
         }
-        let result = try! db.prepare("SELECT test(?)").scalar("a") as! String
+        let result = try db.prepare("SELECT test(?)").scalar("a") as! String
         XCTAssertEqual("ba", result)
     }
 
-    func testFunctionResultOptional() {
-        let _: FunctionResultOptional = try! db.createFunction("test", deterministic: true) { a in
+    func testFunctionResultOptional() throws {
+        let _: FunctionResultOptional = try db.createFunction("test", deterministic: true) { a in
             "b" + a
         }
-        let result = try! db.prepare("SELECT test(?)").scalar("a") as! String
+        let result = try db.prepare("SELECT test(?)").scalar("a") as! String
         XCTAssertEqual("ba", result)
     }
 
-    func testFunctionLeftResultOptional() {
-        let _: FunctionLeftResultOptional = try! db.createFunction("test", deterministic: true) { (a: String?) -> String? in
+    func testFunctionLeftResultOptional() throws {
+        let _: FunctionLeftResultOptional = try db.createFunction("test", deterministic: true) { (a: String?) -> String? in
             "b" + a!
         }
-        let result = try! db.prepare("SELECT test(?)").scalar("a") as! String
+        let result = try db.prepare("SELECT test(?)").scalar("a") as! String
         XCTAssertEqual("ba", result)
     }
 }
@@ -74,76 +74,76 @@ class CustomFunctionWithTwoArgsTests: SQLiteTestCase {
     typealias FunctionRightResultOptional     = (Expression<String>, Expression<String?>) -> Expression<String?>
     typealias FunctionLeftRightResultOptional = (Expression<String?>, Expression<String?>) -> Expression<String?>
 
-    func testNoOptional() {
-        let _: FunctionNoOptional = try! db.createFunction("test", deterministic: true) { a, b in
+    func testNoOptional() throws {
+        let _: FunctionNoOptional = try db.createFunction("test", deterministic: true) { a, b in
             a + b
         }
-        let result = try! db.prepare("SELECT test(?, ?)").scalar("a", "b") as! String
+        let result = try db.prepare("SELECT test(?, ?)").scalar("a", "b") as! String
         XCTAssertEqual("ab", result)
     }
 
-    func testLeftOptional() {
-        let _: FunctionLeftOptional = try! db.createFunction("test", deterministic: true) { a, b in
+    func testLeftOptional() throws {
+        let _: FunctionLeftOptional = try db.createFunction("test", deterministic: true) { a, b in
             a! + b
         }
-        let result = try! db.prepare("SELECT test(?, ?)").scalar("a", "b") as! String
+        let result = try db.prepare("SELECT test(?, ?)").scalar("a", "b") as! String
         XCTAssertEqual("ab", result)
     }
 
-    func testRightOptional() {
-        let _: FunctionRightOptional = try! db.createFunction("test", deterministic: true) { a, b in
+    func testRightOptional() throws {
+        let _: FunctionRightOptional = try db.createFunction("test", deterministic: true) { a, b in
             a + b!
         }
-        let result = try! db.prepare("SELECT test(?, ?)").scalar("a", "b") as! String
+        let result = try db.prepare("SELECT test(?, ?)").scalar("a", "b") as! String
         XCTAssertEqual("ab", result)
     }
 
-    func testResultOptional() {
-        let _: FunctionResultOptional = try! db.createFunction("test", deterministic: true) { a, b in
+    func testResultOptional() throws {
+        let _: FunctionResultOptional = try db.createFunction("test", deterministic: true) { a, b in
             a + b
         }
-        let result = try! db.prepare("SELECT test(?, ?)").scalar("a", "b") as! String?
+        let result = try db.prepare("SELECT test(?, ?)").scalar("a", "b") as! String?
         XCTAssertEqual("ab", result)
     }
 
-    func testFunctionLeftRightOptional() {
-        let _: FunctionLeftRightOptional = try! db.createFunction("test", deterministic: true) { a, b in
+    func testFunctionLeftRightOptional() throws {
+        let _: FunctionLeftRightOptional = try db.createFunction("test", deterministic: true) { a, b in
             a! + b!
         }
-        let result = try! db.prepare("SELECT test(?, ?)").scalar("a", "b") as! String
+        let result = try db.prepare("SELECT test(?, ?)").scalar("a", "b") as! String
         XCTAssertEqual("ab", result)
     }
 
-    func testFunctionLeftResultOptional() {
-        let _: FunctionLeftResultOptional = try! db.createFunction("test", deterministic: true) { a, b in
+    func testFunctionLeftResultOptional() throws {
+        let _: FunctionLeftResultOptional = try db.createFunction("test", deterministic: true) { a, b in
             a! + b
         }
-        let result = try! db.prepare("SELECT test(?, ?)").scalar("a", "b") as! String?
+        let result = try db.prepare("SELECT test(?, ?)").scalar("a", "b") as! String?
         XCTAssertEqual("ab", result)
     }
 
-    func testFunctionRightResultOptional() {
-        let _: FunctionRightResultOptional = try! db.createFunction("test", deterministic: true) { a, b in
+    func testFunctionRightResultOptional() throws {
+        let _: FunctionRightResultOptional = try db.createFunction("test", deterministic: true) { a, b in
             a + b!
         }
-        let result = try! db.prepare("SELECT test(?, ?)").scalar("a", "b") as! String?
+        let result = try db.prepare("SELECT test(?, ?)").scalar("a", "b") as! String?
         XCTAssertEqual("ab", result)
     }
 
-    func testFunctionLeftRightResultOptional() {
-        let _: FunctionLeftRightResultOptional = try! db.createFunction("test", deterministic: true) { a, b in
+    func testFunctionLeftRightResultOptional() throws {
+        let _: FunctionLeftRightResultOptional = try db.createFunction("test", deterministic: true) { a, b in
             a! + b!
         }
-        let result = try! db.prepare("SELECT test(?, ?)").scalar("a", "b") as! String?
+        let result = try db.prepare("SELECT test(?, ?)").scalar("a", "b") as! String?
         XCTAssertEqual("ab", result)
     }
 }
 
 class CustomFunctionTruncation: SQLiteTestCase {
     // https://github.com/stephencelis/SQLite.swift/issues/468
-    func testStringTruncation() {
-        _ = try! db.createFunction("customLower") { (value: String) in value.lowercased() }
-        let result = try! db.prepare("SELECT customLower(?)").scalar("TÖL-AA 12") as? String
+    func testStringTruncation() throws {
+        _ = try db.createFunction("customLower") { (value: String) in value.lowercased() }
+        let result = try db.prepare("SELECT customLower(?)").scalar("TÖL-AA 12") as? String
         XCTAssertEqual("töl-aa 12", result)
     }
 }
diff --git a/Tests/SQLiteTests/FTS4Tests.swift b/Tests/SQLiteTests/FTS4Tests.swift
index d6385b0c..aa6ac41f 100644
--- a/Tests/SQLiteTests/FTS4Tests.swift
+++ b/Tests/SQLiteTests/FTS4Tests.swift
@@ -191,7 +191,7 @@ class FTS4ConfigTests: XCTestCase {
 
 class FTS4IntegrationTests: SQLiteTestCase {
 #if !SQLITE_SWIFT_STANDALONE && !SQLITE_SWIFT_SQLCIPHER
-    func test_registerTokenizer_registersTokenizer() {
+    func test_registerTokenizer_registersTokenizer() throws {
         let emails = VirtualTable("emails")
         let subject = Expression<String?>("subject")
         let body = Expression<String?>("body")
@@ -200,7 +200,7 @@ class FTS4IntegrationTests: SQLiteTestCase {
         let tokenizerName = "tokenizer"
         let tokenizer = CFStringTokenizerCreate(nil, "" as CFString, CFRangeMake(0, 0),
                                                 UInt(kCFStringTokenizerUnitWord), locale)
-        try! db.registerTokenizer(tokenizerName) { string in
+        try db.registerTokenizer(tokenizerName) { string in
             CFStringTokenizerSetString(tokenizer, string as CFString,
                                        CFRangeMake(0, CFStringGetLength(string as CFString)))
             if CFStringTokenizerAdvanceToNextToken(tokenizer).isEmpty {
@@ -214,14 +214,14 @@ class FTS4IntegrationTests: SQLiteTestCase {
             return (token as String, string.range(of: input as String)!)
         }
 
-        try! db.run(emails.create(.FTS4([subject, body], tokenize: .Custom(tokenizerName))))
+        try db.run(emails.create(.FTS4([subject, body], tokenize: .Custom(tokenizerName))))
         assertSQL("""
                   CREATE VIRTUAL TABLE \"emails\" USING fts4(\"subject\", \"body\",
                    tokenize=\"SQLite.swift\" \"tokenizer\")
                   """.replacingOccurrences(of: "\n", with: ""))
 
-        try! _ = db.run(emails.insert(subject <- "Aún más cáfe!"))
-        XCTAssertEqual(1, try! db.scalar(emails.filter(emails.match("aun")).count))
+        try _ = db.run(emails.insert(subject <- "Aún más cáfe!"))
+        XCTAssertEqual(1, try db.scalar(emails.filter(emails.match("aun")).count))
     }
 #endif
 }
diff --git a/Tests/SQLiteTests/QueryIntegrationTests.swift b/Tests/SQLiteTests/QueryIntegrationTests.swift
index db4e2e4e..27b78c0a 100644
--- a/Tests/SQLiteTests/QueryIntegrationTests.swift
+++ b/Tests/SQLiteTests/QueryIntegrationTests.swift
@@ -23,45 +23,45 @@ class QueryIntegrationTests: SQLiteTestCase {
 
     // MARK: -
 
-    func test_select() {
+    func test_select() throws {
         let managerId = Expression<Int64>("manager_id")
         let managers = users.alias("managers")
 
-        let alice = try! db.run(users.insert(email <- "alice@example.com"))
-        _ = try! db.run(users.insert(email <- "betsy@example.com", managerId <- alice))
+        let alice = try db.run(users.insert(email <- "alice@example.com"))
+        _ = try db.run(users.insert(email <- "betsy@example.com", managerId <- alice))
 
-        for user in try! db.prepare(users.join(managers, on: managers[id] == users[managerId])) {
+        for user in try db.prepare(users.join(managers, on: managers[id] == users[managerId])) {
             _ = user[users[managerId]]
         }
     }
 
-    func test_prepareRowIterator() {
+    func test_prepareRowIterator() throws {
         let names = ["a", "b", "c"]
-        try! insertUsers(names)
+        try insertUsers(names)
 
         let emailColumn = Expression<String>("email")
-        let emails = try! db.prepareRowIterator(users).map { $0[emailColumn] }
+        let emails = try db.prepareRowIterator(users).map { $0[emailColumn] }
 
         XCTAssertEqual(names.map({ "\($0)@example.com" }), emails.sorted())
     }
 
-    func test_ambiguousMap() {
+    func test_ambiguousMap() throws {
         let names = ["a", "b", "c"]
-        try! insertUsers(names)
+        try insertUsers(names)
 
-        let emails = try! db.prepare("select email from users", []).map { $0[0] as! String  }
+        let emails = try db.prepare("select email from users", []).map { $0[0] as! String  }
 
         XCTAssertEqual(names.map({ "\($0)@example.com" }), emails.sorted())
     }
 
-    func test_select_optional() {
+    func test_select_optional() throws {
         let managerId = Expression<Int64?>("manager_id")
         let managers = users.alias("managers")
 
-        let alice = try! db.run(users.insert(email <- "alice@example.com"))
-        _ = try! db.run(users.insert(email <- "betsy@example.com", managerId <- alice))
+        let alice = try db.run(users.insert(email <- "alice@example.com"))
+        _ = try db.run(users.insert(email <- "betsy@example.com", managerId <- alice))
 
-        for user in try! db.prepare(users.join(managers, on: managers[id] == users[managerId])) {
+        for user in try db.prepare(users.join(managers, on: managers[id] == users[managerId])) {
             _ = user[users[managerId]]
         }
     }
@@ -107,26 +107,26 @@ class QueryIntegrationTests: SQLiteTestCase {
         XCTAssertNil(values[0].sub?.sub)
     }
 
-    func test_scalar() {
-        XCTAssertEqual(0, try! db.scalar(users.count))
-        XCTAssertEqual(false, try! db.scalar(users.exists))
+    func test_scalar() throws {
+        XCTAssertEqual(0, try db.scalar(users.count))
+        XCTAssertEqual(false, try db.scalar(users.exists))
 
-        try! insertUsers("alice")
-        XCTAssertEqual(1, try! db.scalar(users.select(id.average)))
+        try insertUsers("alice")
+        XCTAssertEqual(1, try db.scalar(users.select(id.average)))
     }
 
-    func test_pluck() {
-        let rowid = try! db.run(users.insert(email <- "alice@example.com"))
-        XCTAssertEqual(rowid, try! db.pluck(users)![id])
+    func test_pluck() throws {
+        let rowid = try db.run(users.insert(email <- "alice@example.com"))
+        XCTAssertEqual(rowid, try db.pluck(users)![id])
     }
 
-    func test_insert() {
-        let id = try! db.run(users.insert(email <- "alice@example.com"))
+    func test_insert() throws {
+        let id = try db.run(users.insert(email <- "alice@example.com"))
         XCTAssertEqual(1, id)
     }
 
-    func test_insert_many() {
-        let id = try! db.run(users.insertMany([[email <- "alice@example.com"], [email <- "geoff@example.com"]]))
+    func test_insert_many() throws {
+        let id = try db.run(users.insertMany([[email <- "alice@example.com"], [email <- "geoff@example.com"]]))
         XCTAssertEqual(2, id)
     }
 
@@ -167,13 +167,13 @@ class QueryIntegrationTests: SQLiteTestCase {
         XCTAssertEqual(42, try fetchAge())
     }
 
-    func test_update() {
-        let changes = try! db.run(users.update(email <- "alice@example.com"))
+    func test_update() throws {
+        let changes = try db.run(users.update(email <- "alice@example.com"))
         XCTAssertEqual(0, changes)
     }
 
-    func test_delete() {
-        let changes = try! db.run(users.delete())
+    func test_delete() throws {
+        let changes = try db.run(users.delete())
         XCTAssertEqual(0, changes)
     }
 
@@ -200,8 +200,8 @@ class QueryIntegrationTests: SQLiteTestCase {
 
     func test_no_such_column() throws {
         let doesNotExist = Expression<String>("doesNotExist")
-        try! insertUser("alice")
-        let row = try! db.pluck(users.filter(email == "alice@example.com"))!
+        try insertUser("alice")
+        let row = try db.pluck(users.filter(email == "alice@example.com"))!
 
         XCTAssertThrowsError(try row.get(doesNotExist)) { error in
             if case QueryError.noSuchColumn(let name, _) = error {
@@ -212,8 +212,8 @@ class QueryIntegrationTests: SQLiteTestCase {
         }
     }
 
-    func test_catchConstraintError() {
-        try! db.run(users.insert(email <- "alice@example.com"))
+    func test_catchConstraintError() throws {
+        try db.run(users.insert(email <- "alice@example.com"))
         do {
             try db.run(users.insert(email <- "alice@example.com"))
             XCTFail("expected error")
@@ -231,19 +231,19 @@ class QueryIntegrationTests: SQLiteTestCase {
         XCTAssertEqual(1, result.count)
     }
 
-    func test_with_recursive() {
+    func test_with_recursive() throws {
         let nodes = Table("nodes")
         let id = Expression<Int64>("id")
         let parent = Expression<Int64?>("parent")
         let value = Expression<Int64>("value")
 
-        try! db.run(nodes.create { builder in
+        try db.run(nodes.create { builder in
             builder.column(id)
             builder.column(parent)
             builder.column(value)
         })
 
-        try! db.run(nodes.insertMany([
+        try db.run(nodes.insertMany([
             [id <- 0, parent <- nil, value <- 2],
             [id <- 1, parent <- 0, value <- 4],
             [id <- 2, parent <- 0, value <- 9],
@@ -254,7 +254,7 @@ class QueryIntegrationTests: SQLiteTestCase {
 
         // Compute the sum of the values of node 5 and its ancestors
         let ancestors = Table("ancestors")
-        let sum = try! db.scalar(
+        let sum = try db.scalar(
             ancestors
                 .select(value.sum)
                 .with(ancestors,
diff --git a/Tests/SQLiteTests/QueryTests.swift b/Tests/SQLiteTests/QueryTests.swift
index eae1d923..82a33808 100644
--- a/Tests/SQLiteTests/QueryTests.swift
+++ b/Tests/SQLiteTests/QueryTests.swift
@@ -375,25 +375,25 @@ class QueryTests: XCTestCase {
     }
     #endif
 
-    func test_insert_and_search_for_UUID() {
+    func test_insert_and_search_for_UUID() throws {
         struct Test: Codable {
             var uuid: UUID
             var string: String
         }
         let testUUID = UUID()
         let testValue = Test(uuid: testUUID, string: "value")
-        let db = try! Connection(.temporary)
-        try! db.run(table.create { t in
+        let db = try Connection(.temporary)
+        try db.run(table.create { t in
             t.column(uuid)
             t.column(string)
         }
         )
 
-        let iQuery = try! table.insert(testValue)
-        try! db.run(iQuery)
+        let iQuery = try table.insert(testValue)
+        try db.run(iQuery)
 
         let fQuery = table.filter(uuid == testUUID)
-        if let result = try! db.pluck(fQuery) {
+        if let result = try db.pluck(fQuery) {
             let testValueReturned = Test(uuid: result[uuid], string: result[string])
             XCTAssertEqual(testUUID, testValueReturned.uuid)
         } else {
diff --git a/Tests/SQLiteTests/ResultTests.swift b/Tests/SQLiteTests/ResultTests.swift
index ccedcfe2..fab4a0bb 100644
--- a/Tests/SQLiteTests/ResultTests.swift
+++ b/Tests/SQLiteTests/ResultTests.swift
@@ -13,7 +13,11 @@ import SQLite3
 #endif
 
 class ResultTests: XCTestCase {
-    let connection = try! Connection(.inMemory)
+    var connection: Connection!
+
+    override func setUpWithError() throws {
+        connection = try Connection(.inMemory)
+    }
 
     func test_init_with_ok_code_returns_nil() {
         XCTAssertNil(Result(errorCode: SQLITE_OK, connection: connection, statement: nil) as Result?)
@@ -44,8 +48,8 @@ class ResultTests: XCTestCase {
             Result(errorCode: SQLITE_MISUSE, connection: connection, statement: nil)?.description)
     }
 
-    func test_description_contains_statement_and_error_code() {
-        let statement = try! Statement(connection, "SELECT 1")
+    func test_description_contains_statement_and_error_code() throws {
+        let statement = try Statement(connection, "SELECT 1")
         XCTAssertEqual("not an error (SELECT 1) (code: 21)",
             Result(errorCode: SQLITE_MISUSE, connection: connection, statement: statement)?.description)
     }
diff --git a/Tests/SQLiteTests/RowTests.swift b/Tests/SQLiteTests/RowTests.swift
index 36721f80..506f6b10 100644
--- a/Tests/SQLiteTests/RowTests.swift
+++ b/Tests/SQLiteTests/RowTests.swift
@@ -3,9 +3,9 @@ import XCTest
 
 class RowTests: XCTestCase {
 
-    public func test_get_value() {
+    public func test_get_value() throws {
         let row = Row(["\"foo\"": 0], ["value"])
-        let result = try! row.get(Expression<String>("foo"))
+        let result = try row.get(Expression<String>("foo"))
 
         XCTAssertEqual("value", result)
     }
@@ -17,9 +17,9 @@ class RowTests: XCTestCase {
         XCTAssertEqual("value", result)
     }
 
-    public func test_get_value_optional() {
+    public func test_get_value_optional() throws {
         let row = Row(["\"foo\"": 0], ["value"])
-        let result = try! row.get(Expression<String?>("foo"))
+        let result = try row.get(Expression<String?>("foo"))
 
         XCTAssertEqual("value", result)
     }
@@ -31,9 +31,9 @@ class RowTests: XCTestCase {
         XCTAssertEqual("value", result)
     }
 
-    public func test_get_value_optional_nil() {
+    public func test_get_value_optional_nil() throws {
         let row = Row(["\"foo\"": 0], [nil])
-        let result = try! row.get(Expression<String?>("foo"))
+        let result = try row.get(Expression<String?>("foo"))
 
         XCTAssertNil(result)
     }
@@ -56,9 +56,9 @@ class RowTests: XCTestCase {
         }
     }
 
-    public func test_get_type_mismatch_optional_returns_nil() {
+    public func test_get_type_mismatch_optional_returns_nil() throws {
         let row = Row(["\"foo\"": 0], ["value"])
-        let result = try! row.get(Expression<Int?>("foo"))
+        let result = try row.get(Expression<Int?>("foo"))
         XCTAssertNil(result)
     }
 
diff --git a/Tests/SQLiteTests/SelectTests.swift b/Tests/SQLiteTests/SelectTests.swift
index d1126b66..52d5bb6b 100644
--- a/Tests/SQLiteTests/SelectTests.swift
+++ b/Tests/SQLiteTests/SelectTests.swift
@@ -20,7 +20,7 @@ class SelectTests: SQLiteTestCase {
         )
     }
 
-    func test_select_columns_from_multiple_tables() {
+    func test_select_columns_from_multiple_tables() throws {
         let usersData = Table("users_name")
         let users = Table("users")
 
@@ -29,14 +29,14 @@ class SelectTests: SQLiteTestCase {
         let userID = Expression<Int64>("user_id")
         let email = Expression<String>("email")
 
-        try! insertUser("Joey")
-        try! db.run(usersData.insert(
+        try insertUser("Joey")
+        try db.run(usersData.insert(
             id <- 1,
             userID <- 1,
             name <- "Joey"
         ))
 
-        try! db.prepare(users.select(name, email).join(usersData, on: userID == users[id])).forEach {
+        try db.prepare(users.select(name, email).join(usersData, on: userID == users[id])).forEach {
             XCTAssertEqual($0[name], "Joey")
             XCTAssertEqual($0[email], "Joey@example.com")
         }
diff --git a/Tests/SQLiteTests/StatementTests.swift b/Tests/SQLiteTests/StatementTests.swift
index aa05de34..3286b792 100644
--- a/Tests/SQLiteTests/StatementTests.swift
+++ b/Tests/SQLiteTests/StatementTests.swift
@@ -7,30 +7,30 @@ class StatementTests: SQLiteTestCase {
         try createUsersTable()
     }
 
-    func test_cursor_to_blob() {
-        try! insertUsers("alice")
-        let statement = try! db.prepare("SELECT email FROM users")
-        XCTAssert(try! statement.step())
+    func test_cursor_to_blob() throws {
+        try insertUsers("alice")
+        let statement = try db.prepare("SELECT email FROM users")
+        XCTAssert(try statement.step())
         let blob = statement.row[0] as Blob
         XCTAssertEqual("alice@example.com", String(bytes: blob.bytes, encoding: .utf8)!)
     }
 
-    func test_zero_sized_blob_returns_null() {
+    func test_zero_sized_blob_returns_null() throws {
         let blobs = Table("blobs")
         let blobColumn = Expression<Blob>("blob_column")
-        try! db.run(blobs.create { $0.column(blobColumn) })
-        try! db.run(blobs.insert(blobColumn <- Blob(bytes: [])))
-        let blobValue = try! db.scalar(blobs.select(blobColumn).limit(1, offset: 0))
+        try db.run(blobs.create { $0.column(blobColumn) })
+        try db.run(blobs.insert(blobColumn <- Blob(bytes: [])))
+        let blobValue = try db.scalar(blobs.select(blobColumn).limit(1, offset: 0))
         XCTAssertEqual([], blobValue.bytes)
     }
 
-    func test_prepareRowIterator() {
+    func test_prepareRowIterator() throws {
         let names = ["a", "b", "c"]
-        try! insertUsers(names)
+        try insertUsers(names)
 
         let emailColumn = Expression<String>("email")
-        let statement = try! db.prepare("SELECT email FROM users")
-        let emails = try! statement.prepareRowIterator().map { $0[emailColumn] }
+        let statement = try db.prepare("SELECT email FROM users")
+        let emails = try statement.prepareRowIterator().map { $0[emailColumn] }
 
         XCTAssertEqual(names.map({ "\($0)@example.com" }), emails.sorted())
     }
diff --git a/Tests/SQLiteTests/TestHelpers.swift b/Tests/SQLiteTests/TestHelpers.swift
index 6e8e9ebb..2a8c7e39 100644
--- a/Tests/SQLiteTests/TestHelpers.swift
+++ b/Tests/SQLiteTests/TestHelpers.swift
@@ -54,8 +54,8 @@ class SQLiteTestCase: XCTestCase {
         )
     }
 
-    func assertSQL(_ SQL: String, _ statement: Statement, _ message: String? = nil, file: StaticString = #file, line: UInt = #line) {
-        try! statement.run()
+    func assertSQL(_ SQL: String, _ statement: Statement, _ message: String? = nil, file: StaticString = #file, line: UInt = #line) throws {
+        try statement.run()
         assertSQL(SQL, 1, message, file: file, line: line)
         if let count = trace[SQL] { trace[SQL] = count - 1 }
     }

From 44383dfe5f4d9c4b07a5f921f153e051e82d32a4 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Sun, 17 Jul 2022 16:21:17 +0200
Subject: [PATCH 159/326] Fix bundle lookup

---
 Tests/SQLiteTests/Fixtures.swift | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/Tests/SQLiteTests/Fixtures.swift b/Tests/SQLiteTests/Fixtures.swift
index f3851cba..1306aa5a 100644
--- a/Tests/SQLiteTests/Fixtures.swift
+++ b/Tests/SQLiteTests/Fixtures.swift
@@ -1,7 +1,11 @@
 import Foundation
 
 func fixture(_ name: String, withExtension: String?) -> String {
+    #if SWIFT_PACKAGE
+    let testBundle = Bundle.module
+    #else
     let testBundle = Bundle(for: SQLiteTestCase.self)
+    #endif
 
     for resource in [name, "fixtures/\(name)"] {
         if let url = testBundle.url(

From 96b014df6095a7434d38950fb819f945d1222191 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Sun, 17 Jul 2022 18:14:08 +0200
Subject: [PATCH 160/326] Always enable URI filenames

https://sqlite.org/uri.html
---
 Sources/SQLite/Core/Connection.swift | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/Sources/SQLite/Core/Connection.swift b/Sources/SQLite/Core/Connection.swift
index 60b9f1bd..35edfce9 100644
--- a/Sources/SQLite/Core/Connection.swift
+++ b/Sources/SQLite/Core/Connection.swift
@@ -105,7 +105,10 @@ public final class Connection {
     /// - Returns: A new database connection.
     public init(_ location: Location = .inMemory, readonly: Bool = false) throws {
         let flags = readonly ? SQLITE_OPEN_READONLY : SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE
-        try check(sqlite3_open_v2(location.description, &_handle, flags | SQLITE_OPEN_FULLMUTEX, nil))
+        try check(sqlite3_open_v2(location.description,
+                                  &_handle,
+                                  flags | SQLITE_OPEN_FULLMUTEX | SQLITE_OPEN_URI,
+                                  nil))
         queue.setSpecific(key: Connection.queueKey, value: queueContext)
     }
 

From 41ce3784a0a521aecb309bc7fbc24fa5017a3daf Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Sun, 17 Jul 2022 19:06:11 +0200
Subject: [PATCH 161/326] Move fixtures to more default `Resources`

---
 Package.swift                                 |  19 ++++++++++--------
 SQLite.swift.podspec                          |   6 +++---
 SQLite.xcodeproj/project.pbxproj              |  16 +++++++--------
 Sources/SQLite/Core/Connection.swift          |   2 +-
 Tests/SQLiteTests/CipherTests.swift           |  15 +++++++-------
 Tests/SQLiteTests/ConnectionTests.swift       |   9 +++++++--
 Tests/SQLiteTests/Fixtures.swift              |   2 +-
 .../encrypted-3.x.sqlite                      | Bin
 .../encrypted-4.x.sqlite                      | Bin
 .../{fixtures => Resources}/test.sqlite       | Bin
 10 files changed, 39 insertions(+), 30 deletions(-)
 rename Tests/SQLiteTests/{fixtures => Resources}/encrypted-3.x.sqlite (100%)
 rename Tests/SQLiteTests/{fixtures => Resources}/encrypted-4.x.sqlite (100%)
 rename Tests/SQLiteTests/{fixtures => Resources}/test.sqlite (100%)

diff --git a/Package.swift b/Package.swift
index 1130d571..b32fd881 100644
--- a/Package.swift
+++ b/Package.swift
@@ -40,9 +40,7 @@ let package = Package(
                 "Info.plist"
             ],
             resources: [
-                .copy("fixtures/encrypted-3.x.sqlite"),
-                .copy("fixtures/encrypted-4.x.sqlite"),
-                .copy("fixtures/test.sqlite")
+                .copy("Resources")
             ]
         )
     ]
@@ -56,10 +54,15 @@ package.targets = [
         dependencies: [.product(name: "CSQLite", package: "CSQLite")],
         exclude: ["Extensions/FTS4.swift", "Extensions/FTS5.swift"]
     ),
-    .testTarget(name: "SQLiteTests", dependencies: ["SQLite"], path: "Tests/SQLiteTests", exclude: [
-        "FTSIntegrationTests.swift",
-        "FTS4Tests.swift",
-        "FTS5Tests.swift"
-    ])
+    .testTarget(
+        name: "SQLiteTests",
+        dependencies: ["SQLite"],
+        path: "Tests/SQLiteTests", exclude: [
+            "FTSIntegrationTests.swift",
+            "FTS4Tests.swift",
+            "FTS5Tests.swift"
+        ],
+        resources: [ .copy("Resources") ]
+    )
 ]
 #endif
diff --git a/SQLite.swift.podspec b/SQLite.swift.podspec
index 4a1787bb..451b4886 100644
--- a/SQLite.swift.podspec
+++ b/SQLite.swift.podspec
@@ -35,7 +35,7 @@ Pod::Spec.new do |s|
     ss.library = 'sqlite3'
 
     ss.test_spec 'tests' do |test_spec|
-      test_spec.resources = 'Tests/SQLiteTests/fixtures/*'
+      test_spec.resources = 'Tests/SQLiteTests/Resources/*'
       test_spec.source_files = 'Tests/SQLiteTests/*.swift'
       test_spec.ios.deployment_target = ios_deployment_target
       test_spec.tvos.deployment_target = tvos_deployment_target
@@ -55,7 +55,7 @@ Pod::Spec.new do |s|
     ss.dependency 'sqlite3'
 
     ss.test_spec 'tests' do |test_spec|
-      test_spec.resources = 'Tests/SQLiteTests/fixtures/*'
+      test_spec.resources = 'Tests/SQLiteTests/Resources/*'
       test_spec.source_files = 'Tests/SQLiteTests/*.swift'
       test_spec.ios.deployment_target = ios_deployment_target
       test_spec.tvos.deployment_target = tvos_deployment_target
@@ -73,7 +73,7 @@ Pod::Spec.new do |s|
     ss.dependency 'SQLCipher', '>= 4.0.0'
 
     ss.test_spec 'tests' do |test_spec|
-      test_spec.resources = 'Tests/SQLiteTests/fixtures/*'
+      test_spec.resources = 'Tests/SQLiteTests/Resources/*'
       test_spec.source_files = 'Tests/SQLiteTests/*.swift'
       test_spec.ios.deployment_target = ios_deployment_target
       test_spec.tvos.deployment_target = tvos_deployment_target
diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj
index 4e01067f..45e6ea63 100644
--- a/SQLite.xcodeproj/project.pbxproj
+++ b/SQLite.xcodeproj/project.pbxproj
@@ -68,7 +68,6 @@
 		19A17490543609FCED53CACC /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1710E73A46D5AC721CDA9 /* Errors.swift */; };
 		19A174D78559CD30679BCCCB /* FTS5Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1721B8984686B9963B45D /* FTS5Tests.swift */; };
 		19A1750CEE9B05267995CF3D /* FTS5.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1730E4390C775C25677D1 /* FTS5.swift */; };
-		19A175DFF47B84757E547C62 /* fixtures in Resources */ = {isa = PBXBuildFile; fileRef = 19A17E2695737FAB5D6086E3 /* fixtures */; };
 		19A176376CB6A94759F7980A /* Connection+Aggregation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A175A9CB446640AE6F2200 /* Connection+Aggregation.swift */; };
 		19A176406BDE9D9C80CC9FA3 /* QueryIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17BA6B4E282C1315A115C /* QueryIntegrationTests.swift */; };
 		19A1769C1F3A7542BECF50FF /* DateAndTimeFunctionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1729B75C33F9A0B9A89C1 /* DateAndTimeFunctionTests.swift */; };
@@ -92,10 +91,8 @@
 		19A17E29278A12BC4F542506 /* DateAndTimeFunctions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17BA55DABB480F9020C8A /* DateAndTimeFunctions.swift */; };
 		19A17EC0D68BA8C03288ADF7 /* FTS5.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1730E4390C775C25677D1 /* FTS5.swift */; };
 		19A17F1B3F0A3C96B5ED6D64 /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17E723300E5ED3771DCB5 /* Result.swift */; };
-		19A17F3E1F7ACA33BD43E138 /* fixtures in Resources */ = {isa = PBXBuildFile; fileRef = 19A17E2695737FAB5D6086E3 /* fixtures */; };
 		19A17F60B685636D1F83C2DD /* Fixtures.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17B93B48B5560E6E51791 /* Fixtures.swift */; };
 		19A17FB80B94E882050AA908 /* FoundationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1794CC4D7827E997E32A7 /* FoundationTests.swift */; };
-		19A17FDA323BAFDEC627E76F /* fixtures in Resources */ = {isa = PBXBuildFile; fileRef = 19A17E2695737FAB5D6086E3 /* fixtures */; };
 		19A17FF4A10B44D3937C8CAC /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1710E73A46D5AC721CDA9 /* Errors.swift */; };
 		3717F908221F5D8800B9BD3D /* CustomAggregationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3717F907221F5D7C00B9BD3D /* CustomAggregationTests.swift */; };
 		3717F909221F5D8900B9BD3D /* CustomAggregationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3717F907221F5D7C00B9BD3D /* CustomAggregationTests.swift */; };
@@ -137,6 +134,9 @@
 		3DF7B792288449BA005DD8CA /* URIQueryParameter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DF7B790288449BA005DD8CA /* URIQueryParameter.swift */; };
 		3DF7B793288449BA005DD8CA /* URIQueryParameter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DF7B790288449BA005DD8CA /* URIQueryParameter.swift */; };
 		3DF7B794288449BA005DD8CA /* URIQueryParameter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DF7B790288449BA005DD8CA /* URIQueryParameter.swift */; };
+		3DF7B79628846FCC005DD8CA /* Resources in Resources */ = {isa = PBXBuildFile; fileRef = 3DF7B79528846FCC005DD8CA /* Resources */; };
+		3DF7B79828846FED005DD8CA /* Resources in Resources */ = {isa = PBXBuildFile; fileRef = 3DF7B79528846FCC005DD8CA /* Resources */; };
+		3DF7B79928847055005DD8CA /* Resources in Resources */ = {isa = PBXBuildFile; fileRef = 3DF7B79528846FCC005DD8CA /* Resources */; };
 		49EB68C41F7B3CB400D89D40 /* Coding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49EB68C31F7B3CB400D89D40 /* Coding.swift */; };
 		49EB68C51F7B3CB400D89D40 /* Coding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49EB68C31F7B3CB400D89D40 /* Coding.swift */; };
 		49EB68C61F7B3CB400D89D40 /* Coding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49EB68C31F7B3CB400D89D40 /* Coding.swift */; };
@@ -267,7 +267,6 @@
 		19A17B93B48B5560E6E51791 /* Fixtures.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Fixtures.swift; sourceTree = "<group>"; };
 		19A17BA55DABB480F9020C8A /* DateAndTimeFunctions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DateAndTimeFunctions.swift; sourceTree = "<group>"; };
 		19A17BA6B4E282C1315A115C /* QueryIntegrationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QueryIntegrationTests.swift; sourceTree = "<group>"; };
-		19A17E2695737FAB5D6086E3 /* fixtures */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = folder; path = fixtures; sourceTree = "<group>"; };
 		19A17E723300E5ED3771DCB5 /* Result.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Result.swift; sourceTree = "<group>"; };
 		19A17EA3A313F129011B3FA0 /* Release.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = Release.md; sourceTree = "<group>"; };
 		3717F907221F5D7C00B9BD3D /* CustomAggregationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomAggregationTests.swift; sourceTree = "<group>"; };
@@ -277,6 +276,7 @@
 		3DF7B78728842972005DD8CA /* Connection+Attach.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Connection+Attach.swift"; sourceTree = "<group>"; };
 		3DF7B78C28842C23005DD8CA /* ResultTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResultTests.swift; sourceTree = "<group>"; };
 		3DF7B790288449BA005DD8CA /* URIQueryParameter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URIQueryParameter.swift; sourceTree = "<group>"; };
+		3DF7B79528846FCC005DD8CA /* Resources */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Resources; sourceTree = "<group>"; };
 		49EB68C31F7B3CB400D89D40 /* Coding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Coding.swift; sourceTree = "<group>"; };
 		997DF2AD287FC06D00F8DF95 /* Query+with.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Query+with.swift"; sourceTree = "<group>"; };
 		A121AC451CA35C79005A31D1 /* SQLite.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SQLite.framework; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -448,7 +448,7 @@
 		EE247AE11C3F04ED00AE3E12 /* SQLiteTests */ = {
 			isa = PBXGroup;
 			children = (
-				19A17E2695737FAB5D6086E3 /* fixtures */,
+				3DF7B79528846FCC005DD8CA /* Resources */,
 				EE247B1A1C3F137700AE3E12 /* AggregateFunctionsTests.swift */,
 				EE247B1B1C3F137700AE3E12 /* BlobTests.swift */,
 				EE247B1D1C3F137700AE3E12 /* ConnectionTests.swift */,
@@ -812,7 +812,7 @@
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				19A17F3E1F7ACA33BD43E138 /* fixtures in Resources */,
+				3DF7B79828846FED005DD8CA /* Resources in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -834,7 +834,7 @@
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				19A17FDA323BAFDEC627E76F /* fixtures in Resources */,
+				3DF7B79628846FCC005DD8CA /* Resources in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -849,7 +849,7 @@
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				19A175DFF47B84757E547C62 /* fixtures in Resources */,
+				3DF7B79928847055005DD8CA /* Resources in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
diff --git a/Sources/SQLite/Core/Connection.swift b/Sources/SQLite/Core/Connection.swift
index 35edfce9..68b83821 100644
--- a/Sources/SQLite/Core/Connection.swift
+++ b/Sources/SQLite/Core/Connection.swift
@@ -104,7 +104,7 @@ public final class Connection {
     ///
     /// - Returns: A new database connection.
     public init(_ location: Location = .inMemory, readonly: Bool = false) throws {
-        let flags = readonly ? SQLITE_OPEN_READONLY : SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE
+        let flags = readonly ? SQLITE_OPEN_READONLY : (SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE)
         try check(sqlite3_open_v2(location.description,
                                   &_handle,
                                   flags | SQLITE_OPEN_FULLMUTEX | SQLITE_OPEN_URI,
diff --git a/Tests/SQLiteTests/CipherTests.swift b/Tests/SQLiteTests/CipherTests.swift
index f25dbe5b..e2d09901 100644
--- a/Tests/SQLiteTests/CipherTests.swift
+++ b/Tests/SQLiteTests/CipherTests.swift
@@ -4,12 +4,13 @@ import SQLite
 import SQLCipher
 
 class CipherTests: XCTestCase {
-
-    let db1 = try Connection()
-    let db2 = try Connection()
+    var db1: Connection!
+    var db2: Connection!
 
     override func setUpWithError() throws {
-        // db
+        db1 = try Connection()
+        db2 = try Connection()
+        // db1
 
         try db1.key("hello")
 
@@ -30,7 +31,7 @@ class CipherTests: XCTestCase {
         XCTAssertEqual(1, try db1.scalar("SELECT count(*) FROM foo") as? Int64)
     }
 
-    func test_key_blob_literal() {
+    func test_key_blob_literal() throws {
         let db = try Connection()
         try db.key("x'2DD29CA851E7B56E4697B0E1F08507293D761A05CE4D1B628663F411A8086D99'")
     }
@@ -55,7 +56,7 @@ class CipherTests: XCTestCase {
         _ = try? FileManager.default.removeItem(atPath: path)
 
         let connA = try Connection(path)
-        defer { try FileManager.default.removeItem(atPath: path) }
+        defer { try? FileManager.default.removeItem(atPath: path) }
 
         try connA.key("hello")
         try connA.run("CREATE TABLE foo (bar TEXT)")
@@ -90,7 +91,7 @@ class CipherTests: XCTestCase {
 
         defer {
             // ensure file can be cleaned up afterwards
-            try FileManager.default.setAttributes([FileAttributeKey.immutable: 0], ofItemAtPath: encryptedFile)
+            try? FileManager.default.setAttributes([FileAttributeKey.immutable: 0], ofItemAtPath: encryptedFile)
         }
 
         let conn = try Connection(encryptedFile)
diff --git a/Tests/SQLiteTests/ConnectionTests.swift b/Tests/SQLiteTests/ConnectionTests.swift
index cf51c104..347516f5 100644
--- a/Tests/SQLiteTests/ConnectionTests.swift
+++ b/Tests/SQLiteTests/ConnectionTests.swift
@@ -47,12 +47,17 @@ class ConnectionTests: SQLiteTestCase {
         XCTAssertEqual(url.lastPathComponent, "SQLite.swift Tests.sqlite3")
     }
 
-    func testLocationWithoutUriParameters() {
+    func test_init_with_Uri_and_Parameters() throws {
+        let testDb = fixture("test", withExtension: "sqlite")
+        _ = try Connection(.uri(testDb, parameters: [.cache(.shared)]))
+    }
+
+    func test_location_without_Uri_parameters() {
         let location: Connection.Location = .uri("foo")
         XCTAssertEqual(location.description, "foo")
     }
 
-    func testLocationWithUriParameters() {
+    func test_location_with_Uri_parameters() {
         let location: Connection.Location = .uri("foo", parameters: [.mode(.readOnly), .cache(.private)])
         XCTAssertEqual(location.description, "file:foo?mode=ro&cache=private")
     }
diff --git a/Tests/SQLiteTests/Fixtures.swift b/Tests/SQLiteTests/Fixtures.swift
index 1306aa5a..95b2b6dc 100644
--- a/Tests/SQLiteTests/Fixtures.swift
+++ b/Tests/SQLiteTests/Fixtures.swift
@@ -7,7 +7,7 @@ func fixture(_ name: String, withExtension: String?) -> String {
     let testBundle = Bundle(for: SQLiteTestCase.self)
     #endif
 
-    for resource in [name, "fixtures/\(name)"] {
+    for resource in [name, "Resources/\(name)"] {
         if let url = testBundle.url(
             forResource: resource,
             withExtension: withExtension) {
diff --git a/Tests/SQLiteTests/fixtures/encrypted-3.x.sqlite b/Tests/SQLiteTests/Resources/encrypted-3.x.sqlite
similarity index 100%
rename from Tests/SQLiteTests/fixtures/encrypted-3.x.sqlite
rename to Tests/SQLiteTests/Resources/encrypted-3.x.sqlite
diff --git a/Tests/SQLiteTests/fixtures/encrypted-4.x.sqlite b/Tests/SQLiteTests/Resources/encrypted-4.x.sqlite
similarity index 100%
rename from Tests/SQLiteTests/fixtures/encrypted-4.x.sqlite
rename to Tests/SQLiteTests/Resources/encrypted-4.x.sqlite
diff --git a/Tests/SQLiteTests/fixtures/test.sqlite b/Tests/SQLiteTests/Resources/test.sqlite
similarity index 100%
rename from Tests/SQLiteTests/fixtures/test.sqlite
rename to Tests/SQLiteTests/Resources/test.sqlite

From 9023fbc52ba86e0b002275698f071efa6eaeb899 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Mon, 18 Jul 2022 00:44:53 +0200
Subject: [PATCH 162/326] Add documenation, update changelog

---
 CHANGELOG.md                                | 24 ++++++++++++++++
 Documentation/Index.md                      | 32 ++++++++++++++++++++-
 SQLite.xcodeproj/project.pbxproj            |  2 ++
 Sources/SQLite/Core/URIQueryParameter.swift |  5 ++--
 4 files changed, 60 insertions(+), 3 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 786084f8..210e324d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,15 @@
+0.14.0 (tbd), [diff][diff-0.14.0]
+========================================
+
+* Support ATTACH/DETACH ([#30][], [##1142][])
+* Support WITH clause ([#1139][])
+* Add value conformance for NSURL ([#1141][])
+* Add decoding for UUID ([#1137][])
+* Fix insertMany([Encodable]) ([#1130][], [#1138]][])
+* Fix incorrect spelling of 'remove_diacritics' ([#1128][])
+* Fix project build order ([#1131][])
+* Performance improvements ([#1109][], [#1115][], [#1132][])
+
 0.13.3 (25-01-2022), [diff][diff-0.13.3]
 ========================================
 
@@ -110,7 +122,9 @@
 [diff-0.13.1]: https://github.com/stephencelis/SQLite.swift/compare/0.13.0...0.13.1
 [diff-0.13.2]: https://github.com/stephencelis/SQLite.swift/compare/0.13.1...0.13.2
 [diff-0.13.3]: https://github.com/stephencelis/SQLite.swift/compare/0.13.2...0.13.3
+[diff-0.14.0]: https://github.com/stephencelis/SQLite.swift/compare/0.13.3...0.14.0
 
+[#30]: https://github.com/stephencelis/SQLite.swift/issues/30
 [#142]: https://github.com/stephencelis/SQLite.swift/issues/142
 [#315]: https://github.com/stephencelis/SQLite.swift/issues/315
 [#426]: https://github.com/stephencelis/SQLite.swift/pull/426
@@ -150,6 +164,16 @@
 [#1095]: https://github.com/stephencelis/SQLite.swift/pull/1095
 [#1100]: https://github.com/stephencelis/SQLite.swift/pull/1100
 [#1105]: https://github.com/stephencelis/SQLite.swift/pull/1105
+[#1109]: https://github.com/stephencelis/SQLite.swift/issues/1109
 [#1112]: https://github.com/stephencelis/SQLite.swift/pull/1112
+[#1115]: https://github.com/stephencelis/SQLite.swift/pull/1115
 [#1119]: https://github.com/stephencelis/SQLite.swift/pull/1119
 [#1121]: https://github.com/stephencelis/SQLite.swift/pull/1121
+[#1128]: https://github.com/stephencelis/SQLite.swift/issues/1128
+[#1130]: https://github.com/stephencelis/SQLite.swift/issues/1130
+[#1131]: https://github.com/stephencelis/SQLite.swift/pull/1131
+[#1132]: https://github.com/stephencelis/SQLite.swift/pull/1132
+[#1137]: https://github.com/stephencelis/SQLite.swift/pull/1137
+[#1138]: https://github.com/stephencelis/SQLite.swift/pull/1138
+[#1139]: https://github.com/stephencelis/SQLite.swift/pull/1139
+[#1142]: https://github.com/stephencelis/SQLite.swift/pull/1142
diff --git a/Documentation/Index.md b/Documentation/Index.md
index 7a9a390f..aded5739 100644
--- a/Documentation/Index.md
+++ b/Documentation/Index.md
@@ -10,6 +10,7 @@
       - [Read-Write Databases](#read-write-databases)
       - [Read-Only Databases](#read-only-databases)
       - [In-Memory Databases](#in-memory-databases)
+      - [URI parameters](#uri-parameters)
       - [Thread-Safety](#thread-safety)
   - [Building Type-Safe SQL](#building-type-safe-sql)
     - [Expressions](#expressions)
@@ -61,9 +62,9 @@
   - [Custom Collations](#custom-collations)
   - [Full-text Search](#full-text-search)
   - [Executing Arbitrary SQL](#executing-arbitrary-sql)
+  - [Attaching and detaching databases](#attaching-and-detaching-databases)
   - [Logging](#logging)
 
-
 [↩]: #sqliteswift-documentation
 
 
@@ -334,6 +335,16 @@ let db = try Connection(.temporary)
 In-memory databases are automatically deleted when the database connection is
 closed.
 
+#### URI parameters
+
+We can pass `.uri` to the `Connection` initializer to control more aspects of
+the database connection with the help of `URIQueryParameter`s:
+
+```swift
+let db = try Connection(.uri("file.sqlite", parameters: [.cache(.private)], .noLock(true)]))
+```
+
+See [Uniform Resource Identifiers](https://www.sqlite.org/uri.html) for more details.
 
 #### Thread-Safety
 
@@ -2070,6 +2081,25 @@ let backup = try db.backup(usingConnection: target)
 try backup.step()
 ```
 
+## Attaching and detaching databases
+
+We can [ATTACH](https://www3.sqlite.org/lang_attach.html) and [DETACH](https://www3.sqlite.org/lang_detach.html)
+databases to an existing connection:
+
+```swift
+let db = try Connection("db.sqlite")
+
+try db.attach(.uri("external.sqlite", parameters: [.mode(.readOnly)), as: "external")
+// ATTACH DATABASE 'file:external.sqlite?mode=ro' AS 'external'
+
+let table = Table("table", database: "external")
+let count = try db.scalar(table.count)
+// SELECT count(*) FROM 'external.table'
+
+try db.detach("external")
+// DETACH DATABASE 'external'
+```
+
 ## Logging
 
 We can log SQL using the database’s `trace` function.
diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj
index 45e6ea63..064357a2 100644
--- a/SQLite.xcodeproj/project.pbxproj
+++ b/SQLite.xcodeproj/project.pbxproj
@@ -277,6 +277,7 @@
 		3DF7B78C28842C23005DD8CA /* ResultTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResultTests.swift; sourceTree = "<group>"; };
 		3DF7B790288449BA005DD8CA /* URIQueryParameter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URIQueryParameter.swift; sourceTree = "<group>"; };
 		3DF7B79528846FCC005DD8CA /* Resources */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Resources; sourceTree = "<group>"; };
+		3DF7B79A2884C353005DD8CA /* CHANGELOG.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = CHANGELOG.md; sourceTree = "<group>"; };
 		49EB68C31F7B3CB400D89D40 /* Coding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Coding.swift; sourceTree = "<group>"; };
 		997DF2AD287FC06D00F8DF95 /* Query+with.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Query+with.swift"; sourceTree = "<group>"; };
 		A121AC451CA35C79005A31D1 /* SQLite.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SQLite.framework; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -534,6 +535,7 @@
 			isa = PBXGroup;
 			children = (
 				EE247B771C3F40D700AE3E12 /* README.md */,
+				3DF7B79A2884C353005DD8CA /* CHANGELOG.md */,
 				EE247B8B1C3F820300AE3E12 /* CONTRIBUTING.md */,
 				EE247B931C3F826100AE3E12 /* SQLite.swift.podspec */,
 				EE247B8D1C3F821200AE3E12 /* Makefile */,
diff --git a/Sources/SQLite/Core/URIQueryParameter.swift b/Sources/SQLite/Core/URIQueryParameter.swift
index c82a7cf7..abbab2e7 100644
--- a/Sources/SQLite/Core/URIQueryParameter.swift
+++ b/Sources/SQLite/Core/URIQueryParameter.swift
@@ -1,5 +1,6 @@
 import Foundation
 
+/// See https://www.sqlite.org/uri.html
 public enum URIQueryParameter: CustomStringConvertible {
     public enum FileMode: String {
         case readOnly = "ro", readWrite = "rw", readWriteCreate = "rwc", memory
@@ -29,7 +30,7 @@ public enum URIQueryParameter: CustomStringConvertible {
     case nolock(Bool)
 
     /// The psow query parameter overrides the `powersafe_overwrite` property of the database file being opened.
-    case psow(Bool)
+    case powersafeOverwrite(Bool)
 
     /// The vfs query parameter causes the database connection to be opened using the VFS called NAME.
     case vfs(String)
@@ -45,7 +46,7 @@ public enum URIQueryParameter: CustomStringConvertible {
         case .modeOf(let filename): return .init(name: "modeOf", value: filename)
         case .mode(let fileMode): return .init(name: "mode", value: fileMode.rawValue)
         case .nolock(let bool): return .init(name: "nolock", value: NSNumber(value: bool).description)
-        case .psow(let bool): return .init(name: "psow", value: NSNumber(value: bool).description)
+        case .powersafeOverwrite(let bool): return .init(name: "psow", value: NSNumber(value: bool).description)
         case .vfs(let name): return .init(name: "vfs", value: name)
         }
     }

From 2eed2003e548630a3116a6d0fb850a341e3405a9 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Mon, 18 Jul 2022 00:55:40 +0200
Subject: [PATCH 163/326] Fix doc errors

---
 CHANGELOG.md                     | 16 +++++++++-------
 Documentation/Index.md           |  6 +++---
 Documentation/Planning.md        |  2 --
 SQLite.xcodeproj/project.pbxproj |  2 ++
 4 files changed, 14 insertions(+), 12 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 210e324d..37e96955 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,16 +1,16 @@
 0.14.0 (tbd), [diff][diff-0.14.0]
 ========================================
 
-* Support ATTACH/DETACH ([#30][], [##1142][])
-* Support WITH clause ([#1139][])
-* Add value conformance for NSURL ([#1141][])
-* Add decoding for UUID ([#1137][])
-* Fix insertMany([Encodable]) ([#1130][], [#1138]][])
-* Fix incorrect spelling of 'remove_diacritics' ([#1128][])
+* Support `ATTACH`/`DETACH` ([#30][], [#1142][])
+* Support `WITH` clause ([#1139][])
+* Add `Value` conformance for `NSURL` ([#1110][], [#1141][])
+* Add decoding for `UUID` ([#1137][])
+* Fix `insertMany([Encodable])` ([#1130][], [#1138][])
+* Fix incorrect spelling of `remove_diacritics` ([#1128][])
 * Fix project build order ([#1131][])
 * Performance improvements ([#1109][], [#1115][], [#1132][])
 
-0.13.3 (25-01-2022), [diff][diff-0.13.3]
+0.13.3 (27-03-2022), [diff][diff-0.13.3]
 ========================================
 
 * UUID Fix ([#1112][])
@@ -165,6 +165,7 @@
 [#1100]: https://github.com/stephencelis/SQLite.swift/pull/1100
 [#1105]: https://github.com/stephencelis/SQLite.swift/pull/1105
 [#1109]: https://github.com/stephencelis/SQLite.swift/issues/1109
+[#1110]: https://github.com/stephencelis/SQLite.swift/pull/1110
 [#1112]: https://github.com/stephencelis/SQLite.swift/pull/1112
 [#1115]: https://github.com/stephencelis/SQLite.swift/pull/1115
 [#1119]: https://github.com/stephencelis/SQLite.swift/pull/1119
@@ -176,4 +177,5 @@
 [#1137]: https://github.com/stephencelis/SQLite.swift/pull/1137
 [#1138]: https://github.com/stephencelis/SQLite.swift/pull/1138
 [#1139]: https://github.com/stephencelis/SQLite.swift/pull/1139
+[#1141]: https://github.com/stephencelis/SQLite.swift/pull/1141
 [#1142]: https://github.com/stephencelis/SQLite.swift/pull/1142
diff --git a/Documentation/Index.md b/Documentation/Index.md
index aded5739..f036520f 100644
--- a/Documentation/Index.md
+++ b/Documentation/Index.md
@@ -341,10 +341,10 @@ We can pass `.uri` to the `Connection` initializer to control more aspects of
 the database connection with the help of `URIQueryParameter`s:
 
 ```swift
-let db = try Connection(.uri("file.sqlite", parameters: [.cache(.private)], .noLock(true)]))
+let db = try Connection(.uri("file.sqlite", parameters: [.cache(.private), .noLock(true)]))
 ```
 
-See [Uniform Resource Identifiers](https://www.sqlite.org/uri.html) for more details.
+See [Uniform Resource Identifiers](https://www.sqlite.org/uri.html#recognized_query_parameters) for more details.
 
 #### Thread-Safety
 
@@ -2089,7 +2089,7 @@ databases to an existing connection:
 ```swift
 let db = try Connection("db.sqlite")
 
-try db.attach(.uri("external.sqlite", parameters: [.mode(.readOnly)), as: "external")
+try db.attach(.uri("external.sqlite", parameters: [.mode(.readOnly)], as: "external")
 // ATTACH DATABASE 'file:external.sqlite?mode=ro' AS 'external'
 
 let table = Table("table", database: "external")
diff --git a/Documentation/Planning.md b/Documentation/Planning.md
index 0dea6d71..6067b81e 100644
--- a/Documentation/Planning.md
+++ b/Documentation/Planning.md
@@ -21,8 +21,6 @@ be referred to when it comes time to add the corresponding feature._
 
 ### Features
 
- * encapsulate ATTACH DATABASE / DETACH DATABASE as methods, per
-   [#30](https://github.com/stephencelis/SQLite.swift/issues/30)
  * provide separate threads for update vs read, so updates don't block reads,
    per [#236](https://github.com/stephencelis/SQLite.swift/issues/236)
  * expose triggers, per
diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj
index 064357a2..45ff075f 100644
--- a/SQLite.xcodeproj/project.pbxproj
+++ b/SQLite.xcodeproj/project.pbxproj
@@ -278,6 +278,7 @@
 		3DF7B790288449BA005DD8CA /* URIQueryParameter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URIQueryParameter.swift; sourceTree = "<group>"; };
 		3DF7B79528846FCC005DD8CA /* Resources */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Resources; sourceTree = "<group>"; };
 		3DF7B79A2884C353005DD8CA /* CHANGELOG.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = CHANGELOG.md; sourceTree = "<group>"; };
+		3DF7B79B2884C901005DD8CA /* Planning.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = Planning.md; sourceTree = "<group>"; };
 		49EB68C31F7B3CB400D89D40 /* Coding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Coding.swift; sourceTree = "<group>"; };
 		997DF2AD287FC06D00F8DF95 /* Query+with.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Query+with.swift"; sourceTree = "<group>"; };
 		A121AC451CA35C79005A31D1 /* SQLite.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SQLite.framework; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -551,6 +552,7 @@
 			isa = PBXGroup;
 			children = (
 				EE247B8F1C3F822500AE3E12 /* Index.md */,
+				3DF7B79B2884C901005DD8CA /* Planning.md */,
 				EE247B901C3F822500AE3E12 /* Resources */,
 				19A17EA3A313F129011B3FA0 /* Release.md */,
 				19A1794B7972D14330A65BBD /* Linux.md */,

From 0af0e4d55e7571bc5a53bb10f12e0669fdf0470e Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Mon, 18 Jul 2022 01:03:57 +0200
Subject: [PATCH 164/326] More supported types

---
 Documentation/Index.md | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/Documentation/Index.md b/Documentation/Index.md
index f036520f..0930aab0 100644
--- a/Documentation/Index.md
+++ b/Documentation/Index.md
@@ -383,6 +383,9 @@ to their [SQLite counterparts](https://www.sqlite.org/datatype3.html).
 | `String`        | `TEXT`      |
 | `nil`           | `NULL`      |
 | `SQLite.Blob`†  | `BLOB`      |
+| `URL`           | `TEXT`      |
+| `UUID`          | `TEXT`      |
+| `Date`          | `TEXT`      |
 
 > *While `Int64` is the basic, raw type (to preserve 64-bit integers on
 > 32-bit platforms), `Int` and `Bool` work transparently.

From 7868fdfcf606c2aaf18fb0471393234113b537a5 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Mon, 18 Jul 2022 01:22:27 +0200
Subject: [PATCH 165/326] Fix doc

---
 Documentation/Index.md | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/Documentation/Index.md b/Documentation/Index.md
index 0930aab0..75c1d2cc 100644
--- a/Documentation/Index.md
+++ b/Documentation/Index.md
@@ -1103,8 +1103,8 @@ users.limit(5, offset: 5)
 
 #### Recursive and Hierarchical Queries
 
-We can perform a recursive or hierarchical query using a [query's](#queries) `with`
-function.
+We can perform a recursive or hierarchical query using a [query's](#queries)
+[`WITH`](https://sqlite.org/lang_with.html) function.
 
 ```swift
 // Get the management chain for the manager with id == 8
@@ -2092,7 +2092,7 @@ databases to an existing connection:
 ```swift
 let db = try Connection("db.sqlite")
 
-try db.attach(.uri("external.sqlite", parameters: [.mode(.readOnly)], as: "external")
+try db.attach(.uri("external.sqlite", parameters: [.mode(.readOnly)]), as: "external")
 // ATTACH DATABASE 'file:external.sqlite?mode=ro' AS 'external'
 
 let table = Table("table", database: "external")

From be14b42fc71f3c3e8cd977d858ecfe593a168434 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Sun, 17 Jul 2022 12:45:04 +0200
Subject: [PATCH 166/326] Add sqlcipher_export

---
 CHANGELOG.md                                |  2 ++
 Documentation/Index.md                      | 11 +++++++++--
 Sources/SQLite/Core/Connection+Attach.swift | 12 ++++++++++-
 Sources/SQLite/Extensions/Cipher.swift      | 22 +++++++++++++++++++--
 Tests/SQLiteTests/CipherTests.swift         |  9 +++++++++
 Tests/SQLiteTests/Fixtures.swift            |  4 ++++
 6 files changed, 55 insertions(+), 5 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 37e96955..eb7213c1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,7 @@
 * Support `WITH` clause ([#1139][])
 * Add `Value` conformance for `NSURL` ([#1110][], [#1141][])
 * Add decoding for `UUID` ([#1137][])
+* SQLCipher: improve documentation ([#1098][]), add `sqlcipher_export`
 * Fix `insertMany([Encodable])` ([#1130][], [#1138][])
 * Fix incorrect spelling of `remove_diacritics` ([#1128][])
 * Fix project build order ([#1131][])
@@ -162,6 +163,7 @@
 [#1077]: https://github.com/stephencelis/SQLite.swift/issues/1077
 [#1094]: https://github.com/stephencelis/SQLite.swift/pull/1094
 [#1095]: https://github.com/stephencelis/SQLite.swift/pull/1095
+[#1098]: https://github.com/stephencelis/SQLite.swift/issues/1098
 [#1100]: https://github.com/stephencelis/SQLite.swift/pull/1100
 [#1105]: https://github.com/stephencelis/SQLite.swift/pull/1105
 [#1109]: https://github.com/stephencelis/SQLite.swift/issues/1109
diff --git a/Documentation/Index.md b/Documentation/Index.md
index 75c1d2cc..dd832ee2 100644
--- a/Documentation/Index.md
+++ b/Documentation/Index.md
@@ -184,9 +184,16 @@ extend `Connection` with methods to change the database key:
 ```swift
 import SQLite
 
-let db = try Connection("path/to/db.sqlite3")
+let db = try Connection("path/to/encrypted.sqlite3")
 try db.key("secret")
-try db.rekey("another secret")
+try db.rekey("new secret") // changes encryption key on already encrypted db
+```
+
+To encrypt an existing database:
+
+```swift
+let db = try Connection("path/to/unencrypted.sqlite3")
+try db.sqlcipher_export(.uri("encrypted.sqlite3"), key: "secret")
 ```
 
 [CocoaPods]: https://cocoapods.org
diff --git a/Sources/SQLite/Core/Connection+Attach.swift b/Sources/SQLite/Core/Connection+Attach.swift
index b9c08117..47e6af5e 100644
--- a/Sources/SQLite/Core/Connection+Attach.swift
+++ b/Sources/SQLite/Core/Connection+Attach.swift
@@ -10,11 +10,21 @@ import SQLite3
 #endif
 
 extension Connection {
-
+    #if SQLITE_SWIFT_SQLCIPHER
+    /// See https://www.zetetic.net/sqlcipher/sqlcipher-api/#attach
+    public func attach(_ location: Location, as schemaName: String, key: String? = nil) throws {
+        if let key = key {
+            try run("ATTACH DATABASE ? AS ? KEY ?", location.description, schemaName, key)
+        } else {
+            try run("ATTACH DATABASE ? AS ?", location.description, schemaName)
+        }
+    }
+    #else
     /// See  https://www3.sqlite.org/lang_attach.html
     public func attach(_ location: Location, as schemaName: String) throws {
         try run("ATTACH DATABASE ? AS ?", location.description, schemaName)
     }
+    #endif
 
     /// See https://www3.sqlite.org/lang_detach.html
     public func detach(_ schemaName: String) throws {
diff --git a/Sources/SQLite/Extensions/Cipher.swift b/Sources/SQLite/Extensions/Cipher.swift
index b96c2761..8af04df9 100644
--- a/Sources/SQLite/Extensions/Cipher.swift
+++ b/Sources/SQLite/Extensions/Cipher.swift
@@ -6,6 +6,8 @@ import SQLCipher
 extension Connection {
 
     /// - Returns: the SQLCipher version
+    ///
+    /// See https://www.zetetic.net/sqlcipher/sqlcipher-api/#cipher_version
     public var cipherVersion: String? {
         (try? scalar("PRAGMA cipher_version")) as? String
     }
@@ -23,6 +25,8 @@ extension Connection {
     ///            of key data.
     ///            e.g. x'2DD29CA851E7B56E4697B0E1F08507293D761A05CE4D1B628663F411A8086D99'
     /// @param db name of the database, defaults to 'main'
+    ///
+    /// See https://www.zetetic.net/sqlcipher/sqlcipher-api/#sqlite3_key
     public func key(_ key: String, db: String = "main") throws {
         try _key_v2(db: db, keyPointer: key, keySize: key.utf8.count)
     }
@@ -39,6 +43,7 @@ extension Connection {
     /// As "PRAGMA cipher_migrate;" is time-consuming, it is recommended to use this function
     /// only after failure of `key(_ key: String, db: String = "main")`, if older versions of
     /// your app may ise older version of SQLCipher
+    ///
     /// See https://www.zetetic.net/sqlcipher/sqlcipher-api/#cipher_migrate
     /// and https://discuss.zetetic.net/t/upgrading-to-sqlcipher-4/3283
     /// for more details regarding SQLCipher upgrade
@@ -51,11 +56,13 @@ extension Connection {
         try _key_v2(db: db, keyPointer: key.bytes, keySize: key.bytes.count, migrate: true)
     }
 
-    /// Change the key on an open database.  If the current database is not encrypted, this routine
-    /// will encrypt it.
+    /// Change the key on an open database. NB: only works if the database is already encrypted.
+    ///
     /// To change the key on an existing encrypted database, it must first be unlocked with the
     /// current encryption key. Once the database is readable and writeable, rekey can be used
     /// to re-encrypt every page in the database with a new key.
+    ///
+    /// See https://www.zetetic.net/sqlcipher/sqlcipher-api/#sqlite3_rekey
     public func rekey(_ key: String, db: String = "main") throws {
         try _rekey_v2(db: db, keyPointer: key, keySize: key.utf8.count)
     }
@@ -64,6 +71,17 @@ extension Connection {
         try _rekey_v2(db: db, keyPointer: key.bytes, keySize: key.bytes.count)
     }
 
+    /// Converts a non-encrypted database to an encrypted one.
+    ///
+    /// See https://www.zetetic.net/sqlcipher/sqlcipher-api/#sqlcipher_export
+    public func sqlcipher_export(_ location: Location, key: String) throws {
+        let schemaName = "cipher_export"
+
+        try attach(location, as: schemaName, key: key)
+        try run("SELECT sqlcipher_export(?)", schemaName)
+        try detach(schemaName)
+    }
+
     // MARK: - private
     private func _key_v2(db: String,
                          keyPointer: UnsafePointer<UInt8>,
diff --git a/Tests/SQLiteTests/CipherTests.swift b/Tests/SQLiteTests/CipherTests.swift
index e2d09901..cc43272e 100644
--- a/Tests/SQLiteTests/CipherTests.swift
+++ b/Tests/SQLiteTests/CipherTests.swift
@@ -99,6 +99,15 @@ class CipherTests: XCTestCase {
         XCTAssertEqual(1, try conn.scalar("SELECT count(*) FROM foo") as? Int64)
     }
 
+    func test_export() throws {
+        let tmp = temporaryFile()
+        try db1.sqlcipher_export(.uri(tmp), key: "mykey")
+
+        let conn = try Connection(tmp)
+        try conn.key("mykey")
+        XCTAssertEqual(1, try conn.scalar("SELECT count(*) FROM foo") as? Int64)
+    }
+
     private func keyData(length: Int = 64) -> NSMutableData {
         let keyData = NSMutableData(length: length)!
         let result  = SecRandomCopyBytes(kSecRandomDefault, length,
diff --git a/Tests/SQLiteTests/Fixtures.swift b/Tests/SQLiteTests/Fixtures.swift
index 95b2b6dc..bd261d2c 100644
--- a/Tests/SQLiteTests/Fixtures.swift
+++ b/Tests/SQLiteTests/Fixtures.swift
@@ -16,3 +16,7 @@ func fixture(_ name: String, withExtension: String?) -> String {
     }
     fatalError("Cannot find \(name).\(withExtension ?? "")")
 }
+
+func temporaryFile() -> String {
+    URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(UUID().uuidString).path
+}

From ceb13aa1adc8b05fd5d8ccb29cf1af97c79ebd1d Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Tue, 19 Jul 2022 01:13:28 +0200
Subject: [PATCH 167/326] Document changes

---
 CHANGELOG.md           | 3 ++-
 Documentation/Index.md | 7 +++++++
 2 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index eb7213c1..68a5133f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,7 +5,7 @@
 * Support `WITH` clause ([#1139][])
 * Add `Value` conformance for `NSURL` ([#1110][], [#1141][])
 * Add decoding for `UUID` ([#1137][])
-* SQLCipher: improve documentation ([#1098][]), add `sqlcipher_export`
+* SQLCipher: improve documentation ([#1098][]), add `sqlcipher_export` ([#1101][])
 * Fix `insertMany([Encodable])` ([#1130][], [#1138][])
 * Fix incorrect spelling of `remove_diacritics` ([#1128][])
 * Fix project build order ([#1131][])
@@ -165,6 +165,7 @@
 [#1095]: https://github.com/stephencelis/SQLite.swift/pull/1095
 [#1098]: https://github.com/stephencelis/SQLite.swift/issues/1098
 [#1100]: https://github.com/stephencelis/SQLite.swift/pull/1100
+[#1101]: https://github.com/stephencelis/SQLite.swift/issues/1101
 [#1105]: https://github.com/stephencelis/SQLite.swift/pull/1105
 [#1109]: https://github.com/stephencelis/SQLite.swift/issues/1109
 [#1110]: https://github.com/stephencelis/SQLite.swift/pull/1110
diff --git a/Documentation/Index.md b/Documentation/Index.md
index dd832ee2..96fc1ee1 100644
--- a/Documentation/Index.md
+++ b/Documentation/Index.md
@@ -2110,6 +2110,13 @@ try db.detach("external")
 // DETACH DATABASE 'external'
 ```
 
+When compiled for SQLCipher, you can additionally pass a `key` parameter to `attach`:
+
+```swift
+try db.attach(.uri("encrypted.sqlite"), as: "encrypted", key: "secret")
+// ATTACH DATABASE 'encrypted.sqlite' AS 'encrypted' KEY 'secret'
+```
+
 ## Logging
 
 We can log SQL using the database’s `trace` function.

From 136c8a8c4358255fc921be5b1f3148acc514ee36 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Tue, 19 Jul 2022 13:01:26 +0200
Subject: [PATCH 168/326] Remove FTS4 Tokenizer / SQLiteObjc

 Closes #1104
---
 Package.swift                           |  11 +-
 SQLite.xcodeproj/project.pbxproj        |  38 +-----
 Sources/SQLite/Extensions/FTS4.swift    |  27 ----
 Sources/SQLite/SQLite.h                 |   2 -
 Sources/SQLiteObjc/SQLiteObjc.m         | 138 --------------------
 Sources/SQLiteObjc/fts3_tokenizer.h     | 161 ------------------------
 Sources/SQLiteObjc/include/SQLiteObjc.h |  38 ------
 Tests/SQLiteTests/FTS4Tests.swift       |  37 ------
 8 files changed, 7 insertions(+), 445 deletions(-)
 delete mode 100644 Sources/SQLiteObjc/SQLiteObjc.m
 delete mode 100644 Sources/SQLiteObjc/fts3_tokenizer.h
 delete mode 100644 Sources/SQLiteObjc/include/SQLiteObjc.h

diff --git a/Package.swift b/Package.swift
index b32fd881..ab9cbd43 100644
--- a/Package.swift
+++ b/Package.swift
@@ -18,18 +18,10 @@ let package = Package(
     targets: [
         .target(
             name: "SQLite",
-            dependencies: ["SQLiteObjc"],
             exclude: [
                 "Info.plist"
             ]
         ),
-        .target(
-            name: "SQLiteObjc",
-            dependencies: [],
-            exclude: [
-                "fts3_tokenizer.h"
-            ]
-        ),
         .testTarget(
             name: "SQLiteTests",
             dependencies: [
@@ -57,7 +49,8 @@ package.targets = [
     .testTarget(
         name: "SQLiteTests",
         dependencies: ["SQLite"],
-        path: "Tests/SQLiteTests", exclude: [
+        path: "Tests/SQLiteTests",
+        exclude: [
             "FTSIntegrationTests.swift",
             "FTS4Tests.swift",
             "FTS5Tests.swift"
diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj
index 45ff075f..3e67c03e 100644
--- a/SQLite.xcodeproj/project.pbxproj
+++ b/SQLite.xcodeproj/project.pbxproj
@@ -17,8 +17,6 @@
 		03A65E741C6BB2DA0062603F /* Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AF81C3F06E900AE3E12 /* Helpers.swift */; };
 		03A65E761C6BB2E60062603F /* Blob.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AEE1C3F06E900AE3E12 /* Blob.swift */; };
 		03A65E771C6BB2E60062603F /* Connection.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AEF1C3F06E900AE3E12 /* Connection.swift */; };
-		03A65E781C6BB2EA0062603F /* fts3_tokenizer.h in Headers */ = {isa = PBXBuildFile; fileRef = EE247AF01C3F06E900AE3E12 /* fts3_tokenizer.h */; };
-		03A65E791C6BB2EF0062603F /* SQLiteObjc.m in Sources */ = {isa = PBXBuildFile; fileRef = EE247AF11C3F06E900AE3E12 /* SQLiteObjc.m */; };
 		03A65E7A1C6BB2F70062603F /* Statement.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AF21C3F06E900AE3E12 /* Statement.swift */; };
 		03A65E7B1C6BB2F70062603F /* Value.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AF31C3F06E900AE3E12 /* Value.swift */; };
 		03A65E7C1C6BB2F70062603F /* FTS4.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AF51C3F06E900AE3E12 /* FTS4.swift */; };
@@ -116,13 +114,7 @@
 		3D67B3F61DB246D100A4F4C6 /* Setter.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B021C3F06E900AE3E12 /* Setter.swift */; };
 		3D67B3F71DB246D700A4F4C6 /* Foundation.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AF71C3F06E900AE3E12 /* Foundation.swift */; };
 		3D67B3F81DB246D700A4F4C6 /* Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AF81C3F06E900AE3E12 /* Helpers.swift */; };
-		3D67B3F91DB246E700A4F4C6 /* SQLiteObjc.m in Sources */ = {isa = PBXBuildFile; fileRef = EE247AF11C3F06E900AE3E12 /* SQLiteObjc.m */; };
 		3D67B3FC1DB2471B00A4F4C6 /* SQLite.h in Headers */ = {isa = PBXBuildFile; fileRef = EE247AD61C3F04ED00AE3E12 /* SQLite.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		3D67B3FD1DB2472D00A4F4C6 /* fts3_tokenizer.h in Headers */ = {isa = PBXBuildFile; fileRef = EE247AF01C3F06E900AE3E12 /* fts3_tokenizer.h */; };
-		3DDC112F26CDBA0200CE369F /* SQLiteObjc.h in Headers */ = {isa = PBXBuildFile; fileRef = 3DDC112E26CDBA0200CE369F /* SQLiteObjc.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		3DDC113626CDBE1900CE369F /* SQLiteObjc.h in Headers */ = {isa = PBXBuildFile; fileRef = 3DDC112E26CDBA0200CE369F /* SQLiteObjc.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		3DDC113726CDBE1900CE369F /* SQLiteObjc.h in Headers */ = {isa = PBXBuildFile; fileRef = 3DDC112E26CDBA0200CE369F /* SQLiteObjc.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		3DDC113826CDBE1C00CE369F /* SQLiteObjc.h in Headers */ = {isa = PBXBuildFile; fileRef = 3DDC112E26CDBA0200CE369F /* SQLiteObjc.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		3DF7B78828842972005DD8CA /* Connection+Attach.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DF7B78728842972005DD8CA /* Connection+Attach.swift */; };
 		3DF7B78928842972005DD8CA /* Connection+Attach.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DF7B78728842972005DD8CA /* Connection+Attach.swift */; };
 		3DF7B78A28842972005DD8CA /* Connection+Attach.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DF7B78728842972005DD8CA /* Connection+Attach.swift */; };
@@ -152,8 +144,6 @@
 		EE247ADE1C3F04ED00AE3E12 /* SQLite.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EE247AD31C3F04ED00AE3E12 /* SQLite.framework */; };
 		EE247B031C3F06E900AE3E12 /* Blob.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AEE1C3F06E900AE3E12 /* Blob.swift */; };
 		EE247B041C3F06E900AE3E12 /* Connection.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AEF1C3F06E900AE3E12 /* Connection.swift */; };
-		EE247B051C3F06E900AE3E12 /* fts3_tokenizer.h in Headers */ = {isa = PBXBuildFile; fileRef = EE247AF01C3F06E900AE3E12 /* fts3_tokenizer.h */; };
-		EE247B061C3F06E900AE3E12 /* SQLiteObjc.m in Sources */ = {isa = PBXBuildFile; fileRef = EE247AF11C3F06E900AE3E12 /* SQLiteObjc.m */; };
 		EE247B071C3F06E900AE3E12 /* Statement.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AF21C3F06E900AE3E12 /* Statement.swift */; };
 		EE247B081C3F06E900AE3E12 /* Value.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AF31C3F06E900AE3E12 /* Value.swift */; };
 		EE247B091C3F06E900AE3E12 /* FTS4.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AF51C3F06E900AE3E12 /* FTS4.swift */; };
@@ -205,8 +195,6 @@
 		EE247B641C3F3FDB00AE3E12 /* Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AF81C3F06E900AE3E12 /* Helpers.swift */; };
 		EE247B651C3F3FEC00AE3E12 /* Blob.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AEE1C3F06E900AE3E12 /* Blob.swift */; };
 		EE247B661C3F3FEC00AE3E12 /* Connection.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AEF1C3F06E900AE3E12 /* Connection.swift */; };
-		EE247B671C3F3FEC00AE3E12 /* fts3_tokenizer.h in Headers */ = {isa = PBXBuildFile; fileRef = EE247AF01C3F06E900AE3E12 /* fts3_tokenizer.h */; };
-		EE247B681C3F3FEC00AE3E12 /* SQLiteObjc.m in Sources */ = {isa = PBXBuildFile; fileRef = EE247AF11C3F06E900AE3E12 /* SQLiteObjc.m */; };
 		EE247B691C3F3FEC00AE3E12 /* Statement.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AF21C3F06E900AE3E12 /* Statement.swift */; };
 		EE247B6A1C3F3FEC00AE3E12 /* Value.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AF31C3F06E900AE3E12 /* Value.swift */; };
 		EE247B6B1C3F3FEC00AE3E12 /* FTS4.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AF51C3F06E900AE3E12 /* FTS4.swift */; };
@@ -272,13 +260,13 @@
 		3717F907221F5D7C00B9BD3D /* CustomAggregationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomAggregationTests.swift; sourceTree = "<group>"; };
 		3D3C3CCB26E5568800759140 /* SQLite.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = SQLite.playground; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
 		3D67B3E51DB2469200A4F4C6 /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = Platforms/WatchOS.platform/Developer/SDKs/WatchOS3.0.sdk/usr/lib/libsqlite3.tbd; sourceTree = DEVELOPER_DIR; };
-		3DDC112E26CDBA0200CE369F /* SQLiteObjc.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SQLiteObjc.h; path = ../SQLiteObjc/include/SQLiteObjc.h; sourceTree = "<group>"; };
 		3DF7B78728842972005DD8CA /* Connection+Attach.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Connection+Attach.swift"; sourceTree = "<group>"; };
 		3DF7B78C28842C23005DD8CA /* ResultTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResultTests.swift; sourceTree = "<group>"; };
 		3DF7B790288449BA005DD8CA /* URIQueryParameter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URIQueryParameter.swift; sourceTree = "<group>"; };
 		3DF7B79528846FCC005DD8CA /* Resources */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Resources; sourceTree = "<group>"; };
 		3DF7B79A2884C353005DD8CA /* CHANGELOG.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = CHANGELOG.md; sourceTree = "<group>"; };
 		3DF7B79B2884C901005DD8CA /* Planning.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = Planning.md; sourceTree = "<group>"; };
+		3DFC0B862886C239001C8FC9 /* Package.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Package.swift; sourceTree = "<group>"; };
 		49EB68C31F7B3CB400D89D40 /* Coding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Coding.swift; sourceTree = "<group>"; };
 		997DF2AD287FC06D00F8DF95 /* Query+with.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Query+with.swift"; sourceTree = "<group>"; };
 		A121AC451CA35C79005A31D1 /* SQLite.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SQLite.framework; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -290,8 +278,6 @@
 		EE247AE41C3F04ED00AE3E12 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
 		EE247AEE1C3F06E900AE3E12 /* Blob.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Blob.swift; sourceTree = "<group>"; };
 		EE247AEF1C3F06E900AE3E12 /* Connection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Connection.swift; sourceTree = "<group>"; };
-		EE247AF01C3F06E900AE3E12 /* fts3_tokenizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = fts3_tokenizer.h; path = ../../SQLiteObjc/fts3_tokenizer.h; sourceTree = "<group>"; };
-		EE247AF11C3F06E900AE3E12 /* SQLiteObjc.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SQLiteObjc.m; path = ../../SQLiteObjc/SQLiteObjc.m; sourceTree = "<group>"; };
 		EE247AF21C3F06E900AE3E12 /* Statement.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Statement.swift; sourceTree = "<group>"; };
 		EE247AF31C3F06E900AE3E12 /* Value.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Value.swift; sourceTree = "<group>"; };
 		EE247AF51C3F06E900AE3E12 /* FTS4.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FTS4.swift; sourceTree = "<group>"; };
@@ -406,9 +392,10 @@
 		EE247AC91C3F04ED00AE3E12 = {
 			isa = PBXGroup;
 			children = (
+				3DFC0B862886C239001C8FC9 /* Package.swift */,
+				3D3C3CCB26E5568800759140 /* SQLite.playground */,
 				EE247AD51C3F04ED00AE3E12 /* SQLite */,
 				EE247AE11C3F04ED00AE3E12 /* SQLiteTests */,
-				3D3C3CCB26E5568800759140 /* SQLite.playground */,
 				EE247B8A1C3F81D000AE3E12 /* Metadata */,
 				EE247AD41C3F04ED00AE3E12 /* Products */,
 				3D67B3E41DB2469200A4F4C6 /* Frameworks */,
@@ -435,7 +422,6 @@
 			isa = PBXGroup;
 			children = (
 				EE247AD61C3F04ED00AE3E12 /* SQLite.h */,
-				3DDC112E26CDBA0200CE369F /* SQLiteObjc.h */,
 				EE247AF71C3F06E900AE3E12 /* Foundation.swift */,
 				EE247AF81C3F06E900AE3E12 /* Helpers.swift */,
 				EE247AD81C3F04ED00AE3E12 /* Info.plist */,
@@ -488,8 +474,6 @@
 			children = (
 				EE247AEE1C3F06E900AE3E12 /* Blob.swift */,
 				EE247AEF1C3F06E900AE3E12 /* Connection.swift */,
-				EE247AF01C3F06E900AE3E12 /* fts3_tokenizer.h */,
-				EE247AF11C3F06E900AE3E12 /* SQLiteObjc.m */,
 				EE247AF21C3F06E900AE3E12 /* Statement.swift */,
 				EE247AF31C3F06E900AE3E12 /* Value.swift */,
 				19A1710E73A46D5AC721CDA9 /* Errors.swift */,
@@ -505,10 +489,10 @@
 		EE247AF41C3F06E900AE3E12 /* Extensions */ = {
 			isa = PBXGroup;
 			children = (
+				19A178A39ACA9667A62663CC /* Cipher.swift */,
 				EE247AF51C3F06E900AE3E12 /* FTS4.swift */,
-				EE247AF61C3F06E900AE3E12 /* RTree.swift */,
 				19A1730E4390C775C25677D1 /* FTS5.swift */,
-				19A178A39ACA9667A62663CC /* Cipher.swift */,
+				EE247AF61C3F06E900AE3E12 /* RTree.swift */,
 			);
 			path = Extensions;
 			sourceTree = "<group>";
@@ -576,8 +560,6 @@
 			isa = PBXHeadersBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				03A65E781C6BB2EA0062603F /* fts3_tokenizer.h in Headers */,
-				3DDC113626CDBE1900CE369F /* SQLiteObjc.h in Headers */,
 				03A65E721C6BB2D30062603F /* SQLite.h in Headers */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
@@ -587,8 +569,6 @@
 			buildActionMask = 2147483647;
 			files = (
 				3D67B3FC1DB2471B00A4F4C6 /* SQLite.h in Headers */,
-				3DDC113726CDBE1900CE369F /* SQLiteObjc.h in Headers */,
-				3D67B3FD1DB2472D00A4F4C6 /* fts3_tokenizer.h in Headers */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -596,8 +576,6 @@
 			isa = PBXHeadersBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				EE247B051C3F06E900AE3E12 /* fts3_tokenizer.h in Headers */,
-				3DDC113826CDBE1C00CE369F /* SQLiteObjc.h in Headers */,
 				EE247AD71C3F04ED00AE3E12 /* SQLite.h in Headers */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
@@ -606,8 +584,6 @@
 			isa = PBXHeadersBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				EE247B671C3F3FEC00AE3E12 /* fts3_tokenizer.h in Headers */,
-				3DDC112F26CDBA0200CE369F /* SQLiteObjc.h in Headers */,
 				EE247B621C3F3FDB00AE3E12 /* SQLite.h in Headers */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
@@ -869,7 +845,6 @@
 				03A65E761C6BB2E60062603F /* Blob.swift in Sources */,
 				03A65E7D1C6BB2F70062603F /* RTree.swift in Sources */,
 				3DF7B793288449BA005DD8CA /* URIQueryParameter.swift in Sources */,
-				03A65E791C6BB2EF0062603F /* SQLiteObjc.m in Sources */,
 				03A65E7B1C6BB2F70062603F /* Value.swift in Sources */,
 				03A65E821C6BB2FB0062603F /* Expression.swift in Sources */,
 				03A65E731C6BB2D80062603F /* Foundation.swift in Sources */,
@@ -933,7 +908,6 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				3D67B3F91DB246E700A4F4C6 /* SQLiteObjc.m in Sources */,
 				3DF7B78B28842972005DD8CA /* Connection+Attach.swift in Sources */,
 				49EB68C71F7B3CB400D89D40 /* Coding.swift in Sources */,
 				997DF2B1287FC06D00F8DF95 /* Query+with.swift in Sources */,
@@ -986,7 +960,6 @@
 				EE247B121C3F06E900AE3E12 /* Operators.swift in Sources */,
 				EE247B141C3F06E900AE3E12 /* Schema.swift in Sources */,
 				EE247B131C3F06E900AE3E12 /* Query.swift in Sources */,
-				EE247B061C3F06E900AE3E12 /* SQLiteObjc.m in Sources */,
 				EE247B071C3F06E900AE3E12 /* Statement.swift in Sources */,
 				EE247B0D1C3F06E900AE3E12 /* AggregateFunctions.swift in Sources */,
 				19A1717B10CC941ACB5533D6 /* FTS5.swift in Sources */,
@@ -1042,7 +1015,6 @@
 				EE247B651C3F3FEC00AE3E12 /* Blob.swift in Sources */,
 				EE247B6C1C3F3FEC00AE3E12 /* RTree.swift in Sources */,
 				3DF7B792288449BA005DD8CA /* URIQueryParameter.swift in Sources */,
-				EE247B681C3F3FEC00AE3E12 /* SQLiteObjc.m in Sources */,
 				EE247B6A1C3F3FEC00AE3E12 /* Value.swift in Sources */,
 				EE247B711C3F3FEC00AE3E12 /* Expression.swift in Sources */,
 				EE247B631C3F3FDB00AE3E12 /* Foundation.swift in Sources */,
diff --git a/Sources/SQLite/Extensions/FTS4.swift b/Sources/SQLite/Extensions/FTS4.swift
index 52fec955..e9e65746 100644
--- a/Sources/SQLite/Extensions/FTS4.swift
+++ b/Sources/SQLite/Extensions/FTS4.swift
@@ -22,10 +22,6 @@
 // THE SOFTWARE.
 //
 
-#if SWIFT_PACKAGE
-import SQLiteObjc
-#endif
-
 extension Module {
 
     public static func FTS4(_ column: Expressible, _ more: Expressible...) -> Module {
@@ -149,29 +145,6 @@ extension Tokenizer: CustomStringConvertible {
 
 }
 
-extension Connection {
-
-    public func registerTokenizer(_ submoduleName: String, next: @escaping (String) -> (String, Range<String.Index>)?) throws {
-        try check(_SQLiteRegisterTokenizer(handle, Tokenizer.moduleName, submoduleName) {
-            (input: UnsafePointer<Int8>, offset: UnsafeMutablePointer<Int32>, length: UnsafeMutablePointer<Int32>) in
-            let string = String(cString: input)
-
-            guard let (token, range) = next(string) else { return nil }
-
-            let view: String.UTF8View = string.utf8
-
-            if let from = range.lowerBound.samePosition(in: view),
-               let to = range.upperBound.samePosition(in: view) {
-                offset.pointee += Int32(string[string.startIndex..<range.lowerBound].utf8.count)
-                length.pointee = Int32(view.distance(from: from, to: to))
-                return token
-            } else {
-                return nil
-            }
-        })
-    }
-}
-
 /// Configuration options shared between the [FTS4](https://www.sqlite.org/fts3.html) and
 /// [FTS5](https://www.sqlite.org/fts5.html) extensions.
 open class FTSConfig {
diff --git a/Sources/SQLite/SQLite.h b/Sources/SQLite/SQLite.h
index fe1e3dfe..21ed899f 100644
--- a/Sources/SQLite/SQLite.h
+++ b/Sources/SQLite/SQLite.h
@@ -2,5 +2,3 @@
 
 FOUNDATION_EXPORT double SQLiteVersionNumber;
 FOUNDATION_EXPORT const unsigned char SQLiteVersionString[];
-
-#import "SQLiteObjc.h"
diff --git a/Sources/SQLiteObjc/SQLiteObjc.m b/Sources/SQLiteObjc/SQLiteObjc.m
deleted file mode 100644
index 9f2be0bd..00000000
--- a/Sources/SQLiteObjc/SQLiteObjc.m
+++ /dev/null
@@ -1,138 +0,0 @@
-//
-// SQLite.swift
-// https://github.com/stephencelis/SQLite.swift
-// Copyright © 2014-2015 Stephen Celis.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-//
-
-#import "SQLiteObjc.h"
-#import "fts3_tokenizer.h"
-
-#pragma mark - FTS
-
-typedef struct __SQLiteTokenizer {
-    sqlite3_tokenizer base;
-    __unsafe_unretained _SQLiteTokenizerNextCallback callback;
-} __SQLiteTokenizer;
-
-typedef struct __SQLiteTokenizerCursor {
-    void * base;
-    const char * input;
-    int inputOffset;
-    int inputLength;
-    int idx;
-} __SQLiteTokenizerCursor;
-
-static NSMutableDictionary * __SQLiteTokenizerMap;
-
-static int __SQLiteTokenizerCreate(int argc, const char * const * argv, sqlite3_tokenizer ** ppTokenizer) {
-    __SQLiteTokenizer * tokenizer = (__SQLiteTokenizer *)sqlite3_malloc(sizeof(__SQLiteTokenizer));
-    if (!tokenizer) {
-        return SQLITE_NOMEM;
-    }
-    memset(tokenizer, 0, sizeof(* tokenizer));
-
-    NSString * key = [NSString stringWithUTF8String:argv[0]];
-    tokenizer->callback = [__SQLiteTokenizerMap objectForKey:key];
-    if (!tokenizer->callback) {
-        return SQLITE_ERROR;
-    }
-
-    *ppTokenizer = &tokenizer->base;
-    return SQLITE_OK;
-}
-
-static int __SQLiteTokenizerDestroy(sqlite3_tokenizer * pTokenizer) {
-    sqlite3_free(pTokenizer);
-    return SQLITE_OK;
-}
-
-static int __SQLiteTokenizerOpen(sqlite3_tokenizer * pTokenizer, const char * pInput, int nBytes, sqlite3_tokenizer_cursor ** ppCursor) {
-    __SQLiteTokenizerCursor * cursor = (__SQLiteTokenizerCursor *)sqlite3_malloc(sizeof(__SQLiteTokenizerCursor));
-    if (!cursor) {
-        return SQLITE_NOMEM;
-    }
-
-    cursor->input = pInput;
-    cursor->inputOffset = 0;
-    cursor->inputLength = 0;
-    cursor->idx = 0;
-
-    *ppCursor = (sqlite3_tokenizer_cursor *)cursor;
-    return SQLITE_OK;
-}
-
-static int __SQLiteTokenizerClose(sqlite3_tokenizer_cursor * pCursor) {
-    sqlite3_free(pCursor);
-    return SQLITE_OK;
-}
-
-static int __SQLiteTokenizerNext(sqlite3_tokenizer_cursor * pCursor, const char ** ppToken, int * pnBytes, int * piStartOffset, int * piEndOffset, int * piPosition) {
-    __SQLiteTokenizerCursor * cursor = (__SQLiteTokenizerCursor *)pCursor;
-    __SQLiteTokenizer * tokenizer = (__SQLiteTokenizer *)cursor->base;
-
-    cursor->inputOffset += cursor->inputLength;
-    const char * input = cursor->input + cursor->inputOffset;
-    const char * token = [tokenizer->callback(input, &cursor->inputOffset, &cursor->inputLength) cStringUsingEncoding:NSUTF8StringEncoding];
-    if (!token) {
-        return SQLITE_DONE;
-    }
-
-    *ppToken = token;
-    *pnBytes = (int)strlen(token);
-    *piStartOffset = cursor->inputOffset;
-    *piEndOffset = cursor->inputOffset + cursor->inputLength;
-    *piPosition = cursor->idx++;
-    return SQLITE_OK;
-}
-
-static const sqlite3_tokenizer_module __SQLiteTokenizerModule = {
-    0,
-    __SQLiteTokenizerCreate,
-    __SQLiteTokenizerDestroy,
-    __SQLiteTokenizerOpen,
-    __SQLiteTokenizerClose,
-    __SQLiteTokenizerNext
-};
-
-int _SQLiteRegisterTokenizer(sqlite3 *db, const char * moduleName, const char * submoduleName, _SQLiteTokenizerNextCallback callback) {
-    static dispatch_once_t onceToken;
-    dispatch_once(&onceToken, ^{
-        __SQLiteTokenizerMap = [NSMutableDictionary new];
-    });
-
-    sqlite3_stmt * stmt;
-    int status = sqlite3_prepare_v2(db, "SELECT fts3_tokenizer(?, ?)", -1, &stmt, 0);
-    if (status != SQLITE_OK ){
-        return status;
-    }
-    const sqlite3_tokenizer_module * pModule = &__SQLiteTokenizerModule;
-    sqlite3_bind_text(stmt, 1, moduleName, -1, SQLITE_STATIC);
-    sqlite3_bind_blob(stmt, 2, &pModule, sizeof(pModule), SQLITE_STATIC);
-    sqlite3_step(stmt);
-    status = sqlite3_finalize(stmt);
-    if (status != SQLITE_OK ){
-        return status;
-    }
-
-    [__SQLiteTokenizerMap setObject:[callback copy] forKey:[NSString stringWithUTF8String:submoduleName]];
-
-    return SQLITE_OK;
-}
diff --git a/Sources/SQLiteObjc/fts3_tokenizer.h b/Sources/SQLiteObjc/fts3_tokenizer.h
deleted file mode 100644
index d8a1e44b..00000000
--- a/Sources/SQLiteObjc/fts3_tokenizer.h
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
-** 2006 July 10
-**
-** The author disclaims copyright to this source code.
-**
-*************************************************************************
-** Defines the interface to tokenizers used by fulltext-search.  There
-** are three basic components:
-**
-** sqlite3_tokenizer_module is a singleton defining the tokenizer
-** interface functions.  This is essentially the class structure for
-** tokenizers.
-**
-** sqlite3_tokenizer is used to define a particular tokenizer, perhaps
-** including customization information defined at creation time.
-**
-** sqlite3_tokenizer_cursor is generated by a tokenizer to generate
-** tokens from a particular input.
-*/
-#ifndef _FTS3_TOKENIZER_H_
-#define _FTS3_TOKENIZER_H_
-
-/* TODO(shess) Only used for SQLITE_OK and SQLITE_DONE at this time.
-** If tokenizers are to be allowed to call sqlite3_*() functions, then
-** we will need a way to register the API consistently.
-*/
-#import "sqlite3.h"
-
-/*
-** Structures used by the tokenizer interface. When a new tokenizer
-** implementation is registered, the caller provides a pointer to
-** an sqlite3_tokenizer_module containing pointers to the callback
-** functions that make up an implementation.
-**
-** When an fts3 table is created, it passes any arguments passed to
-** the tokenizer clause of the CREATE VIRTUAL TABLE statement to the
-** sqlite3_tokenizer_module.xCreate() function of the requested tokenizer
-** implementation. The xCreate() function in turn returns an 
-** sqlite3_tokenizer structure representing the specific tokenizer to
-** be used for the fts3 table (customized by the tokenizer clause arguments).
-**
-** To tokenize an input buffer, the sqlite3_tokenizer_module.xOpen()
-** method is called. It returns an sqlite3_tokenizer_cursor object
-** that may be used to tokenize a specific input buffer based on
-** the tokenization rules supplied by a specific sqlite3_tokenizer
-** object.
-*/
-typedef struct sqlite3_tokenizer_module sqlite3_tokenizer_module;
-typedef struct sqlite3_tokenizer sqlite3_tokenizer;
-typedef struct sqlite3_tokenizer_cursor sqlite3_tokenizer_cursor;
-
-struct sqlite3_tokenizer_module {
-
-  /*
-  ** Structure version. Should always be set to 0 or 1.
-  */
-  int iVersion;
-
-  /*
-  ** Create a new tokenizer. The values in the argv[] array are the
-  ** arguments passed to the "tokenizer" clause of the CREATE VIRTUAL
-  ** TABLE statement that created the fts3 table. For example, if
-  ** the following SQL is executed:
-  **
-  **   CREATE .. USING fts3( ... , tokenizer <tokenizer-name> arg1 arg2)
-  **
-  ** then argc is set to 2, and the argv[] array contains pointers
-  ** to the strings "arg1" and "arg2".
-  **
-  ** This method should return either SQLITE_OK (0), or an SQLite error 
-  ** code. If SQLITE_OK is returned, then *ppTokenizer should be set
-  ** to point at the newly created tokenizer structure. The generic
-  ** sqlite3_tokenizer.pModule variable should not be initialized by
-  ** this callback. The caller will do so.
-  */
-  int (*xCreate)(
-    int argc,                           /* Size of argv array */
-    const char *const*argv,             /* Tokenizer argument strings */
-    sqlite3_tokenizer **ppTokenizer     /* OUT: Created tokenizer */
-  );
-
-  /*
-  ** Destroy an existing tokenizer. The fts3 module calls this method
-  ** exactly once for each successful call to xCreate().
-  */
-  int (*xDestroy)(sqlite3_tokenizer *pTokenizer);
-
-  /*
-  ** Create a tokenizer cursor to tokenize an input buffer. The caller
-  ** is responsible for ensuring that the input buffer remains valid
-  ** until the cursor is closed (using the xClose() method). 
-  */
-  int (*xOpen)(
-    sqlite3_tokenizer *pTokenizer,       /* Tokenizer object */
-    const char *pInput, int nBytes,      /* Input buffer */
-    sqlite3_tokenizer_cursor **ppCursor  /* OUT: Created tokenizer cursor */
-  );
-
-  /*
-  ** Destroy an existing tokenizer cursor. The fts3 module calls this 
-  ** method exactly once for each successful call to xOpen().
-  */
-  int (*xClose)(sqlite3_tokenizer_cursor *pCursor);
-
-  /*
-  ** Retrieve the next token from the tokenizer cursor pCursor. This
-  ** method should either return SQLITE_OK and set the values of the
-  ** "OUT" variables identified below, or SQLITE_DONE to indicate that
-  ** the end of the buffer has been reached, or an SQLite error code.
-  **
-  ** *ppToken should be set to point at a buffer containing the 
-  ** normalized version of the token (i.e. after any case-folding and/or
-  ** stemming has been performed). *pnBytes should be set to the length
-  ** of this buffer in bytes. The input text that generated the token is
-  ** identified by the byte offsets returned in *piStartOffset and
-  ** *piEndOffset. *piStartOffset should be set to the index of the first
-  ** byte of the token in the input buffer. *piEndOffset should be set
-  ** to the index of the first byte just past the end of the token in
-  ** the input buffer.
-  **
-  ** The buffer *ppToken is set to point at is managed by the tokenizer
-  ** implementation. It is only required to be valid until the next call
-  ** to xNext() or xClose(). 
-  */
-  /* TODO(shess) current implementation requires pInput to be
-  ** nul-terminated.  This should either be fixed, or pInput/nBytes
-  ** should be converted to zInput.
-  */
-  int (*xNext)(
-    sqlite3_tokenizer_cursor *pCursor,   /* Tokenizer cursor */
-    const char **ppToken, int *pnBytes,  /* OUT: Normalized text for token */
-    int *piStartOffset,  /* OUT: Byte offset of token in input buffer */
-    int *piEndOffset,    /* OUT: Byte offset of end of token in input buffer */
-    int *piPosition      /* OUT: Number of tokens returned before this one */
-  );
-
-  /***********************************************************************
-  ** Methods below this point are only available if iVersion>=1.
-  */
-
-  /* 
-  ** Configure the language id of a tokenizer cursor.
-  */
-  int (*xLanguageid)(sqlite3_tokenizer_cursor *pCsr, int iLangid);
-};
-
-struct sqlite3_tokenizer {
-  const sqlite3_tokenizer_module *pModule;  /* The module for this tokenizer */
-  /* Tokenizer implementations will typically add additional fields */
-};
-
-struct sqlite3_tokenizer_cursor {
-  sqlite3_tokenizer *pTokenizer;       /* Tokenizer for this cursor. */
-  /* Tokenizer implementations will typically add additional fields */
-};
-
-int fts3_global_term_cnt(int iTerm, int iCol);
-int fts3_term_cnt(int iTerm, int iCol);
-
-
-#endif /* _FTS3_TOKENIZER_H_ */
diff --git a/Sources/SQLiteObjc/include/SQLiteObjc.h b/Sources/SQLiteObjc/include/SQLiteObjc.h
deleted file mode 100644
index 610cdf10..00000000
--- a/Sources/SQLiteObjc/include/SQLiteObjc.h
+++ /dev/null
@@ -1,38 +0,0 @@
-//
-// SQLite.swift
-// https://github.com/stephencelis/SQLite.swift
-// Copyright © 2014-2015 Stephen Celis.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-//
-
-@import Foundation;
-#if defined(SQLITE_SWIFT_STANDALONE)
-@import sqlite3;
-#elif defined(SQLITE_SWIFT_SQLCIPHER)
-@import SQLCipher;
-#else
-@import SQLite3;
-#endif
-
-NS_ASSUME_NONNULL_BEGIN
-typedef NSString * _Nullable (^_SQLiteTokenizerNextCallback)(const char *input, int *inputOffset, int *inputLength);
-int _SQLiteRegisterTokenizer(sqlite3 *db, const char *module, const char *tokenizer, _Nullable _SQLiteTokenizerNextCallback callback);
-NS_ASSUME_NONNULL_END
-
diff --git a/Tests/SQLiteTests/FTS4Tests.swift b/Tests/SQLiteTests/FTS4Tests.swift
index aa6ac41f..5e595007 100644
--- a/Tests/SQLiteTests/FTS4Tests.swift
+++ b/Tests/SQLiteTests/FTS4Tests.swift
@@ -188,40 +188,3 @@ class FTS4ConfigTests: XCTestCase {
         virtualTable.create(.FTS4(config))
     }
 }
-
-class FTS4IntegrationTests: SQLiteTestCase {
-#if !SQLITE_SWIFT_STANDALONE && !SQLITE_SWIFT_SQLCIPHER
-    func test_registerTokenizer_registersTokenizer() throws {
-        let emails = VirtualTable("emails")
-        let subject = Expression<String?>("subject")
-        let body = Expression<String?>("body")
-
-        let locale = CFLocaleCopyCurrent()
-        let tokenizerName = "tokenizer"
-        let tokenizer = CFStringTokenizerCreate(nil, "" as CFString, CFRangeMake(0, 0),
-                                                UInt(kCFStringTokenizerUnitWord), locale)
-        try db.registerTokenizer(tokenizerName) { string in
-            CFStringTokenizerSetString(tokenizer, string as CFString,
-                                       CFRangeMake(0, CFStringGetLength(string as CFString)))
-            if CFStringTokenizerAdvanceToNextToken(tokenizer).isEmpty {
-                return nil
-            }
-            let range = CFStringTokenizerGetCurrentTokenRange(tokenizer)
-            let input = CFStringCreateWithSubstring(kCFAllocatorDefault, string as CFString, range)!
-            let token = CFStringCreateMutableCopy(nil, range.length, input)!
-            CFStringLowercase(token, locale)
-            CFStringTransform(token, nil, kCFStringTransformStripDiacritics, false)
-            return (token as String, string.range(of: input as String)!)
-        }
-
-        try db.run(emails.create(.FTS4([subject, body], tokenize: .Custom(tokenizerName))))
-        assertSQL("""
-                  CREATE VIRTUAL TABLE \"emails\" USING fts4(\"subject\", \"body\",
-                   tokenize=\"SQLite.swift\" \"tokenizer\")
-                  """.replacingOccurrences(of: "\n", with: ""))
-
-        try _ = db.run(emails.insert(subject <- "Aún más cáfe!"))
-        XCTAssertEqual(1, try db.scalar(emails.filter(emails.match("aun")).count))
-    }
-#endif
-}

From f63aca44e06e511dd0bdeeeb4b335b2aafbf36b1 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Tue, 19 Jul 2022 13:40:38 +0200
Subject: [PATCH 169/326] Fix podspec, update docs

---
 CHANGELOG.md           |  3 +++
 Documentation/Index.md |  2 --
 Package.swift          | 23 +++++------------------
 SQLite.swift.podspec   |  9 +++------
 4 files changed, 11 insertions(+), 26 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 68a5133f..71ce9858 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,7 @@
 * Fix incorrect spelling of `remove_diacritics` ([#1128][])
 * Fix project build order ([#1131][])
 * Performance improvements ([#1109][], [#1115][], [#1132][])
+* Removed FTS3/4 tokenizer integration (`registerTokenizer`, [#1104][], [#1144[]])
 
 0.13.3 (27-03-2022), [diff][diff-0.13.3]
 ========================================
@@ -166,6 +167,7 @@
 [#1098]: https://github.com/stephencelis/SQLite.swift/issues/1098
 [#1100]: https://github.com/stephencelis/SQLite.swift/pull/1100
 [#1101]: https://github.com/stephencelis/SQLite.swift/issues/1101
+[#1104]: https://github.com/stephencelis/SQLite.swift/issues/1104
 [#1105]: https://github.com/stephencelis/SQLite.swift/pull/1105
 [#1109]: https://github.com/stephencelis/SQLite.swift/issues/1109
 [#1110]: https://github.com/stephencelis/SQLite.swift/pull/1110
@@ -182,3 +184,4 @@
 [#1139]: https://github.com/stephencelis/SQLite.swift/pull/1139
 [#1141]: https://github.com/stephencelis/SQLite.swift/pull/1141
 [#1142]: https://github.com/stephencelis/SQLite.swift/pull/1142
+[#1144]: https://github.com/stephencelis/SQLite.swift/pull/1144
diff --git a/Documentation/Index.md b/Documentation/Index.md
index 96fc1ee1..b71cdc4f 100644
--- a/Documentation/Index.md
+++ b/Documentation/Index.md
@@ -1983,8 +1983,6 @@ try db.run(emails.create(.FTS5(config)))
 // the last FTS4 query above as:
 let replies = emails.filter(emails.match("subject:\"Re:\"*"))
 // SELECT * FROM "emails" WHERE "emails" MATCH 'subject:"Re:"*'
-
-// https://www.sqlite.org/fts5.html#_changes_to_select_statements_
 ```
 
 ## Executing Arbitrary SQL
diff --git a/Package.swift b/Package.swift
index ab9cbd43..9664e99a 100644
--- a/Package.swift
+++ b/Package.swift
@@ -39,23 +39,10 @@ let package = Package(
 )
 
 #if os(Linux)
-package.dependencies = [.package(url: "https://github.com/stephencelis/CSQLite.git", from: "0.0.3")]
-package.targets = [
-    .target(
-        name: "SQLite",
-        dependencies: [.product(name: "CSQLite", package: "CSQLite")],
-        exclude: ["Extensions/FTS4.swift", "Extensions/FTS5.swift"]
-    ),
-    .testTarget(
-        name: "SQLiteTests",
-        dependencies: ["SQLite"],
-        path: "Tests/SQLiteTests",
-        exclude: [
-            "FTSIntegrationTests.swift",
-            "FTS4Tests.swift",
-            "FTS5Tests.swift"
-        ],
-        resources: [ .copy("Resources") ]
-    )
+package.dependencies = [
+    .package(url: "https://github.com/stephencelis/CSQLite.git", from: "0.0.3")
+]
+package.targets.first?.dependencies += [
+    .product(name: "CSQLite", package: "CSQLite")
 ]
 #endif
diff --git a/SQLite.swift.podspec b/SQLite.swift.podspec
index 451b4886..d36d0330 100644
--- a/SQLite.swift.podspec
+++ b/SQLite.swift.podspec
@@ -29,9 +29,8 @@ Pod::Spec.new do |s|
   s.watchos.deployment_target = watchos_deployment_target
 
   s.subspec 'standard' do |ss|
-    ss.source_files = 'Sources/{SQLite,SQLiteObjc}/**/*.{c,h,m,swift}'
+    ss.source_files = 'Sources/SQLite/**/*.{c,h,m,swift}'
     ss.exclude_files = 'Sources/**/Cipher.swift'
-    ss.private_header_files = 'Sources/SQLiteObjc/fts3_tokenizer.h'
     ss.library = 'sqlite3'
 
     ss.test_spec 'tests' do |test_spec|
@@ -44,9 +43,8 @@ Pod::Spec.new do |s|
   end
 
   s.subspec 'standalone' do |ss|
-    ss.source_files = 'Sources/{SQLite,SQLiteObjc}/**/*.{c,h,m,swift}'
+    ss.source_files = 'Sources/SQLite/**/*.{c,h,m,swift}'
     ss.exclude_files = 'Sources/**/Cipher.swift'
-    ss.private_header_files = 'Sources/SQLiteObjc/fts3_tokenizer.h'
 
     ss.xcconfig = {
       'OTHER_SWIFT_FLAGS' => '$(inherited) -DSQLITE_SWIFT_STANDALONE',
@@ -64,8 +62,7 @@ Pod::Spec.new do |s|
   end
 
   s.subspec 'SQLCipher' do |ss|
-    ss.source_files = 'Sources/{SQLite,SQLiteObjc}/**/*.{c,h,m,swift}'
-    ss.private_header_files = 'Sources/SQLiteObjc/fts3_tokenizer.h'
+    ss.source_files = 'Sources/SQLite/**/*.{c,h,m,swift}'
     ss.xcconfig = {
       'OTHER_SWIFT_FLAGS' => '$(inherited) -DSQLITE_SWIFT_SQLCIPHER',
       'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) SQLITE_HAS_CODEC=1 SQLITE_SWIFT_SQLCIPHER=1'

From 8d9f74e919a75da41f19c47869a4577c10638a0c Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Tue, 19 Jul 2022 13:59:27 +0200
Subject: [PATCH 170/326] Works now

---
 CHANGELOG.md           | 2 +-
 Documentation/Linux.md | 3 +--
 2 files changed, 2 insertions(+), 3 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 71ce9858..7b3f78b4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,7 +10,7 @@
 * Fix incorrect spelling of `remove_diacritics` ([#1128][])
 * Fix project build order ([#1131][])
 * Performance improvements ([#1109][], [#1115][], [#1132][])
-* Removed FTS3/4 tokenizer integration (`registerTokenizer`, [#1104][], [#1144[]])
+* Removed FTS3/4 tokenizer integration (`registerTokenizer`, [#1104][], [#1144][])
 
 0.13.3 (27-03-2022), [diff][diff-0.13.3]
 ========================================
diff --git a/Documentation/Linux.md b/Documentation/Linux.md
index 640ced6f..0c88ff5c 100644
--- a/Documentation/Linux.md
+++ b/Documentation/Linux.md
@@ -2,9 +2,8 @@
 
 ## Limitations
 
-* Custom functions are currently not supported and crash, caused by a bug in Swift.
+* Custom functions/aggregations are currently not supported and crash, caused by a bug in Swift.
 See [#1071](https://github.com/stephencelis/SQLite.swift/issues/1071).
-* FTS5 might not work, see [#1007](https://github.com/stephencelis/SQLite.swift/issues/1007)
 
 ## Debugging
 

From ef4854f0f742f1f52416aab88db75c6b686385ec Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Tue, 19 Jul 2022 14:29:59 +0200
Subject: [PATCH 171/326] Update mileston

---
 Documentation/Planning.md        | 2 +-
 SQLite.xcodeproj/project.pbxproj | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/Documentation/Planning.md b/Documentation/Planning.md
index 6067b81e..cdfca9c4 100644
--- a/Documentation/Planning.md
+++ b/Documentation/Planning.md
@@ -6,7 +6,7 @@ additions and Pull Requests, as well as to keep the Issues list clear of
 enhancement requests so that bugs are more visible.
 
 > ⚠ This document is currently not actively maintained. See
-> the [0.13.4 milestone](https://github.com/stephencelis/SQLite.swift/issues?q=is%3Aopen+is%3Aissue+milestone%3A0.13.4)
+> the [0.14.1 milestone](https://github.com/stephencelis/SQLite.swift/issues?q=is%3Aopen+is%3Aissue+milestone%3A0.14.1)
 > on Github for additional information about planned features for the next release.
 
 ## Roadmap
diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj
index 3e67c03e..2656ed6a 100644
--- a/SQLite.xcodeproj/project.pbxproj
+++ b/SQLite.xcodeproj/project.pbxproj
@@ -392,7 +392,6 @@
 		EE247AC91C3F04ED00AE3E12 = {
 			isa = PBXGroup;
 			children = (
-				3DFC0B862886C239001C8FC9 /* Package.swift */,
 				3D3C3CCB26E5568800759140 /* SQLite.playground */,
 				EE247AD51C3F04ED00AE3E12 /* SQLite */,
 				EE247AE11C3F04ED00AE3E12 /* SQLiteTests */,
@@ -523,6 +522,7 @@
 				3DF7B79A2884C353005DD8CA /* CHANGELOG.md */,
 				EE247B8B1C3F820300AE3E12 /* CONTRIBUTING.md */,
 				EE247B931C3F826100AE3E12 /* SQLite.swift.podspec */,
+				3DFC0B862886C239001C8FC9 /* Package.swift */,
 				EE247B8D1C3F821200AE3E12 /* Makefile */,
 				EE9180931C46EA210038162A /* libsqlite3.tbd */,
 				EE9180911C46E9D30038162A /* libsqlite3.tbd */,

From 4442ad0bbd06d2e843a5bb280cc96070aafec2c8 Mon Sep 17 00:00:00 2001
From: Adam Wulf <adam.wulf@gmail.com>
Date: Thu, 21 Jul 2022 15:01:04 -0500
Subject: [PATCH 172/326] Adding public method to reset a prepared statement.
 This is needed so that clients can reset any prepared statements they keep in
 memory, which allows for transactions to completely finish. without closing
 transactions (regardless of commit), the wal file cannot be checkpointed and
 will grow unbounded.

---
 Sources/SQLite/Core/Statement.swift    |  6 ++-
 Tests/SQLiteTests/StatementTests.swift | 57 ++++++++++++++++++++++++++
 2 files changed, 62 insertions(+), 1 deletion(-)

diff --git a/Sources/SQLite/Core/Statement.swift b/Sources/SQLite/Core/Statement.swift
index 5ec430cf..7ba4026c 100644
--- a/Sources/SQLite/Core/Statement.swift
+++ b/Sources/SQLite/Core/Statement.swift
@@ -185,7 +185,11 @@ public final class Statement {
         try connection.sync { try connection.check(sqlite3_step(handle)) == SQLITE_ROW }
     }
 
-    fileprivate func reset(clearBindings shouldClear: Bool = true) {
+    public func reset() {
+        reset(clearBindings: true)
+    }
+
+    fileprivate func reset(clearBindings shouldClear: Bool) {
         sqlite3_reset(handle)
         if shouldClear { sqlite3_clear_bindings(handle) }
     }
diff --git a/Tests/SQLiteTests/StatementTests.swift b/Tests/SQLiteTests/StatementTests.swift
index 3286b792..77bc09c1 100644
--- a/Tests/SQLiteTests/StatementTests.swift
+++ b/Tests/SQLiteTests/StatementTests.swift
@@ -34,4 +34,61 @@ class StatementTests: SQLiteTestCase {
 
         XCTAssertEqual(names.map({ "\($0)@example.com" }), emails.sorted())
     }
+
+    /// Check that a statement reset will close the implicit transaction, allowing wal file to checkpoint
+    func test_reset_statement() throws {
+        // Remove old test db if any
+        let path = "\(NSTemporaryDirectory())/SQLite.swift Tests.sqlite3"
+        try? FileManager.default.removeItem(atPath: path)
+        try? FileManager.default.removeItem(atPath: path + "-shm")
+        try? FileManager.default.removeItem(atPath: path + "-wal")
+
+        // create new db on disk in wal mode
+        let db = try Connection(.uri(path))
+        let url = URL(fileURLWithPath: db.description)
+        XCTAssertEqual(url.lastPathComponent, "SQLite.swift Tests.sqlite3")
+        try db.run("PRAGMA journal_mode=WAL;")
+
+        // create users table
+        try db.execute("""
+            CREATE TABLE users (
+                id INTEGER PRIMARY KEY,
+                email TEXT NOT NULL UNIQUE,
+                age INTEGER,
+                salary REAL,
+                admin BOOLEAN NOT NULL DEFAULT 0 CHECK (admin IN (0, 1)),
+                manager_id INTEGER,
+                created_at DATETIME,
+                FOREIGN KEY(manager_id) REFERENCES users(id)
+            )
+            """
+        )
+
+        // insert single row
+        try db.run("INSERT INTO \"users\" (email, age, admin) values (?, ?, ?)",
+                   "alice@example.com", 1.datatypeValue, false.datatypeValue)
+
+        // prepare a statement and read a single row. This will incremeent the cursor which
+        // prevents the implicit transaction from closing.
+        // https://www.sqlite.org/lang_transaction.html#implicit_versus_explicit_transactions
+        let statement = try db.prepare("SELECT email FROM users")
+        XCTAssert(try statement.step())
+        let blob = statement.row[0] as Blob
+        XCTAssertEqual("alice@example.com", String(bytes: blob.bytes, encoding: .utf8)!)
+
+        // verify that the transaction is not closed, which prevents wal_checkpoints (both explicit and auto)
+        do {
+            try db.run("pragma wal_checkpoint(truncate)")
+            XCTFail("Database should be locked")
+        } catch {
+            // pass
+        }
+
+        // reset the prepared statement, allowing the implicit transaction to close
+        statement.reset()
+
+        // truncate succeeds
+        try db.run("pragma wal_checkpoint(truncate)")
+    }
+
 }

From dd3e813577b8b6ba363b12cff024dd633e9e29ab Mon Sep 17 00:00:00 2001
From: Nathan Fallet <contact@nathanfallet.me>
Date: Fri, 22 Jul 2022 12:37:48 +0200
Subject: [PATCH 173/326] Create FUNDING.yml

---
 .github/FUNDING.yml | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 .github/FUNDING.yml

diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 00000000..119ccc65
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1 @@
+github: [stephencelis, jberkel, NathanFallet]

From ac1988c9663e8c5f64d21b255d91c154a30e2f35 Mon Sep 17 00:00:00 2001
From: Adam Wulf <adam.wulf@gmail.com>
Date: Fri, 22 Jul 2022 14:12:11 -0500
Subject: [PATCH 174/326] Test cleanup for statement reset

---
 Tests/SQLiteTests/StatementTests.swift | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/Tests/SQLiteTests/StatementTests.swift b/Tests/SQLiteTests/StatementTests.swift
index 77bc09c1..c12fe8cb 100644
--- a/Tests/SQLiteTests/StatementTests.swift
+++ b/Tests/SQLiteTests/StatementTests.swift
@@ -38,15 +38,13 @@ class StatementTests: SQLiteTestCase {
     /// Check that a statement reset will close the implicit transaction, allowing wal file to checkpoint
     func test_reset_statement() throws {
         // Remove old test db if any
-        let path = "\(NSTemporaryDirectory())/SQLite.swift Tests.sqlite3"
+        let path = temporaryFile() + ".sqlite3"
         try? FileManager.default.removeItem(atPath: path)
         try? FileManager.default.removeItem(atPath: path + "-shm")
         try? FileManager.default.removeItem(atPath: path + "-wal")
 
         // create new db on disk in wal mode
         let db = try Connection(.uri(path))
-        let url = URL(fileURLWithPath: db.description)
-        XCTAssertEqual(url.lastPathComponent, "SQLite.swift Tests.sqlite3")
         try db.run("PRAGMA journal_mode=WAL;")
 
         // create users table

From 012194b271dc32a0a9d1182edb4bea5201d8d59d Mon Sep 17 00:00:00 2001
From: Adam Wulf <adam.wulf@gmail.com>
Date: Fri, 22 Jul 2022 15:43:14 -0500
Subject: [PATCH 175/326] Adding documentation for Statement.reset().

---
 Documentation/Index.md | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/Documentation/Index.md b/Documentation/Index.md
index b71cdc4f..ed79f33e 100644
--- a/Documentation/Index.md
+++ b/Documentation/Index.md
@@ -1872,6 +1872,14 @@ let stmt = try db.prepare("SELECT * FROM attachments WHERE typeConformsTo(UTI, ?
 for row in stmt.bind(kUTTypeImage) { /* ... */ }
 ```
 
+> _Note:_ Prepared queries can be reused, and long lived prepared queries should be `reset()` after each use. Otherwise, the transaction (either implicit or explicit) might be held open until the query is reset or finalized. This can affect performance. Statements are reset automatically during `deinit`.
+> 
+> ```swift
+> someObj.statement = try db.prepare("SELECT * FROM attachments WHERE typeConformsTo(UTI, ?)")
+> for row in someObj.statement.bind(kUTTypeImage) { /* ... */ }
+> someObj.statement.reset()
+> ```
+
 [UTTypeConformsTo]: https://developer.apple.com/documentation/coreservices/1444079-uttypeconformsto
 
 ## Custom Aggregations

From 58fd9dc1b6db7544135af10ced07f720e2dc36ac Mon Sep 17 00:00:00 2001
From: Adam Wulf <adam.wulf@gmail.com>
Date: Fri, 22 Jul 2022 15:43:42 -0500
Subject: [PATCH 176/326] Fix close of markdown code block

---
 Documentation/Index.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Documentation/Index.md b/Documentation/Index.md
index ed79f33e..2a877a71 100644
--- a/Documentation/Index.md
+++ b/Documentation/Index.md
@@ -828,7 +828,7 @@ let query = try db.prepare(users)
 for user in query {
     // 💥 can throw an error here
 }
-````
+```
 
 #### Failable iteration
 

From 7b3de3d24b3eaea797a3db1b5a6373196edd4561 Mon Sep 17 00:00:00 2001
From: Adam Wulf <adam.wulf@gmail.com>
Date: Fri, 22 Jul 2022 15:48:15 -0500
Subject: [PATCH 177/326] Add link to relevant sqlite.org documentation page

---
 Documentation/Index.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Documentation/Index.md b/Documentation/Index.md
index 2a877a71..29b18c96 100644
--- a/Documentation/Index.md
+++ b/Documentation/Index.md
@@ -1872,7 +1872,7 @@ let stmt = try db.prepare("SELECT * FROM attachments WHERE typeConformsTo(UTI, ?
 for row in stmt.bind(kUTTypeImage) { /* ... */ }
 ```
 
-> _Note:_ Prepared queries can be reused, and long lived prepared queries should be `reset()` after each use. Otherwise, the transaction (either implicit or explicit) might be held open until the query is reset or finalized. This can affect performance. Statements are reset automatically during `deinit`.
+> _Note:_ Prepared queries can be reused, and long lived prepared queries should be `reset()` after each use. Otherwise, the transaction (either [implicit or explicit](https://www.sqlite.org/lang_transaction.html#implicit_versus_explicit_transactions)) will be held open until the query is reset or finalized. This can affect performance. Statements are reset automatically during `deinit`.
 > 
 > ```swift
 > someObj.statement = try db.prepare("SELECT * FROM attachments WHERE typeConformsTo(UTI, ?)")

From 5a30f299c41f335a0745b0eab773db4f3ee184af Mon Sep 17 00:00:00 2001
From: Adam Wulf <adam.wulf@gmail.com>
Date: Fri, 22 Jul 2022 17:45:52 -0500
Subject: [PATCH 178/326] cleanup test for Statement.reset()

---
 Tests/SQLiteTests/StatementTests.swift | 11 ++---------
 1 file changed, 2 insertions(+), 9 deletions(-)

diff --git a/Tests/SQLiteTests/StatementTests.swift b/Tests/SQLiteTests/StatementTests.swift
index c12fe8cb..b3048211 100644
--- a/Tests/SQLiteTests/StatementTests.swift
+++ b/Tests/SQLiteTests/StatementTests.swift
@@ -37,13 +37,8 @@ class StatementTests: SQLiteTestCase {
 
     /// Check that a statement reset will close the implicit transaction, allowing wal file to checkpoint
     func test_reset_statement() throws {
-        // Remove old test db if any
-        let path = temporaryFile() + ".sqlite3"
-        try? FileManager.default.removeItem(atPath: path)
-        try? FileManager.default.removeItem(atPath: path + "-shm")
-        try? FileManager.default.removeItem(atPath: path + "-wal")
-
         // create new db on disk in wal mode
+        let path = temporaryFile() + ".sqlite3"
         let db = try Connection(.uri(path))
         try db.run("PRAGMA journal_mode=WAL;")
 
@@ -70,9 +65,7 @@ class StatementTests: SQLiteTestCase {
         // prevents the implicit transaction from closing.
         // https://www.sqlite.org/lang_transaction.html#implicit_versus_explicit_transactions
         let statement = try db.prepare("SELECT email FROM users")
-        XCTAssert(try statement.step())
-        let blob = statement.row[0] as Blob
-        XCTAssertEqual("alice@example.com", String(bytes: blob.bytes, encoding: .utf8)!)
+        _ = try statement.step()
 
         // verify that the transaction is not closed, which prevents wal_checkpoints (both explicit and auto)
         do {

From efa5fcb5c24352d266e9a87075dbfb3e1c5fd7ea Mon Sep 17 00:00:00 2001
From: Adam Wulf <adam.wulf@gmail.com>
Date: Fri, 22 Jul 2022 17:47:46 -0500
Subject: [PATCH 179/326] Better check for throwing error in test

---
 Tests/SQLiteTests/StatementTests.swift | 7 +------
 1 file changed, 1 insertion(+), 6 deletions(-)

diff --git a/Tests/SQLiteTests/StatementTests.swift b/Tests/SQLiteTests/StatementTests.swift
index b3048211..e41fa714 100644
--- a/Tests/SQLiteTests/StatementTests.swift
+++ b/Tests/SQLiteTests/StatementTests.swift
@@ -68,12 +68,7 @@ class StatementTests: SQLiteTestCase {
         _ = try statement.step()
 
         // verify that the transaction is not closed, which prevents wal_checkpoints (both explicit and auto)
-        do {
-            try db.run("pragma wal_checkpoint(truncate)")
-            XCTFail("Database should be locked")
-        } catch {
-            // pass
-        }
+        XCTAssertThrowsError(try db.run("pragma wal_checkpoint(truncate)"))
 
         // reset the prepared statement, allowing the implicit transaction to close
         statement.reset()

From da0cd09219fe02d22c721637bc55d0ce600821ff Mon Sep 17 00:00:00 2001
From: Adam Wulf <adam.wulf@gmail.com>
Date: Fri, 22 Jul 2022 19:25:52 -0500
Subject: [PATCH 180/326] Remove unneeded wal mode

---
 Tests/SQLiteTests/StatementTests.swift | 1 -
 1 file changed, 1 deletion(-)

diff --git a/Tests/SQLiteTests/StatementTests.swift b/Tests/SQLiteTests/StatementTests.swift
index e41fa714..3cc68aca 100644
--- a/Tests/SQLiteTests/StatementTests.swift
+++ b/Tests/SQLiteTests/StatementTests.swift
@@ -40,7 +40,6 @@ class StatementTests: SQLiteTestCase {
         // create new db on disk in wal mode
         let path = temporaryFile() + ".sqlite3"
         let db = try Connection(.uri(path))
-        try db.run("PRAGMA journal_mode=WAL;")
 
         // create users table
         try db.execute("""

From 210303f7b21f829bf82e17f41a8f912742ea2be3 Mon Sep 17 00:00:00 2001
From: Adam Wulf <adam.wulf@gmail.com>
Date: Sat, 23 Jul 2022 15:19:10 -0500
Subject: [PATCH 181/326] Use in memory database from test setup, verify error
 when statement isn't reset, update comments

---
 Tests/SQLiteTests/StatementTests.swift | 46 ++++++++++++--------------
 1 file changed, 21 insertions(+), 25 deletions(-)

diff --git a/Tests/SQLiteTests/StatementTests.swift b/Tests/SQLiteTests/StatementTests.swift
index 3cc68aca..bc100cd2 100644
--- a/Tests/SQLiteTests/StatementTests.swift
+++ b/Tests/SQLiteTests/StatementTests.swift
@@ -1,6 +1,16 @@
 import XCTest
 import SQLite
 
+#if SQLITE_SWIFT_STANDALONE
+import sqlite3
+#elseif SQLITE_SWIFT_SQLCIPHER
+import SQLCipher
+#elseif os(Linux)
+import CSQLite
+#else
+import SQLite3
+#endif
+
 class StatementTests: SQLiteTestCase {
     override func setUpWithError() throws {
         try super.setUpWithError()
@@ -37,28 +47,8 @@ class StatementTests: SQLiteTestCase {
 
     /// Check that a statement reset will close the implicit transaction, allowing wal file to checkpoint
     func test_reset_statement() throws {
-        // create new db on disk in wal mode
-        let path = temporaryFile() + ".sqlite3"
-        let db = try Connection(.uri(path))
-
-        // create users table
-        try db.execute("""
-            CREATE TABLE users (
-                id INTEGER PRIMARY KEY,
-                email TEXT NOT NULL UNIQUE,
-                age INTEGER,
-                salary REAL,
-                admin BOOLEAN NOT NULL DEFAULT 0 CHECK (admin IN (0, 1)),
-                manager_id INTEGER,
-                created_at DATETIME,
-                FOREIGN KEY(manager_id) REFERENCES users(id)
-            )
-            """
-        )
-
         // insert single row
-        try db.run("INSERT INTO \"users\" (email, age, admin) values (?, ?, ?)",
-                   "alice@example.com", 1.datatypeValue, false.datatypeValue)
+        try insertUsers("bob")
 
         // prepare a statement and read a single row. This will incremeent the cursor which
         // prevents the implicit transaction from closing.
@@ -66,14 +56,20 @@ class StatementTests: SQLiteTestCase {
         let statement = try db.prepare("SELECT email FROM users")
         _ = try statement.step()
 
-        // verify that the transaction is not closed, which prevents wal_checkpoints (both explicit and auto)
-        XCTAssertThrowsError(try db.run("pragma wal_checkpoint(truncate)"))
+        // verify implicit transaction is not closed, and the users table is still locked
+        XCTAssertThrowsError(try db.run("DROP TABLE users")) { error in
+            if case let Result.error(_, code, _) = error {
+                XCTAssertEqual(code, SQLITE_LOCKED)
+            } else {
+                XCTFail("unexpected error")
+            }
+        }
 
-        // reset the prepared statement, allowing the implicit transaction to close
+        // reset the prepared statement, unlocking the table and allowing the implicit transaction to close
         statement.reset()
 
         // truncate succeeds
-        try db.run("pragma wal_checkpoint(truncate)")
+        try db.run("DROP TABLE users")
     }
 
 }

From d970a11bd66747fd3b67d8ce072d944fca03b341 Mon Sep 17 00:00:00 2001
From: Adam Wulf <adam.wulf@gmail.com>
Date: Sat, 23 Jul 2022 15:21:03 -0500
Subject: [PATCH 182/326] typo

---
 Tests/SQLiteTests/StatementTests.swift | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Tests/SQLiteTests/StatementTests.swift b/Tests/SQLiteTests/StatementTests.swift
index bc100cd2..eeb513fe 100644
--- a/Tests/SQLiteTests/StatementTests.swift
+++ b/Tests/SQLiteTests/StatementTests.swift
@@ -50,7 +50,7 @@ class StatementTests: SQLiteTestCase {
         // insert single row
         try insertUsers("bob")
 
-        // prepare a statement and read a single row. This will incremeent the cursor which
+        // prepare a statement and read a single row. This will increment the cursor which
         // prevents the implicit transaction from closing.
         // https://www.sqlite.org/lang_transaction.html#implicit_versus_explicit_transactions
         let statement = try db.prepare("SELECT email FROM users")

From 5c66460cb97375d9cdc68364afe8e6db434f6cb9 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Sat, 23 Jul 2022 22:21:59 +0200
Subject: [PATCH 183/326] Adds SchemaChanger to perform database schema changes

---
 SQLite.xcodeproj/project.pbxproj              |  70 ++++
 Sources/SQLite/Schema/Connection+Schema.swift | 145 ++++++++
 Sources/SQLite/Schema/SchemaChanger.swift     | 231 ++++++++++++
 Sources/SQLite/Schema/SchemaDefinitions.swift | 336 +++++++++++++++++
 .../Schema/ConnectionSchemaTests.swift        | 139 +++++++
 .../Schema/SchemaChangerTests.swift           |  87 +++++
 .../Schema/SchemaDefinitionsTests.swift       | 342 ++++++++++++++++++
 7 files changed, 1350 insertions(+)
 create mode 100644 Sources/SQLite/Schema/Connection+Schema.swift
 create mode 100644 Sources/SQLite/Schema/SchemaChanger.swift
 create mode 100644 Sources/SQLite/Schema/SchemaDefinitions.swift
 create mode 100644 Tests/SQLiteTests/Schema/ConnectionSchemaTests.swift
 create mode 100644 Tests/SQLiteTests/Schema/SchemaChangerTests.swift
 create mode 100644 Tests/SQLiteTests/Schema/SchemaDefinitionsTests.swift

diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj
index 2656ed6a..abd69301 100644
--- a/SQLite.xcodeproj/project.pbxproj
+++ b/SQLite.xcodeproj/project.pbxproj
@@ -51,24 +51,33 @@
 		19A170ACC97B19730FB7BA4D /* Connection+Aggregation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A175A9CB446640AE6F2200 /* Connection+Aggregation.swift */; };
 		19A17152E32A9585831E3FE0 /* DateAndTimeFunctions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17BA55DABB480F9020C8A /* DateAndTimeFunctions.swift */; };
 		19A1717B10CC941ACB5533D6 /* FTS5.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1730E4390C775C25677D1 /* FTS5.swift */; };
+		19A17188B4D96636F9C0C209 /* ConnectionSchemaTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17CA1DF7D0F7C9A94C51C /* ConnectionSchemaTests.swift */; };
 		19A171967CC511C4F6F773C9 /* RowTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A175C1F9CB3BBAB8FCEC7B /* RowTests.swift */; };
 		19A171BE056457F13BFBC4C3 /* FTSIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A171FDE4D67879B14ACBDF /* FTSIntegrationTests.swift */; };
 		19A171E6FA242F72A308C594 /* FTS5Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1721B8984686B9963B45D /* FTS5Tests.swift */; };
 		19A171F12AB8B07F2FD7201A /* Cipher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A178A39ACA9667A62663CC /* Cipher.swift */; };
 		19A1720B67ED13E6150C6A3D /* RowTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A175C1F9CB3BBAB8FCEC7B /* RowTests.swift */; };
 		19A17254FBA7894891F7297B /* FTS5Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1721B8984686B9963B45D /* FTS5Tests.swift */; };
+		19A1725658E480B9B378F28B /* ConnectionSchemaTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17CA1DF7D0F7C9A94C51C /* ConnectionSchemaTests.swift */; };
 		19A172A9B536D02D4A98AAAD /* QueryIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17BA6B4E282C1315A115C /* QueryIntegrationTests.swift */; };
 		19A172EB202970561E5C4245 /* DateAndTimeFunctionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1729B75C33F9A0B9A89C1 /* DateAndTimeFunctionTests.swift */; };
 		19A173668D948AD4DF1F5352 /* DateAndTimeFunctions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17BA55DABB480F9020C8A /* DateAndTimeFunctions.swift */; };
 		19A1737286A74F3CF7412906 /* DateAndTimeFunctions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17BA55DABB480F9020C8A /* DateAndTimeFunctions.swift */; };
 		19A173EFEF0B3BD0B3ED406C /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17E723300E5ED3771DCB5 /* Result.swift */; };
 		19A17408007B182F884E3A53 /* Fixtures.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17B93B48B5560E6E51791 /* Fixtures.swift */; };
+		19A1740EACD47904AA24B8DC /* SchemaDefinitions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17268AE67B746B96AC125 /* SchemaDefinitions.swift */; };
+		19A174506543905D71BF0518 /* Connection+Schema.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A170A97B51DC5EE365F3C5 /* Connection+Schema.swift */; };
 		19A17490543609FCED53CACC /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1710E73A46D5AC721CDA9 /* Errors.swift */; };
 		19A174D78559CD30679BCCCB /* FTS5Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1721B8984686B9963B45D /* FTS5Tests.swift */; };
 		19A1750CEE9B05267995CF3D /* FTS5.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1730E4390C775C25677D1 /* FTS5.swift */; };
+		19A1750EF4A5F92954A451FF /* Connection+Schema.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A170A97B51DC5EE365F3C5 /* Connection+Schema.swift */; };
 		19A176376CB6A94759F7980A /* Connection+Aggregation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A175A9CB446640AE6F2200 /* Connection+Aggregation.swift */; };
 		19A176406BDE9D9C80CC9FA3 /* QueryIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17BA6B4E282C1315A115C /* QueryIntegrationTests.swift */; };
+		19A1766AC10D13C4EFF349AD /* SchemaChangerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17FDB0B0CFB8987906FD0 /* SchemaChangerTests.swift */; };
 		19A1769C1F3A7542BECF50FF /* DateAndTimeFunctionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1729B75C33F9A0B9A89C1 /* DateAndTimeFunctionTests.swift */; };
+		19A177290558991BCC60E4E3 /* SchemaChanger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A171B262DDE8718513CFDA /* SchemaChanger.swift */; };
+		19A1773A335CAB9D0AE14E8E /* SchemaChanger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A171B262DDE8718513CFDA /* SchemaChanger.swift */; };
+		19A177C25834473FAB32CF3B /* SchemaChangerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17FDB0B0CFB8987906FD0 /* SchemaChangerTests.swift */; };
 		19A177CC33F2E6A24AF90B02 /* CipherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17399EA9E61235D5D77BF /* CipherTests.swift */; };
 		19A178072B371489E6A1E839 /* FoundationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1794CC4D7827E997E32A7 /* FoundationTests.swift */; };
 		19A17835FD5886FDC5A3228F /* Cipher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A178A39ACA9667A62663CC /* Cipher.swift */; };
@@ -78,19 +87,31 @@
 		19A179B59450FE7C4811AB8A /* Connection+Aggregation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A175A9CB446640AE6F2200 /* Connection+Aggregation.swift */; };
 		19A179CCF9671E345E5A9811 /* Cipher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A178A39ACA9667A62663CC /* Cipher.swift */; };
 		19A179E76EA6207669B60C1B /* Cipher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A178A39ACA9667A62663CC /* Cipher.swift */; };
+		19A17A33EA026C2E2CEBAF36 /* Connection+Schema.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A170A97B51DC5EE365F3C5 /* Connection+Schema.swift */; };
+		19A17B0DF1DDB6BBC9C95D64 /* SchemaDefinitions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17268AE67B746B96AC125 /* SchemaDefinitions.swift */; };
+		19A17B36ABC6006AB80F693C /* ConnectionSchemaTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17CA1DF7D0F7C9A94C51C /* ConnectionSchemaTests.swift */; };
+		19A17B62A4125AF4F6014CF5 /* SchemaDefinitionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A176174862D0F0139B3987 /* SchemaDefinitionsTests.swift */; };
+		19A17BA13FD35F058787B7D3 /* SchemaDefinitions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17268AE67B746B96AC125 /* SchemaDefinitions.swift */; };
 		19A17C013B00682B70D53DB8 /* FTSIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A171FDE4D67879B14ACBDF /* FTSIntegrationTests.swift */; };
 		19A17C4B951CB054EE48AB1C /* CipherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17399EA9E61235D5D77BF /* CipherTests.swift */; };
 		19A17C80076860CF7751A056 /* DateAndTimeFunctionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1729B75C33F9A0B9A89C1 /* DateAndTimeFunctionTests.swift */; };
 		19A17C9407AC0EE104E5CC85 /* FTSIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A171FDE4D67879B14ACBDF /* FTSIntegrationTests.swift */; };
 		19A17CB808ACF606726E77A8 /* QueryIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17BA6B4E282C1315A115C /* QueryIntegrationTests.swift */; };
+		19A17D1BEABA610ABF003D67 /* SchemaDefinitions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17268AE67B746B96AC125 /* SchemaDefinitions.swift */; };
 		19A17DC282E36C4F41AA440B /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1710E73A46D5AC721CDA9 /* Errors.swift */; };
 		19A17DF8D4F13A20F5D2269E /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17E723300E5ED3771DCB5 /* Result.swift */; };
+		19A17DFE05ED8B1F7C45F7EE /* SchemaChanger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A171B262DDE8718513CFDA /* SchemaChanger.swift */; };
 		19A17E04C4C0956715C5676A /* FoundationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1794CC4D7827E997E32A7 /* FoundationTests.swift */; };
 		19A17E29278A12BC4F542506 /* DateAndTimeFunctions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17BA55DABB480F9020C8A /* DateAndTimeFunctions.swift */; };
+		19A17E80F736EEE8EE2AA4CE /* SchemaDefinitionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A176174862D0F0139B3987 /* SchemaDefinitionsTests.swift */; };
 		19A17EC0D68BA8C03288ADF7 /* FTS5.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1730E4390C775C25677D1 /* FTS5.swift */; };
+		19A17F0BF02896E1664F4090 /* Connection+Schema.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A170A97B51DC5EE365F3C5 /* Connection+Schema.swift */; };
 		19A17F1B3F0A3C96B5ED6D64 /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17E723300E5ED3771DCB5 /* Result.swift */; };
 		19A17F60B685636D1F83C2DD /* Fixtures.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17B93B48B5560E6E51791 /* Fixtures.swift */; };
 		19A17FB80B94E882050AA908 /* FoundationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1794CC4D7827E997E32A7 /* FoundationTests.swift */; };
+		19A17FC04708C6ED637DDFD4 /* SchemaChangerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17FDB0B0CFB8987906FD0 /* SchemaChangerTests.swift */; };
+		19A17FC07731779C1B8506FA /* SchemaChanger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A171B262DDE8718513CFDA /* SchemaChanger.swift */; };
+		19A17FE78A39E86F330420EC /* SchemaDefinitionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A176174862D0F0139B3987 /* SchemaDefinitionsTests.swift */; };
 		19A17FF4A10B44D3937C8CAC /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1710E73A46D5AC721CDA9 /* Errors.swift */; };
 		3717F908221F5D8800B9BD3D /* CustomAggregationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3717F907221F5D7C00B9BD3D /* CustomAggregationTests.swift */; };
 		3717F909221F5D8900B9BD3D /* CustomAggregationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3717F907221F5D7C00B9BD3D /* CustomAggregationTests.swift */; };
@@ -241,22 +262,28 @@
 		03A65E5A1C6BB0F50062603F /* SQLite.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SQLite.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		03A65E631C6BB0F60062603F /* SQLiteTests tvOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "SQLiteTests tvOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
 		03A65E961C6BB3210062603F /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS.sdk/usr/lib/libsqlite3.tbd; sourceTree = DEVELOPER_DIR; };
+		19A170A97B51DC5EE365F3C5 /* Connection+Schema.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Connection+Schema.swift"; sourceTree = "<group>"; };
 		19A1710E73A46D5AC721CDA9 /* Errors.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Errors.swift; sourceTree = "<group>"; };
+		19A171B262DDE8718513CFDA /* SchemaChanger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SchemaChanger.swift; sourceTree = "<group>"; };
 		19A171FDE4D67879B14ACBDF /* FTSIntegrationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FTSIntegrationTests.swift; sourceTree = "<group>"; };
 		19A1721B8984686B9963B45D /* FTS5Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FTS5Tests.swift; sourceTree = "<group>"; };
+		19A17268AE67B746B96AC125 /* SchemaDefinitions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SchemaDefinitions.swift; sourceTree = "<group>"; };
 		19A1729B75C33F9A0B9A89C1 /* DateAndTimeFunctionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DateAndTimeFunctionTests.swift; sourceTree = "<group>"; };
 		19A1730E4390C775C25677D1 /* FTS5.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FTS5.swift; sourceTree = "<group>"; };
 		19A17399EA9E61235D5D77BF /* CipherTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CipherTests.swift; sourceTree = "<group>"; };
 		19A175A9CB446640AE6F2200 /* Connection+Aggregation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Connection+Aggregation.swift"; sourceTree = "<group>"; };
 		19A175C1F9CB3BBAB8FCEC7B /* RowTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RowTests.swift; sourceTree = "<group>"; };
+		19A176174862D0F0139B3987 /* SchemaDefinitionsTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SchemaDefinitionsTests.swift; sourceTree = "<group>"; };
 		19A178A39ACA9667A62663CC /* Cipher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Cipher.swift; sourceTree = "<group>"; };
 		19A1794B7972D14330A65BBD /* Linux.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = Linux.md; sourceTree = "<group>"; };
 		19A1794CC4D7827E997E32A7 /* FoundationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FoundationTests.swift; sourceTree = "<group>"; };
 		19A17B93B48B5560E6E51791 /* Fixtures.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Fixtures.swift; sourceTree = "<group>"; };
 		19A17BA55DABB480F9020C8A /* DateAndTimeFunctions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DateAndTimeFunctions.swift; sourceTree = "<group>"; };
 		19A17BA6B4E282C1315A115C /* QueryIntegrationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QueryIntegrationTests.swift; sourceTree = "<group>"; };
+		19A17CA1DF7D0F7C9A94C51C /* ConnectionSchemaTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConnectionSchemaTests.swift; sourceTree = "<group>"; };
 		19A17E723300E5ED3771DCB5 /* Result.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Result.swift; sourceTree = "<group>"; };
 		19A17EA3A313F129011B3FA0 /* Release.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = Release.md; sourceTree = "<group>"; };
+		19A17FDB0B0CFB8987906FD0 /* SchemaChangerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SchemaChangerTests.swift; sourceTree = "<group>"; };
 		3717F907221F5D7C00B9BD3D /* CustomAggregationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomAggregationTests.swift; sourceTree = "<group>"; };
 		3D3C3CCB26E5568800759140 /* SQLite.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = SQLite.playground; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
 		3D67B3E51DB2469200A4F4C6 /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = Platforms/WatchOS.platform/Developer/SDKs/WatchOS3.0.sdk/usr/lib/libsqlite3.tbd; sourceTree = DEVELOPER_DIR; };
@@ -381,6 +408,26 @@
 /* End PBXFrameworksBuildPhase section */
 
 /* Begin PBXGroup section */
+		19A1792D261C689FC988A90A /* Schema */ = {
+			isa = PBXGroup;
+			children = (
+				19A171B262DDE8718513CFDA /* SchemaChanger.swift */,
+				19A17268AE67B746B96AC125 /* SchemaDefinitions.swift */,
+				19A170A97B51DC5EE365F3C5 /* Connection+Schema.swift */,
+			);
+			path = Schema;
+			sourceTree = "<group>";
+		};
+		19A17B56FBA20E7245BC8AC0 /* Schema */ = {
+			isa = PBXGroup;
+			children = (
+				19A176174862D0F0139B3987 /* SchemaDefinitionsTests.swift */,
+				19A17FDB0B0CFB8987906FD0 /* SchemaChangerTests.swift */,
+				19A17CA1DF7D0F7C9A94C51C /* ConnectionSchemaTests.swift */,
+			);
+			path = Schema;
+			sourceTree = "<group>";
+		};
 		3D67B3E41DB2469200A4F4C6 /* Frameworks */ = {
 			isa = PBXGroup;
 			children = (
@@ -427,6 +474,7 @@
 				EE247AED1C3F06E900AE3E12 /* Core */,
 				EE247AF41C3F06E900AE3E12 /* Extensions */,
 				EE247AF91C3F06E900AE3E12 /* Typed */,
+				19A1792D261C689FC988A90A /* Schema */,
 			);
 			name = SQLite;
 			path = Sources/SQLite;
@@ -463,6 +511,7 @@
 				19A17BA6B4E282C1315A115C /* QueryIntegrationTests.swift */,
 				19A171FDE4D67879B14ACBDF /* FTSIntegrationTests.swift */,
 				3DF7B78C28842C23005DD8CA /* ResultTests.swift */,
+				19A17B56FBA20E7245BC8AC0 /* Schema */,
 			);
 			name = SQLiteTests;
 			path = Tests/SQLiteTests;
@@ -868,6 +917,9 @@
 				19A17073552293CA063BEA66 /* Result.swift in Sources */,
 				997DF2B0287FC06D00F8DF95 /* Query+with.swift in Sources */,
 				19A179B59450FE7C4811AB8A /* Connection+Aggregation.swift in Sources */,
+				19A17FC07731779C1B8506FA /* SchemaChanger.swift in Sources */,
+				19A1740EACD47904AA24B8DC /* SchemaDefinitions.swift in Sources */,
+				19A1750EF4A5F92954A451FF /* Connection+Schema.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -901,6 +953,9 @@
 				D4DB368E20C09CFD00D5A58E /* SelectTests.swift in Sources */,
 				19A17CB808ACF606726E77A8 /* QueryIntegrationTests.swift in Sources */,
 				19A17C9407AC0EE104E5CC85 /* FTSIntegrationTests.swift in Sources */,
+				19A17B62A4125AF4F6014CF5 /* SchemaDefinitionsTests.swift in Sources */,
+				19A17FC04708C6ED637DDFD4 /* SchemaChangerTests.swift in Sources */,
+				19A17188B4D96636F9C0C209 /* ConnectionSchemaTests.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -935,6 +990,9 @@
 				19A17DC282E36C4F41AA440B /* Errors.swift in Sources */,
 				19A173668D948AD4DF1F5352 /* DateAndTimeFunctions.swift in Sources */,
 				19A17DF8D4F13A20F5D2269E /* Result.swift in Sources */,
+				19A17DFE05ED8B1F7C45F7EE /* SchemaChanger.swift in Sources */,
+				19A17D1BEABA610ABF003D67 /* SchemaDefinitions.swift in Sources */,
+				19A17A33EA026C2E2CEBAF36 /* Connection+Schema.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -970,6 +1028,9 @@
 				19A173EFEF0B3BD0B3ED406C /* Result.swift in Sources */,
 				997DF2AE287FC06D00F8DF95 /* Query+with.swift in Sources */,
 				19A176376CB6A94759F7980A /* Connection+Aggregation.swift in Sources */,
+				19A1773A335CAB9D0AE14E8E /* SchemaChanger.swift in Sources */,
+				19A17BA13FD35F058787B7D3 /* SchemaDefinitions.swift in Sources */,
+				19A174506543905D71BF0518 /* Connection+Schema.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -1003,6 +1064,9 @@
 				D4DB368C20C09CFB00D5A58E /* SelectTests.swift in Sources */,
 				19A172A9B536D02D4A98AAAD /* QueryIntegrationTests.swift in Sources */,
 				19A17C013B00682B70D53DB8 /* FTSIntegrationTests.swift in Sources */,
+				19A17FE78A39E86F330420EC /* SchemaDefinitionsTests.swift in Sources */,
+				19A177C25834473FAB32CF3B /* SchemaChangerTests.swift in Sources */,
+				19A1725658E480B9B378F28B /* ConnectionSchemaTests.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -1038,6 +1102,9 @@
 				19A17F1B3F0A3C96B5ED6D64 /* Result.swift in Sources */,
 				997DF2AF287FC06D00F8DF95 /* Query+with.swift in Sources */,
 				19A170ACC97B19730FB7BA4D /* Connection+Aggregation.swift in Sources */,
+				19A177290558991BCC60E4E3 /* SchemaChanger.swift in Sources */,
+				19A17B0DF1DDB6BBC9C95D64 /* SchemaDefinitions.swift in Sources */,
+				19A17F0BF02896E1664F4090 /* Connection+Schema.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -1071,6 +1138,9 @@
 				D4DB368D20C09CFC00D5A58E /* SelectTests.swift in Sources */,
 				19A176406BDE9D9C80CC9FA3 /* QueryIntegrationTests.swift in Sources */,
 				19A171BE056457F13BFBC4C3 /* FTSIntegrationTests.swift in Sources */,
+				19A17E80F736EEE8EE2AA4CE /* SchemaDefinitionsTests.swift in Sources */,
+				19A1766AC10D13C4EFF349AD /* SchemaChangerTests.swift in Sources */,
+				19A17B36ABC6006AB80F693C /* ConnectionSchemaTests.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
diff --git a/Sources/SQLite/Schema/Connection+Schema.swift b/Sources/SQLite/Schema/Connection+Schema.swift
new file mode 100644
index 00000000..e368bdaf
--- /dev/null
+++ b/Sources/SQLite/Schema/Connection+Schema.swift
@@ -0,0 +1,145 @@
+import Foundation
+
+extension Connection {
+    // Changing the foreign_keys setting affects the execution of all statements prepared using the database
+    // connection, including those prepared before the setting was changed.
+    //
+    // https://sqlite.org/pragma.html#pragma_foreign_keys
+    var foreignKeys: Bool {
+        get { getBoolPragma("foreign_keys") }
+        set { setBoolPragma("foreign_keys", newValue) }
+    }
+
+    var deferForeignKeys: Bool {
+        get { getBoolPragma("defer_foreign_keys") }
+        set { setBoolPragma("defer_foreign_keys", newValue) }
+    }
+
+    // https://sqlite.org/pragma.html#pragma_foreign_key_check
+
+    // There are four columns in each result row.
+    // The first column is the name of the table that
+    // contains the REFERENCES clause.
+    // The second column is the rowid of the row that contains the
+    // invalid REFERENCES clause, or NULL if the child table is a WITHOUT ROWID table.
+    // The third column is the name of the table that is referred to.
+    // The fourth column is the index of the specific foreign key constraint that failed.
+    func foreignKeyCheck() throws -> [ForeignKeyError] {
+        try run("PRAGMA foreign_key_check").compactMap { row -> ForeignKeyError? in
+            guard let table = row[0] as? String,
+                  let rowId = row[1] as? Int64,
+                  let target = row[2] as? String else { return nil }
+
+            return ForeignKeyError(from: table, rowId: rowId, to: target)
+        }
+    }
+
+    // https://sqlite.org/pragma.html#pragma_table_info
+    //
+    // This pragma returns one row for each column in the named table. Columns in the result set include the
+    // column name, data type, whether or not the column can be NULL, and the default value for the column. The
+    // "pk" column in the result set is zero for columns that are not part of the primary key, and is the
+    // index of the column in the primary key for columns that are part of the primary key.
+    func columnInfo(table: String) throws  -> [ColumnDefinition] {
+        func parsePrimaryKey(column: String) throws -> ColumnDefinition.PrimaryKey? {
+            try createTableSQL(name: table).flatMap { .init(sql: $0) }
+        }
+
+        let foreignKeys: [String: [ForeignKeyDefinition]] =
+            Dictionary(grouping: try foreignKeyInfo(table: table), by: { $0.column })
+
+        return try run("PRAGMA table_info(\(table.quote()))").compactMap { row -> ColumnDefinition? in
+            guard let name = row[1] as? String,
+                  let type = row[2] as? String,
+                  let notNull = row[3] as? Int64,
+                  let defaultValue = row[4] as? String?,
+                  let primaryKey = row[5] as? Int64 else { return nil }
+            return ColumnDefinition(name: name,
+                                    primaryKey: primaryKey == 1 ? try parsePrimaryKey(column: name) : nil,
+                                    type: ColumnDefinition.Affinity.from(type),
+                                    null: notNull == 0,
+                                    defaultValue: LiteralValue.from(defaultValue),
+                                    references: foreignKeys[name]?.first)
+        }
+    }
+
+    func indexInfo(table: String) throws -> [IndexDefinition] {
+        func indexSQL(name: String) throws  -> String? {
+            try run("""
+                    SELECT sql FROM sqlite_master WHERE name=? AND type='index'
+                      UNION ALL
+                    SELECT sql FROM sqlite_temp_master WHERE name=? AND type='index'
+                    """, name, name)
+                    .compactMap { row in row[0] as? String }
+                    .first
+        }
+
+        func columns(name: String) throws -> [String] {
+            try run("PRAGMA index_info(\(name.quote()))").compactMap { row in
+                row[2] as? String
+            }
+        }
+
+        return try run("PRAGMA index_list(\(table.quote()))").compactMap { row -> IndexDefinition? in
+            guard let name = row[1] as? String,
+                  let unique = row[2] as? Int64,
+                    // Indexes SQLite creates implicitly for internal use start with "sqlite_".
+                    // See https://www.sqlite.org/fileformat2.html#intschema
+                  !name.starts(with: "sqlite_") else {
+                return nil
+            }
+            return .init(table: table,
+                         name: name,
+                         unique: unique == 1,
+                         columns: try columns(name: name),
+                         indexSQL: try indexSQL(name: name))
+        }
+    }
+
+    func tableInfo() throws -> [String] {
+        try run("SELECT tbl_name FROM sqlite_master WHERE type = 'table'").compactMap { row in
+            if let name = row[0] as? String, !name.starts(with: "sqlite_") {
+                return name
+            } else {
+                return nil
+            }
+        }
+    }
+
+    func foreignKeyInfo(table: String) throws -> [ForeignKeyDefinition] {
+        try run("PRAGMA foreign_key_list(\(table.quote()))").compactMap { row in
+            if let table = row[2] as? String,      // table
+               let column = row[3] as? String,     // from
+               let primaryKey = row[4] as? String, // to
+               let onUpdate = row[5] as? String,
+               let onDelete = row[6] as? String {
+                return ForeignKeyDefinition(table: table, column: column, primaryKey: primaryKey,
+                                            onUpdate: onUpdate == TableBuilder.Dependency.noAction.rawValue ? nil : onUpdate,
+                                            onDelete: onDelete == TableBuilder.Dependency.noAction.rawValue ? nil : onDelete
+                )
+            } else {
+                return nil
+            }
+        }
+    }
+
+    private func createTableSQL(name: String) throws  -> String? {
+        try run("""
+                SELECT sql FROM sqlite_master WHERE name=? AND type='table'
+                  UNION ALL
+                SELECT sql FROM sqlite_temp_master WHERE name=? AND type='table'
+        """, name, name)
+            .compactMap { row in row[0] as? String }
+            .first
+    }
+
+    private func getBoolPragma(_ key: String) -> Bool {
+        guard let binding = try? scalar("PRAGMA \(key)"),
+              let intBinding = binding as? Int64 else { return false }
+        return intBinding == 1
+    }
+
+    private func setBoolPragma(_ key: String, _ newValue: Bool) {
+        _ = try? run("PRAGMA \(key) = \(newValue ? "1" : "0")")
+    }
+}
diff --git a/Sources/SQLite/Schema/SchemaChanger.swift b/Sources/SQLite/Schema/SchemaChanger.swift
new file mode 100644
index 00000000..ff0befc3
--- /dev/null
+++ b/Sources/SQLite/Schema/SchemaChanger.swift
@@ -0,0 +1,231 @@
+import Foundation
+
+/*
+    https://www.sqlite.org/lang_altertable.html
+
+   The only schema altering commands directly supported by SQLite are the "rename table" and "add column"
+   commands shown above.
+
+    (SQLite 3.25.0: RENAME COLUMN)
+    (SQLite 3.35.0: DROP COLUMN)
+
+   However, applications can make other arbitrary changes to the format of a table using a
+   simple sequence of operations. The steps to make arbitrary changes to the schema design of some table X are as follows:
+
+        1. If foreign key constraints are enabled, disable them using PRAGMA foreign_keys=OFF.
+        2. Start a transaction.
+        3. Remember the format of all indexes and triggers associated with table X
+           (SELECT sql FROM sqlite_master WHERE tbl_name='X' AND type='index')
+        4. Use CREATE TABLE to construct a new table "new_X" that is in the desired revised format of table X.
+        5. Transfer content from X into new_X using a statement like: INSERT INTO new_X SELECT ... FROM X.
+        6. Drop the old table X: DROP TABLE X.
+        7. Change the name of new_X to X using: ALTER TABLE new_X RENAME TO X.
+        8. Use CREATE INDEX and CREATE TRIGGER to reconstruct indexes and triggers associated with table X.
+        9. If any views refer to table X in a way that is affected by the schema change, then drop those views using DROP VIEW
+       10. If foreign key constraints were originally enabled then run PRAGMA foreign_key_check
+       11. Commit the transaction started in step 2.
+       12. If foreign keys constraints were originally enabled, reenable them now.
+*/
+public class SchemaChanger: CustomStringConvertible {
+    enum SchemaChangeError: LocalizedError {
+        case foreignKeyError([ForeignKeyError])
+
+        var errorDescription: String? {
+            switch self {
+            case .foreignKeyError(let errors):
+                return "Foreign key errors: \(errors)"
+            }
+        }
+    }
+
+    public enum Operation {
+        case none
+        case add(ColumnDefinition)
+        case remove(String)
+        case renameColumn(String, String)
+        case renameTable(String)
+
+        /// Returns non-nil if the operation can be executed with a simple SQL statement
+        func toSQL(_ table: String) -> String? {
+            switch self {
+            case .add(let definition): return "ALTER TABLE \(table.quote()) ADD COLUMN \(definition.toSQL())"
+            default: return nil
+            }
+        }
+    }
+
+    public class AlterTableDefinition {
+        fileprivate var operations: [Operation] = []
+
+        let name: String
+
+        init(name: String) {
+            self.name = name
+        }
+
+        public func add(_ column: ColumnDefinition) {
+            operations.append(.add(column))
+        }
+
+        public func remove(_ column: String) {
+            operations.append(.remove(column))
+        }
+
+        public func rename(_ column: String, to: String) {
+            operations.append(.renameColumn(column, to))
+        }
+    }
+
+    private let connection: Connection
+    static let tempPrefix = "tmp_"
+    typealias Block = () throws -> Void
+    public typealias AlterTableDefinitionBlock = (AlterTableDefinition) -> Void
+
+    struct Options: OptionSet {
+        let rawValue: Int
+        static let `default`: Options = []
+        static let temp = Options(rawValue: 1)
+    }
+
+    public init(connection: Connection) {
+        self.connection = connection
+    }
+
+    public func alter(table: String, block: AlterTableDefinitionBlock) throws {
+        let alterTableDefinition = AlterTableDefinition(name: table)
+        block(alterTableDefinition)
+
+        for operation in alterTableDefinition.operations {
+            try run(table: table, operation: operation)
+        }
+    }
+
+    public func drop(table: String) throws {
+        try dropTable(table)
+    }
+
+    private func run(table: String, operation: Operation) throws {
+        if let sql = operation.toSQL(table) {
+            try connection.run(sql)
+        } else {
+            try doTheTableDance(table: table, operation: operation)
+        }
+    }
+
+    private func doTheTableDance(table: String, operation: Operation) throws {
+        try connection.transaction {
+            try disableRefIntegrity {
+                let tempTable = "\(SchemaChanger.tempPrefix)\(table)"
+                try moveTable(from: table, to: tempTable, options: [.temp], operation: operation)
+                try moveTable(from: tempTable, to: table)
+                let foreignKeyErrors = try connection.foreignKeyCheck()
+                if foreignKeyErrors.count > 0 {
+                    throw SchemaChangeError.foreignKeyError(foreignKeyErrors)
+                }
+            }
+        }
+    }
+
+    private func disableRefIntegrity(block: Block) throws {
+        let oldForeignKeys = connection.foreignKeys
+        let oldDeferForeignKeys = connection.deferForeignKeys
+
+        connection.deferForeignKeys = true
+        connection.foreignKeys = false
+
+        defer {
+            connection.deferForeignKeys = oldDeferForeignKeys
+            connection.foreignKeys = oldForeignKeys
+        }
+
+        try block()
+    }
+
+    private func moveTable(from: String, to: String, options: Options = .default, operation: Operation = .none) throws {
+        try copyTable(from: from, to: to, options: options, operation: operation)
+        try dropTable(from)
+    }
+
+    private func copyTable(from: String, to: String, options: Options = .default, operation: Operation) throws {
+        let fromDefinition = TableDefinition(
+            name: from,
+            columns: try connection.columnInfo(table: from),
+            indexes: try connection.indexInfo(table: from)
+        )
+        let toDefinition   = fromDefinition.apply(.renameTable(to)).apply(operation)
+
+        try createTable(definition: toDefinition, options: options)
+        try createTableIndexes(definition: toDefinition)
+        if case .remove = operation {
+            try copyTableContents(from: fromDefinition.apply(operation), to: toDefinition)
+        } else {
+            try copyTableContents(from: fromDefinition, to: toDefinition)
+        }
+    }
+
+    private func createTable(definition: TableDefinition, options: Options) throws {
+        try connection.run(definition.toSQL(temporary: options.contains(.temp)))
+    }
+
+    private func createTableIndexes(definition: TableDefinition) throws {
+        for index in definition.indexes {
+            try index.validate()
+            try connection.run(index.toSQL())
+        }
+    }
+
+    private func dropTable(_ table: String) throws {
+        try connection.run("DROP TABLE IF EXISTS \(table.quote())")
+    }
+
+    private func copyTableContents(from: TableDefinition, to: TableDefinition) throws {
+        try connection.run(from.copySQL(to: to))
+    }
+
+    public var description: String {
+        "SQLiteSchemaChanger: \(connection.description)"
+    }
+}
+
+extension IndexDefinition {
+    func renameTable(to: String) -> IndexDefinition {
+        func indexName() -> String {
+            if to.starts(with: SchemaChanger.tempPrefix) {
+                return "\(SchemaChanger.tempPrefix)\(name)"
+            } else if table.starts(with: SchemaChanger.tempPrefix) {
+                return name.replacingOccurrences(of: SchemaChanger.tempPrefix, with: "")
+            } else {
+                return name
+            }
+        }
+        return IndexDefinition(table: to, name: indexName(), unique: unique, columns: columns, where: `where`, orders: orders)
+    }
+
+    func renameColumn(from: String, to: String) -> IndexDefinition {
+        IndexDefinition(table: table, name: name, unique: unique, columns: columns.map {
+            $0 == from ? to : $0
+        }, where: `where`, orders: orders)
+    }
+}
+
+extension TableDefinition {
+    func apply(_ operation: SchemaChanger.Operation) -> TableDefinition {
+        switch operation {
+        case .none: return self
+        case .add: fatalError("Use 'ALTER TABLE ADD COLUMN (...)'")
+        case .remove(let column):
+            return TableDefinition(name: name,
+                columns: columns.filter { $0.name != column },
+                indexes: indexes.filter { !$0.columns.contains(column) }
+            )
+        case .renameColumn(let from, let to):
+            return TableDefinition(
+                name: name,
+                columns: columns.map { $0.rename(from: from, to: to) },
+                indexes: indexes.map { $0.renameColumn(from: from, to: to) }
+        )
+        case .renameTable(let to):
+            return TableDefinition(name: to, columns: columns, indexes: indexes.map { $0.renameTable(to: to) })
+        }
+    }
+}
diff --git a/Sources/SQLite/Schema/SchemaDefinitions.swift b/Sources/SQLite/Schema/SchemaDefinitions.swift
new file mode 100644
index 00000000..563ae331
--- /dev/null
+++ b/Sources/SQLite/Schema/SchemaDefinitions.swift
@@ -0,0 +1,336 @@
+import Foundation
+
+struct TableDefinition: Equatable {
+    let name: String
+    let columns: [ColumnDefinition]
+    let indexes: [IndexDefinition]
+
+    var quotedColumnList: String {
+        columns.map { $0.name.quote() }.joined(separator: ", ")
+    }
+}
+
+// https://sqlite.org/syntax/column-def.html
+// column-name -> type-name -> column-constraint*
+public struct ColumnDefinition: Equatable {
+
+    // The type affinity of a column is the recommended type for data stored in that column.
+    // The important idea here is that the type is recommended, not required. Any column can still
+    // store any type of data. It is just that some columns, given the choice, will prefer to use one
+    // storage class over another. The preferred storage class for a column is called its "affinity".
+    enum Affinity: String, CustomStringConvertible, CaseIterable {
+        case INTEGER
+        case NUMERIC
+        case REAL
+        case TEXT
+        case BLOB
+
+        var description: String {
+            rawValue
+        }
+
+        static func from(_ string: String) -> Affinity {
+            Affinity.allCases.first { $0.rawValue.lowercased() == string.lowercased() } ?? TEXT
+        }
+    }
+
+    enum OnConflict: String, CaseIterable {
+        case ROLLBACK
+        case ABORT
+        case FAIL
+        case IGNORE
+        case REPLACE
+
+        static func from(_ string: String) -> OnConflict? {
+            OnConflict.allCases.first { $0.rawValue == string }
+        }
+    }
+
+    struct PrimaryKey: Equatable {
+        let autoIncrement: Bool
+        let onConflict: OnConflict?
+
+        // swiftlint:disable:next force_try
+        static let pattern = try! NSRegularExpression(pattern: "PRIMARY KEY\\s*(?:ASC|DESC)?\\s*(?:ON CONFLICT (\\w+)?)?\\s*(AUTOINCREMENT)?")
+
+        init(autoIncrement: Bool = true, onConflict: OnConflict? = nil) {
+            self.autoIncrement = autoIncrement
+            self.onConflict = onConflict
+        }
+
+        init?(sql: String) {
+            if let match = PrimaryKey.pattern.firstMatch(in: sql, range: NSRange(location: 0, length: sql.count)) {
+                let conflict = match.range(at: 1)
+                var onConflict: ColumnDefinition.OnConflict?
+                if conflict.location != NSNotFound {
+                    onConflict = .from((sql as NSString).substring(with: conflict))
+                }
+                let autoIncrement = match.range(at: 2).location != NSNotFound
+                self.init(autoIncrement: autoIncrement, onConflict: onConflict)
+            } else {
+                return nil
+            }
+        }
+    }
+
+    let name: String
+    let primaryKey: PrimaryKey?
+    let type: Affinity
+    let null: Bool
+    let defaultValue: LiteralValue
+    let references: ForeignKeyDefinition?
+
+    func rename(from: String, to: String) -> ColumnDefinition {
+        guard from == name else { return self }
+        return ColumnDefinition(name: to, primaryKey: primaryKey, type: type, null: null, defaultValue: defaultValue, references: references)
+    }
+}
+
+enum LiteralValue: Equatable, CustomStringConvertible {
+    // swiftlint:disable force_try
+    private static let singleQuote = try! NSRegularExpression(pattern: "^'(.*)'$")
+    private static let doubleQuote = try! NSRegularExpression(pattern: "^\"(.*)\"$")
+    private static let blob        = try! NSRegularExpression(pattern: "^[xX]\'(.*)\'$")
+    // swiftlint:enable force_try
+
+    case numericLiteral(String)
+    case stringLiteral(String)
+    // BLOB literals are string literals containing hexadecimal data and preceded by a single "x" or "X"
+    // character. Example: X'53514C697465'
+    case blobLiteral(String)
+
+    // If there is no explicit DEFAULT clause attached to a column definition, then the default value of the
+    // column is NULL
+    // swiftlint:disable identifier_name
+    case NULL
+
+    // Beginning with SQLite 3.23.0 (2018-04-02), SQLite recognizes the identifiers "TRUE" and
+    // "FALSE" as boolean literals, if and only if those identifiers are not already used for some other
+    // meaning.
+    //
+    // The boolean identifiers TRUE and FALSE are usually just aliases for the integer values 1 and 0, respectively.
+    case TRUE
+    case FALSE
+    case CURRENT_TIME
+    case CURRENT_DATE
+    case CURRENT_TIMESTAMP
+    // swiftlint:enable identifier_name
+
+    static func from(_ string: String?) -> LiteralValue {
+        func parse(_ value: String) -> LiteralValue {
+            if let match = singleQuote.firstMatch(in: value, range: NSRange(location: 0, length: value.count)) {
+                return stringLiteral((value as NSString).substring(with: match.range(at: 1)).replacingOccurrences(of: "''", with: "'"))
+            } else if let match = doubleQuote.firstMatch(in: value, range: NSRange(location: 0, length: value.count)) {
+                return stringLiteral((value as NSString).substring(with: match.range(at: 1)).replacingOccurrences(of: "\"\"", with: "\""))
+            } else if let match = blob.firstMatch(in: value, range: NSRange(location: 0, length: value.count)) {
+                return blobLiteral((value as NSString).substring(with: match.range(at: 1)))
+            } else {
+                return numericLiteral(value)
+            }
+        }
+        guard let string = string else { return NULL }
+
+        switch string {
+        case "NULL": return NULL
+        case "TRUE": return TRUE
+        case "FALSE": return FALSE
+        case "CURRENT_TIME": return CURRENT_TIME
+        case "CURRENT_TIMESTAMP": return CURRENT_TIMESTAMP
+        case "CURRENT_DATE": return CURRENT_DATE
+        default: return parse(string)
+        }
+    }
+
+    var description: String {
+        switch self {
+        case .NULL: return "NULL"
+        case .TRUE: return "TRUE"
+        case .FALSE: return "FALSE"
+        case .CURRENT_TIMESTAMP: return "CURRENT_TIMESTAMP"
+        case .CURRENT_TIME: return "CURRENT_TIME"
+        case .CURRENT_DATE: return "CURRENT_DATE"
+        case .stringLiteral(let value): return value.quote("'")
+        case .blobLiteral(let value): return "X\(value.quote("'"))"
+        case .numericLiteral(let value): return value
+        }
+    }
+
+    func map<U>(block: (LiteralValue) -> U) -> U? {
+        if self == .NULL {
+            return nil
+        } else {
+            return block(self)
+        }
+    }
+}
+
+// https://sqlite.org/lang_createindex.html
+// schema-name.index-name ON table-name ( indexed-column+ ) WHERE expr
+struct IndexDefinition: Equatable {
+    // SQLite supports index names up to 64 characters.
+    static let maxIndexLength = 64
+
+    // swiftlint:disable force_try
+    static let whereRe = try! NSRegularExpression(pattern: "\\sWHERE\\s+(.+)$")
+    static let orderRe = try! NSRegularExpression(pattern: "\"?(\\w+)\"? DESC")
+    // swiftlint:enable force_try
+
+    enum Order: String { case ASC, DESC }
+
+    init(table: String, name: String, unique: Bool = false, columns: [String], `where`: String? = nil, orders: [String: Order]? = nil) {
+        self.table = table
+        self.name = name
+        self.unique = unique
+        self.columns = columns
+        self.where = `where`
+        self.orders = orders
+    }
+
+    init (table: String, name: String, unique: Bool, columns: [String], indexSQL: String?) {
+        func wherePart(sql: String) -> String? {
+            IndexDefinition.whereRe.firstMatch(in: sql, options: [], range: NSRange(location: 0, length: sql.count)).map {
+                (sql as NSString).substring(with: $0.range(at: 1))
+            }
+        }
+
+        func orders(sql: String) -> [String: IndexDefinition.Order] {
+            IndexDefinition.orderRe.matches(in: sql, range: NSRange(location: 0, length: sql.count))
+                    .reduce([String: IndexDefinition.Order]()) { (memo, result) in
+                        var memo2 = memo
+                        let column = (sql as NSString).substring(with: result.range(at: 1))
+                        memo2[column] = .DESC
+                        return memo2
+            }
+        }
+        self.init(table: table,
+                  name: name,
+                  unique: unique,
+                  columns: columns,
+                  where: indexSQL.flatMap(wherePart),
+                  orders: indexSQL.flatMap(orders))
+    }
+
+    let table: String
+    let name: String
+    let unique: Bool
+    let columns: [String]
+    let `where`: String?
+    let orders: [String: Order]?
+
+    enum IndexError: LocalizedError {
+        case tooLong(String, String)
+
+        var errorDescription: String? {
+            switch self {
+            case .tooLong(let name, let table):
+                return "Index name '\(name)' on table '\(table)' is too long; the limit is " +
+                     "\(IndexDefinition.maxIndexLength) characters"
+            }
+        }
+    }
+
+    func validate() throws {
+        if name.count > IndexDefinition.maxIndexLength {
+            throw IndexError.tooLong(name, table)
+        }
+    }
+}
+
+struct ForeignKeyDefinition: Equatable {
+    let table: String
+    let column: String
+    let primaryKey: String
+    let onUpdate: String?
+    let onDelete: String?
+}
+
+struct ForeignKeyError: CustomStringConvertible {
+    let from: String
+    let rowId: Int64
+    let to: String
+
+    var description: String {
+        "\(from) [\(rowId)] => \(to)"
+    }
+}
+
+extension TableDefinition {
+    func toSQL(temporary: Bool = false) -> String {
+        assert(columns.count > 0, "no columns to create")
+
+        return ([
+            "CREATE",
+            temporary ? "TEMPORARY" : nil,
+            "TABLE",
+            name,
+            "(",
+            columns.map { $0.toSQL() }.joined(separator: ",\n"),
+            ")"
+        ] as [String?]).compactMap { $0 }
+         .joined(separator: " ")
+    }
+
+    func copySQL(to: TableDefinition) -> String {
+        assert(columns.count > 0, "no columns to copy")
+        assert(columns.count == to.columns.count, "column counts don't match")
+        return "INSERT INTO \(to.name.quote()) (\(to.quotedColumnList)) SELECT \(quotedColumnList) FROM \(name.quote())"
+    }
+}
+
+extension ColumnDefinition {
+    func toSQL() -> String {
+        [
+            name.quote(),
+            type.rawValue,
+            defaultValue.map { "DEFAULT \($0)" },
+            primaryKey.map { $0.toSQL() },
+            null ? nil : "NOT NULL",
+            references.map { $0.toSQL() }
+        ].compactMap { $0 }
+         .joined(separator: " ")
+    }
+}
+
+extension IndexDefinition {
+    func toSQL(ifNotExists: Bool = false) -> String {
+        let commaSeparatedColumns = columns.map { (column: String) -> String in
+            column.quote() + (orders?[column].map { " \($0.rawValue)" } ?? "")
+        }.joined(separator: ", ")
+
+        return ([
+            "CREATE",
+            unique ? "UNIQUE" : nil,
+            "INDEX",
+            ifNotExists ? "IF NOT EXISTS" : nil,
+            name.quote(),
+            "ON",
+            table.quote(),
+            "(\(commaSeparatedColumns))",
+            `where`.map { "WHERE \($0)" }
+        ] as [String?]).compactMap { $0 }
+         .joined(separator: " ")
+    }
+}
+
+extension ForeignKeyDefinition {
+    func toSQL() -> String {
+        ([
+            "REFERENCES",
+            table.quote(),
+            "(\(primaryKey.quote()))",
+            onUpdate.map { "ON UPDATE \($0)" },
+            onDelete.map { "ON DELETE \($0)" }
+        ] as [String?]).compactMap { $0 }
+         .joined(separator: " ")
+    }
+}
+
+extension ColumnDefinition.PrimaryKey {
+    func toSQL() -> String {
+        [
+            "PRIMARY KEY",
+            autoIncrement ? "AUTOINCREMENT" : nil,
+            onConflict.map { "ON CONFLICT \($0.rawValue)" }
+        ].compactMap { $0 }.joined(separator: " ")
+    }
+}
diff --git a/Tests/SQLiteTests/Schema/ConnectionSchemaTests.swift b/Tests/SQLiteTests/Schema/ConnectionSchemaTests.swift
new file mode 100644
index 00000000..02321510
--- /dev/null
+++ b/Tests/SQLiteTests/Schema/ConnectionSchemaTests.swift
@@ -0,0 +1,139 @@
+import XCTest
+@testable import SQLite
+
+class ConnectionSchemaTests: SQLiteTestCase {
+
+    override func setUpWithError() throws {
+        try super.setUpWithError()
+        try createUsersTable()
+    }
+
+    func test_column_info() throws {
+        let columns = try db.columnInfo(table: "users")
+        XCTAssertEqual(columns, [
+            ColumnDefinition(name: "id",
+                             primaryKey: .init(autoIncrement: false, onConflict: nil),
+                             type: .INTEGER,
+                             null: true,
+                             defaultValue: .NULL,
+                             references: nil),
+            ColumnDefinition(name: "email",
+                             primaryKey: nil,
+                             type: .TEXT,
+                             null: false,
+                             defaultValue: .NULL,
+                             references: nil),
+            ColumnDefinition(name: "age",
+                             primaryKey: nil,
+                             type: .INTEGER,
+                             null: true,
+                             defaultValue: .NULL,
+                             references: nil),
+            ColumnDefinition(name: "salary",
+                             primaryKey: nil,
+                             type: .REAL,
+                             null: true,
+                             defaultValue: .NULL,
+                             references: nil),
+            ColumnDefinition(name: "admin",
+                             primaryKey: nil,
+                             type: .TEXT,
+                             null: false,
+                             defaultValue: .numericLiteral("0"),
+                             references: nil),
+            ColumnDefinition(name: "manager_id",
+                             primaryKey: nil, type: .INTEGER,
+                             null: true,
+                             defaultValue: .NULL,
+                             references: .init(table: "users", column: "manager_id", primaryKey: "id", onUpdate: nil, onDelete: nil)),
+            ColumnDefinition(name: "created_at",
+                             primaryKey: nil,
+                             type: .TEXT,
+                             null: true,
+                             defaultValue: .NULL,
+                             references: nil)
+        ])
+    }
+
+    func test_column_info_parses_conflict_modifier() throws {
+        try db.run("CREATE TABLE t (\"id\" INTEGER PRIMARY KEY ON CONFLICT IGNORE AUTOINCREMENT)")
+
+        XCTAssertEqual(
+            try db.columnInfo(table: "t"), [
+            ColumnDefinition(
+                name: "id",
+                primaryKey: .init(autoIncrement: true, onConflict: .IGNORE),
+                type: .INTEGER,
+                null: true,
+                defaultValue: .NULL,
+                references: nil)
+            ]
+        )
+    }
+
+    func test_column_info_detects_missing_autoincrement() throws {
+        try db.run("CREATE TABLE t (\"id\" INTEGER PRIMARY KEY)")
+
+        XCTAssertEqual(
+            try db.columnInfo(table: "t"), [
+            ColumnDefinition(
+                    name: "id",
+                    primaryKey: .init(autoIncrement: false),
+                    type: .INTEGER,
+                    null: true,
+                    defaultValue: .NULL,
+                    references: nil)
+            ]
+        )
+    }
+
+    func test_index_info_no_index() throws {
+        let indexes = try db.indexInfo(table: "users")
+        XCTAssertTrue(indexes.isEmpty)
+    }
+
+    func test_index_info_with_index() throws {
+        try db.run("CREATE UNIQUE INDEX index_users ON users (age DESC) WHERE age IS NOT NULL")
+        let indexes = try db.indexInfo(table: "users")
+
+        XCTAssertEqual(indexes, [
+            IndexDefinition(
+                table: "users",
+                name: "index_users",
+                unique: true,
+                columns: ["age"],
+                where: "age IS NOT NULL",
+                orders: ["age": .DESC]
+            )
+        ])
+    }
+
+    func test_table_info_returns_list_of_tables() throws {
+        let tables = try db.tableInfo()
+        XCTAssertEqual(tables, ["users"])
+    }
+
+    func test_foreign_key_info_empty() throws {
+        try db.run("CREATE TABLE t (\"id\" INTEGER PRIMARY KEY)")
+
+        let foreignKeys = try db.foreignKeyInfo(table: "t")
+        XCTAssertTrue(foreignKeys.isEmpty)
+    }
+
+    func test_foreign_key_info() throws {
+        let linkTable = Table("test_links")
+
+        let idColumn = SQLite.Expression<Int64>("id")
+        let testIdColumn = SQLite.Expression<Int64>("test_id")
+
+        try db.run(linkTable.create(block: { definition in
+            definition.column(idColumn, primaryKey: .autoincrement)
+            definition.column(testIdColumn, unique: false, check: nil, references: users, Expression<Int64>("id"))
+        }))
+
+        let foreignKeys = try db.foreignKeyInfo(table: "test_links")
+        XCTAssertEqual(foreignKeys, [
+            ForeignKeyDefinition(table: "users", column: "test_id", primaryKey: "id", onUpdate: nil, onDelete: nil)
+        ])
+    }
+}
diff --git a/Tests/SQLiteTests/Schema/SchemaChangerTests.swift b/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
new file mode 100644
index 00000000..21ae6e59
--- /dev/null
+++ b/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
@@ -0,0 +1,87 @@
+import XCTest
+@testable import SQLite
+
+class SchemaChangerTests: SQLiteTestCase {
+    var schemaChanger: SchemaChanger!
+
+    override func setUpWithError() throws {
+        try super.setUpWithError()
+        try createUsersTable()
+
+        try insertUsers("bob")
+
+        schemaChanger = SchemaChanger(connection: db)
+    }
+
+    func test_empty_migration_does_not_change_column_definitions() throws {
+        let previous = try db.columnInfo(table: "users")
+        try schemaChanger.alter(table: "users") { _ in
+        }
+        let current = try db.columnInfo(table: "users")
+
+        XCTAssertEqual(previous, current)
+    }
+
+    func test_empty_migration_does_not_change_index_definitions() throws {
+        let previous = try db.indexInfo(table: "users")
+        try schemaChanger.alter(table: "users") { _ in
+        }
+        let current = try db.indexInfo(table: "users")
+
+        XCTAssertEqual(previous, current)
+    }
+
+    func test_empty_migration_does_not_change_foreign_key_definitions() throws {
+        let previous = try db.foreignKeyInfo(table: "users")
+        try schemaChanger.alter(table: "users") { _ in
+        }
+        let current = try db.foreignKeyInfo(table: "users")
+
+        XCTAssertEqual(previous, current)
+    }
+
+    func test_empty_migration_does_not_change_the_row_count() throws {
+        let previous = try db.scalar(users.count)
+        try schemaChanger.alter(table: "users") { _ in
+        }
+        let current = try db.scalar(users.count)
+
+        XCTAssertEqual(previous, current)
+    }
+
+    func test_remove_column() throws {
+        try schemaChanger.alter(table: "users") { table in
+            table.remove("age")
+        }
+        let columns = try db.columnInfo(table: "users").map(\.name)
+        XCTAssertFalse(columns.contains("age"))
+    }
+
+    func test_rename_column() throws {
+        try schemaChanger.alter(table: "users") { table in
+            table.rename("age", to: "age2")
+        }
+
+        let columns = try db.columnInfo(table: "users").map(\.name)
+        XCTAssertFalse(columns.contains("age"))
+        XCTAssertTrue(columns.contains("age2"))
+    }
+
+    func test_add_column() throws {
+        let newColumn = ColumnDefinition(name: "new_column", primaryKey: nil, type: .TEXT, null: true, defaultValue: .NULL, references: nil)
+
+        try schemaChanger.alter(table: "users") { table in
+            table.add(newColumn)
+        }
+
+        let columns = try db.columnInfo(table: "users")
+        XCTAssertTrue(columns.contains(newColumn))
+    }
+
+    func test_drop_table() throws {
+        try schemaChanger.drop(table: "users")
+
+        let tables = try db.tableInfo()
+        XCTAssertFalse(tables.contains("users"))
+    }
+}
diff --git a/Tests/SQLiteTests/Schema/SchemaDefinitionsTests.swift b/Tests/SQLiteTests/Schema/SchemaDefinitionsTests.swift
new file mode 100644
index 00000000..07fbf371
--- /dev/null
+++ b/Tests/SQLiteTests/Schema/SchemaDefinitionsTests.swift
@@ -0,0 +1,342 @@
+import XCTest
+@testable import SQLite
+
+class ColumnDefinitionTests: XCTestCase {
+    var definition: ColumnDefinition!
+    var expected: String!
+
+    static let definitions: [(ColumnDefinition, String)] = [
+        (ColumnDefinition(name: "id", primaryKey: .init(), type: .INTEGER, null: false, defaultValue: .NULL, references: nil),
+        "\"id\" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL"),
+
+        (ColumnDefinition(name: "other_id", primaryKey: nil, type: .INTEGER, null: false, defaultValue: .NULL,
+                          references: ForeignKeyDefinition(table: "other_table", column: "", primaryKey: "some_id", onUpdate: nil, onDelete: nil)),
+        "\"other_id\" INTEGER NOT NULL REFERENCES \"other_table\" (\"some_id\")"),
+
+        (ColumnDefinition(name: "text", primaryKey: nil, type: .TEXT, null: true, defaultValue: .NULL, references: nil),
+        "\"text\" TEXT"),
+
+        (ColumnDefinition(name: "text", primaryKey: nil, type: .TEXT, null: false, defaultValue: .NULL, references: nil),
+        "\"text\" TEXT NOT NULL"),
+
+        (ColumnDefinition(name: "text_column", primaryKey: nil, type: .TEXT, null: true, defaultValue: .stringLiteral("fo\"o"), references: nil),
+        "\"text_column\" TEXT DEFAULT 'fo\"o'"),
+
+        (ColumnDefinition(name: "integer_column", primaryKey: nil, type: .INTEGER, null: true, defaultValue: .numericLiteral("123"), references: nil),
+        "\"integer_column\" INTEGER DEFAULT 123"),
+
+        (ColumnDefinition(name: "real_column", primaryKey: nil, type: .REAL, null: true, defaultValue: .numericLiteral("123.123"), references: nil),
+        "\"real_column\" REAL DEFAULT 123.123")
+    ]
+
+    override class var defaultTestSuite: XCTestSuite {
+        let suite = XCTestSuite(forTestCaseClass: ColumnDefinitionTests.self)
+
+        for (column, expected) in ColumnDefinitionTests.definitions {
+            let test = ColumnDefinitionTests(selector: #selector(verify))
+            test.definition = column
+            test.expected = expected
+            suite.addTest(test)
+        }
+        return suite
+    }
+
+    @objc func verify() {
+        XCTAssertEqual(definition.toSQL(), expected)
+    }
+}
+
+class AffinityTests: XCTestCase {
+    func test_from() {
+        XCTAssertEqual(ColumnDefinition.Affinity.from("TEXT"), .TEXT)
+        XCTAssertEqual(ColumnDefinition.Affinity.from("text"), .TEXT)
+        XCTAssertEqual(ColumnDefinition.Affinity.from("INTEGER"), .INTEGER)
+    }
+
+    func test_returns_TEXT_for_unknown_type() {
+        XCTAssertEqual(ColumnDefinition.Affinity.from("baz"), .TEXT)
+    }
+}
+
+class IndexDefinitionTests: XCTestCase {
+    var definition: IndexDefinition!
+    var expected: String!
+    var ifNotExists: Bool!
+
+    static let definitions: [(IndexDefinition, Bool, String)] = [
+        (IndexDefinition(table: "tests", name: "index_tests",
+                         unique: false,
+                         columns: ["test_column"],
+                         where: nil,
+                         orders: nil),
+        false,
+        "CREATE INDEX \"index_tests\" ON \"tests\" (\"test_column\")"),
+
+        (IndexDefinition(table: "tests", name: "index_tests",
+                         unique: true,
+                         columns: ["test_column"],
+                         where: nil,
+                         orders: nil),
+        false,
+        "CREATE UNIQUE INDEX \"index_tests\" ON \"tests\" (\"test_column\")"),
+
+        (IndexDefinition(table: "tests", name: "index_tests",
+                         unique: true,
+                         columns: ["test_column", "bar_column"],
+                         where: "test_column IS NOT NULL",
+                         orders: nil),
+        false,
+        "CREATE UNIQUE INDEX \"index_tests\" ON \"tests\" (\"test_column\", \"bar_column\") WHERE test_column IS NOT NULL"),
+
+        (IndexDefinition(table: "tests", name: "index_tests",
+                         unique: true,
+                         columns: ["test_column", "bar_column"],
+                         where: nil,
+                         orders: ["test_column": .DESC]),
+        false,
+        "CREATE UNIQUE INDEX \"index_tests\" ON \"tests\" (\"test_column\" DESC, \"bar_column\")"),
+
+        (IndexDefinition(table: "tests", name: "index_tests",
+                         unique: false,
+                         columns: ["test_column"],
+                         where: nil,
+                         orders: nil),
+        true,
+        "CREATE INDEX IF NOT EXISTS \"index_tests\" ON \"tests\" (\"test_column\")")
+    ]
+
+    override class var defaultTestSuite: XCTestSuite {
+        let suite = XCTestSuite(forTestCaseClass: IndexDefinitionTests.self)
+
+        for (column, ifNotExists, expected) in IndexDefinitionTests.definitions {
+            let test = IndexDefinitionTests(selector: #selector(verify))
+            test.definition = column
+            test.expected = expected
+            test.ifNotExists = ifNotExists
+            suite.addTest(test)
+        }
+        return suite
+    }
+
+    @objc func verify() {
+        XCTAssertEqual(definition.toSQL(ifNotExists: ifNotExists), expected)
+    }
+
+    func test_validate() {
+
+        let longIndex = IndexDefinition(
+                table: "tests",
+                name: String(repeating: "x", count: 65),
+                unique: false,
+                columns: ["test_column"],
+                where: nil,
+                orders: nil)
+
+        XCTAssertThrowsError(try longIndex.validate()) { error in
+            XCTAssertEqual(error.localizedDescription,
+                           "Index name 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' " +
+                           "on table 'tests' is too long; the limit is 64 characters")
+        }
+    }
+
+    func test_rename() {
+        let index = IndexDefinition(table: "tests", name: "index_tests_something",
+                                    unique: true,
+                                    columns: ["test_column"],
+                                    where: "test_column IS NOT NULL",
+                                    orders: nil)
+
+        let renamedIndex = index.renameTable(to: "foo")
+
+        XCTAssertEqual(renamedIndex,
+           IndexDefinition(
+                table: "foo",
+                name: "index_tests_something",
+                unique: true,
+                columns: ["test_column"],
+                where: "test_column IS NOT NULL",
+                orders: nil
+           )
+        )
+    }
+}
+
+class ForeignKeyDefinitionTests: XCTestCase {
+    func test_toSQL() {
+        XCTAssertEqual(
+            ForeignKeyDefinition(
+                table: "foo",
+                column: "bar",
+                primaryKey: "bar_id",
+                onUpdate: nil,
+                onDelete: "SET NULL"
+            ).toSQL(), """
+               REFERENCES "foo" ("bar_id") ON DELETE SET NULL
+               """
+        )
+    }
+}
+
+class TableDefinitionTests: XCTestCase {
+    func test_quoted_columnList() {
+        let definition = TableDefinition(name: "foo", columns: [
+            ColumnDefinition(name: "id", primaryKey: .init(), type: .INTEGER, null: false, defaultValue: .NULL, references: nil),
+            ColumnDefinition(name: "baz", primaryKey: nil, type: .INTEGER, null: false, defaultValue: .NULL, references: nil)
+        ], indexes: [])
+
+        XCTAssertEqual(definition.quotedColumnList, """
+                                                    "id", "baz"
+                                                    """)
+    }
+
+    func test_toSQL() {
+        let definition = TableDefinition(name: "foo", columns: [
+            ColumnDefinition(name: "id", primaryKey: .init(), type: .INTEGER, null: false, defaultValue: .NULL, references: nil)
+        ], indexes: [])
+
+        XCTAssertEqual(definition.toSQL(), """
+                                           CREATE TABLE foo ( \"id\" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL )
+                                           """)
+    }
+
+    func test_toSQL_temp_table() {
+        let definition = TableDefinition(name: "foo", columns: [
+            ColumnDefinition(name: "id", primaryKey: .init(), type: .INTEGER, null: false, defaultValue: .NULL, references: nil)
+        ], indexes: [])
+
+        XCTAssertEqual(definition.toSQL(temporary: true), """
+                                                          CREATE TEMPORARY TABLE foo ( \"id\" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL )
+                                                          """)
+    }
+
+    /*
+    func test_throws_an_error_when_columns_are_empty() {
+        let empty = TableDefinition(name: "empty", columns: [], indexes: [])
+        XCTAssertThrowsError(empty.toSQL())
+    }
+     */
+
+    func test_copySQL() {
+        let from = TableDefinition(name: "from_table", columns: [
+            ColumnDefinition(name: "id", primaryKey: .init(), type: .INTEGER, null: false, defaultValue: .NULL, references: nil)
+        ], indexes: [])
+
+        let to = TableDefinition(name: "to_table", columns: [
+            ColumnDefinition(name: "id", primaryKey: .init(), type: .INTEGER, null: false, defaultValue: .NULL, references: nil)
+        ], indexes: [])
+
+        XCTAssertEqual(from.copySQL(to: to), """
+                                             INSERT INTO "to_table" ("id") SELECT "id" FROM "from_table"
+                                             """)
+    }
+}
+
+class PrimaryKeyTests: XCTestCase {
+    func test_toSQL() {
+        XCTAssertEqual(ColumnDefinition.PrimaryKey(autoIncrement: false).toSQL(),
+                       "PRIMARY KEY")
+    }
+
+    func test_toSQL_autoincrement() {
+        XCTAssertEqual(ColumnDefinition.PrimaryKey(autoIncrement: true).toSQL(),
+                       "PRIMARY KEY AUTOINCREMENT")
+    }
+
+    func test_toSQL_on_conflict() {
+        XCTAssertEqual(ColumnDefinition.PrimaryKey(autoIncrement: false, onConflict: .ROLLBACK).toSQL(),
+                       "PRIMARY KEY ON CONFLICT ROLLBACK")
+    }
+}
+
+class LiteralValueTests: XCTestCase {
+    func test_recognizes_TRUE() {
+        XCTAssertEqual(LiteralValue.from("TRUE"), .TRUE)
+    }
+
+    func test_recognizes_FALSE() {
+        XCTAssertEqual(LiteralValue.from("FALSE"), .FALSE)
+    }
+
+    func test_recognizes_NULL() {
+        XCTAssertEqual(LiteralValue.from("NULL"), .NULL)
+    }
+
+    func test_recognizes_nil() {
+        XCTAssertEqual(LiteralValue.from(nil), .NULL)
+    }
+
+    func test_recognizes_CURRENT_TIME() {
+        XCTAssertEqual(LiteralValue.from("CURRENT_TIME"), .CURRENT_TIME)
+    }
+
+    func test_recognizes_CURRENT_TIMESTAMP() {
+        XCTAssertEqual(LiteralValue.from("CURRENT_TIMESTAMP"), .CURRENT_TIMESTAMP)
+    }
+
+    func test_recognizes_CURRENT_DATE() {
+        XCTAssertEqual(LiteralValue.from("CURRENT_DATE"), .CURRENT_DATE)
+    }
+
+    func test_recognizes_double_quote_string_literals() {
+        XCTAssertEqual(LiteralValue.from("\"foo\""), .stringLiteral("foo"))
+    }
+
+    func test_recognizes_single_quote_string_literals() {
+        XCTAssertEqual(LiteralValue.from("\'foo\'"), .stringLiteral("foo"))
+    }
+
+    func test_unquotes_double_quote_string_literals() {
+        XCTAssertEqual(LiteralValue.from("\"fo\"\"o\""), .stringLiteral("fo\"o"))
+    }
+
+    func test_unquotes_single_quote_string_literals() {
+        XCTAssertEqual(LiteralValue.from("'fo''o'"), .stringLiteral("fo'o"))
+    }
+
+    func test_recognizes_numeric_literals() {
+        XCTAssertEqual(LiteralValue.from("1.2"), .numericLiteral("1.2"))
+        XCTAssertEqual(LiteralValue.from("0xdeadbeef"), .numericLiteral("0xdeadbeef"))
+    }
+
+    func test_recognizes_blob_literals() {
+        XCTAssertEqual(LiteralValue.from("X'deadbeef'"), .blobLiteral("deadbeef"))
+        XCTAssertEqual(LiteralValue.from("x'deadbeef'"), .blobLiteral("deadbeef"))
+    }
+
+    func test_description_TRUE() {
+        XCTAssertEqual(LiteralValue.TRUE.description, "TRUE")
+    }
+
+    func test_description_FALSE() {
+        XCTAssertEqual(LiteralValue.FALSE.description, "FALSE")
+    }
+
+    func test_description_NULL() {
+        XCTAssertEqual(LiteralValue.NULL.description, "NULL")
+    }
+
+    func test_description_CURRENT_TIME() {
+        XCTAssertEqual(LiteralValue.CURRENT_TIME.description, "CURRENT_TIME")
+    }
+
+    func test_description_CURRENT_TIMESTAMP() {
+        XCTAssertEqual(LiteralValue.CURRENT_TIMESTAMP.description, "CURRENT_TIMESTAMP")
+    }
+
+    func test_description_CURRENT_DATE() {
+        XCTAssertEqual(LiteralValue.CURRENT_DATE.description, "CURRENT_DATE")
+    }
+
+    func test_description_string_literal() {
+        XCTAssertEqual(LiteralValue.stringLiteral("foo").description, "'foo'")
+    }
+
+    func test_description_numeric_literal() {
+        XCTAssertEqual(LiteralValue.numericLiteral("1.2").description, "1.2")
+        XCTAssertEqual(LiteralValue.numericLiteral("0xdeadbeef").description, "0xdeadbeef")
+    }
+
+    func test_description_blob_literal() {
+        XCTAssertEqual(LiteralValue.blobLiteral("deadbeef").description, "X'deadbeef'")
+    }
+}

From affcec8494488f155a846dc58c388ca798a239e7 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Sun, 24 Jul 2022 02:43:39 +0200
Subject: [PATCH 184/326] Use DROP/RENAME COLUMN when available

---
 Sources/SQLite/Schema/Connection+Schema.swift | 13 +++++++++++
 Sources/SQLite/Schema/SchemaChanger.swift     | 22 +++++++++++++++----
 .../Schema/ConnectionSchemaTests.swift        |  7 ++++++
 .../Schema/SchemaChangerTests.swift           | 22 +++++++++++++++++++
 .../Schema/SchemaDefinitionsTests.swift       |  4 ++++
 5 files changed, 64 insertions(+), 4 deletions(-)

diff --git a/Sources/SQLite/Schema/Connection+Schema.swift b/Sources/SQLite/Schema/Connection+Schema.swift
index e368bdaf..c6cf197f 100644
--- a/Sources/SQLite/Schema/Connection+Schema.swift
+++ b/Sources/SQLite/Schema/Connection+Schema.swift
@@ -1,6 +1,19 @@
 import Foundation
 
 extension Connection {
+    var sqliteVersion: String? {
+        (try? scalar("SELECT sqlite_version()")) as? String
+    }
+
+    var sqliteVersionTriple: (Int, Int, Int) {
+        guard let version = sqliteVersion,
+              let splits = .some(version.split(separator: ".", maxSplits: 3)), splits.count == 3,
+              let major = Int(splits[0]), let minor = Int(splits[1]), let point = Int(splits[2]) else {
+            return (0, 0, 0)
+        }
+        return (major, minor, point)
+    }
+
     // Changing the foreign_keys setting affects the execution of all statements prepared using the database
     // connection, including those prepared before the setting was changed.
     //
diff --git a/Sources/SQLite/Schema/SchemaChanger.swift b/Sources/SQLite/Schema/SchemaChanger.swift
index ff0befc3..cd6b6f25 100644
--- a/Sources/SQLite/Schema/SchemaChanger.swift
+++ b/Sources/SQLite/Schema/SchemaChanger.swift
@@ -27,6 +27,8 @@ import Foundation
        12. If foreign keys constraints were originally enabled, reenable them now.
 */
 public class SchemaChanger: CustomStringConvertible {
+    typealias SQLiteVersion = (Int, Int, Int)
+
     enum SchemaChangeError: LocalizedError {
         case foreignKeyError([ForeignKeyError])
 
@@ -46,9 +48,14 @@ public class SchemaChanger: CustomStringConvertible {
         case renameTable(String)
 
         /// Returns non-nil if the operation can be executed with a simple SQL statement
-        func toSQL(_ table: String) -> String? {
+        func toSQL(_ table: String, version: SQLiteVersion) -> String? {
             switch self {
-            case .add(let definition): return "ALTER TABLE \(table.quote()) ADD COLUMN \(definition.toSQL())"
+            case .add(let definition):
+                return "ALTER TABLE \(table.quote()) ADD COLUMN \(definition.toSQL())"
+            case .renameColumn(let from, let to) where version.0 >= 3 && version.1 >= 25:
+                return "ALTER TABLE \(table.quote()) RENAME COLUMN \(from.quote()) TO \(to.quote())"
+            case .remove(let column) where version.0 >= 3 && version.1 >= 35:
+                return "ALTER TABLE \(table.quote()) DROP COLUMN \(column.quote())"
             default: return nil
             }
         }
@@ -77,6 +84,7 @@ public class SchemaChanger: CustomStringConvertible {
     }
 
     private let connection: Connection
+    private let version: SQLiteVersion
     static let tempPrefix = "tmp_"
     typealias Block = () throws -> Void
     public typealias AlterTableDefinitionBlock = (AlterTableDefinition) -> Void
@@ -87,8 +95,14 @@ public class SchemaChanger: CustomStringConvertible {
         static let temp = Options(rawValue: 1)
     }
 
-    public init(connection: Connection) {
+    public convenience init(connection: Connection) {
+        self.init(connection: connection,
+                  version: connection.sqliteVersionTriple)
+    }
+
+    init(connection: Connection, version: SQLiteVersion) {
         self.connection = connection
+        self.version = version
     }
 
     public func alter(table: String, block: AlterTableDefinitionBlock) throws {
@@ -105,7 +119,7 @@ public class SchemaChanger: CustomStringConvertible {
     }
 
     private func run(table: String, operation: Operation) throws {
-        if let sql = operation.toSQL(table) {
+        if let sql = operation.toSQL(table, version: version) {
             try connection.run(sql)
         } else {
             try doTheTableDance(table: table, operation: operation)
diff --git a/Tests/SQLiteTests/Schema/ConnectionSchemaTests.swift b/Tests/SQLiteTests/Schema/ConnectionSchemaTests.swift
index 02321510..9fbad26c 100644
--- a/Tests/SQLiteTests/Schema/ConnectionSchemaTests.swift
+++ b/Tests/SQLiteTests/Schema/ConnectionSchemaTests.swift
@@ -136,4 +136,11 @@ class ConnectionSchemaTests: SQLiteTestCase {
             ForeignKeyDefinition(table: "users", column: "test_id", primaryKey: "id", onUpdate: nil, onDelete: nil)
         ])
     }
+
+    func test_sqlite_version_triple() {
+        let version = db.sqliteVersionTriple
+        XCTAssertEqual(version.0, 3)
+        XCTAssertGreaterThan(version.1, 0)
+        XCTAssertGreaterThanOrEqual(version.2, 0)
+    }
 }
diff --git a/Tests/SQLiteTests/Schema/SchemaChangerTests.swift b/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
index 21ae6e59..165de4a8 100644
--- a/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
+++ b/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
@@ -57,6 +57,16 @@ class SchemaChangerTests: SQLiteTestCase {
         XCTAssertFalse(columns.contains("age"))
     }
 
+    func test_remove_column_legacy() throws {
+        schemaChanger = .init(connection: db, version: (3, 24, 0)) // DROP COLUMN introduced in 3.35.0
+
+        try schemaChanger.alter(table: "users") { table in
+            table.remove("age")
+        }
+        let columns = try db.columnInfo(table: "users").map(\.name)
+        XCTAssertFalse(columns.contains("age"))
+    }
+
     func test_rename_column() throws {
         try schemaChanger.alter(table: "users") { table in
             table.rename("age", to: "age2")
@@ -67,6 +77,18 @@ class SchemaChangerTests: SQLiteTestCase {
         XCTAssertTrue(columns.contains("age2"))
     }
 
+    func test_rename_column_legacy() throws {
+        schemaChanger = .init(connection: db, version: (3, 24, 0)) // RENAME COLUMN introduced in 3.25.0
+
+        try schemaChanger.alter(table: "users") { table in
+            table.rename("age", to: "age2")
+        }
+
+        let columns = try db.columnInfo(table: "users").map(\.name)
+        XCTAssertFalse(columns.contains("age"))
+        XCTAssertTrue(columns.contains("age2"))
+    }
+
     func test_add_column() throws {
         let newColumn = ColumnDefinition(name: "new_column", primaryKey: nil, type: .TEXT, null: true, defaultValue: .NULL, references: nil)
 
diff --git a/Tests/SQLiteTests/Schema/SchemaDefinitionsTests.swift b/Tests/SQLiteTests/Schema/SchemaDefinitionsTests.swift
index 07fbf371..c19840bc 100644
--- a/Tests/SQLiteTests/Schema/SchemaDefinitionsTests.swift
+++ b/Tests/SQLiteTests/Schema/SchemaDefinitionsTests.swift
@@ -29,6 +29,7 @@ class ColumnDefinitionTests: XCTestCase {
         "\"real_column\" REAL DEFAULT 123.123")
     ]
 
+    #if !os(Linux)
     override class var defaultTestSuite: XCTestSuite {
         let suite = XCTestSuite(forTestCaseClass: ColumnDefinitionTests.self)
 
@@ -44,6 +45,7 @@ class ColumnDefinitionTests: XCTestCase {
     @objc func verify() {
         XCTAssertEqual(definition.toSQL(), expected)
     }
+    #endif
 }
 
 class AffinityTests: XCTestCase {
@@ -105,6 +107,7 @@ class IndexDefinitionTests: XCTestCase {
         "CREATE INDEX IF NOT EXISTS \"index_tests\" ON \"tests\" (\"test_column\")")
     ]
 
+    #if !os(Linux)
     override class var defaultTestSuite: XCTestSuite {
         let suite = XCTestSuite(forTestCaseClass: IndexDefinitionTests.self)
 
@@ -121,6 +124,7 @@ class IndexDefinitionTests: XCTestCase {
     @objc func verify() {
         XCTAssertEqual(definition.toSQL(ifNotExists: ifNotExists), expected)
     }
+    #endif
 
     func test_validate() {
 

From 1b645291774b5d36af7630c8bdd8530373d1368d Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Sun, 24 Jul 2022 19:49:41 +0200
Subject: [PATCH 185/326] ColumnDefinition needs to be public

---
 SQLite.xcodeproj/project.pbxproj              | 10 +++
 Sources/SQLite/Core/Connection+Pragmas.swift  | 52 +++++++++++
 Sources/SQLite/Core/Connection.swift          | 11 ---
 Sources/SQLite/Schema/Connection+Schema.swift | 87 ++++++-------------
 Sources/SQLite/Schema/SchemaChanger.swift     |  8 +-
 Sources/SQLite/Schema/SchemaDefinitions.swift | 60 +++++++------
 .../Schema/ConnectionSchemaTests.swift        |  9 +-
 .../Schema/SchemaDefinitionsTests.swift       |  4 +-
 8 files changed, 130 insertions(+), 111 deletions(-)
 create mode 100644 Sources/SQLite/Core/Connection+Pragmas.swift

diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj
index abd69301..a97ef184 100644
--- a/SQLite.xcodeproj/project.pbxproj
+++ b/SQLite.xcodeproj/project.pbxproj
@@ -46,6 +46,7 @@
 		03A65E941C6BB3030062603F /* ValueTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B331C3F142E00AE3E12 /* ValueTests.swift */; };
 		03A65E951C6BB3030062603F /* TestHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B161C3F127200AE3E12 /* TestHelpers.swift */; };
 		03A65E971C6BB3210062603F /* libsqlite3.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 03A65E961C6BB3210062603F /* libsqlite3.tbd */; };
+		19A17018F250343BD0F9F4B0 /* Connection+Pragmas.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17F285B767BFACD96714B /* Connection+Pragmas.swift */; };
 		19A17073552293CA063BEA66 /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17E723300E5ED3771DCB5 /* Result.swift */; };
 		19A1709C3E7A406E62293B2A /* Fixtures.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17B93B48B5560E6E51791 /* Fixtures.swift */; };
 		19A170ACC97B19730FB7BA4D /* Connection+Aggregation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A175A9CB446640AE6F2200 /* Connection+Aggregation.swift */; };
@@ -71,6 +72,7 @@
 		19A174D78559CD30679BCCCB /* FTS5Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1721B8984686B9963B45D /* FTS5Tests.swift */; };
 		19A1750CEE9B05267995CF3D /* FTS5.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1730E4390C775C25677D1 /* FTS5.swift */; };
 		19A1750EF4A5F92954A451FF /* Connection+Schema.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A170A97B51DC5EE365F3C5 /* Connection+Schema.swift */; };
+		19A1760CE25615CA015E2E5F /* Connection+Pragmas.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17F285B767BFACD96714B /* Connection+Pragmas.swift */; };
 		19A176376CB6A94759F7980A /* Connection+Aggregation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A175A9CB446640AE6F2200 /* Connection+Aggregation.swift */; };
 		19A176406BDE9D9C80CC9FA3 /* QueryIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17BA6B4E282C1315A115C /* QueryIntegrationTests.swift */; };
 		19A1766AC10D13C4EFF349AD /* SchemaChangerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17FDB0B0CFB8987906FD0 /* SchemaChangerTests.swift */; };
@@ -82,7 +84,9 @@
 		19A178072B371489E6A1E839 /* FoundationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1794CC4D7827E997E32A7 /* FoundationTests.swift */; };
 		19A17835FD5886FDC5A3228F /* Cipher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A178A39ACA9667A62663CC /* Cipher.swift */; };
 		19A1785195182AF8731A8BDA /* RowTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A175C1F9CB3BBAB8FCEC7B /* RowTests.swift */; };
+		19A178A8B2A34FB6B565DEDA /* Connection+Pragmas.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17F285B767BFACD96714B /* Connection+Pragmas.swift */; };
 		19A1792C0520D4E83C2EB075 /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1710E73A46D5AC721CDA9 /* Errors.swift */; };
+		19A17986405D9A875698408F /* Connection+Pragmas.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17F285B767BFACD96714B /* Connection+Pragmas.swift */; };
 		19A179A0C45377CB09BB358C /* CipherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17399EA9E61235D5D77BF /* CipherTests.swift */; };
 		19A179B59450FE7C4811AB8A /* Connection+Aggregation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A175A9CB446640AE6F2200 /* Connection+Aggregation.swift */; };
 		19A179CCF9671E345E5A9811 /* Cipher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A178A39ACA9667A62663CC /* Cipher.swift */; };
@@ -283,6 +287,7 @@
 		19A17CA1DF7D0F7C9A94C51C /* ConnectionSchemaTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConnectionSchemaTests.swift; sourceTree = "<group>"; };
 		19A17E723300E5ED3771DCB5 /* Result.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Result.swift; sourceTree = "<group>"; };
 		19A17EA3A313F129011B3FA0 /* Release.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = Release.md; sourceTree = "<group>"; };
+		19A17F285B767BFACD96714B /* Connection+Pragmas.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Connection+Pragmas.swift"; sourceTree = "<group>"; };
 		19A17FDB0B0CFB8987906FD0 /* SchemaChangerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SchemaChangerTests.swift; sourceTree = "<group>"; };
 		3717F907221F5D7C00B9BD3D /* CustomAggregationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomAggregationTests.swift; sourceTree = "<group>"; };
 		3D3C3CCB26E5568800759140 /* SQLite.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = SQLite.playground; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
@@ -530,6 +535,7 @@
 				19A175A9CB446640AE6F2200 /* Connection+Aggregation.swift */,
 				3DF7B78728842972005DD8CA /* Connection+Attach.swift */,
 				3DF7B790288449BA005DD8CA /* URIQueryParameter.swift */,
+				19A17F285B767BFACD96714B /* Connection+Pragmas.swift */,
 			);
 			path = Core;
 			sourceTree = "<group>";
@@ -920,6 +926,7 @@
 				19A17FC07731779C1B8506FA /* SchemaChanger.swift in Sources */,
 				19A1740EACD47904AA24B8DC /* SchemaDefinitions.swift in Sources */,
 				19A1750EF4A5F92954A451FF /* Connection+Schema.swift in Sources */,
+				19A17986405D9A875698408F /* Connection+Pragmas.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -993,6 +1000,7 @@
 				19A17DFE05ED8B1F7C45F7EE /* SchemaChanger.swift in Sources */,
 				19A17D1BEABA610ABF003D67 /* SchemaDefinitions.swift in Sources */,
 				19A17A33EA026C2E2CEBAF36 /* Connection+Schema.swift in Sources */,
+				19A178A8B2A34FB6B565DEDA /* Connection+Pragmas.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -1031,6 +1039,7 @@
 				19A1773A335CAB9D0AE14E8E /* SchemaChanger.swift in Sources */,
 				19A17BA13FD35F058787B7D3 /* SchemaDefinitions.swift in Sources */,
 				19A174506543905D71BF0518 /* Connection+Schema.swift in Sources */,
+				19A17018F250343BD0F9F4B0 /* Connection+Pragmas.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -1105,6 +1114,7 @@
 				19A177290558991BCC60E4E3 /* SchemaChanger.swift in Sources */,
 				19A17B0DF1DDB6BBC9C95D64 /* SchemaDefinitions.swift in Sources */,
 				19A17F0BF02896E1664F4090 /* Connection+Schema.swift in Sources */,
+				19A1760CE25615CA015E2E5F /* Connection+Pragmas.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
diff --git a/Sources/SQLite/Core/Connection+Pragmas.swift b/Sources/SQLite/Core/Connection+Pragmas.swift
new file mode 100644
index 00000000..43ff9610
--- /dev/null
+++ b/Sources/SQLite/Core/Connection+Pragmas.swift
@@ -0,0 +1,52 @@
+import Foundation
+
+public typealias UserVersion = Int32
+public typealias SQLiteVersion = (Int, Int, Int)
+
+public extension Connection {
+    /// The user version of the database.
+    /// See SQLite [PRAGMA user_version](https://sqlite.org/pragma.html#pragma_user_version)
+    var userVersion: UserVersion? {
+        get {
+            (try? scalar("PRAGMA user_version") as? Int64).map(Int32.init)
+        }
+        set {
+            _ = try? run("PRAGMA user_version = \(newValue ?? 0)")
+        }
+    }
+
+    /// The version of SQLite.
+    /// See SQLite [sqlite_version()](https://sqlite.org/lang_corefunc.html#sqlite_version)
+    var sqliteVersion: SQLiteVersion {
+        guard let version = (try? scalar("SELECT sqlite_version()")) as? String,
+              let splits = .some(version.split(separator: ".", maxSplits: 3)), splits.count == 3,
+              let major = Int(splits[0]), let minor = Int(splits[1]), let point = Int(splits[2]) else {
+            return (0, 0, 0)
+        }
+        return (major, minor, point)
+    }
+
+    // Changing the foreign_keys setting affects the execution of all statements prepared using the database
+    // connection, including those prepared before the setting was changed.
+    //
+    // https://sqlite.org/pragma.html#pragma_foreign_keys
+    var foreignKeys: Bool {
+        get { getBoolPragma("foreign_keys") }
+        set { setBoolPragma("foreign_keys", newValue) }
+    }
+
+    var deferForeignKeys: Bool {
+        get { getBoolPragma("defer_foreign_keys") }
+        set { setBoolPragma("defer_foreign_keys", newValue) }
+    }
+
+    private func getBoolPragma(_ key: String) -> Bool {
+        guard let binding = try? scalar("PRAGMA \(key)"),
+              let intBinding = binding as? Int64 else { return false }
+        return intBinding == 1
+    }
+
+    private func setBoolPragma(_ key: String, _ newValue: Bool) {
+        _ = try? run("PRAGMA \(key) = \(newValue ? "1" : "0")")
+    }
+}
diff --git a/Sources/SQLite/Core/Connection.swift b/Sources/SQLite/Core/Connection.swift
index 68b83821..0e51e651 100644
--- a/Sources/SQLite/Core/Connection.swift
+++ b/Sources/SQLite/Core/Connection.swift
@@ -156,17 +156,6 @@ public final class Connection {
         Int(sqlite3_total_changes(handle))
     }
 
-    /// The user version of the database.
-    /// See SQLite [PRAGMA user_version](https://sqlite.org/pragma.html#pragma_user_version)
-    public var userVersion: Int32? {
-        get {
-            (try? scalar("PRAGMA user_version") as? Int64).map(Int32.init)
-        }
-        set {
-            _ = try? run("PRAGMA user_version = \(newValue ?? 0)")
-        }
-    }
-
     // MARK: - Execute
 
     /// Executes a batch of SQL statements.
diff --git a/Sources/SQLite/Schema/Connection+Schema.swift b/Sources/SQLite/Schema/Connection+Schema.swift
index c6cf197f..8bd219ed 100644
--- a/Sources/SQLite/Schema/Connection+Schema.swift
+++ b/Sources/SQLite/Schema/Connection+Schema.swift
@@ -1,52 +1,6 @@
 import Foundation
 
 extension Connection {
-    var sqliteVersion: String? {
-        (try? scalar("SELECT sqlite_version()")) as? String
-    }
-
-    var sqliteVersionTriple: (Int, Int, Int) {
-        guard let version = sqliteVersion,
-              let splits = .some(version.split(separator: ".", maxSplits: 3)), splits.count == 3,
-              let major = Int(splits[0]), let minor = Int(splits[1]), let point = Int(splits[2]) else {
-            return (0, 0, 0)
-        }
-        return (major, minor, point)
-    }
-
-    // Changing the foreign_keys setting affects the execution of all statements prepared using the database
-    // connection, including those prepared before the setting was changed.
-    //
-    // https://sqlite.org/pragma.html#pragma_foreign_keys
-    var foreignKeys: Bool {
-        get { getBoolPragma("foreign_keys") }
-        set { setBoolPragma("foreign_keys", newValue) }
-    }
-
-    var deferForeignKeys: Bool {
-        get { getBoolPragma("defer_foreign_keys") }
-        set { setBoolPragma("defer_foreign_keys", newValue) }
-    }
-
-    // https://sqlite.org/pragma.html#pragma_foreign_key_check
-
-    // There are four columns in each result row.
-    // The first column is the name of the table that
-    // contains the REFERENCES clause.
-    // The second column is the rowid of the row that contains the
-    // invalid REFERENCES clause, or NULL if the child table is a WITHOUT ROWID table.
-    // The third column is the name of the table that is referred to.
-    // The fourth column is the index of the specific foreign key constraint that failed.
-    func foreignKeyCheck() throws -> [ForeignKeyError] {
-        try run("PRAGMA foreign_key_check").compactMap { row -> ForeignKeyError? in
-            guard let table = row[0] as? String,
-                  let rowId = row[1] as? Int64,
-                  let target = row[2] as? String else { return nil }
-
-            return ForeignKeyError(from: table, rowId: rowId, to: target)
-        }
-    }
-
     // https://sqlite.org/pragma.html#pragma_table_info
     //
     // This pragma returns one row for each column in the named table. Columns in the result set include the
@@ -58,7 +12,7 @@ extension Connection {
             try createTableSQL(name: table).flatMap { .init(sql: $0) }
         }
 
-        let foreignKeys: [String: [ForeignKeyDefinition]] =
+        let foreignKeys: [String: [ColumnDefinition.ForeignKey]] =
             Dictionary(grouping: try foreignKeyInfo(table: table), by: { $0.column })
 
         return try run("PRAGMA table_info(\(table.quote()))").compactMap { row -> ColumnDefinition? in
@@ -71,7 +25,7 @@ extension Connection {
                                     primaryKey: primaryKey == 1 ? try parsePrimaryKey(column: name) : nil,
                                     type: ColumnDefinition.Affinity.from(type),
                                     null: notNull == 0,
-                                    defaultValue: LiteralValue.from(defaultValue),
+                                    defaultValue: .from(defaultValue),
                                     references: foreignKeys[name]?.first)
         }
     }
@@ -119,16 +73,16 @@ extension Connection {
         }
     }
 
-    func foreignKeyInfo(table: String) throws -> [ForeignKeyDefinition] {
+    func foreignKeyInfo(table: String) throws -> [ColumnDefinition.ForeignKey] {
         try run("PRAGMA foreign_key_list(\(table.quote()))").compactMap { row in
             if let table = row[2] as? String,      // table
                let column = row[3] as? String,     // from
                let primaryKey = row[4] as? String, // to
                let onUpdate = row[5] as? String,
                let onDelete = row[6] as? String {
-                return ForeignKeyDefinition(table: table, column: column, primaryKey: primaryKey,
-                                            onUpdate: onUpdate == TableBuilder.Dependency.noAction.rawValue ? nil : onUpdate,
-                                            onDelete: onDelete == TableBuilder.Dependency.noAction.rawValue ? nil : onDelete
+                return .init(table: table, column: column, primaryKey: primaryKey,
+                             onUpdate: onUpdate == TableBuilder.Dependency.noAction.rawValue ? nil : onUpdate,
+                             onDelete: onDelete == TableBuilder.Dependency.noAction.rawValue ? nil : onDelete
                 )
             } else {
                 return nil
@@ -136,6 +90,25 @@ extension Connection {
         }
     }
 
+    // https://sqlite.org/pragma.html#pragma_foreign_key_check
+
+    // There are four columns in each result row.
+    // The first column is the name of the table that
+    // contains the REFERENCES clause.
+    // The second column is the rowid of the row that contains the
+    // invalid REFERENCES clause, or NULL if the child table is a WITHOUT ROWID table.
+    // The third column is the name of the table that is referred to.
+    // The fourth column is the index of the specific foreign key constraint that failed.
+    func foreignKeyCheck() throws -> [ForeignKeyError] {
+        try run("PRAGMA foreign_key_check").compactMap { row -> ForeignKeyError? in
+            guard let table = row[0] as? String,
+                  let rowId = row[1] as? Int64,
+                  let target = row[2] as? String else { return nil }
+
+            return ForeignKeyError(from: table, rowId: rowId, to: target)
+        }
+    }
+
     private func createTableSQL(name: String) throws  -> String? {
         try run("""
                 SELECT sql FROM sqlite_master WHERE name=? AND type='table'
@@ -145,14 +118,4 @@ extension Connection {
             .compactMap { row in row[0] as? String }
             .first
     }
-
-    private func getBoolPragma(_ key: String) -> Bool {
-        guard let binding = try? scalar("PRAGMA \(key)"),
-              let intBinding = binding as? Int64 else { return false }
-        return intBinding == 1
-    }
-
-    private func setBoolPragma(_ key: String, _ newValue: Bool) {
-        _ = try? run("PRAGMA \(key) = \(newValue ? "1" : "0")")
-    }
 }
diff --git a/Sources/SQLite/Schema/SchemaChanger.swift b/Sources/SQLite/Schema/SchemaChanger.swift
index cd6b6f25..c6529fbe 100644
--- a/Sources/SQLite/Schema/SchemaChanger.swift
+++ b/Sources/SQLite/Schema/SchemaChanger.swift
@@ -27,8 +27,6 @@ import Foundation
        12. If foreign keys constraints were originally enabled, reenable them now.
 */
 public class SchemaChanger: CustomStringConvertible {
-    typealias SQLiteVersion = (Int, Int, Int)
-
     enum SchemaChangeError: LocalizedError {
         case foreignKeyError([ForeignKeyError])
 
@@ -52,9 +50,9 @@ public class SchemaChanger: CustomStringConvertible {
             switch self {
             case .add(let definition):
                 return "ALTER TABLE \(table.quote()) ADD COLUMN \(definition.toSQL())"
-            case .renameColumn(let from, let to) where version.0 >= 3 && version.1 >= 25:
+            case .renameColumn(let from, let to) where version >= (3, 25, 0):
                 return "ALTER TABLE \(table.quote()) RENAME COLUMN \(from.quote()) TO \(to.quote())"
-            case .remove(let column) where version.0 >= 3 && version.1 >= 35:
+            case .remove(let column) where version >= (3, 35, 0):
                 return "ALTER TABLE \(table.quote()) DROP COLUMN \(column.quote())"
             default: return nil
             }
@@ -97,7 +95,7 @@ public class SchemaChanger: CustomStringConvertible {
 
     public convenience init(connection: Connection) {
         self.init(connection: connection,
-                  version: connection.sqliteVersionTriple)
+                  version: connection.sqliteVersion)
     }
 
     init(connection: Connection, version: SQLiteVersion) {
diff --git a/Sources/SQLite/Schema/SchemaDefinitions.swift b/Sources/SQLite/Schema/SchemaDefinitions.swift
index 563ae331..156a06fb 100644
--- a/Sources/SQLite/Schema/SchemaDefinitions.swift
+++ b/Sources/SQLite/Schema/SchemaDefinitions.swift
@@ -18,14 +18,14 @@ public struct ColumnDefinition: Equatable {
     // The important idea here is that the type is recommended, not required. Any column can still
     // store any type of data. It is just that some columns, given the choice, will prefer to use one
     // storage class over another. The preferred storage class for a column is called its "affinity".
-    enum Affinity: String, CustomStringConvertible, CaseIterable {
+    public enum Affinity: String, CustomStringConvertible, CaseIterable {
         case INTEGER
         case NUMERIC
         case REAL
         case TEXT
         case BLOB
 
-        var description: String {
+        public var description: String {
             rawValue
         }
 
@@ -34,7 +34,7 @@ public struct ColumnDefinition: Equatable {
         }
     }
 
-    enum OnConflict: String, CaseIterable {
+    public enum OnConflict: String, CaseIterable {
         case ROLLBACK
         case ABORT
         case FAIL
@@ -46,7 +46,7 @@ public struct ColumnDefinition: Equatable {
         }
     }
 
-    struct PrimaryKey: Equatable {
+    public struct PrimaryKey: Equatable {
         let autoIncrement: Bool
         let onConflict: OnConflict?
 
@@ -73,12 +73,30 @@ public struct ColumnDefinition: Equatable {
         }
     }
 
-    let name: String
-    let primaryKey: PrimaryKey?
-    let type: Affinity
-    let null: Bool
-    let defaultValue: LiteralValue
-    let references: ForeignKeyDefinition?
+    public struct ForeignKey: Equatable {
+        let table: String
+        let column: String
+        let primaryKey: String
+        let onUpdate: String?
+        let onDelete: String?
+    }
+
+    public let name: String
+    public let primaryKey: PrimaryKey?
+    public let type: Affinity
+    public let null: Bool
+    public let defaultValue: LiteralValue
+    public let references: ForeignKey?
+
+    public init(name: String, primaryKey: PrimaryKey?, type: Affinity, null: Bool, defaultValue: LiteralValue,
+                references: ForeignKey?) {
+        self.name = name
+        self.primaryKey = primaryKey
+        self.type = type
+        self.null = null
+        self.defaultValue = defaultValue
+        self.references = references
+    }
 
     func rename(from: String, to: String) -> ColumnDefinition {
         guard from == name else { return self }
@@ -86,7 +104,7 @@ public struct ColumnDefinition: Equatable {
     }
 }
 
-enum LiteralValue: Equatable, CustomStringConvertible {
+public enum LiteralValue: Equatable, CustomStringConvertible {
     // swiftlint:disable force_try
     private static let singleQuote = try! NSRegularExpression(pattern: "^'(.*)'$")
     private static let doubleQuote = try! NSRegularExpression(pattern: "^\"(.*)\"$")
@@ -141,7 +159,7 @@ enum LiteralValue: Equatable, CustomStringConvertible {
         }
     }
 
-    var description: String {
+    public var description: String {
         switch self {
         case .NULL: return "NULL"
         case .TRUE: return "TRUE"
@@ -166,7 +184,7 @@ enum LiteralValue: Equatable, CustomStringConvertible {
 
 // https://sqlite.org/lang_createindex.html
 // schema-name.index-name ON table-name ( indexed-column+ ) WHERE expr
-struct IndexDefinition: Equatable {
+public struct IndexDefinition: Equatable {
     // SQLite supports index names up to 64 characters.
     static let maxIndexLength = 64
 
@@ -175,9 +193,9 @@ struct IndexDefinition: Equatable {
     static let orderRe = try! NSRegularExpression(pattern: "\"?(\\w+)\"? DESC")
     // swiftlint:enable force_try
 
-    enum Order: String { case ASC, DESC }
+    public enum Order: String { case ASC, DESC }
 
-    init(table: String, name: String, unique: Bool = false, columns: [String], `where`: String? = nil, orders: [String: Order]? = nil) {
+    public init(table: String, name: String, unique: Bool = false, columns: [String], `where`: String? = nil, orders: [String: Order]? = nil) {
         self.table = table
         self.name = name
         self.unique = unique
@@ -236,14 +254,6 @@ struct IndexDefinition: Equatable {
     }
 }
 
-struct ForeignKeyDefinition: Equatable {
-    let table: String
-    let column: String
-    let primaryKey: String
-    let onUpdate: String?
-    let onDelete: String?
-}
-
 struct ForeignKeyError: CustomStringConvertible {
     let from: String
     let rowId: Int64
@@ -292,7 +302,7 @@ extension ColumnDefinition {
 }
 
 extension IndexDefinition {
-    func toSQL(ifNotExists: Bool = false) -> String {
+    public func toSQL(ifNotExists: Bool = false) -> String {
         let commaSeparatedColumns = columns.map { (column: String) -> String in
             column.quote() + (orders?[column].map { " \($0.rawValue)" } ?? "")
         }.joined(separator: ", ")
@@ -312,7 +322,7 @@ extension IndexDefinition {
     }
 }
 
-extension ForeignKeyDefinition {
+extension ColumnDefinition.ForeignKey {
     func toSQL() -> String {
         ([
             "REFERENCES",
diff --git a/Tests/SQLiteTests/Schema/ConnectionSchemaTests.swift b/Tests/SQLiteTests/Schema/ConnectionSchemaTests.swift
index 9fbad26c..1c66b761 100644
--- a/Tests/SQLiteTests/Schema/ConnectionSchemaTests.swift
+++ b/Tests/SQLiteTests/Schema/ConnectionSchemaTests.swift
@@ -133,14 +133,11 @@ class ConnectionSchemaTests: SQLiteTestCase {
 
         let foreignKeys = try db.foreignKeyInfo(table: "test_links")
         XCTAssertEqual(foreignKeys, [
-            ForeignKeyDefinition(table: "users", column: "test_id", primaryKey: "id", onUpdate: nil, onDelete: nil)
+            .init(table: "users", column: "test_id", primaryKey: "id", onUpdate: nil, onDelete: nil)
         ])
     }
 
-    func test_sqlite_version_triple() {
-        let version = db.sqliteVersionTriple
-        XCTAssertEqual(version.0, 3)
-        XCTAssertGreaterThan(version.1, 0)
-        XCTAssertGreaterThanOrEqual(version.2, 0)
+    func test_sqlite_version() {
+        XCTAssertTrue(db.sqliteVersion >= (3, 0, 0))
     }
 }
diff --git a/Tests/SQLiteTests/Schema/SchemaDefinitionsTests.swift b/Tests/SQLiteTests/Schema/SchemaDefinitionsTests.swift
index c19840bc..645bfc34 100644
--- a/Tests/SQLiteTests/Schema/SchemaDefinitionsTests.swift
+++ b/Tests/SQLiteTests/Schema/SchemaDefinitionsTests.swift
@@ -10,7 +10,7 @@ class ColumnDefinitionTests: XCTestCase {
         "\"id\" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL"),
 
         (ColumnDefinition(name: "other_id", primaryKey: nil, type: .INTEGER, null: false, defaultValue: .NULL,
-                          references: ForeignKeyDefinition(table: "other_table", column: "", primaryKey: "some_id", onUpdate: nil, onDelete: nil)),
+                          references: .init(table: "other_table", column: "", primaryKey: "some_id", onUpdate: nil, onDelete: nil)),
         "\"other_id\" INTEGER NOT NULL REFERENCES \"other_table\" (\"some_id\")"),
 
         (ColumnDefinition(name: "text", primaryKey: nil, type: .TEXT, null: true, defaultValue: .NULL, references: nil),
@@ -168,7 +168,7 @@ class IndexDefinitionTests: XCTestCase {
 class ForeignKeyDefinitionTests: XCTestCase {
     func test_toSQL() {
         XCTAssertEqual(
-            ForeignKeyDefinition(
+            ColumnDefinition.ForeignKey(
                 table: "foo",
                 column: "bar",
                 primaryKey: "bar_id",

From f57c225bad580ddfb22149f8d86e8e12ddca3dbf Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Mon, 25 Jul 2022 01:04:28 +0200
Subject: [PATCH 186/326] Move tests into separate directories

---
 SQLite.xcodeproj/project.pbxproj              | 424 ++++++++++--------
 Sources/SQLite/Schema/Connection+Schema.swift |  10 -
 Tests/SQLiteTests/{ => Core}/BlobTests.swift  |   0
 .../Core/Connection+AttachTests.swift         |  62 +++
 .../Core/Connection+PragmaTests.swift         |  42 ++
 .../{ => Core}/ConnectionTests.swift          |  52 ---
 .../{ => Core}/CoreFunctionsTests.swift       |   0
 .../SQLiteTests/{ => Core}/ResultTests.swift  |   0
 .../{ => Core}/StatementTests.swift           |   0
 Tests/SQLiteTests/{ => Core}/ValueTests.swift |   0
 .../{ => Extensions}/CipherTests.swift        |   0
 .../{ => Extensions}/FTS4Tests.swift          |   0
 .../{ => Extensions}/FTS5Tests.swift          |   0
 .../FTSIntegrationTests.swift                 |   0
 .../{ => Extensions}/RTreeTests.swift         |   0
 ...sts.swift => Connection+SchemaTests.swift} |   9 -
 .../Schema/SchemaChangerTests.swift           |  10 +-
 .../{ => Schema}/SchemaTests.swift            |   0
 .../{ => Typed}/AggregateFunctionsTests.swift |   0
 .../{ => Typed}/CustomAggregationTests.swift  |   0
 .../{ => Typed}/CustomFunctionsTests.swift    |   0
 .../DateAndTimeFunctionTests.swift            |   0
 .../{ => Typed}/ExpressionTests.swift         |   0
 .../{ => Typed}/OperatorsTests.swift          |   0
 .../{ => Typed}/QueryIntegrationTests.swift   |   0
 .../SQLiteTests/{ => Typed}/QueryTests.swift  |   0
 Tests/SQLiteTests/{ => Typed}/RowTests.swift  |   0
 .../SQLiteTests/{ => Typed}/SelectTests.swift |   0
 .../SQLiteTests/{ => Typed}/SetterTests.swift |   0
 29 files changed, 343 insertions(+), 266 deletions(-)
 rename Tests/SQLiteTests/{ => Core}/BlobTests.swift (100%)
 create mode 100644 Tests/SQLiteTests/Core/Connection+AttachTests.swift
 create mode 100644 Tests/SQLiteTests/Core/Connection+PragmaTests.swift
 rename Tests/SQLiteTests/{ => Core}/ConnectionTests.swift (90%)
 rename Tests/SQLiteTests/{ => Core}/CoreFunctionsTests.swift (100%)
 rename Tests/SQLiteTests/{ => Core}/ResultTests.swift (100%)
 rename Tests/SQLiteTests/{ => Core}/StatementTests.swift (100%)
 rename Tests/SQLiteTests/{ => Core}/ValueTests.swift (100%)
 rename Tests/SQLiteTests/{ => Extensions}/CipherTests.swift (100%)
 rename Tests/SQLiteTests/{ => Extensions}/FTS4Tests.swift (100%)
 rename Tests/SQLiteTests/{ => Extensions}/FTS5Tests.swift (100%)
 rename Tests/SQLiteTests/{ => Extensions}/FTSIntegrationTests.swift (100%)
 rename Tests/SQLiteTests/{ => Extensions}/RTreeTests.swift (100%)
 rename Tests/SQLiteTests/Schema/{ConnectionSchemaTests.swift => Connection+SchemaTests.swift} (95%)
 rename Tests/SQLiteTests/{ => Schema}/SchemaTests.swift (100%)
 rename Tests/SQLiteTests/{ => Typed}/AggregateFunctionsTests.swift (100%)
 rename Tests/SQLiteTests/{ => Typed}/CustomAggregationTests.swift (100%)
 rename Tests/SQLiteTests/{ => Typed}/CustomFunctionsTests.swift (100%)
 rename Tests/SQLiteTests/{ => Typed}/DateAndTimeFunctionTests.swift (100%)
 rename Tests/SQLiteTests/{ => Typed}/ExpressionTests.swift (100%)
 rename Tests/SQLiteTests/{ => Typed}/OperatorsTests.swift (100%)
 rename Tests/SQLiteTests/{ => Typed}/QueryIntegrationTests.swift (100%)
 rename Tests/SQLiteTests/{ => Typed}/QueryTests.swift (100%)
 rename Tests/SQLiteTests/{ => Typed}/RowTests.swift (100%)
 rename Tests/SQLiteTests/{ => Typed}/SelectTests.swift (100%)
 rename Tests/SQLiteTests/{ => Typed}/SetterTests.swift (100%)

diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj
index a97ef184..bc79473e 100644
--- a/SQLite.xcodeproj/project.pbxproj
+++ b/SQLite.xcodeproj/project.pbxproj
@@ -30,96 +30,136 @@
 		03A65E841C6BB2FB0062603F /* Query.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B001C3F06E900AE3E12 /* Query.swift */; };
 		03A65E851C6BB2FB0062603F /* Schema.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B011C3F06E900AE3E12 /* Schema.swift */; };
 		03A65E861C6BB2FB0062603F /* Setter.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B021C3F06E900AE3E12 /* Setter.swift */; };
-		03A65E871C6BB3030062603F /* AggregateFunctionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B1A1C3F137700AE3E12 /* AggregateFunctionsTests.swift */; };
-		03A65E881C6BB3030062603F /* BlobTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B1B1C3F137700AE3E12 /* BlobTests.swift */; };
-		03A65E891C6BB3030062603F /* ConnectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B1D1C3F137700AE3E12 /* ConnectionTests.swift */; };
-		03A65E8A1C6BB3030062603F /* CoreFunctionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B1E1C3F137700AE3E12 /* CoreFunctionsTests.swift */; };
-		03A65E8B1C6BB3030062603F /* CustomFunctionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B1F1C3F137700AE3E12 /* CustomFunctionsTests.swift */; };
-		03A65E8C1C6BB3030062603F /* ExpressionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B201C3F137700AE3E12 /* ExpressionTests.swift */; };
-		03A65E8D1C6BB3030062603F /* FTS4Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B211C3F137700AE3E12 /* FTS4Tests.swift */; };
-		03A65E8E1C6BB3030062603F /* OperatorsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B2A1C3F141E00AE3E12 /* OperatorsTests.swift */; };
-		03A65E8F1C6BB3030062603F /* QueryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B2B1C3F141E00AE3E12 /* QueryTests.swift */; };
-		03A65E901C6BB3030062603F /* RTreeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B2C1C3F141E00AE3E12 /* RTreeTests.swift */; };
-		03A65E911C6BB3030062603F /* SchemaTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B2D1C3F141E00AE3E12 /* SchemaTests.swift */; };
-		03A65E921C6BB3030062603F /* SetterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B181C3F134A00AE3E12 /* SetterTests.swift */; };
-		03A65E931C6BB3030062603F /* StatementTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B321C3F142E00AE3E12 /* StatementTests.swift */; };
-		03A65E941C6BB3030062603F /* ValueTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B331C3F142E00AE3E12 /* ValueTests.swift */; };
 		03A65E951C6BB3030062603F /* TestHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B161C3F127200AE3E12 /* TestHelpers.swift */; };
 		03A65E971C6BB3210062603F /* libsqlite3.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 03A65E961C6BB3210062603F /* libsqlite3.tbd */; };
 		19A17018F250343BD0F9F4B0 /* Connection+Pragmas.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17F285B767BFACD96714B /* Connection+Pragmas.swift */; };
+		19A17021286A4D8D6C2EF12D /* ConnectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17855BD524FF888265B3C /* ConnectionTests.swift */; };
+		19A17026DCDCDA405B09A229 /* Connection+AttachTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A170C08525D3D27CB5F83C /* Connection+AttachTests.swift */; };
 		19A17073552293CA063BEA66 /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17E723300E5ED3771DCB5 /* Result.swift */; };
+		19A1708D3D58D7BC1168E55F /* CipherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1787E16C8562C09C076F5 /* CipherTests.swift */; };
 		19A1709C3E7A406E62293B2A /* Fixtures.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17B93B48B5560E6E51791 /* Fixtures.swift */; };
 		19A170ACC97B19730FB7BA4D /* Connection+Aggregation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A175A9CB446640AE6F2200 /* Connection+Aggregation.swift */; };
+		19A170AEBAA56DC3355A73B3 /* DateAndTimeFunctionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A178AEF32ABC8BF2993FB5 /* DateAndTimeFunctionTests.swift */; };
+		19A170C56745F9D722A73D77 /* AggregateFunctionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17613F00A3F4D4A257A04 /* AggregateFunctionsTests.swift */; };
+		19A170D938343E30119EDFB3 /* RowTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A174FE5B47A97937A27276 /* RowTests.swift */; };
+		19A1714F7CF964D568AB14E0 /* BlobTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A171A2ED4E2640F197F48C /* BlobTests.swift */; };
 		19A17152E32A9585831E3FE0 /* DateAndTimeFunctions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17BA55DABB480F9020C8A /* DateAndTimeFunctions.swift */; };
+		19A1716BF8E15F91A6B5CB7A /* CustomFunctionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17F2B5959B212A33C383F /* CustomFunctionsTests.swift */; };
 		19A1717B10CC941ACB5533D6 /* FTS5.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1730E4390C775C25677D1 /* FTS5.swift */; };
-		19A17188B4D96636F9C0C209 /* ConnectionSchemaTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17CA1DF7D0F7C9A94C51C /* ConnectionSchemaTests.swift */; };
-		19A171967CC511C4F6F773C9 /* RowTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A175C1F9CB3BBAB8FCEC7B /* RowTests.swift */; };
-		19A171BE056457F13BFBC4C3 /* FTSIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A171FDE4D67879B14ACBDF /* FTSIntegrationTests.swift */; };
-		19A171E6FA242F72A308C594 /* FTS5Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1721B8984686B9963B45D /* FTS5Tests.swift */; };
+		19A17188B4D96636F9C0C209 /* Connection+SchemaTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17CA1DF7D0F7C9A94C51C /* Connection+SchemaTests.swift */; };
 		19A171F12AB8B07F2FD7201A /* Cipher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A178A39ACA9667A62663CC /* Cipher.swift */; };
-		19A1720B67ED13E6150C6A3D /* RowTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A175C1F9CB3BBAB8FCEC7B /* RowTests.swift */; };
-		19A17254FBA7894891F7297B /* FTS5Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1721B8984686B9963B45D /* FTS5Tests.swift */; };
-		19A1725658E480B9B378F28B /* ConnectionSchemaTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17CA1DF7D0F7C9A94C51C /* ConnectionSchemaTests.swift */; };
-		19A172A9B536D02D4A98AAAD /* QueryIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17BA6B4E282C1315A115C /* QueryIntegrationTests.swift */; };
-		19A172EB202970561E5C4245 /* DateAndTimeFunctionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1729B75C33F9A0B9A89C1 /* DateAndTimeFunctionTests.swift */; };
+		19A171F243A589C5EBC47937 /* OperatorsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17475DCA068453F787613 /* OperatorsTests.swift */; };
+		19A1725658E480B9B378F28B /* Connection+SchemaTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17CA1DF7D0F7C9A94C51C /* Connection+SchemaTests.swift */; };
+		19A1726002D24C14F876C8FE /* ValueTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17AE284BB1DF31D1B753E /* ValueTests.swift */; };
+		19A172F71EFD65342072D8D2 /* SchemaTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A171A7714C6524093255C5 /* SchemaTests.swift */; };
+		19A173088B85A7E18E8582A7 /* FTSIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17BE1CC4AD4036BAB8EE0 /* FTSIntegrationTests.swift */; };
+		19A173389E53CB24DFA8CEDD /* QueryIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17CAE60446607E99C22A6 /* QueryIntegrationTests.swift */; };
+		19A173465F23C64DF3DF469B /* ConnectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17855BD524FF888265B3C /* ConnectionTests.swift */; };
 		19A173668D948AD4DF1F5352 /* DateAndTimeFunctions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17BA55DABB480F9020C8A /* DateAndTimeFunctions.swift */; };
 		19A1737286A74F3CF7412906 /* DateAndTimeFunctions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17BA55DABB480F9020C8A /* DateAndTimeFunctions.swift */; };
 		19A173EFEF0B3BD0B3ED406C /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17E723300E5ED3771DCB5 /* Result.swift */; };
+		19A173F25449876761347072 /* Connection+AttachTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A170C08525D3D27CB5F83C /* Connection+AttachTests.swift */; };
+		19A173F429D7E46289EB2167 /* ResultTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1745BE8623D8C6808DB3C /* ResultTests.swift */; };
 		19A17408007B182F884E3A53 /* Fixtures.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17B93B48B5560E6E51791 /* Fixtures.swift */; };
 		19A1740EACD47904AA24B8DC /* SchemaDefinitions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17268AE67B746B96AC125 /* SchemaDefinitions.swift */; };
+		19A17411403D60640467209E /* ExpressionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A170F141BF21946D159083 /* ExpressionTests.swift */; };
+		19A174118D11B93DA5DAAF79 /* DateAndTimeFunctionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A178AEF32ABC8BF2993FB5 /* DateAndTimeFunctionTests.swift */; };
+		19A17437659BD7FD787D94A6 /* CustomAggregationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A177EF5E2D91BA86DA4480 /* CustomAggregationTests.swift */; };
+		19A17444861E1443143DEB44 /* FTS4Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1715904F7B6851FCB5EF6 /* FTS4Tests.swift */; };
 		19A174506543905D71BF0518 /* Connection+Schema.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A170A97B51DC5EE365F3C5 /* Connection+Schema.swift */; };
+		19A17457B0461F484AF6BE40 /* CustomFunctionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17F2B5959B212A33C383F /* CustomFunctionsTests.swift */; };
+		19A17482E6FC5E563F3E6A47 /* OperatorsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17475DCA068453F787613 /* OperatorsTests.swift */; };
 		19A17490543609FCED53CACC /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1710E73A46D5AC721CDA9 /* Errors.swift */; };
-		19A174D78559CD30679BCCCB /* FTS5Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1721B8984686B9963B45D /* FTS5Tests.swift */; };
 		19A1750CEE9B05267995CF3D /* FTS5.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1730E4390C775C25677D1 /* FTS5.swift */; };
 		19A1750EF4A5F92954A451FF /* Connection+Schema.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A170A97B51DC5EE365F3C5 /* Connection+Schema.swift */; };
+		19A1755C49154C87304C9146 /* FTSIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17BE1CC4AD4036BAB8EE0 /* FTSIntegrationTests.swift */; };
 		19A1760CE25615CA015E2E5F /* Connection+Pragmas.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17F285B767BFACD96714B /* Connection+Pragmas.swift */; };
 		19A176376CB6A94759F7980A /* Connection+Aggregation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A175A9CB446640AE6F2200 /* Connection+Aggregation.swift */; };
-		19A176406BDE9D9C80CC9FA3 /* QueryIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17BA6B4E282C1315A115C /* QueryIntegrationTests.swift */; };
+		19A1766135CE9786B1878603 /* ValueTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17AE284BB1DF31D1B753E /* ValueTests.swift */; };
 		19A1766AC10D13C4EFF349AD /* SchemaChangerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17FDB0B0CFB8987906FD0 /* SchemaChangerTests.swift */; };
-		19A1769C1F3A7542BECF50FF /* DateAndTimeFunctionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1729B75C33F9A0B9A89C1 /* DateAndTimeFunctionTests.swift */; };
+		19A176B3316281F004F92276 /* RowTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A174FE5B47A97937A27276 /* RowTests.swift */; };
 		19A177290558991BCC60E4E3 /* SchemaChanger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A171B262DDE8718513CFDA /* SchemaChanger.swift */; };
+		19A1772EBE65173EDFB1AFCA /* CustomAggregationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A177EF5E2D91BA86DA4480 /* CustomAggregationTests.swift */; };
+		19A1773155AC2BF2CA86A473 /* SetterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1709D5BDD2691BA160012 /* SetterTests.swift */; };
 		19A1773A335CAB9D0AE14E8E /* SchemaChanger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A171B262DDE8718513CFDA /* SchemaChanger.swift */; };
+		19A17746150A815944A6820B /* SelectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17EC0C43015063945D32E /* SelectTests.swift */; };
+		19A1776BD5127DFDF847FF1F /* FTS5Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17B96EBD42C878E609CDC /* FTS5Tests.swift */; };
+		19A177909023B7B940C5805E /* FTSIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17BE1CC4AD4036BAB8EE0 /* FTSIntegrationTests.swift */; };
+		19A177AA5922527BBDC77CF9 /* QueryIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17CAE60446607E99C22A6 /* QueryIntegrationTests.swift */; };
 		19A177C25834473FAB32CF3B /* SchemaChangerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17FDB0B0CFB8987906FD0 /* SchemaChangerTests.swift */; };
-		19A177CC33F2E6A24AF90B02 /* CipherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17399EA9E61235D5D77BF /* CipherTests.swift */; };
+		19A177D5C6542E2D572162E5 /* QueryIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17CAE60446607E99C22A6 /* QueryIntegrationTests.swift */; };
 		19A178072B371489E6A1E839 /* FoundationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1794CC4D7827E997E32A7 /* FoundationTests.swift */; };
+		19A1781CBA8968ABD3E00877 /* ExpressionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A170F141BF21946D159083 /* ExpressionTests.swift */; };
+		19A1782444437C7FC6B75CBC /* BlobTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A171A2ED4E2640F197F48C /* BlobTests.swift */; };
 		19A17835FD5886FDC5A3228F /* Cipher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A178A39ACA9667A62663CC /* Cipher.swift */; };
-		19A1785195182AF8731A8BDA /* RowTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A175C1F9CB3BBAB8FCEC7B /* RowTests.swift */; };
+		19A178767223229E61C5066F /* FTS4Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1715904F7B6851FCB5EF6 /* FTS4Tests.swift */; };
+		19A17885B646CB0201BE4BD5 /* QueryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A171ED017645C8B04DF9F2 /* QueryTests.swift */; };
 		19A178A8B2A34FB6B565DEDA /* Connection+Pragmas.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17F285B767BFACD96714B /* Connection+Pragmas.swift */; };
+		19A178C041DDCF80B533AD13 /* BlobTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A171A2ED4E2640F197F48C /* BlobTests.swift */; };
+		19A178DA2BB5970778CCAF13 /* FTS5Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17B96EBD42C878E609CDC /* FTS5Tests.swift */; };
+		19A178DF5A96CFEFF1E271F6 /* AggregateFunctionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17613F00A3F4D4A257A04 /* AggregateFunctionsTests.swift */; };
+		19A178F9008614B8A8425635 /* Connection+PragmaTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17C60D3464461057E4D63 /* Connection+PragmaTests.swift */; };
+		19A17900387FDCF578B31E3E /* OperatorsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17475DCA068453F787613 /* OperatorsTests.swift */; };
+		19A17912DB9D3AC8FECF948B /* ResultTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1745BE8623D8C6808DB3C /* ResultTests.swift */; };
+		19A17923494236793893BF72 /* StatementTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1756EB81E9F7F45B12A78 /* StatementTests.swift */; };
 		19A1792C0520D4E83C2EB075 /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1710E73A46D5AC721CDA9 /* Errors.swift */; };
+		19A1793972BDDDB027C113BB /* CustomAggregationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A177EF5E2D91BA86DA4480 /* CustomAggregationTests.swift */; };
+		19A179786A6826D58A70F8BC /* AggregateFunctionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17613F00A3F4D4A257A04 /* AggregateFunctionsTests.swift */; };
 		19A17986405D9A875698408F /* Connection+Pragmas.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17F285B767BFACD96714B /* Connection+Pragmas.swift */; };
-		19A179A0C45377CB09BB358C /* CipherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17399EA9E61235D5D77BF /* CipherTests.swift */; };
+		19A1799AF6643CF5081BFA15 /* DateAndTimeFunctionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A178AEF32ABC8BF2993FB5 /* DateAndTimeFunctionTests.swift */; };
 		19A179B59450FE7C4811AB8A /* Connection+Aggregation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A175A9CB446640AE6F2200 /* Connection+Aggregation.swift */; };
+		19A179BB9A6665B2B99DA546 /* CoreFunctionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17B55F2409B7031443495 /* CoreFunctionsTests.swift */; };
+		19A179BCD483DEA21661FD37 /* Connection+AttachTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A170C08525D3D27CB5F83C /* Connection+AttachTests.swift */; };
 		19A179CCF9671E345E5A9811 /* Cipher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A178A39ACA9667A62663CC /* Cipher.swift */; };
 		19A179E76EA6207669B60C1B /* Cipher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A178A39ACA9667A62663CC /* Cipher.swift */; };
 		19A17A33EA026C2E2CEBAF36 /* Connection+Schema.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A170A97B51DC5EE365F3C5 /* Connection+Schema.swift */; };
+		19A17A391BF056E3D729E70A /* SchemaTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A171A7714C6524093255C5 /* SchemaTests.swift */; };
+		19A17A52BF29D27C9AA229E7 /* QueryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A171ED017645C8B04DF9F2 /* QueryTests.swift */; };
+		19A17A7B3E3B7E76364A2AEE /* CipherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1787E16C8562C09C076F5 /* CipherTests.swift */; };
+		19A17A7DF99B0379FD3396B1 /* CustomFunctionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17F2B5959B212A33C383F /* CustomFunctionsTests.swift */; };
+		19A17A9520802ACF45907970 /* RTreeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17162C9861E5C4900455D /* RTreeTests.swift */; };
+		19A17ABCF0EB4808BDC5B5FF /* RowTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A174FE5B47A97937A27276 /* RowTests.swift */; };
 		19A17B0DF1DDB6BBC9C95D64 /* SchemaDefinitions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17268AE67B746B96AC125 /* SchemaDefinitions.swift */; };
-		19A17B36ABC6006AB80F693C /* ConnectionSchemaTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17CA1DF7D0F7C9A94C51C /* ConnectionSchemaTests.swift */; };
+		19A17B1D9B5CEBE9CE09280C /* RTreeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17162C9861E5C4900455D /* RTreeTests.swift */; };
+		19A17B36ABC6006AB80F693C /* Connection+SchemaTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17CA1DF7D0F7C9A94C51C /* Connection+SchemaTests.swift */; };
 		19A17B62A4125AF4F6014CF5 /* SchemaDefinitionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A176174862D0F0139B3987 /* SchemaDefinitionsTests.swift */; };
 		19A17BA13FD35F058787B7D3 /* SchemaDefinitions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17268AE67B746B96AC125 /* SchemaDefinitions.swift */; };
-		19A17C013B00682B70D53DB8 /* FTSIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A171FDE4D67879B14ACBDF /* FTSIntegrationTests.swift */; };
-		19A17C4B951CB054EE48AB1C /* CipherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17399EA9E61235D5D77BF /* CipherTests.swift */; };
-		19A17C80076860CF7751A056 /* DateAndTimeFunctionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1729B75C33F9A0B9A89C1 /* DateAndTimeFunctionTests.swift */; };
-		19A17C9407AC0EE104E5CC85 /* FTSIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A171FDE4D67879B14ACBDF /* FTSIntegrationTests.swift */; };
-		19A17CB808ACF606726E77A8 /* QueryIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17BA6B4E282C1315A115C /* QueryIntegrationTests.swift */; };
+		19A17BACF4C032513DE1F879 /* Connection+PragmaTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17C60D3464461057E4D63 /* Connection+PragmaTests.swift */; };
+		19A17C74233AFC2EDAFA23DC /* ResultTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1745BE8623D8C6808DB3C /* ResultTests.swift */; };
+		19A17CA4D7B63D845428A9C5 /* StatementTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1756EB81E9F7F45B12A78 /* StatementTests.swift */; };
+		19A17CA6ADB78A2E545BF836 /* CipherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1787E16C8562C09C076F5 /* CipherTests.swift */; };
+		19A17CF65C0196E03BC64519 /* ConnectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17855BD524FF888265B3C /* ConnectionTests.swift */; };
 		19A17D1BEABA610ABF003D67 /* SchemaDefinitions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17268AE67B746B96AC125 /* SchemaDefinitions.swift */; };
+		19A17D6EC40BC35A5DC81BA8 /* StatementTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1756EB81E9F7F45B12A78 /* StatementTests.swift */; };
+		19A17D993398B8215B73E1EA /* CoreFunctionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17B55F2409B7031443495 /* CoreFunctionsTests.swift */; };
+		19A17DAD5975D9367EAA46E2 /* RTreeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17162C9861E5C4900455D /* RTreeTests.swift */; };
 		19A17DC282E36C4F41AA440B /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1710E73A46D5AC721CDA9 /* Errors.swift */; };
+		19A17DD33C2E43DD6EE05A60 /* ExpressionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A170F141BF21946D159083 /* ExpressionTests.swift */; };
+		19A17DE1FCDB5695702AD24D /* SelectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17EC0C43015063945D32E /* SelectTests.swift */; };
+		19A17DE34C477232592A8F6B /* CoreFunctionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17B55F2409B7031443495 /* CoreFunctionsTests.swift */; };
 		19A17DF8D4F13A20F5D2269E /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17E723300E5ED3771DCB5 /* Result.swift */; };
 		19A17DFE05ED8B1F7C45F7EE /* SchemaChanger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A171B262DDE8718513CFDA /* SchemaChanger.swift */; };
 		19A17E04C4C0956715C5676A /* FoundationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1794CC4D7827E997E32A7 /* FoundationTests.swift */; };
+		19A17E0ABA6C415F014CD51C /* SetterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1709D5BDD2691BA160012 /* SetterTests.swift */; };
+		19A17E1DD976D5CE80018749 /* FTS4Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1715904F7B6851FCB5EF6 /* FTS4Tests.swift */; };
 		19A17E29278A12BC4F542506 /* DateAndTimeFunctions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17BA55DABB480F9020C8A /* DateAndTimeFunctions.swift */; };
+		19A17E3F47DA087E2B76D087 /* QueryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A171ED017645C8B04DF9F2 /* QueryTests.swift */; };
 		19A17E80F736EEE8EE2AA4CE /* SchemaDefinitionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A176174862D0F0139B3987 /* SchemaDefinitionsTests.swift */; };
 		19A17EC0D68BA8C03288ADF7 /* FTS5.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1730E4390C775C25677D1 /* FTS5.swift */; };
 		19A17F0BF02896E1664F4090 /* Connection+Schema.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A170A97B51DC5EE365F3C5 /* Connection+Schema.swift */; };
 		19A17F1B3F0A3C96B5ED6D64 /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17E723300E5ED3771DCB5 /* Result.swift */; };
+		19A17F2096E83A3181E03317 /* SchemaTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A171A7714C6524093255C5 /* SchemaTests.swift */; };
 		19A17F60B685636D1F83C2DD /* Fixtures.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17B93B48B5560E6E51791 /* Fixtures.swift */; };
+		19A17F7977364EC8CD33C3C3 /* SelectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17EC0C43015063945D32E /* SelectTests.swift */; };
+		19A17F907258E524B3CA2FAE /* SetterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1709D5BDD2691BA160012 /* SetterTests.swift */; };
+		19A17FACE8E4D54A50BA934E /* FTS5Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17B96EBD42C878E609CDC /* FTS5Tests.swift */; };
 		19A17FB80B94E882050AA908 /* FoundationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1794CC4D7827E997E32A7 /* FoundationTests.swift */; };
+		19A17FBAA26953EB854E790D /* Connection+PragmaTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17C60D3464461057E4D63 /* Connection+PragmaTests.swift */; };
 		19A17FC04708C6ED637DDFD4 /* SchemaChangerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17FDB0B0CFB8987906FD0 /* SchemaChangerTests.swift */; };
 		19A17FC07731779C1B8506FA /* SchemaChanger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A171B262DDE8718513CFDA /* SchemaChanger.swift */; };
+		19A17FD22EF43DF428DD93BA /* ValueTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17AE284BB1DF31D1B753E /* ValueTests.swift */; };
 		19A17FE78A39E86F330420EC /* SchemaDefinitionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A176174862D0F0139B3987 /* SchemaDefinitionsTests.swift */; };
 		19A17FF4A10B44D3937C8CAC /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1710E73A46D5AC721CDA9 /* Errors.swift */; };
-		3717F908221F5D8800B9BD3D /* CustomAggregationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3717F907221F5D7C00B9BD3D /* CustomAggregationTests.swift */; };
-		3717F909221F5D8900B9BD3D /* CustomAggregationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3717F907221F5D7C00B9BD3D /* CustomAggregationTests.swift */; };
-		3717F90A221F5D8900B9BD3D /* CustomAggregationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3717F907221F5D7C00B9BD3D /* CustomAggregationTests.swift */; };
 		3D67B3E61DB2469200A4F4C6 /* libsqlite3.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 3D67B3E51DB2469200A4F4C6 /* libsqlite3.tbd */; };
 		3D67B3E71DB246BA00A4F4C6 /* Blob.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AEE1C3F06E900AE3E12 /* Blob.swift */; };
 		3D67B3E81DB246BA00A4F4C6 /* Connection.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AEF1C3F06E900AE3E12 /* Connection.swift */; };
@@ -144,9 +184,6 @@
 		3DF7B78928842972005DD8CA /* Connection+Attach.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DF7B78728842972005DD8CA /* Connection+Attach.swift */; };
 		3DF7B78A28842972005DD8CA /* Connection+Attach.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DF7B78728842972005DD8CA /* Connection+Attach.swift */; };
 		3DF7B78B28842972005DD8CA /* Connection+Attach.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DF7B78728842972005DD8CA /* Connection+Attach.swift */; };
-		3DF7B78D28842C23005DD8CA /* ResultTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DF7B78C28842C23005DD8CA /* ResultTests.swift */; };
-		3DF7B78E28842C23005DD8CA /* ResultTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DF7B78C28842C23005DD8CA /* ResultTests.swift */; };
-		3DF7B78F28842C23005DD8CA /* ResultTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DF7B78C28842C23005DD8CA /* ResultTests.swift */; };
 		3DF7B791288449BA005DD8CA /* URIQueryParameter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DF7B790288449BA005DD8CA /* URIQueryParameter.swift */; };
 		3DF7B792288449BA005DD8CA /* URIQueryParameter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DF7B790288449BA005DD8CA /* URIQueryParameter.swift */; };
 		3DF7B793288449BA005DD8CA /* URIQueryParameter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DF7B790288449BA005DD8CA /* URIQueryParameter.swift */; };
@@ -162,9 +199,6 @@
 		997DF2AF287FC06D00F8DF95 /* Query+with.swift in Sources */ = {isa = PBXBuildFile; fileRef = 997DF2AD287FC06D00F8DF95 /* Query+with.swift */; };
 		997DF2B0287FC06D00F8DF95 /* Query+with.swift in Sources */ = {isa = PBXBuildFile; fileRef = 997DF2AD287FC06D00F8DF95 /* Query+with.swift */; };
 		997DF2B1287FC06D00F8DF95 /* Query+with.swift in Sources */ = {isa = PBXBuildFile; fileRef = 997DF2AD287FC06D00F8DF95 /* Query+with.swift */; };
-		D4DB368C20C09CFB00D5A58E /* SelectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4DB368A20C09C9B00D5A58E /* SelectTests.swift */; };
-		D4DB368D20C09CFC00D5A58E /* SelectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4DB368A20C09C9B00D5A58E /* SelectTests.swift */; };
-		D4DB368E20C09CFD00D5A58E /* SelectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4DB368A20C09C9B00D5A58E /* SelectTests.swift */; };
 		EE247AD71C3F04ED00AE3E12 /* SQLite.h in Headers */ = {isa = PBXBuildFile; fileRef = EE247AD61C3F04ED00AE3E12 /* SQLite.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		EE247ADE1C3F04ED00AE3E12 /* SQLite.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EE247AD31C3F04ED00AE3E12 /* SQLite.framework */; };
 		EE247B031C3F06E900AE3E12 /* Blob.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AEE1C3F06E900AE3E12 /* Blob.swift */; };
@@ -185,35 +219,7 @@
 		EE247B141C3F06E900AE3E12 /* Schema.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B011C3F06E900AE3E12 /* Schema.swift */; };
 		EE247B151C3F06E900AE3E12 /* Setter.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B021C3F06E900AE3E12 /* Setter.swift */; };
 		EE247B171C3F127200AE3E12 /* TestHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B161C3F127200AE3E12 /* TestHelpers.swift */; };
-		EE247B191C3F134A00AE3E12 /* SetterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B181C3F134A00AE3E12 /* SetterTests.swift */; };
-		EE247B221C3F137700AE3E12 /* AggregateFunctionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B1A1C3F137700AE3E12 /* AggregateFunctionsTests.swift */; };
-		EE247B231C3F137700AE3E12 /* BlobTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B1B1C3F137700AE3E12 /* BlobTests.swift */; };
-		EE247B251C3F137700AE3E12 /* ConnectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B1D1C3F137700AE3E12 /* ConnectionTests.swift */; };
-		EE247B261C3F137700AE3E12 /* CoreFunctionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B1E1C3F137700AE3E12 /* CoreFunctionsTests.swift */; };
-		EE247B271C3F137700AE3E12 /* CustomFunctionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B1F1C3F137700AE3E12 /* CustomFunctionsTests.swift */; };
-		EE247B281C3F137700AE3E12 /* ExpressionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B201C3F137700AE3E12 /* ExpressionTests.swift */; };
-		EE247B291C3F137700AE3E12 /* FTS4Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B211C3F137700AE3E12 /* FTS4Tests.swift */; };
-		EE247B2E1C3F141E00AE3E12 /* OperatorsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B2A1C3F141E00AE3E12 /* OperatorsTests.swift */; };
-		EE247B2F1C3F141E00AE3E12 /* QueryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B2B1C3F141E00AE3E12 /* QueryTests.swift */; };
-		EE247B301C3F141E00AE3E12 /* RTreeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B2C1C3F141E00AE3E12 /* RTreeTests.swift */; };
-		EE247B311C3F141E00AE3E12 /* SchemaTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B2D1C3F141E00AE3E12 /* SchemaTests.swift */; };
-		EE247B341C3F142E00AE3E12 /* StatementTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B321C3F142E00AE3E12 /* StatementTests.swift */; };
-		EE247B351C3F142E00AE3E12 /* ValueTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B331C3F142E00AE3E12 /* ValueTests.swift */; };
 		EE247B461C3F3ED000AE3E12 /* SQLite.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EE247B3C1C3F3ED000AE3E12 /* SQLite.framework */; };
-		EE247B531C3F3FC700AE3E12 /* AggregateFunctionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B1A1C3F137700AE3E12 /* AggregateFunctionsTests.swift */; };
-		EE247B541C3F3FC700AE3E12 /* BlobTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B1B1C3F137700AE3E12 /* BlobTests.swift */; };
-		EE247B551C3F3FC700AE3E12 /* ConnectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B1D1C3F137700AE3E12 /* ConnectionTests.swift */; };
-		EE247B561C3F3FC700AE3E12 /* CoreFunctionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B1E1C3F137700AE3E12 /* CoreFunctionsTests.swift */; };
-		EE247B571C3F3FC700AE3E12 /* CustomFunctionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B1F1C3F137700AE3E12 /* CustomFunctionsTests.swift */; };
-		EE247B581C3F3FC700AE3E12 /* ExpressionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B201C3F137700AE3E12 /* ExpressionTests.swift */; };
-		EE247B591C3F3FC700AE3E12 /* FTS4Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B211C3F137700AE3E12 /* FTS4Tests.swift */; };
-		EE247B5A1C3F3FC700AE3E12 /* OperatorsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B2A1C3F141E00AE3E12 /* OperatorsTests.swift */; };
-		EE247B5B1C3F3FC700AE3E12 /* QueryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B2B1C3F141E00AE3E12 /* QueryTests.swift */; };
-		EE247B5C1C3F3FC700AE3E12 /* RTreeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B2C1C3F141E00AE3E12 /* RTreeTests.swift */; };
-		EE247B5D1C3F3FC700AE3E12 /* SchemaTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B2D1C3F141E00AE3E12 /* SchemaTests.swift */; };
-		EE247B5E1C3F3FC700AE3E12 /* SetterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B181C3F134A00AE3E12 /* SetterTests.swift */; };
-		EE247B5F1C3F3FC700AE3E12 /* StatementTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B321C3F142E00AE3E12 /* StatementTests.swift */; };
-		EE247B601C3F3FC700AE3E12 /* ValueTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B331C3F142E00AE3E12 /* ValueTests.swift */; };
 		EE247B611C3F3FC700AE3E12 /* TestHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B161C3F127200AE3E12 /* TestHelpers.swift */; };
 		EE247B621C3F3FDB00AE3E12 /* SQLite.h in Headers */ = {isa = PBXBuildFile; fileRef = EE247AD61C3F04ED00AE3E12 /* SQLite.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		EE247B631C3F3FDB00AE3E12 /* Foundation.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AF71C3F06E900AE3E12 /* Foundation.swift */; };
@@ -266,34 +272,51 @@
 		03A65E5A1C6BB0F50062603F /* SQLite.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SQLite.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		03A65E631C6BB0F60062603F /* SQLiteTests tvOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "SQLiteTests tvOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
 		03A65E961C6BB3210062603F /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS.sdk/usr/lib/libsqlite3.tbd; sourceTree = DEVELOPER_DIR; };
+		19A1709D5BDD2691BA160012 /* SetterTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SetterTests.swift; sourceTree = "<group>"; };
 		19A170A97B51DC5EE365F3C5 /* Connection+Schema.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Connection+Schema.swift"; sourceTree = "<group>"; };
+		19A170C08525D3D27CB5F83C /* Connection+AttachTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Connection+AttachTests.swift"; sourceTree = "<group>"; };
+		19A170F141BF21946D159083 /* ExpressionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExpressionTests.swift; sourceTree = "<group>"; };
 		19A1710E73A46D5AC721CDA9 /* Errors.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Errors.swift; sourceTree = "<group>"; };
+		19A1715904F7B6851FCB5EF6 /* FTS4Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FTS4Tests.swift; sourceTree = "<group>"; };
+		19A17162C9861E5C4900455D /* RTreeTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RTreeTests.swift; sourceTree = "<group>"; };
+		19A171A2ED4E2640F197F48C /* BlobTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlobTests.swift; sourceTree = "<group>"; };
+		19A171A7714C6524093255C5 /* SchemaTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SchemaTests.swift; sourceTree = "<group>"; };
 		19A171B262DDE8718513CFDA /* SchemaChanger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SchemaChanger.swift; sourceTree = "<group>"; };
-		19A171FDE4D67879B14ACBDF /* FTSIntegrationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FTSIntegrationTests.swift; sourceTree = "<group>"; };
-		19A1721B8984686B9963B45D /* FTS5Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FTS5Tests.swift; sourceTree = "<group>"; };
+		19A171ED017645C8B04DF9F2 /* QueryTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QueryTests.swift; sourceTree = "<group>"; };
 		19A17268AE67B746B96AC125 /* SchemaDefinitions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SchemaDefinitions.swift; sourceTree = "<group>"; };
-		19A1729B75C33F9A0B9A89C1 /* DateAndTimeFunctionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DateAndTimeFunctionTests.swift; sourceTree = "<group>"; };
 		19A1730E4390C775C25677D1 /* FTS5.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FTS5.swift; sourceTree = "<group>"; };
-		19A17399EA9E61235D5D77BF /* CipherTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CipherTests.swift; sourceTree = "<group>"; };
+		19A1745BE8623D8C6808DB3C /* ResultTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ResultTests.swift; sourceTree = "<group>"; };
+		19A17475DCA068453F787613 /* OperatorsTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OperatorsTests.swift; sourceTree = "<group>"; };
+		19A174FE5B47A97937A27276 /* RowTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RowTests.swift; sourceTree = "<group>"; };
+		19A1756EB81E9F7F45B12A78 /* StatementTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatementTests.swift; sourceTree = "<group>"; };
 		19A175A9CB446640AE6F2200 /* Connection+Aggregation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Connection+Aggregation.swift"; sourceTree = "<group>"; };
-		19A175C1F9CB3BBAB8FCEC7B /* RowTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RowTests.swift; sourceTree = "<group>"; };
+		19A17613F00A3F4D4A257A04 /* AggregateFunctionsTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AggregateFunctionsTests.swift; sourceTree = "<group>"; };
 		19A176174862D0F0139B3987 /* SchemaDefinitionsTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SchemaDefinitionsTests.swift; sourceTree = "<group>"; };
+		19A177EF5E2D91BA86DA4480 /* CustomAggregationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomAggregationTests.swift; sourceTree = "<group>"; };
+		19A17855BD524FF888265B3C /* ConnectionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConnectionTests.swift; sourceTree = "<group>"; };
+		19A1787E16C8562C09C076F5 /* CipherTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CipherTests.swift; sourceTree = "<group>"; };
 		19A178A39ACA9667A62663CC /* Cipher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Cipher.swift; sourceTree = "<group>"; };
+		19A178AEF32ABC8BF2993FB5 /* DateAndTimeFunctionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DateAndTimeFunctionTests.swift; sourceTree = "<group>"; };
 		19A1794B7972D14330A65BBD /* Linux.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = Linux.md; sourceTree = "<group>"; };
 		19A1794CC4D7827E997E32A7 /* FoundationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FoundationTests.swift; sourceTree = "<group>"; };
+		19A17AE284BB1DF31D1B753E /* ValueTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ValueTests.swift; sourceTree = "<group>"; };
+		19A17B55F2409B7031443495 /* CoreFunctionsTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreFunctionsTests.swift; sourceTree = "<group>"; };
 		19A17B93B48B5560E6E51791 /* Fixtures.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Fixtures.swift; sourceTree = "<group>"; };
+		19A17B96EBD42C878E609CDC /* FTS5Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FTS5Tests.swift; sourceTree = "<group>"; };
 		19A17BA55DABB480F9020C8A /* DateAndTimeFunctions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DateAndTimeFunctions.swift; sourceTree = "<group>"; };
-		19A17BA6B4E282C1315A115C /* QueryIntegrationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QueryIntegrationTests.swift; sourceTree = "<group>"; };
-		19A17CA1DF7D0F7C9A94C51C /* ConnectionSchemaTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConnectionSchemaTests.swift; sourceTree = "<group>"; };
+		19A17BE1CC4AD4036BAB8EE0 /* FTSIntegrationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FTSIntegrationTests.swift; sourceTree = "<group>"; };
+		19A17C60D3464461057E4D63 /* Connection+PragmaTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Connection+PragmaTests.swift"; sourceTree = "<group>"; };
+		19A17CA1DF7D0F7C9A94C51C /* Connection+SchemaTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Connection+SchemaTests.swift"; sourceTree = "<group>"; };
+		19A17CAE60446607E99C22A6 /* QueryIntegrationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QueryIntegrationTests.swift; sourceTree = "<group>"; };
 		19A17E723300E5ED3771DCB5 /* Result.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Result.swift; sourceTree = "<group>"; };
 		19A17EA3A313F129011B3FA0 /* Release.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = Release.md; sourceTree = "<group>"; };
+		19A17EC0C43015063945D32E /* SelectTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelectTests.swift; sourceTree = "<group>"; };
 		19A17F285B767BFACD96714B /* Connection+Pragmas.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Connection+Pragmas.swift"; sourceTree = "<group>"; };
+		19A17F2B5959B212A33C383F /* CustomFunctionsTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomFunctionsTests.swift; sourceTree = "<group>"; };
 		19A17FDB0B0CFB8987906FD0 /* SchemaChangerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SchemaChangerTests.swift; sourceTree = "<group>"; };
-		3717F907221F5D7C00B9BD3D /* CustomAggregationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomAggregationTests.swift; sourceTree = "<group>"; };
 		3D3C3CCB26E5568800759140 /* SQLite.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = SQLite.playground; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
 		3D67B3E51DB2469200A4F4C6 /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = Platforms/WatchOS.platform/Developer/SDKs/WatchOS3.0.sdk/usr/lib/libsqlite3.tbd; sourceTree = DEVELOPER_DIR; };
 		3DF7B78728842972005DD8CA /* Connection+Attach.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Connection+Attach.swift"; sourceTree = "<group>"; };
-		3DF7B78C28842C23005DD8CA /* ResultTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResultTests.swift; sourceTree = "<group>"; };
 		3DF7B790288449BA005DD8CA /* URIQueryParameter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URIQueryParameter.swift; sourceTree = "<group>"; };
 		3DF7B79528846FCC005DD8CA /* Resources */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Resources; sourceTree = "<group>"; };
 		3DF7B79A2884C353005DD8CA /* CHANGELOG.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = CHANGELOG.md; sourceTree = "<group>"; };
@@ -302,7 +325,6 @@
 		49EB68C31F7B3CB400D89D40 /* Coding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Coding.swift; sourceTree = "<group>"; };
 		997DF2AD287FC06D00F8DF95 /* Query+with.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Query+with.swift"; sourceTree = "<group>"; };
 		A121AC451CA35C79005A31D1 /* SQLite.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SQLite.framework; sourceTree = BUILT_PRODUCTS_DIR; };
-		D4DB368A20C09C9B00D5A58E /* SelectTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectTests.swift; sourceTree = "<group>"; };
 		EE247AD31C3F04ED00AE3E12 /* SQLite.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SQLite.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		EE247AD61C3F04ED00AE3E12 /* SQLite.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SQLite.h; sourceTree = "<group>"; };
 		EE247AD81C3F04ED00AE3E12 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@@ -326,20 +348,6 @@
 		EE247B011C3F06E900AE3E12 /* Schema.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Schema.swift; sourceTree = "<group>"; };
 		EE247B021C3F06E900AE3E12 /* Setter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Setter.swift; sourceTree = "<group>"; };
 		EE247B161C3F127200AE3E12 /* TestHelpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestHelpers.swift; sourceTree = "<group>"; };
-		EE247B181C3F134A00AE3E12 /* SetterTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SetterTests.swift; sourceTree = "<group>"; };
-		EE247B1A1C3F137700AE3E12 /* AggregateFunctionsTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AggregateFunctionsTests.swift; sourceTree = "<group>"; };
-		EE247B1B1C3F137700AE3E12 /* BlobTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlobTests.swift; sourceTree = "<group>"; };
-		EE247B1D1C3F137700AE3E12 /* ConnectionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConnectionTests.swift; sourceTree = "<group>"; };
-		EE247B1E1C3F137700AE3E12 /* CoreFunctionsTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreFunctionsTests.swift; sourceTree = "<group>"; };
-		EE247B1F1C3F137700AE3E12 /* CustomFunctionsTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomFunctionsTests.swift; sourceTree = "<group>"; };
-		EE247B201C3F137700AE3E12 /* ExpressionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExpressionTests.swift; sourceTree = "<group>"; };
-		EE247B211C3F137700AE3E12 /* FTS4Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FTS4Tests.swift; sourceTree = "<group>"; };
-		EE247B2A1C3F141E00AE3E12 /* OperatorsTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OperatorsTests.swift; sourceTree = "<group>"; };
-		EE247B2B1C3F141E00AE3E12 /* QueryTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QueryTests.swift; sourceTree = "<group>"; };
-		EE247B2C1C3F141E00AE3E12 /* RTreeTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RTreeTests.swift; sourceTree = "<group>"; };
-		EE247B2D1C3F141E00AE3E12 /* SchemaTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SchemaTests.swift; sourceTree = "<group>"; };
-		EE247B321C3F142E00AE3E12 /* StatementTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatementTests.swift; sourceTree = "<group>"; };
-		EE247B331C3F142E00AE3E12 /* ValueTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ValueTests.swift; sourceTree = "<group>"; };
 		EE247B3C1C3F3ED000AE3E12 /* SQLite.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SQLite.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		EE247B451C3F3ED000AE3E12 /* SQLiteTests Mac.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "SQLiteTests Mac.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
 		EE247B771C3F40D700AE3E12 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
@@ -423,16 +431,62 @@
 			path = Schema;
 			sourceTree = "<group>";
 		};
+		19A1798E3459573BEE50FA34 /* Core */ = {
+			isa = PBXGroup;
+			children = (
+				19A1756EB81E9F7F45B12A78 /* StatementTests.swift */,
+				19A171A2ED4E2640F197F48C /* BlobTests.swift */,
+				19A17855BD524FF888265B3C /* ConnectionTests.swift */,
+				19A17B55F2409B7031443495 /* CoreFunctionsTests.swift */,
+				19A1745BE8623D8C6808DB3C /* ResultTests.swift */,
+				19A17AE284BB1DF31D1B753E /* ValueTests.swift */,
+				19A17C60D3464461057E4D63 /* Connection+PragmaTests.swift */,
+				19A170C08525D3D27CB5F83C /* Connection+AttachTests.swift */,
+			);
+			path = Core;
+			sourceTree = "<group>";
+		};
+		19A17AECBF878B1DAE0AE3DD /* Typed */ = {
+			isa = PBXGroup;
+			children = (
+				19A170F141BF21946D159083 /* ExpressionTests.swift */,
+				19A171ED017645C8B04DF9F2 /* QueryTests.swift */,
+				19A178AEF32ABC8BF2993FB5 /* DateAndTimeFunctionTests.swift */,
+				19A17F2B5959B212A33C383F /* CustomFunctionsTests.swift */,
+				19A17475DCA068453F787613 /* OperatorsTests.swift */,
+				19A17EC0C43015063945D32E /* SelectTests.swift */,
+				19A17CAE60446607E99C22A6 /* QueryIntegrationTests.swift */,
+				19A17613F00A3F4D4A257A04 /* AggregateFunctionsTests.swift */,
+				19A177EF5E2D91BA86DA4480 /* CustomAggregationTests.swift */,
+				19A1709D5BDD2691BA160012 /* SetterTests.swift */,
+				19A174FE5B47A97937A27276 /* RowTests.swift */,
+			);
+			path = Typed;
+			sourceTree = "<group>";
+		};
 		19A17B56FBA20E7245BC8AC0 /* Schema */ = {
 			isa = PBXGroup;
 			children = (
 				19A176174862D0F0139B3987 /* SchemaDefinitionsTests.swift */,
 				19A17FDB0B0CFB8987906FD0 /* SchemaChangerTests.swift */,
-				19A17CA1DF7D0F7C9A94C51C /* ConnectionSchemaTests.swift */,
+				19A17CA1DF7D0F7C9A94C51C /* Connection+SchemaTests.swift */,
+				19A171A7714C6524093255C5 /* SchemaTests.swift */,
 			);
 			path = Schema;
 			sourceTree = "<group>";
 		};
+		19A17E470E4492D287C0D12F /* Extensions */ = {
+			isa = PBXGroup;
+			children = (
+				19A17B96EBD42C878E609CDC /* FTS5Tests.swift */,
+				19A17BE1CC4AD4036BAB8EE0 /* FTSIntegrationTests.swift */,
+				19A1715904F7B6851FCB5EF6 /* FTS4Tests.swift */,
+				19A1787E16C8562C09C076F5 /* CipherTests.swift */,
+				19A17162C9861E5C4900455D /* RTreeTests.swift */,
+			);
+			path = Extensions;
+			sourceTree = "<group>";
+		};
 		3D67B3E41DB2469200A4F4C6 /* Frameworks */ = {
 			isa = PBXGroup;
 			children = (
@@ -489,34 +543,14 @@
 			isa = PBXGroup;
 			children = (
 				3DF7B79528846FCC005DD8CA /* Resources */,
-				EE247B1A1C3F137700AE3E12 /* AggregateFunctionsTests.swift */,
-				EE247B1B1C3F137700AE3E12 /* BlobTests.swift */,
-				EE247B1D1C3F137700AE3E12 /* ConnectionTests.swift */,
-				EE247B1E1C3F137700AE3E12 /* CoreFunctionsTests.swift */,
-				EE247B1F1C3F137700AE3E12 /* CustomFunctionsTests.swift */,
-				3717F907221F5D7C00B9BD3D /* CustomAggregationTests.swift */,
-				EE247B201C3F137700AE3E12 /* ExpressionTests.swift */,
-				EE247B211C3F137700AE3E12 /* FTS4Tests.swift */,
-				EE247B2A1C3F141E00AE3E12 /* OperatorsTests.swift */,
-				EE247B2B1C3F141E00AE3E12 /* QueryTests.swift */,
-				EE247B2C1C3F141E00AE3E12 /* RTreeTests.swift */,
-				EE247B2D1C3F141E00AE3E12 /* SchemaTests.swift */,
-				EE247B181C3F134A00AE3E12 /* SetterTests.swift */,
-				EE247B321C3F142E00AE3E12 /* StatementTests.swift */,
-				EE247B331C3F142E00AE3E12 /* ValueTests.swift */,
 				EE247B161C3F127200AE3E12 /* TestHelpers.swift */,
 				EE247AE41C3F04ED00AE3E12 /* Info.plist */,
-				19A1721B8984686B9963B45D /* FTS5Tests.swift */,
 				19A1794CC4D7827E997E32A7 /* FoundationTests.swift */,
-				19A17399EA9E61235D5D77BF /* CipherTests.swift */,
 				19A17B93B48B5560E6E51791 /* Fixtures.swift */,
-				19A175C1F9CB3BBAB8FCEC7B /* RowTests.swift */,
-				19A1729B75C33F9A0B9A89C1 /* DateAndTimeFunctionTests.swift */,
-				D4DB368A20C09C9B00D5A58E /* SelectTests.swift */,
-				19A17BA6B4E282C1315A115C /* QueryIntegrationTests.swift */,
-				19A171FDE4D67879B14ACBDF /* FTSIntegrationTests.swift */,
-				3DF7B78C28842C23005DD8CA /* ResultTests.swift */,
 				19A17B56FBA20E7245BC8AC0 /* Schema */,
+				19A17E470E4492D287C0D12F /* Extensions */,
+				19A1798E3459573BEE50FA34 /* Core */,
+				19A17AECBF878B1DAE0AE3DD /* Typed */,
 			);
 			name = SQLiteTests;
 			path = Tests/SQLiteTests;
@@ -934,35 +968,37 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				3DF7B78F28842C23005DD8CA /* ResultTests.swift in Sources */,
-				03A65E881C6BB3030062603F /* BlobTests.swift in Sources */,
-				03A65E901C6BB3030062603F /* RTreeTests.swift in Sources */,
-				03A65E941C6BB3030062603F /* ValueTests.swift in Sources */,
-				03A65E8F1C6BB3030062603F /* QueryTests.swift in Sources */,
-				03A65E8B1C6BB3030062603F /* CustomFunctionsTests.swift in Sources */,
-				03A65E871C6BB3030062603F /* AggregateFunctionsTests.swift in Sources */,
-				03A65E921C6BB3030062603F /* SetterTests.swift in Sources */,
-				03A65E891C6BB3030062603F /* ConnectionTests.swift in Sources */,
-				03A65E8A1C6BB3030062603F /* CoreFunctionsTests.swift in Sources */,
-				3717F90A221F5D8900B9BD3D /* CustomAggregationTests.swift in Sources */,
-				03A65E931C6BB3030062603F /* StatementTests.swift in Sources */,
-				03A65E911C6BB3030062603F /* SchemaTests.swift in Sources */,
-				03A65E8D1C6BB3030062603F /* FTS4Tests.swift in Sources */,
-				03A65E8C1C6BB3030062603F /* ExpressionTests.swift in Sources */,
-				03A65E8E1C6BB3030062603F /* OperatorsTests.swift in Sources */,
 				03A65E951C6BB3030062603F /* TestHelpers.swift in Sources */,
-				19A17254FBA7894891F7297B /* FTS5Tests.swift in Sources */,
 				19A17E04C4C0956715C5676A /* FoundationTests.swift in Sources */,
-				19A179A0C45377CB09BB358C /* CipherTests.swift in Sources */,
 				19A17F60B685636D1F83C2DD /* Fixtures.swift in Sources */,
-				19A1785195182AF8731A8BDA /* RowTests.swift in Sources */,
-				19A1769C1F3A7542BECF50FF /* DateAndTimeFunctionTests.swift in Sources */,
-				D4DB368E20C09CFD00D5A58E /* SelectTests.swift in Sources */,
-				19A17CB808ACF606726E77A8 /* QueryIntegrationTests.swift in Sources */,
-				19A17C9407AC0EE104E5CC85 /* FTSIntegrationTests.swift in Sources */,
 				19A17B62A4125AF4F6014CF5 /* SchemaDefinitionsTests.swift in Sources */,
 				19A17FC04708C6ED637DDFD4 /* SchemaChangerTests.swift in Sources */,
-				19A17188B4D96636F9C0C209 /* ConnectionSchemaTests.swift in Sources */,
+				19A17188B4D96636F9C0C209 /* Connection+SchemaTests.swift in Sources */,
+				19A17FACE8E4D54A50BA934E /* FTS5Tests.swift in Sources */,
+				19A177909023B7B940C5805E /* FTSIntegrationTests.swift in Sources */,
+				19A17E1DD976D5CE80018749 /* FTS4Tests.swift in Sources */,
+				19A17411403D60640467209E /* ExpressionTests.swift in Sources */,
+				19A17CA4D7B63D845428A9C5 /* StatementTests.swift in Sources */,
+				19A17885B646CB0201BE4BD5 /* QueryTests.swift in Sources */,
+				19A1708D3D58D7BC1168E55F /* CipherTests.swift in Sources */,
+				19A178C041DDCF80B533AD13 /* BlobTests.swift in Sources */,
+				19A17021286A4D8D6C2EF12D /* ConnectionTests.swift in Sources */,
+				19A17DE34C477232592A8F6B /* CoreFunctionsTests.swift in Sources */,
+				19A1799AF6643CF5081BFA15 /* DateAndTimeFunctionTests.swift in Sources */,
+				19A17457B0461F484AF6BE40 /* CustomFunctionsTests.swift in Sources */,
+				19A17900387FDCF578B31E3E /* OperatorsTests.swift in Sources */,
+				19A17C74233AFC2EDAFA23DC /* ResultTests.swift in Sources */,
+				19A17A9520802ACF45907970 /* RTreeTests.swift in Sources */,
+				19A17A391BF056E3D729E70A /* SchemaTests.swift in Sources */,
+				19A17746150A815944A6820B /* SelectTests.swift in Sources */,
+				19A1766135CE9786B1878603 /* ValueTests.swift in Sources */,
+				19A177D5C6542E2D572162E5 /* QueryIntegrationTests.swift in Sources */,
+				19A178DF5A96CFEFF1E271F6 /* AggregateFunctionsTests.swift in Sources */,
+				19A17437659BD7FD787D94A6 /* CustomAggregationTests.swift in Sources */,
+				19A17F907258E524B3CA2FAE /* SetterTests.swift in Sources */,
+				19A17ABCF0EB4808BDC5B5FF /* RowTests.swift in Sources */,
+				19A17BACF4C032513DE1F879 /* Connection+PragmaTests.swift in Sources */,
+				19A173F25449876761347072 /* Connection+AttachTests.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -1047,35 +1083,37 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				3DF7B78D28842C23005DD8CA /* ResultTests.swift in Sources */,
-				EE247B261C3F137700AE3E12 /* CoreFunctionsTests.swift in Sources */,
-				EE247B291C3F137700AE3E12 /* FTS4Tests.swift in Sources */,
-				EE247B191C3F134A00AE3E12 /* SetterTests.swift in Sources */,
-				EE247B311C3F141E00AE3E12 /* SchemaTests.swift in Sources */,
 				EE247B171C3F127200AE3E12 /* TestHelpers.swift in Sources */,
-				EE247B281C3F137700AE3E12 /* ExpressionTests.swift in Sources */,
-				EE247B271C3F137700AE3E12 /* CustomFunctionsTests.swift in Sources */,
-				EE247B341C3F142E00AE3E12 /* StatementTests.swift in Sources */,
-				EE247B301C3F141E00AE3E12 /* RTreeTests.swift in Sources */,
-				3717F908221F5D8800B9BD3D /* CustomAggregationTests.swift in Sources */,
-				EE247B231C3F137700AE3E12 /* BlobTests.swift in Sources */,
-				EE247B351C3F142E00AE3E12 /* ValueTests.swift in Sources */,
-				EE247B2F1C3F141E00AE3E12 /* QueryTests.swift in Sources */,
-				EE247B221C3F137700AE3E12 /* AggregateFunctionsTests.swift in Sources */,
-				EE247B2E1C3F141E00AE3E12 /* OperatorsTests.swift in Sources */,
-				EE247B251C3F137700AE3E12 /* ConnectionTests.swift in Sources */,
-				19A171E6FA242F72A308C594 /* FTS5Tests.swift in Sources */,
 				19A17FB80B94E882050AA908 /* FoundationTests.swift in Sources */,
-				19A177CC33F2E6A24AF90B02 /* CipherTests.swift in Sources */,
 				19A17408007B182F884E3A53 /* Fixtures.swift in Sources */,
-				19A1720B67ED13E6150C6A3D /* RowTests.swift in Sources */,
-				19A17C80076860CF7751A056 /* DateAndTimeFunctionTests.swift in Sources */,
-				D4DB368C20C09CFB00D5A58E /* SelectTests.swift in Sources */,
-				19A172A9B536D02D4A98AAAD /* QueryIntegrationTests.swift in Sources */,
-				19A17C013B00682B70D53DB8 /* FTSIntegrationTests.swift in Sources */,
 				19A17FE78A39E86F330420EC /* SchemaDefinitionsTests.swift in Sources */,
 				19A177C25834473FAB32CF3B /* SchemaChangerTests.swift in Sources */,
-				19A1725658E480B9B378F28B /* ConnectionSchemaTests.swift in Sources */,
+				19A1725658E480B9B378F28B /* Connection+SchemaTests.swift in Sources */,
+				19A178DA2BB5970778CCAF13 /* FTS5Tests.swift in Sources */,
+				19A1755C49154C87304C9146 /* FTSIntegrationTests.swift in Sources */,
+				19A17444861E1443143DEB44 /* FTS4Tests.swift in Sources */,
+				19A17DD33C2E43DD6EE05A60 /* ExpressionTests.swift in Sources */,
+				19A17D6EC40BC35A5DC81BA8 /* StatementTests.swift in Sources */,
+				19A17E3F47DA087E2B76D087 /* QueryTests.swift in Sources */,
+				19A17A7B3E3B7E76364A2AEE /* CipherTests.swift in Sources */,
+				19A1782444437C7FC6B75CBC /* BlobTests.swift in Sources */,
+				19A17CF65C0196E03BC64519 /* ConnectionTests.swift in Sources */,
+				19A179BB9A6665B2B99DA546 /* CoreFunctionsTests.swift in Sources */,
+				19A174118D11B93DA5DAAF79 /* DateAndTimeFunctionTests.swift in Sources */,
+				19A17A7DF99B0379FD3396B1 /* CustomFunctionsTests.swift in Sources */,
+				19A171F243A589C5EBC47937 /* OperatorsTests.swift in Sources */,
+				19A173F429D7E46289EB2167 /* ResultTests.swift in Sources */,
+				19A17B1D9B5CEBE9CE09280C /* RTreeTests.swift in Sources */,
+				19A172F71EFD65342072D8D2 /* SchemaTests.swift in Sources */,
+				19A17F7977364EC8CD33C3C3 /* SelectTests.swift in Sources */,
+				19A17FD22EF43DF428DD93BA /* ValueTests.swift in Sources */,
+				19A177AA5922527BBDC77CF9 /* QueryIntegrationTests.swift in Sources */,
+				19A179786A6826D58A70F8BC /* AggregateFunctionsTests.swift in Sources */,
+				19A1793972BDDDB027C113BB /* CustomAggregationTests.swift in Sources */,
+				19A1773155AC2BF2CA86A473 /* SetterTests.swift in Sources */,
+				19A176B3316281F004F92276 /* RowTests.swift in Sources */,
+				19A17FBAA26953EB854E790D /* Connection+PragmaTests.swift in Sources */,
+				19A17026DCDCDA405B09A229 /* Connection+AttachTests.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -1122,35 +1160,37 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				3DF7B78E28842C23005DD8CA /* ResultTests.swift in Sources */,
-				EE247B561C3F3FC700AE3E12 /* CoreFunctionsTests.swift in Sources */,
-				EE247B5A1C3F3FC700AE3E12 /* OperatorsTests.swift in Sources */,
-				EE247B541C3F3FC700AE3E12 /* BlobTests.swift in Sources */,
-				EE247B5D1C3F3FC700AE3E12 /* SchemaTests.swift in Sources */,
-				EE247B591C3F3FC700AE3E12 /* FTS4Tests.swift in Sources */,
-				EE247B531C3F3FC700AE3E12 /* AggregateFunctionsTests.swift in Sources */,
-				EE247B5F1C3F3FC700AE3E12 /* StatementTests.swift in Sources */,
-				EE247B5C1C3F3FC700AE3E12 /* RTreeTests.swift in Sources */,
-				EE247B571C3F3FC700AE3E12 /* CustomFunctionsTests.swift in Sources */,
-				3717F909221F5D8900B9BD3D /* CustomAggregationTests.swift in Sources */,
-				EE247B601C3F3FC700AE3E12 /* ValueTests.swift in Sources */,
-				EE247B551C3F3FC700AE3E12 /* ConnectionTests.swift in Sources */,
 				EE247B611C3F3FC700AE3E12 /* TestHelpers.swift in Sources */,
-				EE247B581C3F3FC700AE3E12 /* ExpressionTests.swift in Sources */,
-				EE247B5E1C3F3FC700AE3E12 /* SetterTests.swift in Sources */,
-				EE247B5B1C3F3FC700AE3E12 /* QueryTests.swift in Sources */,
-				19A174D78559CD30679BCCCB /* FTS5Tests.swift in Sources */,
 				19A178072B371489E6A1E839 /* FoundationTests.swift in Sources */,
-				19A17C4B951CB054EE48AB1C /* CipherTests.swift in Sources */,
 				19A1709C3E7A406E62293B2A /* Fixtures.swift in Sources */,
-				19A171967CC511C4F6F773C9 /* RowTests.swift in Sources */,
-				19A172EB202970561E5C4245 /* DateAndTimeFunctionTests.swift in Sources */,
-				D4DB368D20C09CFC00D5A58E /* SelectTests.swift in Sources */,
-				19A176406BDE9D9C80CC9FA3 /* QueryIntegrationTests.swift in Sources */,
-				19A171BE056457F13BFBC4C3 /* FTSIntegrationTests.swift in Sources */,
 				19A17E80F736EEE8EE2AA4CE /* SchemaDefinitionsTests.swift in Sources */,
 				19A1766AC10D13C4EFF349AD /* SchemaChangerTests.swift in Sources */,
-				19A17B36ABC6006AB80F693C /* ConnectionSchemaTests.swift in Sources */,
+				19A17B36ABC6006AB80F693C /* Connection+SchemaTests.swift in Sources */,
+				19A1776BD5127DFDF847FF1F /* FTS5Tests.swift in Sources */,
+				19A173088B85A7E18E8582A7 /* FTSIntegrationTests.swift in Sources */,
+				19A178767223229E61C5066F /* FTS4Tests.swift in Sources */,
+				19A1781CBA8968ABD3E00877 /* ExpressionTests.swift in Sources */,
+				19A17923494236793893BF72 /* StatementTests.swift in Sources */,
+				19A17A52BF29D27C9AA229E7 /* QueryTests.swift in Sources */,
+				19A17CA6ADB78A2E545BF836 /* CipherTests.swift in Sources */,
+				19A1714F7CF964D568AB14E0 /* BlobTests.swift in Sources */,
+				19A173465F23C64DF3DF469B /* ConnectionTests.swift in Sources */,
+				19A17D993398B8215B73E1EA /* CoreFunctionsTests.swift in Sources */,
+				19A170AEBAA56DC3355A73B3 /* DateAndTimeFunctionTests.swift in Sources */,
+				19A1716BF8E15F91A6B5CB7A /* CustomFunctionsTests.swift in Sources */,
+				19A17482E6FC5E563F3E6A47 /* OperatorsTests.swift in Sources */,
+				19A17912DB9D3AC8FECF948B /* ResultTests.swift in Sources */,
+				19A17DAD5975D9367EAA46E2 /* RTreeTests.swift in Sources */,
+				19A17F2096E83A3181E03317 /* SchemaTests.swift in Sources */,
+				19A17DE1FCDB5695702AD24D /* SelectTests.swift in Sources */,
+				19A1726002D24C14F876C8FE /* ValueTests.swift in Sources */,
+				19A173389E53CB24DFA8CEDD /* QueryIntegrationTests.swift in Sources */,
+				19A170C56745F9D722A73D77 /* AggregateFunctionsTests.swift in Sources */,
+				19A1772EBE65173EDFB1AFCA /* CustomAggregationTests.swift in Sources */,
+				19A17E0ABA6C415F014CD51C /* SetterTests.swift in Sources */,
+				19A170D938343E30119EDFB3 /* RowTests.swift in Sources */,
+				19A178F9008614B8A8425635 /* Connection+PragmaTests.swift in Sources */,
+				19A179BCD483DEA21661FD37 /* Connection+AttachTests.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
diff --git a/Sources/SQLite/Schema/Connection+Schema.swift b/Sources/SQLite/Schema/Connection+Schema.swift
index 8bd219ed..c3b24f38 100644
--- a/Sources/SQLite/Schema/Connection+Schema.swift
+++ b/Sources/SQLite/Schema/Connection+Schema.swift
@@ -63,16 +63,6 @@ extension Connection {
         }
     }
 
-    func tableInfo() throws -> [String] {
-        try run("SELECT tbl_name FROM sqlite_master WHERE type = 'table'").compactMap { row in
-            if let name = row[0] as? String, !name.starts(with: "sqlite_") {
-                return name
-            } else {
-                return nil
-            }
-        }
-    }
-
     func foreignKeyInfo(table: String) throws -> [ColumnDefinition.ForeignKey] {
         try run("PRAGMA foreign_key_list(\(table.quote()))").compactMap { row in
             if let table = row[2] as? String,      // table
diff --git a/Tests/SQLiteTests/BlobTests.swift b/Tests/SQLiteTests/Core/BlobTests.swift
similarity index 100%
rename from Tests/SQLiteTests/BlobTests.swift
rename to Tests/SQLiteTests/Core/BlobTests.swift
diff --git a/Tests/SQLiteTests/Core/Connection+AttachTests.swift b/Tests/SQLiteTests/Core/Connection+AttachTests.swift
new file mode 100644
index 00000000..940a30ca
--- /dev/null
+++ b/Tests/SQLiteTests/Core/Connection+AttachTests.swift
@@ -0,0 +1,62 @@
+import XCTest
+import Foundation
+@testable import SQLite
+
+#if SQLITE_SWIFT_STANDALONE
+import sqlite3
+#elseif SQLITE_SWIFT_SQLCIPHER
+import SQLCipher
+#elseif os(Linux)
+import CSQLite
+#else
+import SQLite3
+#endif
+
+class ConnectionAttachTests: SQLiteTestCase {
+    func test_attach_detach_memory_database() throws {
+        let schemaName = "test"
+
+        try db.attach(.inMemory, as: schemaName)
+
+        let table = Table("attached_users", database: schemaName)
+        let name = Expression<String>("string")
+
+        // create a table, insert some data
+        try db.run(table.create { builder in
+            builder.column(name)
+        })
+        _ = try db.run(table.insert(name <- "test"))
+
+        // query data
+        let rows = try db.prepare(table.select(name)).map { $0[name] }
+        XCTAssertEqual(["test"], rows)
+
+        try db.detach(schemaName)
+    }
+
+    func test_attach_detach_file_database() throws {
+        let schemaName = "test"
+        let testDb = fixture("test", withExtension: "sqlite")
+
+        try db.attach(.uri(testDb, parameters: [.mode(.readOnly)]), as: schemaName)
+
+        let table = Table("tests", database: schemaName)
+        let email = Expression<String>("email")
+
+        let rows = try db.prepare(table.select(email)).map { $0[email] }
+        XCTAssertEqual(["foo@bar.com"], rows)
+
+        try db.detach(schemaName)
+    }
+
+    func test_detach_invalid_schema_name_errors_with_no_such_database() throws {
+        XCTAssertThrowsError(try db.detach("no-exist")) { error in
+            if case let Result.error(message, code, _) = error {
+                XCTAssertEqual(code, SQLITE_ERROR)
+                XCTAssertEqual("no such database: no-exist", message)
+            } else {
+                XCTFail("unexpected error: \(error)")
+            }
+        }
+    }
+}
diff --git a/Tests/SQLiteTests/Core/Connection+PragmaTests.swift b/Tests/SQLiteTests/Core/Connection+PragmaTests.swift
new file mode 100644
index 00000000..2d0742c9
--- /dev/null
+++ b/Tests/SQLiteTests/Core/Connection+PragmaTests.swift
@@ -0,0 +1,42 @@
+import XCTest
+import Foundation
+@testable import SQLite
+
+#if SQLITE_SWIFT_STANDALONE
+import sqlite3
+#elseif SQLITE_SWIFT_SQLCIPHER
+import SQLCipher
+#elseif os(Linux)
+import CSQLite
+#else
+import SQLite3
+#endif
+
+class ConnectionPragmaTests: SQLiteTestCase {
+    func test_userVersion() {
+        db.userVersion = 2
+        XCTAssertEqual(2, db.userVersion!)
+    }
+
+    func test_sqlite_version() {
+        XCTAssertTrue(db.sqliteVersion >= (3, 0, 0))
+    }
+
+    func test_foreignKeys_defaults_to_false() {
+        XCTAssertFalse(db.foreignKeys)
+    }
+
+    func test_foreignKeys_sets_value() {
+        db.foreignKeys = true
+        XCTAssertTrue(db.foreignKeys)
+    }
+
+    func test_defer_foreignKeys_defaults_to_false() {
+        XCTAssertFalse(db.deferForeignKeys)
+    }
+
+    func test_defer_foreignKeys_sets_value() {
+        db.deferForeignKeys = true
+        XCTAssertTrue(db.deferForeignKeys)
+    }
+}
diff --git a/Tests/SQLiteTests/ConnectionTests.swift b/Tests/SQLiteTests/Core/ConnectionTests.swift
similarity index 90%
rename from Tests/SQLiteTests/ConnectionTests.swift
rename to Tests/SQLiteTests/Core/ConnectionTests.swift
index 347516f5..e9ceff08 100644
--- a/Tests/SQLiteTests/ConnectionTests.swift
+++ b/Tests/SQLiteTests/Core/ConnectionTests.swift
@@ -111,11 +111,6 @@ class ConnectionTests: SQLiteTestCase {
         XCTAssertEqual(2, db.totalChanges)
     }
 
-    func test_userVersion() {
-        db.userVersion = 2
-        XCTAssertEqual(2, db.userVersion!)
-    }
-
     func test_prepare_preparesAndReturnsStatements() throws {
         _ = try db.prepare("SELECT * FROM users WHERE admin = 0")
         _ = try db.prepare("SELECT * FROM users WHERE admin = ?", 0)
@@ -445,51 +440,4 @@ class ConnectionTests: SQLiteTestCase {
         }
         semaphores.forEach { $0.wait() }
     }
-
-    func test_attach_detach_memory_database() throws {
-        let schemaName = "test"
-
-        try db.attach(.inMemory, as: schemaName)
-
-        let table = Table("attached_users", database: schemaName)
-        let name = Expression<String>("string")
-
-        // create a table, insert some data
-        try db.run(table.create { builder in
-            builder.column(name)
-        })
-        _ = try db.run(table.insert(name <- "test"))
-
-        // query data
-        let rows = try db.prepare(table.select(name)).map { $0[name] }
-        XCTAssertEqual(["test"], rows)
-
-        try db.detach(schemaName)
-    }
-
-    func test_attach_detach_file_database() throws {
-        let schemaName = "test"
-        let testDb = fixture("test", withExtension: "sqlite")
-
-        try db.attach(.uri(testDb, parameters: [.mode(.readOnly)]), as: schemaName)
-
-        let table = Table("tests", database: schemaName)
-        let email = Expression<String>("email")
-
-        let rows = try db.prepare(table.select(email)).map { $0[email] }
-        XCTAssertEqual(["foo@bar.com"], rows)
-
-        try db.detach(schemaName)
-    }
-
-    func test_detach_invalid_schema_name_errors_with_no_such_database() throws {
-        XCTAssertThrowsError(try db.detach("no-exist")) { error in
-            if case let Result.error(message, code, _) = error {
-                XCTAssertEqual(code, SQLITE_ERROR)
-                XCTAssertEqual("no such database: no-exist", message)
-            } else {
-                XCTFail("unexpected error: \(error)")
-            }
-        }
-    }
 }
diff --git a/Tests/SQLiteTests/CoreFunctionsTests.swift b/Tests/SQLiteTests/Core/CoreFunctionsTests.swift
similarity index 100%
rename from Tests/SQLiteTests/CoreFunctionsTests.swift
rename to Tests/SQLiteTests/Core/CoreFunctionsTests.swift
diff --git a/Tests/SQLiteTests/ResultTests.swift b/Tests/SQLiteTests/Core/ResultTests.swift
similarity index 100%
rename from Tests/SQLiteTests/ResultTests.swift
rename to Tests/SQLiteTests/Core/ResultTests.swift
diff --git a/Tests/SQLiteTests/StatementTests.swift b/Tests/SQLiteTests/Core/StatementTests.swift
similarity index 100%
rename from Tests/SQLiteTests/StatementTests.swift
rename to Tests/SQLiteTests/Core/StatementTests.swift
diff --git a/Tests/SQLiteTests/ValueTests.swift b/Tests/SQLiteTests/Core/ValueTests.swift
similarity index 100%
rename from Tests/SQLiteTests/ValueTests.swift
rename to Tests/SQLiteTests/Core/ValueTests.swift
diff --git a/Tests/SQLiteTests/CipherTests.swift b/Tests/SQLiteTests/Extensions/CipherTests.swift
similarity index 100%
rename from Tests/SQLiteTests/CipherTests.swift
rename to Tests/SQLiteTests/Extensions/CipherTests.swift
diff --git a/Tests/SQLiteTests/FTS4Tests.swift b/Tests/SQLiteTests/Extensions/FTS4Tests.swift
similarity index 100%
rename from Tests/SQLiteTests/FTS4Tests.swift
rename to Tests/SQLiteTests/Extensions/FTS4Tests.swift
diff --git a/Tests/SQLiteTests/FTS5Tests.swift b/Tests/SQLiteTests/Extensions/FTS5Tests.swift
similarity index 100%
rename from Tests/SQLiteTests/FTS5Tests.swift
rename to Tests/SQLiteTests/Extensions/FTS5Tests.swift
diff --git a/Tests/SQLiteTests/FTSIntegrationTests.swift b/Tests/SQLiteTests/Extensions/FTSIntegrationTests.swift
similarity index 100%
rename from Tests/SQLiteTests/FTSIntegrationTests.swift
rename to Tests/SQLiteTests/Extensions/FTSIntegrationTests.swift
diff --git a/Tests/SQLiteTests/RTreeTests.swift b/Tests/SQLiteTests/Extensions/RTreeTests.swift
similarity index 100%
rename from Tests/SQLiteTests/RTreeTests.swift
rename to Tests/SQLiteTests/Extensions/RTreeTests.swift
diff --git a/Tests/SQLiteTests/Schema/ConnectionSchemaTests.swift b/Tests/SQLiteTests/Schema/Connection+SchemaTests.swift
similarity index 95%
rename from Tests/SQLiteTests/Schema/ConnectionSchemaTests.swift
rename to Tests/SQLiteTests/Schema/Connection+SchemaTests.swift
index 1c66b761..56e79734 100644
--- a/Tests/SQLiteTests/Schema/ConnectionSchemaTests.swift
+++ b/Tests/SQLiteTests/Schema/Connection+SchemaTests.swift
@@ -108,11 +108,6 @@ class ConnectionSchemaTests: SQLiteTestCase {
         ])
     }
 
-    func test_table_info_returns_list_of_tables() throws {
-        let tables = try db.tableInfo()
-        XCTAssertEqual(tables, ["users"])
-    }
-
     func test_foreign_key_info_empty() throws {
         try db.run("CREATE TABLE t (\"id\" INTEGER PRIMARY KEY)")
 
@@ -136,8 +131,4 @@ class ConnectionSchemaTests: SQLiteTestCase {
             .init(table: "users", column: "test_id", primaryKey: "id", onUpdate: nil, onDelete: nil)
         ])
     }
-
-    func test_sqlite_version() {
-        XCTAssertTrue(db.sqliteVersion >= (3, 0, 0))
-    }
 }
diff --git a/Tests/SQLiteTests/Schema/SchemaChangerTests.swift b/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
index 165de4a8..4d4f8d50 100644
--- a/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
+++ b/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
@@ -102,8 +102,12 @@ class SchemaChangerTests: SQLiteTestCase {
 
     func test_drop_table() throws {
         try schemaChanger.drop(table: "users")
-
-        let tables = try db.tableInfo()
-        XCTAssertFalse(tables.contains("users"))
+        XCTAssertThrowsError(try db.scalar(users.count)) { error in
+            if case Result.error(let message, _, _) =  error {
+                XCTAssertEqual(message, "no such table: users")
+            } else {
+                XCTFail("unexpected error \(error)")
+            }
+        }
     }
 }
diff --git a/Tests/SQLiteTests/SchemaTests.swift b/Tests/SQLiteTests/Schema/SchemaTests.swift
similarity index 100%
rename from Tests/SQLiteTests/SchemaTests.swift
rename to Tests/SQLiteTests/Schema/SchemaTests.swift
diff --git a/Tests/SQLiteTests/AggregateFunctionsTests.swift b/Tests/SQLiteTests/Typed/AggregateFunctionsTests.swift
similarity index 100%
rename from Tests/SQLiteTests/AggregateFunctionsTests.swift
rename to Tests/SQLiteTests/Typed/AggregateFunctionsTests.swift
diff --git a/Tests/SQLiteTests/CustomAggregationTests.swift b/Tests/SQLiteTests/Typed/CustomAggregationTests.swift
similarity index 100%
rename from Tests/SQLiteTests/CustomAggregationTests.swift
rename to Tests/SQLiteTests/Typed/CustomAggregationTests.swift
diff --git a/Tests/SQLiteTests/CustomFunctionsTests.swift b/Tests/SQLiteTests/Typed/CustomFunctionsTests.swift
similarity index 100%
rename from Tests/SQLiteTests/CustomFunctionsTests.swift
rename to Tests/SQLiteTests/Typed/CustomFunctionsTests.swift
diff --git a/Tests/SQLiteTests/DateAndTimeFunctionTests.swift b/Tests/SQLiteTests/Typed/DateAndTimeFunctionTests.swift
similarity index 100%
rename from Tests/SQLiteTests/DateAndTimeFunctionTests.swift
rename to Tests/SQLiteTests/Typed/DateAndTimeFunctionTests.swift
diff --git a/Tests/SQLiteTests/ExpressionTests.swift b/Tests/SQLiteTests/Typed/ExpressionTests.swift
similarity index 100%
rename from Tests/SQLiteTests/ExpressionTests.swift
rename to Tests/SQLiteTests/Typed/ExpressionTests.swift
diff --git a/Tests/SQLiteTests/OperatorsTests.swift b/Tests/SQLiteTests/Typed/OperatorsTests.swift
similarity index 100%
rename from Tests/SQLiteTests/OperatorsTests.swift
rename to Tests/SQLiteTests/Typed/OperatorsTests.swift
diff --git a/Tests/SQLiteTests/QueryIntegrationTests.swift b/Tests/SQLiteTests/Typed/QueryIntegrationTests.swift
similarity index 100%
rename from Tests/SQLiteTests/QueryIntegrationTests.swift
rename to Tests/SQLiteTests/Typed/QueryIntegrationTests.swift
diff --git a/Tests/SQLiteTests/QueryTests.swift b/Tests/SQLiteTests/Typed/QueryTests.swift
similarity index 100%
rename from Tests/SQLiteTests/QueryTests.swift
rename to Tests/SQLiteTests/Typed/QueryTests.swift
diff --git a/Tests/SQLiteTests/RowTests.swift b/Tests/SQLiteTests/Typed/RowTests.swift
similarity index 100%
rename from Tests/SQLiteTests/RowTests.swift
rename to Tests/SQLiteTests/Typed/RowTests.swift
diff --git a/Tests/SQLiteTests/SelectTests.swift b/Tests/SQLiteTests/Typed/SelectTests.swift
similarity index 100%
rename from Tests/SQLiteTests/SelectTests.swift
rename to Tests/SQLiteTests/Typed/SelectTests.swift
diff --git a/Tests/SQLiteTests/SetterTests.swift b/Tests/SQLiteTests/Typed/SetterTests.swift
similarity index 100%
rename from Tests/SQLiteTests/SetterTests.swift
rename to Tests/SQLiteTests/Typed/SetterTests.swift

From a6e6e687b47944b4c338e23bb054fd925998222e Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Tue, 26 Jul 2022 00:44:55 +0200
Subject: [PATCH 187/326] WIP

---
 Sources/SQLite/Schema/Connection+Schema.swift |  2 +-
 Sources/SQLite/Schema/SchemaChanger.swift     | 73 ++++++++++++++-----
 Sources/SQLite/Schema/SchemaDefinitions.swift | 20 +++--
 .../Schema/Connection+SchemaTests.swift       | 18 ++---
 .../Schema/SchemaChangerTests.swift           | 30 +++++++-
 .../Schema/SchemaDefinitionsTests.swift       | 26 +++----
 6 files changed, 119 insertions(+), 50 deletions(-)

diff --git a/Sources/SQLite/Schema/Connection+Schema.swift b/Sources/SQLite/Schema/Connection+Schema.swift
index c3b24f38..378fad68 100644
--- a/Sources/SQLite/Schema/Connection+Schema.swift
+++ b/Sources/SQLite/Schema/Connection+Schema.swift
@@ -24,7 +24,7 @@ extension Connection {
             return ColumnDefinition(name: name,
                                     primaryKey: primaryKey == 1 ? try parsePrimaryKey(column: name) : nil,
                                     type: ColumnDefinition.Affinity.from(type),
-                                    null: notNull == 0,
+                                    nullable: notNull == 0,
                                     defaultValue: .from(defaultValue),
                                     references: foreignKeys[name]?.first)
         }
diff --git a/Sources/SQLite/Schema/SchemaChanger.swift b/Sources/SQLite/Schema/SchemaChanger.swift
index c6529fbe..daba2f60 100644
--- a/Sources/SQLite/Schema/SchemaChanger.swift
+++ b/Sources/SQLite/Schema/SchemaChanger.swift
@@ -27,36 +27,63 @@ import Foundation
        12. If foreign keys constraints were originally enabled, reenable them now.
 */
 public class SchemaChanger: CustomStringConvertible {
-    enum SchemaChangeError: LocalizedError {
+    public enum Error: LocalizedError {
+        case invalidColumnDefinition(String)
         case foreignKeyError([ForeignKeyError])
 
-        var errorDescription: String? {
+        public var errorDescription: String? {
             switch self {
             case .foreignKeyError(let errors):
                 return "Foreign key errors: \(errors)"
+            case .invalidColumnDefinition(let message):
+                return "Invalid column definition: \(message)"
             }
         }
     }
 
     public enum Operation {
-        case none
-        case add(ColumnDefinition)
-        case remove(String)
+        case addColumn(ColumnDefinition)
+        case dropColumn(String)
         case renameColumn(String, String)
         case renameTable(String)
 
         /// Returns non-nil if the operation can be executed with a simple SQL statement
         func toSQL(_ table: String, version: SQLiteVersion) -> String? {
             switch self {
-            case .add(let definition):
+            case .addColumn(let definition):
                 return "ALTER TABLE \(table.quote()) ADD COLUMN \(definition.toSQL())"
             case .renameColumn(let from, let to) where version >= (3, 25, 0):
                 return "ALTER TABLE \(table.quote()) RENAME COLUMN \(from.quote()) TO \(to.quote())"
-            case .remove(let column) where version >= (3, 35, 0):
+            case .dropColumn(let column) where version >= (3, 35, 0):
                 return "ALTER TABLE \(table.quote()) DROP COLUMN \(column.quote())"
             default: return nil
             }
         }
+
+        func validate() throws {
+            switch self {
+            case .addColumn(let definition):
+                // The new column may take any of the forms permissible in a CREATE TABLE statement, with the following restrictions:
+                // - The column may not have a PRIMARY KEY or UNIQUE constraint.
+                // - The column may not have a default value of CURRENT_TIME, CURRENT_DATE, CURRENT_TIMESTAMP, or an expression in parentheses
+                // - If a NOT NULL constraint is specified, then the column must have a default value other than NULL.
+                guard definition.primaryKey == nil else {
+                    throw Error.invalidColumnDefinition("can not add primary key column")
+                }
+                let invalidValues: [LiteralValue] = [.CURRENT_TIME, .CURRENT_DATE, .CURRENT_TIMESTAMP]
+                if invalidValues.contains(definition.defaultValue) {
+                    throw Error.invalidColumnDefinition("Invalid default value")
+                }
+                if !definition.nullable && definition.defaultValue == .NULL {
+                    throw Error.invalidColumnDefinition("NOT NULL columns must have a default value other than NULL")
+                }
+            case .dropColumn:
+                // The DROP COLUMN command only works if the column is not referenced by any other parts of the schema
+                // and is not a PRIMARY KEY and does not have a UNIQUE constraint
+                break
+            default: break
+            }
+        }
     }
 
     public class AlterTableDefinition {
@@ -69,11 +96,11 @@ public class SchemaChanger: CustomStringConvertible {
         }
 
         public func add(_ column: ColumnDefinition) {
-            operations.append(.add(column))
+            operations.append(.addColumn(column))
         }
 
         public func remove(_ column: String) {
-            operations.append(.remove(column))
+            operations.append(.dropColumn(column))
         }
 
         public func rename(_ column: String, to: String) {
@@ -116,7 +143,15 @@ public class SchemaChanger: CustomStringConvertible {
         try dropTable(table)
     }
 
+    // Beginning with release 3.25.0 (2018-09-15), references to the table within trigger bodies and
+    // view definitions are also renamed.
+    public func rename(table: String, to: String) throws {
+        try connection.run("ALTER TABLE \(table.quote()) RENAME TO \(to.quote())")
+    }
+
     private func run(table: String, operation: Operation) throws {
+        try operation.validate()
+
         if let sql = operation.toSQL(table, version: version) {
             try connection.run(sql)
         } else {
@@ -129,10 +164,10 @@ public class SchemaChanger: CustomStringConvertible {
             try disableRefIntegrity {
                 let tempTable = "\(SchemaChanger.tempPrefix)\(table)"
                 try moveTable(from: table, to: tempTable, options: [.temp], operation: operation)
-                try moveTable(from: tempTable, to: table)
+                try rename(table: tempTable, to: table)
                 let foreignKeyErrors = try connection.foreignKeyCheck()
                 if foreignKeyErrors.count > 0 {
-                    throw SchemaChangeError.foreignKeyError(foreignKeyErrors)
+                    throw Error.foreignKeyError(foreignKeyErrors)
                 }
             }
         }
@@ -153,22 +188,24 @@ public class SchemaChanger: CustomStringConvertible {
         try block()
     }
 
-    private func moveTable(from: String, to: String, options: Options = .default, operation: Operation = .none) throws {
+    private func moveTable(from: String, to: String, options: Options = .default, operation: Operation? = nil) throws {
         try copyTable(from: from, to: to, options: options, operation: operation)
         try dropTable(from)
     }
 
-    private func copyTable(from: String, to: String, options: Options = .default, operation: Operation) throws {
+    private func copyTable(from: String, to: String, options: Options = .default, operation: Operation?) throws {
         let fromDefinition = TableDefinition(
             name: from,
             columns: try connection.columnInfo(table: from),
             indexes: try connection.indexInfo(table: from)
         )
-        let toDefinition   = fromDefinition.apply(.renameTable(to)).apply(operation)
+        let toDefinition   = fromDefinition
+                .apply(.renameTable(to))
+                .apply(operation)
 
         try createTable(definition: toDefinition, options: options)
         try createTableIndexes(definition: toDefinition)
-        if case .remove = operation {
+        if case .dropColumn = operation {
             try copyTableContents(from: fromDefinition.apply(operation), to: toDefinition)
         } else {
             try copyTableContents(from: fromDefinition, to: toDefinition)
@@ -221,11 +258,11 @@ extension IndexDefinition {
 }
 
 extension TableDefinition {
-    func apply(_ operation: SchemaChanger.Operation) -> TableDefinition {
+    func apply(_ operation: SchemaChanger.Operation?) -> TableDefinition {
         switch operation {
         case .none: return self
-        case .add: fatalError("Use 'ALTER TABLE ADD COLUMN (...)'")
-        case .remove(let column):
+        case .addColumn: fatalError("Use 'ALTER TABLE ADD COLUMN (...)'")
+        case .dropColumn(let column):
             return TableDefinition(name: name,
                 columns: columns.filter { $0.name != column },
                 indexes: indexes.filter { !$0.columns.contains(column) }
diff --git a/Sources/SQLite/Schema/SchemaDefinitions.swift b/Sources/SQLite/Schema/SchemaDefinitions.swift
index 156a06fb..a06487b2 100644
--- a/Sources/SQLite/Schema/SchemaDefinitions.swift
+++ b/Sources/SQLite/Schema/SchemaDefinitions.swift
@@ -84,23 +84,27 @@ public struct ColumnDefinition: Equatable {
     public let name: String
     public let primaryKey: PrimaryKey?
     public let type: Affinity
-    public let null: Bool
+    public let nullable: Bool
     public let defaultValue: LiteralValue
     public let references: ForeignKey?
 
-    public init(name: String, primaryKey: PrimaryKey?, type: Affinity, null: Bool, defaultValue: LiteralValue,
-                references: ForeignKey?) {
+    public init(name: String,
+                primaryKey: PrimaryKey? = nil,
+                type: Affinity,
+                nullable: Bool = false,
+                defaultValue: LiteralValue = .NULL,
+                references: ForeignKey? = nil) {
         self.name = name
         self.primaryKey = primaryKey
         self.type = type
-        self.null = null
+        self.nullable = nullable
         self.defaultValue = defaultValue
         self.references = references
     }
 
     func rename(from: String, to: String) -> ColumnDefinition {
         guard from == name else { return self }
-        return ColumnDefinition(name: to, primaryKey: primaryKey, type: type, null: null, defaultValue: defaultValue, references: references)
+        return ColumnDefinition(name: to, primaryKey: primaryKey, type: type, nullable: nullable, defaultValue: defaultValue, references: references)
     }
 }
 
@@ -254,12 +258,12 @@ public struct IndexDefinition: Equatable {
     }
 }
 
-struct ForeignKeyError: CustomStringConvertible {
+public struct ForeignKeyError: CustomStringConvertible {
     let from: String
     let rowId: Int64
     let to: String
 
-    var description: String {
+    public var description: String {
         "\(from) [\(rowId)] => \(to)"
     }
 }
@@ -294,7 +298,7 @@ extension ColumnDefinition {
             type.rawValue,
             defaultValue.map { "DEFAULT \($0)" },
             primaryKey.map { $0.toSQL() },
-            null ? nil : "NOT NULL",
+            nullable ? nil : "NOT NULL",
             references.map { $0.toSQL() }
         ].compactMap { $0 }
          .joined(separator: " ")
diff --git a/Tests/SQLiteTests/Schema/Connection+SchemaTests.swift b/Tests/SQLiteTests/Schema/Connection+SchemaTests.swift
index 56e79734..6eded6b2 100644
--- a/Tests/SQLiteTests/Schema/Connection+SchemaTests.swift
+++ b/Tests/SQLiteTests/Schema/Connection+SchemaTests.swift
@@ -14,42 +14,42 @@ class ConnectionSchemaTests: SQLiteTestCase {
             ColumnDefinition(name: "id",
                              primaryKey: .init(autoIncrement: false, onConflict: nil),
                              type: .INTEGER,
-                             null: true,
+                             nullable: true,
                              defaultValue: .NULL,
                              references: nil),
             ColumnDefinition(name: "email",
                              primaryKey: nil,
                              type: .TEXT,
-                             null: false,
+                             nullable: false,
                              defaultValue: .NULL,
                              references: nil),
             ColumnDefinition(name: "age",
                              primaryKey: nil,
                              type: .INTEGER,
-                             null: true,
+                             nullable: true,
                              defaultValue: .NULL,
                              references: nil),
             ColumnDefinition(name: "salary",
                              primaryKey: nil,
                              type: .REAL,
-                             null: true,
+                             nullable: true,
                              defaultValue: .NULL,
                              references: nil),
             ColumnDefinition(name: "admin",
                              primaryKey: nil,
                              type: .TEXT,
-                             null: false,
+                             nullable: false,
                              defaultValue: .numericLiteral("0"),
                              references: nil),
             ColumnDefinition(name: "manager_id",
                              primaryKey: nil, type: .INTEGER,
-                             null: true,
+                             nullable: true,
                              defaultValue: .NULL,
                              references: .init(table: "users", column: "manager_id", primaryKey: "id", onUpdate: nil, onDelete: nil)),
             ColumnDefinition(name: "created_at",
                              primaryKey: nil,
                              type: .TEXT,
-                             null: true,
+                             nullable: true,
                              defaultValue: .NULL,
                              references: nil)
         ])
@@ -64,7 +64,7 @@ class ConnectionSchemaTests: SQLiteTestCase {
                 name: "id",
                 primaryKey: .init(autoIncrement: true, onConflict: .IGNORE),
                 type: .INTEGER,
-                null: true,
+                nullable: true,
                 defaultValue: .NULL,
                 references: nil)
             ]
@@ -80,7 +80,7 @@ class ConnectionSchemaTests: SQLiteTestCase {
                     name: "id",
                     primaryKey: .init(autoIncrement: false),
                     type: .INTEGER,
-                    null: true,
+                    nullable: true,
                     defaultValue: .NULL,
                     references: nil)
             ]
diff --git a/Tests/SQLiteTests/Schema/SchemaChangerTests.swift b/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
index 4d4f8d50..038a8844 100644
--- a/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
+++ b/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
@@ -90,7 +90,11 @@ class SchemaChangerTests: SQLiteTestCase {
     }
 
     func test_add_column() throws {
-        let newColumn = ColumnDefinition(name: "new_column", primaryKey: nil, type: .TEXT, null: true, defaultValue: .NULL, references: nil)
+        let column = Expression<String>("new_column")
+        let newColumn = ColumnDefinition(name: "new_column",
+                                         type: .TEXT,
+                                         nullable: true,
+                                         defaultValue: .stringLiteral("foo"))
 
         try schemaChanger.alter(table: "users") { table in
             table.add(newColumn)
@@ -98,6 +102,24 @@ class SchemaChangerTests: SQLiteTestCase {
 
         let columns = try db.columnInfo(table: "users")
         XCTAssertTrue(columns.contains(newColumn))
+
+        XCTAssertEqual(try db.pluck(users.select(column))?[column], "foo")
+    }
+
+    func test_add_column_primary_key_fails() throws {
+        let newColumn = ColumnDefinition(name: "new_column",
+                                         primaryKey: .init(autoIncrement: false, onConflict: nil),
+                                         type: .TEXT)
+
+        XCTAssertThrowsError(try schemaChanger.alter(table: "users") { table in
+            table.add(newColumn)
+        }) { error in
+            if case SchemaChanger.Error.invalidColumnDefinition(_) = error {
+                XCTAssertEqual("Invalid column definition: can not add primary key column", error.localizedDescription)
+            } else {
+                XCTFail("invalid error: \(error)")
+            }
+        }
     }
 
     func test_drop_table() throws {
@@ -110,4 +132,10 @@ class SchemaChangerTests: SQLiteTestCase {
             }
         }
     }
+
+    func test_rename_table() throws {
+        try schemaChanger.rename(table: "users", to: "users_new")
+        let users_new = Table("users_new")
+        XCTAssertEqual((try db.scalar(users_new.count)) as Int, 1)
+    }
 }
diff --git a/Tests/SQLiteTests/Schema/SchemaDefinitionsTests.swift b/Tests/SQLiteTests/Schema/SchemaDefinitionsTests.swift
index 645bfc34..9c8c3041 100644
--- a/Tests/SQLiteTests/Schema/SchemaDefinitionsTests.swift
+++ b/Tests/SQLiteTests/Schema/SchemaDefinitionsTests.swift
@@ -6,26 +6,26 @@ class ColumnDefinitionTests: XCTestCase {
     var expected: String!
 
     static let definitions: [(ColumnDefinition, String)] = [
-        (ColumnDefinition(name: "id", primaryKey: .init(), type: .INTEGER, null: false, defaultValue: .NULL, references: nil),
+        (ColumnDefinition(name: "id", primaryKey: .init(), type: .INTEGER, nullable: false, defaultValue: .NULL, references: nil),
         "\"id\" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL"),
 
-        (ColumnDefinition(name: "other_id", primaryKey: nil, type: .INTEGER, null: false, defaultValue: .NULL,
+        (ColumnDefinition(name: "other_id", primaryKey: nil, type: .INTEGER, nullable: false, defaultValue: .NULL,
                           references: .init(table: "other_table", column: "", primaryKey: "some_id", onUpdate: nil, onDelete: nil)),
         "\"other_id\" INTEGER NOT NULL REFERENCES \"other_table\" (\"some_id\")"),
 
-        (ColumnDefinition(name: "text", primaryKey: nil, type: .TEXT, null: true, defaultValue: .NULL, references: nil),
+        (ColumnDefinition(name: "text", primaryKey: nil, type: .TEXT, nullable: true, defaultValue: .NULL, references: nil),
         "\"text\" TEXT"),
 
-        (ColumnDefinition(name: "text", primaryKey: nil, type: .TEXT, null: false, defaultValue: .NULL, references: nil),
+        (ColumnDefinition(name: "text", primaryKey: nil, type: .TEXT, nullable: false, defaultValue: .NULL, references: nil),
         "\"text\" TEXT NOT NULL"),
 
-        (ColumnDefinition(name: "text_column", primaryKey: nil, type: .TEXT, null: true, defaultValue: .stringLiteral("fo\"o"), references: nil),
+        (ColumnDefinition(name: "text_column", primaryKey: nil, type: .TEXT, nullable: true, defaultValue: .stringLiteral("fo\"o"), references: nil),
         "\"text_column\" TEXT DEFAULT 'fo\"o'"),
 
-        (ColumnDefinition(name: "integer_column", primaryKey: nil, type: .INTEGER, null: true, defaultValue: .numericLiteral("123"), references: nil),
+        (ColumnDefinition(name: "integer_column", primaryKey: nil, type: .INTEGER, nullable: true, defaultValue: .numericLiteral("123"), references: nil),
         "\"integer_column\" INTEGER DEFAULT 123"),
 
-        (ColumnDefinition(name: "real_column", primaryKey: nil, type: .REAL, null: true, defaultValue: .numericLiteral("123.123"), references: nil),
+        (ColumnDefinition(name: "real_column", primaryKey: nil, type: .REAL, nullable: true, defaultValue: .numericLiteral("123.123"), references: nil),
         "\"real_column\" REAL DEFAULT 123.123")
     ]
 
@@ -184,8 +184,8 @@ class ForeignKeyDefinitionTests: XCTestCase {
 class TableDefinitionTests: XCTestCase {
     func test_quoted_columnList() {
         let definition = TableDefinition(name: "foo", columns: [
-            ColumnDefinition(name: "id", primaryKey: .init(), type: .INTEGER, null: false, defaultValue: .NULL, references: nil),
-            ColumnDefinition(name: "baz", primaryKey: nil, type: .INTEGER, null: false, defaultValue: .NULL, references: nil)
+            ColumnDefinition(name: "id", primaryKey: .init(), type: .INTEGER, nullable: false, defaultValue: .NULL, references: nil),
+            ColumnDefinition(name: "baz", primaryKey: nil, type: .INTEGER, nullable: false, defaultValue: .NULL, references: nil)
         ], indexes: [])
 
         XCTAssertEqual(definition.quotedColumnList, """
@@ -195,7 +195,7 @@ class TableDefinitionTests: XCTestCase {
 
     func test_toSQL() {
         let definition = TableDefinition(name: "foo", columns: [
-            ColumnDefinition(name: "id", primaryKey: .init(), type: .INTEGER, null: false, defaultValue: .NULL, references: nil)
+            ColumnDefinition(name: "id", primaryKey: .init(), type: .INTEGER, nullable: false, defaultValue: .NULL, references: nil)
         ], indexes: [])
 
         XCTAssertEqual(definition.toSQL(), """
@@ -205,7 +205,7 @@ class TableDefinitionTests: XCTestCase {
 
     func test_toSQL_temp_table() {
         let definition = TableDefinition(name: "foo", columns: [
-            ColumnDefinition(name: "id", primaryKey: .init(), type: .INTEGER, null: false, defaultValue: .NULL, references: nil)
+            ColumnDefinition(name: "id", primaryKey: .init(), type: .INTEGER, nullable: false, defaultValue: .NULL, references: nil)
         ], indexes: [])
 
         XCTAssertEqual(definition.toSQL(temporary: true), """
@@ -222,11 +222,11 @@ class TableDefinitionTests: XCTestCase {
 
     func test_copySQL() {
         let from = TableDefinition(name: "from_table", columns: [
-            ColumnDefinition(name: "id", primaryKey: .init(), type: .INTEGER, null: false, defaultValue: .NULL, references: nil)
+            ColumnDefinition(name: "id", primaryKey: .init(), type: .INTEGER, nullable: false, defaultValue: .NULL, references: nil)
         ], indexes: [])
 
         let to = TableDefinition(name: "to_table", columns: [
-            ColumnDefinition(name: "id", primaryKey: .init(), type: .INTEGER, null: false, defaultValue: .NULL, references: nil)
+            ColumnDefinition(name: "id", primaryKey: .init(), type: .INTEGER, nullable: false, defaultValue: .NULL, references: nil)
         ], indexes: [])
 
         XCTAssertEqual(from.copySQL(to: to), """

From 17a2cb8985b7188a61ddee5af1f9d549b23ab54d Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Tue, 26 Jul 2022 00:47:57 +0200
Subject: [PATCH 188/326] Lint

---
 Tests/SQLiteTests/Schema/SchemaDefinitionsTests.swift | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/Tests/SQLiteTests/Schema/SchemaDefinitionsTests.swift b/Tests/SQLiteTests/Schema/SchemaDefinitionsTests.swift
index 9c8c3041..75321440 100644
--- a/Tests/SQLiteTests/Schema/SchemaDefinitionsTests.swift
+++ b/Tests/SQLiteTests/Schema/SchemaDefinitionsTests.swift
@@ -22,10 +22,12 @@ class ColumnDefinitionTests: XCTestCase {
         (ColumnDefinition(name: "text_column", primaryKey: nil, type: .TEXT, nullable: true, defaultValue: .stringLiteral("fo\"o"), references: nil),
         "\"text_column\" TEXT DEFAULT 'fo\"o'"),
 
-        (ColumnDefinition(name: "integer_column", primaryKey: nil, type: .INTEGER, nullable: true, defaultValue: .numericLiteral("123"), references: nil),
+        (ColumnDefinition(name: "integer_column", primaryKey: nil, type: .INTEGER, nullable: true,
+                          defaultValue: .numericLiteral("123"), references: nil),
         "\"integer_column\" INTEGER DEFAULT 123"),
 
-        (ColumnDefinition(name: "real_column", primaryKey: nil, type: .REAL, nullable: true, defaultValue: .numericLiteral("123.123"), references: nil),
+        (ColumnDefinition(name: "real_column", primaryKey: nil, type: .REAL, nullable: true,
+                          defaultValue: .numericLiteral("123.123"), references: nil),
         "\"real_column\" REAL DEFAULT 123.123")
     ]
 

From b5a83033d5727fb7a201e3700baa678e2e861bca Mon Sep 17 00:00:00 2001
From: Andrew Vanderbilt <105957236+a-vanderbilt@users.noreply.github.com>
Date: Fri, 29 Jul 2022 20:12:52 -0400
Subject: [PATCH 189/326] Fixed typo and added/clarified code comment.

Small typo fix (iff -> if). Added code comment, then modified existing code comment to make the intent of the code clearer.
---
 Documentation/Index.md | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/Documentation/Index.md b/Documentation/Index.md
index 29b18c96..80f8d9f9 100644
--- a/Documentation/Index.md
+++ b/Documentation/Index.md
@@ -288,11 +288,13 @@ On macOS, you can use your app’s **Application Support** directory:
 
 
 ```swift
+
+// set the path corresponding to application support.
 var path = NSSearchPathForDirectoriesInDomains(
     .applicationSupportDirectory, .userDomainMask, true
 ).first! + "/" + Bundle.main.bundleIdentifier!
 
-// create parent directory iff it doesn’t exist
+// create parent directory inside application support if it doesn’t exist
 try FileManager.default.createDirectory(
 atPath: path, withIntermediateDirectories: true, attributes: nil
 )

From f2609b9e520c4dcf23b5f57e5b1e9dd1a12db147 Mon Sep 17 00:00:00 2001
From: Nathan Fallet <contact@nathanfallet.me>
Date: Sat, 30 Jul 2022 09:09:59 +0200
Subject: [PATCH 190/326] Quick update

---
 Documentation/Index.md | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/Documentation/Index.md b/Documentation/Index.md
index 80f8d9f9..f90b5960 100644
--- a/Documentation/Index.md
+++ b/Documentation/Index.md
@@ -288,8 +288,7 @@ On macOS, you can use your app’s **Application Support** directory:
 
 
 ```swift
-
-// set the path corresponding to application support.
+// set the path corresponding to application support
 var path = NSSearchPathForDirectoriesInDomains(
     .applicationSupportDirectory, .userDomainMask, true
 ).first! + "/" + Bundle.main.bundleIdentifier!

From 607a37aca433adffc83cc1c10ddd07a09774b322 Mon Sep 17 00:00:00 2001
From: Goban <gobanest@gmail.com>
Date: Tue, 30 Aug 2022 12:04:13 +0900
Subject: [PATCH 191/326] Update Index.md

'pathForResource(_:ofType:)' has been renamed to 'path(forResource:ofType:)'
---
 Documentation/Index.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Documentation/Index.md b/Documentation/Index.md
index f90b5960..d6374e6e 100644
--- a/Documentation/Index.md
+++ b/Documentation/Index.md
@@ -308,7 +308,7 @@ into your Xcode project and added it to your application target), you can
 establish a _read-only_ connection to it.
 
 ```swift
-let path = Bundle.main.pathForResource("db", ofType: "sqlite3")!
+let path = Bundle.main.path(forResource: "db", ofType: "sqlite3")!
 
 let db = try Connection(path, readonly: true)
 ```

From 6a02c202c34c4dc81272a3e792025ee6257bdee9 Mon Sep 17 00:00:00 2001
From: Nathan Fallet <contact@nathanfallet.me>
Date: Tue, 30 Aug 2022 09:06:19 +0200
Subject: [PATCH 192/326] Fix linting

---
 Sources/SQLite/Typed/Query.swift | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/Sources/SQLite/Typed/Query.swift b/Sources/SQLite/Typed/Query.swift
index cfa7544e..7cb2aef3 100644
--- a/Sources/SQLite/Typed/Query.swift
+++ b/Sources/SQLite/Typed/Query.swift
@@ -1035,11 +1035,9 @@ extension Connection {
                 select.clauses.select = (false, [Expression<Void>(literal: "*") as Expressible])
                 let queries = [select] + query.clauses.join.map { $0.query }
                 if !namespace.isEmpty {
-                    for q in queries {
-                        if q.tableName().expression.template == namespace {
-                            try expandGlob(true)(q)
-                            continue column
-                        }
+                    for q in queries where q.tableName().expression.template == namespace {
+                        try expandGlob(true)(q)
+                        continue column
                     }
                     throw QueryError.noSuchTable(name: namespace)
                 }

From f25798f3d047a923976d55f4ba9b8ab9e25e0428 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Sun, 18 Sep 2022 23:51:49 +0200
Subject: [PATCH 193/326] SQLiteVersion type

---
 SQLite.xcodeproj/project.pbxproj              | 10 +++++++++
 Sources/SQLite/Core/Connection+Pragmas.swift  |  5 ++---
 Sources/SQLite/Core/SQLiteVersion.swift       | 22 +++++++++++++++++++
 Sources/SQLite/Schema/Connection+Schema.swift | 16 ++++++++++++++
 Sources/SQLite/Schema/SchemaChanger.swift     |  4 ++--
 .../Core/Connection+PragmaTests.swift         |  2 +-
 .../Schema/SchemaChangerTests.swift           |  4 ++--
 7 files changed, 55 insertions(+), 8 deletions(-)
 create mode 100644 Sources/SQLite/Core/SQLiteVersion.swift

diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj
index bc79473e..d2005575 100644
--- a/SQLite.xcodeproj/project.pbxproj
+++ b/SQLite.xcodeproj/project.pbxproj
@@ -199,6 +199,10 @@
 		997DF2AF287FC06D00F8DF95 /* Query+with.swift in Sources */ = {isa = PBXBuildFile; fileRef = 997DF2AD287FC06D00F8DF95 /* Query+with.swift */; };
 		997DF2B0287FC06D00F8DF95 /* Query+with.swift in Sources */ = {isa = PBXBuildFile; fileRef = 997DF2AD287FC06D00F8DF95 /* Query+with.swift */; };
 		997DF2B1287FC06D00F8DF95 /* Query+with.swift in Sources */ = {isa = PBXBuildFile; fileRef = 997DF2AD287FC06D00F8DF95 /* Query+with.swift */; };
+		DB7C5DA628D7C9B6006395CF /* SQLiteVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB7C5DA528D7C9B6006395CF /* SQLiteVersion.swift */; };
+		DB7C5DA728D7C9B6006395CF /* SQLiteVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB7C5DA528D7C9B6006395CF /* SQLiteVersion.swift */; };
+		DB7C5DA828D7C9B6006395CF /* SQLiteVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB7C5DA528D7C9B6006395CF /* SQLiteVersion.swift */; };
+		DB7C5DA928D7C9B6006395CF /* SQLiteVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB7C5DA528D7C9B6006395CF /* SQLiteVersion.swift */; };
 		EE247AD71C3F04ED00AE3E12 /* SQLite.h in Headers */ = {isa = PBXBuildFile; fileRef = EE247AD61C3F04ED00AE3E12 /* SQLite.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		EE247ADE1C3F04ED00AE3E12 /* SQLite.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EE247AD31C3F04ED00AE3E12 /* SQLite.framework */; };
 		EE247B031C3F06E900AE3E12 /* Blob.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AEE1C3F06E900AE3E12 /* Blob.swift */; };
@@ -325,6 +329,7 @@
 		49EB68C31F7B3CB400D89D40 /* Coding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Coding.swift; sourceTree = "<group>"; };
 		997DF2AD287FC06D00F8DF95 /* Query+with.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Query+with.swift"; sourceTree = "<group>"; };
 		A121AC451CA35C79005A31D1 /* SQLite.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SQLite.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+		DB7C5DA528D7C9B6006395CF /* SQLiteVersion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SQLiteVersion.swift; sourceTree = "<group>"; };
 		EE247AD31C3F04ED00AE3E12 /* SQLite.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SQLite.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		EE247AD61C3F04ED00AE3E12 /* SQLite.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SQLite.h; sourceTree = "<group>"; };
 		EE247AD81C3F04ED00AE3E12 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@@ -569,6 +574,7 @@
 				19A175A9CB446640AE6F2200 /* Connection+Aggregation.swift */,
 				3DF7B78728842972005DD8CA /* Connection+Attach.swift */,
 				3DF7B790288449BA005DD8CA /* URIQueryParameter.swift */,
+				DB7C5DA528D7C9B6006395CF /* SQLiteVersion.swift */,
 				19A17F285B767BFACD96714B /* Connection+Pragmas.swift */,
 			);
 			path = Core;
@@ -954,6 +960,7 @@
 				02A43A9A22738CF100FEC494 /* Backup.swift in Sources */,
 				19A17FF4A10B44D3937C8CAC /* Errors.swift in Sources */,
 				19A1737286A74F3CF7412906 /* DateAndTimeFunctions.swift in Sources */,
+				DB7C5DA828D7C9B6006395CF /* SQLiteVersion.swift in Sources */,
 				19A17073552293CA063BEA66 /* Result.swift in Sources */,
 				997DF2B0287FC06D00F8DF95 /* Query+with.swift in Sources */,
 				19A179B59450FE7C4811AB8A /* Connection+Aggregation.swift in Sources */,
@@ -1012,6 +1019,7 @@
 				3D67B3F71DB246D700A4F4C6 /* Foundation.swift in Sources */,
 				3D67B3F81DB246D700A4F4C6 /* Helpers.swift in Sources */,
 				3D67B3E91DB246D100A4F4C6 /* Statement.swift in Sources */,
+				DB7C5DA928D7C9B6006395CF /* SQLiteVersion.swift in Sources */,
 				3D67B3EA1DB246D100A4F4C6 /* Value.swift in Sources */,
 				3D67B3EB1DB246D100A4F4C6 /* FTS4.swift in Sources */,
 				3D67B3EC1DB246D100A4F4C6 /* RTree.swift in Sources */,
@@ -1069,6 +1077,7 @@
 				02A43A9822738CF100FEC494 /* Backup.swift in Sources */,
 				19A1792C0520D4E83C2EB075 /* Errors.swift in Sources */,
 				19A17E29278A12BC4F542506 /* DateAndTimeFunctions.swift in Sources */,
+				DB7C5DA628D7C9B6006395CF /* SQLiteVersion.swift in Sources */,
 				19A173EFEF0B3BD0B3ED406C /* Result.swift in Sources */,
 				997DF2AE287FC06D00F8DF95 /* Query+with.swift in Sources */,
 				19A176376CB6A94759F7980A /* Connection+Aggregation.swift in Sources */,
@@ -1146,6 +1155,7 @@
 				02A43A9922738CF100FEC494 /* Backup.swift in Sources */,
 				19A17490543609FCED53CACC /* Errors.swift in Sources */,
 				19A17152E32A9585831E3FE0 /* DateAndTimeFunctions.swift in Sources */,
+				DB7C5DA728D7C9B6006395CF /* SQLiteVersion.swift in Sources */,
 				19A17F1B3F0A3C96B5ED6D64 /* Result.swift in Sources */,
 				997DF2AF287FC06D00F8DF95 /* Query+with.swift in Sources */,
 				19A170ACC97B19730FB7BA4D /* Connection+Aggregation.swift in Sources */,
diff --git a/Sources/SQLite/Core/Connection+Pragmas.swift b/Sources/SQLite/Core/Connection+Pragmas.swift
index 43ff9610..8f6d854b 100644
--- a/Sources/SQLite/Core/Connection+Pragmas.swift
+++ b/Sources/SQLite/Core/Connection+Pragmas.swift
@@ -1,7 +1,6 @@
 import Foundation
 
 public typealias UserVersion = Int32
-public typealias SQLiteVersion = (Int, Int, Int)
 
 public extension Connection {
     /// The user version of the database.
@@ -21,9 +20,9 @@ public extension Connection {
         guard let version = (try? scalar("SELECT sqlite_version()")) as? String,
               let splits = .some(version.split(separator: ".", maxSplits: 3)), splits.count == 3,
               let major = Int(splits[0]), let minor = Int(splits[1]), let point = Int(splits[2]) else {
-            return (0, 0, 0)
+            return .zero
         }
-        return (major, minor, point)
+        return .init(major: major, minor: minor, point: point)
     }
 
     // Changing the foreign_keys setting affects the execution of all statements prepared using the database
diff --git a/Sources/SQLite/Core/SQLiteVersion.swift b/Sources/SQLite/Core/SQLiteVersion.swift
new file mode 100644
index 00000000..fa7358da
--- /dev/null
+++ b/Sources/SQLite/Core/SQLiteVersion.swift
@@ -0,0 +1,22 @@
+import Foundation
+
+public struct SQLiteVersion: Comparable, CustomStringConvertible {
+    public let major: Int
+    public let minor: Int
+    public var point: Int = 0
+
+    public var description: String {
+        "SQLite \(major).\(minor).\(point)"
+    }
+
+    public static func <(lhs: SQLiteVersion, rhs: SQLiteVersion) -> Bool {
+        lhs.tuple < rhs.tuple
+    }
+
+    public static func ==(lhs: SQLiteVersion, rhs: SQLiteVersion) -> Bool {
+        lhs.tuple == rhs.tuple
+    }
+
+    static var zero: SQLiteVersion = .init(major: 0, minor: 0)
+    private var tuple: (Int, Int, Int) { (major, minor, point) }
+}
diff --git a/Sources/SQLite/Schema/Connection+Schema.swift b/Sources/SQLite/Schema/Connection+Schema.swift
index 378fad68..d92236ee 100644
--- a/Sources/SQLite/Schema/Connection+Schema.swift
+++ b/Sources/SQLite/Schema/Connection+Schema.swift
@@ -80,6 +80,16 @@ extension Connection {
         }
     }
 
+    func tableInfo() throws -> [String] {
+        try run("SELECT tbl_name FROM sqlite_master WHERE type = 'table'").compactMap { row in
+            if let name = row[0] as? String, !name.starts(with: "sqlite_") {
+                return name
+            } else {
+                return nil
+            }
+        }
+    }
+
     // https://sqlite.org/pragma.html#pragma_foreign_key_check
 
     // There are four columns in each result row.
@@ -99,6 +109,12 @@ extension Connection {
         }
     }
 
+    // https://sqlite.org/pragma.html#pragma_integrity_check
+    func integrityCheck() throws -> [String] {
+        try run("PRAGMA integrity_check").compactMap { $0[0] as? String }.filter { $0 != "ok" }
+    }
+
+
     private func createTableSQL(name: String) throws  -> String? {
         try run("""
                 SELECT sql FROM sqlite_master WHERE name=? AND type='table'
diff --git a/Sources/SQLite/Schema/SchemaChanger.swift b/Sources/SQLite/Schema/SchemaChanger.swift
index daba2f60..991e3338 100644
--- a/Sources/SQLite/Schema/SchemaChanger.swift
+++ b/Sources/SQLite/Schema/SchemaChanger.swift
@@ -52,9 +52,9 @@ public class SchemaChanger: CustomStringConvertible {
             switch self {
             case .addColumn(let definition):
                 return "ALTER TABLE \(table.quote()) ADD COLUMN \(definition.toSQL())"
-            case .renameColumn(let from, let to) where version >= (3, 25, 0):
+            case .renameColumn(let from, let to) where version >= .init(major: 3, minor: 25):
                 return "ALTER TABLE \(table.quote()) RENAME COLUMN \(from.quote()) TO \(to.quote())"
-            case .dropColumn(let column) where version >= (3, 35, 0):
+            case .dropColumn(let column) where version >= .init(major: 3, minor: 35):
                 return "ALTER TABLE \(table.quote()) DROP COLUMN \(column.quote())"
             default: return nil
             }
diff --git a/Tests/SQLiteTests/Core/Connection+PragmaTests.swift b/Tests/SQLiteTests/Core/Connection+PragmaTests.swift
index 2d0742c9..2bcdb6af 100644
--- a/Tests/SQLiteTests/Core/Connection+PragmaTests.swift
+++ b/Tests/SQLiteTests/Core/Connection+PragmaTests.swift
@@ -19,7 +19,7 @@ class ConnectionPragmaTests: SQLiteTestCase {
     }
 
     func test_sqlite_version() {
-        XCTAssertTrue(db.sqliteVersion >= (3, 0, 0))
+        XCTAssertTrue(db.sqliteVersion >= .init(major: 3, minor: 0))
     }
 
     func test_foreignKeys_defaults_to_false() {
diff --git a/Tests/SQLiteTests/Schema/SchemaChangerTests.swift b/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
index 038a8844..738588fd 100644
--- a/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
+++ b/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
@@ -58,7 +58,7 @@ class SchemaChangerTests: SQLiteTestCase {
     }
 
     func test_remove_column_legacy() throws {
-        schemaChanger = .init(connection: db, version: (3, 24, 0)) // DROP COLUMN introduced in 3.35.0
+        schemaChanger = .init(connection: db, version: .init(major: 3, minor: 24)) // DROP COLUMN introduced in 3.35.0
 
         try schemaChanger.alter(table: "users") { table in
             table.remove("age")
@@ -78,7 +78,7 @@ class SchemaChangerTests: SQLiteTestCase {
     }
 
     func test_rename_column_legacy() throws {
-        schemaChanger = .init(connection: db, version: (3, 24, 0)) // RENAME COLUMN introduced in 3.25.0
+        schemaChanger = .init(connection: db, version: .init(major: 3, minor: 24)) // RENAME COLUMN introduced in 3.25.0
 
         try schemaChanger.alter(table: "users") { table in
             table.rename("age", to: "age2")

From ec35c7ea060f08a3f36382564f2a69206da7aa2b Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Sun, 18 Sep 2022 23:57:43 +0200
Subject: [PATCH 194/326] Lint

---
 Sources/SQLite/Schema/Connection+Schema.swift | 1 -
 Sources/SQLite/Typed/Query.swift              | 8 +++-----
 2 files changed, 3 insertions(+), 6 deletions(-)

diff --git a/Sources/SQLite/Schema/Connection+Schema.swift b/Sources/SQLite/Schema/Connection+Schema.swift
index d92236ee..9c945e18 100644
--- a/Sources/SQLite/Schema/Connection+Schema.swift
+++ b/Sources/SQLite/Schema/Connection+Schema.swift
@@ -114,7 +114,6 @@ extension Connection {
         try run("PRAGMA integrity_check").compactMap { $0[0] as? String }.filter { $0 != "ok" }
     }
 
-
     private func createTableSQL(name: String) throws  -> String? {
         try run("""
                 SELECT sql FROM sqlite_master WHERE name=? AND type='table'
diff --git a/Sources/SQLite/Typed/Query.swift b/Sources/SQLite/Typed/Query.swift
index cfa7544e..7cb2aef3 100644
--- a/Sources/SQLite/Typed/Query.swift
+++ b/Sources/SQLite/Typed/Query.swift
@@ -1035,11 +1035,9 @@ extension Connection {
                 select.clauses.select = (false, [Expression<Void>(literal: "*") as Expressible])
                 let queries = [select] + query.clauses.join.map { $0.query }
                 if !namespace.isEmpty {
-                    for q in queries {
-                        if q.tableName().expression.template == namespace {
-                            try expandGlob(true)(q)
-                            continue column
-                        }
+                    for q in queries where q.tableName().expression.template == namespace {
+                        try expandGlob(true)(q)
+                        continue column
                     }
                     throw QueryError.noSuchTable(name: namespace)
                 }

From 00a86686c4cf88caf09cde6ad0f50d4064a8b653 Mon Sep 17 00:00:00 2001
From: Michael Henry Pantaleon <me@iamkel.net>
Date: Wed, 12 Oct 2022 23:20:23 +1100
Subject: [PATCH 195/326] Fix playground example as the prepare func can throw
 an error

---
 SQLite.playground/Contents.swift | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/SQLite.playground/Contents.swift b/SQLite.playground/Contents.swift
index c089076d..945adb50 100644
--- a/SQLite.playground/Contents.swift
+++ b/SQLite.playground/Contents.swift
@@ -99,5 +99,5 @@ db.createAggregation("customConcat",
                      initialValue: "users:",
                      reduce: reduce,
                      result: { $0 })
-let result = db.prepare("SELECT customConcat(email) FROM users").scalar() as! String
+let result = try db.prepare("SELECT customConcat(email) FROM users").scalar() as! String
 print(result)

From d7c26353330ea851691f3f0359aa13e7dbedced5 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Sun, 16 Oct 2022 02:24:31 +0200
Subject: [PATCH 196/326] Split into classes, improve queries

---
 Makefile                                      |   2 +
 SQLite.xcodeproj/project.pbxproj              |  10 +
 Sources/SQLite/Schema/Connection+Schema.swift | 129 ++-----------
 Sources/SQLite/Schema/SchemaChanger.swift     |   6 +-
 Sources/SQLite/Schema/SchemaDefinitions.swift | 107 ++++++----
 Sources/SQLite/Schema/SchemaReader.swift      | 182 ++++++++++++++++++
 Sources/SQLite/Typed/Query.swift              |  17 ++
 Tests/SQLiteTests/Core/ConnectionTests.swift  |   2 +-
 .../Schema/Connection+SchemaTests.swift       | 140 +++-----------
 .../Schema/SchemaChangerTests.swift           |  24 +--
 .../Schema/SchemaDefinitionsTests.swift       | 132 ++++++++-----
 .../Schema/SchemaReaderTests.swift            | 180 +++++++++++++++++
 12 files changed, 603 insertions(+), 328 deletions(-)
 create mode 100644 Sources/SQLite/Schema/SchemaReader.swift
 create mode 100644 Tests/SQLiteTests/Schema/SchemaReaderTests.swift

diff --git a/Makefile b/Makefile
index c62da000..74bf5d18 100644
--- a/Makefile
+++ b/Makefile
@@ -18,6 +18,8 @@ build:
 
 lint:
 	swiftlint --strict
+lint-fix:
+	swiftlint lint fix
 
 test:
 ifdef XCPRETTY
diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj
index d2005575..a58e0887 100644
--- a/SQLite.xcodeproj/project.pbxproj
+++ b/SQLite.xcodeproj/project.pbxproj
@@ -199,6 +199,10 @@
 		997DF2AF287FC06D00F8DF95 /* Query+with.swift in Sources */ = {isa = PBXBuildFile; fileRef = 997DF2AD287FC06D00F8DF95 /* Query+with.swift */; };
 		997DF2B0287FC06D00F8DF95 /* Query+with.swift in Sources */ = {isa = PBXBuildFile; fileRef = 997DF2AD287FC06D00F8DF95 /* Query+with.swift */; };
 		997DF2B1287FC06D00F8DF95 /* Query+with.swift in Sources */ = {isa = PBXBuildFile; fileRef = 997DF2AD287FC06D00F8DF95 /* Query+with.swift */; };
+		DB58B21128FB864300F8EEA4 /* SchemaReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB58B21028FB864300F8EEA4 /* SchemaReader.swift */; };
+		DB58B21228FB864300F8EEA4 /* SchemaReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB58B21028FB864300F8EEA4 /* SchemaReader.swift */; };
+		DB58B21328FB864300F8EEA4 /* SchemaReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB58B21028FB864300F8EEA4 /* SchemaReader.swift */; };
+		DB58B21428FB864300F8EEA4 /* SchemaReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB58B21028FB864300F8EEA4 /* SchemaReader.swift */; };
 		DB7C5DA628D7C9B6006395CF /* SQLiteVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB7C5DA528D7C9B6006395CF /* SQLiteVersion.swift */; };
 		DB7C5DA728D7C9B6006395CF /* SQLiteVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB7C5DA528D7C9B6006395CF /* SQLiteVersion.swift */; };
 		DB7C5DA828D7C9B6006395CF /* SQLiteVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB7C5DA528D7C9B6006395CF /* SQLiteVersion.swift */; };
@@ -329,6 +333,7 @@
 		49EB68C31F7B3CB400D89D40 /* Coding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Coding.swift; sourceTree = "<group>"; };
 		997DF2AD287FC06D00F8DF95 /* Query+with.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Query+with.swift"; sourceTree = "<group>"; };
 		A121AC451CA35C79005A31D1 /* SQLite.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SQLite.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+		DB58B21028FB864300F8EEA4 /* SchemaReader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SchemaReader.swift; sourceTree = "<group>"; };
 		DB7C5DA528D7C9B6006395CF /* SQLiteVersion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SQLiteVersion.swift; sourceTree = "<group>"; };
 		EE247AD31C3F04ED00AE3E12 /* SQLite.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SQLite.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		EE247AD61C3F04ED00AE3E12 /* SQLite.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SQLite.h; sourceTree = "<group>"; };
@@ -429,6 +434,7 @@
 		19A1792D261C689FC988A90A /* Schema */ = {
 			isa = PBXGroup;
 			children = (
+				DB58B21028FB864300F8EEA4 /* SchemaReader.swift */,
 				19A171B262DDE8718513CFDA /* SchemaChanger.swift */,
 				19A17268AE67B746B96AC125 /* SchemaDefinitions.swift */,
 				19A170A97B51DC5EE365F3C5 /* Connection+Schema.swift */,
@@ -968,6 +974,7 @@
 				19A1740EACD47904AA24B8DC /* SchemaDefinitions.swift in Sources */,
 				19A1750EF4A5F92954A451FF /* Connection+Schema.swift in Sources */,
 				19A17986405D9A875698408F /* Connection+Pragmas.swift in Sources */,
+				DB58B21328FB864300F8EEA4 /* SchemaReader.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -1018,6 +1025,7 @@
 				997DF2B1287FC06D00F8DF95 /* Query+with.swift in Sources */,
 				3D67B3F71DB246D700A4F4C6 /* Foundation.swift in Sources */,
 				3D67B3F81DB246D700A4F4C6 /* Helpers.swift in Sources */,
+				DB58B21428FB864300F8EEA4 /* SchemaReader.swift in Sources */,
 				3D67B3E91DB246D100A4F4C6 /* Statement.swift in Sources */,
 				DB7C5DA928D7C9B6006395CF /* SQLiteVersion.swift in Sources */,
 				3D67B3EA1DB246D100A4F4C6 /* Value.swift in Sources */,
@@ -1085,6 +1093,7 @@
 				19A17BA13FD35F058787B7D3 /* SchemaDefinitions.swift in Sources */,
 				19A174506543905D71BF0518 /* Connection+Schema.swift in Sources */,
 				19A17018F250343BD0F9F4B0 /* Connection+Pragmas.swift in Sources */,
+				DB58B21128FB864300F8EEA4 /* SchemaReader.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -1163,6 +1172,7 @@
 				19A17B0DF1DDB6BBC9C95D64 /* SchemaDefinitions.swift in Sources */,
 				19A17F0BF02896E1664F4090 /* Connection+Schema.swift in Sources */,
 				19A1760CE25615CA015E2E5F /* Connection+Pragmas.swift in Sources */,
+				DB58B21228FB864300F8EEA4 /* SchemaReader.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
diff --git a/Sources/SQLite/Schema/Connection+Schema.swift b/Sources/SQLite/Schema/Connection+Schema.swift
index 9c945e18..868b40d5 100644
--- a/Sources/SQLite/Schema/Connection+Schema.swift
+++ b/Sources/SQLite/Schema/Connection+Schema.swift
@@ -1,96 +1,7 @@
 import Foundation
 
-extension Connection {
-    // https://sqlite.org/pragma.html#pragma_table_info
-    //
-    // This pragma returns one row for each column in the named table. Columns in the result set include the
-    // column name, data type, whether or not the column can be NULL, and the default value for the column. The
-    // "pk" column in the result set is zero for columns that are not part of the primary key, and is the
-    // index of the column in the primary key for columns that are part of the primary key.
-    func columnInfo(table: String) throws  -> [ColumnDefinition] {
-        func parsePrimaryKey(column: String) throws -> ColumnDefinition.PrimaryKey? {
-            try createTableSQL(name: table).flatMap { .init(sql: $0) }
-        }
-
-        let foreignKeys: [String: [ColumnDefinition.ForeignKey]] =
-            Dictionary(grouping: try foreignKeyInfo(table: table), by: { $0.column })
-
-        return try run("PRAGMA table_info(\(table.quote()))").compactMap { row -> ColumnDefinition? in
-            guard let name = row[1] as? String,
-                  let type = row[2] as? String,
-                  let notNull = row[3] as? Int64,
-                  let defaultValue = row[4] as? String?,
-                  let primaryKey = row[5] as? Int64 else { return nil }
-            return ColumnDefinition(name: name,
-                                    primaryKey: primaryKey == 1 ? try parsePrimaryKey(column: name) : nil,
-                                    type: ColumnDefinition.Affinity.from(type),
-                                    nullable: notNull == 0,
-                                    defaultValue: .from(defaultValue),
-                                    references: foreignKeys[name]?.first)
-        }
-    }
-
-    func indexInfo(table: String) throws -> [IndexDefinition] {
-        func indexSQL(name: String) throws  -> String? {
-            try run("""
-                    SELECT sql FROM sqlite_master WHERE name=? AND type='index'
-                      UNION ALL
-                    SELECT sql FROM sqlite_temp_master WHERE name=? AND type='index'
-                    """, name, name)
-                    .compactMap { row in row[0] as? String }
-                    .first
-        }
-
-        func columns(name: String) throws -> [String] {
-            try run("PRAGMA index_info(\(name.quote()))").compactMap { row in
-                row[2] as? String
-            }
-        }
-
-        return try run("PRAGMA index_list(\(table.quote()))").compactMap { row -> IndexDefinition? in
-            guard let name = row[1] as? String,
-                  let unique = row[2] as? Int64,
-                    // Indexes SQLite creates implicitly for internal use start with "sqlite_".
-                    // See https://www.sqlite.org/fileformat2.html#intschema
-                  !name.starts(with: "sqlite_") else {
-                return nil
-            }
-            return .init(table: table,
-                         name: name,
-                         unique: unique == 1,
-                         columns: try columns(name: name),
-                         indexSQL: try indexSQL(name: name))
-        }
-    }
-
-    func foreignKeyInfo(table: String) throws -> [ColumnDefinition.ForeignKey] {
-        try run("PRAGMA foreign_key_list(\(table.quote()))").compactMap { row in
-            if let table = row[2] as? String,      // table
-               let column = row[3] as? String,     // from
-               let primaryKey = row[4] as? String, // to
-               let onUpdate = row[5] as? String,
-               let onDelete = row[6] as? String {
-                return .init(table: table, column: column, primaryKey: primaryKey,
-                             onUpdate: onUpdate == TableBuilder.Dependency.noAction.rawValue ? nil : onUpdate,
-                             onDelete: onDelete == TableBuilder.Dependency.noAction.rawValue ? nil : onDelete
-                )
-            } else {
-                return nil
-            }
-        }
-    }
-
-    func tableInfo() throws -> [String] {
-        try run("SELECT tbl_name FROM sqlite_master WHERE type = 'table'").compactMap { row in
-            if let name = row[0] as? String, !name.starts(with: "sqlite_") {
-                return name
-            } else {
-                return nil
-            }
-        }
-    }
-
-    // https://sqlite.org/pragma.html#pragma_foreign_key_check
+public extension Connection {
+    var schemaReader: SchemaReader { SchemaReader(connection: self) }
 
     // There are four columns in each result row.
     // The first column is the name of the table that
@@ -99,28 +10,24 @@ extension Connection {
     // invalid REFERENCES clause, or NULL if the child table is a WITHOUT ROWID table.
     // The third column is the name of the table that is referred to.
     // The fourth column is the index of the specific foreign key constraint that failed.
-    func foreignKeyCheck() throws -> [ForeignKeyError] {
-        try run("PRAGMA foreign_key_check").compactMap { row -> ForeignKeyError? in
-            guard let table = row[0] as? String,
-                  let rowId = row[1] as? Int64,
-                  let target = row[2] as? String else { return nil }
-
-            return ForeignKeyError(from: table, rowId: rowId, to: target)
-        }
+    //
+    // https://sqlite.org/pragma.html#pragma_foreign_key_check
+    func foreignKeyCheck(table: String? = nil) throws -> [ForeignKeyError] {
+        try run("PRAGMA foreign_key_check" + (table.map { "(\($0.quote()))" } ?? ""))
+            .compactMap { row -> ForeignKeyError? in
+                guard let table = row[0] as? String,
+                      let rowId = row[1] as? Int64,
+                      let target = row[2] as? String else { return nil }
+
+                return ForeignKeyError(from: table, rowId: rowId, to: target)
+            }
     }
 
+    // This pragma does a low-level formatting and consistency check of the database.
     // https://sqlite.org/pragma.html#pragma_integrity_check
-    func integrityCheck() throws -> [String] {
-        try run("PRAGMA integrity_check").compactMap { $0[0] as? String }.filter { $0 != "ok" }
-    }
-
-    private func createTableSQL(name: String) throws  -> String? {
-        try run("""
-                SELECT sql FROM sqlite_master WHERE name=? AND type='table'
-                  UNION ALL
-                SELECT sql FROM sqlite_temp_master WHERE name=? AND type='table'
-        """, name, name)
-            .compactMap { row in row[0] as? String }
-            .first
+    func integrityCheck(table: String? = nil, maxErrors: Int? = nil) throws -> [String] {
+        try run("PRAGMA integrity_check" + (table.map { "(\($0.quote()))" } ?? ""))
+            .compactMap { $0[0] as? String }
+            .filter { $0 != "ok" }
     }
 }
diff --git a/Sources/SQLite/Schema/SchemaChanger.swift b/Sources/SQLite/Schema/SchemaChanger.swift
index 991e3338..b557da0a 100644
--- a/Sources/SQLite/Schema/SchemaChanger.swift
+++ b/Sources/SQLite/Schema/SchemaChanger.swift
@@ -109,6 +109,7 @@ public class SchemaChanger: CustomStringConvertible {
     }
 
     private let connection: Connection
+    private let schemaReader: SchemaReader
     private let version: SQLiteVersion
     static let tempPrefix = "tmp_"
     typealias Block = () throws -> Void
@@ -127,6 +128,7 @@ public class SchemaChanger: CustomStringConvertible {
 
     init(connection: Connection, version: SQLiteVersion) {
         self.connection = connection
+        schemaReader = connection.schemaReader
         self.version = version
     }
 
@@ -196,8 +198,8 @@ public class SchemaChanger: CustomStringConvertible {
     private func copyTable(from: String, to: String, options: Options = .default, operation: Operation?) throws {
         let fromDefinition = TableDefinition(
             name: from,
-            columns: try connection.columnInfo(table: from),
-            indexes: try connection.indexInfo(table: from)
+            columns: try schemaReader.columnDefinitions(table: from),
+            indexes: try schemaReader.indexDefinitions(table: from)
         )
         let toDefinition   = fromDefinition
                 .apply(.renameTable(to))
diff --git a/Sources/SQLite/Schema/SchemaDefinitions.swift b/Sources/SQLite/Schema/SchemaDefinitions.swift
index a06487b2..fe2e3931 100644
--- a/Sources/SQLite/Schema/SchemaDefinitions.swift
+++ b/Sources/SQLite/Schema/SchemaDefinitions.swift
@@ -10,6 +10,29 @@ struct TableDefinition: Equatable {
     }
 }
 
+// https://sqlite.org/schematab.html#interpretation_of_the_schema_table
+public struct ObjectDefinition: Equatable {
+    public enum ObjectType: String {
+        case table, index, view, trigger
+    }
+    public let type: ObjectType
+
+    // The name of the object
+    public let name: String
+
+    // The name of a table or view that the object is associated with.
+    //  * For a table or view, a copy of the name column.
+    //  * For an index, the name of the table that is indexed
+    //  * For a trigger, the column stores the name of the table or view that causes the trigger to fire.
+    public let tableName: String
+
+    // The page number of the root b-tree page for tables and indexes, otherwise 0 or NULL
+    public let rootpage: Int64
+
+    // SQL text that describes the object (NULL for the internal indexes)
+    public let sql: String?
+}
+
 // https://sqlite.org/syntax/column-def.html
 // column-name -> type-name -> column-constraint*
 public struct ColumnDefinition: Equatable {
@@ -29,8 +52,8 @@ public struct ColumnDefinition: Equatable {
             rawValue
         }
 
-        static func from(_ string: String) -> Affinity {
-            Affinity.allCases.first { $0.rawValue.lowercased() == string.lowercased() } ?? TEXT
+        init(_ string: String) {
+            self = Affinity.allCases.first { $0.rawValue.lowercased() == string.lowercased() } ?? .TEXT
         }
     }
 
@@ -41,8 +64,9 @@ public struct ColumnDefinition: Equatable {
         case IGNORE
         case REPLACE
 
-        static func from(_ string: String) -> OnConflict? {
-            OnConflict.allCases.first { $0.rawValue == string }
+        init?(_ string: String) {
+            guard let value = (OnConflict.allCases.first { $0.rawValue == string }) else { return nil }
+            self = value
         }
     }
 
@@ -59,17 +83,20 @@ public struct ColumnDefinition: Equatable {
         }
 
         init?(sql: String) {
-            if let match = PrimaryKey.pattern.firstMatch(in: sql, range: NSRange(location: 0, length: sql.count)) {
-                let conflict = match.range(at: 1)
-                var onConflict: ColumnDefinition.OnConflict?
-                if conflict.location != NSNotFound {
-                    onConflict = .from((sql as NSString).substring(with: conflict))
-                }
-                let autoIncrement = match.range(at: 2).location != NSNotFound
-                self.init(autoIncrement: autoIncrement, onConflict: onConflict)
-            } else {
+            guard let match = PrimaryKey.pattern.firstMatch(
+                in: sql,
+                range: NSRange(location: 0, length: sql.count)) else {
                 return nil
             }
+            let conflict = match.range(at: 1)
+            let onConflict: ColumnDefinition.OnConflict?
+            if conflict.location != NSNotFound {
+                onConflict = OnConflict((sql as NSString).substring(with: conflict))
+            } else {
+                onConflict = nil
+            }
+            let autoIncrement = match.range(at: 2).location != NSNotFound
+            self.init(autoIncrement: autoIncrement, onConflict: onConflict)
         }
     }
 
@@ -138,28 +165,19 @@ public enum LiteralValue: Equatable, CustomStringConvertible {
     case CURRENT_TIMESTAMP
     // swiftlint:enable identifier_name
 
-    static func from(_ string: String?) -> LiteralValue {
-        func parse(_ value: String) -> LiteralValue {
-            if let match = singleQuote.firstMatch(in: value, range: NSRange(location: 0, length: value.count)) {
-                return stringLiteral((value as NSString).substring(with: match.range(at: 1)).replacingOccurrences(of: "''", with: "'"))
-            } else if let match = doubleQuote.firstMatch(in: value, range: NSRange(location: 0, length: value.count)) {
-                return stringLiteral((value as NSString).substring(with: match.range(at: 1)).replacingOccurrences(of: "\"\"", with: "\""))
-            } else if let match = blob.firstMatch(in: value, range: NSRange(location: 0, length: value.count)) {
-                return blobLiteral((value as NSString).substring(with: match.range(at: 1)))
-            } else {
-                return numericLiteral(value)
-            }
+    init(_ string: String?) {
+        guard let string = string else {
+            self = .NULL
+            return
         }
-        guard let string = string else { return NULL }
-
         switch string {
-        case "NULL": return NULL
-        case "TRUE": return TRUE
-        case "FALSE": return FALSE
-        case "CURRENT_TIME": return CURRENT_TIME
-        case "CURRENT_TIMESTAMP": return CURRENT_TIMESTAMP
-        case "CURRENT_DATE": return CURRENT_DATE
-        default: return parse(string)
+        case "NULL": self = .NULL
+        case "TRUE": self = .TRUE
+        case "FALSE": self = .FALSE
+        case "CURRENT_TIME": self = .CURRENT_TIME
+        case "CURRENT_TIMESTAMP": self = .CURRENT_TIMESTAMP
+        case "CURRENT_DATE": self = .CURRENT_DATE
+        default: self = LiteralValue.parse(string)
         }
     }
 
@@ -184,6 +202,17 @@ public enum LiteralValue: Equatable, CustomStringConvertible {
             return block(self)
         }
     }
+    private static func parse(_ string: String) -> LiteralValue {
+        if let match = LiteralValue.singleQuote.firstMatch(in: string, range: NSRange(location: 0, length: string.count)) {
+            return .stringLiteral((string as NSString).substring(with: match.range(at: 1)).replacingOccurrences(of: "''", with: "'"))
+        } else if let match = LiteralValue.doubleQuote.firstMatch(in: string, range: NSRange(location: 0, length: string.count)) {
+            return .stringLiteral((string as NSString).substring(with: match.range(at: 1)).replacingOccurrences(of: "\"\"", with: "\""))
+        } else if let match = LiteralValue.blob.firstMatch(in: string, range: NSRange(location: 0, length: string.count)) {
+            return .blobLiteral((string as NSString).substring(with: match.range(at: 1)))
+        } else {
+            return .numericLiteral(string)
+        }
+    }
 }
 
 // https://sqlite.org/lang_createindex.html
@@ -259,9 +288,9 @@ public struct IndexDefinition: Equatable {
 }
 
 public struct ForeignKeyError: CustomStringConvertible {
-    let from: String
-    let rowId: Int64
-    let to: String
+    public let from: String
+    public let rowId: Int64
+    public let to: String
 
     public var description: String {
         "\(from) [\(rowId)] => \(to)"
@@ -270,7 +299,7 @@ public struct ForeignKeyError: CustomStringConvertible {
 
 extension TableDefinition {
     func toSQL(temporary: Bool = false) -> String {
-        assert(columns.count > 0, "no columns to create")
+        precondition(columns.count > 0, "no columns to create")
 
         return ([
             "CREATE",
@@ -285,8 +314,8 @@ extension TableDefinition {
     }
 
     func copySQL(to: TableDefinition) -> String {
-        assert(columns.count > 0, "no columns to copy")
-        assert(columns.count == to.columns.count, "column counts don't match")
+        precondition(columns.count > 0)
+        precondition(columns.count == to.columns.count, "column counts don't match")
         return "INSERT INTO \(to.name.quote()) (\(to.quotedColumnList)) SELECT \(quotedColumnList) FROM \(name.quote())"
     }
 }
diff --git a/Sources/SQLite/Schema/SchemaReader.swift b/Sources/SQLite/Schema/SchemaReader.swift
new file mode 100644
index 00000000..3f02eeae
--- /dev/null
+++ b/Sources/SQLite/Schema/SchemaReader.swift
@@ -0,0 +1,182 @@
+import Foundation
+
+public class SchemaReader {
+    private let connection: Connection
+
+    init(connection: Connection) {
+        self.connection = connection
+    }
+
+    // https://sqlite.org/pragma.html#pragma_table_info
+    //
+    // This pragma returns one row for each column in the named table. Columns in the result set include the
+    // column name, data type, whether or not the column can be NULL, and the default value for the column. The
+    // "pk" column in the result set is zero for columns that are not part of the primary key, and is the
+    // index of the column in the primary key for columns that are part of the primary key.
+    public func columnDefinitions(table: String) throws  -> [ColumnDefinition] {
+        func parsePrimaryKey(column: String) throws -> ColumnDefinition.PrimaryKey? {
+            try createTableSQL(name: table).flatMap { .init(sql: $0) }
+        }
+
+        let foreignKeys: [String: [ColumnDefinition.ForeignKey]] =
+            Dictionary(grouping: try foreignKeys(table: table), by: { $0.column })
+
+        return try connection.prepareRowIterator("PRAGMA table_info(\(table.quote()))")
+            .map { (row: Row) -> ColumnDefinition in
+                ColumnDefinition(
+                    name: row[TableInfoTable.nameColumn],
+                    primaryKey: row[TableInfoTable.primaryKeyColumn] == 1 ?
+                        try parsePrimaryKey(column: row[TableInfoTable.nameColumn]) : nil,
+                    type: ColumnDefinition.Affinity(row[TableInfoTable.typeColumn]),
+                    nullable: row[TableInfoTable.notNullColumn] == 0,
+                    defaultValue: LiteralValue(row[TableInfoTable.defaultValueColumn]),
+                    references: foreignKeys[row[TableInfoTable.nameColumn]]?.first
+                )
+            }
+    }
+
+    public func objectDefinitions(name: String? = nil,
+                                  type: ObjectDefinition.ObjectType? = nil,
+                                  temp: Bool = false) throws -> [ObjectDefinition] {
+        var query: QueryType = temp ? SchemaTable.tempName : SchemaTable.name
+        if let name = name {
+            query = query.where(SchemaTable.nameColumn == name)
+        }
+        if let type = type {
+            query = query.where(SchemaTable.typeColumn == type.rawValue)
+        }
+        return try connection.prepare(query).map { row -> ObjectDefinition in
+            guard let type = ObjectDefinition.ObjectType(rawValue: row[SchemaTable.typeColumn]) else {
+                fatalError("unexpected type")
+            }
+            return ObjectDefinition(
+                type: type,
+                name: row[SchemaTable.nameColumn],
+                tableName: row[SchemaTable.nameColumn],
+                rootpage: row[SchemaTable.rootPageColumn] ?? 0,
+                sql: row[SchemaTable.sqlColumn]
+            )
+        }
+    }
+
+    public func indexDefinitions(table: String) throws -> [IndexDefinition] {
+        func indexSQL(name: String) throws -> String? {
+            try objectDefinitions(name: name, type: .index)
+                .compactMap(\.sql)
+                .first
+        }
+
+        func columns(name: String) throws -> [String] {
+            try connection.prepareRowIterator("PRAGMA index_info(\(name.quote()))")
+                .compactMap { row in
+                    row[IndexInfoTable.nameColumn]
+                }
+        }
+
+        return try connection.prepareRowIterator("PRAGMA index_list(\(table.quote()))")
+            .compactMap { row -> IndexDefinition? in
+                let name = row[IndexListTable.nameColumn]
+                guard !name.starts(with: "sqlite_") else {
+                    // Indexes SQLite creates implicitly for internal use start with "sqlite_".
+                    // See https://www.sqlite.org/fileformat2.html#intschema
+                    return nil
+                }
+                return IndexDefinition(
+                    table: table,
+                    name: name,
+                    unique: row[IndexListTable.uniqueColumn] == 1,
+                    columns: try columns(name: name),
+                    indexSQL: try indexSQL(name: name)
+                )
+            }
+    }
+
+    func foreignKeys(table: String) throws -> [ColumnDefinition.ForeignKey] {
+        try connection.prepareRowIterator("PRAGMA foreign_key_list(\(table.quote()))")
+            .map { row in
+                ColumnDefinition.ForeignKey(
+                    table: row[ForeignKeyListTable.tableColumn],
+                    column: row[ForeignKeyListTable.fromColumn],
+                    primaryKey: row[ForeignKeyListTable.toColumn],
+                    onUpdate: row[ForeignKeyListTable.onUpdateColumn] == TableBuilder.Dependency.noAction.rawValue
+                            ? nil : row[ForeignKeyListTable.onUpdateColumn],
+                    onDelete: row[ForeignKeyListTable.onDeleteColumn] == TableBuilder.Dependency.noAction.rawValue
+                            ? nil : row[ForeignKeyListTable.onDeleteColumn]
+                )
+            }
+    }
+
+    func tableDefinitions() throws -> [TableDefinition] {
+        try objectDefinitions(type: .table)
+                .map { table in
+                    TableDefinition(
+                            name: table.name,
+                            columns: try columnDefinitions(table: table.name),
+                            indexes: try indexDefinitions(table: table.name)
+                    )
+                }
+    }
+
+    private func createTableSQL(name: String) throws -> String? {
+        try (
+            objectDefinitions(name: name, type: .table) +
+            objectDefinitions(name: name, type: .table, temp: true)
+        ).compactMap(\.sql).first
+    }
+}
+
+private class SchemaTable {
+    internal static let name = Table("sqlite_schema", database: "main")
+    internal static let tempName = Table("sqlite_schema", database: "temp")
+
+    static let typeColumn = Expression<String>("type")
+    static let nameColumn = Expression<String>("name")
+    static let tableNameColumn = Expression<String>("tbl_name")
+    static let rootPageColumn = Expression<Int64?>("rootpage")
+    static let sqlColumn = Expression<String?>("sql")
+}
+
+private class TableInfoTable {
+    static let idColumn = Expression<Int64>("cid")
+    static let nameColumn = Expression<String>("name")
+    static let typeColumn = Expression<String>("type")
+    static let notNullColumn = Expression<Int64>("notnull")
+    static let defaultValueColumn = Expression<String?>("dflt_value")
+    static let primaryKeyColumn = Expression<Int64?>("pk")
+}
+
+private class IndexInfoTable {
+    // The rank of the column within the index. (0 means left-most.)
+    static let seqnoColumn = Expression<Int64>("seqno")
+    // The rank of the column within the table being indexed.
+    // A value of -1 means rowid and a value of -2 means that an expression is being used.
+    static let cidColumn = Expression<Int64>("cid")
+    // The name of the column being indexed. This columns is NULL if the column is the rowid or an expression.
+    static let nameColumn = Expression<String?>("name")
+}
+
+private class IndexListTable {
+    // A sequence number assigned to each index for internal tracking purposes.
+    static let seqColumn = Expression<Int64>("seq")
+    // The name of the index
+    static let nameColumn = Expression<String>("name")
+    // "1" if the index is UNIQUE and "0" if not.
+    static let uniqueColumn = Expression<Int64>("unique")
+    // "c" if the index was created by a CREATE INDEX statement,
+    // "u" if the index was created by a UNIQUE constraint, or
+    // "pk" if the index was created by a PRIMARY KEY constraint.
+    static let originColumn = Expression<String>("origin")
+    // "1" if the index is a partial index and "0" if not.
+    static let partialColumn = Expression<Int64>("partial")
+}
+
+private class ForeignKeyListTable {
+    static let idColumn = Expression<Int64>("id")
+    static let seqColumn = Expression<Int64>("seq")
+    static let tableColumn = Expression<String>("table")
+    static let fromColumn = Expression<String>("from")
+    static let toColumn = Expression<String>("to")
+    static let onUpdateColumn = Expression<String>("on_update")
+    static let onDeleteColumn = Expression<String>("on_delete")
+    static let matchColumn = Expression<String>("match")
+}
diff --git a/Sources/SQLite/Typed/Query.swift b/Sources/SQLite/Typed/Query.swift
index 7cb2aef3..04665f39 100644
--- a/Sources/SQLite/Typed/Query.swift
+++ b/Sources/SQLite/Typed/Query.swift
@@ -991,6 +991,15 @@ public struct RowIterator: FailableIterator {
         }
         return elements
     }
+
+    public func compactMap<T>(_ transform: (Element) throws -> T?) throws -> [T] {
+        var elements = [T]()
+        while let row = try failableNext() {
+            guard let element = try transform(row) else { continue }
+            elements.append(element)
+        }
+        return elements
+    }
 }
 
 extension Connection {
@@ -1012,6 +1021,14 @@ extension Connection {
         return RowIterator(statement: statement, columnNames: try columnNamesForQuery(query))
     }
 
+    public func prepareRowIterator(_ statement: String, bindings: Binding?...) throws -> RowIterator {
+        try prepare(statement, bindings).prepareRowIterator()
+    }
+
+    public func prepareRowIterator(_ statement: String, bindings: [Binding?]) throws -> RowIterator {
+        try prepare(statement, bindings).prepareRowIterator()
+    }
+
     private func columnNamesForQuery(_ query: QueryType) throws -> [String: Int] {
         var (columnNames, idx) = ([String: Int](), 0)
         column: for each in query.clauses.select.columns {
diff --git a/Tests/SQLiteTests/Core/ConnectionTests.swift b/Tests/SQLiteTests/Core/ConnectionTests.swift
index e9ceff08..9d623f3f 100644
--- a/Tests/SQLiteTests/Core/ConnectionTests.swift
+++ b/Tests/SQLiteTests/Core/ConnectionTests.swift
@@ -399,7 +399,7 @@ class ConnectionTests: SQLiteTestCase {
         XCTAssertEqual(1, try db.scalar("SELECT ? = ? COLLATE \"NO DIACRITIC\"", "cafe", "café") as? Int64)
     }
 
-    func test_interrupt_interruptsLongRunningQuery() throws {
+    func XXX_test_interrupt_interruptsLongRunningQuery() throws {
         let semaphore = DispatchSemaphore(value: 0)
         db.createFunction("sleep") { _ in
             DispatchQueue.global(qos: .background).async {
diff --git a/Tests/SQLiteTests/Schema/Connection+SchemaTests.swift b/Tests/SQLiteTests/Schema/Connection+SchemaTests.swift
index 6eded6b2..96300529 100644
--- a/Tests/SQLiteTests/Schema/Connection+SchemaTests.swift
+++ b/Tests/SQLiteTests/Schema/Connection+SchemaTests.swift
@@ -8,127 +8,43 @@ class ConnectionSchemaTests: SQLiteTestCase {
         try createUsersTable()
     }
 
-    func test_column_info() throws {
-        let columns = try db.columnInfo(table: "users")
-        XCTAssertEqual(columns, [
-            ColumnDefinition(name: "id",
-                             primaryKey: .init(autoIncrement: false, onConflict: nil),
-                             type: .INTEGER,
-                             nullable: true,
-                             defaultValue: .NULL,
-                             references: nil),
-            ColumnDefinition(name: "email",
-                             primaryKey: nil,
-                             type: .TEXT,
-                             nullable: false,
-                             defaultValue: .NULL,
-                             references: nil),
-            ColumnDefinition(name: "age",
-                             primaryKey: nil,
-                             type: .INTEGER,
-                             nullable: true,
-                             defaultValue: .NULL,
-                             references: nil),
-            ColumnDefinition(name: "salary",
-                             primaryKey: nil,
-                             type: .REAL,
-                             nullable: true,
-                             defaultValue: .NULL,
-                             references: nil),
-            ColumnDefinition(name: "admin",
-                             primaryKey: nil,
-                             type: .TEXT,
-                             nullable: false,
-                             defaultValue: .numericLiteral("0"),
-                             references: nil),
-            ColumnDefinition(name: "manager_id",
-                             primaryKey: nil, type: .INTEGER,
-                             nullable: true,
-                             defaultValue: .NULL,
-                             references: .init(table: "users", column: "manager_id", primaryKey: "id", onUpdate: nil, onDelete: nil)),
-            ColumnDefinition(name: "created_at",
-                             primaryKey: nil,
-                             type: .TEXT,
-                             nullable: true,
-                             defaultValue: .NULL,
-                             references: nil)
-        ])
+    func test_foreignKeyCheck() throws {
+        let errors = try db.foreignKeyCheck()
+        XCTAssert(errors.isEmpty)
     }
 
-    func test_column_info_parses_conflict_modifier() throws {
-        try db.run("CREATE TABLE t (\"id\" INTEGER PRIMARY KEY ON CONFLICT IGNORE AUTOINCREMENT)")
-
-        XCTAssertEqual(
-            try db.columnInfo(table: "t"), [
-            ColumnDefinition(
-                name: "id",
-                primaryKey: .init(autoIncrement: true, onConflict: .IGNORE),
-                type: .INTEGER,
-                nullable: true,
-                defaultValue: .NULL,
-                references: nil)
-            ]
-        )
+    func test_foreignKeyCheck_with_table() throws {
+        let errors = try db.foreignKeyCheck(table: "users")
+        XCTAssert(errors.isEmpty)
     }
 
-    func test_column_info_detects_missing_autoincrement() throws {
-        try db.run("CREATE TABLE t (\"id\" INTEGER PRIMARY KEY)")
-
-        XCTAssertEqual(
-            try db.columnInfo(table: "t"), [
-            ColumnDefinition(
-                    name: "id",
-                    primaryKey: .init(autoIncrement: false),
-                    type: .INTEGER,
-                    nullable: true,
-                    defaultValue: .NULL,
-                    references: nil)
-            ]
-        )
+    func test_foreignKeyCheck_table_not_found() throws {
+        XCTAssertThrowsError(try db.foreignKeyCheck(table: "xxx")) { error in
+            guard case Result.error(let message, _, _) = error else {
+                assertionFailure("invalid error type")
+                return
+            }
+            XCTAssertEqual(message, "no such table: xxx")
+        }
     }
 
-    func test_index_info_no_index() throws {
-        let indexes = try db.indexInfo(table: "users")
-        XCTAssertTrue(indexes.isEmpty)
+    func test_integrityCheck_global() throws {
+        let results = try db.integrityCheck()
+        XCTAssert(results.isEmpty)
     }
 
-    func test_index_info_with_index() throws {
-        try db.run("CREATE UNIQUE INDEX index_users ON users (age DESC) WHERE age IS NOT NULL")
-        let indexes = try db.indexInfo(table: "users")
-
-        XCTAssertEqual(indexes, [
-            IndexDefinition(
-                table: "users",
-                name: "index_users",
-                unique: true,
-                columns: ["age"],
-                where: "age IS NOT NULL",
-                orders: ["age": .DESC]
-            )
-        ])
+    func test_integrityCheck_table() throws {
+        let results = try db.integrityCheck(table: "users")
+        XCTAssert(results.isEmpty)
     }
 
-    func test_foreign_key_info_empty() throws {
-        try db.run("CREATE TABLE t (\"id\" INTEGER PRIMARY KEY)")
-
-        let foreignKeys = try db.foreignKeyInfo(table: "t")
-        XCTAssertTrue(foreignKeys.isEmpty)
-    }
-
-    func test_foreign_key_info() throws {
-        let linkTable = Table("test_links")
-
-        let idColumn = SQLite.Expression<Int64>("id")
-        let testIdColumn = SQLite.Expression<Int64>("test_id")
-
-        try db.run(linkTable.create(block: { definition in
-            definition.column(idColumn, primaryKey: .autoincrement)
-            definition.column(testIdColumn, unique: false, check: nil, references: users, Expression<Int64>("id"))
-        }))
-
-        let foreignKeys = try db.foreignKeyInfo(table: "test_links")
-        XCTAssertEqual(foreignKeys, [
-            .init(table: "users", column: "test_id", primaryKey: "id", onUpdate: nil, onDelete: nil)
-        ])
+    func test_integrityCheck_table_not_found() throws {
+        XCTAssertThrowsError(try db.integrityCheck(table: "xxx")) { error in
+            guard case Result.error(let message, _, _) = error else {
+                assertionFailure("invalid error type")
+                return
+            }
+            XCTAssertEqual(message, "no such table: xxx")
+        }
     }
 }
diff --git a/Tests/SQLiteTests/Schema/SchemaChangerTests.swift b/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
index 738588fd..af8b6565 100644
--- a/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
+++ b/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
@@ -3,6 +3,7 @@ import XCTest
 
 class SchemaChangerTests: SQLiteTestCase {
     var schemaChanger: SchemaChanger!
+    var schemaReader: SchemaReader!
 
     override func setUpWithError() throws {
         try super.setUpWithError()
@@ -10,32 +11,33 @@ class SchemaChangerTests: SQLiteTestCase {
 
         try insertUsers("bob")
 
+        schemaReader = SchemaReader(connection: db)
         schemaChanger = SchemaChanger(connection: db)
     }
 
     func test_empty_migration_does_not_change_column_definitions() throws {
-        let previous = try db.columnInfo(table: "users")
+        let previous = try schemaReader.columnDefinitions(table: "users")
         try schemaChanger.alter(table: "users") { _ in
         }
-        let current = try db.columnInfo(table: "users")
+        let current = try schemaReader.columnDefinitions(table: "users")
 
         XCTAssertEqual(previous, current)
     }
 
     func test_empty_migration_does_not_change_index_definitions() throws {
-        let previous = try db.indexInfo(table: "users")
+        let previous = try schemaReader.indexDefinitions(table: "users")
         try schemaChanger.alter(table: "users") { _ in
         }
-        let current = try db.indexInfo(table: "users")
+        let current = try schemaReader.indexDefinitions(table: "users")
 
         XCTAssertEqual(previous, current)
     }
 
     func test_empty_migration_does_not_change_foreign_key_definitions() throws {
-        let previous = try db.foreignKeyInfo(table: "users")
+        let previous = try schemaReader.foreignKeys(table: "users")
         try schemaChanger.alter(table: "users") { _ in
         }
-        let current = try db.foreignKeyInfo(table: "users")
+        let current = try schemaReader.foreignKeys(table: "users")
 
         XCTAssertEqual(previous, current)
     }
@@ -53,7 +55,7 @@ class SchemaChangerTests: SQLiteTestCase {
         try schemaChanger.alter(table: "users") { table in
             table.remove("age")
         }
-        let columns = try db.columnInfo(table: "users").map(\.name)
+        let columns = try schemaReader.columnDefinitions(table: "users").map(\.name)
         XCTAssertFalse(columns.contains("age"))
     }
 
@@ -63,7 +65,7 @@ class SchemaChangerTests: SQLiteTestCase {
         try schemaChanger.alter(table: "users") { table in
             table.remove("age")
         }
-        let columns = try db.columnInfo(table: "users").map(\.name)
+        let columns = try schemaReader.columnDefinitions(table: "users").map(\.name)
         XCTAssertFalse(columns.contains("age"))
     }
 
@@ -72,7 +74,7 @@ class SchemaChangerTests: SQLiteTestCase {
             table.rename("age", to: "age2")
         }
 
-        let columns = try db.columnInfo(table: "users").map(\.name)
+        let columns = try schemaReader.columnDefinitions(table: "users").map(\.name)
         XCTAssertFalse(columns.contains("age"))
         XCTAssertTrue(columns.contains("age2"))
     }
@@ -84,7 +86,7 @@ class SchemaChangerTests: SQLiteTestCase {
             table.rename("age", to: "age2")
         }
 
-        let columns = try db.columnInfo(table: "users").map(\.name)
+        let columns = try schemaReader.columnDefinitions(table: "users").map(\.name)
         XCTAssertFalse(columns.contains("age"))
         XCTAssertTrue(columns.contains("age2"))
     }
@@ -100,7 +102,7 @@ class SchemaChangerTests: SQLiteTestCase {
             table.add(newColumn)
         }
 
-        let columns = try db.columnInfo(table: "users")
+        let columns = try schemaReader.columnDefinitions(table: "users")
         XCTAssertTrue(columns.contains(newColumn))
 
         XCTAssertEqual(try db.pluck(users.select(column))?[column], "foo")
diff --git a/Tests/SQLiteTests/Schema/SchemaDefinitionsTests.swift b/Tests/SQLiteTests/Schema/SchemaDefinitionsTests.swift
index 75321440..384aab3f 100644
--- a/Tests/SQLiteTests/Schema/SchemaDefinitionsTests.swift
+++ b/Tests/SQLiteTests/Schema/SchemaDefinitionsTests.swift
@@ -5,37 +5,38 @@ class ColumnDefinitionTests: XCTestCase {
     var definition: ColumnDefinition!
     var expected: String!
 
-    static let definitions: [(ColumnDefinition, String)] = [
-        (ColumnDefinition(name: "id", primaryKey: .init(), type: .INTEGER, nullable: false, defaultValue: .NULL, references: nil),
-        "\"id\" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL"),
+    static let definitions: [(String, ColumnDefinition)] = [
+        ("\"id\" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL",
+        ColumnDefinition(name: "id", primaryKey: .init(), type: .INTEGER, nullable: false, defaultValue: .NULL, references: nil)),
 
-        (ColumnDefinition(name: "other_id", primaryKey: nil, type: .INTEGER, nullable: false, defaultValue: .NULL,
-                          references: .init(table: "other_table", column: "", primaryKey: "some_id", onUpdate: nil, onDelete: nil)),
-        "\"other_id\" INTEGER NOT NULL REFERENCES \"other_table\" (\"some_id\")"),
+        ("\"other_id\" INTEGER NOT NULL REFERENCES \"other_table\" (\"some_id\")",
+        ColumnDefinition(name: "other_id", primaryKey: nil, type: .INTEGER, nullable: false, defaultValue: .NULL,
+                         references: .init(table: "other_table", column: "", primaryKey: "some_id", onUpdate: nil, onDelete: nil))),
 
-        (ColumnDefinition(name: "text", primaryKey: nil, type: .TEXT, nullable: true, defaultValue: .NULL, references: nil),
-        "\"text\" TEXT"),
+        ("\"text\" TEXT",
+        ColumnDefinition(name: "text", primaryKey: nil, type: .TEXT, nullable: true, defaultValue: .NULL, references: nil)),
 
-        (ColumnDefinition(name: "text", primaryKey: nil, type: .TEXT, nullable: false, defaultValue: .NULL, references: nil),
-        "\"text\" TEXT NOT NULL"),
+        ("\"text\" TEXT NOT NULL",
+        ColumnDefinition(name: "text", primaryKey: nil, type: .TEXT, nullable: false, defaultValue: .NULL, references: nil)),
 
-        (ColumnDefinition(name: "text_column", primaryKey: nil, type: .TEXT, nullable: true, defaultValue: .stringLiteral("fo\"o"), references: nil),
-        "\"text_column\" TEXT DEFAULT 'fo\"o'"),
+        ("\"text_column\" TEXT DEFAULT 'fo\"o'",
+        ColumnDefinition(name: "text_column", primaryKey: nil, type: .TEXT, nullable: true,
+        defaultValue: .stringLiteral("fo\"o"), references: nil)),
 
-        (ColumnDefinition(name: "integer_column", primaryKey: nil, type: .INTEGER, nullable: true,
-                          defaultValue: .numericLiteral("123"), references: nil),
-        "\"integer_column\" INTEGER DEFAULT 123"),
+        ("\"integer_column\" INTEGER DEFAULT 123",
+        ColumnDefinition(name: "integer_column", primaryKey: nil, type: .INTEGER, nullable: true,
+                          defaultValue: .numericLiteral("123"), references: nil)),
 
-        (ColumnDefinition(name: "real_column", primaryKey: nil, type: .REAL, nullable: true,
-                          defaultValue: .numericLiteral("123.123"), references: nil),
-        "\"real_column\" REAL DEFAULT 123.123")
+        ("\"real_column\" REAL DEFAULT 123.123",
+        ColumnDefinition(name: "real_column", primaryKey: nil, type: .REAL, nullable: true,
+                          defaultValue: .numericLiteral("123.123"), references: nil))
     ]
 
     #if !os(Linux)
     override class var defaultTestSuite: XCTestSuite {
         let suite = XCTestSuite(forTestCaseClass: ColumnDefinitionTests.self)
 
-        for (column, expected) in ColumnDefinitionTests.definitions {
+        for (expected, column) in ColumnDefinitionTests.definitions {
             let test = ColumnDefinitionTests(selector: #selector(verify))
             test.definition = column
             test.expected = expected
@@ -51,14 +52,17 @@ class ColumnDefinitionTests: XCTestCase {
 }
 
 class AffinityTests: XCTestCase {
-    func test_from() {
-        XCTAssertEqual(ColumnDefinition.Affinity.from("TEXT"), .TEXT)
-        XCTAssertEqual(ColumnDefinition.Affinity.from("text"), .TEXT)
-        XCTAssertEqual(ColumnDefinition.Affinity.from("INTEGER"), .INTEGER)
+    func test_init() {
+        XCTAssertEqual(ColumnDefinition.Affinity("TEXT"), .TEXT)
+        XCTAssertEqual(ColumnDefinition.Affinity("text"), .TEXT)
+        XCTAssertEqual(ColumnDefinition.Affinity("INTEGER"), .INTEGER)
+        XCTAssertEqual(ColumnDefinition.Affinity("BLOB"), .BLOB)
+        XCTAssertEqual(ColumnDefinition.Affinity("REAL"), .REAL)
+        XCTAssertEqual(ColumnDefinition.Affinity("NUMERIC"), .NUMERIC)
     }
 
     func test_returns_TEXT_for_unknown_type() {
-        XCTAssertEqual(ColumnDefinition.Affinity.from("baz"), .TEXT)
+        XCTAssertEqual(ColumnDefinition.Affinity("baz"), .TEXT)
     }
 }
 
@@ -215,13 +219,6 @@ class TableDefinitionTests: XCTestCase {
                                                           """)
     }
 
-    /*
-    func test_throws_an_error_when_columns_are_empty() {
-        let empty = TableDefinition(name: "empty", columns: [], indexes: [])
-        XCTAssertThrowsError(empty.toSQL())
-    }
-     */
-
     func test_copySQL() {
         let from = TableDefinition(name: "from_table", columns: [
             ColumnDefinition(name: "id", primaryKey: .init(), type: .INTEGER, nullable: false, defaultValue: .NULL, references: nil)
@@ -239,74 +236,105 @@ class TableDefinitionTests: XCTestCase {
 
 class PrimaryKeyTests: XCTestCase {
     func test_toSQL() {
-        XCTAssertEqual(ColumnDefinition.PrimaryKey(autoIncrement: false).toSQL(),
-                       "PRIMARY KEY")
+        XCTAssertEqual(
+            ColumnDefinition.PrimaryKey(autoIncrement: false).toSQL(),
+            "PRIMARY KEY"
+        )
     }
 
     func test_toSQL_autoincrement() {
-        XCTAssertEqual(ColumnDefinition.PrimaryKey(autoIncrement: true).toSQL(),
-                       "PRIMARY KEY AUTOINCREMENT")
+        XCTAssertEqual(
+            ColumnDefinition.PrimaryKey(autoIncrement: true).toSQL(),
+            "PRIMARY KEY AUTOINCREMENT"
+        )
     }
 
     func test_toSQL_on_conflict() {
-        XCTAssertEqual(ColumnDefinition.PrimaryKey(autoIncrement: false, onConflict: .ROLLBACK).toSQL(),
-                       "PRIMARY KEY ON CONFLICT ROLLBACK")
+        XCTAssertEqual(
+            ColumnDefinition.PrimaryKey(autoIncrement: false, onConflict: .ROLLBACK).toSQL(),
+            "PRIMARY KEY ON CONFLICT ROLLBACK"
+        )
+    }
+
+    func test_fromSQL() {
+        XCTAssertEqual(
+            ColumnDefinition.PrimaryKey(sql: "PRIMARY KEY"),
+            ColumnDefinition.PrimaryKey(autoIncrement: false)
+        )
+    }
+
+    func test_fromSQL_invalid_sql_is_nil() {
+        XCTAssertNil(ColumnDefinition.PrimaryKey(sql: "FOO"))
+    }
+
+    func test_fromSQL_autoincrement() {
+        XCTAssertEqual(
+            ColumnDefinition.PrimaryKey(sql: "PRIMARY KEY AUTOINCREMENT"),
+            ColumnDefinition.PrimaryKey(autoIncrement: true)
+        )
+    }
+
+    func test_fromSQL_on_conflict() {
+        XCTAssertEqual(
+            ColumnDefinition.PrimaryKey(sql: "PRIMARY KEY ON CONFLICT ROLLBACK"),
+            ColumnDefinition.PrimaryKey(autoIncrement: false, onConflict: .ROLLBACK)
+        )
     }
 }
 
 class LiteralValueTests: XCTestCase {
     func test_recognizes_TRUE() {
-        XCTAssertEqual(LiteralValue.from("TRUE"), .TRUE)
+        XCTAssertEqual(LiteralValue("TRUE"), .TRUE)
     }
 
     func test_recognizes_FALSE() {
-        XCTAssertEqual(LiteralValue.from("FALSE"), .FALSE)
+        XCTAssertEqual(LiteralValue("FALSE"), .FALSE)
     }
 
     func test_recognizes_NULL() {
-        XCTAssertEqual(LiteralValue.from("NULL"), .NULL)
+        XCTAssertEqual(LiteralValue("NULL"), .NULL)
     }
 
     func test_recognizes_nil() {
-        XCTAssertEqual(LiteralValue.from(nil), .NULL)
+        XCTAssertEqual(LiteralValue(nil), .NULL)
     }
 
     func test_recognizes_CURRENT_TIME() {
-        XCTAssertEqual(LiteralValue.from("CURRENT_TIME"), .CURRENT_TIME)
+        XCTAssertEqual(LiteralValue("CURRENT_TIME"), .CURRENT_TIME)
     }
 
     func test_recognizes_CURRENT_TIMESTAMP() {
-        XCTAssertEqual(LiteralValue.from("CURRENT_TIMESTAMP"), .CURRENT_TIMESTAMP)
+        XCTAssertEqual(LiteralValue("CURRENT_TIMESTAMP"), .CURRENT_TIMESTAMP)
     }
 
     func test_recognizes_CURRENT_DATE() {
-        XCTAssertEqual(LiteralValue.from("CURRENT_DATE"), .CURRENT_DATE)
+        XCTAssertEqual(LiteralValue("CURRENT_DATE"), .CURRENT_DATE)
     }
 
     func test_recognizes_double_quote_string_literals() {
-        XCTAssertEqual(LiteralValue.from("\"foo\""), .stringLiteral("foo"))
+        XCTAssertEqual(LiteralValue("\"foo\""), .stringLiteral("foo"))
     }
 
     func test_recognizes_single_quote_string_literals() {
-        XCTAssertEqual(LiteralValue.from("\'foo\'"), .stringLiteral("foo"))
+        XCTAssertEqual(LiteralValue("\'foo\'"), .stringLiteral("foo"))
     }
 
     func test_unquotes_double_quote_string_literals() {
-        XCTAssertEqual(LiteralValue.from("\"fo\"\"o\""), .stringLiteral("fo\"o"))
+        XCTAssertEqual(LiteralValue("\"fo\"\"o\""), .stringLiteral("fo\"o"))
     }
 
     func test_unquotes_single_quote_string_literals() {
-        XCTAssertEqual(LiteralValue.from("'fo''o'"), .stringLiteral("fo'o"))
+        XCTAssertEqual(LiteralValue("'fo''o'"), .stringLiteral("fo'o"))
     }
 
     func test_recognizes_numeric_literals() {
-        XCTAssertEqual(LiteralValue.from("1.2"), .numericLiteral("1.2"))
-        XCTAssertEqual(LiteralValue.from("0xdeadbeef"), .numericLiteral("0xdeadbeef"))
+        XCTAssertEqual(LiteralValue("1.2"), .numericLiteral("1.2"))
+        XCTAssertEqual(LiteralValue("0xdeadbeef"), .numericLiteral("0xdeadbeef"))
     }
 
     func test_recognizes_blob_literals() {
-        XCTAssertEqual(LiteralValue.from("X'deadbeef'"), .blobLiteral("deadbeef"))
-        XCTAssertEqual(LiteralValue.from("x'deadbeef'"), .blobLiteral("deadbeef"))
+        XCTAssertEqual(LiteralValue("X'deadbeef'"), .blobLiteral("deadbeef"))
+        XCTAssertEqual(LiteralValue("x'deadbeef'"), .blobLiteral("deadbeef"))
     }
 
     func test_description_TRUE() {
diff --git a/Tests/SQLiteTests/Schema/SchemaReaderTests.swift b/Tests/SQLiteTests/Schema/SchemaReaderTests.swift
new file mode 100644
index 00000000..165dbc28
--- /dev/null
+++ b/Tests/SQLiteTests/Schema/SchemaReaderTests.swift
@@ -0,0 +1,180 @@
+import XCTest
+@testable import SQLite
+
+class SchemaReaderTests: SQLiteTestCase {
+    private var schemaReader: SchemaReader!
+
+    override func setUpWithError() throws {
+        try super.setUpWithError()
+        try createUsersTable()
+
+        schemaReader = db.schemaReader
+    }
+
+    func test_columnDefinitions() throws {
+        let columns = try schemaReader.columnDefinitions(table: "users")
+        XCTAssertEqual(columns, [
+            ColumnDefinition(name: "id",
+                             primaryKey: .init(autoIncrement: false, onConflict: nil),
+                             type: .INTEGER,
+                             nullable: true,
+                             defaultValue: .NULL,
+                             references: nil),
+            ColumnDefinition(name: "email",
+                             primaryKey: nil,
+                             type: .TEXT,
+                             nullable: false,
+                             defaultValue: .NULL,
+                             references: nil),
+            ColumnDefinition(name: "age",
+                             primaryKey: nil,
+                             type: .INTEGER,
+                             nullable: true,
+                             defaultValue: .NULL,
+                             references: nil),
+            ColumnDefinition(name: "salary",
+                             primaryKey: nil,
+                             type: .REAL,
+                             nullable: true,
+                             defaultValue: .NULL,
+                             references: nil),
+            ColumnDefinition(name: "admin",
+                             primaryKey: nil,
+                             type: .TEXT,
+                             nullable: false,
+                             defaultValue: .numericLiteral("0"),
+                             references: nil),
+            ColumnDefinition(name: "manager_id",
+                             primaryKey: nil, type: .INTEGER,
+                             nullable: true,
+                             defaultValue: .NULL,
+                             references: .init(table: "users", column: "manager_id", primaryKey: "id", onUpdate: nil, onDelete: nil)),
+            ColumnDefinition(name: "created_at",
+                             primaryKey: nil,
+                             type: .TEXT,
+                             nullable: true,
+                             defaultValue: .NULL,
+                             references: nil)
+        ])
+    }
+
+    func test_columnDefinitions_parses_conflict_modifier() throws {
+        try db.run("CREATE TABLE t (\"id\" INTEGER PRIMARY KEY ON CONFLICT IGNORE AUTOINCREMENT)")
+
+        XCTAssertEqual(
+            try schemaReader.columnDefinitions(table: "t"), [
+            ColumnDefinition(
+                name: "id",
+                primaryKey: .init(autoIncrement: true, onConflict: .IGNORE),
+                type: .INTEGER,
+                nullable: true,
+                defaultValue: .NULL,
+                references: nil)
+            ]
+        )
+    }
+
+    func test_columnDefinitions_detects_missing_autoincrement() throws {
+        try db.run("CREATE TABLE t (\"id\" INTEGER PRIMARY KEY)")
+
+        XCTAssertEqual(
+            try schemaReader.columnDefinitions(table: "t"), [
+            ColumnDefinition(
+                    name: "id",
+                    primaryKey: .init(autoIncrement: false),
+                    type: .INTEGER,
+                    nullable: true,
+                    defaultValue: .NULL,
+                    references: nil)
+            ]
+        )
+    }
+
+    func test_indexDefinitions_no_index() throws {
+        let indexes = try schemaReader.indexDefinitions(table: "users")
+        XCTAssertTrue(indexes.isEmpty)
+    }
+
+    func test_indexDefinitions_with_index() throws {
+        try db.run("CREATE UNIQUE INDEX index_users ON users (age DESC) WHERE age IS NOT NULL")
+        let indexes = try schemaReader.indexDefinitions(table: "users")
+
+        XCTAssertEqual(indexes, [
+            IndexDefinition(
+                table: "users",
+                name: "index_users",
+                unique: true,
+                columns: ["age"],
+                where: "age IS NOT NULL",
+                orders: ["age": .DESC]
+            )
+        ])
+    }
+
+    func test_foreignKeys_info_empty() throws {
+        try db.run("CREATE TABLE t (\"id\" INTEGER PRIMARY KEY)")
+
+        let foreignKeys = try schemaReader.foreignKeys(table: "t")
+        XCTAssertTrue(foreignKeys.isEmpty)
+    }
+
+    func test_foreignKeys() throws {
+        let linkTable = Table("test_links")
+
+        let idColumn = SQLite.Expression<Int64>("id")
+        let testIdColumn = SQLite.Expression<Int64>("test_id")
+
+        try db.run(linkTable.create(block: { definition in
+            definition.column(idColumn, primaryKey: .autoincrement)
+            definition.column(testIdColumn, unique: false, check: nil, references: users, Expression<Int64>("id"))
+        }))
+
+        let foreignKeys = try schemaReader.foreignKeys(table: "test_links")
+        XCTAssertEqual(foreignKeys, [
+            .init(table: "users", column: "test_id", primaryKey: "id", onUpdate: nil, onDelete: nil)
+        ])
+    }
+
+    func test_tableDefinitions() throws {
+        let tables = try schemaReader.tableDefinitions()
+        XCTAssertEqual(tables.count, 1)
+        XCTAssertEqual(tables.first?.name, "users")
+    }
+
+    func test_objectDefinitions() throws {
+        let tables = try schemaReader.objectDefinitions()
+
+        XCTAssertEqual(tables.map { table in [table.name, table.tableName, table.type.rawValue]}, [
+            ["users", "users", "table"],
+            ["sqlite_autoindex_users_1", "sqlite_autoindex_users_1", "index"]
+        ])
+    }
+
+    func test_objectDefinitions_temporary() throws {
+        let tables = try schemaReader.objectDefinitions(temp: true)
+        XCTAssert(tables.isEmpty)
+
+        try db.run("CREATE TEMPORARY TABLE foo (bar TEXT)")
+
+        let tables2 = try schemaReader.objectDefinitions(temp: true)
+        XCTAssertEqual(tables2.map { table in [table.name, table.tableName, table.type.rawValue]}, [
+            ["foo", "foo", "table"]
+        ])
+    }
+
+    func test_objectDefinitionsFilterByType() throws {
+        let tables = try schemaReader.objectDefinitions(type: .table)
+
+        XCTAssertEqual(tables.map { table in [table.name, table.tableName, table.type.rawValue]}, [
+            ["users", "users", "table"]
+        ])
+    }
+
+    func test_objectDefinitionsFilterByName() throws {
+        let tables = try schemaReader.objectDefinitions(name: "users")
+
+        XCTAssertEqual(tables.map { table in [table.name, table.tableName, table.type.rawValue]}, [
+            ["users", "users", "table"]
+        ])
+    }
+}

From 2fc0decf41a26787c4d3b2b23ef07786b44f801b Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Sun, 16 Oct 2022 19:54:06 +0200
Subject: [PATCH 197/326] Fix tests on older version of SQLite

---
 .swiftlint.yml                                |  1 +
 SQLite.xcodeproj/project.pbxproj              | 10 +++++++
 Sources/SQLite/Core/SQLiteFeature.swift       | 19 ++++++++++++++
 Sources/SQLite/Schema/Connection+Schema.swift |  6 +++--
 Sources/SQLite/Schema/SchemaReader.swift      | 26 ++++++++++++++-----
 .../Schema/Connection+SchemaTests.swift       |  4 ++-
 6 files changed, 57 insertions(+), 9 deletions(-)
 create mode 100644 Sources/SQLite/Core/SQLiteFeature.swift

diff --git a/.swiftlint.yml b/.swiftlint.yml
index 34bb253b..d40be28e 100644
--- a/.swiftlint.yml
+++ b/.swiftlint.yml
@@ -3,6 +3,7 @@ disabled_rules: # rule identifiers to exclude from running
   - operator_whitespace
   - large_tuple
   - closure_parameter_position
+  - inclusive_language  # sqlite_master etc.
 included: # paths to include during linting. `--path` is ignored if present. takes precendence over `excluded`.
   - Sources
   - Tests
diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj
index a58e0887..6fe89283 100644
--- a/SQLite.xcodeproj/project.pbxproj
+++ b/SQLite.xcodeproj/project.pbxproj
@@ -203,6 +203,10 @@
 		DB58B21228FB864300F8EEA4 /* SchemaReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB58B21028FB864300F8EEA4 /* SchemaReader.swift */; };
 		DB58B21328FB864300F8EEA4 /* SchemaReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB58B21028FB864300F8EEA4 /* SchemaReader.swift */; };
 		DB58B21428FB864300F8EEA4 /* SchemaReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB58B21028FB864300F8EEA4 /* SchemaReader.swift */; };
+		DB58B21628FC7C4600F8EEA4 /* SQLiteFeature.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB58B21528FC7C4600F8EEA4 /* SQLiteFeature.swift */; };
+		DB58B21728FC7C4600F8EEA4 /* SQLiteFeature.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB58B21528FC7C4600F8EEA4 /* SQLiteFeature.swift */; };
+		DB58B21828FC7C4600F8EEA4 /* SQLiteFeature.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB58B21528FC7C4600F8EEA4 /* SQLiteFeature.swift */; };
+		DB58B21928FC7C4600F8EEA4 /* SQLiteFeature.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB58B21528FC7C4600F8EEA4 /* SQLiteFeature.swift */; };
 		DB7C5DA628D7C9B6006395CF /* SQLiteVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB7C5DA528D7C9B6006395CF /* SQLiteVersion.swift */; };
 		DB7C5DA728D7C9B6006395CF /* SQLiteVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB7C5DA528D7C9B6006395CF /* SQLiteVersion.swift */; };
 		DB7C5DA828D7C9B6006395CF /* SQLiteVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB7C5DA528D7C9B6006395CF /* SQLiteVersion.swift */; };
@@ -334,6 +338,7 @@
 		997DF2AD287FC06D00F8DF95 /* Query+with.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Query+with.swift"; sourceTree = "<group>"; };
 		A121AC451CA35C79005A31D1 /* SQLite.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SQLite.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		DB58B21028FB864300F8EEA4 /* SchemaReader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SchemaReader.swift; sourceTree = "<group>"; };
+		DB58B21528FC7C4600F8EEA4 /* SQLiteFeature.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SQLiteFeature.swift; sourceTree = "<group>"; };
 		DB7C5DA528D7C9B6006395CF /* SQLiteVersion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SQLiteVersion.swift; sourceTree = "<group>"; };
 		EE247AD31C3F04ED00AE3E12 /* SQLite.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SQLite.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		EE247AD61C3F04ED00AE3E12 /* SQLite.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SQLite.h; sourceTree = "<group>"; };
@@ -580,6 +585,7 @@
 				19A175A9CB446640AE6F2200 /* Connection+Aggregation.swift */,
 				3DF7B78728842972005DD8CA /* Connection+Attach.swift */,
 				3DF7B790288449BA005DD8CA /* URIQueryParameter.swift */,
+				DB58B21528FC7C4600F8EEA4 /* SQLiteFeature.swift */,
 				DB7C5DA528D7C9B6006395CF /* SQLiteVersion.swift */,
 				19A17F285B767BFACD96714B /* Connection+Pragmas.swift */,
 			);
@@ -970,6 +976,7 @@
 				19A17073552293CA063BEA66 /* Result.swift in Sources */,
 				997DF2B0287FC06D00F8DF95 /* Query+with.swift in Sources */,
 				19A179B59450FE7C4811AB8A /* Connection+Aggregation.swift in Sources */,
+				DB58B21828FC7C4600F8EEA4 /* SQLiteFeature.swift in Sources */,
 				19A17FC07731779C1B8506FA /* SchemaChanger.swift in Sources */,
 				19A1740EACD47904AA24B8DC /* SchemaDefinitions.swift in Sources */,
 				19A1750EF4A5F92954A451FF /* Connection+Schema.swift in Sources */,
@@ -1030,6 +1037,7 @@
 				DB7C5DA928D7C9B6006395CF /* SQLiteVersion.swift in Sources */,
 				3D67B3EA1DB246D100A4F4C6 /* Value.swift in Sources */,
 				3D67B3EB1DB246D100A4F4C6 /* FTS4.swift in Sources */,
+				DB58B21928FC7C4600F8EEA4 /* SQLiteFeature.swift in Sources */,
 				3D67B3EC1DB246D100A4F4C6 /* RTree.swift in Sources */,
 				3D67B3ED1DB246D100A4F4C6 /* FTS5.swift in Sources */,
 				3D67B3EE1DB246D100A4F4C6 /* AggregateFunctions.swift in Sources */,
@@ -1089,6 +1097,7 @@
 				19A173EFEF0B3BD0B3ED406C /* Result.swift in Sources */,
 				997DF2AE287FC06D00F8DF95 /* Query+with.swift in Sources */,
 				19A176376CB6A94759F7980A /* Connection+Aggregation.swift in Sources */,
+				DB58B21628FC7C4600F8EEA4 /* SQLiteFeature.swift in Sources */,
 				19A1773A335CAB9D0AE14E8E /* SchemaChanger.swift in Sources */,
 				19A17BA13FD35F058787B7D3 /* SchemaDefinitions.swift in Sources */,
 				19A174506543905D71BF0518 /* Connection+Schema.swift in Sources */,
@@ -1168,6 +1177,7 @@
 				19A17F1B3F0A3C96B5ED6D64 /* Result.swift in Sources */,
 				997DF2AF287FC06D00F8DF95 /* Query+with.swift in Sources */,
 				19A170ACC97B19730FB7BA4D /* Connection+Aggregation.swift in Sources */,
+				DB58B21728FC7C4600F8EEA4 /* SQLiteFeature.swift in Sources */,
 				19A177290558991BCC60E4E3 /* SchemaChanger.swift in Sources */,
 				19A17B0DF1DDB6BBC9C95D64 /* SchemaDefinitions.swift in Sources */,
 				19A17F0BF02896E1664F4090 /* Connection+Schema.swift in Sources */,
diff --git a/Sources/SQLite/Core/SQLiteFeature.swift b/Sources/SQLite/Core/SQLiteFeature.swift
new file mode 100644
index 00000000..36b5b31b
--- /dev/null
+++ b/Sources/SQLite/Core/SQLiteFeature.swift
@@ -0,0 +1,19 @@
+import Foundation
+
+enum SQLiteFeature {
+    case partialIntegrityCheck      // PRAGMA integrity_check(table)
+    case sqliteSchemaTable          // sqlite_master => sqlite_schema
+
+    func isSupported(by version: SQLiteVersion) -> Bool {
+        switch self {
+        case .partialIntegrityCheck, .sqliteSchemaTable:
+            return version > SQLiteVersion(major: 3, minor: 33)
+        }
+    }
+}
+
+extension Connection {
+    func supports(_ feature: SQLiteFeature) -> Bool {
+        feature.isSupported(by: sqliteVersion)
+    }
+}
diff --git a/Sources/SQLite/Schema/Connection+Schema.swift b/Sources/SQLite/Schema/Connection+Schema.swift
index 868b40d5..d36812eb 100644
--- a/Sources/SQLite/Schema/Connection+Schema.swift
+++ b/Sources/SQLite/Schema/Connection+Schema.swift
@@ -25,8 +25,10 @@ public extension Connection {
 
     // This pragma does a low-level formatting and consistency check of the database.
     // https://sqlite.org/pragma.html#pragma_integrity_check
-    func integrityCheck(table: String? = nil, maxErrors: Int? = nil) throws -> [String] {
-        try run("PRAGMA integrity_check" + (table.map { "(\($0.quote()))" } ?? ""))
+    func integrityCheck(table: String? = nil) throws -> [String] {
+        precondition(table == nil || supports(.partialIntegrityCheck), "partial integrity check not supported")
+
+        return try run("PRAGMA integrity_check" + (table.map { "(\($0.quote()))" } ?? ""))
             .compactMap { $0[0] as? String }
             .filter { $0 != "ok" }
     }
diff --git a/Sources/SQLite/Schema/SchemaReader.swift b/Sources/SQLite/Schema/SchemaReader.swift
index 3f02eeae..bfa2e887 100644
--- a/Sources/SQLite/Schema/SchemaReader.swift
+++ b/Sources/SQLite/Schema/SchemaReader.swift
@@ -38,7 +38,7 @@ public class SchemaReader {
     public func objectDefinitions(name: String? = nil,
                                   type: ObjectDefinition.ObjectType? = nil,
                                   temp: Bool = false) throws -> [ObjectDefinition] {
-        var query: QueryType = temp ? SchemaTable.tempName : SchemaTable.name
+        var query: QueryType = connection.schemaTable(temp: temp)
         if let name = name {
             query = query.where(SchemaTable.nameColumn == name)
         }
@@ -99,9 +99,9 @@ public class SchemaReader {
                     column: row[ForeignKeyListTable.fromColumn],
                     primaryKey: row[ForeignKeyListTable.toColumn],
                     onUpdate: row[ForeignKeyListTable.onUpdateColumn] == TableBuilder.Dependency.noAction.rawValue
-                            ? nil : row[ForeignKeyListTable.onUpdateColumn],
+                        ? nil : row[ForeignKeyListTable.onUpdateColumn],
                     onDelete: row[ForeignKeyListTable.onDeleteColumn] == TableBuilder.Dependency.noAction.rawValue
-                            ? nil : row[ForeignKeyListTable.onDeleteColumn]
+                        ? nil : row[ForeignKeyListTable.onDeleteColumn]
                 )
             }
     }
@@ -110,9 +110,9 @@ public class SchemaReader {
         try objectDefinitions(type: .table)
                 .map { table in
                     TableDefinition(
-                            name: table.name,
-                            columns: try columnDefinitions(table: table.name),
-                            indexes: try indexDefinitions(table: table.name)
+                        name: table.name,
+                        columns: try columnDefinitions(table: table.name),
+                        indexes: try indexDefinitions(table: table.name)
                     )
                 }
     }
@@ -129,6 +129,10 @@ private class SchemaTable {
     internal static let name = Table("sqlite_schema", database: "main")
     internal static let tempName = Table("sqlite_schema", database: "temp")
 
+    // legacy table names
+    internal static let masterName = Table("sqlite_master")
+    internal static let tempMasterName = Table("sqlite_temp_master")
+
     static let typeColumn = Expression<String>("type")
     static let nameColumn = Expression<String>("name")
     static let tableNameColumn = Expression<String>("tbl_name")
@@ -180,3 +184,13 @@ private class ForeignKeyListTable {
     static let onDeleteColumn = Expression<String>("on_delete")
     static let matchColumn = Expression<String>("match")
 }
+
+private extension Connection {
+    func schemaTable(temp: Bool = false) -> Table {
+        if supports(.sqliteSchemaTable) {
+            return temp ? SchemaTable.tempName : SchemaTable.name
+        } else {
+            return temp ? SchemaTable.tempMasterName : SchemaTable.masterName
+        }
+    }
+}
diff --git a/Tests/SQLiteTests/Schema/Connection+SchemaTests.swift b/Tests/SQLiteTests/Schema/Connection+SchemaTests.swift
index 96300529..57e2726b 100644
--- a/Tests/SQLiteTests/Schema/Connection+SchemaTests.swift
+++ b/Tests/SQLiteTests/Schema/Connection+SchemaTests.swift
@@ -33,12 +33,14 @@ class ConnectionSchemaTests: SQLiteTestCase {
         XCTAssert(results.isEmpty)
     }
 
-    func test_integrityCheck_table() throws {
+    func test_partial_integrityCheck_table() throws {
+        guard db.supports(.partialIntegrityCheck) else { return }
         let results = try db.integrityCheck(table: "users")
         XCTAssert(results.isEmpty)
     }
 
     func test_integrityCheck_table_not_found() throws {
+        guard db.supports(.partialIntegrityCheck) else { return }
         XCTAssertThrowsError(try db.integrityCheck(table: "xxx")) { error in
             guard case Result.error(let message, _, _) = error else {
                 assertionFailure("invalid error type")

From a6a151b3395c83f845d6d88d7d7565df824ef239 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Sun, 16 Oct 2022 20:57:25 +0200
Subject: [PATCH 198/326] Reuse SQLiteFeature in SchemaChanger

---
 Sources/SQLite/Core/SQLiteFeature.swift       |  8 +++-
 Sources/SQLite/Schema/Connection+Schema.swift |  4 +-
 Sources/SQLite/Schema/SchemaChanger.swift     |  6 +--
 Sources/SQLite/Schema/SchemaDefinitions.swift |  5 ++-
 Sources/SQLite/Schema/SchemaReader.swift      | 40 +++++++++----------
 .../Schema/SchemaReaderTests.swift            |  4 +-
 6 files changed, 37 insertions(+), 30 deletions(-)

diff --git a/Sources/SQLite/Core/SQLiteFeature.swift b/Sources/SQLite/Core/SQLiteFeature.swift
index 36b5b31b..50d910a3 100644
--- a/Sources/SQLite/Core/SQLiteFeature.swift
+++ b/Sources/SQLite/Core/SQLiteFeature.swift
@@ -3,11 +3,17 @@ import Foundation
 enum SQLiteFeature {
     case partialIntegrityCheck      // PRAGMA integrity_check(table)
     case sqliteSchemaTable          // sqlite_master => sqlite_schema
+    case renameColumn               // ALTER TABLE ... RENAME COLUMN
+    case dropColumn                 // ALTER TABLE ... DROP COLUMN
 
     func isSupported(by version: SQLiteVersion) -> Bool {
         switch self {
         case .partialIntegrityCheck, .sqliteSchemaTable:
-            return version > SQLiteVersion(major: 3, minor: 33)
+            return version >= .init(major: 3, minor: 33)
+        case .renameColumn:
+            return version >= .init(major: 3, minor: 25)
+        case .dropColumn:
+            return version >= .init(major: 3, minor: 35)
         }
     }
 }
diff --git a/Sources/SQLite/Schema/Connection+Schema.swift b/Sources/SQLite/Schema/Connection+Schema.swift
index d36812eb..2977af17 100644
--- a/Sources/SQLite/Schema/Connection+Schema.swift
+++ b/Sources/SQLite/Schema/Connection+Schema.swift
@@ -1,7 +1,7 @@
 import Foundation
 
 public extension Connection {
-    var schemaReader: SchemaReader { SchemaReader(connection: self) }
+    var schema: SchemaReader { SchemaReader(connection: self) }
 
     // There are four columns in each result row.
     // The first column is the name of the table that
@@ -14,7 +14,7 @@ public extension Connection {
     // https://sqlite.org/pragma.html#pragma_foreign_key_check
     func foreignKeyCheck(table: String? = nil) throws -> [ForeignKeyError] {
         try run("PRAGMA foreign_key_check" + (table.map { "(\($0.quote()))" } ?? ""))
-            .compactMap { row -> ForeignKeyError? in
+            .compactMap { (row: [Binding?]) -> ForeignKeyError? in
                 guard let table = row[0] as? String,
                       let rowId = row[1] as? Int64,
                       let target = row[2] as? String else { return nil }
diff --git a/Sources/SQLite/Schema/SchemaChanger.swift b/Sources/SQLite/Schema/SchemaChanger.swift
index b557da0a..4e1b42c5 100644
--- a/Sources/SQLite/Schema/SchemaChanger.swift
+++ b/Sources/SQLite/Schema/SchemaChanger.swift
@@ -52,9 +52,9 @@ public class SchemaChanger: CustomStringConvertible {
             switch self {
             case .addColumn(let definition):
                 return "ALTER TABLE \(table.quote()) ADD COLUMN \(definition.toSQL())"
-            case .renameColumn(let from, let to) where version >= .init(major: 3, minor: 25):
+            case .renameColumn(let from, let to) where SQLiteFeature.renameColumn.isSupported(by: version):
                 return "ALTER TABLE \(table.quote()) RENAME COLUMN \(from.quote()) TO \(to.quote())"
-            case .dropColumn(let column) where version >= .init(major: 3, minor: 35):
+            case .dropColumn(let column) where SQLiteFeature.dropColumn.isSupported(by: version):
                 return "ALTER TABLE \(table.quote()) DROP COLUMN \(column.quote())"
             default: return nil
             }
@@ -128,7 +128,7 @@ public class SchemaChanger: CustomStringConvertible {
 
     init(connection: Connection, version: SQLiteVersion) {
         self.connection = connection
-        schemaReader = connection.schemaReader
+        schemaReader = connection.schema
         self.version = version
     }
 
diff --git a/Sources/SQLite/Schema/SchemaDefinitions.swift b/Sources/SQLite/Schema/SchemaDefinitions.swift
index fe2e3931..4159b35b 100644
--- a/Sources/SQLite/Schema/SchemaDefinitions.swift
+++ b/Sources/SQLite/Schema/SchemaDefinitions.swift
@@ -245,8 +245,9 @@ public struct IndexDefinition: Equatable {
         }
 
         func orders(sql: String) -> [String: IndexDefinition.Order] {
-            IndexDefinition.orderRe.matches(in: sql, range: NSRange(location: 0, length: sql.count))
-                    .reduce([String: IndexDefinition.Order]()) { (memo, result) in
+            IndexDefinition.orderRe
+                .matches(in: sql, range: NSRange(location: 0, length: sql.count))
+                .reduce([String: IndexDefinition.Order]()) { (memo, result) in
                         var memo2 = memo
                         let column = (sql as NSString).substring(with: result.range(at: 1))
                         memo2[column] = .DESC
diff --git a/Sources/SQLite/Schema/SchemaReader.swift b/Sources/SQLite/Schema/SchemaReader.swift
index bfa2e887..b1292471 100644
--- a/Sources/SQLite/Schema/SchemaReader.swift
+++ b/Sources/SQLite/Schema/SchemaReader.swift
@@ -38,7 +38,7 @@ public class SchemaReader {
     public func objectDefinitions(name: String? = nil,
                                   type: ObjectDefinition.ObjectType? = nil,
                                   temp: Bool = false) throws -> [ObjectDefinition] {
-        var query: QueryType = connection.schemaTable(temp: temp)
+        var query: QueryType = SchemaTable.get(for: connection, temp: temp)
         if let name = name {
             query = query.where(SchemaTable.nameColumn == name)
         }
@@ -125,14 +125,22 @@ public class SchemaReader {
     }
 }
 
-private class SchemaTable {
-    internal static let name = Table("sqlite_schema", database: "main")
-    internal static let tempName = Table("sqlite_schema", database: "temp")
+private enum SchemaTable {
+    private static let name = Table("sqlite_schema", database: "main")
+    private static let tempName = Table("sqlite_schema", database: "temp")
+    // legacy names (< 3.33.0)
+    private static let masterName = Table("sqlite_master")
+    private static let tempMasterName = Table("sqlite_temp_master")
 
-    // legacy table names
-    internal static let masterName = Table("sqlite_master")
-    internal static let tempMasterName = Table("sqlite_temp_master")
+    static func get(for connection: Connection, temp: Bool = false) -> Table {
+        if connection.supports(.sqliteSchemaTable) {
+            return temp ? SchemaTable.tempName : SchemaTable.name
+        } else {
+            return temp ? SchemaTable.tempMasterName : SchemaTable.masterName
+        }
+    }
 
+    // columns
     static let typeColumn = Expression<String>("type")
     static let nameColumn = Expression<String>("name")
     static let tableNameColumn = Expression<String>("tbl_name")
@@ -140,7 +148,7 @@ private class SchemaTable {
     static let sqlColumn = Expression<String?>("sql")
 }
 
-private class TableInfoTable {
+private enum TableInfoTable {
     static let idColumn = Expression<Int64>("cid")
     static let nameColumn = Expression<String>("name")
     static let typeColumn = Expression<String>("type")
@@ -149,7 +157,7 @@ private class TableInfoTable {
     static let primaryKeyColumn = Expression<Int64?>("pk")
 }
 
-private class IndexInfoTable {
+private enum IndexInfoTable {
     // The rank of the column within the index. (0 means left-most.)
     static let seqnoColumn = Expression<Int64>("seqno")
     // The rank of the column within the table being indexed.
@@ -159,7 +167,7 @@ private class IndexInfoTable {
     static let nameColumn = Expression<String?>("name")
 }
 
-private class IndexListTable {
+private enum IndexListTable {
     // A sequence number assigned to each index for internal tracking purposes.
     static let seqColumn = Expression<Int64>("seq")
     // The name of the index
@@ -174,7 +182,7 @@ private class IndexListTable {
     static let partialColumn = Expression<Int64>("partial")
 }
 
-private class ForeignKeyListTable {
+private enum ForeignKeyListTable {
     static let idColumn = Expression<Int64>("id")
     static let seqColumn = Expression<Int64>("seq")
     static let tableColumn = Expression<String>("table")
@@ -184,13 +192,3 @@ private class ForeignKeyListTable {
     static let onDeleteColumn = Expression<String>("on_delete")
     static let matchColumn = Expression<String>("match")
 }
-
-private extension Connection {
-    func schemaTable(temp: Bool = false) -> Table {
-        if supports(.sqliteSchemaTable) {
-            return temp ? SchemaTable.tempName : SchemaTable.name
-        } else {
-            return temp ? SchemaTable.tempMasterName : SchemaTable.masterName
-        }
-    }
-}
diff --git a/Tests/SQLiteTests/Schema/SchemaReaderTests.swift b/Tests/SQLiteTests/Schema/SchemaReaderTests.swift
index 165dbc28..95dba921 100644
--- a/Tests/SQLiteTests/Schema/SchemaReaderTests.swift
+++ b/Tests/SQLiteTests/Schema/SchemaReaderTests.swift
@@ -8,7 +8,7 @@ class SchemaReaderTests: SQLiteTestCase {
         try super.setUpWithError()
         try createUsersTable()
 
-        schemaReader = db.schemaReader
+        schemaReader = db.schema
     }
 
     func test_columnDefinitions() throws {
@@ -168,6 +168,7 @@ class SchemaReaderTests: SQLiteTestCase {
         XCTAssertEqual(tables.map { table in [table.name, table.tableName, table.type.rawValue]}, [
             ["users", "users", "table"]
         ])
+        XCTAssertTrue((try schemaReader.objectDefinitions(type: .trigger)).isEmpty)
     }
 
     func test_objectDefinitionsFilterByName() throws {
@@ -176,5 +177,6 @@ class SchemaReaderTests: SQLiteTestCase {
         XCTAssertEqual(tables.map { table in [table.name, table.tableName, table.type.rawValue]}, [
             ["users", "users", "table"]
         ])
+        XCTAssertTrue((try schemaReader.objectDefinitions(name: "xxx")).isEmpty)
     }
 }

From 0f6c3e30245d09bdd1fd50d5e0ac0d162131426c Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Tue, 18 Oct 2022 01:11:08 +0200
Subject: [PATCH 199/326] Add documentation

---
 Documentation/Index.md                        | 82 ++++++++++++++++++-
 Sources/SQLite/Schema/SchemaChanger.swift     |  2 +-
 Sources/SQLite/Schema/SchemaDefinitions.swift |  4 +
 Sources/SQLite/Schema/SchemaReader.swift      |  2 +-
 .../Schema/SchemaChangerTests.swift           | 34 ++++----
 .../Schema/SchemaReaderTests.swift            | 34 +++++++-
 6 files changed, 134 insertions(+), 24 deletions(-)

diff --git a/Documentation/Index.md b/Documentation/Index.md
index 29b18c96..e40f786d 100644
--- a/Documentation/Index.md
+++ b/Documentation/Index.md
@@ -1371,6 +1371,37 @@ try db.transaction {
 
 > _Note:_ Transactions run in a serial queue.
 
+## Querying the Schema
+
+We can obtain generic information about objects in the current schema with a `SchemaReader`:
+
+```swift
+let schema = db.schema
+```
+
+To query the data:
+
+```swift
+let indexes = try schema.objectDefinitions(type: .index)
+let tables = try schema.objectDefinitions(type: .table)
+let triggers = try schema.objectDefinitions(type: .trigger)
+```
+
+### Indexes and Columns
+
+Specialized methods are available to get more detailed information:
+
+```swift
+let indexes = try schema.indexDefinitions("users")
+let columns = try schema.columnDefinitions("users")
+
+for index in indexes {
+    print("\(index.name) columns:\(index.columns))")
+}
+for column in columns {
+    print("\(column.name) pk:\(column.primaryKey) nullable: \(column.nullable)")
+}
+```
 
 ## Altering the Schema
 
@@ -1454,11 +1485,56 @@ tables](#creating-a-table).
 
 ### Renaming Columns
 
-Added in SQLite 3.25.0, not exposed yet. [#1073](https://github.com/stephencelis/SQLite.swift/issues/1073)
+We can rename columns with the help of the `SchemaChanger` class:
+
+```swift
+let schemaChanger = SchemaChanger(connection: db)
+try schemaChanger.alter(table: "users") { table in
+    table.rename("old_name", to: "new_name")
+}
+```
 
 ### Dropping Columns
 
-Added in SQLite 3.35.0, not exposed yet. [#1073](https://github.com/stephencelis/SQLite.swift/issues/1073)
+```swift
+let schemaChanger = SchemaChanger(connection: db)
+try schemaChanger.alter(table: "users") { table in
+    table.drop("column")
+}
+```
+
+These operations will work with all versions of SQLite and use modern SQL
+operations such as `DROP COLUMN` when available.
+
+### Adding Columns (SchemaChanger)
+
+The `SchemaChanger` provides an alternative API to add new columns:
+
+```swift
+let newColumn = ColumnDefinition(
+    name: "new_text_column",
+    type: .TEXT,
+    nullable: true, 
+    defaultValue: .stringLiteral("foo")
+)
+
+let schemaChanger = SchemaChanger(connection: db)
+
+try schemaChanger.alter(table: "users") { table in
+    table.add(newColumn)
+}
+```
+
+### Renaming/dropping Tables (SchemaChanger)
+
+The `SchemaChanger` provides an alternative API to rename and drop tables:
+
+```swift
+let schemaChanger = SchemaChanger(connection: db)
+
+try schemaChanger.rename(table: "users", to: "users_new")
+try schemaChanger.drop(table: "emails")
+```
 
 ### Indexes
 
@@ -1515,7 +1591,6 @@ try db.run(users.dropIndex(email, ifExists: true))
 // DROP INDEX IF EXISTS "index_users_on_email"
 ```
 
-
 ### Dropping Tables
 
 We can build
@@ -1535,7 +1610,6 @@ try db.run(users.drop(ifExists: true))
 // DROP TABLE IF EXISTS "users"
 ```
 
-
 ### Migrations and Schema Versioning
 
 You can use the convenience property on `Connection` to query and set the
diff --git a/Sources/SQLite/Schema/SchemaChanger.swift b/Sources/SQLite/Schema/SchemaChanger.swift
index 4e1b42c5..d2ada22c 100644
--- a/Sources/SQLite/Schema/SchemaChanger.swift
+++ b/Sources/SQLite/Schema/SchemaChanger.swift
@@ -99,7 +99,7 @@ public class SchemaChanger: CustomStringConvertible {
             operations.append(.addColumn(column))
         }
 
-        public func remove(_ column: String) {
+        public func drop(_ column: String) {
             operations.append(.dropColumn(column))
         }
 
diff --git a/Sources/SQLite/Schema/SchemaDefinitions.swift b/Sources/SQLite/Schema/SchemaDefinitions.swift
index 4159b35b..a2700dd2 100644
--- a/Sources/SQLite/Schema/SchemaDefinitions.swift
+++ b/Sources/SQLite/Schema/SchemaDefinitions.swift
@@ -31,6 +31,10 @@ public struct ObjectDefinition: Equatable {
 
     // SQL text that describes the object (NULL for the internal indexes)
     public let sql: String?
+
+    public var isInternal: Bool {
+        name.starts(with: "sqlite_") || sql == nil
+    }
 }
 
 // https://sqlite.org/syntax/column-def.html
diff --git a/Sources/SQLite/Schema/SchemaReader.swift b/Sources/SQLite/Schema/SchemaReader.swift
index b1292471..1c26fdd6 100644
--- a/Sources/SQLite/Schema/SchemaReader.swift
+++ b/Sources/SQLite/Schema/SchemaReader.swift
@@ -52,7 +52,7 @@ public class SchemaReader {
             return ObjectDefinition(
                 type: type,
                 name: row[SchemaTable.nameColumn],
-                tableName: row[SchemaTable.nameColumn],
+                tableName: row[SchemaTable.tableNameColumn],
                 rootpage: row[SchemaTable.rootPageColumn] ?? 0,
                 sql: row[SchemaTable.sqlColumn]
             )
diff --git a/Tests/SQLiteTests/Schema/SchemaChangerTests.swift b/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
index af8b6565..d023b8e5 100644
--- a/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
+++ b/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
@@ -3,7 +3,7 @@ import XCTest
 
 class SchemaChangerTests: SQLiteTestCase {
     var schemaChanger: SchemaChanger!
-    var schemaReader: SchemaReader!
+    var schema: SchemaReader!
 
     override func setUpWithError() throws {
         try super.setUpWithError()
@@ -11,33 +11,33 @@ class SchemaChangerTests: SQLiteTestCase {
 
         try insertUsers("bob")
 
-        schemaReader = SchemaReader(connection: db)
+        schema = SchemaReader(connection: db)
         schemaChanger = SchemaChanger(connection: db)
     }
 
     func test_empty_migration_does_not_change_column_definitions() throws {
-        let previous = try schemaReader.columnDefinitions(table: "users")
+        let previous = try schema.columnDefinitions(table: "users")
         try schemaChanger.alter(table: "users") { _ in
         }
-        let current = try schemaReader.columnDefinitions(table: "users")
+        let current = try schema.columnDefinitions(table: "users")
 
         XCTAssertEqual(previous, current)
     }
 
     func test_empty_migration_does_not_change_index_definitions() throws {
-        let previous = try schemaReader.indexDefinitions(table: "users")
+        let previous = try schema.indexDefinitions(table: "users")
         try schemaChanger.alter(table: "users") { _ in
         }
-        let current = try schemaReader.indexDefinitions(table: "users")
+        let current = try schema.indexDefinitions(table: "users")
 
         XCTAssertEqual(previous, current)
     }
 
     func test_empty_migration_does_not_change_foreign_key_definitions() throws {
-        let previous = try schemaReader.foreignKeys(table: "users")
+        let previous = try schema.foreignKeys(table: "users")
         try schemaChanger.alter(table: "users") { _ in
         }
-        let current = try schemaReader.foreignKeys(table: "users")
+        let current = try schema.foreignKeys(table: "users")
 
         XCTAssertEqual(previous, current)
     }
@@ -51,21 +51,21 @@ class SchemaChangerTests: SQLiteTestCase {
         XCTAssertEqual(previous, current)
     }
 
-    func test_remove_column() throws {
+    func test_drop_column() throws {
         try schemaChanger.alter(table: "users") { table in
-            table.remove("age")
+            table.drop("age")
         }
-        let columns = try schemaReader.columnDefinitions(table: "users").map(\.name)
+        let columns = try schema.columnDefinitions(table: "users").map(\.name)
         XCTAssertFalse(columns.contains("age"))
     }
 
-    func test_remove_column_legacy() throws {
+    func test_drop_column_legacy() throws {
         schemaChanger = .init(connection: db, version: .init(major: 3, minor: 24)) // DROP COLUMN introduced in 3.35.0
 
         try schemaChanger.alter(table: "users") { table in
-            table.remove("age")
+            table.drop("age")
         }
-        let columns = try schemaReader.columnDefinitions(table: "users").map(\.name)
+        let columns = try schema.columnDefinitions(table: "users").map(\.name)
         XCTAssertFalse(columns.contains("age"))
     }
 
@@ -74,7 +74,7 @@ class SchemaChangerTests: SQLiteTestCase {
             table.rename("age", to: "age2")
         }
 
-        let columns = try schemaReader.columnDefinitions(table: "users").map(\.name)
+        let columns = try schema.columnDefinitions(table: "users").map(\.name)
         XCTAssertFalse(columns.contains("age"))
         XCTAssertTrue(columns.contains("age2"))
     }
@@ -86,7 +86,7 @@ class SchemaChangerTests: SQLiteTestCase {
             table.rename("age", to: "age2")
         }
 
-        let columns = try schemaReader.columnDefinitions(table: "users").map(\.name)
+        let columns = try schema.columnDefinitions(table: "users").map(\.name)
         XCTAssertFalse(columns.contains("age"))
         XCTAssertTrue(columns.contains("age2"))
     }
@@ -102,7 +102,7 @@ class SchemaChangerTests: SQLiteTestCase {
             table.add(newColumn)
         }
 
-        let columns = try schemaReader.columnDefinitions(table: "users")
+        let columns = try schema.columnDefinitions(table: "users")
         XCTAssertTrue(columns.contains(newColumn))
 
         XCTAssertEqual(try db.pluck(users.select(column))?[column], "foo")
diff --git a/Tests/SQLiteTests/Schema/SchemaReaderTests.swift b/Tests/SQLiteTests/Schema/SchemaReaderTests.swift
index 95dba921..dd5ae103 100644
--- a/Tests/SQLiteTests/Schema/SchemaReaderTests.swift
+++ b/Tests/SQLiteTests/Schema/SchemaReaderTests.swift
@@ -146,7 +146,7 @@ class SchemaReaderTests: SQLiteTestCase {
 
         XCTAssertEqual(tables.map { table in [table.name, table.tableName, table.type.rawValue]}, [
             ["users", "users", "table"],
-            ["sqlite_autoindex_users_1", "sqlite_autoindex_users_1", "index"]
+            ["sqlite_autoindex_users_1", "users", "index"]
         ])
     }
 
@@ -162,6 +162,38 @@ class SchemaReaderTests: SQLiteTestCase {
         ])
     }
 
+    func test_objectDefinitions_indexes() throws {
+        let emailIndex = users.createIndex(Expression<String>("email"), unique: false, ifNotExists: true)
+        try db.run(emailIndex)
+
+        let indexes = try schemaReader.objectDefinitions(type: .index)
+            .filter { !$0.isInternal }
+
+        XCTAssertEqual(indexes.map { index in [index.name, index.tableName, index.type.rawValue, index.sql]}, [
+            ["index_users_on_email",
+             "users",
+             "index",
+             "CREATE INDEX \"index_users_on_email\" ON \"users\" (\"email\")"]
+        ])
+    }
+
+    func test_objectDefinitions_triggers() throws {
+        let trigger = """
+        CREATE TRIGGER test_trigger
+            AFTER INSERT ON users BEGIN
+                UPDATE USERS SET name = "update" WHERE id = NEW.rowid;
+            END;
+        """
+
+        try db.run(trigger)
+
+        let triggers = try schemaReader.objectDefinitions(type: .trigger)
+
+        XCTAssertEqual(triggers.map { trigger in [trigger.name, trigger.tableName, trigger.type.rawValue]}, [
+            ["test_trigger", "users", "trigger"]
+        ])
+    }
+
     func test_objectDefinitionsFilterByType() throws {
         let tables = try schemaReader.objectDefinitions(type: .table)
 

From 137788e332f07e04d0a7d07150a043cc04c0d5e5 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Tue, 18 Oct 2022 01:17:31 +0200
Subject: [PATCH 200/326] Named param for rename/drop

---
 Documentation/Index.md                            | 4 ++--
 Sources/SQLite/Schema/SchemaChanger.swift         | 4 ++--
 Tests/SQLiteTests/Schema/SchemaChangerTests.swift | 8 ++++----
 3 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/Documentation/Index.md b/Documentation/Index.md
index e40f786d..68e66457 100644
--- a/Documentation/Index.md
+++ b/Documentation/Index.md
@@ -1490,7 +1490,7 @@ We can rename columns with the help of the `SchemaChanger` class:
 ```swift
 let schemaChanger = SchemaChanger(connection: db)
 try schemaChanger.alter(table: "users") { table in
-    table.rename("old_name", to: "new_name")
+    table.rename(column: "old_name", to: "new_name")
 }
 ```
 
@@ -1499,7 +1499,7 @@ try schemaChanger.alter(table: "users") { table in
 ```swift
 let schemaChanger = SchemaChanger(connection: db)
 try schemaChanger.alter(table: "users") { table in
-    table.drop("column")
+    table.drop(column: "email")
 }
 ```
 
diff --git a/Sources/SQLite/Schema/SchemaChanger.swift b/Sources/SQLite/Schema/SchemaChanger.swift
index d2ada22c..67f2b0d3 100644
--- a/Sources/SQLite/Schema/SchemaChanger.swift
+++ b/Sources/SQLite/Schema/SchemaChanger.swift
@@ -99,11 +99,11 @@ public class SchemaChanger: CustomStringConvertible {
             operations.append(.addColumn(column))
         }
 
-        public func drop(_ column: String) {
+        public func drop(column: String) {
             operations.append(.dropColumn(column))
         }
 
-        public func rename(_ column: String, to: String) {
+        public func rename(column: String, to: String) {
             operations.append(.renameColumn(column, to))
         }
     }
diff --git a/Tests/SQLiteTests/Schema/SchemaChangerTests.swift b/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
index d023b8e5..2894e5ce 100644
--- a/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
+++ b/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
@@ -53,7 +53,7 @@ class SchemaChangerTests: SQLiteTestCase {
 
     func test_drop_column() throws {
         try schemaChanger.alter(table: "users") { table in
-            table.drop("age")
+            table.drop(column: "age")
         }
         let columns = try schema.columnDefinitions(table: "users").map(\.name)
         XCTAssertFalse(columns.contains("age"))
@@ -63,7 +63,7 @@ class SchemaChangerTests: SQLiteTestCase {
         schemaChanger = .init(connection: db, version: .init(major: 3, minor: 24)) // DROP COLUMN introduced in 3.35.0
 
         try schemaChanger.alter(table: "users") { table in
-            table.drop("age")
+            table.drop(column: "age")
         }
         let columns = try schema.columnDefinitions(table: "users").map(\.name)
         XCTAssertFalse(columns.contains("age"))
@@ -71,7 +71,7 @@ class SchemaChangerTests: SQLiteTestCase {
 
     func test_rename_column() throws {
         try schemaChanger.alter(table: "users") { table in
-            table.rename("age", to: "age2")
+            table.rename(column: "age", to: "age2")
         }
 
         let columns = try schema.columnDefinitions(table: "users").map(\.name)
@@ -83,7 +83,7 @@ class SchemaChangerTests: SQLiteTestCase {
         schemaChanger = .init(connection: db, version: .init(major: 3, minor: 24)) // RENAME COLUMN introduced in 3.25.0
 
         try schemaChanger.alter(table: "users") { table in
-            table.rename("age", to: "age2")
+            table.rename(column: "age", to: "age2")
         }
 
         let columns = try schema.columnDefinitions(table: "users").map(\.name)

From f2c8bdaf71c57a5087efabf447da33f874ae5787 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Tue, 18 Oct 2022 01:25:48 +0200
Subject: [PATCH 201/326] Changelog

---
 CHANGELOG.md           | 4 ++++
 Documentation/Index.md | 2 +-
 2 files changed, 5 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7b3f78b4..3a12f377 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,7 @@
 0.14.0 (tbd), [diff][diff-0.14.0]
 ========================================
 
+* Support more complex schema changes and queries ([#1073][], [#1146][] [#1148][])
 * Support `ATTACH`/`DETACH` ([#30][], [#1142][])
 * Support `WITH` clause ([#1139][])
 * Add `Value` conformance for `NSURL` ([#1110][], [#1141][])
@@ -160,6 +161,7 @@
 [#866]: https://github.com/stephencelis/SQLite.swift/pull/866
 [#881]: https://github.com/stephencelis/SQLite.swift/pull/881
 [#919]: https://github.com/stephencelis/SQLite.swift/pull/919
+[#1073]: https://github.com/stephencelis/SQLite.swift/issues/1073
 [#1075]: https://github.com/stephencelis/SQLite.swift/pull/1075
 [#1077]: https://github.com/stephencelis/SQLite.swift/issues/1077
 [#1094]: https://github.com/stephencelis/SQLite.swift/pull/1094
@@ -185,3 +187,5 @@
 [#1141]: https://github.com/stephencelis/SQLite.swift/pull/1141
 [#1142]: https://github.com/stephencelis/SQLite.swift/pull/1142
 [#1144]: https://github.com/stephencelis/SQLite.swift/pull/1144
+[#1146]: https://github.com/stephencelis/SQLite.swift/pull/1146
+[#1148]: https://github.com/stephencelis/SQLite.swift/pull/1148
diff --git a/Documentation/Index.md b/Documentation/Index.md
index 68e66457..d275db8e 100644
--- a/Documentation/Index.md
+++ b/Documentation/Index.md
@@ -1514,7 +1514,7 @@ The `SchemaChanger` provides an alternative API to add new columns:
 let newColumn = ColumnDefinition(
     name: "new_text_column",
     type: .TEXT,
-    nullable: true, 
+    nullable: true,
     defaultValue: .stringLiteral("foo")
 )
 

From 140134d68fd82190972f30232e33363c8ae58a79 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Tue, 18 Oct 2022 02:06:21 +0200
Subject: [PATCH 202/326] Add playground examples

---
 SQLite.playground/Contents.swift              | 30 ++++++++++++++++++-
 Sources/SQLite/Schema/SchemaDefinitions.swift |  2 +-
 .../Schema/SchemaDefinitionsTests.swift       | 10 +++++++
 3 files changed, 40 insertions(+), 2 deletions(-)

diff --git a/SQLite.playground/Contents.swift b/SQLite.playground/Contents.swift
index c089076d..73091735 100644
--- a/SQLite.playground/Contents.swift
+++ b/SQLite.playground/Contents.swift
@@ -99,5 +99,33 @@ db.createAggregation("customConcat",
                      initialValue: "users:",
                      reduce: reduce,
                      result: { $0 })
-let result = db.prepare("SELECT customConcat(email) FROM users").scalar() as! String
+let result = try db.prepare("SELECT customConcat(email) FROM users").scalar() as! String
 print(result)
+
+/// schema queries
+let schema = db.schema
+let objects = try schema.objectDefinitions()
+print(objects)
+
+let columns = try schema.columnDefinitions(table: "users")
+print(columns)
+
+/// schema alteration
+
+let schemaChanger = SchemaChanger(connection: db)
+try schemaChanger.alter(table: "users") { table in
+    table.add(.init(name: "age", type: .INTEGER))
+    table.rename(column: "email", to: "electronic_mail")
+    table.drop(column: "name")
+}
+
+let changedColumns = try schema.columnDefinitions(table: "users")
+print(changedColumns)
+
+let age = Expression<Int?>("age")
+let electronicMail = Expression<String>("electronic_mail")
+
+let newRowid = try db.run(users.insert(
+    electronicMail <- "carol@mac.com",
+    age <- 33
+))
diff --git a/Sources/SQLite/Schema/SchemaDefinitions.swift b/Sources/SQLite/Schema/SchemaDefinitions.swift
index a2700dd2..b06ddfc7 100644
--- a/Sources/SQLite/Schema/SchemaDefinitions.swift
+++ b/Sources/SQLite/Schema/SchemaDefinitions.swift
@@ -122,7 +122,7 @@ public struct ColumnDefinition: Equatable {
     public init(name: String,
                 primaryKey: PrimaryKey? = nil,
                 type: Affinity,
-                nullable: Bool = false,
+                nullable: Bool = true,
                 defaultValue: LiteralValue = .NULL,
                 references: ForeignKey? = nil) {
         self.name = name
diff --git a/Tests/SQLiteTests/Schema/SchemaDefinitionsTests.swift b/Tests/SQLiteTests/Schema/SchemaDefinitionsTests.swift
index 384aab3f..ef97b981 100644
--- a/Tests/SQLiteTests/Schema/SchemaDefinitionsTests.swift
+++ b/Tests/SQLiteTests/Schema/SchemaDefinitionsTests.swift
@@ -49,6 +49,16 @@ class ColumnDefinitionTests: XCTestCase {
         XCTAssertEqual(definition.toSQL(), expected)
     }
     #endif
+
+    func testNullableByDefault() {
+        let test = ColumnDefinition(name: "test", type: .REAL)
+        XCTAssertEqual(test.name, "test")
+        XCTAssertTrue(test.nullable)
+        XCTAssertEqual(test.defaultValue, .NULL)
+        XCTAssertEqual(test.type, .REAL)
+        XCTAssertNil(test.references)
+        XCTAssertNil(test.primaryKey)
+    }
 }
 
 class AffinityTests: XCTestCase {

From 020ec7aabbda32a384d29fcc4ecf42e52ef48190 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Tue, 18 Oct 2022 02:40:18 +0200
Subject: [PATCH 203/326] Better example

---
 SQLite.playground/Contents.swift              | 2 +-
 Sources/SQLite/Schema/SchemaDefinitions.swift | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/SQLite.playground/Contents.swift b/SQLite.playground/Contents.swift
index 73091735..5a7ea379 100644
--- a/SQLite.playground/Contents.swift
+++ b/SQLite.playground/Contents.swift
@@ -114,7 +114,7 @@ print(columns)
 
 let schemaChanger = SchemaChanger(connection: db)
 try schemaChanger.alter(table: "users") { table in
-    table.add(.init(name: "age", type: .INTEGER))
+    table.add(ColumnDefinition(name: "age", type: .INTEGER))
     table.rename(column: "email", to: "electronic_mail")
     table.drop(column: "name")
 }
diff --git a/Sources/SQLite/Schema/SchemaDefinitions.swift b/Sources/SQLite/Schema/SchemaDefinitions.swift
index b06ddfc7..284fc4c3 100644
--- a/Sources/SQLite/Schema/SchemaDefinitions.swift
+++ b/Sources/SQLite/Schema/SchemaDefinitions.swift
@@ -154,7 +154,6 @@ public enum LiteralValue: Equatable, CustomStringConvertible {
 
     // If there is no explicit DEFAULT clause attached to a column definition, then the default value of the
     // column is NULL
-    // swiftlint:disable identifier_name
     case NULL
 
     // Beginning with SQLite 3.23.0 (2018-04-02), SQLite recognizes the identifiers "TRUE" and
@@ -164,6 +163,7 @@ public enum LiteralValue: Equatable, CustomStringConvertible {
     // The boolean identifiers TRUE and FALSE are usually just aliases for the integer values 1 and 0, respectively.
     case TRUE
     case FALSE
+    // swiftlint:disable identifier_name
     case CURRENT_TIME
     case CURRENT_DATE
     case CURRENT_TIMESTAMP

From 773ac2611faaeeb16cedd83406ed3b281ffcf8ea Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Sun, 23 Oct 2022 21:39:32 +0200
Subject: [PATCH 204/326] Reactivate #416

---
 Sources/SQLite/Core/Blob.swift              | 51 ++++++++++++++++-----
 Sources/SQLite/Core/Connection.swift        |  2 +-
 Sources/SQLite/Core/Statement.swift         |  2 +-
 Sources/SQLite/Foundation.swift             |  6 +--
 Tests/SQLiteTests/Core/BlobTests.swift      | 10 +++-
 Tests/SQLiteTests/Core/StatementTests.swift |  4 +-
 Tests/SQLiteTests/FoundationTests.swift     |  2 +-
 7 files changed, 55 insertions(+), 22 deletions(-)

diff --git a/Sources/SQLite/Core/Blob.swift b/Sources/SQLite/Core/Blob.swift
index cd31483b..e1ce5d9c 100644
--- a/Sources/SQLite/Core/Blob.swift
+++ b/Sources/SQLite/Core/Blob.swift
@@ -21,26 +21,55 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 //
+import Foundation
 
-public struct Blob {
+public final class Blob {
 
-    public let bytes: [UInt8]
+    public let data: NSData
 
-    public init(bytes: [UInt8]) {
-        self.bytes = bytes
+    public var bytes: UnsafeRawPointer {
+        data.bytes
     }
 
-    public init(bytes: UnsafeRawPointer, length: Int) {
-        let i8bufptr = UnsafeBufferPointer(start: bytes.assumingMemoryBound(to: UInt8.self), count: length)
-        self.init(bytes: [UInt8](i8bufptr))
+    public var length: Int {
+        data.count
     }
 
-    public func toHex() -> String {
-        bytes.map {
-            ($0 < 16 ? "0" : "") + String($0, radix: 16, uppercase: false)
-        }.joined(separator: "")
+    public convenience init(bytes: [UInt8]) {
+        let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: bytes.count)
+        for idx in 0..<bytes.count {
+            buffer.advanced(by: idx).pointee = bytes[idx]
+        }
+
+        let data = NSData(
+            bytesNoCopy: UnsafeMutableRawPointer(buffer),
+            length: bytes.count,
+            freeWhenDone: true
+        )
+        self.init(data: data)
     }
 
+    public convenience init(bytes: UnsafeRawPointer, length: Int) {
+        self.init(data: NSData(bytes: bytes, length: length))
+    }
+
+    public init(data: NSData) {
+        self.data = data
+    }
+
+    public func toHex() -> String {
+        let bytes = bytes.assumingMemoryBound(to: UInt8.self)
+
+        var hex = ""
+        for idx in 0..<length {
+            let byte = bytes.advanced(by: idx).pointee
+            if byte < 16 {
+                hex += "0"
+            }
+            hex += String(byte, radix: 16, uppercase: false)
+        }
+        return hex
+    }
 }
 
 extension Blob: CustomStringConvertible {
diff --git a/Sources/SQLite/Core/Connection.swift b/Sources/SQLite/Core/Connection.swift
index 0e51e651..2a7ec487 100644
--- a/Sources/SQLite/Core/Connection.swift
+++ b/Sources/SQLite/Core/Connection.swift
@@ -741,7 +741,7 @@ extension Context {
     func set(result: Binding?) {
         switch result {
         case let blob as Blob:
-            sqlite3_result_blob(self, blob.bytes, Int32(blob.bytes.count), nil)
+            sqlite3_result_blob(self, blob.bytes, Int32(blob.length), nil)
         case let double as Double:
             sqlite3_result_double(self, double)
         case let int as Int64:
diff --git a/Sources/SQLite/Core/Statement.swift b/Sources/SQLite/Core/Statement.swift
index 7ba4026c..ec06a5bb 100644
--- a/Sources/SQLite/Core/Statement.swift
+++ b/Sources/SQLite/Core/Statement.swift
@@ -103,7 +103,7 @@ public final class Statement {
         if value == nil {
             sqlite3_bind_null(handle, Int32(idx))
         } else if let value = value as? Blob {
-            sqlite3_bind_blob(handle, Int32(idx), value.bytes, Int32(value.bytes.count), SQLITE_TRANSIENT)
+            sqlite3_bind_blob(handle, Int32(idx), value.bytes, Int32(value.length), SQLITE_TRANSIENT)
         } else if let value = value as? Double {
             sqlite3_bind_double(handle, Int32(idx), value)
         } else if let value = value as? Int64 {
diff --git a/Sources/SQLite/Foundation.swift b/Sources/SQLite/Foundation.swift
index 44a31736..e81c4964 100644
--- a/Sources/SQLite/Foundation.swift
+++ b/Sources/SQLite/Foundation.swift
@@ -31,13 +31,11 @@ extension Data: Value {
     }
 
     public static func fromDatatypeValue(_ dataValue: Blob) -> Data {
-        Data(dataValue.bytes)
+        dataValue.data as Data
     }
 
     public var datatypeValue: Blob {
-        withUnsafeBytes { (pointer: UnsafeRawBufferPointer) -> Blob in
-            Blob(bytes: pointer.baseAddress!, length: count)
-        }
+        Blob(data: self as NSData)
     }
 
 }
diff --git a/Tests/SQLiteTests/Core/BlobTests.swift b/Tests/SQLiteTests/Core/BlobTests.swift
index 87eb5709..463ef10a 100644
--- a/Tests/SQLiteTests/Core/BlobTests.swift
+++ b/Tests/SQLiteTests/Core/BlobTests.swift
@@ -11,13 +11,19 @@ class BlobTests: XCTestCase {
 
     func test_init_array() {
         let blob = Blob(bytes: [42, 42, 42])
-        XCTAssertEqual(blob.bytes, [42, 42, 42])
+        XCTAssertEqual(blob.byteArray, [42, 42, 42])
     }
 
     func test_init_unsafeRawPointer() {
         let pointer = UnsafeMutablePointer<UInt8>.allocate(capacity: 3)
         pointer.initialize(repeating: 42, count: 3)
         let blob = Blob(bytes: pointer, length: 3)
-        XCTAssertEqual(blob.bytes, [42, 42, 42])
+        XCTAssertEqual(blob.byteArray, [42, 42, 42])
+    }
+}
+
+extension Blob {
+    var byteArray: [UInt8] {
+        [UInt8](data)
     }
 }
diff --git a/Tests/SQLiteTests/Core/StatementTests.swift b/Tests/SQLiteTests/Core/StatementTests.swift
index eeb513fe..28772ae1 100644
--- a/Tests/SQLiteTests/Core/StatementTests.swift
+++ b/Tests/SQLiteTests/Core/StatementTests.swift
@@ -22,7 +22,7 @@ class StatementTests: SQLiteTestCase {
         let statement = try db.prepare("SELECT email FROM users")
         XCTAssert(try statement.step())
         let blob = statement.row[0] as Blob
-        XCTAssertEqual("alice@example.com", String(bytes: blob.bytes, encoding: .utf8)!)
+        XCTAssertEqual("alice@example.com", String(data: blob.data as Data, encoding: .utf8)!)
     }
 
     func test_zero_sized_blob_returns_null() throws {
@@ -31,7 +31,7 @@ class StatementTests: SQLiteTestCase {
         try db.run(blobs.create { $0.column(blobColumn) })
         try db.run(blobs.insert(blobColumn <- Blob(bytes: [])))
         let blobValue = try db.scalar(blobs.select(blobColumn).limit(1, offset: 0))
-        XCTAssertEqual([], blobValue.bytes)
+        XCTAssertEqual([], blobValue.byteArray)
     }
 
     func test_prepareRowIterator() throws {
diff --git a/Tests/SQLiteTests/FoundationTests.swift b/Tests/SQLiteTests/FoundationTests.swift
index 453febcd..cca15cc1 100644
--- a/Tests/SQLiteTests/FoundationTests.swift
+++ b/Tests/SQLiteTests/FoundationTests.swift
@@ -5,7 +5,7 @@ class FoundationTests: XCTestCase {
     func testDataFromBlob() {
         let data = Data([1, 2, 3])
         let blob = data.datatypeValue
-        XCTAssertEqual([1, 2, 3], blob.bytes)
+        XCTAssertEqual([1, 2, 3], blob.byteArray)
     }
 
     func testBlobToData() {

From 3b91a00c71b34cc3f4343b2c622bf400717a7c3d Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Sun, 23 Oct 2022 22:13:21 +0200
Subject: [PATCH 205/326] Fix test

---
 Sources/SQLite/Core/Statement.swift | 19 +++++++++++--------
 1 file changed, 11 insertions(+), 8 deletions(-)

diff --git a/Sources/SQLite/Core/Statement.swift b/Sources/SQLite/Core/Statement.swift
index ec06a5bb..df4ceebf 100644
--- a/Sources/SQLite/Core/Statement.swift
+++ b/Sources/SQLite/Core/Statement.swift
@@ -100,21 +100,24 @@ public final class Statement {
     }
 
     fileprivate func bind(_ value: Binding?, atIndex idx: Int) {
-        if value == nil {
+        switch value {
+        case .none:
             sqlite3_bind_null(handle, Int32(idx))
-        } else if let value = value as? Blob {
+        case let value as Blob where value.length == 0:
+            sqlite3_bind_zeroblob(handle, Int32(idx), 0)
+        case let value as Blob:
             sqlite3_bind_blob(handle, Int32(idx), value.bytes, Int32(value.length), SQLITE_TRANSIENT)
-        } else if let value = value as? Double {
+        case let value as Double:
             sqlite3_bind_double(handle, Int32(idx), value)
-        } else if let value = value as? Int64 {
+        case let value as Int64:
             sqlite3_bind_int64(handle, Int32(idx), value)
-        } else if let value = value as? String {
+        case let value as String:
             sqlite3_bind_text(handle, Int32(idx), value, -1, SQLITE_TRANSIENT)
-        } else if let value = value as? Int {
+        case let value as Int:
             self.bind(value.datatypeValue, atIndex: idx)
-        } else if let value = value as? Bool {
+        case let value as Bool:
             self.bind(value.datatypeValue, atIndex: idx)
-        } else if let value = value {
+        case .some(let value):
             fatalError("tried to bind unexpected value \(value)")
         }
     }

From 0715e9512daf86fe42e592b54eb633a049adb4b7 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Sun, 23 Oct 2022 22:26:46 +0200
Subject: [PATCH 206/326] Fix blob equality

---
 Sources/SQLite/Core/Blob.swift         |  8 +++++++-
 Tests/SQLiteTests/Core/BlobTests.swift | 16 +++++++++++++++-
 2 files changed, 22 insertions(+), 2 deletions(-)

diff --git a/Sources/SQLite/Core/Blob.swift b/Sources/SQLite/Core/Blob.swift
index e1ce5d9c..83b78c2c 100644
--- a/Sources/SQLite/Core/Blob.swift
+++ b/Sources/SQLite/Core/Blob.swift
@@ -36,6 +36,10 @@ public final class Blob {
     }
 
     public convenience init(bytes: [UInt8]) {
+        guard bytes.count > 0 else {
+            self.init(data: NSData())
+            return
+        }
         let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: bytes.count)
         for idx in 0..<bytes.count {
             buffer.advanced(by: idx).pointee = bytes[idx]
@@ -54,10 +58,12 @@ public final class Blob {
     }
 
     public init(data: NSData) {
+        precondition(!(data is NSMutableData), "Blob cannot be initialized with mutable data")
         self.data = data
     }
 
     public func toHex() -> String {
+        guard length > 0 else { return "" }
         let bytes = bytes.assumingMemoryBound(to: UInt8.self)
 
         var hex = ""
@@ -85,5 +91,5 @@ extension Blob: Equatable {
 }
 
 public func ==(lhs: Blob, rhs: Blob) -> Bool {
-    lhs.bytes == rhs.bytes
+    lhs.data == rhs.data
 }
diff --git a/Tests/SQLiteTests/Core/BlobTests.swift b/Tests/SQLiteTests/Core/BlobTests.swift
index 463ef10a..9c8700cc 100644
--- a/Tests/SQLiteTests/Core/BlobTests.swift
+++ b/Tests/SQLiteTests/Core/BlobTests.swift
@@ -5,10 +5,14 @@ class BlobTests: XCTestCase {
 
     func test_toHex() {
         let blob = Blob(bytes: [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 150, 250, 255])
-
         XCTAssertEqual(blob.toHex(), "000a141e28323c46505a6496faff")
     }
 
+    func test_toHex_empty() {
+        let blob = Blob(bytes: [])
+        XCTAssertEqual(blob.toHex(), "")
+    }
+
     func test_init_array() {
         let blob = Blob(bytes: [42, 42, 42])
         XCTAssertEqual(blob.byteArray, [42, 42, 42])
@@ -20,6 +24,16 @@ class BlobTests: XCTestCase {
         let blob = Blob(bytes: pointer, length: 3)
         XCTAssertEqual(blob.byteArray, [42, 42, 42])
     }
+
+    func test_equality() {
+        let blob1 = Blob(bytes: [42, 42, 42])
+        let blob2 = Blob(bytes: [42, 42, 42])
+        let blob3 = Blob(bytes: [42, 42, 43])
+
+        XCTAssertEqual(Blob(bytes: []), Blob(bytes: []))
+        XCTAssertEqual(blob1, blob2)
+        XCTAssertNotEqual(blob1, blob3)
+    }
 }
 
 extension Blob {

From 0ce78d92aebea37bc68585ae4d0ead17641d86b1 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Sun, 23 Oct 2022 22:29:40 +0200
Subject: [PATCH 207/326] Remove helper

---
 Tests/SQLiteTests/Core/BlobTests.swift      | 10 ++--------
 Tests/SQLiteTests/Core/StatementTests.swift |  3 +--
 Tests/SQLiteTests/FoundationTests.swift     |  2 +-
 3 files changed, 4 insertions(+), 11 deletions(-)

diff --git a/Tests/SQLiteTests/Core/BlobTests.swift b/Tests/SQLiteTests/Core/BlobTests.swift
index 9c8700cc..01f38197 100644
--- a/Tests/SQLiteTests/Core/BlobTests.swift
+++ b/Tests/SQLiteTests/Core/BlobTests.swift
@@ -15,14 +15,14 @@ class BlobTests: XCTestCase {
 
     func test_init_array() {
         let blob = Blob(bytes: [42, 42, 42])
-        XCTAssertEqual(blob.byteArray, [42, 42, 42])
+        XCTAssertEqual([UInt8](blob.data), [42, 42, 42])
     }
 
     func test_init_unsafeRawPointer() {
         let pointer = UnsafeMutablePointer<UInt8>.allocate(capacity: 3)
         pointer.initialize(repeating: 42, count: 3)
         let blob = Blob(bytes: pointer, length: 3)
-        XCTAssertEqual(blob.byteArray, [42, 42, 42])
+        XCTAssertEqual([UInt8](blob.data), [42, 42, 42])
     }
 
     func test_equality() {
@@ -35,9 +35,3 @@ class BlobTests: XCTestCase {
         XCTAssertNotEqual(blob1, blob3)
     }
 }
-
-extension Blob {
-    var byteArray: [UInt8] {
-        [UInt8](data)
-    }
-}
diff --git a/Tests/SQLiteTests/Core/StatementTests.swift b/Tests/SQLiteTests/Core/StatementTests.swift
index 28772ae1..2ab84c09 100644
--- a/Tests/SQLiteTests/Core/StatementTests.swift
+++ b/Tests/SQLiteTests/Core/StatementTests.swift
@@ -31,7 +31,7 @@ class StatementTests: SQLiteTestCase {
         try db.run(blobs.create { $0.column(blobColumn) })
         try db.run(blobs.insert(blobColumn <- Blob(bytes: [])))
         let blobValue = try db.scalar(blobs.select(blobColumn).limit(1, offset: 0))
-        XCTAssertEqual([], blobValue.byteArray)
+        XCTAssertEqual([], [UInt8](blobValue.data))
     }
 
     func test_prepareRowIterator() throws {
@@ -71,5 +71,4 @@ class StatementTests: SQLiteTestCase {
         // truncate succeeds
         try db.run("DROP TABLE users")
     }
-
 }
diff --git a/Tests/SQLiteTests/FoundationTests.swift b/Tests/SQLiteTests/FoundationTests.swift
index cca15cc1..ffd3fae1 100644
--- a/Tests/SQLiteTests/FoundationTests.swift
+++ b/Tests/SQLiteTests/FoundationTests.swift
@@ -5,7 +5,7 @@ class FoundationTests: XCTestCase {
     func testDataFromBlob() {
         let data = Data([1, 2, 3])
         let blob = data.datatypeValue
-        XCTAssertEqual([1, 2, 3], blob.byteArray)
+        XCTAssertEqual([1, 2, 3], [UInt8](blob.data))
     }
 
     func testBlobToData() {

From 932d580deae26f04004bcd0aefa536ef021219c8 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Sun, 23 Oct 2022 23:08:55 +0200
Subject: [PATCH 208/326] Fix SQLCipher subspec

---
 Sources/SQLite/Core/Blob.swift         | 5 ++---
 Sources/SQLite/Extensions/Cipher.swift | 6 +++---
 Tests/SQLiteTests/Core/BlobTests.swift | 4 ++++
 3 files changed, 9 insertions(+), 6 deletions(-)

diff --git a/Sources/SQLite/Core/Blob.swift b/Sources/SQLite/Core/Blob.swift
index 83b78c2c..fb7ab11b 100644
--- a/Sources/SQLite/Core/Blob.swift
+++ b/Sources/SQLite/Core/Blob.swift
@@ -27,8 +27,8 @@ public final class Blob {
 
     public let data: NSData
 
-    public var bytes: UnsafeRawPointer {
-        data.bytes
+    public var bytes: UnsafePointer<UInt8> {
+        data.bytes.assumingMemoryBound(to: UInt8.self)
     }
 
     public var length: Int {
@@ -64,7 +64,6 @@ public final class Blob {
 
     public func toHex() -> String {
         guard length > 0 else { return "" }
-        let bytes = bytes.assumingMemoryBound(to: UInt8.self)
 
         var hex = ""
         for idx in 0..<length {
diff --git a/Sources/SQLite/Extensions/Cipher.swift b/Sources/SQLite/Extensions/Cipher.swift
index 8af04df9..71889ae5 100644
--- a/Sources/SQLite/Extensions/Cipher.swift
+++ b/Sources/SQLite/Extensions/Cipher.swift
@@ -32,7 +32,7 @@ extension Connection {
     }
 
     public func key(_ key: Blob, db: String = "main") throws {
-        try _key_v2(db: db, keyPointer: key.bytes, keySize: key.bytes.count)
+        try _key_v2(db: db, keyPointer: key.bytes, keySize: key.length)
     }
 
     /// Same as `key(_ key: String, db: String = "main")`, running "PRAGMA cipher_migrate;"
@@ -53,7 +53,7 @@ extension Connection {
 
     /// Same as `[`keyAndMigrate(_ key: String, db: String = "main")` accepting byte array as key
     public func keyAndMigrate(_ key: Blob, db: String = "main") throws {
-        try _key_v2(db: db, keyPointer: key.bytes, keySize: key.bytes.count, migrate: true)
+        try _key_v2(db: db, keyPointer: key.bytes, keySize: key.length, migrate: true)
     }
 
     /// Change the key on an open database. NB: only works if the database is already encrypted.
@@ -68,7 +68,7 @@ extension Connection {
     }
 
     public func rekey(_ key: Blob, db: String = "main") throws {
-        try _rekey_v2(db: db, keyPointer: key.bytes, keySize: key.bytes.count)
+        try _rekey_v2(db: db, keyPointer: key.bytes, keySize: key.length)
     }
 
     /// Converts a non-encrypted database to an encrypted one.
diff --git a/Tests/SQLiteTests/Core/BlobTests.swift b/Tests/SQLiteTests/Core/BlobTests.swift
index 01f38197..d7ed1211 100644
--- a/Tests/SQLiteTests/Core/BlobTests.swift
+++ b/Tests/SQLiteTests/Core/BlobTests.swift
@@ -34,4 +34,8 @@ class BlobTests: XCTestCase {
         XCTAssertEqual(blob1, blob2)
         XCTAssertNotEqual(blob1, blob3)
     }
+
+    func XXX_test_init_with_mutable_data_fails() {
+        _ = Blob(data: NSMutableData())
+    }
 }

From b3a21cab601e22e750e6d46ea526c643f7625b5a Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Sun, 23 Oct 2022 23:47:17 +0200
Subject: [PATCH 209/326] Avoid NSMutableData

---
 Tests/SQLiteTests/Extensions/CipherTests.swift | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/Tests/SQLiteTests/Extensions/CipherTests.swift b/Tests/SQLiteTests/Extensions/CipherTests.swift
index cc43272e..bd7f2042 100644
--- a/Tests/SQLiteTests/Extensions/CipherTests.swift
+++ b/Tests/SQLiteTests/Extensions/CipherTests.swift
@@ -19,7 +19,7 @@ class CipherTests: XCTestCase {
 
         // db2
         let key2 = keyData()
-        try db2.key(Blob(bytes: key2.bytes, length: key2.length))
+        try db2.key(Blob(data: key2))
 
         try db2.run("CREATE TABLE foo (bar TEXT)")
         try db2.run("INSERT INTO foo (bar) VALUES ('world')")
@@ -47,7 +47,7 @@ class CipherTests: XCTestCase {
 
     func test_data_rekey() throws {
         let newKey = keyData()
-        try db2.rekey(Blob(bytes: newKey.bytes, length: newKey.length))
+        try db2.rekey(Blob(data: newKey))
         XCTAssertEqual(1, try db2.scalar("SELECT count(*) FROM foo") as? Int64)
     }
 
@@ -108,12 +108,12 @@ class CipherTests: XCTestCase {
         XCTAssertEqual(1, try conn.scalar("SELECT count(*) FROM foo") as? Int64)
     }
 
-    private func keyData(length: Int = 64) -> NSMutableData {
+    private func keyData(length: Int = 64) -> NSData {
         let keyData = NSMutableData(length: length)!
         let result  = SecRandomCopyBytes(kSecRandomDefault, length,
                                          keyData.mutableBytes.assumingMemoryBound(to: UInt8.self))
         XCTAssertEqual(0, result)
-        return keyData
+        return NSData(data: keyData)
     }
 }
 #endif

From bf021c503067675295a18f69ecfc6eb3903c8195 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Sun, 23 Oct 2022 23:51:43 +0200
Subject: [PATCH 210/326] Update changelog

---
 CHANGELOG.md | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3a12f377..49590fbe 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,7 +10,8 @@
 * Fix `insertMany([Encodable])` ([#1130][], [#1138][])
 * Fix incorrect spelling of `remove_diacritics` ([#1128][])
 * Fix project build order ([#1131][])
-* Performance improvements ([#1109][], [#1115][], [#1132][])
+* Blob performance improvements ([#416][], [#1167][])
+* Various performance improvements ([#1109][], [#1115][], [#1132][])
 * Removed FTS3/4 tokenizer integration (`registerTokenizer`, [#1104][], [#1144][])
 
 0.13.3 (27-03-2022), [diff][diff-0.13.3]
@@ -130,6 +131,7 @@
 [#30]: https://github.com/stephencelis/SQLite.swift/issues/30
 [#142]: https://github.com/stephencelis/SQLite.swift/issues/142
 [#315]: https://github.com/stephencelis/SQLite.swift/issues/315
+[#416]: https://github.com/stephencelis/SQLite.swift/pull/416
 [#426]: https://github.com/stephencelis/SQLite.swift/pull/426
 [#481]: https://github.com/stephencelis/SQLite.swift/pull/481
 [#532]: https://github.com/stephencelis/SQLite.swift/issues/532
@@ -189,3 +191,4 @@
 [#1144]: https://github.com/stephencelis/SQLite.swift/pull/1144
 [#1146]: https://github.com/stephencelis/SQLite.swift/pull/1146
 [#1148]: https://github.com/stephencelis/SQLite.swift/pull/1148
+[#1167]: https://github.com/stephencelis/SQLite.swift/pull/1167

From 06e55e794ffb49eda3f97e0ca627fb76ed2f98f6 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Mon, 24 Oct 2022 12:10:54 +0200
Subject: [PATCH 211/326] Simplify

---
 Sources/SQLite/Core/Blob.swift         | 12 +-----------
 Tests/SQLiteTests/Core/BlobTests.swift | 14 ++++++++++++--
 2 files changed, 13 insertions(+), 13 deletions(-)

diff --git a/Sources/SQLite/Core/Blob.swift b/Sources/SQLite/Core/Blob.swift
index fb7ab11b..c8bd2c15 100644
--- a/Sources/SQLite/Core/Blob.swift
+++ b/Sources/SQLite/Core/Blob.swift
@@ -40,17 +40,7 @@ public final class Blob {
             self.init(data: NSData())
             return
         }
-        let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: bytes.count)
-        for idx in 0..<bytes.count {
-            buffer.advanced(by: idx).pointee = bytes[idx]
-        }
-
-        let data = NSData(
-            bytesNoCopy: UnsafeMutableRawPointer(buffer),
-            length: bytes.count,
-            freeWhenDone: true
-        )
-        self.init(data: data)
+        self.init(data: NSData(bytes: bytes, length: bytes.count))
     }
 
     public convenience init(bytes: UnsafeRawPointer, length: Int) {
diff --git a/Tests/SQLiteTests/Core/BlobTests.swift b/Tests/SQLiteTests/Core/BlobTests.swift
index d7ed1211..bef4bb83 100644
--- a/Tests/SQLiteTests/Core/BlobTests.swift
+++ b/Tests/SQLiteTests/Core/BlobTests.swift
@@ -13,9 +13,19 @@ class BlobTests: XCTestCase {
         XCTAssertEqual(blob.toHex(), "")
     }
 
+    func test_description() {
+        let blob = Blob(bytes: [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 150, 250, 255])
+        XCTAssertEqual(blob.description, "x'000a141e28323c46505a6496faff'")
+    }
+
+    func test_description_empty() {
+        let blob = Blob(bytes: [])
+        XCTAssertEqual(blob.description, "x''")
+    }
+
     func test_init_array() {
-        let blob = Blob(bytes: [42, 42, 42])
-        XCTAssertEqual([UInt8](blob.data), [42, 42, 42])
+        let blob = Blob(bytes: [42, 43, 44])
+        XCTAssertEqual([UInt8](blob.data), [42, 43, 44])
     }
 
     func test_init_unsafeRawPointer() {

From 570efefb451ff021aa5d19414f9a30d357042a96 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Wed, 26 Oct 2022 01:04:29 +0200
Subject: [PATCH 212/326] prepareRowIterator should be internal

---
 Documentation/Index.md                      | 8 --------
 Sources/SQLite/Core/Statement.swift         | 4 ++--
 Tests/SQLiteTests/Core/StatementTests.swift | 2 +-
 3 files changed, 3 insertions(+), 11 deletions(-)

diff --git a/Documentation/Index.md b/Documentation/Index.md
index 3d0639b3..fa91f7b7 100644
--- a/Documentation/Index.md
+++ b/Documentation/Index.md
@@ -2125,15 +2125,7 @@ using the following functions.
         }
     }
     ```
-    Statements with results may be iterated over, using a `RowIterator` if
-    useful.
     
-    ```swift
-    let emailColumn = Expression<String>("email")
-    let stmt = try db.prepare("SELECT id, email FROM users")
-    let emails = try! stmt.prepareRowIterator().map { $0[emailColumn] }
-    ```
-
   - `run` prepares a single `Statement` object from a SQL string, optionally
     binds values to it (using the statement’s `bind` function), executes,
     and returns the statement.
diff --git a/Sources/SQLite/Core/Statement.swift b/Sources/SQLite/Core/Statement.swift
index df4ceebf..80450258 100644
--- a/Sources/SQLite/Core/Statement.swift
+++ b/Sources/SQLite/Core/Statement.swift
@@ -236,8 +236,8 @@ extension Statement: FailableIterator {
 }
 
 extension Statement {
-    public func prepareRowIterator() -> RowIterator {
-        return RowIterator(statement: self, columnNames: self.columnNameMap)
+    func prepareRowIterator() -> RowIterator {
+        RowIterator(statement: self, columnNames: columnNameMap)
     }
 
     var columnNameMap: [String: Int] {
diff --git a/Tests/SQLiteTests/Core/StatementTests.swift b/Tests/SQLiteTests/Core/StatementTests.swift
index 2ab84c09..0d82e890 100644
--- a/Tests/SQLiteTests/Core/StatementTests.swift
+++ b/Tests/SQLiteTests/Core/StatementTests.swift
@@ -1,5 +1,5 @@
 import XCTest
-import SQLite
+@testable import SQLite
 
 #if SQLITE_SWIFT_STANDALONE
 import sqlite3

From 11bfa260eef892b0a187a6b77ffe8a94d4e97641 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Tue, 18 Oct 2022 23:50:13 +0200
Subject: [PATCH 213/326] Mention query parameter in changelog

---
 CHANGELOG.md | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 49590fbe..01b7de5a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,7 @@
 
 * Support more complex schema changes and queries ([#1073][], [#1146][] [#1148][])
 * Support `ATTACH`/`DETACH` ([#30][], [#1142][])
+* Expose connection flags (via `URIQueryParameter`) to open db ([#1074][]))
 * Support `WITH` clause ([#1139][])
 * Add `Value` conformance for `NSURL` ([#1110][], [#1141][])
 * Add decoding for `UUID` ([#1137][])
@@ -164,6 +165,7 @@
 [#881]: https://github.com/stephencelis/SQLite.swift/pull/881
 [#919]: https://github.com/stephencelis/SQLite.swift/pull/919
 [#1073]: https://github.com/stephencelis/SQLite.swift/issues/1073
+[#1074]: https://github.com/stephencelis/SQLite.swift/issues/1074
 [#1075]: https://github.com/stephencelis/SQLite.swift/pull/1075
 [#1077]: https://github.com/stephencelis/SQLite.swift/issues/1077
 [#1094]: https://github.com/stephencelis/SQLite.swift/pull/1094

From ff3c4a2d4cc8533c45addba6bbd2d0d90e8833d8 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Fri, 21 Oct 2022 12:20:24 +0200
Subject: [PATCH 214/326] Remove outdated/unavailable project refs

---
 README.md | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/README.md b/README.md
index 1b04c7c8..cdc11dd5 100644
--- a/README.md
+++ b/README.md
@@ -267,19 +267,14 @@ These projects enhance or use SQLite.swift:
 
  - [SQLiteMigrationManager.swift][] (inspired by
    [FMDBMigrationManager][])
- - [Delta: Math helper](https://apps.apple.com/app/delta-math-helper/id1436506800)
-   (see [Delta/Utils/Database.swift](https://github.com/GroupeMINASTE/Delta-iOS/blob/master/Delta/Utils/Database.swift) for production implementation example)
-
 
 ## Alternatives
 
 Looking for something else? Try another Swift wrapper (or [FMDB][]):
 
- - [Camembert](https://github.com/remirobert/Camembert)
  - [GRDB](https://github.com/groue/GRDB.swift)
  - [SQLiteDB](https://github.com/FahimF/SQLiteDB)
  - [Squeal](https://github.com/nerdyc/Squeal)
- - [SwiftData](https://github.com/ryanfowler/SwiftData)
 
 [Swift]: https://swift.org/
 [SQLite3]: https://www.sqlite.org

From dad179a555d0d2714ab1fcf69ba938073d17ff54 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Fri, 21 Oct 2022 18:47:32 +0200
Subject: [PATCH 215/326] Bump version number

---
 Documentation/Index.md                | 12 ++++++------
 README.md                             |  8 ++++----
 SQLite.swift.podspec                  |  2 +-
 SQLite.xcodeproj/project.pbxproj      |  4 ++--
 Sources/SQLite/Typed/Expression.swift |  3 +--
 Tests/SPM/Package.swift               |  2 +-
 6 files changed, 15 insertions(+), 16 deletions(-)

diff --git a/Documentation/Index.md b/Documentation/Index.md
index fa91f7b7..65cf5114 100644
--- a/Documentation/Index.md
+++ b/Documentation/Index.md
@@ -83,7 +83,7 @@ process of downloading, compiling, and linking dependencies.
 
   ```swift
   dependencies: [
-    .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.13.3")
+    .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.14.0")
   ]
   ```
 
@@ -104,7 +104,7 @@ install SQLite.swift with Carthage:
  2. Update your Cartfile to include the following:
 
     ```ruby
-    github "stephencelis/SQLite.swift" ~> 0.13.3
+    github "stephencelis/SQLite.swift" ~> 0.14.0
     ```
 
  3. Run `carthage update` and [add the appropriate framework][Carthage Usage].
@@ -134,7 +134,7 @@ install SQLite.swift with Carthage:
     use_frameworks!
 
     target 'YourAppTargetName' do
-        pod 'SQLite.swift', '~> 0.13.3'
+        pod 'SQLite.swift', '~> 0.14.0'
     end
     ```
 
@@ -148,7 +148,7 @@ with the OS you can require the `standalone` subspec:
 
 ```ruby
 target 'YourAppTargetName' do
-  pod 'SQLite.swift/standalone', '~> 0.13.3'
+  pod 'SQLite.swift/standalone', '~> 0.14.0'
 end
 ```
 
@@ -158,7 +158,7 @@ dependency to sqlite3 or one of its subspecs:
 
 ```ruby
 target 'YourAppTargetName' do
-  pod 'SQLite.swift/standalone', '~> 0.13.3'
+  pod 'SQLite.swift/standalone', '~> 0.14.0'
   pod 'sqlite3/fts5', '= 3.15.0'  # SQLite 3.15.0 with FTS5 enabled
 end
 ```
@@ -174,7 +174,7 @@ If you want to use [SQLCipher][] with SQLite.swift you can require the
 target 'YourAppTargetName' do
   # Make sure you only require the subspec, otherwise you app might link against
   # the system SQLite, which means the SQLCipher-specific methods won't work.
-  pod 'SQLite.swift/SQLCipher', '~> 0.13.3'
+  pod 'SQLite.swift/SQLCipher', '~> 0.14.0'
 end
 ```
 
diff --git a/README.md b/README.md
index cdc11dd5..ab1b67f5 100644
--- a/README.md
+++ b/README.md
@@ -136,7 +136,7 @@ Swift code.
 
   ```swift
   dependencies: [
-      .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.13.3")
+      .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.14.0")
   ]
   ```
 
@@ -160,7 +160,7 @@ install SQLite.swift with Carthage:
  2. Update your Cartfile to include the following:
 
     ```ruby
-    github "stephencelis/SQLite.swift" ~> 0.13.3
+    github "stephencelis/SQLite.swift" ~> 0.14.0
     ```
 
  3. Run `carthage update` and
@@ -191,7 +191,7 @@ SQLite.swift with CocoaPods:
     use_frameworks!
 
     target 'YourAppTargetName' do
-        pod 'SQLite.swift', '~> 0.13.3'
+        pod 'SQLite.swift', '~> 0.14.0'
     end
     ```
 
@@ -250,7 +250,7 @@ device:
 [Submit a pull request]: https://github.com/stephencelis/SQLite.swift/fork
 
 
-## Author
+## Original author
 
  - [Stephen Celis](mailto:stephen@stephencelis.com)
    ([@stephencelis](https://twitter.com/stephencelis))
diff --git a/SQLite.swift.podspec b/SQLite.swift.podspec
index d36d0330..c30aa765 100644
--- a/SQLite.swift.podspec
+++ b/SQLite.swift.podspec
@@ -1,6 +1,6 @@
 Pod::Spec.new do |s|
   s.name             = "SQLite.swift"
-  s.version          = "0.13.3"
+  s.version          = "0.14.0"
   s.summary          = "A type-safe, Swift-language layer over SQLite3."
 
   s.description      = <<-DESC
diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj
index 6fe89283..3d62e980 100644
--- a/SQLite.xcodeproj/project.pbxproj
+++ b/SQLite.xcodeproj/project.pbxproj
@@ -1497,7 +1497,7 @@
 				INFOPLIST_FILE = "$(SRCROOT)/Sources/SQLite/Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
-				MARKETING_VERSION = 0.13.3;
+				MARKETING_VERSION = 0.14.0;
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
 				PRODUCT_NAME = SQLite;
 				SKIP_INSTALL = YES;
@@ -1520,7 +1520,7 @@
 				INFOPLIST_FILE = "$(SRCROOT)/Sources/SQLite/Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
-				MARKETING_VERSION = 0.13.3;
+				MARKETING_VERSION = 0.14.0;
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
 				PRODUCT_NAME = SQLite;
 				SKIP_INSTALL = YES;
diff --git a/Sources/SQLite/Typed/Expression.swift b/Sources/SQLite/Typed/Expression.swift
index 95cdd3b9..de5afe2e 100644
--- a/Sources/SQLite/Typed/Expression.swift
+++ b/Sources/SQLite/Typed/Expression.swift
@@ -73,8 +73,7 @@ public protocol Expressible {
 extension Expressible {
 
     // naïve compiler for statements that can’t be bound, e.g., CREATE TABLE
-    // FIXME: make internal (0.13.0)
-    public func asSQL() -> String {
+    func asSQL() -> String {
         let expressed = expression
         var idx = 0
         return expressed.template.reduce("") { template, character in
diff --git a/Tests/SPM/Package.swift b/Tests/SPM/Package.swift
index dd1cd45c..d5d2e9ff 100644
--- a/Tests/SPM/Package.swift
+++ b/Tests/SPM/Package.swift
@@ -15,7 +15,7 @@ let package = Package(
         // for testing from same repository
         .package(path: "../..")
         // normally this would be:
-        // .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.13.0")
+        // .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.14.0")
     ],
     targets: [
         .target(

From 136a5c5c07d0084d459bb6fa466eba1e564c3e9f Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Sat, 22 Oct 2022 23:51:34 +0200
Subject: [PATCH 216/326] Add ExpressionTests

---
 Sources/SQLite/Typed/Expression.swift         | 20 +++++++-------
 Tests/SQLiteTests/TestHelpers.swift           |  2 +-
 Tests/SQLiteTests/Typed/ExpressionTests.swift | 27 ++++++++++++++++++-
 .../Typed/QueryIntegrationTests.swift         |  7 ++++-
 4 files changed, 43 insertions(+), 13 deletions(-)

diff --git a/Sources/SQLite/Typed/Expression.swift b/Sources/SQLite/Typed/Expression.swift
index de5afe2e..af125cc2 100644
--- a/Sources/SQLite/Typed/Expression.swift
+++ b/Sources/SQLite/Typed/Expression.swift
@@ -64,31 +64,31 @@ public struct Expression<Datatype>: ExpressionType {
 
 }
 
-public protocol Expressible {
+public protocol Expressible: CustomStringConvertible {
 
     var expression: Expression<Void> { get }
 
 }
 
 extension Expressible {
+    public var description: String {
+        asSQL()
+    }
 
     // naïve compiler for statements that can’t be bound, e.g., CREATE TABLE
     func asSQL() -> String {
         let expressed = expression
-        var idx = 0
-        return expressed.template.reduce("") { template, character in
-            let transcoded: String
+        return expressed.template.reduce(("", 0)) { memo, character in
+            let (template, index) = memo
 
             if character == "?" {
-                transcoded = transcode(expressed.bindings[idx])
-                idx += 1
+                precondition(index < expressed.bindings.count, "not enough bindings for expression")
+                return (template + transcode(expressed.bindings[index]), index + 1)
             } else {
-                transcoded = String(character)
+                return (template + String(character), index)
             }
-            return template + transcoded
-        }
+        }.0
     }
-
 }
 
 extension ExpressionType {
diff --git a/Tests/SQLiteTests/TestHelpers.swift b/Tests/SQLiteTests/TestHelpers.swift
index 2a8c7e39..65cff8bb 100644
--- a/Tests/SQLiteTests/TestHelpers.swift
+++ b/Tests/SQLiteTests/TestHelpers.swift
@@ -12,7 +12,7 @@ class SQLiteTestCase: XCTestCase {
         trace = [String: Int]()
 
         db.trace { SQL in
-            print(SQL)
+            // print("SQL: \(SQL)")
             self.trace[SQL, default: 0] += 1
         }
     }
diff --git a/Tests/SQLiteTests/Typed/ExpressionTests.swift b/Tests/SQLiteTests/Typed/ExpressionTests.swift
index 32100d4d..9155a45c 100644
--- a/Tests/SQLiteTests/Typed/ExpressionTests.swift
+++ b/Tests/SQLiteTests/Typed/ExpressionTests.swift
@@ -1,5 +1,30 @@
 import XCTest
-import SQLite
+@testable import SQLite
 
 class ExpressionTests: XCTestCase {
+
+    func test_asSQL_expression_bindings() {
+        let expression = Expression<String>("foo ? bar", ["baz"])
+        XCTAssertEqual(expression.asSQL(), "foo 'baz' bar")
+    }
+
+    func test_asSQL_expression_bindings_quoting() {
+        let expression = Expression<String>("foo ? bar", ["'baz'"])
+        XCTAssertEqual(expression.asSQL(), "foo '''baz''' bar")
+    }
+
+    func test_expression_custom_string_convertible() {
+        let expression = Expression<String>("foo ? bar", ["baz"])
+        XCTAssertEqual(expression.asSQL(), expression.description)
+    }
+
+    func test_init_literal() {
+        let expression = Expression<String>(literal: "literal")
+        XCTAssertEqual(expression.template, "literal")
+    }
+
+    func test_init_identifier() {
+        let expression = Expression<String>("identifier")
+        XCTAssertEqual(expression.template, "\"identifier\"")
+    }
 }
diff --git a/Tests/SQLiteTests/Typed/QueryIntegrationTests.swift b/Tests/SQLiteTests/Typed/QueryIntegrationTests.swift
index 27b78c0a..3fd388e9 100644
--- a/Tests/SQLiteTests/Typed/QueryIntegrationTests.swift
+++ b/Tests/SQLiteTests/Typed/QueryIntegrationTests.swift
@@ -192,7 +192,12 @@ class QueryIntegrationTests: SQLiteTestCase {
         let query3 = users.select(users[*], Expression<Int>(literal: "1 AS weight")).filter(email == "sally@example.com")
         let query4 = users.select(users[*], Expression<Int>(literal: "2 AS weight")).filter(email == "alice@example.com")
 
-        print(query3.union(query4).order(Expression<Int>(literal: "weight")).asSQL())
+        let sql = query3.union(query4).order(Expression<Int>(literal: "weight")).asSQL()
+        XCTAssertEqual(sql,
+        """
+        SELECT "users".*, 1 AS weight FROM "users" WHERE ("email" = 'sally@example.com') UNION \
+        SELECT "users".*, 2 AS weight FROM "users" WHERE ("email" = 'alice@example.com') ORDER BY weight
+        """)
 
         let orderedIDs = try db.prepare(query3.union(query4).order(Expression<Int>(literal: "weight"), email)).map { $0[id] }
         XCTAssertEqual(Array(expectedIDs.reversed()), orderedIDs)

From fc8f8df40b8434337338633fc5ae853736c1adad Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Mon, 24 Oct 2022 10:40:21 +0200
Subject: [PATCH 217/326] Remove outdated docs/links

---
 README.md | 13 ++++---------
 1 file changed, 4 insertions(+), 9 deletions(-)

diff --git a/README.md b/README.md
index ab1b67f5..0d9396c6 100644
--- a/README.md
+++ b/README.md
@@ -19,6 +19,7 @@ syntax _and_ intent.
  - [Well-documented][See Documentation]
  - Extensively tested
  - [SQLCipher][] support via CocoaPods
+ - [Schema query/migration][]
  - Works on [Linux](Documentation/Linux.md) (with some limitations)
  - Active support at
    [StackOverflow](https://stackoverflow.com/questions/tagged/sqlite.swift),
@@ -27,6 +28,7 @@ syntax _and_ intent.
 
 [SQLCipher]: https://www.zetetic.net/sqlcipher/
 [Full-text search]: Documentation/Index.md#full-text-search
+[Schema query/migration]: Documentation/Index.md#querying-the-schema
 [See Documentation]: Documentation/Index.md#sqliteswift-documentation
 
 
@@ -115,17 +117,10 @@ interactively, from the Xcode project’s playground.
 
 ![SQLite.playground Screen Shot](Documentation/Resources/playground@2x.png)
 
-For a more comprehensive example, see
-[this article][Create a Data Access Layer with SQLite.swift and Swift 2]
-and the [companion repository][SQLiteDataAccessLayer2].
-
-
-[Create a Data Access Layer with SQLite.swift and Swift 2]: https://masteringswift.blogspot.com/2015/09/create-data-access-layer-with.html
-[SQLiteDataAccessLayer2]: https://github.com/hoffmanjon/SQLiteDataAccessLayer2/tree/master
-
 ## Installation
 
-> _Note:_ Version 0.11.6 and later requires Swift 5 (and [Xcode](https://developer.apple.com/xcode/downloads/) 10.2) or greater. Version 0.11.5 requires Swift 4.2 (and [Xcode](https://developer.apple.com/xcode/downloads/) 10.1) or greater.
+> _Note:_ Version 0.11.6 and later requires Swift 5 (and [Xcode](https://developer.apple.com/xcode/downloads/) 10.2) or greater.
+> Version 0.11.5 requires Swift 4.2 (and [Xcode](https://developer.apple.com/xcode/downloads/) 10.1) or greater.
 
 ### Swift Package Manager
 

From 0eb58d8fa82e864d1071c92a2fc88747484d3f48 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Wed, 26 Oct 2022 23:34:17 +0200
Subject: [PATCH 218/326] Doc updates

---
 CHANGELOG.md                                  |  1 +
 Documentation/Index.md                        | 90 +++++++++----------
 Documentation/Release.md                      |  1 +
 Documentation/Upgrading.md                    | 11 +++
 SQLite.playground/Contents.swift              |  1 +
 Sources/SQLite/Schema/SchemaChanger.swift     | 10 +--
 .../Schema/SchemaChangerTests.swift           | 14 +++
 7 files changed, 78 insertions(+), 50 deletions(-)
 create mode 100644 Documentation/Upgrading.md

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 01b7de5a..92c89e9d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,6 @@
 0.14.0 (tbd), [diff][diff-0.14.0]
 ========================================
+For breaking changes, see [Upgrading.md](Documentation/Upgrading.md).
 
 * Support more complex schema changes and queries ([#1073][], [#1146][] [#1148][])
 * Support `ATTACH`/`DETACH` ([#30][], [#1142][])
diff --git a/Documentation/Index.md b/Documentation/Index.md
index 65cf5114..bdd1da28 100644
--- a/Documentation/Index.md
+++ b/Documentation/Index.md
@@ -41,14 +41,19 @@
   - [Updating Rows](#updating-rows)
   - [Deleting Rows](#deleting-rows)
   - [Transactions and Savepoints](#transactions-and-savepoints)
+  - [Querying the Schema](#querying-the-schema)
   - [Altering the Schema](#altering-the-schema)
     - [Renaming Tables](#renaming-tables)
+    - [Dropping Tables](#dropping-tables)
     - [Adding Columns](#adding-columns)
       - [Added Column Constraints](#added-column-constraints)
+    - [Schema Changer](#schemachanger)
+      - [Renaming Columns](#renaming-columns)
+      - [Dropping Columns](#dropping-columns)
+      - [Renaming/dropping tables](#renamingdropping-tables)
     - [Indexes](#indexes)
       - [Creating Indexes](#creating-indexes)
       - [Dropping Indexes](#dropping-indexes)
-    - [Dropping Tables](#dropping-tables)
     - [Migrations and Schema Versioning](#migrations-and-schema-versioning)
   - [Custom Types](#custom-types)
     - [Date-Time Values](#date-time-values)
@@ -1409,7 +1414,6 @@ for column in columns {
 SQLite.swift comes with several functions (in addition to `Table.create`) for
 altering a database schema in a type-safe manner.
 
-
 ### Renaming Tables
 
 We can build an `ALTER TABLE … RENAME TO` statement by calling the `rename`
@@ -1420,6 +1424,24 @@ try db.run(users.rename(Table("users_old")))
 // ALTER TABLE "users" RENAME TO "users_old"
 ```
 
+### Dropping Tables
+
+We can build
+[`DROP TABLE` statements](https://www.sqlite.org/lang_droptable.html)
+by calling the `dropTable` function on a `SchemaType`.
+
+```swift
+try db.run(users.drop())
+// DROP TABLE "users"
+```
+
+The `drop` function has one additional parameter, `ifExists`, which (when
+`true`) adds an `IF EXISTS` clause to the statement.
+
+```swift
+try db.run(users.drop(ifExists: true))
+// DROP TABLE IF EXISTS "users"
+```
 
 ### Adding Columns
 
@@ -1484,57 +1506,54 @@ tables](#creating-a-table).
     // ALTER TABLE "posts" ADD COLUMN "user_id" INTEGER REFERENCES "users" ("id")
     ```
 
-### Renaming Columns
+### SchemaChanger
 
-We can rename columns with the help of the `SchemaChanger` class:
+Version 0.14.0 introduces `SchemaChanger`, an alternative API to perform more complex
+migrations such as renaming columns. These operations work with all versions of
+SQLite but use SQL statements such as `ALTER TABLE RENAME COLUMN` when available.
+
+#### Adding Columns
 
 ```swift
+let newColumn = ColumnDefinition(
+    name: "new_text_column",
+    type: .TEXT,
+    nullable: true,
+    defaultValue: .stringLiteral("foo")
+)
+
 let schemaChanger = SchemaChanger(connection: db)
+
 try schemaChanger.alter(table: "users") { table in
-    table.rename(column: "old_name", to: "new_name")
+    table.add(newColumn)
 }
 ```
 
-### Dropping Columns
+#### Renaming Columns
 
 ```swift
 let schemaChanger = SchemaChanger(connection: db)
 try schemaChanger.alter(table: "users") { table in
-    table.drop(column: "email")
+    table.rename(column: "old_name", to: "new_name")
 }
 ```
 
-These operations will work with all versions of SQLite and use modern SQL
-operations such as `DROP COLUMN` when available.
-
-### Adding Columns (SchemaChanger)
-
-The `SchemaChanger` provides an alternative API to add new columns:
+#### Dropping Columns
 
 ```swift
-let newColumn = ColumnDefinition(
-    name: "new_text_column",
-    type: .TEXT,
-    nullable: true,
-    defaultValue: .stringLiteral("foo")
-)
-
 let schemaChanger = SchemaChanger(connection: db)
-
 try schemaChanger.alter(table: "users") { table in
-    table.add(newColumn)
+    table.drop(column: "email")
 }
 ```
 
-### Renaming/dropping Tables (SchemaChanger)
-
-The `SchemaChanger` provides an alternative API to rename and drop tables:
+#### Renaming/dropping Tables
 
 ```swift
 let schemaChanger = SchemaChanger(connection: db)
 
 try schemaChanger.rename(table: "users", to: "users_new")
-try schemaChanger.drop(table: "emails")
+try schemaChanger.drop(table: "emails", ifExists: false)
 ```
 
 ### Indexes
@@ -1592,25 +1611,6 @@ try db.run(users.dropIndex(email, ifExists: true))
 // DROP INDEX IF EXISTS "index_users_on_email"
 ```
 
-### Dropping Tables
-
-We can build
-[`DROP TABLE` statements](https://www.sqlite.org/lang_droptable.html)
-by calling the `dropTable` function on a `SchemaType`.
-
-```swift
-try db.run(users.drop())
-// DROP TABLE "users"
-```
-
-The `drop` function has one additional parameter, `ifExists`, which (when
-`true`) adds an `IF EXISTS` clause to the statement.
-
-```swift
-try db.run(users.drop(ifExists: true))
-// DROP TABLE IF EXISTS "users"
-```
-
 ### Migrations and Schema Versioning
 
 You can use the convenience property on `Connection` to query and set the
diff --git a/Documentation/Release.md b/Documentation/Release.md
index 87d715f6..8ec67970 100644
--- a/Documentation/Release.md
+++ b/Documentation/Release.md
@@ -3,6 +3,7 @@
 * [ ] Make sure current master branch has a green build
 * [ ] Make sure `SQLite.playground` runs without errors
 * [ ] Make sure `CHANGELOG.md` is up-to-date
+* [ ] Add content to `Documentation/Upgrading.md` if needed
 * [ ] Update the version number in `SQLite.swift.podspec`
 * [ ] Run `pod lib lint` locally
 * [ ] Update the version numbers mentioned in `README.md`, `Documentation/Index.md`
diff --git a/Documentation/Upgrading.md b/Documentation/Upgrading.md
new file mode 100644
index 00000000..8c398467
--- /dev/null
+++ b/Documentation/Upgrading.md
@@ -0,0 +1,11 @@
+# Upgrading
+
+## 0.13 → 0.14
+
+- `Expression.asSQL()` is no longer available. Expressions now implement `CustomStringConvertible`,
+  where `description` returns the SQL.
+- `Statement.prepareRowIterator()` is now longer available. Instead, use the methods
+  of the same name on `Connection`.
+- `Blob` no longer wraps byte arrays and now uses `NSData`, which enables memory and
+ performance improvements.
+- `Connection.registerTokenizer` is no longer available to register custom FTS4 tokenizers.
diff --git a/SQLite.playground/Contents.swift b/SQLite.playground/Contents.swift
index 5a7ea379..0eafdd0d 100644
--- a/SQLite.playground/Contents.swift
+++ b/SQLite.playground/Contents.swift
@@ -52,6 +52,7 @@ for user in try Array(rowIterator) {
 
 /// also with `map()`
 let mapRowIterator = try db.prepareRowIterator(users)
+
 let userIds = try mapRowIterator.map { $0[id] }
 
 /// using `failableNext()` on `RowIterator`
diff --git a/Sources/SQLite/Schema/SchemaChanger.swift b/Sources/SQLite/Schema/SchemaChanger.swift
index 67f2b0d3..af710908 100644
--- a/Sources/SQLite/Schema/SchemaChanger.swift
+++ b/Sources/SQLite/Schema/SchemaChanger.swift
@@ -141,8 +141,8 @@ public class SchemaChanger: CustomStringConvertible {
         }
     }
 
-    public func drop(table: String) throws {
-        try dropTable(table)
+    public func drop(table: String, ifExists: Bool = true) throws {
+        try dropTable(table, ifExists: ifExists)
     }
 
     // Beginning with release 3.25.0 (2018-09-15), references to the table within trigger bodies and
@@ -192,7 +192,7 @@ public class SchemaChanger: CustomStringConvertible {
 
     private func moveTable(from: String, to: String, options: Options = .default, operation: Operation? = nil) throws {
         try copyTable(from: from, to: to, options: options, operation: operation)
-        try dropTable(from)
+        try dropTable(from, ifExists: true)
     }
 
     private func copyTable(from: String, to: String, options: Options = .default, operation: Operation?) throws {
@@ -225,8 +225,8 @@ public class SchemaChanger: CustomStringConvertible {
         }
     }
 
-    private func dropTable(_ table: String) throws {
-        try connection.run("DROP TABLE IF EXISTS \(table.quote())")
+    private func dropTable(_ table: String, ifExists: Bool) throws {
+        try connection.run("DROP TABLE \(ifExists ? "IF EXISTS" : "") \(table.quote())")
     }
 
     private func copyTableContents(from: TableDefinition, to: TableDefinition) throws {
diff --git a/Tests/SQLiteTests/Schema/SchemaChangerTests.swift b/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
index 2894e5ce..40f65b62 100644
--- a/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
+++ b/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
@@ -135,6 +135,20 @@ class SchemaChangerTests: SQLiteTestCase {
         }
     }
 
+    func test_drop_table_if_exists_true() throws {
+        try schemaChanger.drop(table: "xxx", ifExists: true)
+    }
+
+    func test_drop_table_if_exists_false() throws {
+        XCTAssertThrowsError(try schemaChanger.drop(table: "xxx", ifExists: false)) { error in
+            if case Result.error(let message, _, _) =  error {
+                XCTAssertEqual(message, "no such table: xxx")
+            } else {
+                XCTFail("unexpected error \(error)")
+            }
+        }
+    }
+
     func test_rename_table() throws {
         try schemaChanger.rename(table: "users", to: "users_new")
         let users_new = Table("users_new")

From 25708c9804d906d039a293538e45877e9262d433 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Wed, 26 Oct 2022 23:48:00 +0200
Subject: [PATCH 219/326] Updates

---
 CHANGELOG.md           | 2 +-
 Documentation/Index.md | 6 +++---
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 92c89e9d..2783a67d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,4 @@
-0.14.0 (tbd), [diff][diff-0.14.0]
+0.14.0 (27-10-2022), [diff][diff-0.14.0]
 ========================================
 For breaking changes, see [Upgrading.md](Documentation/Upgrading.md).
 
diff --git a/Documentation/Index.md b/Documentation/Index.md
index bdd1da28..20469037 100644
--- a/Documentation/Index.md
+++ b/Documentation/Index.md
@@ -50,7 +50,7 @@
     - [Schema Changer](#schemachanger)
       - [Renaming Columns](#renaming-columns)
       - [Dropping Columns](#dropping-columns)
-      - [Renaming/dropping tables](#renamingdropping-tables)
+      - [Renaming/dropping Tables](#renamingdropping-tables)
     - [Indexes](#indexes)
       - [Creating Indexes](#creating-indexes)
       - [Dropping Indexes](#dropping-indexes)
@@ -173,7 +173,7 @@ See the [sqlite3 podspec][sqlite3pod] for more details.
 #### Using SQLite.swift with SQLCipher
 
 If you want to use [SQLCipher][] with SQLite.swift you can require the
-`SQLCipher` subspec in your Podfile:
+`SQLCipher` subspec in your Podfile (SPM is not supported yet, see [#1084](/issues/1084)):
 
 ```ruby
 target 'YourAppTargetName' do
@@ -2183,7 +2183,7 @@ try db.detach("external")
 // DETACH DATABASE 'external'
 ```
 
-When compiled for SQLCipher, you can additionally pass a `key` parameter to `attach`:
+When compiled for SQLCipher, we can additionally pass a `key` parameter to `attach`:
 
 ```swift
 try db.attach(.uri("encrypted.sqlite"), as: "encrypted", key: "secret")

From ac09389994bb6549a3f978d996f2d3a06b5a0071 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Thu, 27 Oct 2022 00:04:22 +0200
Subject: [PATCH 220/326] Mention group container problems

Closes #1042
---
 Documentation/Index.md | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/Documentation/Index.md b/Documentation/Index.md
index 20469037..363443d6 100644
--- a/Documentation/Index.md
+++ b/Documentation/Index.md
@@ -9,6 +9,7 @@
     - [Connecting to a Database](#connecting-to-a-database)
       - [Read-Write Databases](#read-write-databases)
       - [Read-Only Databases](#read-only-databases)
+      - [In a Shared Group Container](#in-a-shared-group-container)
       - [In-Memory Databases](#in-memory-databases)
       - [URI parameters](#uri-parameters)
       - [Thread-Safety](#thread-safety)
@@ -173,7 +174,7 @@ See the [sqlite3 podspec][sqlite3pod] for more details.
 #### Using SQLite.swift with SQLCipher
 
 If you want to use [SQLCipher][] with SQLite.swift you can require the
-`SQLCipher` subspec in your Podfile (SPM is not supported yet, see [#1084](/issues/1084)):
+`SQLCipher` subspec in your Podfile (SPM is not supported yet, see [#1084](https://github.com/stephencelis/SQLite.swift/issues/1084)):
 
 ```ruby
 target 'YourAppTargetName' do
@@ -330,6 +331,13 @@ let db = try Connection(path, readonly: true)
 > We welcome changes to the above sample code to show how to successfully copy and use a bundled "seed"
 > database for writing in an app.
 
+#### In a shared group container
+
+It is not recommend to store databases in a [shared group container],
+some users have reported crashes ([#1042](https://github.com/stephencelis/SQLite.swift/issues/1042)).
+
+[shared group container]: https://developer.apple.com/documentation/foundation/filemanager/1412643-containerurl#
+
 #### In-Memory Databases
 
 If you omit the path, SQLite.swift will provision an [in-memory

From 194d99addcfbf76795d00c072470623b4ded5ad8 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Thu, 27 Oct 2022 00:51:19 +0200
Subject: [PATCH 221/326] Fix ambiguous use of description

---
 Sources/SQLite/Extensions/FTS4.swift          | 50 ++++++++-----------
 Sources/SQLite/Extensions/FTS5.swift          | 18 +++----
 Sources/SQLite/Typed/Expression.swift         | 10 ++--
 Tests/SQLiteTests/Typed/ExpressionTests.swift |  5 ++
 4 files changed, 39 insertions(+), 44 deletions(-)

diff --git a/Sources/SQLite/Extensions/FTS4.swift b/Sources/SQLite/Extensions/FTS4.swift
index e9e65746..8e91e453 100644
--- a/Sources/SQLite/Extensions/FTS4.swift
+++ b/Sources/SQLite/Extensions/FTS4.swift
@@ -117,7 +117,7 @@ public struct Tokenizer {
 
     // https://sqlite.org/fts5.html#the_experimental_trigram_tokenizer
     public static func Trigram(caseSensitive: Bool = false) -> Tokenizer {
-        return Tokenizer("trigram", ["case_sensitive", caseSensitive ? "1" : "0"])
+        Tokenizer("trigram", ["case_sensitive", caseSensitive ? "1" : "0"])
     }
 
     public static func Custom(_ name: String) -> Tokenizer {
@@ -236,18 +236,12 @@ open class FTSConfig {
             }
         }
 
-        @discardableResult mutating func append(_ key: String, value: CustomStringConvertible?) -> Options {
-            append(key, value: value?.description)
+        @discardableResult mutating func append(_ key: String, value: String) -> Options {
+            append(key, value: Expression<String>(value))
         }
 
-        @discardableResult mutating func append(_ key: String, value: String?) -> Options {
-            append(key, value: value.map { Expression<String>($0) })
-        }
-
-        @discardableResult mutating func append(_ key: String, value: Expressible?) -> Options {
-            if let value = value {
-                arguments.append("=".join([Expression<Void>(literal: key), value]))
-            }
+        @discardableResult mutating func append(_ key: String, value: Expressible) -> Options {
+            arguments.append("=".join([Expression<Void>(literal: key), value]))
             return self
         }
     }
@@ -256,26 +250,16 @@ open class FTSConfig {
 /// Configuration for the [FTS4](https://www.sqlite.org/fts3.html) extension.
 open class FTS4Config: FTSConfig {
     /// [The matchinfo= option](https://www.sqlite.org/fts3.html#section_6_4)
-    public enum MatchInfo: CustomStringConvertible {
+    public enum MatchInfo: String {
         case fts3
-        public var description: String {
-            "fts3"
-        }
     }
 
     /// [FTS4 options](https://www.sqlite.org/fts3.html#fts4_options)
-    public enum Order: CustomStringConvertible {
+    public enum Order: String {
         /// Data structures are optimized for returning results in ascending order by docid (default)
         case asc
         /// FTS4 stores its data in such a way as to optimize returning results in descending order by docid.
         case desc
-
-        public var description: String {
-            switch self {
-            case .asc: return "asc"
-            case .desc: return "desc"
-            }
-        }
     }
 
     var compressFunction: String?
@@ -322,11 +306,21 @@ open class FTS4Config: FTSConfig {
         for (column, _) in (columnDefinitions.filter { $0.options.contains(.unindexed) }) {
             options.append("notindexed", value: column)
         }
-        options.append("languageid", value: languageId)
-        options.append("compress", value: compressFunction)
-        options.append("uncompress", value: uncompressFunction)
-        options.append("matchinfo", value: matchInfo)
-        options.append("order", value: order)
+        if let languageId = languageId {
+            options.append("languageid", value: languageId)
+        }
+        if let compressFunction = compressFunction {
+            options.append("compress", value: compressFunction)
+        }
+        if let uncompressFunction = uncompressFunction {
+            options.append("uncompress", value: uncompressFunction)
+        }
+        if let matchInfo = matchInfo {
+            options.append("matchinfo", value: matchInfo.rawValue)
+        }
+        if let order = order {
+            options.append("order", value: order.rawValue)
+        }
         return options
     }
 }
diff --git a/Sources/SQLite/Extensions/FTS5.swift b/Sources/SQLite/Extensions/FTS5.swift
index f108bbec..2e4f65fb 100644
--- a/Sources/SQLite/Extensions/FTS5.swift
+++ b/Sources/SQLite/Extensions/FTS5.swift
@@ -33,21 +33,13 @@ extension Module {
 /// **Note:** this is currently only applicable when using SQLite.swift together with a FTS5-enabled version
 /// of SQLite.
 open class FTS5Config: FTSConfig {
-    public enum Detail: CustomStringConvertible {
+    public enum Detail: String {
         /// store rowid, column number, term offset
         case full
         /// store rowid, column number
         case column
         /// store rowid
         case none
-
-        public var description: String {
-            switch self {
-            case .full: return "full"
-            case .column: return "column"
-            case .none: return "none"
-            }
-        }
     }
 
     var detail: Detail?
@@ -77,11 +69,15 @@ open class FTS5Config: FTSConfig {
 
     override func options() -> Options {
         var options = super.options()
-        options.append("content_rowid", value: contentRowId)
+        if let contentRowId = contentRowId {
+            options.append("content_rowid", value: contentRowId)
+        }
         if let columnSize = columnSize {
             options.append("columnsize", value: Expression<Int>(value: columnSize))
         }
-        options.append("detail", value: detail)
+        if let detail = detail {
+            options.append("detail", value: detail.rawValue)
+        }
         return options
     }
 
diff --git a/Sources/SQLite/Typed/Expression.swift b/Sources/SQLite/Typed/Expression.swift
index af125cc2..dcc44fe4 100644
--- a/Sources/SQLite/Typed/Expression.swift
+++ b/Sources/SQLite/Typed/Expression.swift
@@ -22,7 +22,7 @@
 // THE SOFTWARE.
 //
 
-public protocol ExpressionType: Expressible { // extensions cannot have inheritance clauses
+public protocol ExpressionType: Expressible, CustomStringConvertible { // extensions cannot have inheritance clauses
 
     associatedtype UnderlyingType = Void
 
@@ -47,6 +47,9 @@ extension ExpressionType {
         self.init(expression.template, expression.bindings)
     }
 
+    public var description: String {
+        asSQL()
+    }
 }
 
 /// An `Expression` represents a raw SQL fragment and any associated bindings.
@@ -64,16 +67,13 @@ public struct Expression<Datatype>: ExpressionType {
 
 }
 
-public protocol Expressible: CustomStringConvertible {
+public protocol Expressible {
 
     var expression: Expression<Void> { get }
 
 }
 
 extension Expressible {
-    public var description: String {
-        asSQL()
-    }
 
     // naïve compiler for statements that can’t be bound, e.g., CREATE TABLE
     func asSQL() -> String {
diff --git a/Tests/SQLiteTests/Typed/ExpressionTests.swift b/Tests/SQLiteTests/Typed/ExpressionTests.swift
index 9155a45c..147d62e9 100644
--- a/Tests/SQLiteTests/Typed/ExpressionTests.swift
+++ b/Tests/SQLiteTests/Typed/ExpressionTests.swift
@@ -18,6 +18,11 @@ class ExpressionTests: XCTestCase {
         XCTAssertEqual(expression.asSQL(), expression.description)
     }
 
+    func test_builtin_unambiguously_custom_string_convertible() {
+        let integer: Int = 45
+        XCTAssertEqual(integer.description, "45")
+    }
+
     func test_init_literal() {
         let expression = Expression<String>(literal: "literal")
         XCTAssertEqual(expression.template, "literal")

From 367f6aaee8228f71eedcfcc34e1433e79c46c632 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Thu, 27 Oct 2022 10:17:46 +0200
Subject: [PATCH 222/326] add(_ column:) => add(column:)

---
 Documentation/Index.md                            | 2 +-
 SQLite.playground/Contents.swift                  | 2 +-
 Sources/SQLite/Schema/SchemaChanger.swift         | 2 +-
 Tests/SQLiteTests/Schema/SchemaChangerTests.swift | 4 ++--
 4 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/Documentation/Index.md b/Documentation/Index.md
index 363443d6..c518c739 100644
--- a/Documentation/Index.md
+++ b/Documentation/Index.md
@@ -1533,7 +1533,7 @@ let newColumn = ColumnDefinition(
 let schemaChanger = SchemaChanger(connection: db)
 
 try schemaChanger.alter(table: "users") { table in
-    table.add(newColumn)
+    table.add(column: newColumn)
 }
 ```
 
diff --git a/SQLite.playground/Contents.swift b/SQLite.playground/Contents.swift
index 0eafdd0d..f651cbc1 100644
--- a/SQLite.playground/Contents.swift
+++ b/SQLite.playground/Contents.swift
@@ -115,7 +115,7 @@ print(columns)
 
 let schemaChanger = SchemaChanger(connection: db)
 try schemaChanger.alter(table: "users") { table in
-    table.add(ColumnDefinition(name: "age", type: .INTEGER))
+    table.add(column: ColumnDefinition(name: "age", type: .INTEGER))
     table.rename(column: "email", to: "electronic_mail")
     table.drop(column: "name")
 }
diff --git a/Sources/SQLite/Schema/SchemaChanger.swift b/Sources/SQLite/Schema/SchemaChanger.swift
index af710908..af7b5e27 100644
--- a/Sources/SQLite/Schema/SchemaChanger.swift
+++ b/Sources/SQLite/Schema/SchemaChanger.swift
@@ -95,7 +95,7 @@ public class SchemaChanger: CustomStringConvertible {
             self.name = name
         }
 
-        public func add(_ column: ColumnDefinition) {
+        public func add(column: ColumnDefinition) {
             operations.append(.addColumn(column))
         }
 
diff --git a/Tests/SQLiteTests/Schema/SchemaChangerTests.swift b/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
index 40f65b62..9f8e21f4 100644
--- a/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
+++ b/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
@@ -99,7 +99,7 @@ class SchemaChangerTests: SQLiteTestCase {
                                          defaultValue: .stringLiteral("foo"))
 
         try schemaChanger.alter(table: "users") { table in
-            table.add(newColumn)
+            table.add(column: newColumn)
         }
 
         let columns = try schema.columnDefinitions(table: "users")
@@ -114,7 +114,7 @@ class SchemaChangerTests: SQLiteTestCase {
                                          type: .TEXT)
 
         XCTAssertThrowsError(try schemaChanger.alter(table: "users") { table in
-            table.add(newColumn)
+            table.add(column: newColumn)
         }) { error in
             if case SchemaChanger.Error.invalidColumnDefinition(_) = error {
                 XCTAssertEqual("Invalid column definition: can not add primary key column", error.localizedDescription)

From 2c4af8526e112e3a23df141e988d08a28e475ecd Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Thu, 27 Oct 2022 10:21:13 +0200
Subject: [PATCH 223/326] Update docs

---
 Documentation/Index.md | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/Documentation/Index.md b/Documentation/Index.md
index c518c739..ada9b7c3 100644
--- a/Documentation/Index.md
+++ b/Documentation/Index.md
@@ -49,9 +49,10 @@
     - [Adding Columns](#adding-columns)
       - [Added Column Constraints](#added-column-constraints)
     - [Schema Changer](#schemachanger)
+      - [Adding Columns](#adding-columns)
       - [Renaming Columns](#renaming-columns)
       - [Dropping Columns](#dropping-columns)
-      - [Renaming/dropping Tables](#renamingdropping-tables)
+      - [Renaming/Dropping Tables](#renamingdropping-tables)
     - [Indexes](#indexes)
       - [Creating Indexes](#creating-indexes)
       - [Dropping Indexes](#dropping-indexes)
@@ -1555,7 +1556,7 @@ try schemaChanger.alter(table: "users") { table in
 }
 ```
 
-#### Renaming/dropping Tables
+#### Renaming/Dropping Tables
 
 ```swift
 let schemaChanger = SchemaChanger(connection: db)

From 3161f06acd378ff3e5e91c0c590c9dc2edba1ce5 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Thu, 27 Oct 2022 12:06:14 +0200
Subject: [PATCH 224/326] Use direct links

---
 README.md | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/README.md b/README.md
index 0d9396c6..ac0c2a7a 100644
--- a/README.md
+++ b/README.md
@@ -277,10 +277,10 @@ Looking for something else? Try another Swift wrapper (or [FMDB][]):
 
 [GitHubActionBadge]: https://img.shields.io/github/workflow/status/stephencelis/SQLite.swift/Build%20and%20test
 
-[CocoaPodsVersionBadge]: https://cocoapod-badges.herokuapp.com/v/SQLite.swift/badge.png
+[CocoaPodsVersionBadge]: https://img.shields.io/cocoapods/v/SQLite.swift.svg?style=flat
 [CocoaPodsVersionLink]: https://cocoapods.org/pods/SQLite.swift
 
-[PlatformBadge]: https://cocoapod-badges.herokuapp.com/p/SQLite.swift/badge.png
+[PlatformBadge]: https://img.shields.io/cocoapods/p/SQLite.swift.svg?style=flat
 [PlatformLink]: https://cocoapods.org/pods/SQLite.swift
 
 [CartagheBadge]: https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat

From 1235d44cd3e8596e842a47c03efbcbdb61797024 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Tue, 1 Nov 2022 22:28:21 +0100
Subject: [PATCH 225/326] Revert changes to Blob

---
 CHANGELOG.md                                  |  6 +++
 Documentation/Index.md                        | 12 ++---
 README.md                                     |  4 +-
 SQLite.swift.podspec                          |  2 +-
 Sources/SQLite/Core/Blob.swift                | 50 ++++---------------
 Sources/SQLite/Core/Connection.swift          |  2 +-
 Sources/SQLite/Core/Statement.swift           |  4 +-
 Sources/SQLite/Extensions/Cipher.swift        |  6 +--
 Sources/SQLite/Foundation.swift               |  6 ++-
 Tests/SPM/Package.swift                       |  2 +-
 Tests/SQLiteTests/Core/BlobTests.swift        |  8 +--
 Tests/SQLiteTests/Core/StatementTests.swift   |  4 +-
 .../SQLiteTests/Extensions/CipherTests.swift  | 11 ++--
 Tests/SQLiteTests/FoundationTests.swift       |  2 +-
 14 files changed, 47 insertions(+), 72 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2783a67d..fc759118 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,8 @@
+0.14.1 (01-11-2022), [diff][diff-0.14.1]
+========================================
+
+* Reverted `Blob` changes (See [#1167][] for rationale).
+
 0.14.0 (27-10-2022), [diff][diff-0.14.0]
 ========================================
 For breaking changes, see [Upgrading.md](Documentation/Upgrading.md).
@@ -129,6 +134,7 @@ For breaking changes, see [Upgrading.md](Documentation/Upgrading.md).
 [diff-0.13.2]: https://github.com/stephencelis/SQLite.swift/compare/0.13.1...0.13.2
 [diff-0.13.3]: https://github.com/stephencelis/SQLite.swift/compare/0.13.2...0.13.3
 [diff-0.14.0]: https://github.com/stephencelis/SQLite.swift/compare/0.13.3...0.14.0
+[diff-0.14.1]: https://github.com/stephencelis/SQLite.swift/compare/0.14.0...0.14.1
 
 [#30]: https://github.com/stephencelis/SQLite.swift/issues/30
 [#142]: https://github.com/stephencelis/SQLite.swift/issues/142
diff --git a/Documentation/Index.md b/Documentation/Index.md
index ada9b7c3..730fe30b 100644
--- a/Documentation/Index.md
+++ b/Documentation/Index.md
@@ -90,7 +90,7 @@ process of downloading, compiling, and linking dependencies.
 
   ```swift
   dependencies: [
-    .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.14.0")
+    .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.14.1")
   ]
   ```
 
@@ -111,7 +111,7 @@ install SQLite.swift with Carthage:
  2. Update your Cartfile to include the following:
 
     ```ruby
-    github "stephencelis/SQLite.swift" ~> 0.14.0
+    github "stephencelis/SQLite.swift" ~> 0.14.1
     ```
 
  3. Run `carthage update` and [add the appropriate framework][Carthage Usage].
@@ -141,7 +141,7 @@ install SQLite.swift with Carthage:
     use_frameworks!
 
     target 'YourAppTargetName' do
-        pod 'SQLite.swift', '~> 0.14.0'
+        pod 'SQLite.swift', '~> 0.14.1'
     end
     ```
 
@@ -155,7 +155,7 @@ with the OS you can require the `standalone` subspec:
 
 ```ruby
 target 'YourAppTargetName' do
-  pod 'SQLite.swift/standalone', '~> 0.14.0'
+  pod 'SQLite.swift/standalone', '~> 0.14.1'
 end
 ```
 
@@ -165,7 +165,7 @@ dependency to sqlite3 or one of its subspecs:
 
 ```ruby
 target 'YourAppTargetName' do
-  pod 'SQLite.swift/standalone', '~> 0.14.0'
+  pod 'SQLite.swift/standalone', '~> 0.14.1'
   pod 'sqlite3/fts5', '= 3.15.0'  # SQLite 3.15.0 with FTS5 enabled
 end
 ```
@@ -181,7 +181,7 @@ If you want to use [SQLCipher][] with SQLite.swift you can require the
 target 'YourAppTargetName' do
   # Make sure you only require the subspec, otherwise you app might link against
   # the system SQLite, which means the SQLCipher-specific methods won't work.
-  pod 'SQLite.swift/SQLCipher', '~> 0.14.0'
+  pod 'SQLite.swift/SQLCipher', '~> 0.14.1'
 end
 ```
 
diff --git a/README.md b/README.md
index ac0c2a7a..4cf72d03 100644
--- a/README.md
+++ b/README.md
@@ -131,7 +131,7 @@ Swift code.
 
   ```swift
   dependencies: [
-      .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.14.0")
+      .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.14.1")
   ]
   ```
 
@@ -155,7 +155,7 @@ install SQLite.swift with Carthage:
  2. Update your Cartfile to include the following:
 
     ```ruby
-    github "stephencelis/SQLite.swift" ~> 0.14.0
+    github "stephencelis/SQLite.swift" ~> 0.14.1
     ```
 
  3. Run `carthage update` and
diff --git a/SQLite.swift.podspec b/SQLite.swift.podspec
index c30aa765..f32282d3 100644
--- a/SQLite.swift.podspec
+++ b/SQLite.swift.podspec
@@ -1,6 +1,6 @@
 Pod::Spec.new do |s|
   s.name             = "SQLite.swift"
-  s.version          = "0.14.0"
+  s.version          = "0.14.1"
   s.summary          = "A type-safe, Swift-language layer over SQLite3."
 
   s.description      = <<-DESC
diff --git a/Sources/SQLite/Core/Blob.swift b/Sources/SQLite/Core/Blob.swift
index c8bd2c15..a709fb42 100644
--- a/Sources/SQLite/Core/Blob.swift
+++ b/Sources/SQLite/Core/Blob.swift
@@ -21,64 +21,36 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 //
-import Foundation
 
-public final class Blob {
+public struct Blob {
 
-    public let data: NSData
+    public let bytes: [UInt8]
 
-    public var bytes: UnsafePointer<UInt8> {
-        data.bytes.assumingMemoryBound(to: UInt8.self)
+    public init(bytes: [UInt8]) {
+        self.bytes = bytes
     }
 
-    public var length: Int {
-        data.count
-    }
-
-    public convenience init(bytes: [UInt8]) {
-        guard bytes.count > 0 else {
-            self.init(data: NSData())
-            return
-        }
-        self.init(data: NSData(bytes: bytes, length: bytes.count))
-    }
-
-    public convenience init(bytes: UnsafeRawPointer, length: Int) {
-        self.init(data: NSData(bytes: bytes, length: length))
-    }
-
-    public init(data: NSData) {
-        precondition(!(data is NSMutableData), "Blob cannot be initialized with mutable data")
-        self.data = data
+    public init(bytes: UnsafeRawPointer, length: Int) {
+        let i8bufptr = UnsafeBufferPointer(start: bytes.assumingMemoryBound(to: UInt8.self), count: length)
+        self.init(bytes: [UInt8](i8bufptr))
     }
 
     public func toHex() -> String {
-        guard length > 0 else { return "" }
-
-        var hex = ""
-        for idx in 0..<length {
-            let byte = bytes.advanced(by: idx).pointee
-            if byte < 16 {
-                hex += "0"
-            }
-            hex += String(byte, radix: 16, uppercase: false)
-        }
-        return hex
+        bytes.map {
+           ($0 < 16 ? "0" : "") + String($0, radix: 16, uppercase: false)
+        }.joined(separator: "")
     }
 }
 
 extension Blob: CustomStringConvertible {
-
     public var description: String {
         "x'\(toHex())'"
     }
-
 }
 
 extension Blob: Equatable {
-
 }
 
 public func ==(lhs: Blob, rhs: Blob) -> Bool {
-    lhs.data == rhs.data
+    lhs.bytes == rhs.bytes
 }
diff --git a/Sources/SQLite/Core/Connection.swift b/Sources/SQLite/Core/Connection.swift
index 2a7ec487..0e51e651 100644
--- a/Sources/SQLite/Core/Connection.swift
+++ b/Sources/SQLite/Core/Connection.swift
@@ -741,7 +741,7 @@ extension Context {
     func set(result: Binding?) {
         switch result {
         case let blob as Blob:
-            sqlite3_result_blob(self, blob.bytes, Int32(blob.length), nil)
+            sqlite3_result_blob(self, blob.bytes, Int32(blob.bytes.count), nil)
         case let double as Double:
             sqlite3_result_double(self, double)
         case let int as Int64:
diff --git a/Sources/SQLite/Core/Statement.swift b/Sources/SQLite/Core/Statement.swift
index 80450258..6cb2e5d3 100644
--- a/Sources/SQLite/Core/Statement.swift
+++ b/Sources/SQLite/Core/Statement.swift
@@ -103,10 +103,10 @@ public final class Statement {
         switch value {
         case .none:
             sqlite3_bind_null(handle, Int32(idx))
-        case let value as Blob where value.length == 0:
+        case let value as Blob where value.bytes.count == 0:
             sqlite3_bind_zeroblob(handle, Int32(idx), 0)
         case let value as Blob:
-            sqlite3_bind_blob(handle, Int32(idx), value.bytes, Int32(value.length), SQLITE_TRANSIENT)
+            sqlite3_bind_blob(handle, Int32(idx), value.bytes, Int32(value.bytes.count), SQLITE_TRANSIENT)
         case let value as Double:
             sqlite3_bind_double(handle, Int32(idx), value)
         case let value as Int64:
diff --git a/Sources/SQLite/Extensions/Cipher.swift b/Sources/SQLite/Extensions/Cipher.swift
index 71889ae5..8af04df9 100644
--- a/Sources/SQLite/Extensions/Cipher.swift
+++ b/Sources/SQLite/Extensions/Cipher.swift
@@ -32,7 +32,7 @@ extension Connection {
     }
 
     public func key(_ key: Blob, db: String = "main") throws {
-        try _key_v2(db: db, keyPointer: key.bytes, keySize: key.length)
+        try _key_v2(db: db, keyPointer: key.bytes, keySize: key.bytes.count)
     }
 
     /// Same as `key(_ key: String, db: String = "main")`, running "PRAGMA cipher_migrate;"
@@ -53,7 +53,7 @@ extension Connection {
 
     /// Same as `[`keyAndMigrate(_ key: String, db: String = "main")` accepting byte array as key
     public func keyAndMigrate(_ key: Blob, db: String = "main") throws {
-        try _key_v2(db: db, keyPointer: key.bytes, keySize: key.length, migrate: true)
+        try _key_v2(db: db, keyPointer: key.bytes, keySize: key.bytes.count, migrate: true)
     }
 
     /// Change the key on an open database. NB: only works if the database is already encrypted.
@@ -68,7 +68,7 @@ extension Connection {
     }
 
     public func rekey(_ key: Blob, db: String = "main") throws {
-        try _rekey_v2(db: db, keyPointer: key.bytes, keySize: key.length)
+        try _rekey_v2(db: db, keyPointer: key.bytes, keySize: key.bytes.count)
     }
 
     /// Converts a non-encrypted database to an encrypted one.
diff --git a/Sources/SQLite/Foundation.swift b/Sources/SQLite/Foundation.swift
index e81c4964..44a31736 100644
--- a/Sources/SQLite/Foundation.swift
+++ b/Sources/SQLite/Foundation.swift
@@ -31,11 +31,13 @@ extension Data: Value {
     }
 
     public static func fromDatatypeValue(_ dataValue: Blob) -> Data {
-        dataValue.data as Data
+        Data(dataValue.bytes)
     }
 
     public var datatypeValue: Blob {
-        Blob(data: self as NSData)
+        withUnsafeBytes { (pointer: UnsafeRawBufferPointer) -> Blob in
+            Blob(bytes: pointer.baseAddress!, length: count)
+        }
     }
 
 }
diff --git a/Tests/SPM/Package.swift b/Tests/SPM/Package.swift
index d5d2e9ff..3e329552 100644
--- a/Tests/SPM/Package.swift
+++ b/Tests/SPM/Package.swift
@@ -15,7 +15,7 @@ let package = Package(
         // for testing from same repository
         .package(path: "../..")
         // normally this would be:
-        // .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.14.0")
+        // .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.14.1")
     ],
     targets: [
         .target(
diff --git a/Tests/SQLiteTests/Core/BlobTests.swift b/Tests/SQLiteTests/Core/BlobTests.swift
index bef4bb83..f2e9435e 100644
--- a/Tests/SQLiteTests/Core/BlobTests.swift
+++ b/Tests/SQLiteTests/Core/BlobTests.swift
@@ -25,14 +25,14 @@ class BlobTests: XCTestCase {
 
     func test_init_array() {
         let blob = Blob(bytes: [42, 43, 44])
-        XCTAssertEqual([UInt8](blob.data), [42, 43, 44])
+        XCTAssertEqual(blob.bytes, [42, 43, 44])
     }
 
     func test_init_unsafeRawPointer() {
         let pointer = UnsafeMutablePointer<UInt8>.allocate(capacity: 3)
         pointer.initialize(repeating: 42, count: 3)
         let blob = Blob(bytes: pointer, length: 3)
-        XCTAssertEqual([UInt8](blob.data), [42, 42, 42])
+        XCTAssertEqual(blob.bytes, [42, 42, 42])
     }
 
     func test_equality() {
@@ -44,8 +44,4 @@ class BlobTests: XCTestCase {
         XCTAssertEqual(blob1, blob2)
         XCTAssertNotEqual(blob1, blob3)
     }
-
-    func XXX_test_init_with_mutable_data_fails() {
-        _ = Blob(data: NSMutableData())
-    }
 }
diff --git a/Tests/SQLiteTests/Core/StatementTests.swift b/Tests/SQLiteTests/Core/StatementTests.swift
index 0d82e890..5f212505 100644
--- a/Tests/SQLiteTests/Core/StatementTests.swift
+++ b/Tests/SQLiteTests/Core/StatementTests.swift
@@ -22,7 +22,7 @@ class StatementTests: SQLiteTestCase {
         let statement = try db.prepare("SELECT email FROM users")
         XCTAssert(try statement.step())
         let blob = statement.row[0] as Blob
-        XCTAssertEqual("alice@example.com", String(data: blob.data as Data, encoding: .utf8)!)
+        XCTAssertEqual("alice@example.com", String(bytes: blob.bytes, encoding: .utf8)!)
     }
 
     func test_zero_sized_blob_returns_null() throws {
@@ -31,7 +31,7 @@ class StatementTests: SQLiteTestCase {
         try db.run(blobs.create { $0.column(blobColumn) })
         try db.run(blobs.insert(blobColumn <- Blob(bytes: [])))
         let blobValue = try db.scalar(blobs.select(blobColumn).limit(1, offset: 0))
-        XCTAssertEqual([], [UInt8](blobValue.data))
+        XCTAssertEqual([], blobValue.bytes)
     }
 
     func test_prepareRowIterator() throws {
diff --git a/Tests/SQLiteTests/Extensions/CipherTests.swift b/Tests/SQLiteTests/Extensions/CipherTests.swift
index bd7f2042..bc89cfa2 100644
--- a/Tests/SQLiteTests/Extensions/CipherTests.swift
+++ b/Tests/SQLiteTests/Extensions/CipherTests.swift
@@ -19,7 +19,7 @@ class CipherTests: XCTestCase {
 
         // db2
         let key2 = keyData()
-        try db2.key(Blob(data: key2))
+        try db2.key(Blob(bytes: key2.bytes, length: key2.length))
 
         try db2.run("CREATE TABLE foo (bar TEXT)")
         try db2.run("INSERT INTO foo (bar) VALUES ('world')")
@@ -47,7 +47,7 @@ class CipherTests: XCTestCase {
 
     func test_data_rekey() throws {
         let newKey = keyData()
-        try db2.rekey(Blob(data: newKey))
+        try db2.rekey(Blob(bytes: newKey.bytes, length: newKey.length))
         XCTAssertEqual(1, try db2.scalar("SELECT count(*) FROM foo") as? Int64)
     }
 
@@ -79,12 +79,11 @@ class CipherTests: XCTestCase {
         // sqlite> CREATE TABLE foo (bar TEXT);
         // sqlite> INSERT INTO foo (bar) VALUES ('world');
         guard let cipherVersion: String = db1.cipherVersion,
-            cipherVersion.starts(with: "3.") || cipherVersion.starts(with: "4.")
-            else { return }
+            cipherVersion.starts(with: "3.") || cipherVersion.starts(with: "4.") else { return }
 
         let encryptedFile = cipherVersion.starts(with: "3.") ?
-            fixture("encrypted-3.x", withExtension: "sqlite") :
-            fixture("encrypted-4.x", withExtension: "sqlite")
+                fixture("encrypted-3.x", withExtension: "sqlite") :
+                fixture("encrypted-4.x", withExtension: "sqlite")
 
         try FileManager.default.setAttributes([FileAttributeKey.immutable: 1], ofItemAtPath: encryptedFile)
         XCTAssertFalse(FileManager.default.isWritableFile(atPath: encryptedFile))
diff --git a/Tests/SQLiteTests/FoundationTests.swift b/Tests/SQLiteTests/FoundationTests.swift
index ffd3fae1..453febcd 100644
--- a/Tests/SQLiteTests/FoundationTests.swift
+++ b/Tests/SQLiteTests/FoundationTests.swift
@@ -5,7 +5,7 @@ class FoundationTests: XCTestCase {
     func testDataFromBlob() {
         let data = Data([1, 2, 3])
         let blob = data.datatypeValue
-        XCTAssertEqual([1, 2, 3], [UInt8](blob.data))
+        XCTAssertEqual([1, 2, 3], blob.bytes)
     }
 
     func testBlobToData() {

From f06b8df5bc0fb73d6ac46adaf5f047fded953f79 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Wed, 2 Nov 2022 09:04:08 +0100
Subject: [PATCH 226/326] Removed

---
 Documentation/Upgrading.md | 2 --
 1 file changed, 2 deletions(-)

diff --git a/Documentation/Upgrading.md b/Documentation/Upgrading.md
index 8c398467..f2cc2ecb 100644
--- a/Documentation/Upgrading.md
+++ b/Documentation/Upgrading.md
@@ -6,6 +6,4 @@
   where `description` returns the SQL.
 - `Statement.prepareRowIterator()` is now longer available. Instead, use the methods
   of the same name on `Connection`.
-- `Blob` no longer wraps byte arrays and now uses `NSData`, which enables memory and
- performance improvements.
 - `Connection.registerTokenizer` is no longer available to register custom FTS4 tokenizers.

From d926517c090042ee3268048f1d46020f94b39d0c Mon Sep 17 00:00:00 2001
From: Geoff MacDonald <geoffmacd@gmail.com>
Date: Tue, 20 Dec 2022 12:18:53 -0800
Subject: [PATCH 227/326] adds support for extended error codes

---
 Sources/SQLite/Core/Connection.swift          |  7 ++++++
 Sources/SQLite/Core/Result.swift              | 24 ++++++++++++++++++-
 Tests/SQLiteTests/Core/ConnectionTests.swift  |  4 ++++
 Tests/SQLiteTests/Core/ResultTests.swift      | 13 ++++++++++
 .../Typed/QueryIntegrationTests.swift         | 13 ++++++++++
 5 files changed, 60 insertions(+), 1 deletion(-)

diff --git a/Sources/SQLite/Core/Connection.swift b/Sources/SQLite/Core/Connection.swift
index 0e51e651..f74ed82c 100644
--- a/Sources/SQLite/Core/Connection.swift
+++ b/Sources/SQLite/Core/Connection.swift
@@ -156,6 +156,13 @@ public final class Connection {
         Int(sqlite3_total_changes(handle))
     }
 
+    /// Whether or not the database will return extended error codes when errors are handled.
+    public var usesExtendedErrorCodes: Bool = false {
+        didSet {
+            sqlite3_extended_result_codes(handle, usesExtendedErrorCodes ? 1 : 0)
+        }
+    }
+    
     // MARK: - Execute
 
     /// Executes a batch of SQL statements.
diff --git a/Sources/SQLite/Core/Result.swift b/Sources/SQLite/Core/Result.swift
index 3de4d25d..06f9cc74 100644
--- a/Sources/SQLite/Core/Result.swift
+++ b/Sources/SQLite/Core/Result.swift
@@ -21,11 +21,27 @@ public enum Result: Error {
     /// - statement: the statement which produced the error
     case error(message: String, code: Int32, statement: Statement?)
 
+    /// Represents a SQLite specific [extended error code] (https://sqlite.org/rescode.html#primary_result_codes_versus_extended_result_codes)
+    ///
+    /// - message: English-language text that describes the error
+    ///
+    /// - extendedCode: SQLite [extended error code](https://sqlite.org/rescode.html#extended_result_code_list)
+    ///
+    /// - statement: the statement which produced the error
+    case extendedError(message: String, extendedCode: Int32, statement: Statement?)
+
     init?(errorCode: Int32, connection: Connection, statement: Statement? = nil) {
         guard !Result.successCodes.contains(errorCode) else { return nil }
 
         let message = String(cString: sqlite3_errmsg(connection.handle))
-        self = .error(message: message, code: errorCode, statement: statement)
+
+        guard connection.usesExtendedErrorCodes else {
+            self = .error(message: message, code: errorCode, statement: statement)
+            return
+        }
+
+        let extendedErrorCode = sqlite3_extended_errcode(connection.handle)
+        self = .extendedError(message: message, extendedCode: extendedErrorCode, statement: statement)
     }
 
 }
@@ -40,6 +56,12 @@ extension Result: CustomStringConvertible {
             } else {
                 return "\(message) (code: \(errorCode))"
             }
+        case let .extendedError(message,  extendedCode, statement):
+            if let statement = statement {
+                return "\(message) (\(statement)) (extended code: \(extendedCode))"
+            } else {
+                return "\(message) (extended code: \(extendedCode))"
+            }
         }
     }
 }
diff --git a/Tests/SQLiteTests/Core/ConnectionTests.swift b/Tests/SQLiteTests/Core/ConnectionTests.swift
index 9d623f3f..6a1d94ae 100644
--- a/Tests/SQLiteTests/Core/ConnectionTests.swift
+++ b/Tests/SQLiteTests/Core/ConnectionTests.swift
@@ -111,6 +111,10 @@ class ConnectionTests: SQLiteTestCase {
         XCTAssertEqual(2, db.totalChanges)
     }
 
+    func test_useExtendedErrorCodes_returnsFalseDefault() throws {
+        XCTAssertFalse(db.usesExtendedErrorCodes)
+    }
+
     func test_prepare_preparesAndReturnsStatements() throws {
         _ = try db.prepare("SELECT * FROM users WHERE admin = 0")
         _ = try db.prepare("SELECT * FROM users WHERE admin = ?", 0)
diff --git a/Tests/SQLiteTests/Core/ResultTests.swift b/Tests/SQLiteTests/Core/ResultTests.swift
index fab4a0bb..d3c8bb1f 100644
--- a/Tests/SQLiteTests/Core/ResultTests.swift
+++ b/Tests/SQLiteTests/Core/ResultTests.swift
@@ -53,4 +53,17 @@ class ResultTests: XCTestCase {
         XCTAssertEqual("not an error (SELECT 1) (code: 21)",
             Result(errorCode: SQLITE_MISUSE, connection: connection, statement: statement)?.description)
     }
+
+    func test_init_extended_with_other_code_returns_error() {
+        connection.usesExtendedErrorCodes = true
+        if case .some(.extendedError(let message, let extendedCode, let statement)) =
+            Result(errorCode: SQLITE_MISUSE, connection: connection, statement: nil) {
+            XCTAssertEqual("not an error", message)
+            XCTAssertEqual(extendedCode, 0)
+            XCTAssertNil(statement)
+            XCTAssert(connection === connection)
+        } else {
+            XCTFail("no error")
+        }
+    }
 }
diff --git a/Tests/SQLiteTests/Typed/QueryIntegrationTests.swift b/Tests/SQLiteTests/Typed/QueryIntegrationTests.swift
index 3fd388e9..a86708ec 100644
--- a/Tests/SQLiteTests/Typed/QueryIntegrationTests.swift
+++ b/Tests/SQLiteTests/Typed/QueryIntegrationTests.swift
@@ -229,6 +229,19 @@ class QueryIntegrationTests: SQLiteTestCase {
         }
     }
 
+    func test_extendedErrorCodes_catchConstraintError() throws {
+        db.usesExtendedErrorCodes = true
+        try db.run(users.insert(email <- "alice@example.com"))
+        do {
+            try db.run(users.insert(email <- "alice@example.com"))
+            XCTFail("expected error")
+        } catch let Result.extendedError(_,  extendedCode, _) where extendedCode == 2_067 {
+            // SQLITE_CONSTRAINT_UNIQUE expected
+        } catch let error {
+            XCTFail("unexpected error: \(error)")
+        }
+    }
+
     // https://github.com/stephencelis/SQLite.swift/issues/285
     func test_order_by_random() throws {
         try insertUsers(["a", "b", "c'"])

From 0fc4b7b223e5402fd497c5929e220bbec02608d5 Mon Sep 17 00:00:00 2001
From: Geoff MacDonald <geoffmacd@gmail.com>
Date: Tue, 20 Dec 2022 12:21:24 -0800
Subject: [PATCH 228/326] lint

---
 Sources/SQLite/Core/Connection.swift                | 2 +-
 Sources/SQLite/Core/Result.swift                    | 2 +-
 Tests/SQLiteTests/Typed/QueryIntegrationTests.swift | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/Sources/SQLite/Core/Connection.swift b/Sources/SQLite/Core/Connection.swift
index f74ed82c..26cadea4 100644
--- a/Sources/SQLite/Core/Connection.swift
+++ b/Sources/SQLite/Core/Connection.swift
@@ -162,7 +162,7 @@ public final class Connection {
             sqlite3_extended_result_codes(handle, usesExtendedErrorCodes ? 1 : 0)
         }
     }
-    
+
     // MARK: - Execute
 
     /// Executes a batch of SQL statements.
diff --git a/Sources/SQLite/Core/Result.swift b/Sources/SQLite/Core/Result.swift
index 06f9cc74..9fe47ada 100644
--- a/Sources/SQLite/Core/Result.swift
+++ b/Sources/SQLite/Core/Result.swift
@@ -56,7 +56,7 @@ extension Result: CustomStringConvertible {
             } else {
                 return "\(message) (code: \(errorCode))"
             }
-        case let .extendedError(message,  extendedCode, statement):
+        case let .extendedError(message, extendedCode, statement):
             if let statement = statement {
                 return "\(message) (\(statement)) (extended code: \(extendedCode))"
             } else {
diff --git a/Tests/SQLiteTests/Typed/QueryIntegrationTests.swift b/Tests/SQLiteTests/Typed/QueryIntegrationTests.swift
index a86708ec..3b973fa0 100644
--- a/Tests/SQLiteTests/Typed/QueryIntegrationTests.swift
+++ b/Tests/SQLiteTests/Typed/QueryIntegrationTests.swift
@@ -235,7 +235,7 @@ class QueryIntegrationTests: SQLiteTestCase {
         do {
             try db.run(users.insert(email <- "alice@example.com"))
             XCTFail("expected error")
-        } catch let Result.extendedError(_,  extendedCode, _) where extendedCode == 2_067 {
+        } catch let Result.extendedError(_, extendedCode, _) where extendedCode == 2_067 {
             // SQLITE_CONSTRAINT_UNIQUE expected
         } catch let error {
             XCTFail("unexpected error: \(error)")

From 4b9ea97872a241fad38c221afad7a701fc987868 Mon Sep 17 00:00:00 2001
From: Matthew Jee <matthew@marmphco.com>
Date: Fri, 23 Dec 2022 00:37:03 -0800
Subject: [PATCH 229/326] Fix incorrect column names for SELECT * preceded by a
 WITH

In  #1139 I introduced support for the `WITH` clause. My implementation contains a bug: the statement preparer doesn't produce the correct result column names for queries containing a `SELECT *` preceded by a `WITH`.

For example, consider the following statement:

```
WITH temp AS ( SELECT id, email from users) SELECT * from temp
```

An error would be thrown when preparing this statement because the glob expansion procedure would try to look up the column names for the result by looking up the column names for the query `SELECT * from temp`. This does not work because `temp` is a temporary view defined in the `WITH` clause.

To fix this, I modified the glob expansion procedure to include the `WITH` clause in the query used to look up the result column names.
---
 Sources/SQLite/Typed/Query.swift              | 19 +++++++++++
 .../Typed/QueryIntegrationTests.swift         | 33 +++++++++++++++++++
 2 files changed, 52 insertions(+)

diff --git a/Sources/SQLite/Typed/Query.swift b/Sources/SQLite/Typed/Query.swift
index 04665f39..feff2f69 100644
--- a/Sources/SQLite/Typed/Query.swift
+++ b/Sources/SQLite/Typed/Query.swift
@@ -1036,10 +1036,29 @@ extension Connection {
             let column = names.removeLast()
             let namespace = names.joined(separator: ".")
 
+            // Return a copy of the input "with" clause stripping all subclauses besides "select", "join", and "with".
+            func strip(_ with: WithClauses) -> WithClauses {
+                var stripped = WithClauses()
+                stripped.recursive = with.recursive
+                for subclause in with.clauses {
+                    let query = subclause.query
+                    var strippedQuery = type(of: query).init(query.clauses.from.name, database: query.clauses.from.database)
+                    strippedQuery.clauses.select = query.clauses.select
+                    strippedQuery.clauses.join = query.clauses.join
+                    strippedQuery.clauses.with = strip(query.clauses.with)
+
+                    var strippedSubclause = WithClauses.Clause(alias: subclause.alias, query: strippedQuery)
+                    strippedSubclause.columns = subclause.columns
+                    stripped.clauses.append(strippedSubclause)
+                }
+                return stripped
+            }
+
             func expandGlob(_ namespace: Bool) -> (QueryType) throws -> Void {
                 { (queryType: QueryType) throws -> Void in
                     var query = type(of: queryType).init(queryType.clauses.from.name, database: queryType.clauses.from.database)
                     query.clauses.select = queryType.clauses.select
+                    query.clauses.with = strip(queryType.clauses.with)
                     let expression = query.expression
                     var names = try self.prepare(expression.template, expression.bindings).columnNames.map { $0.quote() }
                     if namespace { names = names.map { "\(queryType.tableName().expression.template).\($0)" } }
diff --git a/Tests/SQLiteTests/Typed/QueryIntegrationTests.swift b/Tests/SQLiteTests/Typed/QueryIntegrationTests.swift
index 3fd388e9..d99b8457 100644
--- a/Tests/SQLiteTests/Typed/QueryIntegrationTests.swift
+++ b/Tests/SQLiteTests/Typed/QueryIntegrationTests.swift
@@ -276,6 +276,39 @@ class QueryIntegrationTests: SQLiteTestCase {
 
         XCTAssertEqual(21, sum)
     }
+
+    /// Verify that `*` is properly expanded in a SELECT statement following a WITH clause.
+    func test_with_glob_expansion() throws {
+        let names = Table("names")
+        let name = Expression<String>("name")
+        try db.run(names.create { builder in
+            builder.column(email)
+            builder.column(name)
+        })
+
+        try db.run(users.insert(email <- "alice@example.com"))
+        try db.run(names.insert(email <- "alice@example.com", name <- "Alice"))
+
+        // WITH intermediate AS ( SELECT ... ) SELECT * FROM intermediate
+        let intermediate = Table("intermediate")
+        let rows = try db.prepare(
+            intermediate
+                .with(intermediate,
+                      as: users
+                        .select([id, users[email], name])
+                        .join(names, on: names[email] == users[email])
+                        .where(users[email] == "alice@example.com")
+                     ))
+
+        // There should be at least one row in the result.
+        let row = try XCTUnwrap(rows.makeIterator().next())
+        
+        // Verify the column names
+        XCTAssertEqual(row.columnNames.count, 3)
+        XCTAssertNotNil(row[id])
+        XCTAssertNotNil(row[name])
+        XCTAssertNotNil(row[email])
+    }
 }
 
 extension Connection {

From bdc3be7fdf9b6289ee0e0ea5c28002839d8cf54d Mon Sep 17 00:00:00 2001
From: Matthew Jee <matthew@marmphco.com>
Date: Fri, 23 Dec 2022 00:45:34 -0800
Subject: [PATCH 230/326] linter error

---
 Tests/SQLiteTests/Typed/QueryIntegrationTests.swift | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Tests/SQLiteTests/Typed/QueryIntegrationTests.swift b/Tests/SQLiteTests/Typed/QueryIntegrationTests.swift
index d99b8457..f5105a09 100644
--- a/Tests/SQLiteTests/Typed/QueryIntegrationTests.swift
+++ b/Tests/SQLiteTests/Typed/QueryIntegrationTests.swift
@@ -302,7 +302,7 @@ class QueryIntegrationTests: SQLiteTestCase {
 
         // There should be at least one row in the result.
         let row = try XCTUnwrap(rows.makeIterator().next())
-        
+
         // Verify the column names
         XCTAssertEqual(row.columnNames.count, 3)
         XCTAssertNotNil(row[id])

From b7c6fd6f94eedf6995ff607ae1625f71041377aa Mon Sep 17 00:00:00 2001
From: Dimitris Apostolou <dimitris.apostolou@icloud.com>
Date: Sun, 8 Jan 2023 14:07:12 +0200
Subject: [PATCH 231/326] Fix typos

---
 Documentation/Index.md                 | 2 +-
 SQLite.playground/Contents.swift       | 2 +-
 Sources/SQLite/Extensions/Cipher.swift | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/Documentation/Index.md b/Documentation/Index.md
index 730fe30b..583d5def 100644
--- a/Documentation/Index.md
+++ b/Documentation/Index.md
@@ -691,7 +691,7 @@ do {
 }
 ```
 
-Multiple rows can be inserted at once by similarily calling `insertMany` with an array of
+Multiple rows can be inserted at once by similarly calling `insertMany` with an array of
 per-row [setters](#setters).
 
 ```swift
diff --git a/SQLite.playground/Contents.swift b/SQLite.playground/Contents.swift
index f651cbc1..d893e696 100644
--- a/SQLite.playground/Contents.swift
+++ b/SQLite.playground/Contents.swift
@@ -65,7 +65,7 @@ do {
     // Handle error
 }
 
-/// define a virtual tabe for the FTS index
+/// define a virtual table for the FTS index
 let emails = VirtualTable("emails")
 
 let subject = Expression<String>("subject")
diff --git a/Sources/SQLite/Extensions/Cipher.swift b/Sources/SQLite/Extensions/Cipher.swift
index 8af04df9..03194ef1 100644
--- a/Sources/SQLite/Extensions/Cipher.swift
+++ b/Sources/SQLite/Extensions/Cipher.swift
@@ -93,7 +93,7 @@ extension Connection {
             // per recommendation of SQLCipher authors
             let migrateResult = try scalar("PRAGMA cipher_migrate;")
             if (migrateResult as? String) != "0" {
-                // "0" is the result of successfull migration
+                // "0" is the result of successful migration
                 throw Result.error(message: "Error in cipher migration, result \(migrateResult.debugDescription)", code: 1, statement: nil)
             }
         }

From 0d905d788e386854effc7133f1faf31fe9a3c9e2 Mon Sep 17 00:00:00 2001
From: "Pongsakorn Onsri(Ken)" <pongsakorn.onsri@nanameue.jp>
Date: Fri, 17 Mar 2023 17:21:31 +0700
Subject: [PATCH 232/326] fix Xcode build error

---
 Sources/SQLite/Core/Connection+Pragmas.swift | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Sources/SQLite/Core/Connection+Pragmas.swift b/Sources/SQLite/Core/Connection+Pragmas.swift
index 8f6d854b..2c4f0efb 100644
--- a/Sources/SQLite/Core/Connection+Pragmas.swift
+++ b/Sources/SQLite/Core/Connection+Pragmas.swift
@@ -7,7 +7,7 @@ public extension Connection {
     /// See SQLite [PRAGMA user_version](https://sqlite.org/pragma.html#pragma_user_version)
     var userVersion: UserVersion? {
         get {
-            (try? scalar("PRAGMA user_version") as? Int64).map(Int32.init)
+            (try? scalar("PRAGMA user_version") as? Int64)?.map(Int32.init)
         }
         set {
             _ = try? run("PRAGMA user_version = \(newValue ?? 0)")

From 5f96ca46c1843f26c040b467d9f87519f03a0888 Mon Sep 17 00:00:00 2001
From: Stefan Saasen <stefan@ohoj-software.com>
Date: Fri, 7 Apr 2023 15:41:11 +0200
Subject: [PATCH 233/326] Make ithe IndexDefinition properties public

The [documentation](https://github.com/stephencelis/SQLite.swift/blob/master/Documentation/Index.md#querying-the-schema)
suggests that the index schema information can be queried. That requires
the `IndexDefinition` properties to be public.
---
 Sources/SQLite/Schema/SchemaDefinitions.swift | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/Sources/SQLite/Schema/SchemaDefinitions.swift b/Sources/SQLite/Schema/SchemaDefinitions.swift
index 284fc4c3..b314e5c0 100644
--- a/Sources/SQLite/Schema/SchemaDefinitions.swift
+++ b/Sources/SQLite/Schema/SchemaDefinitions.swift
@@ -266,12 +266,12 @@ public struct IndexDefinition: Equatable {
                   orders: indexSQL.flatMap(orders))
     }
 
-    let table: String
-    let name: String
-    let unique: Bool
-    let columns: [String]
-    let `where`: String?
-    let orders: [String: Order]?
+    public let table: String
+    public let name: String
+    public let unique: Bool
+    public let columns: [String]
+    public let `where`: String?
+    public let orders: [String: Order]?
 
     enum IndexError: LocalizedError {
         case tooLong(String, String)

From 0718088b7885dfb08f8930111e13aa12fa9a55c6 Mon Sep 17 00:00:00 2001
From: fwcd <fwcdmail@gmail.com>
Date: Fri, 21 Apr 2023 16:10:41 +0200
Subject: [PATCH 234/326] Fix GitHub Actions build badge

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 4cf72d03..90333b16 100644
--- a/README.md
+++ b/README.md
@@ -275,7 +275,7 @@ Looking for something else? Try another Swift wrapper (or [FMDB][]):
 [SQLite3]: https://www.sqlite.org
 [SQLite.swift]: https://github.com/stephencelis/SQLite.swift
 
-[GitHubActionBadge]: https://img.shields.io/github/workflow/status/stephencelis/SQLite.swift/Build%20and%20test
+[GitHubActionBadge]: https://img.shields.io/github/actions/workflow/status/stephencelis/SQLite.swift/build.yml?branch=master
 
 [CocoaPodsVersionBadge]: https://img.shields.io/cocoapods/v/SQLite.swift.svg?style=flat
 [CocoaPodsVersionLink]: https://cocoapods.org/pods/SQLite.swift

From 481b531c6906e11d8efcc7c36ea89b7e572e6ce7 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Tue, 18 Oct 2022 22:14:04 +0200
Subject: [PATCH 235/326] Run on macOS 12

Bump deployment targets

Disable watchOS validation

CocoaPods/CocoaPods#11558

Adjust deployment targets for Xcode 14
---
 .github/workflows/build.yml      |  4 ++--
 Package.swift                    |  8 ++++----
 SQLite.swift.podspec             |  8 ++++----
 SQLite.xcodeproj/project.pbxproj | 30 ++++++++++++++++++++++--------
 Tests/SPM/Package.swift          |  8 ++++----
 run-tests.sh                     |  4 ++--
 6 files changed, 38 insertions(+), 24 deletions(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index d1659d9d..beec5ca2 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -2,10 +2,10 @@ name: Build and test
 on: [push, pull_request]
 env:
   IOS_SIMULATOR: iPhone 12
-  IOS_VERSION: "15.2"
+  IOS_VERSION: "16.0"
 jobs:
   build:
-    runs-on: macos-11
+    runs-on: macos-12
     steps:
     - uses: actions/checkout@v2
     - name: Install
diff --git a/Package.swift b/Package.swift
index 9664e99a..de40367a 100644
--- a/Package.swift
+++ b/Package.swift
@@ -4,10 +4,10 @@ import PackageDescription
 let package = Package(
     name: "SQLite.swift",
     platforms: [
-        .iOS(.v9),
-        .macOS(.v10_10),
-        .watchOS(.v3),
-        .tvOS(.v9)
+        .iOS(.v11),
+        .macOS(.v10_13),
+        .watchOS(.v4),
+        .tvOS(.v11)
     ],
     products: [
         .library(
diff --git a/SQLite.swift.podspec b/SQLite.swift.podspec
index f32282d3..4edddd67 100644
--- a/SQLite.swift.podspec
+++ b/SQLite.swift.podspec
@@ -18,10 +18,10 @@ Pod::Spec.new do |s|
   s.default_subspec  = 'standard'
   s.swift_versions = ['5']
 
-  ios_deployment_target = '9.0'
-  tvos_deployment_target = '9.1'
-  osx_deployment_target = '10.10'
-  watchos_deployment_target = '3.0'
+  ios_deployment_target = '11.0'
+  tvos_deployment_target = '11.0'
+  osx_deployment_target = '10.13'
+  watchos_deployment_target = '4.0'
 
   s.ios.deployment_target = ios_deployment_target
   s.tvos.deployment_target = tvos_deployment_target
diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj
index 3d62e980..def32855 100644
--- a/SQLite.xcodeproj/project.pbxproj
+++ b/SQLite.xcodeproj/project.pbxproj
@@ -1263,6 +1263,7 @@
 				SDKROOT = appletvos;
 				SKIP_INSTALL = YES;
 				SWIFT_TREAT_WARNINGS_AS_ERRORS = YES;
+				TVOS_DEPLOYMENT_TARGET = 11.0;
 			};
 			name = Debug;
 		};
@@ -1284,6 +1285,7 @@
 				SDKROOT = appletvos;
 				SKIP_INSTALL = YES;
 				SWIFT_TREAT_WARNINGS_AS_ERRORS = YES;
+				TVOS_DEPLOYMENT_TARGET = 11.0;
 			};
 			name = Release;
 		};
@@ -1297,6 +1299,7 @@
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SDKROOT = appletvos;
 				SWIFT_TREAT_WARNINGS_AS_ERRORS = YES;
+				TVOS_DEPLOYMENT_TARGET = 11.0;
 			};
 			name = Debug;
 		};
@@ -1310,6 +1313,7 @@
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SDKROOT = appletvos;
 				SWIFT_TREAT_WARNINGS_AS_ERRORS = YES;
+				TVOS_DEPLOYMENT_TARGET = 11.0;
 			};
 			name = Release;
 		};
@@ -1333,6 +1337,7 @@
 				SKIP_INSTALL = YES;
 				SWIFT_TREAT_WARNINGS_AS_ERRORS = YES;
 				TARGETED_DEVICE_FAMILY = 4;
+				WATCHOS_DEPLOYMENT_TARGET = 4.0;
 			};
 			name = Debug;
 		};
@@ -1356,6 +1361,7 @@
 				SKIP_INSTALL = YES;
 				SWIFT_TREAT_WARNINGS_AS_ERRORS = YES;
 				TARGETED_DEVICE_FAMILY = 4;
+				WATCHOS_DEPLOYMENT_TARGET = 4.0;
 			};
 			name = Release;
 		};
@@ -1408,8 +1414,8 @@
 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
-				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
-				MACOSX_DEPLOYMENT_TARGET = 10.10;
+				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
+				MACOSX_DEPLOYMENT_TARGET = 10.13;
 				MTL_ENABLE_DEBUG_INFO = YES;
 				ONLY_ACTIVE_ARCH = YES;
 				PRODUCT_NAME = "";
@@ -1417,10 +1423,10 @@
 				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
 				SWIFT_VERSION = 5.0;
 				TARGETED_DEVICE_FAMILY = "1,2,3";
-				TVOS_DEPLOYMENT_TARGET = 9.1;
+				TVOS_DEPLOYMENT_TARGET = 11.0;
 				VERSIONING_SYSTEM = "apple-generic";
 				VERSION_INFO_PREFIX = "";
-				WATCHOS_DEPLOYMENT_TARGET = 3.0;
+				WATCHOS_DEPLOYMENT_TARGET = 4.0;
 			};
 			name = Debug;
 		};
@@ -1467,19 +1473,19 @@
 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
-				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
-				MACOSX_DEPLOYMENT_TARGET = 10.10;
+				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
+				MACOSX_DEPLOYMENT_TARGET = 10.13;
 				MTL_ENABLE_DEBUG_INFO = NO;
 				PRODUCT_NAME = "";
 				SDKROOT = iphoneos;
 				SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
 				SWIFT_VERSION = 5.0;
 				TARGETED_DEVICE_FAMILY = "1,2,3";
-				TVOS_DEPLOYMENT_TARGET = 9.1;
+				TVOS_DEPLOYMENT_TARGET = 11.0;
 				VALIDATE_PRODUCT = YES;
 				VERSIONING_SYSTEM = "apple-generic";
 				VERSION_INFO_PREFIX = "";
-				WATCHOS_DEPLOYMENT_TARGET = 3.0;
+				WATCHOS_DEPLOYMENT_TARGET = 4.0;
 			};
 			name = Release;
 		};
@@ -1496,6 +1502,7 @@
 				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
 				INFOPLIST_FILE = "$(SRCROOT)/Sources/SQLite/Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
 				MARKETING_VERSION = 0.14.0;
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
@@ -1519,6 +1526,7 @@
 				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
 				INFOPLIST_FILE = "$(SRCROOT)/Sources/SQLite/Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
 				MARKETING_VERSION = 0.14.0;
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
@@ -1533,6 +1541,7 @@
 			buildSettings = {
 				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
 				INFOPLIST_FILE = Tests/SQLiteTests/Info.plist;
+				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLiteTests;
 				PRODUCT_NAME = "$(TARGET_NAME)";
@@ -1545,6 +1554,7 @@
 			buildSettings = {
 				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
 				INFOPLIST_FILE = Tests/SQLiteTests/Info.plist;
+				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLiteTests;
 				PRODUCT_NAME = "$(TARGET_NAME)";
@@ -1567,6 +1577,7 @@
 				INFOPLIST_FILE = "$(SRCROOT)/Sources/SQLite/Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
+				MACOSX_DEPLOYMENT_TARGET = 10.13;
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
 				PRODUCT_NAME = SQLite;
 				SDKROOT = macosx;
@@ -1591,6 +1602,7 @@
 				INFOPLIST_FILE = "$(SRCROOT)/Sources/SQLite/Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
+				MACOSX_DEPLOYMENT_TARGET = 10.13;
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
 				PRODUCT_NAME = SQLite;
 				SDKROOT = macosx;
@@ -1608,6 +1620,7 @@
 				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
 				INFOPLIST_FILE = Tests/SQLiteTests/Info.plist;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
+				MACOSX_DEPLOYMENT_TARGET = 10.13;
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLiteTests;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SDKROOT = macosx;
@@ -1623,6 +1636,7 @@
 				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
 				INFOPLIST_FILE = Tests/SQLiteTests/Info.plist;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
+				MACOSX_DEPLOYMENT_TARGET = 10.13;
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLiteTests;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SDKROOT = macosx;
diff --git a/Tests/SPM/Package.swift b/Tests/SPM/Package.swift
index 3e329552..51f176f4 100644
--- a/Tests/SPM/Package.swift
+++ b/Tests/SPM/Package.swift
@@ -6,10 +6,10 @@ import PackageDescription
 let package = Package(
     name: "test",
     platforms: [
-        .iOS(.v9),
-        .macOS(.v10_10),
-        .watchOS(.v3),
-        .tvOS(.v9)
+        .iOS(.v11),
+        .macOS(.v10_13),
+        .watchOS(.v4),
+        .tvOS(.v11)
     ],
     dependencies: [
         // for testing from same repository
diff --git a/run-tests.sh b/run-tests.sh
index 49330c12..c54300ee 100755
--- a/run-tests.sh
+++ b/run-tests.sh
@@ -8,9 +8,9 @@ if [ -n "$BUILD_SCHEME" ]; then
     fi
 elif [ -n "$VALIDATOR_SUBSPEC" ]; then
     if [ "$VALIDATOR_SUBSPEC" == "none" ]; then
-      pod lib lint --no-subspecs --fail-fast
+      pod lib lint --no-subspecs --fail-fast --platforms=ios,osx,tvos
     else
-      pod lib lint --subspec="${VALIDATOR_SUBSPEC}" --fail-fast
+      pod lib lint --subspec="${VALIDATOR_SUBSPEC}" --fail-fast --platforms=ios,osx,tvos
     fi
 elif [ -n "$CARTHAGE_PLATFORM" ]; then
     cd Tests/Carthage && make test CARTHAGE_PLATFORM="$CARTHAGE_PLATFORM"

From a2897d5512f897e566247808bf7d87b2f033b5e7 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Sun, 21 May 2023 12:45:26 +0200
Subject: [PATCH 236/326] Run on macos-13

---
 .github/workflows/build.yml | 4 ++--
 .gitignore                  | 1 +
 Package.swift               | 2 +-
 Tests/SPM/Package.swift     | 7 ++-----
 4 files changed, 6 insertions(+), 8 deletions(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index beec5ca2..ae806ecc 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -2,10 +2,10 @@ name: Build and test
 on: [push, pull_request]
 env:
   IOS_SIMULATOR: iPhone 12
-  IOS_VERSION: "16.0"
+  IOS_VERSION: "16.4"
 jobs:
   build:
-    runs-on: macos-12
+    runs-on: macos-13
     steps:
     - uses: actions/checkout@v2
     - name: Install
diff --git a/.gitignore b/.gitignore
index 5882e0cb..b7d43ec9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -25,3 +25,4 @@ DerivedData
 # Swift Package Manager
 .build
 Packages/
+.swiftpm/
diff --git a/Package.swift b/Package.swift
index de40367a..70bde7ef 100644
--- a/Package.swift
+++ b/Package.swift
@@ -1,4 +1,4 @@
-// swift-tools-version:5.3
+// swift-tools-version:5.7
 import PackageDescription
 
 let package = Package(
diff --git a/Tests/SPM/Package.swift b/Tests/SPM/Package.swift
index 51f176f4..3c446ae6 100644
--- a/Tests/SPM/Package.swift
+++ b/Tests/SPM/Package.swift
@@ -1,4 +1,4 @@
-// swift-tools-version:5.3
+// swift-tools-version:5.7
 // The swift-tools-version declares the minimum version of Swift required to build this package.
 
 import PackageDescription
@@ -18,9 +18,6 @@ let package = Package(
         // .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.14.1")
     ],
     targets: [
-        .target(
-            name: "test",
-            dependencies: [.product(name: "SQLite", package: "SQLite.swift")]
-	)
+        .executableTarget(name: "test", dependencies: [.product(name: "SQLite", package: "SQLite.swift")])
     ]
 )

From 85f1d443e9b758aaeea3fc5c9b3abef67675931b Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Sun, 21 May 2023 15:09:30 +0200
Subject: [PATCH 237/326] Automatically download swiftlint / xcbeautifier, lint
 fixes

---
 .gitignore                                    |  3 +
 .swiftlint.yml                                |  5 +-
 Makefile                                      | 81 +++++++++++++------
 Sources/SQLite/Core/Backup.swift              |  2 +-
 Sources/SQLite/Helpers.swift                  |  1 +
 Sources/SQLite/Schema/SchemaReader.swift      |  2 +-
 Sources/SQLite/Typed/Query.swift              |  2 +-
 Tests/.swiftlint.yml                          |  4 +-
 Tests/SQLiteTests/Schema/SchemaTests.swift    |  2 +
 .../Typed/CustomFunctionsTests.swift          | 16 ++--
 10 files changed, 80 insertions(+), 38 deletions(-)

diff --git a/.gitignore b/.gitignore
index b7d43ec9..e7b2ad4d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -22,6 +22,9 @@ DerivedData
 # Carthage
 /Carthage/
 
+# Makefile
+bin/
+
 # Swift Package Manager
 .build
 Packages/
diff --git a/.swiftlint.yml b/.swiftlint.yml
index d40be28e..e728e191 100644
--- a/.swiftlint.yml
+++ b/.swiftlint.yml
@@ -4,6 +4,7 @@ disabled_rules: # rule identifiers to exclude from running
   - large_tuple
   - closure_parameter_position
   - inclusive_language  # sqlite_master etc.
+  - blanket_disable_command
 included: # paths to include during linting. `--path` is ignored if present. takes precendence over `excluded`.
   - Sources
   - Tests
@@ -26,8 +27,8 @@ identifier_name:
     - SQLITE_TRANSIENT
 
 type_body_length:
-  warning: 260
-  error: 260
+  warning: 350
+  error: 350
 
 function_body_length:
   warning: 60
diff --git a/Makefile b/Makefile
index 74bf5d18..73b33f97 100644
--- a/Makefile
+++ b/Makefile
@@ -1,41 +1,76 @@
-BUILD_TOOL = xcodebuild
+XCODEBUILD = xcodebuild
 BUILD_SCHEME = SQLite Mac
-IOS_SIMULATOR = iPhone 12
-IOS_VERSION = 15.0
+IOS_SIMULATOR = iPhone 14
+IOS_VERSION = 16.4
+
+# tool settings
+SWIFTLINT_VERSION=0.52.2
+SWIFTLINT=bin/swiftlint-$(SWIFTLINT_VERSION)
+SWIFTLINT_URL=https://github.com/realm/SwiftLint/releases/download/$(SWIFTLINT_VERSION)/portable_swiftlint.zip
+XCBEAUTIFY_VERSION=0.20.0
+XCBEAUTIFY=bin/xcbeautify-$(XCBEAUTIFY_VERSION)
+ifeq ($(shell uname), Linux)
+	XCBEAUTIFY_PLATFORM=x86_64-unknown-linux-gnu.tar.xz
+else
+	XCBEAUTIFY_PLATFORM=universal-apple-macosx.zip
+endif
+XCBEAUTIFY_URL=https://github.com/tuist/xcbeautify/releases/download/$(XCBEAUTIFY_VERSION)/xcbeautify-$(XCBEAUTIFY_VERSION)-$(XCBEAUTIFY_PLATFORM)
+CURL_OPTS=--fail --silent -L --retry 3
+
 ifeq ($(BUILD_SCHEME),SQLite iOS)
 	BUILD_ARGUMENTS = -scheme "$(BUILD_SCHEME)" -destination "platform=iOS Simulator,name=$(IOS_SIMULATOR),OS=$(IOS_VERSION)"
 else
 	BUILD_ARGUMENTS = -scheme "$(BUILD_SCHEME)"
 endif
 
-XCPRETTY := $(shell command -v xcpretty)
-TEST_ACTIONS := clean build build-for-testing test-without-building
+test: $(XCBEAUTIFY)
+	set -o pipefail; \
+	$(XCODEBUILD) $(BUILD_ARGUMENTS) test | $(XCBEAUTIFY)
 
-default: test
+build: $(XCBEAUTIFY)
+	set -o pipefail; \
+	$(XCODEBUILD) $(BUILD_ARGUMENTS) | $(XCBEAUTIFY)
 
-build:
-	$(BUILD_TOOL) $(BUILD_ARGUMENTS)
+lint: $(SWIFTLINT)
+	$< --strict
 
-lint:
-	swiftlint --strict
-lint-fix:
-	swiftlint lint fix
-
-test:
-ifdef XCPRETTY
-	@set -o pipefail && $(BUILD_TOOL) $(BUILD_ARGUMENTS) $(TEST_ACTIONS) | $(XCPRETTY) -c
-else
-	$(BUILD_TOOL) $(BUILD_ARGUMENTS) $(TEST_ACTIONS)
-endif
+lint-fix: $(SWIFTLINT)
+	$< lint fix
 
 clean:
-	$(BUILD_TOOL) $(BUILD_ARGUMENTS) clean
+	$(XCODEBUILD) $(BUILD_ARGUMENTS) clean
 
 repl:
-	@$(BUILD_TOOL) $(BUILD_ARGUMENTS) -derivedDataPath $(TMPDIR)/SQLite.swift > /dev/null && \
-		swift -F '$(TMPDIR)/SQLite.swift/Build/Products/Debug'
+	@$(XCODEBUILD) $(BUILD_ARGUMENTS) -derivedDataPath $(TMPDIR)/SQLite.swift > /dev/null && \
+		swift repl -F '$(TMPDIR)/SQLite.swift/Build/Products/Debug'
 
 sloc:
-	@zsh -c "grep -vE '^ *//|^$$' Sources/**/*.{swift,h,m} | wc -l"
+	@zsh -c "grep -vE '^ *//|^$$' Sources/**/*.{swift,h} | wc -l"
+
+$(SWIFTLINT):
+	set -e ; \
+	curl $(CURL_OPTS) $(SWIFTLINT_URL) -o swiftlint.zip; \
+	unzip -o swiftlint.zip swiftlint; \
+	mkdir -p bin; \
+	mv swiftlint $@ && rm -f swiftlint.zip
+
+$(XCBEAUTIFY):
+	set -e; \
+	FILE=$(XCBEAUTIFY_PLATFORM); \
+	curl $(CURL_OPTS) $(XCBEAUTIFY_URL) -o $$FILE; \
+	case "$${FILE#*.}" in \
+	  "zip") \
+		unzip -o $$FILE xcbeautify; \
+		;; \
+	  "tar.xz") \
+	  	tar -xvf $$FILE xcbeautify; \
+		;; \
+	  *) \
+		echo "unknown extension $${FILE#*.}!"; \
+		exit 1; \
+		;; \
+	esac; \
+	mkdir -p bin; \
+	mv xcbeautify $@ && rm -f $$FILE;
 
 .PHONY: test clean repl sloc
diff --git a/Sources/SQLite/Core/Backup.swift b/Sources/SQLite/Core/Backup.swift
index 84ebf1e0..0eebbdd5 100644
--- a/Sources/SQLite/Core/Backup.swift
+++ b/Sources/SQLite/Core/Backup.swift
@@ -140,7 +140,7 @@ public final class Backup {
     /// - Parameter pagesToCopy: The maximal number of pages to copy in one step
     ///
     /// - Throws: `Result.Error` if step fails.
-    //
+    ///
     /// See: <https://www.sqlite.org/c3ref/backup_finish.html#sqlite3backupstep>
     public func step(pagesToCopy pages: Pages = .all) throws {
         let status = sqlite3_backup_step(handle, pages.number)
diff --git a/Sources/SQLite/Helpers.swift b/Sources/SQLite/Helpers.swift
index e3d37e11..79d057d6 100644
--- a/Sources/SQLite/Helpers.swift
+++ b/Sources/SQLite/Helpers.swift
@@ -38,6 +38,7 @@ public func *(_: Expression<Binding>?, _: Expression<Binding>?) -> Expression<Vo
     Expression(literal: "*")
 }
 
+// swiftlint:disable:next type_name
 public protocol _OptionalType {
 
     associatedtype WrappedType
diff --git a/Sources/SQLite/Schema/SchemaReader.swift b/Sources/SQLite/Schema/SchemaReader.swift
index 1c26fdd6..0c02ba9f 100644
--- a/Sources/SQLite/Schema/SchemaReader.swift
+++ b/Sources/SQLite/Schema/SchemaReader.swift
@@ -13,7 +13,7 @@ public class SchemaReader {
     // column name, data type, whether or not the column can be NULL, and the default value for the column. The
     // "pk" column in the result set is zero for columns that are not part of the primary key, and is the
     // index of the column in the primary key for columns that are part of the primary key.
-    public func columnDefinitions(table: String) throws  -> [ColumnDefinition] {
+    public func columnDefinitions(table: String) throws -> [ColumnDefinition] {
         func parsePrimaryKey(column: String) throws -> ColumnDefinition.PrimaryKey? {
             try createTableSQL(name: table).flatMap { .init(sql: $0) }
         }
diff --git a/Sources/SQLite/Typed/Query.swift b/Sources/SQLite/Typed/Query.swift
index feff2f69..c59b728f 100644
--- a/Sources/SQLite/Typed/Query.swift
+++ b/Sources/SQLite/Typed/Query.swift
@@ -1105,7 +1105,7 @@ extension Connection {
         return value(try scalar(expression.template, expression.bindings))
     }
 
-    public func scalar<V: Value>(_ query: Select<V?>) throws ->  V.ValueType? {
+    public func scalar<V: Value>(_ query: Select<V?>) throws -> V.ValueType? {
         let expression = query.expression
         guard let value = try scalar(expression.template, expression.bindings) as? V.Datatype else { return nil }
         return V.fromDatatypeValue(value)
diff --git a/Tests/.swiftlint.yml b/Tests/.swiftlint.yml
index 3537085c..81d6c314 100644
--- a/Tests/.swiftlint.yml
+++ b/Tests/.swiftlint.yml
@@ -6,8 +6,8 @@ disabled_rules:
   - identifier_name
 
 type_body_length:
-  warning: 800
-  error: 800
+  warning: 1000
+  error: 1000
 
 function_body_length:
   warning: 200
diff --git a/Tests/SQLiteTests/Schema/SchemaTests.swift b/Tests/SQLiteTests/Schema/SchemaTests.swift
index a7de8bcf..4f4a49d1 100644
--- a/Tests/SQLiteTests/Schema/SchemaTests.swift
+++ b/Tests/SQLiteTests/Schema/SchemaTests.swift
@@ -72,6 +72,7 @@ class SchemaTests: XCTestCase {
     }
 
     // thoroughness test for ambiguity
+    // swiftlint:disable:next function_body_length
     func test_column_compilesColumnDefinitionExpression() {
         XCTAssertEqual(
             "CREATE TABLE \"table\" (\"int64\" INTEGER NOT NULL)",
@@ -390,6 +391,7 @@ class SchemaTests: XCTestCase {
         )
     }
 
+    // swiftlint:disable:next function_body_length
     func test_column_withStringExpression_compilesCollatedColumnDefinitionExpression() {
         XCTAssertEqual(
             "CREATE TABLE \"table\" (\"string\" TEXT NOT NULL COLLATE RTRIM)",
diff --git a/Tests/SQLiteTests/Typed/CustomFunctionsTests.swift b/Tests/SQLiteTests/Typed/CustomFunctionsTests.swift
index c4eb51e6..0f46b380 100644
--- a/Tests/SQLiteTests/Typed/CustomFunctionsTests.swift
+++ b/Tests/SQLiteTests/Typed/CustomFunctionsTests.swift
@@ -5,8 +5,8 @@ import SQLite
 #if !os(Linux)
 
 class CustomFunctionNoArgsTests: SQLiteTestCase {
-    typealias FunctionNoOptional              = ()  -> Expression<String>
-    typealias FunctionResultOptional          = ()  -> Expression<String?>
+    typealias FunctionNoOptional              = () -> Expression<String>
+    typealias FunctionResultOptional          = () -> Expression<String?>
 
     func testFunctionNoOptional() throws {
         let _: FunctionNoOptional = try db.createFunction("test", deterministic: true) {
@@ -26,9 +26,9 @@ class CustomFunctionNoArgsTests: SQLiteTestCase {
 }
 
 class CustomFunctionWithOneArgTests: SQLiteTestCase {
-    typealias FunctionNoOptional              = (Expression<String>)  -> Expression<String>
+    typealias FunctionNoOptional              = (Expression<String>) -> Expression<String>
     typealias FunctionLeftOptional            = (Expression<String?>) -> Expression<String>
-    typealias FunctionResultOptional          = (Expression<String>)  -> Expression<String?>
+    typealias FunctionResultOptional          = (Expression<String>) -> Expression<String?>
     typealias FunctionLeftResultOptional      = (Expression<String?>) -> Expression<String?>
 
     func testFunctionNoOptional() throws {
@@ -65,12 +65,12 @@ class CustomFunctionWithOneArgTests: SQLiteTestCase {
 }
 
 class CustomFunctionWithTwoArgsTests: SQLiteTestCase {
-    typealias FunctionNoOptional              = (Expression<String>, Expression<String>)  -> Expression<String>
-    typealias FunctionLeftOptional            = (Expression<String?>, Expression<String>)  -> Expression<String>
+    typealias FunctionNoOptional              = (Expression<String>, Expression<String>) -> Expression<String>
+    typealias FunctionLeftOptional            = (Expression<String?>, Expression<String>) -> Expression<String>
     typealias FunctionRightOptional           = (Expression<String>, Expression<String?>) -> Expression<String>
-    typealias FunctionResultOptional          = (Expression<String>, Expression<String>)  -> Expression<String?>
+    typealias FunctionResultOptional          = (Expression<String>, Expression<String>) -> Expression<String?>
     typealias FunctionLeftRightOptional       = (Expression<String?>, Expression<String?>) -> Expression<String>
-    typealias FunctionLeftResultOptional      = (Expression<String?>, Expression<String>)  -> Expression<String?>
+    typealias FunctionLeftResultOptional      = (Expression<String?>, Expression<String>) -> Expression<String?>
     typealias FunctionRightResultOptional     = (Expression<String>, Expression<String?>) -> Expression<String?>
     typealias FunctionLeftRightResultOptional = (Expression<String?>, Expression<String?>) -> Expression<String?>
 

From 4a0ab2a7445712e71ff803e020b74f9557ae64a8 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Sun, 21 May 2023 15:14:36 +0200
Subject: [PATCH 238/326] Fix simulator version

---
 .github/workflows/build.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index ae806ecc..ac705e4e 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -1,7 +1,7 @@
 name: Build and test
 on: [push, pull_request]
 env:
-  IOS_SIMULATOR: iPhone 12
+  IOS_SIMULATOR: "iPhone 14"
   IOS_VERSION: "16.4"
 jobs:
   build:

From e4f4ddff5f124652421d7cd99512181bed86b0e7 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Sun, 21 May 2023 15:22:26 +0200
Subject: [PATCH 239/326] Use newer Xcode

---
 .github/workflows/build.yml | 10 ++++------
 1 file changed, 4 insertions(+), 6 deletions(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index ac705e4e..fbe4ec49 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -8,13 +8,11 @@ jobs:
     runs-on: macos-13
     steps:
     - uses: actions/checkout@v2
-    - name: Install
+    - name: "Select Xcode"
       run: |
-        gem update bundler
-        gem install xcpretty --no-document
-        brew update
-        brew outdated carthage || brew upgrade carthage
-        brew outdated swiftlint || brew upgrade swiftlint
+        xcode-select -p
+        xcode-select -s /Applications/Xcode-14.3.app/Contents/Developer
+        xcode-select -p
     - name: "Lint"
       run: make lint
     - name: "Run tests (PACKAGE_MANAGER_COMMAND: test)"

From 1bc58a5477cc47856d1e6a31b834f1cc0f66c00b Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Sun, 21 May 2023 15:28:43 +0200
Subject: [PATCH 240/326] Fix path

---
 .github/workflows/build.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index fbe4ec49..8c893c77 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -11,7 +11,7 @@ jobs:
     - name: "Select Xcode"
       run: |
         xcode-select -p
-        xcode-select -s /Applications/Xcode-14.3.app/Contents/Developer
+        xcode-select -s /Applications/Xcode_14.3.app/Contents/Developer
         xcode-select -p
     - name: "Lint"
       run: make lint

From 79bab4610b74cc7a8dee118a6b9ebce769aaec61 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Sun, 21 May 2023 15:33:04 +0200
Subject: [PATCH 241/326] sudo

---
 .github/workflows/build.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 8c893c77..15f325ed 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -11,7 +11,7 @@ jobs:
     - name: "Select Xcode"
       run: |
         xcode-select -p
-        xcode-select -s /Applications/Xcode_14.3.app/Contents/Developer
+        sudo xcode-select -s /Applications/Xcode_14.3.app/Contents/Developer
         xcode-select -p
     - name: "Lint"
       run: make lint

From b028fcdda1489a3bb0c738f8809154f9c4be7f5b Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Sun, 21 May 2023 17:48:05 +0200
Subject: [PATCH 242/326] Try Xcode 14.2

---
 .github/workflows/build.yml | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 15f325ed..242a9021 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -2,7 +2,7 @@ name: Build and test
 on: [push, pull_request]
 env:
   IOS_SIMULATOR: "iPhone 14"
-  IOS_VERSION: "16.4"
+  IOS_VERSION: "16.2"
 jobs:
   build:
     runs-on: macos-13
@@ -11,8 +11,7 @@ jobs:
     - name: "Select Xcode"
       run: |
         xcode-select -p
-        sudo xcode-select -s /Applications/Xcode_14.3.app/Contents/Developer
-        xcode-select -p
+        sudo xcode-select -s /Applications/Xcode_14.2.app/Contents/Developer
     - name: "Lint"
       run: make lint
     - name: "Run tests (PACKAGE_MANAGER_COMMAND: test)"

From 101109848902bb2c7061286c86f15a638d6d5a17 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Sun, 21 May 2023 22:28:40 +0200
Subject: [PATCH 243/326] Update docs

---
 .github/workflows/build.yml |  2 ++
 CHANGELOG.md                |  5 +++++
 Documentation/Index.md      | 31 ++++++++++++++-----------------
 README.md                   |  5 +----
 4 files changed, 22 insertions(+), 21 deletions(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 242a9021..9cc2f56e 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -9,6 +9,8 @@ jobs:
     steps:
     - uses: actions/checkout@v2
     - name: "Select Xcode"
+      # Currently only works with Xcode 14.2:
+      # https://github.com/CocoaPods/CocoaPods/issues/11839
       run: |
         xcode-select -p
         sudo xcode-select -s /Applications/Xcode_14.2.app/Contents/Developer
diff --git a/CHANGELOG.md b/CHANGELOG.md
index fc759118..970b4ca1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,8 @@
+0.15.0 (unreleased)
+========================================
+
+* New minimum deployment targets: iOS/tvOS 11.0, watchOS 4.0
+
 0.14.1 (01-11-2022), [diff][diff-0.14.1]
 ========================================
 
diff --git a/Documentation/Index.md b/Documentation/Index.md
index 583d5def..5d72aa4b 100644
--- a/Documentation/Index.md
+++ b/Documentation/Index.md
@@ -77,9 +77,6 @@
 
 ## Installation
 
-> _Note:_ SQLite.swift requires Swift 5 (and
-> [Xcode 10.2](https://developer.apple.com/xcode/downloads/)) or greater.
-
 ### Swift Package Manager
 
 The [Swift Package Manager][] is a tool for managing the distribution of
@@ -1142,11 +1139,11 @@ let query = managers
 chain.with(chain, recursive: true, as: query)
 // WITH RECURSIVE
 //   "chain" AS (
-//     SELECT * FROM "managers" WHERE "id" = 8 
-//     UNION 
-//     SELECT * from "chain" 
+//     SELECT * FROM "managers" WHERE "id" = 8
+//     UNION
+//     SELECT * from "chain"
 //     JOIN "managers" ON "chain"."manager_id" = "managers"."id"
-//   ) 
+//   )
 // SELECT * FROM "chain"
 ```
 
@@ -1156,7 +1153,7 @@ Column names and a materialization hint can optionally be provided.
 // Add a "level" column to the query representing manager's position in the chain
 let level = Expression<Int64>("level")
 
-let queryWithLevel = 
+let queryWithLevel =
     managers
         .select(id, managerId, 0)
         .where(id == 8)
@@ -1166,18 +1163,18 @@ let queryWithLevel =
                 .join(managers, on: chain[managerId] == managers[id])
         )
 
-chain.with(chain, 
-           columns: [id, managerId, level], 
+chain.with(chain,
+           columns: [id, managerId, level],
            recursive: true,
            hint: .materialize,
            as: queryWithLevel)
 // WITH RECURSIVE
 //   "chain" ("id", "manager_id", "level") AS MATERIALIZED (
-//     SELECT ("id", "manager_id", 0) FROM "managers" WHERE "id" = 8 
-//     UNION 
-//     SELECT ("manager"."id", "manager"."manager_id", "level" + 1) FROM "chain" 
+//     SELECT ("id", "manager_id", 0) FROM "managers" WHERE "id" = 8
+//     UNION
+//     SELECT ("manager"."id", "manager"."manager_id", "level" + 1) FROM "chain"
 //     JOIN "managers" ON "chain"."manager_id" = "managers"."id"
-//   ) 
+//   )
 // SELECT * FROM "chain"
 ```
 
@@ -1266,7 +1263,7 @@ let count = try db.scalar(users.filter(name != nil).count)
 We can upsert rows into a table by calling a [query’s](#queries) `upsert`
 function with a list of [setters](#setters)—typically [typed column
 expressions](#expressions) and values (which can also be expressions)—each
-joined by the `<-` operator. Upserting is like inserting, except if there is a 
+joined by the `<-` operator. Upserting is like inserting, except if there is a
 conflict on the specified column value, SQLite will perform an update on the row instead.
 
 ```swift
@@ -1957,7 +1954,7 @@ for row in stmt.bind(kUTTypeImage) { /* ... */ }
 ```
 
 > _Note:_ Prepared queries can be reused, and long lived prepared queries should be `reset()` after each use. Otherwise, the transaction (either [implicit or explicit](https://www.sqlite.org/lang_transaction.html#implicit_versus_explicit_transactions)) will be held open until the query is reset or finalized. This can affect performance. Statements are reset automatically during `deinit`.
-> 
+>
 > ```swift
 > someObj.statement = try db.prepare("SELECT * FROM attachments WHERE typeConformsTo(UTI, ?)")
 > for row in someObj.statement.bind(kUTTypeImage) { /* ... */ }
@@ -2134,7 +2131,7 @@ using the following functions.
         }
     }
     ```
-    
+
   - `run` prepares a single `Statement` object from a SQL string, optionally
     binds values to it (using the statement’s `bind` function), executes,
     and returns the statement.
diff --git a/README.md b/README.md
index 90333b16..e6e5a894 100644
--- a/README.md
+++ b/README.md
@@ -90,7 +90,7 @@ API.
 // Wrap everything in a do...catch to handle errors
 do {
     // ...
-    
+
     let stmt = try db.prepare("INSERT INTO users (email) VALUES (?)")
     for email in ["betty@icloud.com", "cathy@icloud.com"] {
         try stmt.run(email)
@@ -119,9 +119,6 @@ interactively, from the Xcode project’s playground.
 
 ## Installation
 
-> _Note:_ Version 0.11.6 and later requires Swift 5 (and [Xcode](https://developer.apple.com/xcode/downloads/) 10.2) or greater.
-> Version 0.11.5 requires Swift 4.2 (and [Xcode](https://developer.apple.com/xcode/downloads/) 10.1) or greater.
-
 ### Swift Package Manager
 
 The [Swift Package Manager][] is a tool for managing the distribution of

From ab3c598db29a7a2db3e06e9642736cb18e670a0e Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Sun, 21 May 2023 23:59:35 +0200
Subject: [PATCH 244/326] Use shorthand optional binding

---
 .swiftlint.yml                                |  2 ++
 Sources/SQLite/Core/Connection+Attach.swift   |  2 +-
 Sources/SQLite/Core/Connection.swift          | 18 +++++------
 Sources/SQLite/Core/Result.swift              |  4 +--
 Sources/SQLite/Extensions/FTS4.swift          | 16 +++++-----
 Sources/SQLite/Extensions/FTS5.swift          |  6 ++--
 Sources/SQLite/Helpers.swift                  |  2 +-
 Sources/SQLite/Schema/SchemaDefinitions.swift |  2 +-
 Sources/SQLite/Schema/SchemaReader.swift      |  4 +--
 Sources/SQLite/Typed/Coding.swift             | 12 ++++----
 Sources/SQLite/Typed/CoreFunctions.swift      | 30 +++++++++----------
 Sources/SQLite/Typed/Operators.swift          | 16 +++++-----
 12 files changed, 58 insertions(+), 56 deletions(-)

diff --git a/.swiftlint.yml b/.swiftlint.yml
index e728e191..1bb21cd5 100644
--- a/.swiftlint.yml
+++ b/.swiftlint.yml
@@ -1,3 +1,5 @@
+opt_in_rules:
+  - shorthand_optional_binding
 disabled_rules: # rule identifiers to exclude from running
   - todo
   - operator_whitespace
diff --git a/Sources/SQLite/Core/Connection+Attach.swift b/Sources/SQLite/Core/Connection+Attach.swift
index 47e6af5e..8a25e51d 100644
--- a/Sources/SQLite/Core/Connection+Attach.swift
+++ b/Sources/SQLite/Core/Connection+Attach.swift
@@ -13,7 +13,7 @@ extension Connection {
     #if SQLITE_SWIFT_SQLCIPHER
     /// See https://www.zetetic.net/sqlcipher/sqlcipher-api/#attach
     public func attach(_ location: Location, as schemaName: String, key: String? = nil) throws {
-        if let key = key {
+        if let key {
             try run("ATTACH DATABASE ? AS ? KEY ?", location.description, schemaName, key)
         } else {
             try run("ATTACH DATABASE ? AS ?", location.description, schemaName)
diff --git a/Sources/SQLite/Core/Connection.swift b/Sources/SQLite/Core/Connection.swift
index 26cadea4..f2c3b781 100644
--- a/Sources/SQLite/Core/Connection.swift
+++ b/Sources/SQLite/Core/Connection.swift
@@ -414,7 +414,7 @@ public final class Connection {
     ///   times it’s been called for this lock. If it returns `true`, it will
     ///   try again. If it returns `false`, no further attempts will be made.
     public func busyHandler(_ callback: ((_ tries: Int) -> Bool)?) {
-        guard let callback = callback else {
+        guard let callback else {
             sqlite3_busy_handler(handle, nil, nil)
             busyHandler = nil
             return
@@ -449,7 +449,7 @@ public final class Connection {
     @available(watchOS, deprecated: 3.0)
     @available(tvOS, deprecated: 10.0)
     fileprivate func trace_v1(_ callback: ((String) -> Void)?) {
-        guard let callback = callback else {
+        guard let callback else {
             sqlite3_trace(handle, nil /* xCallback */, nil /* pCtx */)
             trace = nil
             return
@@ -458,7 +458,7 @@ public final class Connection {
             callback(String(cString: pointer.assumingMemoryBound(to: UInt8.self)))
         }
         sqlite3_trace(handle, { (context: UnsafeMutableRawPointer?, SQL: UnsafePointer<Int8>?) in
-                    if let context = context, let SQL = SQL {
+                    if let context, let SQL {
                         unsafeBitCast(context, to: Trace.self)(SQL)
                     }
             },
@@ -469,7 +469,7 @@ public final class Connection {
 
     @available(iOS 10.0, OSX 10.12, tvOS 10.0, watchOS 3.0, *)
     fileprivate func trace_v2(_ callback: ((String) -> Void)?) {
-        guard let callback = callback else {
+        guard let callback else {
             // If the X callback is NULL or if the M mask is zero, then tracing is disabled.
             sqlite3_trace_v2(handle, 0 /* mask */, nil /* xCallback */, nil /* pCtx */)
             trace = nil
@@ -485,7 +485,7 @@ public final class Connection {
                  // callback was invoked. The C argument is a copy of the context pointer.
                  // The P and X arguments are pointers whose meanings depend on T.
                  (_: UInt32, context: UnsafeMutableRawPointer?, pointer: UnsafeMutableRawPointer?, _: UnsafeMutableRawPointer?) in
-                 if let pointer = pointer,
+                 if let pointer,
                     let expandedSQL = sqlite3_expanded_sql(OpaquePointer(pointer)) {
                      unsafeBitCast(context, to: Trace.self)(expandedSQL)
                      sqlite3_free(expandedSQL)
@@ -507,7 +507,7 @@ public final class Connection {
     ///   `.Insert`, `.Update`, or `.Delete`), database name, table name, and
     ///   rowid.
     public func updateHook(_ callback: ((_ operation: Operation, _ db: String, _ table: String, _ rowid: Int64) -> Void)?) {
-        guard let callback = callback else {
+        guard let callback else {
             sqlite3_update_hook(handle, nil, nil)
             updateHook = nil
             return
@@ -535,7 +535,7 @@ public final class Connection {
     ///   committed. If this callback throws, the transaction will be rolled
     ///   back.
     public func commitHook(_ callback: (() throws -> Void)?) {
-        guard let callback = callback else {
+        guard let callback else {
             sqlite3_commit_hook(handle, nil, nil)
             commitHook = nil
             return
@@ -562,7 +562,7 @@ public final class Connection {
     /// - Parameter callback: A callback invoked when a transaction is rolled
     ///   back.
     public func rollbackHook(_ callback: (() -> Void)?) {
-        guard let callback = callback else {
+        guard let callback else {
             sqlite3_rollback_hook(handle, nil, nil)
             rollbackHook = nil
             return
@@ -650,7 +650,7 @@ public final class Connection {
         try check(sqlite3_create_collation_v2(handle, collation, SQLITE_UTF8,
             unsafeBitCast(box, to: UnsafeMutableRawPointer.self), { (callback: UnsafeMutableRawPointer?, _,
                                                                      lhs: UnsafeRawPointer?, _, rhs: UnsafeRawPointer?) in /* xCompare */
-            if let lhs = lhs, let rhs = rhs {
+            if let lhs, let rhs {
                 return unsafeBitCast(callback, to: Collation.self)(lhs, rhs)
             } else {
                 fatalError("sqlite3_create_collation_v2 callback called with NULL pointer")
diff --git a/Sources/SQLite/Core/Result.swift b/Sources/SQLite/Core/Result.swift
index 9fe47ada..ee59e5d1 100644
--- a/Sources/SQLite/Core/Result.swift
+++ b/Sources/SQLite/Core/Result.swift
@@ -51,13 +51,13 @@ extension Result: CustomStringConvertible {
     public var description: String {
         switch self {
         case let .error(message, errorCode, statement):
-            if let statement = statement {
+            if let statement {
                 return "\(message) (\(statement)) (code: \(errorCode))"
             } else {
                 return "\(message) (code: \(errorCode))"
             }
         case let .extendedError(message, extendedCode, statement):
-            if let statement = statement {
+            if let statement {
                 return "\(message) (\(statement)) (extended code: \(extendedCode))"
             } else {
                 return "\(message) (extended code: \(extendedCode))"
diff --git a/Sources/SQLite/Extensions/FTS4.swift b/Sources/SQLite/Extensions/FTS4.swift
index 8e91e453..c02cfdc1 100644
--- a/Sources/SQLite/Extensions/FTS4.swift
+++ b/Sources/SQLite/Extensions/FTS4.swift
@@ -98,7 +98,7 @@ public struct Tokenizer {
                                  separators: Set<Character> = []) -> Tokenizer {
         var arguments = [String]()
 
-        if let removeDiacritics = removeDiacritics {
+        if let removeDiacritics {
             arguments.append("remove_diacritics=\(removeDiacritics ? 1 : 0)".quote())
         }
 
@@ -208,13 +208,13 @@ open class FTSConfig {
     func options() -> Options {
         var options = Options()
         options.append(formatColumnDefinitions())
-        if let tokenizer = tokenizer {
+        if let tokenizer {
             options.append("tokenize", value: Expression<Void>(literal: tokenizer.description))
         }
         options.appendCommaSeparated("prefix", values: prefixes.sorted().map { String($0) })
         if isContentless {
             options.append("content", value: "")
-        } else if let externalContentSchema = externalContentSchema {
+        } else if let externalContentSchema {
             options.append("content", value: externalContentSchema.tableName())
         }
         return options
@@ -306,19 +306,19 @@ open class FTS4Config: FTSConfig {
         for (column, _) in (columnDefinitions.filter { $0.options.contains(.unindexed) }) {
             options.append("notindexed", value: column)
         }
-        if let languageId = languageId {
+        if let languageId {
             options.append("languageid", value: languageId)
         }
-        if let compressFunction = compressFunction {
+        if let compressFunction {
             options.append("compress", value: compressFunction)
         }
-        if let uncompressFunction = uncompressFunction {
+        if let uncompressFunction {
             options.append("uncompress", value: uncompressFunction)
         }
-        if let matchInfo = matchInfo {
+        if let matchInfo {
             options.append("matchinfo", value: matchInfo.rawValue)
         }
-        if let order = order {
+        if let order {
             options.append("order", value: order.rawValue)
         }
         return options
diff --git a/Sources/SQLite/Extensions/FTS5.swift b/Sources/SQLite/Extensions/FTS5.swift
index 2e4f65fb..3e84e171 100644
--- a/Sources/SQLite/Extensions/FTS5.swift
+++ b/Sources/SQLite/Extensions/FTS5.swift
@@ -69,13 +69,13 @@ open class FTS5Config: FTSConfig {
 
     override func options() -> Options {
         var options = super.options()
-        if let contentRowId = contentRowId {
+        if let contentRowId {
             options.append("content_rowid", value: contentRowId)
         }
-        if let columnSize = columnSize {
+        if let columnSize {
             options.append("columnsize", value: Expression<Int>(value: columnSize))
         }
-        if let detail = detail {
+        if let detail {
             options.append("detail", value: detail.rawValue)
         }
         return options
diff --git a/Sources/SQLite/Helpers.swift b/Sources/SQLite/Helpers.swift
index 79d057d6..adfadc8a 100644
--- a/Sources/SQLite/Helpers.swift
+++ b/Sources/SQLite/Helpers.swift
@@ -109,7 +109,7 @@ extension String {
 }
 
 func transcode(_ literal: Binding?) -> String {
-    guard let literal = literal else { return "NULL" }
+    guard let literal else { return "NULL" }
 
     switch literal {
     case let blob as Blob:
diff --git a/Sources/SQLite/Schema/SchemaDefinitions.swift b/Sources/SQLite/Schema/SchemaDefinitions.swift
index b314e5c0..2d38e1fb 100644
--- a/Sources/SQLite/Schema/SchemaDefinitions.swift
+++ b/Sources/SQLite/Schema/SchemaDefinitions.swift
@@ -170,7 +170,7 @@ public enum LiteralValue: Equatable, CustomStringConvertible {
     // swiftlint:enable identifier_name
 
     init(_ string: String?) {
-        guard let string = string else {
+        guard let string else {
             self = .NULL
             return
         }
diff --git a/Sources/SQLite/Schema/SchemaReader.swift b/Sources/SQLite/Schema/SchemaReader.swift
index 0c02ba9f..58a9b1bd 100644
--- a/Sources/SQLite/Schema/SchemaReader.swift
+++ b/Sources/SQLite/Schema/SchemaReader.swift
@@ -39,10 +39,10 @@ public class SchemaReader {
                                   type: ObjectDefinition.ObjectType? = nil,
                                   temp: Bool = false) throws -> [ObjectDefinition] {
         var query: QueryType = SchemaTable.get(for: connection, temp: temp)
-        if let name = name {
+        if let name {
             query = query.where(SchemaTable.nameColumn == name)
         }
-        if let type = type {
+        if let type {
             query = query.where(SchemaTable.typeColumn == type.rawValue)
         }
         return try connection.prepare(query).map { row -> ObjectDefinition in
diff --git a/Sources/SQLite/Typed/Coding.swift b/Sources/SQLite/Typed/Coding.swift
index b96bc64e..e7db03fb 100644
--- a/Sources/SQLite/Typed/Coding.swift
+++ b/Sources/SQLite/Typed/Coding.swift
@@ -215,7 +215,7 @@ private class SQLiteEncoder: Encoder {
         }
 
         func encodeIfPresent(_ value: Int?, forKey key: SQLiteEncoder.SQLiteKeyedEncodingContainer<Key>.Key) throws {
-            if let value = value {
+            if let value {
                 try encode(value, forKey: key)
             } else if forcingNilValueSetters {
                 encoder.setters.append(Expression<Int?>(key.stringValue) <- nil)
@@ -223,7 +223,7 @@ private class SQLiteEncoder: Encoder {
         }
 
         func encodeIfPresent(_ value: Bool?, forKey key: Key) throws {
-            if let value = value {
+            if let value {
                 try encode(value, forKey: key)
             } else if forcingNilValueSetters {
                 encoder.setters.append(Expression<Bool?>(key.stringValue) <- nil)
@@ -231,7 +231,7 @@ private class SQLiteEncoder: Encoder {
         }
 
         func encodeIfPresent(_ value: Float?, forKey key: Key) throws {
-            if let value = value {
+            if let value {
                 try encode(value, forKey: key)
             } else if forcingNilValueSetters {
                 encoder.setters.append(Expression<Double?>(key.stringValue) <- nil)
@@ -239,7 +239,7 @@ private class SQLiteEncoder: Encoder {
         }
 
         func encodeIfPresent(_ value: Double?, forKey key: Key) throws {
-            if let value = value {
+            if let value {
                 try encode(value, forKey: key)
             } else if forcingNilValueSetters {
                 encoder.setters.append(Expression<Double?>(key.stringValue) <- nil)
@@ -247,7 +247,7 @@ private class SQLiteEncoder: Encoder {
         }
 
         func encodeIfPresent(_ value: String?, forKey key: MyKey) throws {
-            if let value = value {
+            if let value {
                 try encode(value, forKey: key)
             } else if forcingNilValueSetters {
                 encoder.setters.append(Expression<String?>(key.stringValue) <- nil)
@@ -270,7 +270,7 @@ private class SQLiteEncoder: Encoder {
         }
 
         func encodeIfPresent<T>(_ value: T?, forKey key: Key) throws where T: Swift.Encodable {
-            guard let value = value else {
+            guard let value else {
                 guard forcingNilValueSetters else {
                     return
                 }
diff --git a/Sources/SQLite/Typed/CoreFunctions.swift b/Sources/SQLite/Typed/CoreFunctions.swift
index dc6a1044..4429bb5f 100644
--- a/Sources/SQLite/Typed/CoreFunctions.swift
+++ b/Sources/SQLite/Typed/CoreFunctions.swift
@@ -100,7 +100,7 @@ extension ExpressionType where UnderlyingType == Double {
     ///
     /// - Returns: A copy of the expression wrapped with the `round` function.
     public func round(_ precision: Int? = nil) -> Expression<UnderlyingType> {
-        guard let precision = precision else {
+        guard let precision else {
             return Function.round.wrap([self])
         }
         return Function.round.wrap([self, Int(precision)])
@@ -120,7 +120,7 @@ extension ExpressionType where UnderlyingType == Double? {
     ///
     /// - Returns: A copy of the expression wrapped with the `round` function.
     public func round(_ precision: Int? = nil) -> Expression<UnderlyingType> {
-        guard let precision = precision else {
+        guard let precision else {
             return Function.round.wrap(self)
         }
         return Function.round.wrap([self, Int(precision)])
@@ -250,7 +250,7 @@ extension ExpressionType where UnderlyingType == String {
     /// - Returns: A copy of the expression appended with a `LIKE` query against
     ///   the given pattern.
     public func like(_ pattern: String, escape character: Character? = nil) -> Expression<Bool> {
-        guard let character = character else {
+        guard let character else {
             return "LIKE".infix(self, pattern)
         }
         return Expression("(\(template) LIKE ? ESCAPE ?)", bindings + [pattern, String(character)])
@@ -274,7 +274,7 @@ extension ExpressionType where UnderlyingType == String {
     /// - Returns: A copy of the expression appended with a `LIKE` query against
     ///   the given pattern.
     public func like(_ pattern: Expression<String>, escape character: Character? = nil) -> Expression<Bool> {
-        guard let character = character else {
+        guard let character else {
             return Function.like.infix(self, pattern)
         }
         let like: Expression<Bool> =  Function.like.infix(self, pattern, wrap: false)
@@ -349,7 +349,7 @@ extension ExpressionType where UnderlyingType == String {
     ///
     /// - Returns: A copy of the expression wrapped with the `ltrim` function.
     public func ltrim(_ characters: Set<Character>? = nil) -> Expression<UnderlyingType> {
-        guard let characters = characters else {
+        guard let characters else {
             return Function.ltrim.wrap(self)
         }
         return Function.ltrim.wrap([self, String(characters)])
@@ -367,7 +367,7 @@ extension ExpressionType where UnderlyingType == String {
     ///
     /// - Returns: A copy of the expression wrapped with the `rtrim` function.
     public func rtrim(_ characters: Set<Character>? = nil) -> Expression<UnderlyingType> {
-        guard let characters = characters else {
+        guard let characters else {
             return Function.rtrim.wrap(self)
         }
         return Function.rtrim.wrap([self, String(characters)])
@@ -385,7 +385,7 @@ extension ExpressionType where UnderlyingType == String {
     ///
     /// - Returns: A copy of the expression wrapped with the `trim` function.
     public func trim(_ characters: Set<Character>? = nil) -> Expression<UnderlyingType> {
-        guard let characters = characters else {
+        guard let characters else {
             return Function.trim.wrap([self])
         }
         return Function.trim.wrap([self, String(characters)])
@@ -409,7 +409,7 @@ extension ExpressionType where UnderlyingType == String {
     }
 
     public func substring(_ location: Int, length: Int? = nil) -> Expression<UnderlyingType> {
-        guard let length = length else {
+        guard let length else {
             return Function.substr.wrap([self, location])
         }
         return Function.substr.wrap([self, location, length])
@@ -475,7 +475,7 @@ extension ExpressionType where UnderlyingType == String? {
     /// - Returns: A copy of the expression appended with a `LIKE` query against
     ///   the given pattern.
     public func like(_ pattern: String, escape character: Character? = nil) -> Expression<Bool?> {
-        guard let character = character else {
+        guard let character else {
             return Function.like.infix(self, pattern)
         }
         return Expression("(\(template) LIKE ? ESCAPE ?)", bindings + [pattern, String(character)])
@@ -499,7 +499,7 @@ extension ExpressionType where UnderlyingType == String? {
     /// - Returns: A copy of the expression appended with a `LIKE` query against
     ///   the given pattern.
     public func like(_ pattern: Expression<String>, escape character: Character? = nil) -> Expression<Bool?> {
-        guard let character = character else {
+        guard let character else {
             return Function.like.infix(self, pattern)
         }
         let like: Expression<Bool> = Function.like.infix(self, pattern, wrap: false)
@@ -574,7 +574,7 @@ extension ExpressionType where UnderlyingType == String? {
     ///
     /// - Returns: A copy of the expression wrapped with the `ltrim` function.
     public func ltrim(_ characters: Set<Character>? = nil) -> Expression<UnderlyingType> {
-        guard let characters = characters else {
+        guard let characters else {
             return Function.ltrim.wrap(self)
         }
         return Function.ltrim.wrap([self, String(characters)])
@@ -592,7 +592,7 @@ extension ExpressionType where UnderlyingType == String? {
     ///
     /// - Returns: A copy of the expression wrapped with the `rtrim` function.
     public func rtrim(_ characters: Set<Character>? = nil) -> Expression<UnderlyingType> {
-        guard let characters = characters else {
+        guard let characters else {
             return Function.rtrim.wrap(self)
         }
         return Function.rtrim.wrap([self, String(characters)])
@@ -610,7 +610,7 @@ extension ExpressionType where UnderlyingType == String? {
     ///
     /// - Returns: A copy of the expression wrapped with the `trim` function.
     public func trim(_ characters: Set<Character>? = nil) -> Expression<UnderlyingType> {
-        guard let characters = characters else {
+        guard let characters else {
             return Function.trim.wrap(self)
         }
         return Function.trim.wrap([self, String(characters)])
@@ -649,7 +649,7 @@ extension ExpressionType where UnderlyingType == String? {
     ///
     /// - Returns: A copy of the expression wrapped with the `substr` function.
     public func substring(_ location: Int, length: Int? = nil) -> Expression<UnderlyingType> {
-        guard let length = length else {
+        guard let length else {
             return Function.substr.wrap([self, location])
         }
         return Function.substr.wrap([self, location, length])
@@ -726,7 +726,7 @@ extension String {
     /// - Returns: A copy of the expression appended with a `LIKE` query against
     ///   the given pattern.
     public func like(_ pattern: Expression<String>, escape character: Character? = nil) -> Expression<Bool> {
-        guard let character = character else {
+        guard let character else {
             return Function.like.infix(self, pattern)
         }
         let like: Expression<Bool> = Function.like.infix(self, pattern, wrap: false)
diff --git a/Sources/SQLite/Typed/Operators.swift b/Sources/SQLite/Typed/Operators.swift
index 5ffbbceb..1c611cbc 100644
--- a/Sources/SQLite/Typed/Operators.swift
+++ b/Sources/SQLite/Typed/Operators.swift
@@ -367,14 +367,14 @@ public func ==<V: Value>(lhs: Expression<V>, rhs: V) -> Expression<Bool> where V
     Operator.eq.infix(lhs, rhs)
 }
 public func ==<V: Value>(lhs: Expression<V?>, rhs: V?) -> Expression<Bool?> where V.Datatype: Equatable {
-    guard let rhs = rhs else { return "IS".infix(lhs, Expression<V?>(value: nil)) }
+    guard let rhs else { return "IS".infix(lhs, Expression<V?>(value: nil)) }
     return Operator.eq.infix(lhs, rhs)
 }
 public func ==<V: Value>(lhs: V, rhs: Expression<V>) -> Expression<Bool> where V.Datatype: Equatable {
     Operator.eq.infix(lhs, rhs)
 }
 public func ==<V: Value>(lhs: V?, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype: Equatable {
-    guard let lhs = lhs else { return "IS".infix(Expression<V?>(value: nil), rhs) }
+    guard let lhs else { return "IS".infix(Expression<V?>(value: nil), rhs) }
     return Operator.eq.infix(lhs, rhs)
 }
 
@@ -394,14 +394,14 @@ public func ===<V: Value>(lhs: Expression<V>, rhs: V) -> Expression<Bool> where
     "IS".infix(lhs, rhs)
 }
 public func ===<V: Value>(lhs: Expression<V?>, rhs: V?) -> Expression<Bool?> where V.Datatype: Equatable {
-    guard let rhs = rhs else { return "IS".infix(lhs, Expression<V?>(value: nil)) }
+    guard let rhs else { return "IS".infix(lhs, Expression<V?>(value: nil)) }
     return "IS".infix(lhs, rhs)
 }
 public func ===<V: Value>(lhs: V, rhs: Expression<V>) -> Expression<Bool> where V.Datatype: Equatable {
     "IS".infix(lhs, rhs)
 }
 public func ===<V: Value>(lhs: V?, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype: Equatable {
-    guard let lhs = lhs else { return "IS".infix(Expression<V?>(value: nil), rhs) }
+    guard let lhs else { return "IS".infix(Expression<V?>(value: nil), rhs) }
     return "IS".infix(lhs, rhs)
 }
 
@@ -421,14 +421,14 @@ public func !=<V: Value>(lhs: Expression<V>, rhs: V) -> Expression<Bool> where V
     Operator.neq.infix(lhs, rhs)
 }
 public func !=<V: Value>(lhs: Expression<V?>, rhs: V?) -> Expression<Bool?> where V.Datatype: Equatable {
-    guard let rhs = rhs else { return "IS NOT".infix(lhs, Expression<V?>(value: nil)) }
+    guard let rhs else { return "IS NOT".infix(lhs, Expression<V?>(value: nil)) }
     return Operator.neq.infix(lhs, rhs)
 }
 public func !=<V: Value>(lhs: V, rhs: Expression<V>) -> Expression<Bool> where V.Datatype: Equatable {
     Operator.neq.infix(lhs, rhs)
 }
 public func !=<V: Value>(lhs: V?, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype: Equatable {
-    guard let lhs = lhs else { return "IS NOT".infix(Expression<V?>(value: nil), rhs) }
+    guard let lhs else { return "IS NOT".infix(Expression<V?>(value: nil), rhs) }
     return Operator.neq.infix(lhs, rhs)
 }
 
@@ -448,14 +448,14 @@ public func !==<V: Value>(lhs: Expression<V>, rhs: V) -> Expression<Bool> where
     "IS NOT".infix(lhs, rhs)
 }
 public func !==<V: Value>(lhs: Expression<V?>, rhs: V?) -> Expression<Bool?> where V.Datatype: Equatable {
-    guard let rhs = rhs else { return "IS NOT".infix(lhs, Expression<V?>(value: nil)) }
+    guard let rhs else { return "IS NOT".infix(lhs, Expression<V?>(value: nil)) }
     return "IS NOT".infix(lhs, rhs)
 }
 public func !==<V: Value>(lhs: V, rhs: Expression<V>) -> Expression<Bool> where V.Datatype: Equatable {
     "IS NOT".infix(lhs, rhs)
 }
 public func !==<V: Value>(lhs: V?, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype: Equatable {
-    guard let lhs = lhs else { return "IS NOT".infix(Expression<V?>(value: nil), rhs) }
+    guard let lhs else { return "IS NOT".infix(Expression<V?>(value: nil), rhs) }
     return "IS NOT".infix(lhs, rhs)
 }
 

From 6aab9fcdc27c97f04b4153589908ec871b17ba55 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Mon, 22 May 2023 00:56:52 +0200
Subject: [PATCH 245/326] Re-enable watch tests

---
 run-tests.sh | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/run-tests.sh b/run-tests.sh
index c54300ee..49330c12 100755
--- a/run-tests.sh
+++ b/run-tests.sh
@@ -8,9 +8,9 @@ if [ -n "$BUILD_SCHEME" ]; then
     fi
 elif [ -n "$VALIDATOR_SUBSPEC" ]; then
     if [ "$VALIDATOR_SUBSPEC" == "none" ]; then
-      pod lib lint --no-subspecs --fail-fast --platforms=ios,osx,tvos
+      pod lib lint --no-subspecs --fail-fast
     else
-      pod lib lint --subspec="${VALIDATOR_SUBSPEC}" --fail-fast --platforms=ios,osx,tvos
+      pod lib lint --subspec="${VALIDATOR_SUBSPEC}" --fail-fast
     fi
 elif [ -n "$CARTHAGE_PLATFORM" ]; then
     cd Tests/Carthage && make test CARTHAGE_PLATFORM="$CARTHAGE_PLATFORM"

From ff93ebc831c01daa0117be6c19e0960347e001a6 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Sat, 27 May 2023 23:09:08 +0200
Subject: [PATCH 246/326] Handle FK definitions w/o key references

Closes #1199
---
 SQLite.xcodeproj/project.pbxproj              |  8 ++++
 Sources/SQLite/Schema/SchemaDefinitions.swift |  4 +-
 Sources/SQLite/Schema/SchemaReader.swift      |  2 +-
 .../Schema/SchemaReaderTests.swift            | 38 +++++++++++++++++++
 4 files changed, 49 insertions(+), 3 deletions(-)

diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj
index def32855..e6bb4fc9 100644
--- a/SQLite.xcodeproj/project.pbxproj
+++ b/SQLite.xcodeproj/project.pbxproj
@@ -211,6 +211,9 @@
 		DB7C5DA728D7C9B6006395CF /* SQLiteVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB7C5DA528D7C9B6006395CF /* SQLiteVersion.swift */; };
 		DB7C5DA828D7C9B6006395CF /* SQLiteVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB7C5DA528D7C9B6006395CF /* SQLiteVersion.swift */; };
 		DB7C5DA928D7C9B6006395CF /* SQLiteVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB7C5DA528D7C9B6006395CF /* SQLiteVersion.swift */; };
+		DBB93D5A2A22A373009BB96E /* SchemaReaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBB93D592A22A373009BB96E /* SchemaReaderTests.swift */; };
+		DBB93D5B2A22A373009BB96E /* SchemaReaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBB93D592A22A373009BB96E /* SchemaReaderTests.swift */; };
+		DBB93D5C2A22A373009BB96E /* SchemaReaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBB93D592A22A373009BB96E /* SchemaReaderTests.swift */; };
 		EE247AD71C3F04ED00AE3E12 /* SQLite.h in Headers */ = {isa = PBXBuildFile; fileRef = EE247AD61C3F04ED00AE3E12 /* SQLite.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		EE247ADE1C3F04ED00AE3E12 /* SQLite.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EE247AD31C3F04ED00AE3E12 /* SQLite.framework */; };
 		EE247B031C3F06E900AE3E12 /* Blob.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AEE1C3F06E900AE3E12 /* Blob.swift */; };
@@ -340,6 +343,7 @@
 		DB58B21028FB864300F8EEA4 /* SchemaReader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SchemaReader.swift; sourceTree = "<group>"; };
 		DB58B21528FC7C4600F8EEA4 /* SQLiteFeature.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SQLiteFeature.swift; sourceTree = "<group>"; };
 		DB7C5DA528D7C9B6006395CF /* SQLiteVersion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SQLiteVersion.swift; sourceTree = "<group>"; };
+		DBB93D592A22A373009BB96E /* SchemaReaderTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SchemaReaderTests.swift; sourceTree = "<group>"; };
 		EE247AD31C3F04ED00AE3E12 /* SQLite.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SQLite.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		EE247AD61C3F04ED00AE3E12 /* SQLite.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SQLite.h; sourceTree = "<group>"; };
 		EE247AD81C3F04ED00AE3E12 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@@ -483,6 +487,7 @@
 		19A17B56FBA20E7245BC8AC0 /* Schema */ = {
 			isa = PBXGroup;
 			children = (
+				DBB93D592A22A373009BB96E /* SchemaReaderTests.swift */,
 				19A176174862D0F0139B3987 /* SchemaDefinitionsTests.swift */,
 				19A17FDB0B0CFB8987906FD0 /* SchemaChangerTests.swift */,
 				19A17CA1DF7D0F7C9A94C51C /* Connection+SchemaTests.swift */,
@@ -998,6 +1003,7 @@
 				19A17FACE8E4D54A50BA934E /* FTS5Tests.swift in Sources */,
 				19A177909023B7B940C5805E /* FTSIntegrationTests.swift in Sources */,
 				19A17E1DD976D5CE80018749 /* FTS4Tests.swift in Sources */,
+				DBB93D5C2A22A373009BB96E /* SchemaReaderTests.swift in Sources */,
 				19A17411403D60640467209E /* ExpressionTests.swift in Sources */,
 				19A17CA4D7B63D845428A9C5 /* StatementTests.swift in Sources */,
 				19A17885B646CB0201BE4BD5 /* QueryTests.swift in Sources */,
@@ -1119,6 +1125,7 @@
 				19A178DA2BB5970778CCAF13 /* FTS5Tests.swift in Sources */,
 				19A1755C49154C87304C9146 /* FTSIntegrationTests.swift in Sources */,
 				19A17444861E1443143DEB44 /* FTS4Tests.swift in Sources */,
+				DBB93D5A2A22A373009BB96E /* SchemaReaderTests.swift in Sources */,
 				19A17DD33C2E43DD6EE05A60 /* ExpressionTests.swift in Sources */,
 				19A17D6EC40BC35A5DC81BA8 /* StatementTests.swift in Sources */,
 				19A17E3F47DA087E2B76D087 /* QueryTests.swift in Sources */,
@@ -1199,6 +1206,7 @@
 				19A1776BD5127DFDF847FF1F /* FTS5Tests.swift in Sources */,
 				19A173088B85A7E18E8582A7 /* FTSIntegrationTests.swift in Sources */,
 				19A178767223229E61C5066F /* FTS4Tests.swift in Sources */,
+				DBB93D5B2A22A373009BB96E /* SchemaReaderTests.swift in Sources */,
 				19A1781CBA8968ABD3E00877 /* ExpressionTests.swift in Sources */,
 				19A17923494236793893BF72 /* StatementTests.swift in Sources */,
 				19A17A52BF29D27C9AA229E7 /* QueryTests.swift in Sources */,
diff --git a/Sources/SQLite/Schema/SchemaDefinitions.swift b/Sources/SQLite/Schema/SchemaDefinitions.swift
index 2d38e1fb..9f4c126f 100644
--- a/Sources/SQLite/Schema/SchemaDefinitions.swift
+++ b/Sources/SQLite/Schema/SchemaDefinitions.swift
@@ -107,7 +107,7 @@ public struct ColumnDefinition: Equatable {
     public struct ForeignKey: Equatable {
         let table: String
         let column: String
-        let primaryKey: String
+        let primaryKey: String?
         let onUpdate: String?
         let onDelete: String?
     }
@@ -365,7 +365,7 @@ extension ColumnDefinition.ForeignKey {
         ([
             "REFERENCES",
             table.quote(),
-            "(\(primaryKey.quote()))",
+            primaryKey.map { "(\($0.quote()))" },
             onUpdate.map { "ON UPDATE \($0)" },
             onDelete.map { "ON DELETE \($0)" }
         ] as [String?]).compactMap { $0 }
diff --git a/Sources/SQLite/Schema/SchemaReader.swift b/Sources/SQLite/Schema/SchemaReader.swift
index 58a9b1bd..eb31888b 100644
--- a/Sources/SQLite/Schema/SchemaReader.swift
+++ b/Sources/SQLite/Schema/SchemaReader.swift
@@ -187,7 +187,7 @@ private enum ForeignKeyListTable {
     static let seqColumn = Expression<Int64>("seq")
     static let tableColumn = Expression<String>("table")
     static let fromColumn = Expression<String>("from")
-    static let toColumn = Expression<String>("to")
+    static let toColumn = Expression<String?>("to") // when null, use primary key
     static let onUpdateColumn = Expression<String>("on_update")
     static let onDeleteColumn = Expression<String>("on_delete")
     static let matchColumn = Expression<String>("match")
diff --git a/Tests/SQLiteTests/Schema/SchemaReaderTests.swift b/Tests/SQLiteTests/Schema/SchemaReaderTests.swift
index dd5ae103..e90578e7 100644
--- a/Tests/SQLiteTests/Schema/SchemaReaderTests.swift
+++ b/Tests/SQLiteTests/Schema/SchemaReaderTests.swift
@@ -135,6 +135,44 @@ class SchemaReaderTests: SQLiteTestCase {
         ])
     }
 
+    func test_foreignKeys_references_column() throws {
+        let sql = """
+          CREATE TABLE artist(
+            artistid    INTEGER PRIMARY KEY,
+            artistname  TEXT
+          );
+          CREATE TABLE track(
+            trackid     INTEGER,
+            trackname   TEXT,
+            trackartist INTEGER REFERENCES artist(artistid)
+          );
+          """
+        try db.execute(sql)
+        let trackColumns = try db.schema.foreignKeys(table: "track")
+        XCTAssertEqual(trackColumns.map { $0.toSQL() }.joined(separator: "\n"), """
+            REFERENCES "artist" ("artistid")
+            """)
+    }
+
+    func test_foreignKeys_references_null_column() throws {
+        let sql = """
+          CREATE TABLE artist(
+            artistid    INTEGER PRIMARY KEY,
+            artistname  TEXT
+          );
+          CREATE TABLE track(
+            trackid     INTEGER,
+            trackname   TEXT,
+            trackartist INTEGER REFERENCES artist
+          );
+          """
+        try db.execute(sql)
+        let trackColumns = try db.schema.foreignKeys(table: "track")
+        XCTAssertEqual(trackColumns.map { $0.toSQL() }.joined(separator: "\n"), """
+            REFERENCES "artist"
+            """)
+    }
+
     func test_tableDefinitions() throws {
         let tables = try schemaReader.tableDefinitions()
         XCTAssertEqual(tables.count, 1)

From 7997c85cbf6c0a1f8caf300b483f04028548e40e Mon Sep 17 00:00:00 2001
From: Stefan Saasen <stefan@ohoj-software.com>
Date: Wed, 7 Jun 2023 15:45:35 +0200
Subject: [PATCH 247/326] SchemaReader: return the correct column definition
 for a composite primary key

The `PRAGMA` `table_info` that is used to return the column definitions,
returns one row for each defined column. The `pk` column contains:

> ... either zero for columns that are not part of the primary key, or the 1-based index of the column within the primary key).

See https://www.sqlite.org/pragma.html#pragma_table_info

Checking whether the `pk` column equals 1 only detects a single primary
key and ignores other columns that are part of a composite primary key.
---
 Sources/SQLite/Schema/SchemaReader.swift      |  2 +-
 .../Schema/SchemaReaderTests.swift            | 37 +++++++++++++++++++
 2 files changed, 38 insertions(+), 1 deletion(-)

diff --git a/Sources/SQLite/Schema/SchemaReader.swift b/Sources/SQLite/Schema/SchemaReader.swift
index 58a9b1bd..2be15f29 100644
--- a/Sources/SQLite/Schema/SchemaReader.swift
+++ b/Sources/SQLite/Schema/SchemaReader.swift
@@ -25,7 +25,7 @@ public class SchemaReader {
             .map { (row: Row) -> ColumnDefinition in
                 ColumnDefinition(
                     name: row[TableInfoTable.nameColumn],
-                    primaryKey: row[TableInfoTable.primaryKeyColumn] == 1 ?
+                    primaryKey: (row[TableInfoTable.primaryKeyColumn] ?? 0) > 0 ?
                         try parsePrimaryKey(column: row[TableInfoTable.nameColumn]) : nil,
                     type: ColumnDefinition.Affinity(row[TableInfoTable.typeColumn]),
                     nullable: row[TableInfoTable.notNullColumn] == 0,
diff --git a/Tests/SQLiteTests/Schema/SchemaReaderTests.swift b/Tests/SQLiteTests/Schema/SchemaReaderTests.swift
index dd5ae103..49871c10 100644
--- a/Tests/SQLiteTests/Schema/SchemaReaderTests.swift
+++ b/Tests/SQLiteTests/Schema/SchemaReaderTests.swift
@@ -90,6 +90,43 @@ class SchemaReaderTests: SQLiteTestCase {
         )
     }
 
+    func test_columnDefinitions_composite_primary_keys() throws {
+        try db.run("""
+        CREATE TABLE t (
+          col1 INTEGER,
+          col2 INTEGER,
+          col3 INTEGER,
+          PRIMARY KEY (col1, col2)
+        );
+        """)
+
+        XCTAssertEqual(
+            try schemaReader.columnDefinitions(table: "t"), [
+            ColumnDefinition(
+                    name: "col1",
+                    primaryKey: .init(autoIncrement: false),
+                    type: .INTEGER,
+                    nullable: true,
+                    defaultValue: .NULL,
+                    references: nil),
+            ColumnDefinition(
+                    name: "col2",
+                    primaryKey: .init(autoIncrement: false),
+                    type: .INTEGER,
+                    nullable: true,
+                    defaultValue: .NULL,
+                    references: nil),
+            ColumnDefinition(
+                    name: "col3",
+                    primaryKey: nil,
+                    type: .INTEGER,
+                    nullable: true,
+                    defaultValue: .NULL,
+                    references: nil)
+            ]
+        )
+    }
+
     func test_indexDefinitions_no_index() throws {
         let indexes = try schemaReader.indexDefinitions(table: "users")
         XCTAssertTrue(indexes.isEmpty)

From 656ca871e357df8ef45b7344c777102cdbf7dfc6 Mon Sep 17 00:00:00 2001
From: Stefan Saasen <stefan@ohoj-software.com>
Date: Tue, 30 May 2023 16:20:29 +0200
Subject: [PATCH 248/326] Fix column affinity parsing to match how SQLite
 determines affinity

See https://www.sqlite.org/datatype3.html#determination_of_column_affinity
for how SQLite determines column affinity.
---
 Sources/SQLite/Schema/SchemaDefinitions.swift | 14 +++-
 .../Schema/SchemaDefinitionsTests.swift       | 64 ++++++++++++++++++-
 .../Schema/SchemaReaderTests.swift            |  4 +-
 3 files changed, 77 insertions(+), 5 deletions(-)

diff --git a/Sources/SQLite/Schema/SchemaDefinitions.swift b/Sources/SQLite/Schema/SchemaDefinitions.swift
index 2d38e1fb..3c6b0523 100644
--- a/Sources/SQLite/Schema/SchemaDefinitions.swift
+++ b/Sources/SQLite/Schema/SchemaDefinitions.swift
@@ -57,7 +57,19 @@ public struct ColumnDefinition: Equatable {
         }
 
         init(_ string: String) {
-            self = Affinity.allCases.first { $0.rawValue.lowercased() == string.lowercased() } ?? .TEXT
+            let test = string.uppercased()
+            // https://sqlite.org/datatype3.html#determination_of_column_affinity
+            if test.contains("INT") { // Rule 1
+                self = .INTEGER
+            } else if ["CHAR", "CLOB", "TEXT"].first(where: {test.contains($0)}) != nil { // Rule 2
+                self = .TEXT
+            } else if string.contains("BLOB") { // Rule 3
+                self = .BLOB
+            } else if ["REAL", "FLOA", "DOUB"].first(where: {test.contains($0)}) != nil { // Rule 4
+                self = .REAL
+            } else { // Rule 5
+                self = .NUMERIC
+            }
         }
     }
 
diff --git a/Tests/SQLiteTests/Schema/SchemaDefinitionsTests.swift b/Tests/SQLiteTests/Schema/SchemaDefinitionsTests.swift
index ef97b981..8b7e27e3 100644
--- a/Tests/SQLiteTests/Schema/SchemaDefinitionsTests.swift
+++ b/Tests/SQLiteTests/Schema/SchemaDefinitionsTests.swift
@@ -71,8 +71,68 @@ class AffinityTests: XCTestCase {
         XCTAssertEqual(ColumnDefinition.Affinity("NUMERIC"), .NUMERIC)
     }
 
-    func test_returns_TEXT_for_unknown_type() {
-        XCTAssertEqual(ColumnDefinition.Affinity("baz"), .TEXT)
+    // [Determination Of Column Affinity](https://sqlite.org/datatype3.html#determination_of_column_affinity)
+    // Rule 1
+    func testIntegerAffinity() {
+        let declared = [
+            "INT",
+            "INTEGER",
+            "TINYINT",
+            "SMALLINT",
+            "MEDIUMINT",
+            "BIGINT",
+            "UNSIGNED BIG INT",
+            "INT2",
+            "INT8"
+        ]
+        XCTAssertTrue(declared.allSatisfy({ColumnDefinition.Affinity($0) == .INTEGER}))
+    }
+
+    // Rule 2
+    func testTextAffinity() {
+        let declared = [
+            "CHARACTER(20)",
+            "VARCHAR(255)",
+            "VARYING CHARACTER(255)",
+            "NCHAR(55)",
+            "NATIVE CHARACTER(70)",
+            "NVARCHAR(100)",
+            "TEXT",
+            "CLOB"
+        ]
+        XCTAssertTrue(declared.allSatisfy({ColumnDefinition.Affinity($0) == .TEXT}))
+    }
+
+    // Rule 3
+    func testBlobAffinity() {
+        XCTAssertEqual(ColumnDefinition.Affinity("BLOB"), .BLOB)
+    }
+
+    // Rule 4
+    func testRealAffinity() {
+        let declared = [
+            "REAL",
+            "DOUBLE",
+            "DOUBLE PRECISION",
+            "FLOAT"
+        ]
+        XCTAssertTrue(declared.allSatisfy({ColumnDefinition.Affinity($0) == .REAL}))
+    }
+
+    // Rule 5
+    func testNumericAffinity() {
+        let declared = [
+            "NUMERIC",
+            "DECIMAL(10,5)",
+            "BOOLEAN",
+            "DATE",
+            "DATETIME"
+        ]
+        XCTAssertTrue(declared.allSatisfy({ColumnDefinition.Affinity($0) == .NUMERIC}))
+    }
+
+    func test_returns_NUMERIC_for_unknown_type() {
+        XCTAssertEqual(ColumnDefinition.Affinity("baz"), .NUMERIC)
     }
 }
 
diff --git a/Tests/SQLiteTests/Schema/SchemaReaderTests.swift b/Tests/SQLiteTests/Schema/SchemaReaderTests.swift
index dd5ae103..b03045de 100644
--- a/Tests/SQLiteTests/Schema/SchemaReaderTests.swift
+++ b/Tests/SQLiteTests/Schema/SchemaReaderTests.swift
@@ -40,7 +40,7 @@ class SchemaReaderTests: SQLiteTestCase {
                              references: nil),
             ColumnDefinition(name: "admin",
                              primaryKey: nil,
-                             type: .TEXT,
+                             type: .NUMERIC,
                              nullable: false,
                              defaultValue: .numericLiteral("0"),
                              references: nil),
@@ -51,7 +51,7 @@ class SchemaReaderTests: SQLiteTestCase {
                              references: .init(table: "users", column: "manager_id", primaryKey: "id", onUpdate: nil, onDelete: nil)),
             ColumnDefinition(name: "created_at",
                              primaryKey: nil,
-                             type: .TEXT,
+                             type: .NUMERIC,
                              nullable: true,
                              defaultValue: .NULL,
                              references: nil)

From 3023a1f63336f70c1470bdef59ac2dfded258206 Mon Sep 17 00:00:00 2001
From: Jacob Hearst <jacob.hearst@jamf.com>
Date: Mon, 16 Oct 2023 07:33:54 -0500
Subject: [PATCH 249/326] Add optional support for decoding

---
 Sources/SQLite/Typed/Coding.swift | 48 +++++++++++++++++++++++++++++++
 1 file changed, 48 insertions(+)

diff --git a/Sources/SQLite/Typed/Coding.swift b/Sources/SQLite/Typed/Coding.swift
index e7db03fb..8c7755c3 100644
--- a/Sources/SQLite/Typed/Coding.swift
+++ b/Sources/SQLite/Typed/Coding.swift
@@ -471,6 +471,54 @@ private class SQLiteDecoder: Decoder {
             }
         }
 
+        func decodeIfPresent(_ type: Bool.Type, forKey key: Key) throws -> Bool? {
+            try? row.get(Expression(key.stringValue))
+        }
+
+        func decodeIfPresent(_ type: Int.Type, forKey key: Key) throws -> Int? {
+            try? row.get(Expression(key.stringValue))
+        }
+
+        func decodeIfPresent(_ type: Int64.Type, forKey key: Key) throws -> Int64? {
+            try? row.get(Expression(key.stringValue))
+        }
+
+        func decodeIfPresent(_ type: Float.Type, forKey key: Key) throws -> Float? {
+            try? Float(row.get(Expression<Double>(key.stringValue)))
+        }
+
+        func decodeIfPresent(_ type: Double.Type, forKey key: Key) throws -> Double? {
+            try? row.get(Expression(key.stringValue))
+        }
+
+        func decodeIfPresent(_ type: String.Type, forKey key: Key) throws -> String? {
+            try? row.get(Expression(key.stringValue))
+        }
+
+        func decode<T>(_ type: T.Type, forKey key: Key) throws -> T? where T: Swift.Decodable {
+            switch type {
+            case is Data.Type:
+                let data = try row.get(Expression<Data>(key.stringValue))
+                return data as? T
+            case is Date.Type:
+                let date = try row.get(Expression<Date>(key.stringValue))
+                return date as? T
+            case is UUID.Type:
+                let uuid = try row.get(Expression<UUID>(key.stringValue))
+                return uuid as? T
+            default:
+                guard let JSONString = try row.get(Expression<String?>(key.stringValue)) else {
+                    throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: codingPath,
+                                                                                 debugDescription: "an unsupported type was found"))
+                }
+                guard let data = JSONString.data(using: .utf8) else {
+                    throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: codingPath,
+                                                                            debugDescription: "invalid utf8 data found"))
+                }
+                return try JSONDecoder().decode(type, from: data)
+            }
+        }
+
         func nestedContainer<NestedKey>(keyedBy type: NestedKey.Type, forKey key: Key) throws
         -> KeyedDecodingContainer<NestedKey> where NestedKey: CodingKey {
             throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: codingPath,

From e7c259226373e9469d9166b842e5395dae302b6e Mon Sep 17 00:00:00 2001
From: Jacob Hearst <jacob.hearst@jamf.com>
Date: Thu, 19 Oct 2023 07:13:41 -0500
Subject: [PATCH 250/326] Fix copypasta

---
 Sources/SQLite/Typed/Coding.swift | 19 +++++++++++--------
 1 file changed, 11 insertions(+), 8 deletions(-)

diff --git a/Sources/SQLite/Typed/Coding.swift b/Sources/SQLite/Typed/Coding.swift
index 8c7755c3..f62f5b6b 100644
--- a/Sources/SQLite/Typed/Coding.swift
+++ b/Sources/SQLite/Typed/Coding.swift
@@ -495,19 +495,22 @@ private class SQLiteDecoder: Decoder {
             try? row.get(Expression(key.stringValue))
         }
 
-        func decode<T>(_ type: T.Type, forKey key: Key) throws -> T? where T: Swift.Decodable {
+        func decodeIfPresent<T>(_ type: T.Type, forKey key: Key) throws -> T? where T: Swift.Decodable {
             switch type {
             case is Data.Type:
-                let data = try row.get(Expression<Data>(key.stringValue))
-                return data as? T
+                if let data = try? row.get(Expression<Data>(key.stringValue)) {
+                    return data as? T
+                }
             case is Date.Type:
-                let date = try row.get(Expression<Date>(key.stringValue))
-                return date as? T
+                if let date = try? row.get(Expression<Date>(key.stringValue)) {
+                    return date as? T
+                }
             case is UUID.Type:
-                let uuid = try row.get(Expression<UUID>(key.stringValue))
-                return uuid as? T
+                if let uuid = try? row.get(Expression<UUID>(key.stringValue)) {
+                    return uuid as? T
+                }
             default:
-                guard let JSONString = try row.get(Expression<String?>(key.stringValue)) else {
+                guard let JSONString = try? row.get(Expression<String?>(key.stringValue)) else {
                     throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: codingPath,
                                                                                  debugDescription: "an unsupported type was found"))
                 }

From 128246f30a396ef864318e3a7ca851e90b3e84a1 Mon Sep 17 00:00:00 2001
From: Jacob Hearst <jacob.hearst@jamf.com>
Date: Thu, 19 Oct 2023 11:04:58 -0500
Subject: [PATCH 251/326] Fix missing return

---
 Sources/SQLite/Typed/Coding.swift | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/Sources/SQLite/Typed/Coding.swift b/Sources/SQLite/Typed/Coding.swift
index f62f5b6b..52612063 100644
--- a/Sources/SQLite/Typed/Coding.swift
+++ b/Sources/SQLite/Typed/Coding.swift
@@ -520,6 +520,8 @@ private class SQLiteDecoder: Decoder {
                 }
                 return try JSONDecoder().decode(type, from: data)
             }
+
+            return nil
         }
 
         func nestedContainer<NestedKey>(keyedBy type: NestedKey.Type, forKey key: Key) throws

From 1c6bf76948de5b6ab1ef7a45629788e3c5c70d0a Mon Sep 17 00:00:00 2001
From: Jacob Hearst <jacob.hearst@jamf.com>
Date: Fri, 20 Oct 2023 07:20:58 -0500
Subject: [PATCH 252/326] Fix smoothbrain

---
 Sources/SQLite/Typed/Coding.swift | 14 +++-----------
 1 file changed, 3 insertions(+), 11 deletions(-)

diff --git a/Sources/SQLite/Typed/Coding.swift b/Sources/SQLite/Typed/Coding.swift
index 52612063..bfddc5ee 100644
--- a/Sources/SQLite/Typed/Coding.swift
+++ b/Sources/SQLite/Typed/Coding.swift
@@ -498,17 +498,11 @@ private class SQLiteDecoder: Decoder {
         func decodeIfPresent<T>(_ type: T.Type, forKey key: Key) throws -> T? where T: Swift.Decodable {
             switch type {
             case is Data.Type:
-                if let data = try? row.get(Expression<Data>(key.stringValue)) {
-                    return data as? T
-                }
+                return try? row.get(Expression<Data>(key.stringValue)) as? T
             case is Date.Type:
-                if let date = try? row.get(Expression<Date>(key.stringValue)) {
-                    return date as? T
-                }
+                return try? row.get(Expression<Date>(key.stringValue)) as? T
             case is UUID.Type:
-                if let uuid = try? row.get(Expression<UUID>(key.stringValue)) {
-                    return uuid as? T
-                }
+                return try? row.get(Expression<UUID>(key.stringValue)) as? T
             default:
                 guard let JSONString = try? row.get(Expression<String?>(key.stringValue)) else {
                     throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: codingPath,
@@ -520,8 +514,6 @@ private class SQLiteDecoder: Decoder {
                 }
                 return try JSONDecoder().decode(type, from: data)
             }
-
-            return nil
         }
 
         func nestedContainer<NestedKey>(keyedBy type: NestedKey.Type, forKey key: Key) throws

From fc96d30b72db986b7f930d951eca14c16e32b9b5 Mon Sep 17 00:00:00 2001
From: Geoff MacDonald <geoffmacd@gmail.com>
Date: Sat, 18 Nov 2023 18:30:28 -0800
Subject: [PATCH 253/326] Implements built-in window functions

---
 SQLite.xcodeproj/project.pbxproj              |  18 +++
 Sources/SQLite/Typed/AggregateFunctions.swift |   6 +-
 Sources/SQLite/Typed/WindowFunctions.swift    | 145 ++++++++++++++++++
 .../Typed/QueryIntegrationTests.swift         |  95 ++++++++++++
 .../Typed/WindowFunctionsTests.swift          |  58 +++++++
 5 files changed, 319 insertions(+), 3 deletions(-)
 create mode 100644 Sources/SQLite/Typed/WindowFunctions.swift
 create mode 100644 Tests/SQLiteTests/Typed/WindowFunctionsTests.swift

diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj
index def32855..95ed6806 100644
--- a/SQLite.xcodeproj/project.pbxproj
+++ b/SQLite.xcodeproj/project.pbxproj
@@ -195,6 +195,13 @@
 		49EB68C51F7B3CB400D89D40 /* Coding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49EB68C31F7B3CB400D89D40 /* Coding.swift */; };
 		49EB68C61F7B3CB400D89D40 /* Coding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49EB68C31F7B3CB400D89D40 /* Coding.swift */; };
 		49EB68C71F7B3CB400D89D40 /* Coding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49EB68C31F7B3CB400D89D40 /* Coding.swift */; };
+		64A8EE432B095FBB00F583F7 /* WindowFunctions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64A8EE422B095FBB00F583F7 /* WindowFunctions.swift */; };
+		64A8EE442B095FBB00F583F7 /* WindowFunctions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64A8EE422B095FBB00F583F7 /* WindowFunctions.swift */; };
+		64A8EE452B095FBB00F583F7 /* WindowFunctions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64A8EE422B095FBB00F583F7 /* WindowFunctions.swift */; };
+		64A8EE462B095FBB00F583F7 /* WindowFunctions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64A8EE422B095FBB00F583F7 /* WindowFunctions.swift */; };
+		64B8E1702B09748000545AFB /* WindowFunctionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64B8E16F2B09748000545AFB /* WindowFunctionsTests.swift */; };
+		64B8E1712B09748000545AFB /* WindowFunctionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64B8E16F2B09748000545AFB /* WindowFunctionsTests.swift */; };
+		64B8E1722B09748000545AFB /* WindowFunctionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64B8E16F2B09748000545AFB /* WindowFunctionsTests.swift */; };
 		997DF2AE287FC06D00F8DF95 /* Query+with.swift in Sources */ = {isa = PBXBuildFile; fileRef = 997DF2AD287FC06D00F8DF95 /* Query+with.swift */; };
 		997DF2AF287FC06D00F8DF95 /* Query+with.swift in Sources */ = {isa = PBXBuildFile; fileRef = 997DF2AD287FC06D00F8DF95 /* Query+with.swift */; };
 		997DF2B0287FC06D00F8DF95 /* Query+with.swift in Sources */ = {isa = PBXBuildFile; fileRef = 997DF2AD287FC06D00F8DF95 /* Query+with.swift */; };
@@ -335,6 +342,8 @@
 		3DF7B79B2884C901005DD8CA /* Planning.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = Planning.md; sourceTree = "<group>"; };
 		3DFC0B862886C239001C8FC9 /* Package.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Package.swift; sourceTree = "<group>"; };
 		49EB68C31F7B3CB400D89D40 /* Coding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Coding.swift; sourceTree = "<group>"; };
+		64A8EE422B095FBB00F583F7 /* WindowFunctions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowFunctions.swift; sourceTree = "<group>"; };
+		64B8E16F2B09748000545AFB /* WindowFunctionsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowFunctionsTests.swift; sourceTree = "<group>"; };
 		997DF2AD287FC06D00F8DF95 /* Query+with.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Query+with.swift"; sourceTree = "<group>"; };
 		A121AC451CA35C79005A31D1 /* SQLite.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SQLite.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		DB58B21028FB864300F8EEA4 /* SchemaReader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SchemaReader.swift; sourceTree = "<group>"; };
@@ -476,6 +485,7 @@
 				19A177EF5E2D91BA86DA4480 /* CustomAggregationTests.swift */,
 				19A1709D5BDD2691BA160012 /* SetterTests.swift */,
 				19A174FE5B47A97937A27276 /* RowTests.swift */,
+				64B8E16F2B09748000545AFB /* WindowFunctionsTests.swift */,
 			);
 			path = Typed;
 			sourceTree = "<group>";
@@ -607,6 +617,7 @@
 			isa = PBXGroup;
 			children = (
 				EE247AFA1C3F06E900AE3E12 /* AggregateFunctions.swift */,
+				64A8EE422B095FBB00F583F7 /* WindowFunctions.swift */,
 				EE247AFB1C3F06E900AE3E12 /* Collation.swift */,
 				EE247AFC1C3F06E900AE3E12 /* CoreFunctions.swift */,
 				EE247AFD1C3F06E900AE3E12 /* CustomFunctions.swift */,
@@ -960,6 +971,7 @@
 				3DF7B78A28842972005DD8CA /* Connection+Attach.swift in Sources */,
 				03A65E811C6BB2FB0062603F /* CustomFunctions.swift in Sources */,
 				03A65E7A1C6BB2F70062603F /* Statement.swift in Sources */,
+				64A8EE452B095FBB00F583F7 /* WindowFunctions.swift in Sources */,
 				03A65E741C6BB2DA0062603F /* Helpers.swift in Sources */,
 				03A65E831C6BB2FB0062603F /* Operators.swift in Sources */,
 				03A65E851C6BB2FB0062603F /* Schema.swift in Sources */,
@@ -1014,6 +1026,7 @@
 				19A17746150A815944A6820B /* SelectTests.swift in Sources */,
 				19A1766135CE9786B1878603 /* ValueTests.swift in Sources */,
 				19A177D5C6542E2D572162E5 /* QueryIntegrationTests.swift in Sources */,
+				64B8E1722B09748000545AFB /* WindowFunctionsTests.swift in Sources */,
 				19A178DF5A96CFEFF1E271F6 /* AggregateFunctionsTests.swift in Sources */,
 				19A17437659BD7FD787D94A6 /* CustomAggregationTests.swift in Sources */,
 				19A17F907258E524B3CA2FAE /* SetterTests.swift in Sources */,
@@ -1057,6 +1070,7 @@
 				19A17DC282E36C4F41AA440B /* Errors.swift in Sources */,
 				19A173668D948AD4DF1F5352 /* DateAndTimeFunctions.swift in Sources */,
 				19A17DF8D4F13A20F5D2269E /* Result.swift in Sources */,
+				64A8EE462B095FBB00F583F7 /* WindowFunctions.swift in Sources */,
 				19A17DFE05ED8B1F7C45F7EE /* SchemaChanger.swift in Sources */,
 				19A17D1BEABA610ABF003D67 /* SchemaDefinitions.swift in Sources */,
 				19A17A33EA026C2E2CEBAF36 /* Connection+Schema.swift in Sources */,
@@ -1081,6 +1095,7 @@
 				EE247B151C3F06E900AE3E12 /* Setter.swift in Sources */,
 				3DF7B78828842972005DD8CA /* Connection+Attach.swift in Sources */,
 				EE247B101C3F06E900AE3E12 /* CustomFunctions.swift in Sources */,
+				64A8EE432B095FBB00F583F7 /* WindowFunctions.swift in Sources */,
 				EE247B091C3F06E900AE3E12 /* FTS4.swift in Sources */,
 				EE247B081C3F06E900AE3E12 /* Value.swift in Sources */,
 				EE247B121C3F06E900AE3E12 /* Operators.swift in Sources */,
@@ -1135,6 +1150,7 @@
 				19A17F7977364EC8CD33C3C3 /* SelectTests.swift in Sources */,
 				19A17FD22EF43DF428DD93BA /* ValueTests.swift in Sources */,
 				19A177AA5922527BBDC77CF9 /* QueryIntegrationTests.swift in Sources */,
+				64B8E1702B09748000545AFB /* WindowFunctionsTests.swift in Sources */,
 				19A179786A6826D58A70F8BC /* AggregateFunctionsTests.swift in Sources */,
 				19A1793972BDDDB027C113BB /* CustomAggregationTests.swift in Sources */,
 				19A1773155AC2BF2CA86A473 /* SetterTests.swift in Sources */,
@@ -1161,6 +1177,7 @@
 				3DF7B78928842972005DD8CA /* Connection+Attach.swift in Sources */,
 				EE247B701C3F3FEC00AE3E12 /* CustomFunctions.swift in Sources */,
 				EE247B691C3F3FEC00AE3E12 /* Statement.swift in Sources */,
+				64A8EE442B095FBB00F583F7 /* WindowFunctions.swift in Sources */,
 				EE247B641C3F3FDB00AE3E12 /* Helpers.swift in Sources */,
 				EE247B721C3F3FEC00AE3E12 /* Operators.swift in Sources */,
 				EE247B741C3F3FEC00AE3E12 /* Schema.swift in Sources */,
@@ -1215,6 +1232,7 @@
 				19A17DE1FCDB5695702AD24D /* SelectTests.swift in Sources */,
 				19A1726002D24C14F876C8FE /* ValueTests.swift in Sources */,
 				19A173389E53CB24DFA8CEDD /* QueryIntegrationTests.swift in Sources */,
+				64B8E1712B09748000545AFB /* WindowFunctionsTests.swift in Sources */,
 				19A170C56745F9D722A73D77 /* AggregateFunctionsTests.swift in Sources */,
 				19A1772EBE65173EDFB1AFCA /* CustomAggregationTests.swift in Sources */,
 				19A17E0ABA6C415F014CD51C /* SetterTests.swift in Sources */,
diff --git a/Sources/SQLite/Typed/AggregateFunctions.swift b/Sources/SQLite/Typed/AggregateFunctions.swift
index bf4fb8fc..17fc4a20 100644
--- a/Sources/SQLite/Typed/AggregateFunctions.swift
+++ b/Sources/SQLite/Typed/AggregateFunctions.swift
@@ -166,7 +166,7 @@ extension ExpressionType where UnderlyingType: Value, UnderlyingType.Datatype: N
     ///     salary.average
     ///     // avg("salary")
     ///
-    /// - Returns: A copy of the expression wrapped with the `min` aggregate
+    /// - Returns: A copy of the expression wrapped with the `avg` aggregate
     ///   function.
     public var average: Expression<Double?> {
         Function.avg.wrap(self)
@@ -179,7 +179,7 @@ extension ExpressionType where UnderlyingType: Value, UnderlyingType.Datatype: N
     ///     salary.sum
     ///     // sum("salary")
     ///
-    /// - Returns: A copy of the expression wrapped with the `min` aggregate
+    /// - Returns: A copy of the expression wrapped with the `sum` aggregate
     ///   function.
     public var sum: Expression<UnderlyingType?> {
         Function.sum.wrap(self)
@@ -192,7 +192,7 @@ extension ExpressionType where UnderlyingType: Value, UnderlyingType.Datatype: N
     ///     salary.total
     ///     // total("salary")
     ///
-    /// - Returns: A copy of the expression wrapped with the `min` aggregate
+    /// - Returns: A copy of the expression wrapped with the `total` aggregate
     ///   function.
     public var total: Expression<Double> {
         Function.total.wrap(self)
diff --git a/Sources/SQLite/Typed/WindowFunctions.swift b/Sources/SQLite/Typed/WindowFunctions.swift
new file mode 100644
index 00000000..e71e1ada
--- /dev/null
+++ b/Sources/SQLite/Typed/WindowFunctions.swift
@@ -0,0 +1,145 @@
+import Foundation
+
+// see https://www.sqlite.org/windowfunctions.html#builtins
+private enum WindowFunction: String {
+    // swiftlint:disable identifier_name
+    case ntile
+    case row_number
+    case rank
+    case dense_rank
+    case percent_rank
+    case cume_dist
+    case lag
+    case lead
+    case first_value
+    case last_value
+    case nth_value
+    // swiftlint:enable identifier_name
+
+    func wrap<T>(_ value: Int? = nil) -> Expression<T> {
+        if let value {
+            return self.rawValue.wrap(Expression(value: value))
+        }
+        return Expression(literal: "\(rawValue)()")
+    }
+
+    func over<T>(value: Int? = nil, _ orderBy: Expressible) -> Expression<T> {
+        return Expression<T>(" ".join([
+            self.wrap(value),
+            Expression<T>("OVER (ORDER BY \(orderBy.expression.template))", orderBy.expression.bindings)
+        ]).expression)
+    }
+
+    func over<T>(valueExpr: Expressible, _ orderBy: Expressible) -> Expression<T> {
+        return Expression<T>(" ".join([
+            self.rawValue.wrap(valueExpr),
+            Expression<T>("OVER (ORDER BY \(orderBy.expression.template))", orderBy.expression.bindings)
+        ]).expression)
+    }
+}
+
+extension ExpressionType where UnderlyingType: Value {
+    /// Builds a copy of the expression with `lag(self, offset, default) OVER (ORDER BY {orderBy})` window function
+    ///
+    /// - Parameter orderBy: Expression to evaluate window order
+    /// - Returns: An expression returning `lag(self, offset, default) OVER (ORDER BY {orderBy})` window function
+    public func lag(offset: Int = 0, default: Expressible? = nil, _ orderBy: Expressible) -> Expression<UnderlyingType> {
+        if let defaultExpression = `default` {
+            return Expression(
+                "lag(\(template), \(offset), \(defaultExpression.asSQL())) OVER (ORDER BY \(orderBy.expression.template))",
+                bindings + orderBy.expression.bindings
+            )
+
+        }
+        return Expression("lag(\(template), \(offset)) OVER (ORDER BY \(orderBy.expression.template))", bindings + orderBy.expression.bindings)
+    }
+
+    /// Builds a copy of the expression with `lead(self, offset, default) OVER (ORDER BY {orderBy})` window function
+    ///
+    /// - Parameter orderBy: Expression to evaluate window order
+    /// - Returns: An expression returning `lead(self, offset, default) OVER (ORDER BY {orderBy})` window function
+    public func lead(offset: Int = 0, default: Expressible? = nil, _ orderBy: Expressible) -> Expression<UnderlyingType> {
+        if let defaultExpression = `default` {
+            return Expression(
+                "lead(\(template), \(offset), \(defaultExpression.asSQL())) OVER (ORDER BY \(orderBy.expression.template))",
+                bindings + orderBy.expression.bindings)
+
+        }
+        return Expression("lead(\(template), \(offset)) OVER (ORDER BY \(orderBy.expression.template))", bindings + orderBy.expression.bindings)
+    }
+
+    /// Builds a copy of the expression with `first_value(self) OVER (ORDER BY {orderBy})` window function
+    ///
+    /// - Parameter orderBy: Expression to evaluate window order
+    /// - Returns: An expression returning `first_value(self) OVER (ORDER BY {orderBy})` window function
+    public func firstValue(_ orderBy: Expressible) -> Expression<UnderlyingType> {
+        WindowFunction.first_value.over(valueExpr: self, orderBy)
+    }
+
+    /// Builds a copy of the expression with `last_value(self) OVER (ORDER BY {orderBy})` window function
+    ///
+    /// - Parameter orderBy: Expression to evaluate window order
+    /// - Returns: An expression returning `last_value(self) OVER (ORDER BY {orderBy})` window function
+    public func lastValue(_ orderBy: Expressible) -> Expression<UnderlyingType> {
+        WindowFunction.last_value.over(valueExpr: self, orderBy)
+    }
+
+    /// Builds a copy of the expression with `nth_value(self) OVER (ORDER BY {orderBy})` window function
+    ///
+    /// - Parameter index: Row N of the window frame to return
+    /// - Parameter orderBy: Expression to evaluate window order
+    /// - Returns: An expression returning `nth_value(self) OVER (ORDER BY {orderBy})` window function
+    public func value(_ index: Int, _ orderBy: Expressible) -> Expression<UnderlyingType> {
+        Expression("nth_value(\(template), \(index)) OVER (ORDER BY \(orderBy.expression.template))", bindings + orderBy.expression.bindings)
+    }
+}
+
+/// Builds an expression representing `ntile(size) OVER (ORDER BY {orderBy})`
+///
+/// - Parameter orderBy: Expression to evaluate window order
+/// - Returns: An expression returning  `ntile(size) OVER (ORDER BY {orderBy})`
+public func ntile(_ size: Int, _ orderBy: Expressible) -> Expression<Int> {
+//    Expression.ntile(size, orderBy)
+
+        WindowFunction.ntile.over(value: size, orderBy)
+}
+
+/// Builds an expression representing `row_count() OVER (ORDER BY {orderBy})`
+///
+/// - Parameter orderBy: Expression to evaluate window order
+/// - Returns: An expression returning `row_count() OVER (ORDER BY {orderBy})`
+public func rowNumber(_ orderBy: Expressible) -> Expression<Int> {
+    WindowFunction.row_number.over(orderBy)
+}
+
+/// Builds an expression representing `rank() OVER (ORDER BY {orderBy})`
+///
+/// - Parameter orderBy: Expression to evaluate window order
+/// - Returns: An expression returning `rank() OVER (ORDER BY {orderBy})`
+public func rank(_ orderBy: Expressible) -> Expression<Int> {
+    WindowFunction.rank.over(orderBy)
+}
+
+/// Builds an expression representing `dense_rank() OVER (ORDER BY {orderBy})`
+///
+/// - Parameter orderBy: Expression to evaluate window order
+/// - Returns: An expression returning `dense_rank() OVER ('over')`
+public func denseRank(_ orderBy: Expressible) -> Expression<Int> {
+    WindowFunction.dense_rank.over(orderBy)
+}
+
+/// Builds an expression representing `percent_rank() OVER (ORDER BY {orderBy})`
+///
+/// - Parameter orderBy: Expression to evaluate window order
+/// - Returns: An expression returning `percent_rank() OVER (ORDER BY {orderBy})`
+public func percentRank(_ orderBy: Expressible) -> Expression<Double> {
+    WindowFunction.percent_rank.over(orderBy)
+}
+
+/// Builds an expression representing `cume_dist() OVER (ORDER BY {orderBy})`
+///
+/// - Parameter orderBy: Expression to evaluate window order
+/// - Returns: An expression returning `cume_dist() OVER (ORDER BY {orderBy})`
+public func cumeDist(_ orderBy: Expressible) -> Expression<Double> {
+    WindowFunction.cume_dist.over(orderBy)
+}
diff --git a/Tests/SQLiteTests/Typed/QueryIntegrationTests.swift b/Tests/SQLiteTests/Typed/QueryIntegrationTests.swift
index d8d31a79..aa45cafe 100644
--- a/Tests/SQLiteTests/Typed/QueryIntegrationTests.swift
+++ b/Tests/SQLiteTests/Typed/QueryIntegrationTests.swift
@@ -322,6 +322,101 @@ class QueryIntegrationTests: SQLiteTestCase {
         XCTAssertNotNil(row[name])
         XCTAssertNotNil(row[email])
     }
+
+    func test_select_ntile_function() throws {
+        let users = Table("users")
+
+        try insertUser("Joey")
+        try insertUser("Timmy")
+        try insertUser("Jimmy")
+        try insertUser("Billy")
+
+        let bucket = ntile(1, id.asc)
+        try db.prepare(users.select(id, bucket)).forEach {
+            XCTAssertEqual($0[bucket], 1) // only 1 window
+        }
+    }
+
+    func test_select_cume_dist_function() throws {
+        let users = Table("users")
+
+        try insertUser("Joey")
+        try insertUser("Timmy")
+        try insertUser("Jimmy")
+        try insertUser("Billy")
+
+        let cumeDist = cumeDist(email)
+        let results = try db.prepare(users.select(id, cumeDist)).map {
+            $0[cumeDist]
+        }
+        XCTAssertEqual([0.25, 0.5, 0.75, 1], results)
+    }
+
+    func test_select_window_row_number() throws {
+        let users = Table("users")
+
+        try insertUser("Billy")
+        try insertUser("Jimmy")
+        try insertUser("Joey")
+        try insertUser("Timmy")
+
+        let rowNumber = rowNumber(email.asc)
+        var expectedRowNum = 1
+        try db.prepare(users.select(id, rowNumber)).forEach {
+            // should retrieve row numbers in order of INSERT above
+            XCTAssertEqual($0[rowNumber], expectedRowNum)
+            expectedRowNum += 1
+        }
+    }
+
+    func test_select_window_ranking() throws {
+        let users = Table("users")
+
+        try insertUser("Billy")
+        try insertUser("Jimmy")
+        try insertUser("Joey")
+        try insertUser("Timmy")
+
+        let percentRank = percentRank(email)
+        let actualPercentRank: [Int] = try db.prepare(users.select(id, percentRank)).map {
+            Int($0[percentRank] * 100)
+        }
+        XCTAssertEqual([0, 33, 66, 100], actualPercentRank)
+
+        let rank = rank(email)
+        let actualRank: [Int] = try db.prepare(users.select(id, rank)).map {
+            $0[rank]
+        }
+        XCTAssertEqual([1, 2, 3, 4], actualRank)
+
+        let denseRank = denseRank(email)
+        let actualDenseRank: [Int] = try db.prepare(users.select(id, denseRank)).map {
+            $0[denseRank]
+        }
+        XCTAssertEqual([1, 2, 3, 4], actualDenseRank)
+    }
+
+    func test_select_window_values() throws {
+        let users = Table("users")
+
+        try insertUser("Billy")
+        try insertUser("Jimmy")
+        try insertUser("Joey")
+        try insertUser("Timmy")
+
+        let firstValue = email.firstValue(email.desc)
+        try db.prepare(users.select(id, firstValue)).forEach {
+            XCTAssertEqual($0[firstValue], "Timmy@example.com") // should grab last email alphabetically
+        }
+
+        let lastValue = email.lastValue(email.asc)
+        var row = try db.pluck(users.select(id, lastValue))!
+        XCTAssertEqual(row[lastValue], "Billy@example.com")
+
+        let nthValue = email.value(1, email.asc)
+        row = try db.pluck(users.select(id, nthValue))!
+        XCTAssertEqual(row[nthValue], "Billy@example.com")
+    }
 }
 
 extension Connection {
diff --git a/Tests/SQLiteTests/Typed/WindowFunctionsTests.swift b/Tests/SQLiteTests/Typed/WindowFunctionsTests.swift
new file mode 100644
index 00000000..6ded152b
--- /dev/null
+++ b/Tests/SQLiteTests/Typed/WindowFunctionsTests.swift
@@ -0,0 +1,58 @@
+import XCTest
+import SQLite
+
+class WindowFunctionsTests: XCTestCase {
+
+    func test_ntile_wrapsExpressionWithOverClause() {
+        assertSQL("ntile(1) OVER (ORDER BY \"int\" DESC)", ntile(1, int.desc))
+        assertSQL("ntile(20) OVER (ORDER BY \"intOptional\" ASC)", ntile(20, intOptional.asc))
+        assertSQL("ntile(20) OVER (ORDER BY \"double\" ASC)", ntile(20, double.asc))
+        assertSQL("ntile(1) OVER (ORDER BY \"doubleOptional\" ASC)", ntile(1, doubleOptional.asc))
+        assertSQL("ntile(1) OVER (ORDER BY \"int\" DESC)", ntile(1, int.desc))
+    }
+
+    func test_row_number_wrapsExpressionWithOverClause() {
+        assertSQL("row_number() OVER (ORDER BY \"int\" DESC)", rowNumber(int.desc))
+    }
+
+    func test_rank_wrapsExpressionWithOverClause() {
+        assertSQL("rank() OVER (ORDER BY \"int\" DESC)", rank(int.desc))
+    }
+
+    func test_dense_rank_wrapsExpressionWithOverClause() {
+        assertSQL("dense_rank() OVER (ORDER BY \"int\" DESC)", denseRank(int.desc))
+    }
+
+    func test_percent_rank_wrapsExpressionWithOverClause() {
+        assertSQL("percent_rank() OVER (ORDER BY \"int\" DESC)", percentRank(int.desc))
+    }
+
+    func test_cume_dist_wrapsExpressionWithOverClause() {
+        assertSQL("cume_dist() OVER (ORDER BY \"int\" DESC)", cumeDist(int.desc))
+    }
+
+    func test_lag_wrapsExpressionWithOverClause() {
+        assertSQL("lag(\"int\", 0) OVER (ORDER BY \"int\" DESC)", int.lag(int.desc))
+        assertSQL("lag(\"int\", 7) OVER (ORDER BY \"int\" DESC)", int.lag(offset: 7, int.desc))
+        assertSQL("lag(\"int\", 1, 3) OVER (ORDER BY \"int\" DESC)", int.lag(offset: 1, default: Expression<Int>(value: 3), int.desc))
+    }
+
+    func test_lead_wrapsExpressionWithOverClause() {
+        assertSQL("lead(\"int\", 0) OVER (ORDER BY \"int\" DESC)", int.lead(int.desc))
+        assertSQL("lead(\"int\", 7) OVER (ORDER BY \"int\" DESC)", int.lead(offset: 7, int.desc))
+        assertSQL("lead(\"int\", 1, 3) OVER (ORDER BY \"int\" DESC)", int.lead(offset: 1, default: Expression<Int>(value: 3), int.desc))
+    }
+
+    func test_firstValue_wrapsExpressionWithOverClause() {
+        assertSQL("first_value(\"int\") OVER (ORDER BY \"int\" DESC)", int.firstValue(int.desc))
+        assertSQL("first_value(\"double\") OVER (ORDER BY \"int\" DESC)", double.firstValue(int.desc))
+    }
+
+    func test_lastValue_wrapsExpressionWithOverClause() {
+        assertSQL("last_value(\"int\") OVER (ORDER BY \"int\" DESC)", int.lastValue(int.desc))
+    }
+
+    func test_nth_value_wrapsExpressionWithOverClause() {
+        assertSQL("nth_value(\"int\", 3) OVER (ORDER BY \"int\" DESC)", int.value(3, int.desc))
+    }
+}

From 2fc62a96f03112f77604285cd0ad24b60558c22a Mon Sep 17 00:00:00 2001
From: Geoff MacDonald <geoffmacd@gmail.com>
Date: Sat, 18 Nov 2023 18:42:41 -0800
Subject: [PATCH 254/326] update docs

---
 Documentation/Index.md | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/Documentation/Index.md b/Documentation/Index.md
index 5d72aa4b..a03c26a3 100644
--- a/Documentation/Index.md
+++ b/Documentation/Index.md
@@ -64,6 +64,7 @@
   - [Other Operators](#other-operators)
   - [Core SQLite Functions](#core-sqlite-functions)
   - [Aggregate SQLite Functions](#aggregate-sqlite-functions)
+  - [Window SQLite Functions](#window-sqlite-functions)
   - [Date and Time Functions](#date-and-time-functions)
   - [Custom SQL Functions](#custom-sql-functions)
   - [Custom Collations](#custom-collations)
@@ -1871,6 +1872,11 @@ Most of SQLite’s
 [aggregate functions](https://www.sqlite.org/lang_aggfunc.html) have been
 surfaced in and type-audited for SQLite.swift.
 
+## Window SQLite Functions
+
+Most of SQLite's [window functions](https://www.sqlite.org/windowfunctions.html) have been
+surfaced in and type-audited for SQLite.swift. Currently only `OVER (ORDER BY ...)` windowing is possible. 
+
 ## Date and Time functions
 
 SQLite's [date and time](https://www.sqlite.org/lang_datefunc.html)

From dedef242d9253e5d6169dfe91d9e67ba4597291b Mon Sep 17 00:00:00 2001
From: ivan <ivanlares1@gmail.com>
Date: Thu, 4 Jan 2024 11:22:10 -0800
Subject: [PATCH 255/326] Add privacy manifest

---
 SQLite.xcodeproj/project.pbxproj     | 16 ++++++++++++++++
 Sources/SQLite/PrivacyInfo.xcprivacy | 14 ++++++++++++++
 2 files changed, 30 insertions(+)
 create mode 100644 Sources/SQLite/PrivacyInfo.xcprivacy

diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj
index def32855..8eb1df66 100644
--- a/SQLite.xcodeproj/project.pbxproj
+++ b/SQLite.xcodeproj/project.pbxproj
@@ -211,6 +211,13 @@
 		DB7C5DA728D7C9B6006395CF /* SQLiteVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB7C5DA528D7C9B6006395CF /* SQLiteVersion.swift */; };
 		DB7C5DA828D7C9B6006395CF /* SQLiteVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB7C5DA528D7C9B6006395CF /* SQLiteVersion.swift */; };
 		DB7C5DA928D7C9B6006395CF /* SQLiteVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB7C5DA528D7C9B6006395CF /* SQLiteVersion.swift */; };
+		EAE1E1542B473B0C0048B157 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = EAE1E1532B473B0C0048B157 /* PrivacyInfo.xcprivacy */; };
+		EAE1E1552B473B0C0048B157 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = EAE1E1532B473B0C0048B157 /* PrivacyInfo.xcprivacy */; };
+		EAE1E1562B473B0C0048B157 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = EAE1E1532B473B0C0048B157 /* PrivacyInfo.xcprivacy */; };
+		EAE1E1572B473B0C0048B157 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = EAE1E1532B473B0C0048B157 /* PrivacyInfo.xcprivacy */; };
+		EAE1E1582B473B0C0048B157 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = EAE1E1532B473B0C0048B157 /* PrivacyInfo.xcprivacy */; };
+		EAE1E1592B473B0C0048B157 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = EAE1E1532B473B0C0048B157 /* PrivacyInfo.xcprivacy */; };
+		EAE1E15A2B473B0C0048B157 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = EAE1E1532B473B0C0048B157 /* PrivacyInfo.xcprivacy */; };
 		EE247AD71C3F04ED00AE3E12 /* SQLite.h in Headers */ = {isa = PBXBuildFile; fileRef = EE247AD61C3F04ED00AE3E12 /* SQLite.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		EE247ADE1C3F04ED00AE3E12 /* SQLite.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EE247AD31C3F04ED00AE3E12 /* SQLite.framework */; };
 		EE247B031C3F06E900AE3E12 /* Blob.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AEE1C3F06E900AE3E12 /* Blob.swift */; };
@@ -340,6 +347,7 @@
 		DB58B21028FB864300F8EEA4 /* SchemaReader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SchemaReader.swift; sourceTree = "<group>"; };
 		DB58B21528FC7C4600F8EEA4 /* SQLiteFeature.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SQLiteFeature.swift; sourceTree = "<group>"; };
 		DB7C5DA528D7C9B6006395CF /* SQLiteVersion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SQLiteVersion.swift; sourceTree = "<group>"; };
+		EAE1E1532B473B0C0048B157 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
 		EE247AD31C3F04ED00AE3E12 /* SQLite.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SQLite.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		EE247AD61C3F04ED00AE3E12 /* SQLite.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SQLite.h; sourceTree = "<group>"; };
 		EE247AD81C3F04ED00AE3E12 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@@ -546,6 +554,7 @@
 				EE247AF71C3F06E900AE3E12 /* Foundation.swift */,
 				EE247AF81C3F06E900AE3E12 /* Helpers.swift */,
 				EE247AD81C3F04ED00AE3E12 /* Info.plist */,
+				EAE1E1532B473B0C0048B157 /* PrivacyInfo.xcprivacy */,
 				EE247AED1C3F06E900AE3E12 /* Core */,
 				EE247AF41C3F06E900AE3E12 /* Extensions */,
 				EE247AF91C3F06E900AE3E12 /* Typed */,
@@ -892,6 +901,7 @@
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				EAE1E1582B473B0C0048B157 /* PrivacyInfo.xcprivacy in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -899,6 +909,7 @@
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				EAE1E1592B473B0C0048B157 /* PrivacyInfo.xcprivacy in Resources */,
 				3DF7B79828846FED005DD8CA /* Resources in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
@@ -907,6 +918,7 @@
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				EAE1E15A2B473B0C0048B157 /* PrivacyInfo.xcprivacy in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -914,6 +926,7 @@
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				EAE1E1542B473B0C0048B157 /* PrivacyInfo.xcprivacy in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -921,6 +934,7 @@
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				EAE1E1552B473B0C0048B157 /* PrivacyInfo.xcprivacy in Resources */,
 				3DF7B79628846FCC005DD8CA /* Resources in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
@@ -929,6 +943,7 @@
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				EAE1E1562B473B0C0048B157 /* PrivacyInfo.xcprivacy in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -936,6 +951,7 @@
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				EAE1E1572B473B0C0048B157 /* PrivacyInfo.xcprivacy in Resources */,
 				3DF7B79928847055005DD8CA /* Resources in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
diff --git a/Sources/SQLite/PrivacyInfo.xcprivacy b/Sources/SQLite/PrivacyInfo.xcprivacy
new file mode 100644
index 00000000..987771fa
--- /dev/null
+++ b/Sources/SQLite/PrivacyInfo.xcprivacy
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>NSPrivacyTrackingDomains</key>
+	<array/>
+	<key>NSPrivacyCollectedDataTypes</key>
+	<array/>
+	<key>NSPrivacyAccessedAPITypes</key>
+	<array/>
+	<key>NSPrivacyTracking</key>
+	<false/>
+</dict>
+</plist>

From e7c4212a213f8dec5d68ec6e7acadeed8f4fb912 Mon Sep 17 00:00:00 2001
From: Anthony Miller <anthonymdev@gmail.com>
Date: Wed, 24 Jan 2024 15:18:34 -0800
Subject: [PATCH 256/326] Add visionOS support to Package.swift

---
 Package.swift | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/Package.swift b/Package.swift
index 70bde7ef..238661ae 100644
--- a/Package.swift
+++ b/Package.swift
@@ -1,4 +1,4 @@
-// swift-tools-version:5.7
+// swift-tools-version:5.9
 import PackageDescription
 
 let package = Package(
@@ -7,7 +7,8 @@ let package = Package(
         .iOS(.v11),
         .macOS(.v10_13),
         .watchOS(.v4),
-        .tvOS(.v11)
+        .tvOS(.v11),
+        .visionOS(.v1)
     ],
     products: [
         .library(

From 240f0802792cce82ebd0c27300cd1748b322e200 Mon Sep 17 00:00:00 2001
From: Anthony Miller <anthonymdev@gmail.com>
Date: Wed, 24 Jan 2024 15:18:43 -0800
Subject: [PATCH 257/326] Create visionOS target and test plan

---
 SQLite.xcodeproj/project.pbxproj              | 344 +++++++++++++++++-
 .../xcschemes/SQLite visionOS.xcscheme        |  71 ++++
 Tests/SQLite visionOS.xctestplan              |  24 ++
 3 files changed, 435 insertions(+), 4 deletions(-)
 create mode 100644 SQLite.xcodeproj/xcshareddata/xcschemes/SQLite visionOS.xcscheme
 create mode 100644 Tests/SQLite visionOS.xctestplan

diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj
index def32855..857c6fd9 100644
--- a/SQLite.xcodeproj/project.pbxproj
+++ b/SQLite.xcodeproj/project.pbxproj
@@ -211,6 +211,76 @@
 		DB7C5DA728D7C9B6006395CF /* SQLiteVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB7C5DA528D7C9B6006395CF /* SQLiteVersion.swift */; };
 		DB7C5DA828D7C9B6006395CF /* SQLiteVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB7C5DA528D7C9B6006395CF /* SQLiteVersion.swift */; };
 		DB7C5DA928D7C9B6006395CF /* SQLiteVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB7C5DA528D7C9B6006395CF /* SQLiteVersion.swift */; };
+		DEB306BA2B61CEF500F9D46B /* SQLite.h in Headers */ = {isa = PBXBuildFile; fileRef = EE247AD61C3F04ED00AE3E12 /* SQLite.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		DEB306BC2B61CEF500F9D46B /* CoreFunctions.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AFC1C3F06E900AE3E12 /* CoreFunctions.swift */; };
+		DEB306BD2B61CEF500F9D46B /* Coding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49EB68C31F7B3CB400D89D40 /* Coding.swift */; };
+		DEB306BE2B61CEF500F9D46B /* RTree.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AF61C3F06E900AE3E12 /* RTree.swift */; };
+		DEB306BF2B61CEF500F9D46B /* Blob.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AEE1C3F06E900AE3E12 /* Blob.swift */; };
+		DEB306C02B61CEF500F9D46B /* URIQueryParameter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DF7B790288449BA005DD8CA /* URIQueryParameter.swift */; };
+		DEB306C12B61CEF500F9D46B /* Foundation.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AF71C3F06E900AE3E12 /* Foundation.swift */; };
+		DEB306C22B61CEF500F9D46B /* Connection.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AEF1C3F06E900AE3E12 /* Connection.swift */; };
+		DEB306C32B61CEF500F9D46B /* Expression.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AFE1C3F06E900AE3E12 /* Expression.swift */; };
+		DEB306C42B61CEF500F9D46B /* Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AF81C3F06E900AE3E12 /* Helpers.swift */; };
+		DEB306C52B61CEF500F9D46B /* Collation.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AFB1C3F06E900AE3E12 /* Collation.swift */; };
+		DEB306C62B61CEF500F9D46B /* Setter.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B021C3F06E900AE3E12 /* Setter.swift */; };
+		DEB306C72B61CEF500F9D46B /* Connection+Attach.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DF7B78728842972005DD8CA /* Connection+Attach.swift */; };
+		DEB306C82B61CEF500F9D46B /* CustomFunctions.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AFD1C3F06E900AE3E12 /* CustomFunctions.swift */; };
+		DEB306C92B61CEF500F9D46B /* FTS4.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AF51C3F06E900AE3E12 /* FTS4.swift */; };
+		DEB306CA2B61CEF500F9D46B /* Value.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AF31C3F06E900AE3E12 /* Value.swift */; };
+		DEB306CB2B61CEF500F9D46B /* Operators.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AFF1C3F06E900AE3E12 /* Operators.swift */; };
+		DEB306CC2B61CEF500F9D46B /* Schema.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B011C3F06E900AE3E12 /* Schema.swift */; };
+		DEB306CD2B61CEF500F9D46B /* Query.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B001C3F06E900AE3E12 /* Query.swift */; };
+		DEB306CE2B61CEF500F9D46B /* Statement.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AF21C3F06E900AE3E12 /* Statement.swift */; };
+		DEB306CF2B61CEF500F9D46B /* AggregateFunctions.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AFA1C3F06E900AE3E12 /* AggregateFunctions.swift */; };
+		DEB306D02B61CEF500F9D46B /* FTS5.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1730E4390C775C25677D1 /* FTS5.swift */; };
+		DEB306D12B61CEF500F9D46B /* Cipher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A178A39ACA9667A62663CC /* Cipher.swift */; };
+		DEB306D22B61CEF500F9D46B /* Backup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02A43A9722738CF100FEC494 /* Backup.swift */; };
+		DEB306D32B61CEF500F9D46B /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1710E73A46D5AC721CDA9 /* Errors.swift */; };
+		DEB306D42B61CEF500F9D46B /* DateAndTimeFunctions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17BA55DABB480F9020C8A /* DateAndTimeFunctions.swift */; };
+		DEB306D52B61CEF500F9D46B /* SQLiteVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB7C5DA528D7C9B6006395CF /* SQLiteVersion.swift */; };
+		DEB306D62B61CEF500F9D46B /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17E723300E5ED3771DCB5 /* Result.swift */; };
+		DEB306D72B61CEF500F9D46B /* Query+with.swift in Sources */ = {isa = PBXBuildFile; fileRef = 997DF2AD287FC06D00F8DF95 /* Query+with.swift */; };
+		DEB306D82B61CEF500F9D46B /* Connection+Aggregation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A175A9CB446640AE6F2200 /* Connection+Aggregation.swift */; };
+		DEB306D92B61CEF500F9D46B /* SQLiteFeature.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB58B21528FC7C4600F8EEA4 /* SQLiteFeature.swift */; };
+		DEB306DA2B61CEF500F9D46B /* SchemaChanger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A171B262DDE8718513CFDA /* SchemaChanger.swift */; };
+		DEB306DB2B61CEF500F9D46B /* SchemaDefinitions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17268AE67B746B96AC125 /* SchemaDefinitions.swift */; };
+		DEB306DC2B61CEF500F9D46B /* Connection+Schema.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A170A97B51DC5EE365F3C5 /* Connection+Schema.swift */; };
+		DEB306DD2B61CEF500F9D46B /* Connection+Pragmas.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17F285B767BFACD96714B /* Connection+Pragmas.swift */; };
+		DEB306DE2B61CEF500F9D46B /* SchemaReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB58B21028FB864300F8EEA4 /* SchemaReader.swift */; };
+		DEB306E02B61CEF500F9D46B /* libsqlite3.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = EE9180931C46EA210038162A /* libsqlite3.tbd */; };
+		DEB306EB2B61CF9500F9D46B /* TestHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B161C3F127200AE3E12 /* TestHelpers.swift */; };
+		DEB306EC2B61CF9500F9D46B /* FoundationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1794CC4D7827E997E32A7 /* FoundationTests.swift */; };
+		DEB306ED2B61CF9500F9D46B /* Fixtures.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17B93B48B5560E6E51791 /* Fixtures.swift */; };
+		DEB306EE2B61CF9500F9D46B /* SchemaDefinitionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A176174862D0F0139B3987 /* SchemaDefinitionsTests.swift */; };
+		DEB306EF2B61CF9500F9D46B /* SchemaChangerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17FDB0B0CFB8987906FD0 /* SchemaChangerTests.swift */; };
+		DEB306F02B61CF9500F9D46B /* Connection+SchemaTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17CA1DF7D0F7C9A94C51C /* Connection+SchemaTests.swift */; };
+		DEB306F12B61CF9500F9D46B /* FTS5Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17B96EBD42C878E609CDC /* FTS5Tests.swift */; };
+		DEB306F22B61CF9500F9D46B /* FTSIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17BE1CC4AD4036BAB8EE0 /* FTSIntegrationTests.swift */; };
+		DEB306F32B61CF9500F9D46B /* FTS4Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1715904F7B6851FCB5EF6 /* FTS4Tests.swift */; };
+		DEB306F42B61CF9500F9D46B /* ExpressionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A170F141BF21946D159083 /* ExpressionTests.swift */; };
+		DEB306F52B61CF9500F9D46B /* StatementTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1756EB81E9F7F45B12A78 /* StatementTests.swift */; };
+		DEB306F62B61CF9500F9D46B /* QueryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A171ED017645C8B04DF9F2 /* QueryTests.swift */; };
+		DEB306F72B61CF9500F9D46B /* CipherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1787E16C8562C09C076F5 /* CipherTests.swift */; };
+		DEB306F82B61CF9500F9D46B /* BlobTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A171A2ED4E2640F197F48C /* BlobTests.swift */; };
+		DEB306F92B61CF9500F9D46B /* ConnectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17855BD524FF888265B3C /* ConnectionTests.swift */; };
+		DEB306FA2B61CF9500F9D46B /* CoreFunctionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17B55F2409B7031443495 /* CoreFunctionsTests.swift */; };
+		DEB306FB2B61CF9500F9D46B /* DateAndTimeFunctionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A178AEF32ABC8BF2993FB5 /* DateAndTimeFunctionTests.swift */; };
+		DEB306FC2B61CF9500F9D46B /* CustomFunctionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17F2B5959B212A33C383F /* CustomFunctionsTests.swift */; };
+		DEB306FD2B61CF9500F9D46B /* OperatorsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17475DCA068453F787613 /* OperatorsTests.swift */; };
+		DEB306FE2B61CF9500F9D46B /* ResultTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1745BE8623D8C6808DB3C /* ResultTests.swift */; };
+		DEB306FF2B61CF9500F9D46B /* RTreeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17162C9861E5C4900455D /* RTreeTests.swift */; };
+		DEB307002B61CF9500F9D46B /* SchemaTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A171A7714C6524093255C5 /* SchemaTests.swift */; };
+		DEB307012B61CF9500F9D46B /* SelectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17EC0C43015063945D32E /* SelectTests.swift */; };
+		DEB307022B61CF9500F9D46B /* ValueTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17AE284BB1DF31D1B753E /* ValueTests.swift */; };
+		DEB307032B61CF9500F9D46B /* QueryIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17CAE60446607E99C22A6 /* QueryIntegrationTests.swift */; };
+		DEB307042B61CF9500F9D46B /* AggregateFunctionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17613F00A3F4D4A257A04 /* AggregateFunctionsTests.swift */; };
+		DEB307052B61CF9500F9D46B /* CustomAggregationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A177EF5E2D91BA86DA4480 /* CustomAggregationTests.swift */; };
+		DEB307062B61CF9500F9D46B /* SetterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1709D5BDD2691BA160012 /* SetterTests.swift */; };
+		DEB307072B61CF9500F9D46B /* RowTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A174FE5B47A97937A27276 /* RowTests.swift */; };
+		DEB307082B61CF9500F9D46B /* Connection+PragmaTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17C60D3464461057E4D63 /* Connection+PragmaTests.swift */; };
+		DEB307092B61CF9500F9D46B /* Connection+AttachTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A170C08525D3D27CB5F83C /* Connection+AttachTests.swift */; };
+		DEB3070B2B61CF9500F9D46B /* SQLite.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 03A65E5A1C6BB0F50062603F /* SQLite.framework */; };
+		DEB3070D2B61CF9500F9D46B /* Resources in Resources */ = {isa = PBXBuildFile; fileRef = 3DF7B79528846FCC005DD8CA /* Resources */; };
 		EE247AD71C3F04ED00AE3E12 /* SQLite.h in Headers */ = {isa = PBXBuildFile; fileRef = EE247AD61C3F04ED00AE3E12 /* SQLite.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		EE247ADE1C3F04ED00AE3E12 /* SQLite.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EE247AD31C3F04ED00AE3E12 /* SQLite.framework */; };
 		EE247B031C3F06E900AE3E12 /* Blob.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AEE1C3F06E900AE3E12 /* Blob.swift */; };
@@ -263,6 +333,13 @@
 			remoteGlobalIDString = 03A65E591C6BB0F50062603F;
 			remoteInfo = "SQLite tvOS";
 		};
+		DEB307142B61D07F00F9D46B /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = EE247ACA1C3F04ED00AE3E12 /* Project object */;
+			proxyType = 1;
+			remoteGlobalIDString = DEB306B82B61CEF500F9D46B;
+			remoteInfo = "SQLite visionOS";
+		};
 		EE247ADF1C3F04ED00AE3E12 /* PBXContainerItemProxy */ = {
 			isa = PBXContainerItemProxy;
 			containerPortal = EE247ACA1C3F04ED00AE3E12 /* Project object */;
@@ -340,6 +417,9 @@
 		DB58B21028FB864300F8EEA4 /* SchemaReader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SchemaReader.swift; sourceTree = "<group>"; };
 		DB58B21528FC7C4600F8EEA4 /* SQLiteFeature.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SQLiteFeature.swift; sourceTree = "<group>"; };
 		DB7C5DA528D7C9B6006395CF /* SQLiteVersion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SQLiteVersion.swift; sourceTree = "<group>"; };
+		DEB306E52B61CEF500F9D46B /* SQLite.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SQLite.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+		DEB307112B61CF9500F9D46B /* SQLiteTests visionOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "SQLiteTests visionOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
+		DEB307132B61D04500F9D46B /* SQLite visionOS.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; name = "SQLite visionOS.xctestplan"; path = "Tests/SQLite visionOS.xctestplan"; sourceTree = "<group>"; };
 		EE247AD31C3F04ED00AE3E12 /* SQLite.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SQLite.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		EE247AD61C3F04ED00AE3E12 /* SQLite.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SQLite.h; sourceTree = "<group>"; };
 		EE247AD81C3F04ED00AE3E12 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@@ -401,6 +481,22 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		DEB306DF2B61CEF500F9D46B /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				DEB306E02B61CEF500F9D46B /* libsqlite3.tbd in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		DEB3070A2B61CF9500F9D46B /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				DEB3070B2B61CF9500F9D46B /* SQLite.framework in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 		EE247ACF1C3F04ED00AE3E12 /* Frameworks */ = {
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
@@ -517,6 +613,7 @@
 				3D3C3CCB26E5568800759140 /* SQLite.playground */,
 				EE247AD51C3F04ED00AE3E12 /* SQLite */,
 				EE247AE11C3F04ED00AE3E12 /* SQLiteTests */,
+				DEB307132B61D04500F9D46B /* SQLite visionOS.xctestplan */,
 				EE247B8A1C3F81D000AE3E12 /* Metadata */,
 				EE247AD41C3F04ED00AE3E12 /* Products */,
 				3D67B3E41DB2469200A4F4C6 /* Frameworks */,
@@ -535,6 +632,8 @@
 				03A65E5A1C6BB0F50062603F /* SQLite.framework */,
 				03A65E631C6BB0F60062603F /* SQLiteTests tvOS.xctest */,
 				A121AC451CA35C79005A31D1 /* SQLite.framework */,
+				DEB306E52B61CEF500F9D46B /* SQLite.framework */,
+				DEB307112B61CF9500F9D46B /* SQLiteTests visionOS.xctest */,
 			);
 			name = Products;
 			sourceTree = "<group>";
@@ -679,6 +778,14 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		DEB306B92B61CEF500F9D46B /* Headers */ = {
+			isa = PBXHeadersBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				DEB306BA2B61CEF500F9D46B /* SQLite.h in Headers */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 		EE247AD01C3F04ED00AE3E12 /* Headers */ = {
 			isa = PBXHeadersBuildPhase;
 			buildActionMask = 2147483647;
@@ -752,6 +859,42 @@
 			productReference = A121AC451CA35C79005A31D1 /* SQLite.framework */;
 			productType = "com.apple.product-type.framework";
 		};
+		DEB306B82B61CEF500F9D46B /* SQLite visionOS */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = DEB306E22B61CEF500F9D46B /* Build configuration list for PBXNativeTarget "SQLite visionOS" */;
+			buildPhases = (
+				DEB306B92B61CEF500F9D46B /* Headers */,
+				DEB306BB2B61CEF500F9D46B /* Sources */,
+				DEB306DF2B61CEF500F9D46B /* Frameworks */,
+				DEB306E12B61CEF500F9D46B /* Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = "SQLite visionOS";
+			productName = SQLite;
+			productReference = DEB306E52B61CEF500F9D46B /* SQLite.framework */;
+			productType = "com.apple.product-type.framework";
+		};
+		DEB306E72B61CF9500F9D46B /* SQLiteTests visionOS */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = DEB3070E2B61CF9500F9D46B /* Build configuration list for PBXNativeTarget "SQLiteTests visionOS" */;
+			buildPhases = (
+				DEB306EA2B61CF9500F9D46B /* Sources */,
+				DEB3070A2B61CF9500F9D46B /* Frameworks */,
+				DEB3070C2B61CF9500F9D46B /* Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+				DEB307152B61D07F00F9D46B /* PBXTargetDependency */,
+			);
+			name = "SQLiteTests visionOS";
+			productName = "SQLite tvOSTests";
+			productReference = DEB307112B61CF9500F9D46B /* SQLiteTests visionOS.xctest */;
+			productType = "com.apple.product-type.bundle.unit-test";
+		};
 		EE247AD21C3F04ED00AE3E12 /* SQLite iOS */ = {
 			isa = PBXNativeTarget;
 			buildConfigurationList = EE247AE71C3F04ED00AE3E12 /* Build configuration list for PBXNativeTarget "SQLite iOS" */;
@@ -883,6 +1026,8 @@
 				03A65E591C6BB0F50062603F /* SQLite tvOS */,
 				03A65E621C6BB0F60062603F /* SQLiteTests tvOS */,
 				A121AC441CA35C79005A31D1 /* SQLite watchOS */,
+				DEB306B82B61CEF500F9D46B /* SQLite visionOS */,
+				DEB306E72B61CF9500F9D46B /* SQLiteTests visionOS */,
 			);
 		};
 /* End PBXProject section */
@@ -910,6 +1055,21 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		DEB306E12B61CEF500F9D46B /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		DEB3070C2B61CF9500F9D46B /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				DEB3070D2B61CF9500F9D46B /* Resources in Resources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 		EE247AD11C3F04ED00AE3E12 /* Resources */ = {
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
@@ -1064,6 +1224,86 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		DEB306BB2B61CEF500F9D46B /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				DEB306BC2B61CEF500F9D46B /* CoreFunctions.swift in Sources */,
+				DEB306BD2B61CEF500F9D46B /* Coding.swift in Sources */,
+				DEB306BE2B61CEF500F9D46B /* RTree.swift in Sources */,
+				DEB306BF2B61CEF500F9D46B /* Blob.swift in Sources */,
+				DEB306C02B61CEF500F9D46B /* URIQueryParameter.swift in Sources */,
+				DEB306C12B61CEF500F9D46B /* Foundation.swift in Sources */,
+				DEB306C22B61CEF500F9D46B /* Connection.swift in Sources */,
+				DEB306C32B61CEF500F9D46B /* Expression.swift in Sources */,
+				DEB306C42B61CEF500F9D46B /* Helpers.swift in Sources */,
+				DEB306C52B61CEF500F9D46B /* Collation.swift in Sources */,
+				DEB306C62B61CEF500F9D46B /* Setter.swift in Sources */,
+				DEB306C72B61CEF500F9D46B /* Connection+Attach.swift in Sources */,
+				DEB306C82B61CEF500F9D46B /* CustomFunctions.swift in Sources */,
+				DEB306C92B61CEF500F9D46B /* FTS4.swift in Sources */,
+				DEB306CA2B61CEF500F9D46B /* Value.swift in Sources */,
+				DEB306CB2B61CEF500F9D46B /* Operators.swift in Sources */,
+				DEB306CC2B61CEF500F9D46B /* Schema.swift in Sources */,
+				DEB306CD2B61CEF500F9D46B /* Query.swift in Sources */,
+				DEB306CE2B61CEF500F9D46B /* Statement.swift in Sources */,
+				DEB306CF2B61CEF500F9D46B /* AggregateFunctions.swift in Sources */,
+				DEB306D02B61CEF500F9D46B /* FTS5.swift in Sources */,
+				DEB306D12B61CEF500F9D46B /* Cipher.swift in Sources */,
+				DEB306D22B61CEF500F9D46B /* Backup.swift in Sources */,
+				DEB306D32B61CEF500F9D46B /* Errors.swift in Sources */,
+				DEB306D42B61CEF500F9D46B /* DateAndTimeFunctions.swift in Sources */,
+				DEB306D52B61CEF500F9D46B /* SQLiteVersion.swift in Sources */,
+				DEB306D62B61CEF500F9D46B /* Result.swift in Sources */,
+				DEB306D72B61CEF500F9D46B /* Query+with.swift in Sources */,
+				DEB306D82B61CEF500F9D46B /* Connection+Aggregation.swift in Sources */,
+				DEB306D92B61CEF500F9D46B /* SQLiteFeature.swift in Sources */,
+				DEB306DA2B61CEF500F9D46B /* SchemaChanger.swift in Sources */,
+				DEB306DB2B61CEF500F9D46B /* SchemaDefinitions.swift in Sources */,
+				DEB306DC2B61CEF500F9D46B /* Connection+Schema.swift in Sources */,
+				DEB306DD2B61CEF500F9D46B /* Connection+Pragmas.swift in Sources */,
+				DEB306DE2B61CEF500F9D46B /* SchemaReader.swift in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		DEB306EA2B61CF9500F9D46B /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				DEB306EB2B61CF9500F9D46B /* TestHelpers.swift in Sources */,
+				DEB306EC2B61CF9500F9D46B /* FoundationTests.swift in Sources */,
+				DEB306ED2B61CF9500F9D46B /* Fixtures.swift in Sources */,
+				DEB306EE2B61CF9500F9D46B /* SchemaDefinitionsTests.swift in Sources */,
+				DEB306EF2B61CF9500F9D46B /* SchemaChangerTests.swift in Sources */,
+				DEB306F02B61CF9500F9D46B /* Connection+SchemaTests.swift in Sources */,
+				DEB306F12B61CF9500F9D46B /* FTS5Tests.swift in Sources */,
+				DEB306F22B61CF9500F9D46B /* FTSIntegrationTests.swift in Sources */,
+				DEB306F32B61CF9500F9D46B /* FTS4Tests.swift in Sources */,
+				DEB306F42B61CF9500F9D46B /* ExpressionTests.swift in Sources */,
+				DEB306F52B61CF9500F9D46B /* StatementTests.swift in Sources */,
+				DEB306F62B61CF9500F9D46B /* QueryTests.swift in Sources */,
+				DEB306F72B61CF9500F9D46B /* CipherTests.swift in Sources */,
+				DEB306F82B61CF9500F9D46B /* BlobTests.swift in Sources */,
+				DEB306F92B61CF9500F9D46B /* ConnectionTests.swift in Sources */,
+				DEB306FA2B61CF9500F9D46B /* CoreFunctionsTests.swift in Sources */,
+				DEB306FB2B61CF9500F9D46B /* DateAndTimeFunctionTests.swift in Sources */,
+				DEB306FC2B61CF9500F9D46B /* CustomFunctionsTests.swift in Sources */,
+				DEB306FD2B61CF9500F9D46B /* OperatorsTests.swift in Sources */,
+				DEB306FE2B61CF9500F9D46B /* ResultTests.swift in Sources */,
+				DEB306FF2B61CF9500F9D46B /* RTreeTests.swift in Sources */,
+				DEB307002B61CF9500F9D46B /* SchemaTests.swift in Sources */,
+				DEB307012B61CF9500F9D46B /* SelectTests.swift in Sources */,
+				DEB307022B61CF9500F9D46B /* ValueTests.swift in Sources */,
+				DEB307032B61CF9500F9D46B /* QueryIntegrationTests.swift in Sources */,
+				DEB307042B61CF9500F9D46B /* AggregateFunctionsTests.swift in Sources */,
+				DEB307052B61CF9500F9D46B /* CustomAggregationTests.swift in Sources */,
+				DEB307062B61CF9500F9D46B /* SetterTests.swift in Sources */,
+				DEB307072B61CF9500F9D46B /* RowTests.swift in Sources */,
+				DEB307082B61CF9500F9D46B /* Connection+PragmaTests.swift in Sources */,
+				DEB307092B61CF9500F9D46B /* Connection+AttachTests.swift in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 		EE247ACE1C3F04ED00AE3E12 /* Sources */ = {
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
@@ -1232,6 +1472,11 @@
 			target = 03A65E591C6BB0F50062603F /* SQLite tvOS */;
 			targetProxy = 03A65E651C6BB0F60062603F /* PBXContainerItemProxy */;
 		};
+		DEB307152B61D07F00F9D46B /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			target = DEB306B82B61CEF500F9D46B /* SQLite visionOS */;
+			targetProxy = DEB307142B61D07F00F9D46B /* PBXContainerItemProxy */;
+		};
 		EE247AE01C3F04ED00AE3E12 /* PBXTargetDependency */ = {
 			isa = PBXTargetDependency;
 			target = EE247AD21C3F04ED00AE3E12 /* SQLite iOS */;
@@ -1365,6 +1610,83 @@
 			};
 			name = Release;
 		};
+		DEB306E32B61CEF500F9D46B /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				APPLICATION_EXTENSION_API_ONLY = YES;
+				CLANG_ENABLE_MODULES = YES;
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+				DEFINES_MODULE = YES;
+				DYLIB_COMPATIBILITY_VERSION = 1;
+				DYLIB_CURRENT_VERSION = 1;
+				DYLIB_INSTALL_NAME_BASE = "@rpath";
+				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
+				INFOPLIST_FILE = "$(SRCROOT)/Sources/SQLite/Info.plist";
+				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				MARKETING_VERSION = 0.14.0;
+				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
+				PRODUCT_NAME = SQLite;
+				SDKROOT = xros;
+				SKIP_INSTALL = YES;
+				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+				SWIFT_TREAT_WARNINGS_AS_ERRORS = YES;
+				TARGETED_DEVICE_FAMILY = 7;
+			};
+			name = Debug;
+		};
+		DEB306E42B61CEF500F9D46B /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				APPLICATION_EXTENSION_API_ONLY = YES;
+				CLANG_ENABLE_MODULES = YES;
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+				DEFINES_MODULE = YES;
+				DYLIB_COMPATIBILITY_VERSION = 1;
+				DYLIB_CURRENT_VERSION = 1;
+				DYLIB_INSTALL_NAME_BASE = "@rpath";
+				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
+				INFOPLIST_FILE = "$(SRCROOT)/Sources/SQLite/Info.plist";
+				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				MARKETING_VERSION = 0.14.0;
+				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
+				PRODUCT_NAME = SQLite;
+				SDKROOT = xros;
+				SKIP_INSTALL = YES;
+				SWIFT_TREAT_WARNINGS_AS_ERRORS = YES;
+				TARGETED_DEVICE_FAMILY = 7;
+			};
+			name = Release;
+		};
+		DEB3070F2B61CF9500F9D46B /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
+				INFOPLIST_FILE = Tests/SQLiteTests/Info.plist;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLiteTests;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SDKROOT = xros;
+				SWIFT_TREAT_WARNINGS_AS_ERRORS = YES;
+				TARGETED_DEVICE_FAMILY = 7;
+			};
+			name = Debug;
+		};
+		DEB307102B61CF9500F9D46B /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
+				INFOPLIST_FILE = Tests/SQLiteTests/Info.plist;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLiteTests;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SDKROOT = xros;
+				SWIFT_TREAT_WARNINGS_AS_ERRORS = YES;
+				TARGETED_DEVICE_FAMILY = 7;
+			};
+			name = Release;
+		};
 		EE247AE51C3F04ED00AE3E12 /* Debug */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
@@ -1502,7 +1824,6 @@
 				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
 				INFOPLIST_FILE = "$(SRCROOT)/Sources/SQLite/Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
-				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
 				MARKETING_VERSION = 0.14.0;
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
@@ -1526,7 +1847,6 @@
 				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
 				INFOPLIST_FILE = "$(SRCROOT)/Sources/SQLite/Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
-				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
 				MARKETING_VERSION = 0.14.0;
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
@@ -1541,7 +1861,6 @@
 			buildSettings = {
 				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
 				INFOPLIST_FILE = Tests/SQLiteTests/Info.plist;
-				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLiteTests;
 				PRODUCT_NAME = "$(TARGET_NAME)";
@@ -1554,7 +1873,6 @@
 			buildSettings = {
 				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
 				INFOPLIST_FILE = Tests/SQLiteTests/Info.plist;
-				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLiteTests;
 				PRODUCT_NAME = "$(TARGET_NAME)";
@@ -1674,6 +1992,24 @@
 			defaultConfigurationIsVisible = 0;
 			defaultConfigurationName = Release;
 		};
+		DEB306E22B61CEF500F9D46B /* Build configuration list for PBXNativeTarget "SQLite visionOS" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				DEB306E32B61CEF500F9D46B /* Debug */,
+				DEB306E42B61CEF500F9D46B /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		DEB3070E2B61CF9500F9D46B /* Build configuration list for PBXNativeTarget "SQLiteTests visionOS" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				DEB3070F2B61CF9500F9D46B /* Debug */,
+				DEB307102B61CF9500F9D46B /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
 		EE247ACD1C3F04ED00AE3E12 /* Build configuration list for PBXProject "SQLite" */ = {
 			isa = XCConfigurationList;
 			buildConfigurations = (
diff --git a/SQLite.xcodeproj/xcshareddata/xcschemes/SQLite visionOS.xcscheme b/SQLite.xcodeproj/xcshareddata/xcschemes/SQLite visionOS.xcscheme
new file mode 100644
index 00000000..58783573
--- /dev/null
+++ b/SQLite.xcodeproj/xcshareddata/xcschemes/SQLite visionOS.xcscheme	
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "1520"
+   version = "1.7">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "DEB306B82B61CEF500F9D46B"
+               BuildableName = "SQLite visionOS.framework"
+               BlueprintName = "SQLite visionOS"
+               ReferencedContainer = "container:SQLite.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+      <TestPlans>
+         <TestPlanReference
+            reference = "container:Tests/SQLite visionOS.xctestplan"
+            default = "YES">
+         </TestPlanReference>
+      </TestPlans>
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      allowLocationSimulation = "YES">
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "DEB306B82B61CEF500F9D46B"
+            BuildableName = "SQLite visionOS.framework"
+            BlueprintName = "SQLite visionOS"
+            ReferencedContainer = "container:SQLite.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
diff --git a/Tests/SQLite visionOS.xctestplan b/Tests/SQLite visionOS.xctestplan
new file mode 100644
index 00000000..9b4e90ed
--- /dev/null
+++ b/Tests/SQLite visionOS.xctestplan	
@@ -0,0 +1,24 @@
+{
+  "configurations" : [
+    {
+      "id" : "72B91FD4-441C-4C06-9E92-CAEDCB7325AB",
+      "name" : "Configuration 1",
+      "options" : {
+
+      }
+    }
+  ],
+  "defaultOptions" : {
+
+  },
+  "testTargets" : [
+    {
+      "target" : {
+        "containerPath" : "container:SQLite.xcodeproj",
+        "identifier" : "DEB306E72B61CF9500F9D46B",
+        "name" : "SQLiteTests visionOS"
+      }
+    }
+  ],
+  "version" : 1
+}

From 9043277c83fcddf18d325192616ec5214c3a50df Mon Sep 17 00:00:00 2001
From: Nathan Fallet <contact@nathanfallet.me>
Date: Tue, 6 Feb 2024 18:54:57 +0100
Subject: [PATCH 258/326] Using macOS 14 to build

---
 .github/workflows/build.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 9cc2f56e..d4fc87d8 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -5,7 +5,7 @@ env:
   IOS_VERSION: "16.2"
 jobs:
   build:
-    runs-on: macos-13
+    runs-on: macos-14
     steps:
     - uses: actions/checkout@v2
     - name: "Select Xcode"

From 623ac53da9a23e6e8d53acdcc07291b8496580c5 Mon Sep 17 00:00:00 2001
From: Nathan Fallet <contact@nathanfallet.me>
Date: Tue, 6 Feb 2024 18:56:12 +0100
Subject: [PATCH 259/326] Xcode 15?

---
 .github/workflows/build.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index d4fc87d8..ec25e7bf 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -13,7 +13,7 @@ jobs:
       # https://github.com/CocoaPods/CocoaPods/issues/11839
       run: |
         xcode-select -p
-        sudo xcode-select -s /Applications/Xcode_14.2.app/Contents/Developer
+        sudo xcode-select -s /Applications/Xcode_15.0.1.app/Contents/Developer
     - name: "Lint"
       run: make lint
     - name: "Run tests (PACKAGE_MANAGER_COMMAND: test)"

From 0c68b99da0e8378a0d43c7ad6911b98e437dd8c1 Mon Sep 17 00:00:00 2001
From: Nathan Fallet <contact@nathanfallet.me>
Date: Tue, 6 Feb 2024 19:00:02 +0100
Subject: [PATCH 260/326] Testing using iOS 17

---
 .github/workflows/build.yml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index ec25e7bf..7be89270 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -1,8 +1,8 @@
 name: Build and test
 on: [push, pull_request]
 env:
-  IOS_SIMULATOR: "iPhone 14"
-  IOS_VERSION: "16.2"
+  IOS_SIMULATOR: "iPhone 15"
+  IOS_VERSION: "17.2"
 jobs:
   build:
     runs-on: macos-14

From 03724bf3d28a16ac0197027d47956b8039c42e69 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Elfred=20Pag=C3=A1n?= <elfred@elfredpagan.com>
Date: Tue, 20 Feb 2024 20:06:37 -0600
Subject: [PATCH 261/326] make fromDatatypeValue throw

When using custom types, sometimes decoding can fail, say due to
changes in the type structure. In this case decoding would fail
and the only way to handle it is forcing a crash.

This change allows you to use `try row.get()` instead. Givng you
the chance to handle the mismatch.
---
 Sources/SQLite/Core/Value.swift        |  2 +-
 Sources/SQLite/Helpers.swift           |  4 ++--
 Sources/SQLite/Typed/Query.swift       | 12 +++++------
 Tests/SQLiteTests/Typed/RowTests.swift | 30 ++++++++++++++++++++++++++
 4 files changed, 39 insertions(+), 9 deletions(-)

diff --git a/Sources/SQLite/Core/Value.swift b/Sources/SQLite/Core/Value.swift
index 9c463f0c..249a7728 100644
--- a/Sources/SQLite/Core/Value.swift
+++ b/Sources/SQLite/Core/Value.swift
@@ -39,7 +39,7 @@ public protocol Value: Expressible { // extensions cannot have inheritance claus
 
     static var declaredDatatype: String { get }
 
-    static func fromDatatypeValue(_ datatypeValue: Datatype) -> ValueType
+    static func fromDatatypeValue(_ datatypeValue: Datatype) throws -> ValueType
 
     var datatypeValue: Datatype { get }
 
diff --git a/Sources/SQLite/Helpers.swift b/Sources/SQLite/Helpers.swift
index adfadc8a..c27ccf05 100644
--- a/Sources/SQLite/Helpers.swift
+++ b/Sources/SQLite/Helpers.swift
@@ -121,9 +121,9 @@ func transcode(_ literal: Binding?) -> String {
     }
 }
 
-// swiftlint:disable force_cast
+// swiftlint:disable force_cast force_try
 func value<A: Value>(_ binding: Binding) -> A {
-    A.fromDatatypeValue(binding as! A.Datatype) as! A
+    try! A.fromDatatypeValue(binding as! A.Datatype) as! A
 }
 
 func value<A: Value>(_ binding: Binding?) -> A {
diff --git a/Sources/SQLite/Typed/Query.swift b/Sources/SQLite/Typed/Query.swift
index c59b728f..2a83665c 100644
--- a/Sources/SQLite/Typed/Query.swift
+++ b/Sources/SQLite/Typed/Query.swift
@@ -1097,7 +1097,7 @@ extension Connection {
     public func scalar<V: Value>(_ query: ScalarQuery<V?>) throws -> V.ValueType? {
         let expression = query.expression
         guard let value = try scalar(expression.template, expression.bindings) as? V.Datatype else { return nil }
-        return V.fromDatatypeValue(value)
+        return try V.fromDatatypeValue(value)
     }
 
     public func scalar<V: Value>(_ query: Select<V>) throws -> V {
@@ -1108,7 +1108,7 @@ extension Connection {
     public func scalar<V: Value>(_ query: Select<V?>) throws -> V.ValueType? {
         let expression = query.expression
         guard let value = try scalar(expression.template, expression.bindings) as? V.Datatype else { return nil }
-        return V.fromDatatypeValue(value)
+        return try V.fromDatatypeValue(value)
     }
 
     public func pluck(_ query: QueryType) throws -> Row? {
@@ -1200,9 +1200,9 @@ public struct Row {
     }
 
     public func get<V: Value>(_ column: Expression<V?>) throws -> V? {
-        func valueAtIndex(_ idx: Int) -> V? {
+        func valueAtIndex(_ idx: Int) throws -> V? {
             guard let value = values[idx] as? V.Datatype else { return nil }
-            return V.fromDatatypeValue(value) as? V
+            return try V.fromDatatypeValue(value) as? V
         }
 
         guard let idx = columnNames[column.template] else {
@@ -1224,10 +1224,10 @@ public struct Row {
                     similar: columnNames.keys.filter(similar).sorted()
                 )
             }
-            return valueAtIndex(columnNames[firstIndex].value)
+            return try valueAtIndex(columnNames[firstIndex].value)
         }
 
-        return valueAtIndex(idx)
+        return try valueAtIndex(idx)
     }
 
     public subscript<T: Value>(column: Expression<T>) -> T {
diff --git a/Tests/SQLiteTests/Typed/RowTests.swift b/Tests/SQLiteTests/Typed/RowTests.swift
index 506f6b10..14fa373b 100644
--- a/Tests/SQLiteTests/Typed/RowTests.swift
+++ b/Tests/SQLiteTests/Typed/RowTests.swift
@@ -85,4 +85,34 @@ class RowTests: XCTestCase {
             }
         }
     }
+
+    public func test_get_datatype_throws() {
+        // swiftlint:disable nesting
+        struct MyType: Value {
+            enum MyError: Error {
+                case failed
+            }
+
+            public static var declaredDatatype: String {
+                Blob.declaredDatatype
+            }
+
+            public static func fromDatatypeValue(_ dataValue: Blob) throws -> Data {
+                throw MyError.failed
+            }
+
+            public var datatypeValue: Blob {
+                return Blob(bytes: [])
+            }
+        }
+
+        let row = Row(["\"foo\"": 0], [Blob(bytes: [])])
+        XCTAssertThrowsError(try row.get(Expression<MyType>("foo"))) { error in
+            if case MyType.MyError.failed = error {
+                XCTAssertTrue(true)
+            } else {
+                XCTFail("unexpected error: \(error)")
+            }
+        }
+    }
 }

From 5bfaddf455a152a276e534b24c98ff08c67d3f63 Mon Sep 17 00:00:00 2001
From: NathanFallet <contact@nathanfallet.me>
Date: Fri, 23 Feb 2024 13:50:42 +0100
Subject: [PATCH 262/326] fix: json tests (due to a swift upgrade)

---
 Tests/SQLiteTests/TestHelpers.swift      | 10 ++++++++++
 Tests/SQLiteTests/Typed/QueryTests.swift | 15 ++++++++++++---
 2 files changed, 22 insertions(+), 3 deletions(-)

diff --git a/Tests/SQLiteTests/TestHelpers.swift b/Tests/SQLiteTests/TestHelpers.swift
index 65cff8bb..e2da5927 100644
--- a/Tests/SQLiteTests/TestHelpers.swift
+++ b/Tests/SQLiteTests/TestHelpers.swift
@@ -105,6 +105,16 @@ func assertSQL(_ expression1: @autoclosure () -> String, _ expression2: @autoclo
     XCTAssertEqual(expression1(), expression2().asSQL(), file: file, line: line)
 }
 
+func extractAndReplace(_ value: String, regex: String, with replacement: String) -> (String, String) {
+    // We cannot use `Regex` because it is not available before iOS 16 :(
+    let regex = try! NSRegularExpression(pattern: regex)
+    let valueRange = NSRange(location: 0, length: value.utf16.count)
+    let match = regex.firstMatch(in: value, options: [], range: valueRange)!.range
+    let range = Range(match, in: value)!
+    let extractedValue = String(value[range])
+    return (value.replacingCharacters(in: range, with: replacement), extractedValue)
+}
+
 let table = Table("table")
 let qualifiedTable = Table("table", database: "main")
 let virtualTable = VirtualTable("virtual_table")
diff --git a/Tests/SQLiteTests/Typed/QueryTests.swift b/Tests/SQLiteTests/Typed/QueryTests.swift
index 82a33808..f698c621 100644
--- a/Tests/SQLiteTests/Typed/QueryTests.swift
+++ b/Tests/SQLiteTests/Typed/QueryTests.swift
@@ -364,13 +364,22 @@ class QueryTests: XCTestCase {
         let insert = try emails.insert(value)
         let encodedJSON = try JSONEncoder().encode(value1)
         let encodedJSONString = String(data: encodedJSON, encoding: .utf8)!
-        assertSQL(
+
+        let expectedSQL =
             """
             INSERT INTO \"emails\" (\"int\", \"string\", \"bool\", \"float\", \"double\", \"date\", \"uuid\", \"optional\",
              \"sub\") VALUES (1, '2', 1, 3.0, 4.0, '1970-01-01T00:00:00.000', 'E621E1F8-C36C-495A-93FC-0C247A3E6E5F',
              'optional', '\(encodedJSONString)')
-            """.replacingOccurrences(of: "\n", with: ""),
-            insert
+            """.replacingOccurrences(of: "\n", with: "")
+
+        // As JSON serialization gives a different result each time, we extract JSON and compare it by deserializing it
+        // and keep comparing the query but with the json replaced by the `JSON` string
+        let (expectedQuery, expectedJSON) = extractAndReplace(expectedSQL, regex: "\\{.*\\}", with: "JSON")
+        let (actualQuery, actualJSON) = extractAndReplace(insert.asSQL(), regex: "\\{.*\\}", with: "JSON")
+        XCTAssertEqual(expectedQuery, actualQuery)
+        XCTAssertEqual(
+            try JSONDecoder().decode(TestCodable.self, from: expectedJSON.data(using: .utf8)!),
+            try JSONDecoder().decode(TestCodable.self, from: actualJSON.data(using: .utf8)!)
         )
     }
     #endif

From 485b677b8991287bf9b91de2b7d18f3eff21c38e Mon Sep 17 00:00:00 2001
From: Nathan Fallet <nathan.fallet@gmail.com>
Date: Fri, 23 Feb 2024 14:26:22 +0100
Subject: [PATCH 263/326] Revert "Add privacy manifest"

---
 SQLite.xcodeproj/project.pbxproj     | 16 ----------------
 Sources/SQLite/PrivacyInfo.xcprivacy | 14 --------------
 2 files changed, 30 deletions(-)
 delete mode 100644 Sources/SQLite/PrivacyInfo.xcprivacy

diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj
index 8eb1df66..def32855 100644
--- a/SQLite.xcodeproj/project.pbxproj
+++ b/SQLite.xcodeproj/project.pbxproj
@@ -211,13 +211,6 @@
 		DB7C5DA728D7C9B6006395CF /* SQLiteVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB7C5DA528D7C9B6006395CF /* SQLiteVersion.swift */; };
 		DB7C5DA828D7C9B6006395CF /* SQLiteVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB7C5DA528D7C9B6006395CF /* SQLiteVersion.swift */; };
 		DB7C5DA928D7C9B6006395CF /* SQLiteVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB7C5DA528D7C9B6006395CF /* SQLiteVersion.swift */; };
-		EAE1E1542B473B0C0048B157 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = EAE1E1532B473B0C0048B157 /* PrivacyInfo.xcprivacy */; };
-		EAE1E1552B473B0C0048B157 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = EAE1E1532B473B0C0048B157 /* PrivacyInfo.xcprivacy */; };
-		EAE1E1562B473B0C0048B157 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = EAE1E1532B473B0C0048B157 /* PrivacyInfo.xcprivacy */; };
-		EAE1E1572B473B0C0048B157 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = EAE1E1532B473B0C0048B157 /* PrivacyInfo.xcprivacy */; };
-		EAE1E1582B473B0C0048B157 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = EAE1E1532B473B0C0048B157 /* PrivacyInfo.xcprivacy */; };
-		EAE1E1592B473B0C0048B157 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = EAE1E1532B473B0C0048B157 /* PrivacyInfo.xcprivacy */; };
-		EAE1E15A2B473B0C0048B157 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = EAE1E1532B473B0C0048B157 /* PrivacyInfo.xcprivacy */; };
 		EE247AD71C3F04ED00AE3E12 /* SQLite.h in Headers */ = {isa = PBXBuildFile; fileRef = EE247AD61C3F04ED00AE3E12 /* SQLite.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		EE247ADE1C3F04ED00AE3E12 /* SQLite.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EE247AD31C3F04ED00AE3E12 /* SQLite.framework */; };
 		EE247B031C3F06E900AE3E12 /* Blob.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AEE1C3F06E900AE3E12 /* Blob.swift */; };
@@ -347,7 +340,6 @@
 		DB58B21028FB864300F8EEA4 /* SchemaReader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SchemaReader.swift; sourceTree = "<group>"; };
 		DB58B21528FC7C4600F8EEA4 /* SQLiteFeature.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SQLiteFeature.swift; sourceTree = "<group>"; };
 		DB7C5DA528D7C9B6006395CF /* SQLiteVersion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SQLiteVersion.swift; sourceTree = "<group>"; };
-		EAE1E1532B473B0C0048B157 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
 		EE247AD31C3F04ED00AE3E12 /* SQLite.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SQLite.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		EE247AD61C3F04ED00AE3E12 /* SQLite.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SQLite.h; sourceTree = "<group>"; };
 		EE247AD81C3F04ED00AE3E12 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@@ -554,7 +546,6 @@
 				EE247AF71C3F06E900AE3E12 /* Foundation.swift */,
 				EE247AF81C3F06E900AE3E12 /* Helpers.swift */,
 				EE247AD81C3F04ED00AE3E12 /* Info.plist */,
-				EAE1E1532B473B0C0048B157 /* PrivacyInfo.xcprivacy */,
 				EE247AED1C3F06E900AE3E12 /* Core */,
 				EE247AF41C3F06E900AE3E12 /* Extensions */,
 				EE247AF91C3F06E900AE3E12 /* Typed */,
@@ -901,7 +892,6 @@
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				EAE1E1582B473B0C0048B157 /* PrivacyInfo.xcprivacy in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -909,7 +899,6 @@
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				EAE1E1592B473B0C0048B157 /* PrivacyInfo.xcprivacy in Resources */,
 				3DF7B79828846FED005DD8CA /* Resources in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
@@ -918,7 +907,6 @@
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				EAE1E15A2B473B0C0048B157 /* PrivacyInfo.xcprivacy in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -926,7 +914,6 @@
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				EAE1E1542B473B0C0048B157 /* PrivacyInfo.xcprivacy in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -934,7 +921,6 @@
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				EAE1E1552B473B0C0048B157 /* PrivacyInfo.xcprivacy in Resources */,
 				3DF7B79628846FCC005DD8CA /* Resources in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
@@ -943,7 +929,6 @@
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				EAE1E1562B473B0C0048B157 /* PrivacyInfo.xcprivacy in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -951,7 +936,6 @@
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				EAE1E1572B473B0C0048B157 /* PrivacyInfo.xcprivacy in Resources */,
 				3DF7B79928847055005DD8CA /* Resources in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
diff --git a/Sources/SQLite/PrivacyInfo.xcprivacy b/Sources/SQLite/PrivacyInfo.xcprivacy
deleted file mode 100644
index 987771fa..00000000
--- a/Sources/SQLite/PrivacyInfo.xcprivacy
+++ /dev/null
@@ -1,14 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-	<key>NSPrivacyTrackingDomains</key>
-	<array/>
-	<key>NSPrivacyCollectedDataTypes</key>
-	<array/>
-	<key>NSPrivacyAccessedAPITypes</key>
-	<array/>
-	<key>NSPrivacyTracking</key>
-	<false/>
-</dict>
-</plist>

From fec58b90b7a340377d16dfcce89027a6b6f81e26 Mon Sep 17 00:00:00 2001
From: NathanFallet <contact@nathanfallet.me>
Date: Fri, 23 Feb 2024 14:47:00 +0100
Subject: [PATCH 264/326] fix: ios deployment target

---
 SQLite.swift.podspec                                          | 2 +-
 SQLite.xcodeproj/project.pbxproj                              | 4 ++--
 .../xcshareddata/xcschemes/SQLite visionOS.xcscheme           | 4 ++--
 3 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/SQLite.swift.podspec b/SQLite.swift.podspec
index 4edddd67..b8aaaa8f 100644
--- a/SQLite.swift.podspec
+++ b/SQLite.swift.podspec
@@ -18,7 +18,7 @@ Pod::Spec.new do |s|
   s.default_subspec  = 'standard'
   s.swift_versions = ['5']
 
-  ios_deployment_target = '11.0'
+  ios_deployment_target = '17.0'
   tvos_deployment_target = '11.0'
   osx_deployment_target = '10.13'
   watchos_deployment_target = '4.0'
diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj
index 857c6fd9..d23b6d64 100644
--- a/SQLite.xcodeproj/project.pbxproj
+++ b/SQLite.xcodeproj/project.pbxproj
@@ -1736,7 +1736,7 @@
 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
-				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
+				IPHONEOS_DEPLOYMENT_TARGET = 17.0;
 				MACOSX_DEPLOYMENT_TARGET = 10.13;
 				MTL_ENABLE_DEBUG_INFO = YES;
 				ONLY_ACTIVE_ARCH = YES;
@@ -1795,7 +1795,7 @@
 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
-				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
+				IPHONEOS_DEPLOYMENT_TARGET = 17.0;
 				MACOSX_DEPLOYMENT_TARGET = 10.13;
 				MTL_ENABLE_DEBUG_INFO = NO;
 				PRODUCT_NAME = "";
diff --git a/SQLite.xcodeproj/xcshareddata/xcschemes/SQLite visionOS.xcscheme b/SQLite.xcodeproj/xcshareddata/xcschemes/SQLite visionOS.xcscheme
index 58783573..c1536b66 100644
--- a/SQLite.xcodeproj/xcshareddata/xcschemes/SQLite visionOS.xcscheme	
+++ b/SQLite.xcodeproj/xcshareddata/xcschemes/SQLite visionOS.xcscheme	
@@ -15,7 +15,7 @@
             <BuildableReference
                BuildableIdentifier = "primary"
                BlueprintIdentifier = "DEB306B82B61CEF500F9D46B"
-               BuildableName = "SQLite visionOS.framework"
+               BuildableName = "SQLite.framework"
                BlueprintName = "SQLite visionOS"
                ReferencedContainer = "container:SQLite.xcodeproj">
             </BuildableReference>
@@ -55,7 +55,7 @@
          <BuildableReference
             BuildableIdentifier = "primary"
             BlueprintIdentifier = "DEB306B82B61CEF500F9D46B"
-            BuildableName = "SQLite visionOS.framework"
+            BuildableName = "SQLite.framework"
             BlueprintName = "SQLite visionOS"
             ReferencedContainer = "container:SQLite.xcodeproj">
          </BuildableReference>

From ce125ce64c03e9fcd142f18aca254637c223be8c Mon Sep 17 00:00:00 2001
From: NathanFallet <contact@nathanfallet.me>
Date: Fri, 23 Feb 2024 15:37:04 +0100
Subject: [PATCH 265/326] fix: deployment targets?

---
 SQLite.swift.podspec | 21 ++++++++++++++++++---
 1 file changed, 18 insertions(+), 3 deletions(-)

diff --git a/SQLite.swift.podspec b/SQLite.swift.podspec
index b8aaaa8f..2a75f169 100644
--- a/SQLite.swift.podspec
+++ b/SQLite.swift.podspec
@@ -19,9 +19,9 @@ Pod::Spec.new do |s|
   s.swift_versions = ['5']
 
   ios_deployment_target = '17.0'
-  tvos_deployment_target = '11.0'
-  osx_deployment_target = '10.13'
-  watchos_deployment_target = '4.0'
+  tvos_deployment_target = '17.0'
+  osx_deployment_target = '14.0'
+  watchos_deployment_target = '10.0'
 
   s.ios.deployment_target = ios_deployment_target
   s.tvos.deployment_target = tvos_deployment_target
@@ -32,6 +32,11 @@ Pod::Spec.new do |s|
     ss.source_files = 'Sources/SQLite/**/*.{c,h,m,swift}'
     ss.exclude_files = 'Sources/**/Cipher.swift'
     ss.library = 'sqlite3'
+    
+    ss.ios.deployment_target = ios_deployment_target
+    ss.tvos.deployment_target = tvos_deployment_target
+    ss.osx.deployment_target = osx_deployment_target
+    ss.watchos.deployment_target = watchos_deployment_target
 
     ss.test_spec 'tests' do |test_spec|
       test_spec.resources = 'Tests/SQLiteTests/Resources/*'
@@ -51,6 +56,11 @@ Pod::Spec.new do |s|
       'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) SQLITE_SWIFT_STANDALONE=1'
     }
     ss.dependency 'sqlite3'
+    
+    ss.ios.deployment_target = ios_deployment_target
+    ss.tvos.deployment_target = tvos_deployment_target
+    ss.osx.deployment_target = osx_deployment_target
+    ss.watchos.deployment_target = watchos_deployment_target
 
     ss.test_spec 'tests' do |test_spec|
       test_spec.resources = 'Tests/SQLiteTests/Resources/*'
@@ -68,6 +78,11 @@ Pod::Spec.new do |s|
       'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) SQLITE_HAS_CODEC=1 SQLITE_SWIFT_SQLCIPHER=1'
     }
     ss.dependency 'SQLCipher', '>= 4.0.0'
+    
+    ss.ios.deployment_target = ios_deployment_target
+    ss.tvos.deployment_target = tvos_deployment_target
+    ss.osx.deployment_target = osx_deployment_target
+    ss.watchos.deployment_target = watchos_deployment_target
 
     ss.test_spec 'tests' do |test_spec|
       test_spec.resources = 'Tests/SQLiteTests/Resources/*'

From e7813517c80c5bd79d4e7029ec8a89121f5afe60 Mon Sep 17 00:00:00 2001
From: ivan <ivanlares1@gmail.com>
Date: Fri, 23 Feb 2024 12:51:28 -0800
Subject: [PATCH 266/326] Add privacy manifest

- fixes build issue https://github.com/stephencelis/SQLite.swift/actions/runs/8019552944/job/21907598856
---
 SQLite.xcodeproj/project.pbxproj     | 10 ++++++++++
 Sources/SQLite/PrivacyInfo.xcprivacy | 14 ++++++++++++++
 2 files changed, 24 insertions(+)
 create mode 100644 Sources/SQLite/PrivacyInfo.xcprivacy

diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj
index 37c8a802..0b246a00 100644
--- a/SQLite.xcodeproj/project.pbxproj
+++ b/SQLite.xcodeproj/project.pbxproj
@@ -221,6 +221,10 @@
 		DBB93D5A2A22A373009BB96E /* SchemaReaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBB93D592A22A373009BB96E /* SchemaReaderTests.swift */; };
 		DBB93D5B2A22A373009BB96E /* SchemaReaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBB93D592A22A373009BB96E /* SchemaReaderTests.swift */; };
 		DBB93D5C2A22A373009BB96E /* SchemaReaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBB93D592A22A373009BB96E /* SchemaReaderTests.swift */; };
+		EAE5A0372B893C43007C7EA4 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = EAE5A0362B893C43007C7EA4 /* PrivacyInfo.xcprivacy */; };
+		EAE5A0382B893C43007C7EA4 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = EAE5A0362B893C43007C7EA4 /* PrivacyInfo.xcprivacy */; };
+		EAE5A0392B893C43007C7EA4 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = EAE5A0362B893C43007C7EA4 /* PrivacyInfo.xcprivacy */; };
+		EAE5A03A2B893C43007C7EA4 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = EAE5A0362B893C43007C7EA4 /* PrivacyInfo.xcprivacy */; };
 		EE247AD71C3F04ED00AE3E12 /* SQLite.h in Headers */ = {isa = PBXBuildFile; fileRef = EE247AD61C3F04ED00AE3E12 /* SQLite.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		EE247ADE1C3F04ED00AE3E12 /* SQLite.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EE247AD31C3F04ED00AE3E12 /* SQLite.framework */; };
 		EE247B031C3F06E900AE3E12 /* Blob.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AEE1C3F06E900AE3E12 /* Blob.swift */; };
@@ -353,6 +357,7 @@
 		DB58B21528FC7C4600F8EEA4 /* SQLiteFeature.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SQLiteFeature.swift; sourceTree = "<group>"; };
 		DB7C5DA528D7C9B6006395CF /* SQLiteVersion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SQLiteVersion.swift; sourceTree = "<group>"; };
 		DBB93D592A22A373009BB96E /* SchemaReaderTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SchemaReaderTests.swift; sourceTree = "<group>"; };
+		EAE5A0362B893C43007C7EA4 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
 		EE247AD31C3F04ED00AE3E12 /* SQLite.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SQLite.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		EE247AD61C3F04ED00AE3E12 /* SQLite.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SQLite.h; sourceTree = "<group>"; };
 		EE247AD81C3F04ED00AE3E12 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@@ -560,6 +565,7 @@
 				EE247AD61C3F04ED00AE3E12 /* SQLite.h */,
 				EE247AF71C3F06E900AE3E12 /* Foundation.swift */,
 				EE247AF81C3F06E900AE3E12 /* Helpers.swift */,
+				EAE5A0362B893C43007C7EA4 /* PrivacyInfo.xcprivacy */,
 				EE247AD81C3F04ED00AE3E12 /* Info.plist */,
 				EE247AED1C3F06E900AE3E12 /* Core */,
 				EE247AF41C3F06E900AE3E12 /* Extensions */,
@@ -908,6 +914,7 @@
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				EAE5A0392B893C43007C7EA4 /* PrivacyInfo.xcprivacy in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -923,6 +930,7 @@
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				EAE5A03A2B893C43007C7EA4 /* PrivacyInfo.xcprivacy in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -930,6 +938,7 @@
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				EAE5A0372B893C43007C7EA4 /* PrivacyInfo.xcprivacy in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -945,6 +954,7 @@
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				EAE5A0382B893C43007C7EA4 /* PrivacyInfo.xcprivacy in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
diff --git a/Sources/SQLite/PrivacyInfo.xcprivacy b/Sources/SQLite/PrivacyInfo.xcprivacy
new file mode 100644
index 00000000..987771fa
--- /dev/null
+++ b/Sources/SQLite/PrivacyInfo.xcprivacy
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>NSPrivacyTrackingDomains</key>
+	<array/>
+	<key>NSPrivacyCollectedDataTypes</key>
+	<array/>
+	<key>NSPrivacyAccessedAPITypes</key>
+	<array/>
+	<key>NSPrivacyTracking</key>
+	<false/>
+</dict>
+</plist>

From e78ae0220e17525a15ac68c697a155eb7a672a8e Mon Sep 17 00:00:00 2001
From: NathanFallet <contact@nathanfallet.me>
Date: Sat, 24 Feb 2024 11:51:41 +0100
Subject: [PATCH 267/326] changelogs

---
 CHANGELOG.md            | 33 ++++++++++++++++++++++++++++++--
 Documentation/Index.md  | 42 ++++++++++++++++++++++++++++++-----------
 README.md               |  4 ++--
 SQLite.swift.podspec    |  2 +-
 Tests/SPM/Package.swift |  2 +-
 5 files changed, 66 insertions(+), 17 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 970b4ca1..4c5c1fba 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,20 @@
-0.15.0 (unreleased)
+0.15.0 (24-02-2024), [diff][diff-0.15.0]
 ========================================
 
+* Fix incorrect behavior when preparing `SELECT *` preceded by a `WITH` ([#1179][])
+* Adds support for returning extended error codes ([#1178][])
+* Fix typos ([#1182][])
+* fix Xcode build error ([#1192][])
+* Make the IndexDefinition properties public ([#1196][])
+* Fix GitHub Actions build badge ([#1200][])
+* Run CI on macOS 13 ([#1206][])
+* SchemaReader: return the correct column definition for a composite primary key ([#1217][])
+* Add optional support for decoding ([#1224][])
+* make fromDatatypeValue throw ([#1242][])
+* Implements built-in window functions ([#1228][])
+* Fix column affinity parsing to match how SQLite determines affinity ([#1218][])
+* Handle FK definitions w/o key references ([#1210][])
+* Add privacy manifest ([#1245][])
 * New minimum deployment targets: iOS/tvOS 11.0, watchOS 4.0
 
 0.14.1 (01-11-2022), [diff][diff-0.14.1]
@@ -14,7 +28,7 @@ For breaking changes, see [Upgrading.md](Documentation/Upgrading.md).
 
 * Support more complex schema changes and queries ([#1073][], [#1146][] [#1148][])
 * Support `ATTACH`/`DETACH` ([#30][], [#1142][])
-* Expose connection flags (via `URIQueryParameter`) to open db ([#1074][]))
+* Expose connection flags (via `URIQueryParameter`) to open db ([#1074][])
 * Support `WITH` clause ([#1139][])
 * Add `Value` conformance for `NSURL` ([#1110][], [#1141][])
 * Add decoding for `UUID` ([#1137][])
@@ -140,6 +154,7 @@ For breaking changes, see [Upgrading.md](Documentation/Upgrading.md).
 [diff-0.13.3]: https://github.com/stephencelis/SQLite.swift/compare/0.13.2...0.13.3
 [diff-0.14.0]: https://github.com/stephencelis/SQLite.swift/compare/0.13.3...0.14.0
 [diff-0.14.1]: https://github.com/stephencelis/SQLite.swift/compare/0.14.0...0.14.1
+[diff-0.15.0]: https://github.com/stephencelis/SQLite.swift/compare/0.14.0...0.15.0
 
 [#30]: https://github.com/stephencelis/SQLite.swift/issues/30
 [#142]: https://github.com/stephencelis/SQLite.swift/issues/142
@@ -206,3 +221,17 @@ For breaking changes, see [Upgrading.md](Documentation/Upgrading.md).
 [#1146]: https://github.com/stephencelis/SQLite.swift/pull/1146
 [#1148]: https://github.com/stephencelis/SQLite.swift/pull/1148
 [#1167]: https://github.com/stephencelis/SQLite.swift/pull/1167
+[#1179]: https://github.com/stephencelis/SQLite.swift/pull/1179
+[#1178]: https://github.com/stephencelis/SQLite.swift/pull/1178
+[#1182]: https://github.com/stephencelis/SQLite.swift/pull/1182
+[#1192]: https://github.com/stephencelis/SQLite.swift/pull/1192
+[#1196]: https://github.com/stephencelis/SQLite.swift/pull/1196
+[#1200]: https://github.com/stephencelis/SQLite.swift/pull/1200
+[#1206]: https://github.com/stephencelis/SQLite.swift/pull/1206
+[#1217]: https://github.com/stephencelis/SQLite.swift/pull/1217
+[#1224]: https://github.com/stephencelis/SQLite.swift/pull/1224
+[#1242]: https://github.com/stephencelis/SQLite.swift/pull/1242
+[#1228]: https://github.com/stephencelis/SQLite.swift/pull/1228
+[#1218]: https://github.com/stephencelis/SQLite.swift/pull/1218
+[#1210]: https://github.com/stephencelis/SQLite.swift/pull/1210
+[#1245]: https://github.com/stephencelis/SQLite.swift/pull/1245
diff --git a/Documentation/Index.md b/Documentation/Index.md
index a03c26a3..7f900bc5 100644
--- a/Documentation/Index.md
+++ b/Documentation/Index.md
@@ -1,21 +1,24 @@
 # SQLite.swift Documentation
 
+- [SQLite.swift Documentation](#sqliteswift-documentation)
   - [Installation](#installation)
     - [Swift Package Manager](#swift-package-manager)
     - [Carthage](#carthage)
     - [CocoaPods](#cocoapods)
+      - [Requiring a specific version of SQLite](#requiring-a-specific-version-of-sqlite)
+      - [Using SQLite.swift with SQLCipher](#using-sqliteswift-with-sqlcipher)
     - [Manual](#manual)
   - [Getting Started](#getting-started)
     - [Connecting to a Database](#connecting-to-a-database)
       - [Read-Write Databases](#read-write-databases)
       - [Read-Only Databases](#read-only-databases)
-      - [In a Shared Group Container](#in-a-shared-group-container)
+      - [In a shared group container](#in-a-shared-group-container)
       - [In-Memory Databases](#in-memory-databases)
       - [URI parameters](#uri-parameters)
       - [Thread-Safety](#thread-safety)
   - [Building Type-Safe SQL](#building-type-safe-sql)
     - [Expressions](#expressions)
-      - [Compound Expressions](#compound-expressions)
+    - [Compound Expressions](#compound-expressions)
     - [Queries](#queries)
   - [Creating a Table](#creating-a-table)
     - [Create Table Options](#create-table-options)
@@ -24,8 +27,11 @@
   - [Inserting Rows](#inserting-rows)
     - [Handling SQLite errors](#handling-sqlite-errors)
     - [Setters](#setters)
+          - [Infix Setters](#infix-setters)
+          - [Postfix Setters](#postfix-setters)
   - [Selecting Rows](#selecting-rows)
     - [Iterating and Accessing Values](#iterating-and-accessing-values)
+      - [Failable iteration](#failable-iteration)
     - [Plucking Rows](#plucking-rows)
     - [Building Complex Queries](#building-complex-queries)
       - [Selecting Columns](#selecting-columns)
@@ -34,6 +40,9 @@
         - [Table Aliasing](#table-aliasing)
       - [Filtering Rows](#filtering-rows)
         - [Filter Operators and Functions](#filter-operators-and-functions)
+          - [Infix Filter Operators](#infix-filter-operators)
+          - [Prefix Filter Operators](#prefix-filter-operators)
+          - [Filtering Functions](#filtering-functions)
       - [Sorting Rows](#sorting-rows)
       - [Limiting and Paging Results](#limiting-and-paging-results)
       - [Recursive and Hierarchical Queries](#recursive-and-hierarchical-queries)
@@ -43,13 +52,14 @@
   - [Deleting Rows](#deleting-rows)
   - [Transactions and Savepoints](#transactions-and-savepoints)
   - [Querying the Schema](#querying-the-schema)
+    - [Indexes and Columns](#indexes-and-columns)
   - [Altering the Schema](#altering-the-schema)
     - [Renaming Tables](#renaming-tables)
     - [Dropping Tables](#dropping-tables)
     - [Adding Columns](#adding-columns)
       - [Added Column Constraints](#added-column-constraints)
-    - [Schema Changer](#schemachanger)
-      - [Adding Columns](#adding-columns)
+    - [SchemaChanger](#schemachanger)
+      - [Adding Columns](#adding-columns-1)
       - [Renaming Columns](#renaming-columns)
       - [Dropping Columns](#dropping-columns)
       - [Renaming/Dropping Tables](#renamingdropping-tables)
@@ -61,17 +71,27 @@
     - [Date-Time Values](#date-time-values)
     - [Binary Data](#binary-data)
   - [Codable Types](#codable-types)
+    - [Inserting Codable Types](#inserting-codable-types)
+    - [Updating Codable Types](#updating-codable-types)
+    - [Retrieving Codable Types](#retrieving-codable-types)
+    - [Restrictions](#restrictions)
   - [Other Operators](#other-operators)
+          - [Other Infix Operators](#other-infix-operators)
+          - [Other Prefix Operators](#other-prefix-operators)
   - [Core SQLite Functions](#core-sqlite-functions)
   - [Aggregate SQLite Functions](#aggregate-sqlite-functions)
   - [Window SQLite Functions](#window-sqlite-functions)
-  - [Date and Time Functions](#date-and-time-functions)
+  - [Date and Time functions](#date-and-time-functions)
   - [Custom SQL Functions](#custom-sql-functions)
+  - [Custom Aggregations](#custom-aggregations)
   - [Custom Collations](#custom-collations)
   - [Full-text Search](#full-text-search)
+    - [FTS5](#fts5)
   - [Executing Arbitrary SQL](#executing-arbitrary-sql)
+  - [Online Database Backup](#online-database-backup)
   - [Attaching and detaching databases](#attaching-and-detaching-databases)
   - [Logging](#logging)
+  - [Vacuum](#vacuum)
 
 [↩]: #sqliteswift-documentation
 
@@ -88,7 +108,7 @@ process of downloading, compiling, and linking dependencies.
 
   ```swift
   dependencies: [
-    .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.14.1")
+    .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.15.0")
   ]
   ```
 
@@ -109,7 +129,7 @@ install SQLite.swift with Carthage:
  2. Update your Cartfile to include the following:
 
     ```ruby
-    github "stephencelis/SQLite.swift" ~> 0.14.1
+    github "stephencelis/SQLite.swift" ~> 0.15.0
     ```
 
  3. Run `carthage update` and [add the appropriate framework][Carthage Usage].
@@ -139,7 +159,7 @@ install SQLite.swift with Carthage:
     use_frameworks!
 
     target 'YourAppTargetName' do
-        pod 'SQLite.swift', '~> 0.14.1'
+        pod 'SQLite.swift', '~> 0.15.0'
     end
     ```
 
@@ -153,7 +173,7 @@ with the OS you can require the `standalone` subspec:
 
 ```ruby
 target 'YourAppTargetName' do
-  pod 'SQLite.swift/standalone', '~> 0.14.1'
+  pod 'SQLite.swift/standalone', '~> 0.15.0'
 end
 ```
 
@@ -163,7 +183,7 @@ dependency to sqlite3 or one of its subspecs:
 
 ```ruby
 target 'YourAppTargetName' do
-  pod 'SQLite.swift/standalone', '~> 0.14.1'
+  pod 'SQLite.swift/standalone', '~> 0.15.0'
   pod 'sqlite3/fts5', '= 3.15.0'  # SQLite 3.15.0 with FTS5 enabled
 end
 ```
@@ -179,7 +199,7 @@ If you want to use [SQLCipher][] with SQLite.swift you can require the
 target 'YourAppTargetName' do
   # Make sure you only require the subspec, otherwise you app might link against
   # the system SQLite, which means the SQLCipher-specific methods won't work.
-  pod 'SQLite.swift/SQLCipher', '~> 0.14.1'
+  pod 'SQLite.swift/SQLCipher', '~> 0.15.0'
 end
 ```
 
diff --git a/README.md b/README.md
index e6e5a894..117edb5a 100644
--- a/README.md
+++ b/README.md
@@ -128,7 +128,7 @@ Swift code.
 
   ```swift
   dependencies: [
-      .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.14.1")
+      .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.15.0")
   ]
   ```
 
@@ -152,7 +152,7 @@ install SQLite.swift with Carthage:
  2. Update your Cartfile to include the following:
 
     ```ruby
-    github "stephencelis/SQLite.swift" ~> 0.14.1
+    github "stephencelis/SQLite.swift" ~> 0.15.0
     ```
 
  3. Run `carthage update` and
diff --git a/SQLite.swift.podspec b/SQLite.swift.podspec
index 4edddd67..b691aeab 100644
--- a/SQLite.swift.podspec
+++ b/SQLite.swift.podspec
@@ -1,6 +1,6 @@
 Pod::Spec.new do |s|
   s.name             = "SQLite.swift"
-  s.version          = "0.14.1"
+  s.version          = "0.15.0"
   s.summary          = "A type-safe, Swift-language layer over SQLite3."
 
   s.description      = <<-DESC
diff --git a/Tests/SPM/Package.swift b/Tests/SPM/Package.swift
index 3c446ae6..c82b3b9e 100644
--- a/Tests/SPM/Package.swift
+++ b/Tests/SPM/Package.swift
@@ -15,7 +15,7 @@ let package = Package(
         // for testing from same repository
         .package(path: "../..")
         // normally this would be:
-        // .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.14.1")
+        // .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.15.0")
     ],
     targets: [
         .executableTarget(name: "test", dependencies: [.product(name: "SQLite", package: "SQLite.swift")])

From 20ebe9b334ff557c1166e26ca6c89e94a77a7f2d Mon Sep 17 00:00:00 2001
From: Chris Stockbridge <chris.stockbridge@vantaca.com>
Date: Mon, 4 Mar 2024 20:07:22 -0500
Subject: [PATCH 268/326] Adding failing test that includes an optional struct

This test passes with release 0.14.1, but fails starting with 0.15
---
 .../Typed/QueryIntegrationTests.swift         | 27 +++++++++++++++++++
 1 file changed, 27 insertions(+)

diff --git a/Tests/SQLiteTests/Typed/QueryIntegrationTests.swift b/Tests/SQLiteTests/Typed/QueryIntegrationTests.swift
index aa45cafe..7ac72d0c 100644
--- a/Tests/SQLiteTests/Typed/QueryIntegrationTests.swift
+++ b/Tests/SQLiteTests/Typed/QueryIntegrationTests.swift
@@ -152,6 +152,33 @@ class QueryIntegrationTests: SQLiteTestCase {
          XCTAssertEqual(values.count, 2)
     }
 
+    func test_insert_custom_encodable_type() throws {
+        struct TestTypeWithOptionalArray: Codable {
+            var myInt: Int
+            var myString: String
+            var myOptionalArray: [Int]?
+        }
+
+        let table = Table("custom_codable")
+        try db.run(table.create { builder in
+            builder.column(Expression<Int?>("myInt"))
+            builder.column(Expression<String?>("myString"))
+            builder.column(Expression<String?>("myOptionalArray"))
+        })
+
+        let customType = TestTypeWithOptionalArray(myInt: 13, myString: "foo", myOptionalArray: [1, 2, 3])
+        try db.run(table.insert(customType))
+        let rows = try db.prepare(table)
+        let values: [TestTypeWithOptionalArray] = try rows.map({ try $0.decode() })
+        XCTAssertEqual(values.count, 1, "return one optional custom type")
+
+        let customTypeWithNil = TestTypeWithOptionalArray(myInt: 123, myString: "String", myOptionalArray: nil)
+        try db.run(table.insert(customTypeWithNil))
+        let rowsNil = try db.prepare(table)
+        let valuesNil: [TestTypeWithOptionalArray] = try rowsNil.map({ try $0.decode() })
+        XCTAssertEqual(valuesNil.count, 2, "return two custom objects, including one that contains a nil optional")
+    }
+
     func test_upsert() throws {
         try XCTSkipUnless(db.satisfiesMinimumVersion(minor: 24))
         let fetchAge = { () throws -> Int? in

From bad16cb5d1a09e93bbdb40f50df5f222920a10e3 Mon Sep 17 00:00:00 2001
From: Chris Stockbridge <chris.stockbridge@vantaca.com>
Date: Tue, 5 Mar 2024 11:11:50 -0500
Subject: [PATCH 269/326] Bugfix: returning nil when decoding an optional
 should not throw an error

---
 Sources/SQLite/Typed/Coding.swift | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/Sources/SQLite/Typed/Coding.swift b/Sources/SQLite/Typed/Coding.swift
index bfddc5ee..d0061851 100644
--- a/Sources/SQLite/Typed/Coding.swift
+++ b/Sources/SQLite/Typed/Coding.swift
@@ -504,9 +504,8 @@ private class SQLiteDecoder: Decoder {
             case is UUID.Type:
                 return try? row.get(Expression<UUID>(key.stringValue)) as? T
             default:
-                guard let JSONString = try? row.get(Expression<String?>(key.stringValue)) else {
-                    throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: codingPath,
-                                                                                 debugDescription: "an unsupported type was found"))
+                guard let JSONString = try row.get(Expression<String?>(key.stringValue)) else {
+                    return nil
                 }
                 guard let data = JSONString.data(using: .utf8) else {
                     throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: codingPath,

From cda8a1274a5c426c043bea6d381fddab630dad95 Mon Sep 17 00:00:00 2001
From: Steven <tongwanglin@gmail.com>
Date: Sun, 17 Mar 2024 01:10:17 +0800
Subject: [PATCH 270/326] Update CoreFunctions.swift fix typo

fix lower to upper
---
 Sources/SQLite/Typed/CoreFunctions.swift | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Sources/SQLite/Typed/CoreFunctions.swift b/Sources/SQLite/Typed/CoreFunctions.swift
index 4429bb5f..c4359d8b 100644
--- a/Sources/SQLite/Typed/CoreFunctions.swift
+++ b/Sources/SQLite/Typed/CoreFunctions.swift
@@ -224,7 +224,7 @@ extension ExpressionType where UnderlyingType == String {
     ///
     ///     let name = Expression<String>("name")
     ///     name.uppercaseString
-    ///     // lower("name")
+    ///     // upper("name")
     ///
     /// - Returns: A copy of the expression wrapped with the `upper` function.
     public var uppercaseString: Expression<UnderlyingType> {

From 6d821f83499500cb7bb7f258655fe83ac6f890ed Mon Sep 17 00:00:00 2001
From: Sagar Dagdu <shags032@gmail.com>
Date: Sat, 13 Apr 2024 08:23:49 +0530
Subject: [PATCH 271/326] Add dependency on custom cocoapods fork

---
 Gemfile      |   1 +
 Gemfile.lock | 116 +++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 117 insertions(+)
 create mode 100644 Gemfile
 create mode 100644 Gemfile.lock

diff --git a/Gemfile b/Gemfile
new file mode 100644
index 00000000..2f97c391
--- /dev/null
+++ b/Gemfile
@@ -0,0 +1 @@
+gem 'cocoapods', :git => 'https://github.com/SagarSDagdu/CocoaPods.git', tag: '1.15.2.1-sagard'
diff --git a/Gemfile.lock b/Gemfile.lock
new file mode 100644
index 00000000..75374a30
--- /dev/null
+++ b/Gemfile.lock
@@ -0,0 +1,116 @@
+GIT
+  remote: https://github.com/SagarSDagdu/CocoaPods.git
+  revision: d96f491f79abd2804d1359c5228cce404dd365b7
+  tag: 1.15.2.1-sagard
+  specs:
+    cocoapods (1.15.2.1.pre.sagard)
+      addressable (~> 2.8)
+      claide (>= 1.0.2, < 2.0)
+      cocoapods-core (= 1.15.2)
+      cocoapods-deintegrate (>= 1.0.3, < 2.0)
+      cocoapods-downloader (>= 2.1, < 3.0)
+      cocoapods-plugins (>= 1.0.0, < 2.0)
+      cocoapods-search (>= 1.0.0, < 2.0)
+      cocoapods-trunk (>= 1.6.0, < 2.0)
+      cocoapods-try (>= 1.1.0, < 2.0)
+      colored2 (~> 3.1)
+      escape (~> 0.0.4)
+      fourflusher (>= 2.3.0, < 3.0)
+      gh_inspector (~> 1.0)
+      molinillo (~> 0.8.0)
+      nap (~> 1.0)
+      ruby-macho (>= 2.3.0, < 3.0)
+      xcodeproj (>= 1.23.0, < 2.0)
+
+GEM
+  specs:
+    CFPropertyList (3.0.7)
+      base64
+      nkf
+      rexml
+    activesupport (7.1.3.2)
+      base64
+      bigdecimal
+      concurrent-ruby (~> 1.0, >= 1.0.2)
+      connection_pool (>= 2.2.5)
+      drb
+      i18n (>= 1.6, < 2)
+      minitest (>= 5.1)
+      mutex_m
+      tzinfo (~> 2.0)
+    addressable (2.8.6)
+      public_suffix (>= 2.0.2, < 6.0)
+    algoliasearch (1.27.5)
+      httpclient (~> 2.8, >= 2.8.3)
+      json (>= 1.5.1)
+    atomos (0.1.3)
+    base64 (0.2.0)
+    bigdecimal (3.1.5)
+    claide (1.1.0)
+    cocoapods-core (1.15.2)
+      activesupport (>= 5.0, < 8)
+      addressable (~> 2.8)
+      algoliasearch (~> 1.0)
+      concurrent-ruby (~> 1.1)
+      fuzzy_match (~> 2.0.4)
+      nap (~> 1.0)
+      netrc (~> 0.11)
+      public_suffix (~> 4.0)
+      typhoeus (~> 1.0)
+    cocoapods-deintegrate (1.0.5)
+    cocoapods-downloader (2.1)
+    cocoapods-plugins (1.0.0)
+      nap
+    cocoapods-search (1.0.1)
+    cocoapods-trunk (1.6.0)
+      nap (>= 0.8, < 2.0)
+      netrc (~> 0.11)
+    cocoapods-try (1.2.0)
+    colored2 (3.1.2)
+    concurrent-ruby (1.2.3)
+    connection_pool (2.4.1)
+    drb (2.2.0)
+      ruby2_keywords
+    escape (0.0.4)
+    ethon (0.16.0)
+      ffi (>= 1.15.0)
+    ffi (1.16.3)
+    fourflusher (2.3.1)
+    fuzzy_match (2.0.4)
+    gh_inspector (1.1.3)
+    httpclient (2.8.3)
+    i18n (1.14.4)
+      concurrent-ruby (~> 1.0)
+    json (2.7.1)
+    minitest (5.22.0)
+    molinillo (0.8.0)
+    mutex_m (0.2.0)
+    nanaimo (0.3.0)
+    nap (1.1.0)
+    netrc (0.11.0)
+    nkf (0.1.3)
+    public_suffix (4.0.7)
+    rexml (3.2.6)
+    ruby-macho (2.5.1)
+    ruby2_keywords (0.0.5)
+    typhoeus (1.4.1)
+      ethon (>= 0.9.0)
+    tzinfo (2.0.6)
+      concurrent-ruby (~> 1.0)
+    xcodeproj (1.24.0)
+      CFPropertyList (>= 2.3.3, < 4.0)
+      atomos (~> 0.1.3)
+      claide (>= 1.0.2, < 2.0)
+      colored2 (~> 3.1)
+      nanaimo (~> 0.3.0)
+      rexml (~> 3.2.4)
+
+PLATFORMS
+  arm64-darwin-23
+  ruby
+
+DEPENDENCIES
+  cocoapods!
+
+BUNDLED WITH
+   2.5.4

From 6491e381d7f827ced234e6aa7edca0a92f25eff6 Mon Sep 17 00:00:00 2001
From: Sagar Dagdu <shags032@gmail.com>
Date: Sat, 13 Apr 2024 08:24:03 +0530
Subject: [PATCH 272/326] Update deployment targets for Xcode 15

---
 SQLite.swift.podspec             |  4 ++--
 SQLite.xcodeproj/project.pbxproj | 24 ++++++++++++------------
 2 files changed, 14 insertions(+), 14 deletions(-)

diff --git a/SQLite.swift.podspec b/SQLite.swift.podspec
index b691aeab..4a7a390c 100644
--- a/SQLite.swift.podspec
+++ b/SQLite.swift.podspec
@@ -18,8 +18,8 @@ Pod::Spec.new do |s|
   s.default_subspec  = 'standard'
   s.swift_versions = ['5']
 
-  ios_deployment_target = '11.0'
-  tvos_deployment_target = '11.0'
+  ios_deployment_target = '12.0'
+  tvos_deployment_target = '12.0'
   osx_deployment_target = '10.13'
   watchos_deployment_target = '4.0'
 
diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj
index 0b246a00..c5215807 100644
--- a/SQLite.xcodeproj/project.pbxproj
+++ b/SQLite.xcodeproj/project.pbxproj
@@ -1299,7 +1299,7 @@
 				SDKROOT = appletvos;
 				SKIP_INSTALL = YES;
 				SWIFT_TREAT_WARNINGS_AS_ERRORS = YES;
-				TVOS_DEPLOYMENT_TARGET = 11.0;
+				TVOS_DEPLOYMENT_TARGET = 12.0;
 			};
 			name = Debug;
 		};
@@ -1321,7 +1321,7 @@
 				SDKROOT = appletvos;
 				SKIP_INSTALL = YES;
 				SWIFT_TREAT_WARNINGS_AS_ERRORS = YES;
-				TVOS_DEPLOYMENT_TARGET = 11.0;
+				TVOS_DEPLOYMENT_TARGET = 12.0;
 			};
 			name = Release;
 		};
@@ -1335,7 +1335,7 @@
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SDKROOT = appletvos;
 				SWIFT_TREAT_WARNINGS_AS_ERRORS = YES;
-				TVOS_DEPLOYMENT_TARGET = 11.0;
+				TVOS_DEPLOYMENT_TARGET = 12.0;
 			};
 			name = Debug;
 		};
@@ -1349,7 +1349,7 @@
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SDKROOT = appletvos;
 				SWIFT_TREAT_WARNINGS_AS_ERRORS = YES;
-				TVOS_DEPLOYMENT_TARGET = 11.0;
+				TVOS_DEPLOYMENT_TARGET = 12.0;
 			};
 			name = Release;
 		};
@@ -1450,7 +1450,7 @@
 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
-				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
+				IPHONEOS_DEPLOYMENT_TARGET = 13.0;
 				MACOSX_DEPLOYMENT_TARGET = 10.13;
 				MTL_ENABLE_DEBUG_INFO = YES;
 				ONLY_ACTIVE_ARCH = YES;
@@ -1459,7 +1459,7 @@
 				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
 				SWIFT_VERSION = 5.0;
 				TARGETED_DEVICE_FAMILY = "1,2,3";
-				TVOS_DEPLOYMENT_TARGET = 11.0;
+				TVOS_DEPLOYMENT_TARGET = 12.0;
 				VERSIONING_SYSTEM = "apple-generic";
 				VERSION_INFO_PREFIX = "";
 				WATCHOS_DEPLOYMENT_TARGET = 4.0;
@@ -1509,7 +1509,7 @@
 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
-				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
+				IPHONEOS_DEPLOYMENT_TARGET = 13.0;
 				MACOSX_DEPLOYMENT_TARGET = 10.13;
 				MTL_ENABLE_DEBUG_INFO = NO;
 				PRODUCT_NAME = "";
@@ -1517,7 +1517,7 @@
 				SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
 				SWIFT_VERSION = 5.0;
 				TARGETED_DEVICE_FAMILY = "1,2,3";
-				TVOS_DEPLOYMENT_TARGET = 11.0;
+				TVOS_DEPLOYMENT_TARGET = 12.0;
 				VALIDATE_PRODUCT = YES;
 				VERSIONING_SYSTEM = "apple-generic";
 				VERSION_INFO_PREFIX = "";
@@ -1538,7 +1538,7 @@
 				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
 				INFOPLIST_FILE = "$(SRCROOT)/Sources/SQLite/Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
-				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
+				IPHONEOS_DEPLOYMENT_TARGET = 12.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
 				MARKETING_VERSION = 0.14.0;
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
@@ -1562,7 +1562,7 @@
 				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
 				INFOPLIST_FILE = "$(SRCROOT)/Sources/SQLite/Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
-				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
+				IPHONEOS_DEPLOYMENT_TARGET = 12.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
 				MARKETING_VERSION = 0.14.0;
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
@@ -1577,7 +1577,7 @@
 			buildSettings = {
 				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
 				INFOPLIST_FILE = Tests/SQLiteTests/Info.plist;
-				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
+				IPHONEOS_DEPLOYMENT_TARGET = 12.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLiteTests;
 				PRODUCT_NAME = "$(TARGET_NAME)";
@@ -1590,7 +1590,7 @@
 			buildSettings = {
 				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
 				INFOPLIST_FILE = Tests/SQLiteTests/Info.plist;
-				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
+				IPHONEOS_DEPLOYMENT_TARGET = 12.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLiteTests;
 				PRODUCT_NAME = "$(TARGET_NAME)";

From c4ec236be4b089984c8239869309bd4c637642b6 Mon Sep 17 00:00:00 2001
From: NathanFallet <contact@nathanfallet.me>
Date: Sat, 13 Apr 2024 15:17:15 +0200
Subject: [PATCH 273/326] using temp cocoapods (fix xcode 15 build, to be
 reverted later)

---
 run-tests.sh | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/run-tests.sh b/run-tests.sh
index 49330c12..1bf8de96 100755
--- a/run-tests.sh
+++ b/run-tests.sh
@@ -7,10 +7,11 @@ if [ -n "$BUILD_SCHEME" ]; then
         make test BUILD_SCHEME="$BUILD_SCHEME"
     fi
 elif [ -n "$VALIDATOR_SUBSPEC" ]; then
+    bundle install
     if [ "$VALIDATOR_SUBSPEC" == "none" ]; then
-      pod lib lint --no-subspecs --fail-fast
+      bundle exec pod lib lint --no-subspecs --fail-fast
     else
-      pod lib lint --subspec="${VALIDATOR_SUBSPEC}" --fail-fast
+      bundle exec pod lib lint --subspec="${VALIDATOR_SUBSPEC}" --fail-fast
     fi
 elif [ -n "$CARTHAGE_PLATFORM" ]; then
     cd Tests/Carthage && make test CARTHAGE_PLATFORM="$CARTHAGE_PLATFORM"

From a6e7697bac187dd6ee5ee8026e7fca91235e5b73 Mon Sep 17 00:00:00 2001
From: NathanFallet <contact@nathanfallet.me>
Date: Sat, 13 Apr 2024 15:31:08 +0200
Subject: [PATCH 274/326] fixing bundle versions?

---
 Gemfile.lock | 2 +-
 run-tests.sh | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/Gemfile.lock b/Gemfile.lock
index 75374a30..159cbf9c 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -82,7 +82,7 @@ GEM
     i18n (1.14.4)
       concurrent-ruby (~> 1.0)
     json (2.7.1)
-    minitest (5.22.0)
+    minitest (5.22.3)
     molinillo (0.8.0)
     mutex_m (0.2.0)
     nanaimo (0.3.0)
diff --git a/run-tests.sh b/run-tests.sh
index 1bf8de96..13975f0e 100755
--- a/run-tests.sh
+++ b/run-tests.sh
@@ -7,7 +7,7 @@ if [ -n "$BUILD_SCHEME" ]; then
         make test BUILD_SCHEME="$BUILD_SCHEME"
     fi
 elif [ -n "$VALIDATOR_SUBSPEC" ]; then
-    bundle install
+    bundle install --deployment
     if [ "$VALIDATOR_SUBSPEC" == "none" ]; then
       bundle exec pod lib lint --no-subspecs --fail-fast
     else

From 9bed2450cbd38352a5a3316b2817bec8baf4f5d3 Mon Sep 17 00:00:00 2001
From: NathanFallet <contact@nathanfallet.me>
Date: Sat, 13 Apr 2024 15:38:17 +0200
Subject: [PATCH 275/326] adding source for gems as otherwise it does not
 resolve correctly in CI

---
 Gemfile      |  2 ++
 Gemfile.lock | 11 +++++------
 run-tests.sh |  2 +-
 3 files changed, 8 insertions(+), 7 deletions(-)

diff --git a/Gemfile b/Gemfile
index 2f97c391..eb6036cd 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1 +1,3 @@
+source "https://rubygems.org"
+
 gem 'cocoapods', :git => 'https://github.com/SagarSDagdu/CocoaPods.git', tag: '1.15.2.1-sagard'
diff --git a/Gemfile.lock b/Gemfile.lock
index 159cbf9c..a58783ea 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -23,6 +23,7 @@ GIT
       xcodeproj (>= 1.23.0, < 2.0)
 
 GEM
+  remote: https://rubygems.org/
   specs:
     CFPropertyList (3.0.7)
       base64
@@ -45,7 +46,7 @@ GEM
       json (>= 1.5.1)
     atomos (0.1.3)
     base64 (0.2.0)
-    bigdecimal (3.1.5)
+    bigdecimal (3.1.7)
     claide (1.1.0)
     cocoapods-core (1.15.2)
       activesupport (>= 5.0, < 8)
@@ -69,8 +70,7 @@ GEM
     colored2 (3.1.2)
     concurrent-ruby (1.2.3)
     connection_pool (2.4.1)
-    drb (2.2.0)
-      ruby2_keywords
+    drb (2.2.1)
     escape (0.0.4)
     ethon (0.16.0)
       ffi (>= 1.15.0)
@@ -81,18 +81,17 @@ GEM
     httpclient (2.8.3)
     i18n (1.14.4)
       concurrent-ruby (~> 1.0)
-    json (2.7.1)
+    json (2.7.2)
     minitest (5.22.3)
     molinillo (0.8.0)
     mutex_m (0.2.0)
     nanaimo (0.3.0)
     nap (1.1.0)
     netrc (0.11.0)
-    nkf (0.1.3)
+    nkf (0.2.0)
     public_suffix (4.0.7)
     rexml (3.2.6)
     ruby-macho (2.5.1)
-    ruby2_keywords (0.0.5)
     typhoeus (1.4.1)
       ethon (>= 0.9.0)
     tzinfo (2.0.6)
diff --git a/run-tests.sh b/run-tests.sh
index 13975f0e..1bf8de96 100755
--- a/run-tests.sh
+++ b/run-tests.sh
@@ -7,7 +7,7 @@ if [ -n "$BUILD_SCHEME" ]; then
         make test BUILD_SCHEME="$BUILD_SCHEME"
     fi
 elif [ -n "$VALIDATOR_SUBSPEC" ]; then
-    bundle install --deployment
+    bundle install
     if [ "$VALIDATOR_SUBSPEC" == "none" ]; then
       bundle exec pod lib lint --no-subspecs --fail-fast
     else

From 6be8ca943138e3b3d77bb14b74c1b498d75b0cf4 Mon Sep 17 00:00:00 2001
From: NathanFallet <contact@nathanfallet.me>
Date: Sat, 13 Apr 2024 16:30:09 +0200
Subject: [PATCH 276/326] fix carthage location?

---
 Tests/Carthage/Makefile | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Tests/Carthage/Makefile b/Tests/Carthage/Makefile
index b44d85b9..fe708ab0 100644
--- a/Tests/Carthage/Makefile
+++ b/Tests/Carthage/Makefile
@@ -1,4 +1,4 @@
-CARTHAGE := /usr/local/bin/carthage
+CARTHAGE := $(shell which carthage)
 CARTHAGE_PLATFORM := iOS
 CARTHAGE_CONFIGURATION := Release
 CARTHAGE_DIR := Carthage

From a2a550e6266e80ebe4d896d1360cff4ef9ad202a Mon Sep 17 00:00:00 2001
From: NathanFallet <contact@nathanfallet.me>
Date: Sat, 13 Apr 2024 17:18:49 +0200
Subject: [PATCH 277/326] bump xcode version + carthage visionOS tests

---
 .github/workflows/build.yml | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 7be89270..9fbba1c5 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -13,7 +13,7 @@ jobs:
       # https://github.com/CocoaPods/CocoaPods/issues/11839
       run: |
         xcode-select -p
-        sudo xcode-select -s /Applications/Xcode_15.0.1.app/Contents/Developer
+        sudo xcode-select -s /Applications/Xcode_15.3.app/Contents/Developer
     - name: "Lint"
       run: make lint
     - name: "Run tests (PACKAGE_MANAGER_COMMAND: test)"
@@ -64,6 +64,10 @@ jobs:
       env:
         CARTHAGE_PLATFORM: tvOS
       run: ./run-tests.sh
+    - name: "Run tests (CARTHAGE_PLATFORM: visionOS)"
+      env:
+        CARTHAGE_PLATFORM: visionOS
+      run: ./run-tests.sh
   build-linux:
     runs-on: ubuntu-latest
     steps:

From f0af5e0a2b6917ae38e3019e3456fd0fa9365864 Mon Sep 17 00:00:00 2001
From: NathanFallet <contact@nathanfallet.me>
Date: Sat, 13 Apr 2024 18:55:58 +0200
Subject: [PATCH 278/326] Changelogs v0.15.1

---
 CHANGELOG.md            | 13 +++++++++++++
 Documentation/Index.md  | 12 ++++++------
 README.md               |  4 ++--
 SQLite.swift.podspec    |  2 +-
 Tests/SPM/Package.swift |  2 +-
 5 files changed, 23 insertions(+), 10 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4c5c1fba..79085ce3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,11 @@
+0.15.1 (14-04-2024), [diff][diff-0.15.1]
+========================================
+
+* Update CoreFunctions.swift fix typo ([#1249][])
+* Fix #1247 support nil case when decoding optionals ([#1248][])
+* Change deployment targets for Xcode 15 and add dependency on custom Cocoapods fork ([#1255][])
+* Add VisionOS support ([#1237][])
+
 0.15.0 (24-02-2024), [diff][diff-0.15.0]
 ========================================
 
@@ -155,6 +163,7 @@ For breaking changes, see [Upgrading.md](Documentation/Upgrading.md).
 [diff-0.14.0]: https://github.com/stephencelis/SQLite.swift/compare/0.13.3...0.14.0
 [diff-0.14.1]: https://github.com/stephencelis/SQLite.swift/compare/0.14.0...0.14.1
 [diff-0.15.0]: https://github.com/stephencelis/SQLite.swift/compare/0.14.0...0.15.0
+[diff-0.15.1]: https://github.com/stephencelis/SQLite.swift/compare/0.15.0...0.15.1
 
 [#30]: https://github.com/stephencelis/SQLite.swift/issues/30
 [#142]: https://github.com/stephencelis/SQLite.swift/issues/142
@@ -235,3 +244,7 @@ For breaking changes, see [Upgrading.md](Documentation/Upgrading.md).
 [#1218]: https://github.com/stephencelis/SQLite.swift/pull/1218
 [#1210]: https://github.com/stephencelis/SQLite.swift/pull/1210
 [#1245]: https://github.com/stephencelis/SQLite.swift/pull/1245
+[#1249]: https://github.com/stephencelis/SQLite.swift/pull/1249
+[#1248]: https://github.com/stephencelis/SQLite.swift/pull/1248
+[#1255]: https://github.com/stephencelis/SQLite.swift/pull/1255
+[#1237]: https://github.com/stephencelis/SQLite.swift/pull/1237
diff --git a/Documentation/Index.md b/Documentation/Index.md
index 7f900bc5..edb9dab3 100644
--- a/Documentation/Index.md
+++ b/Documentation/Index.md
@@ -108,7 +108,7 @@ process of downloading, compiling, and linking dependencies.
 
   ```swift
   dependencies: [
-    .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.15.0")
+    .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.15.1")
   ]
   ```
 
@@ -129,7 +129,7 @@ install SQLite.swift with Carthage:
  2. Update your Cartfile to include the following:
 
     ```ruby
-    github "stephencelis/SQLite.swift" ~> 0.15.0
+    github "stephencelis/SQLite.swift" ~> 0.15.1
     ```
 
  3. Run `carthage update` and [add the appropriate framework][Carthage Usage].
@@ -159,7 +159,7 @@ install SQLite.swift with Carthage:
     use_frameworks!
 
     target 'YourAppTargetName' do
-        pod 'SQLite.swift', '~> 0.15.0'
+        pod 'SQLite.swift', '~> 0.15.1'
     end
     ```
 
@@ -173,7 +173,7 @@ with the OS you can require the `standalone` subspec:
 
 ```ruby
 target 'YourAppTargetName' do
-  pod 'SQLite.swift/standalone', '~> 0.15.0'
+  pod 'SQLite.swift/standalone', '~> 0.15.1'
 end
 ```
 
@@ -183,7 +183,7 @@ dependency to sqlite3 or one of its subspecs:
 
 ```ruby
 target 'YourAppTargetName' do
-  pod 'SQLite.swift/standalone', '~> 0.15.0'
+  pod 'SQLite.swift/standalone', '~> 0.15.1'
   pod 'sqlite3/fts5', '= 3.15.0'  # SQLite 3.15.0 with FTS5 enabled
 end
 ```
@@ -199,7 +199,7 @@ If you want to use [SQLCipher][] with SQLite.swift you can require the
 target 'YourAppTargetName' do
   # Make sure you only require the subspec, otherwise you app might link against
   # the system SQLite, which means the SQLCipher-specific methods won't work.
-  pod 'SQLite.swift/SQLCipher', '~> 0.15.0'
+  pod 'SQLite.swift/SQLCipher', '~> 0.15.1'
 end
 ```
 
diff --git a/README.md b/README.md
index 117edb5a..51090e5d 100644
--- a/README.md
+++ b/README.md
@@ -128,7 +128,7 @@ Swift code.
 
   ```swift
   dependencies: [
-      .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.15.0")
+      .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.15.1")
   ]
   ```
 
@@ -152,7 +152,7 @@ install SQLite.swift with Carthage:
  2. Update your Cartfile to include the following:
 
     ```ruby
-    github "stephencelis/SQLite.swift" ~> 0.15.0
+    github "stephencelis/SQLite.swift" ~> 0.15.1
     ```
 
  3. Run `carthage update` and
diff --git a/SQLite.swift.podspec b/SQLite.swift.podspec
index 860fe3d4..035fffb0 100644
--- a/SQLite.swift.podspec
+++ b/SQLite.swift.podspec
@@ -1,6 +1,6 @@
 Pod::Spec.new do |s|
   s.name             = "SQLite.swift"
-  s.version          = "0.15.0"
+  s.version          = "0.15.1"
   s.summary          = "A type-safe, Swift-language layer over SQLite3."
 
   s.description      = <<-DESC
diff --git a/Tests/SPM/Package.swift b/Tests/SPM/Package.swift
index c82b3b9e..5a96daff 100644
--- a/Tests/SPM/Package.swift
+++ b/Tests/SPM/Package.swift
@@ -15,7 +15,7 @@ let package = Package(
         // for testing from same repository
         .package(path: "../..")
         // normally this would be:
-        // .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.15.0")
+        // .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.15.1")
     ],
     targets: [
         .executableTarget(name: "test", dependencies: [.product(name: "SQLite", package: "SQLite.swift")])

From fc236997c8e1b21be4ecaaa547496857483dcf6b Mon Sep 17 00:00:00 2001
From: NathanFallet <contact@nathanfallet.me>
Date: Tue, 16 Apr 2024 05:40:47 +0200
Subject: [PATCH 279/326] fix: visionos to cocoapods

---
 CHANGELOG.md            |  6 ++++++
 Documentation/Index.md  | 12 ++++++------
 README.md               |  4 ++--
 SQLite.swift.podspec    |  7 ++++++-
 Tests/SPM/Package.swift |  2 +-
 5 files changed, 21 insertions(+), 10 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 79085ce3..376c19d8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+0.15.2 (16-04-2024), [diff][diff-0.15.2]
+========================================
+* fix: visionos to cocoapods ([#1260][])
+
 0.15.1 (14-04-2024), [diff][diff-0.15.1]
 ========================================
 
@@ -164,6 +168,7 @@ For breaking changes, see [Upgrading.md](Documentation/Upgrading.md).
 [diff-0.14.1]: https://github.com/stephencelis/SQLite.swift/compare/0.14.0...0.14.1
 [diff-0.15.0]: https://github.com/stephencelis/SQLite.swift/compare/0.14.0...0.15.0
 [diff-0.15.1]: https://github.com/stephencelis/SQLite.swift/compare/0.15.0...0.15.1
+[diff-0.15.2]: https://github.com/stephencelis/SQLite.swift/compare/0.15.1...0.15.2
 
 [#30]: https://github.com/stephencelis/SQLite.swift/issues/30
 [#142]: https://github.com/stephencelis/SQLite.swift/issues/142
@@ -248,3 +253,4 @@ For breaking changes, see [Upgrading.md](Documentation/Upgrading.md).
 [#1248]: https://github.com/stephencelis/SQLite.swift/pull/1248
 [#1255]: https://github.com/stephencelis/SQLite.swift/pull/1255
 [#1237]: https://github.com/stephencelis/SQLite.swift/pull/1237
+[#1260]: https://github.com/stephencelis/SQLite.swift/pull/1260
diff --git a/Documentation/Index.md b/Documentation/Index.md
index edb9dab3..adbbff8b 100644
--- a/Documentation/Index.md
+++ b/Documentation/Index.md
@@ -108,7 +108,7 @@ process of downloading, compiling, and linking dependencies.
 
   ```swift
   dependencies: [
-    .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.15.1")
+    .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.15.2")
   ]
   ```
 
@@ -129,7 +129,7 @@ install SQLite.swift with Carthage:
  2. Update your Cartfile to include the following:
 
     ```ruby
-    github "stephencelis/SQLite.swift" ~> 0.15.1
+    github "stephencelis/SQLite.swift" ~> 0.15.2
     ```
 
  3. Run `carthage update` and [add the appropriate framework][Carthage Usage].
@@ -159,7 +159,7 @@ install SQLite.swift with Carthage:
     use_frameworks!
 
     target 'YourAppTargetName' do
-        pod 'SQLite.swift', '~> 0.15.1'
+        pod 'SQLite.swift', '~> 0.15.2'
     end
     ```
 
@@ -173,7 +173,7 @@ with the OS you can require the `standalone` subspec:
 
 ```ruby
 target 'YourAppTargetName' do
-  pod 'SQLite.swift/standalone', '~> 0.15.1'
+  pod 'SQLite.swift/standalone', '~> 0.15.2'
 end
 ```
 
@@ -183,7 +183,7 @@ dependency to sqlite3 or one of its subspecs:
 
 ```ruby
 target 'YourAppTargetName' do
-  pod 'SQLite.swift/standalone', '~> 0.15.1'
+  pod 'SQLite.swift/standalone', '~> 0.15.2'
   pod 'sqlite3/fts5', '= 3.15.0'  # SQLite 3.15.0 with FTS5 enabled
 end
 ```
@@ -199,7 +199,7 @@ If you want to use [SQLCipher][] with SQLite.swift you can require the
 target 'YourAppTargetName' do
   # Make sure you only require the subspec, otherwise you app might link against
   # the system SQLite, which means the SQLCipher-specific methods won't work.
-  pod 'SQLite.swift/SQLCipher', '~> 0.15.1'
+  pod 'SQLite.swift/SQLCipher', '~> 0.15.2'
 end
 ```
 
diff --git a/README.md b/README.md
index 51090e5d..a87ab937 100644
--- a/README.md
+++ b/README.md
@@ -128,7 +128,7 @@ Swift code.
 
   ```swift
   dependencies: [
-      .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.15.1")
+      .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.15.2")
   ]
   ```
 
@@ -152,7 +152,7 @@ install SQLite.swift with Carthage:
  2. Update your Cartfile to include the following:
 
     ```ruby
-    github "stephencelis/SQLite.swift" ~> 0.15.1
+    github "stephencelis/SQLite.swift" ~> 0.15.2
     ```
 
  3. Run `carthage update` and
diff --git a/SQLite.swift.podspec b/SQLite.swift.podspec
index 035fffb0..efed67c1 100644
--- a/SQLite.swift.podspec
+++ b/SQLite.swift.podspec
@@ -1,6 +1,6 @@
 Pod::Spec.new do |s|
   s.name             = "SQLite.swift"
-  s.version          = "0.15.1"
+  s.version          = "0.15.2"
   s.summary          = "A type-safe, Swift-language layer over SQLite3."
 
   s.description      = <<-DESC
@@ -22,11 +22,13 @@ Pod::Spec.new do |s|
   tvos_deployment_target = '12.0'
   osx_deployment_target = '10.13'
   watchos_deployment_target = '4.0'
+  visionos_deployment_target = '1.0'
 
   s.ios.deployment_target = ios_deployment_target
   s.tvos.deployment_target = tvos_deployment_target
   s.osx.deployment_target = osx_deployment_target
   s.watchos.deployment_target = watchos_deployment_target
+  s.visionos.deployment_target = visionos_deployment_target
 
   s.subspec 'standard' do |ss|
     ss.source_files = 'Sources/SQLite/**/*.{c,h,m,swift}'
@@ -37,6 +39,7 @@ Pod::Spec.new do |s|
     ss.tvos.deployment_target = tvos_deployment_target
     ss.osx.deployment_target = osx_deployment_target
     ss.watchos.deployment_target = watchos_deployment_target
+    ss.visionos.deployment_target = visionos_deployment_target
 
     ss.test_spec 'tests' do |test_spec|
       test_spec.resources = 'Tests/SQLiteTests/Resources/*'
@@ -61,6 +64,7 @@ Pod::Spec.new do |s|
     ss.tvos.deployment_target = tvos_deployment_target
     ss.osx.deployment_target = osx_deployment_target
     ss.watchos.deployment_target = watchos_deployment_target
+    ss.visionos.deployment_target = visionos_deployment_target
 
     ss.test_spec 'tests' do |test_spec|
       test_spec.resources = 'Tests/SQLiteTests/Resources/*'
@@ -83,6 +87,7 @@ Pod::Spec.new do |s|
     ss.tvos.deployment_target = tvos_deployment_target
     ss.osx.deployment_target = osx_deployment_target
     ss.watchos.deployment_target = watchos_deployment_target
+    ss.visionos.deployment_target = visionos_deployment_target
 
     ss.test_spec 'tests' do |test_spec|
       test_spec.resources = 'Tests/SQLiteTests/Resources/*'
diff --git a/Tests/SPM/Package.swift b/Tests/SPM/Package.swift
index 5a96daff..b73ecd83 100644
--- a/Tests/SPM/Package.swift
+++ b/Tests/SPM/Package.swift
@@ -15,7 +15,7 @@ let package = Package(
         // for testing from same repository
         .package(path: "../..")
         // normally this would be:
-        // .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.15.1")
+        // .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.15.2")
     ],
     targets: [
         .executableTarget(name: "test", dependencies: [.product(name: "SQLite", package: "SQLite.swift")])

From 8f75f4609221c55901cf60b1438b7a8441af145e Mon Sep 17 00:00:00 2001
From: NathanFallet <contact@nathanfallet.me>
Date: Tue, 16 Apr 2024 06:06:25 +0200
Subject: [PATCH 280/326] gonna fix that later

---
 SQLite.swift.podspec | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/SQLite.swift.podspec b/SQLite.swift.podspec
index efed67c1..88713c9e 100644
--- a/SQLite.swift.podspec
+++ b/SQLite.swift.podspec
@@ -87,7 +87,7 @@ Pod::Spec.new do |s|
     ss.tvos.deployment_target = tvos_deployment_target
     ss.osx.deployment_target = osx_deployment_target
     ss.watchos.deployment_target = watchos_deployment_target
-    ss.visionos.deployment_target = visionos_deployment_target
+    #ss.visionos.deployment_target = visionos_deployment_target # Not supported by SQLCipher for now
 
     ss.test_spec 'tests' do |test_spec|
       test_spec.resources = 'Tests/SQLiteTests/Resources/*'

From 15fe07e3d5d0555a7f03966114ce5a9390823af7 Mon Sep 17 00:00:00 2001
From: Sagar Dagdu <shags032@gmail.com>
Date: Thu, 18 Apr 2024 18:57:43 +0530
Subject: [PATCH 281/326] Update the marketing version to the version we will
 be releasing next

For recent releases, the `Info.plist` shipped inside the `xcframeworks` do not match the release version. Fixing that to reflect the marketing version we will be releasing next.
---
 SQLite.xcodeproj/project.pbxproj | 22 ++++++++++++++--------
 1 file changed, 14 insertions(+), 8 deletions(-)

diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj
index 79de22e7..a2d7edbe 100644
--- a/SQLite.xcodeproj/project.pbxproj
+++ b/SQLite.xcodeproj/project.pbxproj
@@ -218,6 +218,9 @@
 		DB7C5DA728D7C9B6006395CF /* SQLiteVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB7C5DA528D7C9B6006395CF /* SQLiteVersion.swift */; };
 		DB7C5DA828D7C9B6006395CF /* SQLiteVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB7C5DA528D7C9B6006395CF /* SQLiteVersion.swift */; };
 		DB7C5DA928D7C9B6006395CF /* SQLiteVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB7C5DA528D7C9B6006395CF /* SQLiteVersion.swift */; };
+		DBB93D5A2A22A373009BB96E /* SchemaReaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBB93D592A22A373009BB96E /* SchemaReaderTests.swift */; };
+		DBB93D5B2A22A373009BB96E /* SchemaReaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBB93D592A22A373009BB96E /* SchemaReaderTests.swift */; };
+		DBB93D5C2A22A373009BB96E /* SchemaReaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBB93D592A22A373009BB96E /* SchemaReaderTests.swift */; };
 		DEB306BA2B61CEF500F9D46B /* SQLite.h in Headers */ = {isa = PBXBuildFile; fileRef = EE247AD61C3F04ED00AE3E12 /* SQLite.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		DEB306BC2B61CEF500F9D46B /* CoreFunctions.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AFC1C3F06E900AE3E12 /* CoreFunctions.swift */; };
 		DEB306BD2B61CEF500F9D46B /* Coding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49EB68C31F7B3CB400D89D40 /* Coding.swift */; };
@@ -288,9 +291,6 @@
 		DEB307092B61CF9500F9D46B /* Connection+AttachTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A170C08525D3D27CB5F83C /* Connection+AttachTests.swift */; };
 		DEB3070B2B61CF9500F9D46B /* SQLite.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 03A65E5A1C6BB0F50062603F /* SQLite.framework */; };
 		DEB3070D2B61CF9500F9D46B /* Resources in Resources */ = {isa = PBXBuildFile; fileRef = 3DF7B79528846FCC005DD8CA /* Resources */; };
-		DBB93D5A2A22A373009BB96E /* SchemaReaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBB93D592A22A373009BB96E /* SchemaReaderTests.swift */; };
-		DBB93D5B2A22A373009BB96E /* SchemaReaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBB93D592A22A373009BB96E /* SchemaReaderTests.swift */; };
-		DBB93D5C2A22A373009BB96E /* SchemaReaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBB93D592A22A373009BB96E /* SchemaReaderTests.swift */; };
 		EAE5A0372B893C43007C7EA4 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = EAE5A0362B893C43007C7EA4 /* PrivacyInfo.xcprivacy */; };
 		EAE5A0382B893C43007C7EA4 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = EAE5A0362B893C43007C7EA4 /* PrivacyInfo.xcprivacy */; };
 		EAE5A0392B893C43007C7EA4 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = EAE5A0362B893C43007C7EA4 /* PrivacyInfo.xcprivacy */; };
@@ -433,10 +433,10 @@
 		DB58B21028FB864300F8EEA4 /* SchemaReader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SchemaReader.swift; sourceTree = "<group>"; };
 		DB58B21528FC7C4600F8EEA4 /* SQLiteFeature.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SQLiteFeature.swift; sourceTree = "<group>"; };
 		DB7C5DA528D7C9B6006395CF /* SQLiteVersion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SQLiteVersion.swift; sourceTree = "<group>"; };
+		DBB93D592A22A373009BB96E /* SchemaReaderTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SchemaReaderTests.swift; sourceTree = "<group>"; };
 		DEB306E52B61CEF500F9D46B /* SQLite.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SQLite.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		DEB307112B61CF9500F9D46B /* SQLiteTests visionOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "SQLiteTests visionOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
 		DEB307132B61D04500F9D46B /* SQLite visionOS.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; name = "SQLite visionOS.xctestplan"; path = "Tests/SQLite visionOS.xctestplan"; sourceTree = "<group>"; };
-		DBB93D592A22A373009BB96E /* SchemaReaderTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SchemaReaderTests.swift; sourceTree = "<group>"; };
 		EAE5A0362B893C43007C7EA4 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
 		EE247AD31C3F04ED00AE3E12 /* SQLite.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SQLite.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		EE247AD61C3F04ED00AE3E12 /* SQLite.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SQLite.h; sourceTree = "<group>"; };
@@ -1539,6 +1539,7 @@
 				INFOPLIST_FILE = "$(SRCROOT)/Sources/SQLite/Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				MARKETING_VERSION = 0.15.3;
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
 				PRODUCT_NAME = SQLite;
 				SDKROOT = appletvos;
@@ -1561,6 +1562,7 @@
 				INFOPLIST_FILE = "$(SRCROOT)/Sources/SQLite/Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				MARKETING_VERSION = 0.15.3;
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
 				PRODUCT_NAME = SQLite;
 				SDKROOT = appletvos;
@@ -1612,6 +1614,7 @@
 				INFOPLIST_FILE = "$(SRCROOT)/Sources/SQLite/Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				MARKETING_VERSION = 0.15.3;
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
 				PRODUCT_NAME = SQLite;
 				SDKROOT = watchos;
@@ -1636,6 +1639,7 @@
 				INFOPLIST_FILE = "$(SRCROOT)/Sources/SQLite/Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				MARKETING_VERSION = 0.15.3;
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
 				PRODUCT_NAME = SQLite;
 				SDKROOT = watchos;
@@ -1660,7 +1664,7 @@
 				INFOPLIST_FILE = "$(SRCROOT)/Sources/SQLite/Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
-				MARKETING_VERSION = 0.14.0;
+				MARKETING_VERSION = 0.15.3;
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
 				PRODUCT_NAME = SQLite;
 				SDKROOT = xros;
@@ -1685,7 +1689,7 @@
 				INFOPLIST_FILE = "$(SRCROOT)/Sources/SQLite/Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
-				MARKETING_VERSION = 0.14.0;
+				MARKETING_VERSION = 0.15.3;
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
 				PRODUCT_NAME = SQLite;
 				SDKROOT = xros;
@@ -1862,7 +1866,7 @@
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
 				IPHONEOS_DEPLOYMENT_TARGET = 12.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
-				MARKETING_VERSION = 0.14.0;
+				MARKETING_VERSION = 0.15.3;
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
 				PRODUCT_NAME = SQLite;
 				SKIP_INSTALL = YES;
@@ -1886,7 +1890,7 @@
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
 				IPHONEOS_DEPLOYMENT_TARGET = 12.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
-				MARKETING_VERSION = 0.14.0;
+				MARKETING_VERSION = 0.15.3;
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
 				PRODUCT_NAME = SQLite;
 				SKIP_INSTALL = YES;
@@ -1936,6 +1940,7 @@
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
 				MACOSX_DEPLOYMENT_TARGET = 10.13;
+				MARKETING_VERSION = 0.15.3;
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
 				PRODUCT_NAME = SQLite;
 				SDKROOT = macosx;
@@ -1961,6 +1966,7 @@
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
 				MACOSX_DEPLOYMENT_TARGET = 10.13;
+				MARKETING_VERSION = 0.15.3;
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
 				PRODUCT_NAME = SQLite;
 				SDKROOT = macosx;

From 5d3db22d9b1e979a3436d199f10bc7dddd4d3a39 Mon Sep 17 00:00:00 2001
From: Sagar Dagdu <shags032@gmail.com>
Date: Thu, 18 Apr 2024 21:26:22 +0530
Subject: [PATCH 282/326] Update `podspec` to include privacy manifest

Update `podspec` to include privacy manifest
---
 SQLite.swift.podspec | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/SQLite.swift.podspec b/SQLite.swift.podspec
index 88713c9e..e6aa2c6a 100644
--- a/SQLite.swift.podspec
+++ b/SQLite.swift.podspec
@@ -34,6 +34,7 @@ Pod::Spec.new do |s|
     ss.source_files = 'Sources/SQLite/**/*.{c,h,m,swift}'
     ss.exclude_files = 'Sources/**/Cipher.swift'
     ss.library = 'sqlite3'
+    ss.resource_bundle = { 'SQLite.swift' => 'Sources/SQLite/PrivacyInfo.xcprivacy' }
     
     ss.ios.deployment_target = ios_deployment_target
     ss.tvos.deployment_target = tvos_deployment_target
@@ -53,6 +54,7 @@ Pod::Spec.new do |s|
   s.subspec 'standalone' do |ss|
     ss.source_files = 'Sources/SQLite/**/*.{c,h,m,swift}'
     ss.exclude_files = 'Sources/**/Cipher.swift'
+    ss.resource_bundle = { 'SQLite.swift' => 'Sources/SQLite/PrivacyInfo.xcprivacy' }
 
     ss.xcconfig = {
       'OTHER_SWIFT_FLAGS' => '$(inherited) -DSQLITE_SWIFT_STANDALONE',
@@ -77,6 +79,8 @@ Pod::Spec.new do |s|
 
   s.subspec 'SQLCipher' do |ss|
     ss.source_files = 'Sources/SQLite/**/*.{c,h,m,swift}'
+    ss.resource_bundle = { 'SQLite.swift' => 'Sources/SQLite/PrivacyInfo.xcprivacy' }
+
     ss.xcconfig = {
       'OTHER_SWIFT_FLAGS' => '$(inherited) -DSQLITE_SWIFT_SQLCIPHER',
       'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) SQLITE_HAS_CODEC=1 SQLITE_SWIFT_SQLCIPHER=1'

From a95fc6df17d108bd99210db5e8a9bac90fe984b8 Mon Sep 17 00:00:00 2001
From: NathanFallet <contact@nathanfallet.me>
Date: Fri, 19 Apr 2024 02:50:43 +0200
Subject: [PATCH 283/326] v0.15.3 (oups)

---
 CHANGELOG.md            |  5 +++++
 Documentation/Index.md  | 12 ++++++------
 README.md               |  4 ++--
 SQLite.swift.podspec    |  2 +-
 Tests/SPM/Package.swift |  2 +-
 5 files changed, 15 insertions(+), 10 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 376c19d8..c96724fd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+0.15.3 (19-04-2024), [diff][diff-0.15.3]
+========================================
+* Update `podspec` to include privacy manifest ([#1265][])
+
 0.15.2 (16-04-2024), [diff][diff-0.15.2]
 ========================================
 * fix: visionos to cocoapods ([#1260][])
@@ -254,3 +258,4 @@ For breaking changes, see [Upgrading.md](Documentation/Upgrading.md).
 [#1255]: https://github.com/stephencelis/SQLite.swift/pull/1255
 [#1237]: https://github.com/stephencelis/SQLite.swift/pull/1237
 [#1260]: https://github.com/stephencelis/SQLite.swift/pull/1260
+[#1265]: https://github.com/stephencelis/SQLite.swift/pull/1265
diff --git a/Documentation/Index.md b/Documentation/Index.md
index adbbff8b..fcd2d642 100644
--- a/Documentation/Index.md
+++ b/Documentation/Index.md
@@ -108,7 +108,7 @@ process of downloading, compiling, and linking dependencies.
 
   ```swift
   dependencies: [
-    .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.15.2")
+    .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.15.3")
   ]
   ```
 
@@ -129,7 +129,7 @@ install SQLite.swift with Carthage:
  2. Update your Cartfile to include the following:
 
     ```ruby
-    github "stephencelis/SQLite.swift" ~> 0.15.2
+    github "stephencelis/SQLite.swift" ~> 0.15.3
     ```
 
  3. Run `carthage update` and [add the appropriate framework][Carthage Usage].
@@ -159,7 +159,7 @@ install SQLite.swift with Carthage:
     use_frameworks!
 
     target 'YourAppTargetName' do
-        pod 'SQLite.swift', '~> 0.15.2'
+        pod 'SQLite.swift', '~> 0.15.3'
     end
     ```
 
@@ -173,7 +173,7 @@ with the OS you can require the `standalone` subspec:
 
 ```ruby
 target 'YourAppTargetName' do
-  pod 'SQLite.swift/standalone', '~> 0.15.2'
+  pod 'SQLite.swift/standalone', '~> 0.15.3'
 end
 ```
 
@@ -183,7 +183,7 @@ dependency to sqlite3 or one of its subspecs:
 
 ```ruby
 target 'YourAppTargetName' do
-  pod 'SQLite.swift/standalone', '~> 0.15.2'
+  pod 'SQLite.swift/standalone', '~> 0.15.3'
   pod 'sqlite3/fts5', '= 3.15.0'  # SQLite 3.15.0 with FTS5 enabled
 end
 ```
@@ -199,7 +199,7 @@ If you want to use [SQLCipher][] with SQLite.swift you can require the
 target 'YourAppTargetName' do
   # Make sure you only require the subspec, otherwise you app might link against
   # the system SQLite, which means the SQLCipher-specific methods won't work.
-  pod 'SQLite.swift/SQLCipher', '~> 0.15.2'
+  pod 'SQLite.swift/SQLCipher', '~> 0.15.3'
 end
 ```
 
diff --git a/README.md b/README.md
index a87ab937..a4d713b6 100644
--- a/README.md
+++ b/README.md
@@ -128,7 +128,7 @@ Swift code.
 
   ```swift
   dependencies: [
-      .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.15.2")
+      .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.15.3")
   ]
   ```
 
@@ -152,7 +152,7 @@ install SQLite.swift with Carthage:
  2. Update your Cartfile to include the following:
 
     ```ruby
-    github "stephencelis/SQLite.swift" ~> 0.15.2
+    github "stephencelis/SQLite.swift" ~> 0.15.3
     ```
 
  3. Run `carthage update` and
diff --git a/SQLite.swift.podspec b/SQLite.swift.podspec
index e6aa2c6a..184ce23e 100644
--- a/SQLite.swift.podspec
+++ b/SQLite.swift.podspec
@@ -1,6 +1,6 @@
 Pod::Spec.new do |s|
   s.name             = "SQLite.swift"
-  s.version          = "0.15.2"
+  s.version          = "0.15.3"
   s.summary          = "A type-safe, Swift-language layer over SQLite3."
 
   s.description      = <<-DESC
diff --git a/Tests/SPM/Package.swift b/Tests/SPM/Package.swift
index b73ecd83..6521211a 100644
--- a/Tests/SPM/Package.swift
+++ b/Tests/SPM/Package.swift
@@ -15,7 +15,7 @@ let package = Package(
         // for testing from same repository
         .package(path: "../..")
         // normally this would be:
-        // .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.15.2")
+        // .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.15.3")
     ],
     targets: [
         .executableTarget(name: "test", dependencies: [.product(name: "SQLite", package: "SQLite.swift")])

From 363141ac6c070799239593cf6dcad63fe33edd17 Mon Sep 17 00:00:00 2001
From: YiKai Deng <dyikai@gmail.com>
Date: Thu, 26 Sep 2024 17:33:15 +0800
Subject: [PATCH 284/326] Add `CustomStringConvertible` for Setter

---
 Documentation/Upgrading.md                    |  4 +-
 Sources/SQLite/Typed/Setter.swift             |  6 ++
 .../Core/Connection+AttachTests.swift         |  4 +-
 .../SQLiteTests/Core/CoreFunctionsTests.swift | 16 ++---
 Tests/SQLiteTests/Core/StatementTests.swift   |  4 +-
 Tests/SQLiteTests/Extensions/FTS4Tests.swift  |  6 +-
 .../Extensions/FTSIntegrationTests.swift      |  2 +-
 .../Schema/SchemaChangerTests.swift           |  2 +-
 .../Schema/SchemaReaderTests.swift            |  4 +-
 Tests/SQLiteTests/TestHelpers.swift           | 32 ++++-----
 .../Typed/CustomFunctionsTests.swift          | 28 ++++----
 Tests/SQLiteTests/Typed/ExpressionTests.swift | 10 +--
 Tests/SQLiteTests/Typed/OperatorsTests.swift  |  2 +-
 .../Typed/QueryIntegrationTests.swift         | 70 +++++++++----------
 Tests/SQLiteTests/Typed/QueryTests.swift      | 22 +++---
 Tests/SQLiteTests/Typed/RowTests.swift        | 22 +++---
 Tests/SQLiteTests/Typed/SelectTests.swift     |  8 +--
 Tests/SQLiteTests/Typed/SetterTests.swift     |  3 +
 .../Typed/WindowFunctionsTests.swift          |  4 +-
 19 files changed, 130 insertions(+), 119 deletions(-)

diff --git a/Documentation/Upgrading.md b/Documentation/Upgrading.md
index f2cc2ecb..0e12aacf 100644
--- a/Documentation/Upgrading.md
+++ b/Documentation/Upgrading.md
@@ -4,6 +4,8 @@
 
 - `Expression.asSQL()` is no longer available. Expressions now implement `CustomStringConvertible`,
   where `description` returns the SQL.
-- `Statement.prepareRowIterator()` is now longer available. Instead, use the methods
+- `Statement.prepareRowIterator()` is no longer available. Instead, use the methods
   of the same name on `Connection`.
 - `Connection.registerTokenizer` is no longer available to register custom FTS4 tokenizers.
+- `Setter.asSQL()` is no longer available. Instead, Setter now implement `CustomStringConvertible`,
+  where `description` returns the SQL.
diff --git a/Sources/SQLite/Typed/Setter.swift b/Sources/SQLite/Typed/Setter.swift
index 7910cab8..8dc8a0e0 100644
--- a/Sources/SQLite/Typed/Setter.swift
+++ b/Sources/SQLite/Typed/Setter.swift
@@ -75,6 +75,12 @@ extension Setter: Expressible {
 
 }
 
+extension Setter: CustomStringConvertible {
+    public var description: String {
+        asSQL()
+    }
+}
+
 public func <-<V: Value>(column: Expression<V>, value: Expression<V>) -> Setter {
     Setter(column: column, value: value)
 }
diff --git a/Tests/SQLiteTests/Core/Connection+AttachTests.swift b/Tests/SQLiteTests/Core/Connection+AttachTests.swift
index 940a30ca..f37300ca 100644
--- a/Tests/SQLiteTests/Core/Connection+AttachTests.swift
+++ b/Tests/SQLiteTests/Core/Connection+AttachTests.swift
@@ -19,7 +19,7 @@ class ConnectionAttachTests: SQLiteTestCase {
         try db.attach(.inMemory, as: schemaName)
 
         let table = Table("attached_users", database: schemaName)
-        let name = Expression<String>("string")
+        let name = SQLite.Expression<String>("string")
 
         // create a table, insert some data
         try db.run(table.create { builder in
@@ -41,7 +41,7 @@ class ConnectionAttachTests: SQLiteTestCase {
         try db.attach(.uri(testDb, parameters: [.mode(.readOnly)]), as: schemaName)
 
         let table = Table("tests", database: schemaName)
-        let email = Expression<String>("email")
+        let email = SQLite.Expression<String>("email")
 
         let rows = try db.prepare(table.select(email)).map { $0[email] }
         XCTAssertEqual(["foo@bar.com"], rows)
diff --git a/Tests/SQLiteTests/Core/CoreFunctionsTests.swift b/Tests/SQLiteTests/Core/CoreFunctionsTests.swift
index e03e3769..b866c4e9 100644
--- a/Tests/SQLiteTests/Core/CoreFunctionsTests.swift
+++ b/Tests/SQLiteTests/Core/CoreFunctionsTests.swift
@@ -12,8 +12,8 @@ class CoreFunctionsTests: XCTestCase {
     }
 
     func test_random_generatesExpressionWithRandomFunction() {
-        assertSQL("random()", Expression<Int64>.random())
-        assertSQL("random()", Expression<Int>.random())
+        assertSQL("random()", SQLite.Expression<Int64>.random())
+        assertSQL("random()", SQLite.Expression<Int>.random())
     }
 
     func test_length_wrapsStringExpressionWithLengthFunction() {
@@ -38,14 +38,14 @@ class CoreFunctionsTests: XCTestCase {
         assertSQL("(\"string\" LIKE '%\\%' ESCAPE '\\')", string.like("%\\%", escape: "\\"))
         assertSQL("(\"stringOptional\" LIKE '_\\_' ESCAPE '\\')", stringOptional.like("_\\_", escape: "\\"))
 
-        assertSQL("(\"string\" LIKE \"a\")", string.like(Expression<String>("a")))
-        assertSQL("(\"stringOptional\" LIKE \"a\")", stringOptional.like(Expression<String>("a")))
+        assertSQL("(\"string\" LIKE \"a\")", string.like(SQLite.Expression<String>("a")))
+        assertSQL("(\"stringOptional\" LIKE \"a\")", stringOptional.like(SQLite.Expression<String>("a")))
 
-        assertSQL("(\"string\" LIKE \"a\" ESCAPE '\\')", string.like(Expression<String>("a"), escape: "\\"))
-        assertSQL("(\"stringOptional\" LIKE \"a\" ESCAPE '\\')", stringOptional.like(Expression<String>("a"), escape: "\\"))
+        assertSQL("(\"string\" LIKE \"a\" ESCAPE '\\')", string.like(SQLite.Expression<String>("a"), escape: "\\"))
+        assertSQL("(\"stringOptional\" LIKE \"a\" ESCAPE '\\')", stringOptional.like(SQLite.Expression<String>("a"), escape: "\\"))
 
-        assertSQL("('string' LIKE \"a\")", "string".like(Expression<String>("a")))
-        assertSQL("('string' LIKE \"a\" ESCAPE '\\')", "string".like(Expression<String>("a"), escape: "\\"))
+        assertSQL("('string' LIKE \"a\")", "string".like(SQLite.Expression<String>("a")))
+        assertSQL("('string' LIKE \"a\" ESCAPE '\\')", "string".like(SQLite.Expression<String>("a"), escape: "\\"))
     }
 
     func test_glob_buildsExpressionWithGlobOperator() {
diff --git a/Tests/SQLiteTests/Core/StatementTests.swift b/Tests/SQLiteTests/Core/StatementTests.swift
index 5f212505..dbf99d7c 100644
--- a/Tests/SQLiteTests/Core/StatementTests.swift
+++ b/Tests/SQLiteTests/Core/StatementTests.swift
@@ -27,7 +27,7 @@ class StatementTests: SQLiteTestCase {
 
     func test_zero_sized_blob_returns_null() throws {
         let blobs = Table("blobs")
-        let blobColumn = Expression<Blob>("blob_column")
+        let blobColumn = SQLite.Expression<Blob>("blob_column")
         try db.run(blobs.create { $0.column(blobColumn) })
         try db.run(blobs.insert(blobColumn <- Blob(bytes: [])))
         let blobValue = try db.scalar(blobs.select(blobColumn).limit(1, offset: 0))
@@ -38,7 +38,7 @@ class StatementTests: SQLiteTestCase {
         let names = ["a", "b", "c"]
         try insertUsers(names)
 
-        let emailColumn = Expression<String>("email")
+        let emailColumn = SQLite.Expression<String>("email")
         let statement = try db.prepare("SELECT email FROM users")
         let emails = try statement.prepareRowIterator().map { $0[emailColumn] }
 
diff --git a/Tests/SQLiteTests/Extensions/FTS4Tests.swift b/Tests/SQLiteTests/Extensions/FTS4Tests.swift
index 5e595007..f7258fb5 100644
--- a/Tests/SQLiteTests/Extensions/FTS4Tests.swift
+++ b/Tests/SQLiteTests/Extensions/FTS4Tests.swift
@@ -35,9 +35,9 @@ class FTS4Tests: XCTestCase {
     }
 
     func test_match_onVirtualTableAsExpression_compilesMatchExpression() {
-        assertSQL("(\"virtual_table\" MATCH 'string')", virtualTable.match("string") as Expression<Bool>)
-        assertSQL("(\"virtual_table\" MATCH \"string\")", virtualTable.match(string) as Expression<Bool>)
-        assertSQL("(\"virtual_table\" MATCH \"stringOptional\")", virtualTable.match(stringOptional) as Expression<Bool?>)
+        assertSQL("(\"virtual_table\" MATCH 'string')", virtualTable.match("string") as SQLite.Expression<Bool>)
+        assertSQL("(\"virtual_table\" MATCH \"string\")", virtualTable.match(string) as SQLite.Expression<Bool>)
+        assertSQL("(\"virtual_table\" MATCH \"stringOptional\")", virtualTable.match(stringOptional) as SQLite.Expression<Bool?>)
     }
 
     func test_match_onVirtualTableAsQueryType_compilesMatchExpression() {
diff --git a/Tests/SQLiteTests/Extensions/FTSIntegrationTests.swift b/Tests/SQLiteTests/Extensions/FTSIntegrationTests.swift
index da9ba8dc..1129ae08 100644
--- a/Tests/SQLiteTests/Extensions/FTSIntegrationTests.swift
+++ b/Tests/SQLiteTests/Extensions/FTSIntegrationTests.swift
@@ -11,7 +11,7 @@ import SQLite3
 @testable import SQLite
 
 class FTSIntegrationTests: SQLiteTestCase {
-    let email = Expression<String>("email")
+    let email = SQLite.Expression<String>("email")
     let index = VirtualTable("index")
 
     private func createIndex() throws {
diff --git a/Tests/SQLiteTests/Schema/SchemaChangerTests.swift b/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
index 9f8e21f4..2bec6a06 100644
--- a/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
+++ b/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
@@ -92,7 +92,7 @@ class SchemaChangerTests: SQLiteTestCase {
     }
 
     func test_add_column() throws {
-        let column = Expression<String>("new_column")
+        let column = SQLite.Expression<String>("new_column")
         let newColumn = ColumnDefinition(name: "new_column",
                                          type: .TEXT,
                                          nullable: true,
diff --git a/Tests/SQLiteTests/Schema/SchemaReaderTests.swift b/Tests/SQLiteTests/Schema/SchemaReaderTests.swift
index d57ebff7..8c033e41 100644
--- a/Tests/SQLiteTests/Schema/SchemaReaderTests.swift
+++ b/Tests/SQLiteTests/Schema/SchemaReaderTests.swift
@@ -163,7 +163,7 @@ class SchemaReaderTests: SQLiteTestCase {
 
         try db.run(linkTable.create(block: { definition in
             definition.column(idColumn, primaryKey: .autoincrement)
-            definition.column(testIdColumn, unique: false, check: nil, references: users, Expression<Int64>("id"))
+            definition.column(testIdColumn, unique: false, check: nil, references: users, SQLite.Expression<Int64>("id"))
         }))
 
         let foreignKeys = try schemaReader.foreignKeys(table: "test_links")
@@ -238,7 +238,7 @@ class SchemaReaderTests: SQLiteTestCase {
     }
 
     func test_objectDefinitions_indexes() throws {
-        let emailIndex = users.createIndex(Expression<String>("email"), unique: false, ifNotExists: true)
+        let emailIndex = users.createIndex(SQLite.Expression<String>("email"), unique: false, ifNotExists: true)
         try db.run(emailIndex)
 
         let indexes = try schemaReader.objectDefinitions(type: .index)
diff --git a/Tests/SQLiteTests/TestHelpers.swift b/Tests/SQLiteTests/TestHelpers.swift
index e2da5927..56415a59 100644
--- a/Tests/SQLiteTests/TestHelpers.swift
+++ b/Tests/SQLiteTests/TestHelpers.swift
@@ -74,29 +74,29 @@ class SQLiteTestCase: XCTestCase {
 
 }
 
-let bool = Expression<Bool>("bool")
-let boolOptional = Expression<Bool?>("boolOptional")
+let bool = SQLite.Expression<Bool>("bool")
+let boolOptional = SQLite.Expression<Bool?>("boolOptional")
 
-let data = Expression<Blob>("blob")
-let dataOptional = Expression<Blob?>("blobOptional")
+let data = SQLite.Expression<Blob>("blob")
+let dataOptional = SQLite.Expression<Blob?>("blobOptional")
 
-let date = Expression<Date>("date")
-let dateOptional = Expression<Date?>("dateOptional")
+let date = SQLite.Expression<Date>("date")
+let dateOptional = SQLite.Expression<Date?>("dateOptional")
 
-let double = Expression<Double>("double")
-let doubleOptional = Expression<Double?>("doubleOptional")
+let double = SQLite.Expression<Double>("double")
+let doubleOptional = SQLite.Expression<Double?>("doubleOptional")
 
-let int = Expression<Int>("int")
-let intOptional = Expression<Int?>("intOptional")
+let int = SQLite.Expression<Int>("int")
+let intOptional = SQLite.Expression<Int?>("intOptional")
 
-let int64 = Expression<Int64>("int64")
-let int64Optional = Expression<Int64?>("int64Optional")
+let int64 = SQLite.Expression<Int64>("int64")
+let int64Optional = SQLite.Expression<Int64?>("int64Optional")
 
-let string = Expression<String>("string")
-let stringOptional = Expression<String?>("stringOptional")
+let string = SQLite.Expression<String>("string")
+let stringOptional = SQLite.Expression<String?>("stringOptional")
 
-let uuid = Expression<UUID>("uuid")
-let uuidOptional = Expression<UUID?>("uuidOptional")
+let uuid = SQLite.Expression<UUID>("uuid")
+let uuidOptional = SQLite.Expression<UUID?>("uuidOptional")
 
 let testUUIDValue = UUID(uuidString: "E621E1F8-C36C-495A-93FC-0C247A3E6E5F")!
 
diff --git a/Tests/SQLiteTests/Typed/CustomFunctionsTests.swift b/Tests/SQLiteTests/Typed/CustomFunctionsTests.swift
index 0f46b380..8598b6fb 100644
--- a/Tests/SQLiteTests/Typed/CustomFunctionsTests.swift
+++ b/Tests/SQLiteTests/Typed/CustomFunctionsTests.swift
@@ -5,8 +5,8 @@ import SQLite
 #if !os(Linux)
 
 class CustomFunctionNoArgsTests: SQLiteTestCase {
-    typealias FunctionNoOptional              = () -> Expression<String>
-    typealias FunctionResultOptional          = () -> Expression<String?>
+    typealias FunctionNoOptional              = () -> SQLite.Expression<String>
+    typealias FunctionResultOptional          = () -> SQLite.Expression<String?>
 
     func testFunctionNoOptional() throws {
         let _: FunctionNoOptional = try db.createFunction("test", deterministic: true) {
@@ -26,10 +26,10 @@ class CustomFunctionNoArgsTests: SQLiteTestCase {
 }
 
 class CustomFunctionWithOneArgTests: SQLiteTestCase {
-    typealias FunctionNoOptional              = (Expression<String>) -> Expression<String>
-    typealias FunctionLeftOptional            = (Expression<String?>) -> Expression<String>
-    typealias FunctionResultOptional          = (Expression<String>) -> Expression<String?>
-    typealias FunctionLeftResultOptional      = (Expression<String?>) -> Expression<String?>
+    typealias FunctionNoOptional              = (SQLite.Expression<String>) -> SQLite.Expression<String>
+    typealias FunctionLeftOptional            = (SQLite.Expression<String?>) -> SQLite.Expression<String>
+    typealias FunctionResultOptional          = (SQLite.Expression<String>) -> SQLite.Expression<String?>
+    typealias FunctionLeftResultOptional      = (SQLite.Expression<String?>) -> SQLite.Expression<String?>
 
     func testFunctionNoOptional() throws {
         let _: FunctionNoOptional = try db.createFunction("test", deterministic: true) { a in
@@ -65,14 +65,14 @@ class CustomFunctionWithOneArgTests: SQLiteTestCase {
 }
 
 class CustomFunctionWithTwoArgsTests: SQLiteTestCase {
-    typealias FunctionNoOptional              = (Expression<String>, Expression<String>) -> Expression<String>
-    typealias FunctionLeftOptional            = (Expression<String?>, Expression<String>) -> Expression<String>
-    typealias FunctionRightOptional           = (Expression<String>, Expression<String?>) -> Expression<String>
-    typealias FunctionResultOptional          = (Expression<String>, Expression<String>) -> Expression<String?>
-    typealias FunctionLeftRightOptional       = (Expression<String?>, Expression<String?>) -> Expression<String>
-    typealias FunctionLeftResultOptional      = (Expression<String?>, Expression<String>) -> Expression<String?>
-    typealias FunctionRightResultOptional     = (Expression<String>, Expression<String?>) -> Expression<String?>
-    typealias FunctionLeftRightResultOptional = (Expression<String?>, Expression<String?>) -> Expression<String?>
+    typealias FunctionNoOptional              = (SQLite.Expression<String>, SQLite.Expression<String>) -> SQLite.Expression<String>
+    typealias FunctionLeftOptional            = (SQLite.Expression<String?>, SQLite.Expression<String>) -> SQLite.Expression<String>
+    typealias FunctionRightOptional           = (SQLite.Expression<String>, SQLite.Expression<String?>) -> SQLite.Expression<String>
+    typealias FunctionResultOptional          = (SQLite.Expression<String>, SQLite.Expression<String>) -> SQLite.Expression<String?>
+    typealias FunctionLeftRightOptional       = (SQLite.Expression<String?>, SQLite.Expression<String?>) -> SQLite.Expression<String>
+    typealias FunctionLeftResultOptional      = (SQLite.Expression<String?>, SQLite.Expression<String>) -> SQLite.Expression<String?>
+    typealias FunctionRightResultOptional     = (SQLite.Expression<String>, SQLite.Expression<String?>) -> SQLite.Expression<String?>
+    typealias FunctionLeftRightResultOptional = (SQLite.Expression<String?>, SQLite.Expression<String?>) -> SQLite.Expression<String?>
 
     func testNoOptional() throws {
         let _: FunctionNoOptional = try db.createFunction("test", deterministic: true) { a, b in
diff --git a/Tests/SQLiteTests/Typed/ExpressionTests.swift b/Tests/SQLiteTests/Typed/ExpressionTests.swift
index 147d62e9..1b97fe94 100644
--- a/Tests/SQLiteTests/Typed/ExpressionTests.swift
+++ b/Tests/SQLiteTests/Typed/ExpressionTests.swift
@@ -4,17 +4,17 @@ import XCTest
 class ExpressionTests: XCTestCase {
 
     func test_asSQL_expression_bindings() {
-        let expression = Expression<String>("foo ? bar", ["baz"])
+        let expression = SQLite.Expression<String>("foo ? bar", ["baz"])
         XCTAssertEqual(expression.asSQL(), "foo 'baz' bar")
     }
 
     func test_asSQL_expression_bindings_quoting() {
-        let expression = Expression<String>("foo ? bar", ["'baz'"])
+        let expression = SQLite.Expression<String>("foo ? bar", ["'baz'"])
         XCTAssertEqual(expression.asSQL(), "foo '''baz''' bar")
     }
 
     func test_expression_custom_string_convertible() {
-        let expression = Expression<String>("foo ? bar", ["baz"])
+        let expression = SQLite.Expression<String>("foo ? bar", ["baz"])
         XCTAssertEqual(expression.asSQL(), expression.description)
     }
 
@@ -24,12 +24,12 @@ class ExpressionTests: XCTestCase {
     }
 
     func test_init_literal() {
-        let expression = Expression<String>(literal: "literal")
+        let expression = SQLite.Expression<String>(literal: "literal")
         XCTAssertEqual(expression.template, "literal")
     }
 
     func test_init_identifier() {
-        let expression = Expression<String>("identifier")
+        let expression = SQLite.Expression<String>("identifier")
         XCTAssertEqual(expression.template, "\"identifier\"")
     }
 }
diff --git a/Tests/SQLiteTests/Typed/OperatorsTests.swift b/Tests/SQLiteTests/Typed/OperatorsTests.swift
index 370b910b..7a5d83da 100644
--- a/Tests/SQLiteTests/Typed/OperatorsTests.swift
+++ b/Tests/SQLiteTests/Typed/OperatorsTests.swift
@@ -356,7 +356,7 @@ class OperatorsTests: XCTestCase {
     }
 
     func test_precedencePreserved() {
-        let n = Expression<Int>(value: 1)
+        let n = SQLite.Expression<Int>(value: 1)
         assertSQL("(((1 = 1) AND (1 = 1)) OR (1 = 1))", (n == n && n == n) || n == n)
         assertSQL("((1 = 1) AND ((1 = 1) OR (1 = 1)))", n == n && (n == n || n == n))
     }
diff --git a/Tests/SQLiteTests/Typed/QueryIntegrationTests.swift b/Tests/SQLiteTests/Typed/QueryIntegrationTests.swift
index 7ac72d0c..6be98ca0 100644
--- a/Tests/SQLiteTests/Typed/QueryIntegrationTests.swift
+++ b/Tests/SQLiteTests/Typed/QueryIntegrationTests.swift
@@ -12,9 +12,9 @@ import SQLite3
 
 class QueryIntegrationTests: SQLiteTestCase {
 
-    let id = Expression<Int64>("id")
-    let email = Expression<String>("email")
-    let age = Expression<Int>("age")
+    let id = SQLite.Expression<Int64>("id")
+    let email = SQLite.Expression<String>("email")
+    let age = SQLite.Expression<Int>("age")
 
     override func setUpWithError() throws {
         try super.setUpWithError()
@@ -24,7 +24,7 @@ class QueryIntegrationTests: SQLiteTestCase {
     // MARK: -
 
     func test_select() throws {
-        let managerId = Expression<Int64>("manager_id")
+        let managerId = SQLite.Expression<Int64>("manager_id")
         let managers = users.alias("managers")
 
         let alice = try db.run(users.insert(email <- "alice@example.com"))
@@ -39,7 +39,7 @@ class QueryIntegrationTests: SQLiteTestCase {
         let names = ["a", "b", "c"]
         try insertUsers(names)
 
-        let emailColumn = Expression<String>("email")
+        let emailColumn = SQLite.Expression<String>("email")
         let emails = try db.prepareRowIterator(users).map { $0[emailColumn] }
 
         XCTAssertEqual(names.map({ "\($0)@example.com" }), emails.sorted())
@@ -55,7 +55,7 @@ class QueryIntegrationTests: SQLiteTestCase {
     }
 
     func test_select_optional() throws {
-        let managerId = Expression<Int64?>("manager_id")
+        let managerId = SQLite.Expression<Int64?>("manager_id")
         let managers = users.alias("managers")
 
         let alice = try db.run(users.insert(email <- "alice@example.com"))
@@ -69,15 +69,15 @@ class QueryIntegrationTests: SQLiteTestCase {
     func test_select_codable() throws {
         let table = Table("codable")
         try db.run(table.create { builder in
-            builder.column(Expression<Int>("int"))
-            builder.column(Expression<String>("string"))
-            builder.column(Expression<Bool>("bool"))
-            builder.column(Expression<Double>("float"))
-            builder.column(Expression<Double>("double"))
-            builder.column(Expression<Date>("date"))
-            builder.column(Expression<UUID>("uuid"))
-            builder.column(Expression<String?>("optional"))
-            builder.column(Expression<Data>("sub"))
+            builder.column(SQLite.Expression<Int>("int"))
+            builder.column(SQLite.Expression<String>("string"))
+            builder.column(SQLite.Expression<Bool>("bool"))
+            builder.column(SQLite.Expression<Double>("float"))
+            builder.column(SQLite.Expression<Double>("double"))
+            builder.column(SQLite.Expression<Date>("date"))
+            builder.column(SQLite.Expression<UUID>("uuid"))
+            builder.column(SQLite.Expression<String?>("optional"))
+            builder.column(SQLite.Expression<Data>("sub"))
         })
 
         let value1 = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4,
@@ -133,13 +133,13 @@ class QueryIntegrationTests: SQLiteTestCase {
     func test_insert_many_encodables() throws {
         let table = Table("codable")
         try db.run(table.create { builder in
-            builder.column(Expression<Int?>("int"))
-            builder.column(Expression<String?>("string"))
-            builder.column(Expression<Bool?>("bool"))
-            builder.column(Expression<Double?>("float"))
-            builder.column(Expression<Double?>("double"))
-            builder.column(Expression<Date?>("date"))
-            builder.column(Expression<UUID?>("uuid"))
+            builder.column(SQLite.Expression<Int?>("int"))
+            builder.column(SQLite.Expression<String?>("string"))
+            builder.column(SQLite.Expression<Bool?>("bool"))
+            builder.column(SQLite.Expression<Double?>("float"))
+            builder.column(SQLite.Expression<Double?>("double"))
+            builder.column(SQLite.Expression<Date?>("date"))
+            builder.column(SQLite.Expression<UUID?>("uuid"))
         })
 
         let value1 = TestOptionalCodable(int: 5, string: "6", bool: true, float: 7, double: 8,
@@ -161,9 +161,9 @@ class QueryIntegrationTests: SQLiteTestCase {
 
         let table = Table("custom_codable")
         try db.run(table.create { builder in
-            builder.column(Expression<Int?>("myInt"))
-            builder.column(Expression<String?>("myString"))
-            builder.column(Expression<String?>("myOptionalArray"))
+            builder.column(SQLite.Expression<Int?>("myInt"))
+            builder.column(SQLite.Expression<String?>("myString"))
+            builder.column(SQLite.Expression<String?>("myOptionalArray"))
         })
 
         let customType = TestTypeWithOptionalArray(myInt: 13, myString: "foo", myOptionalArray: [1, 2, 3])
@@ -216,22 +216,22 @@ class QueryIntegrationTests: SQLiteTestCase {
         let actualIDs = try db.prepare(query1.union(query2)).map { $0[id] }
         XCTAssertEqual(expectedIDs, actualIDs)
 
-        let query3 = users.select(users[*], Expression<Int>(literal: "1 AS weight")).filter(email == "sally@example.com")
-        let query4 = users.select(users[*], Expression<Int>(literal: "2 AS weight")).filter(email == "alice@example.com")
+        let query3 = users.select(users[*], SQLite.Expression<Int>(literal: "1 AS weight")).filter(email == "sally@example.com")
+        let query4 = users.select(users[*], SQLite.Expression<Int>(literal: "2 AS weight")).filter(email == "alice@example.com")
 
-        let sql = query3.union(query4).order(Expression<Int>(literal: "weight")).asSQL()
+        let sql = query3.union(query4).order(SQLite.Expression<Int>(literal: "weight")).asSQL()
         XCTAssertEqual(sql,
         """
         SELECT "users".*, 1 AS weight FROM "users" WHERE ("email" = 'sally@example.com') UNION \
         SELECT "users".*, 2 AS weight FROM "users" WHERE ("email" = 'alice@example.com') ORDER BY weight
         """)
 
-        let orderedIDs = try db.prepare(query3.union(query4).order(Expression<Int>(literal: "weight"), email)).map { $0[id] }
+        let orderedIDs = try db.prepare(query3.union(query4).order(SQLite.Expression<Int>(literal: "weight"), email)).map { $0[id] }
         XCTAssertEqual(Array(expectedIDs.reversed()), orderedIDs)
     }
 
     func test_no_such_column() throws {
-        let doesNotExist = Expression<String>("doesNotExist")
+        let doesNotExist = SQLite.Expression<String>("doesNotExist")
         try insertUser("alice")
         let row = try db.pluck(users.filter(email == "alice@example.com"))!
 
@@ -272,15 +272,15 @@ class QueryIntegrationTests: SQLiteTestCase {
     // https://github.com/stephencelis/SQLite.swift/issues/285
     func test_order_by_random() throws {
         try insertUsers(["a", "b", "c'"])
-        let result = Array(try db.prepare(users.select(email).order(Expression<Int>.random()).limit(1)))
+        let result = Array(try db.prepare(users.select(email).order(SQLite.Expression<Int>.random()).limit(1)))
         XCTAssertEqual(1, result.count)
     }
 
     func test_with_recursive() throws {
         let nodes = Table("nodes")
-        let id = Expression<Int64>("id")
-        let parent = Expression<Int64?>("parent")
-        let value = Expression<Int64>("value")
+        let id = SQLite.Expression<Int64>("id")
+        let parent = SQLite.Expression<Int64?>("parent")
+        let value = SQLite.Expression<Int64>("value")
 
         try db.run(nodes.create { builder in
             builder.column(id)
@@ -320,7 +320,7 @@ class QueryIntegrationTests: SQLiteTestCase {
     /// Verify that `*` is properly expanded in a SELECT statement following a WITH clause.
     func test_with_glob_expansion() throws {
         let names = Table("names")
-        let name = Expression<String>("name")
+        let name = SQLite.Expression<String>("name")
         try db.run(names.create { builder in
             builder.column(email)
             builder.column(name)
diff --git a/Tests/SQLiteTests/Typed/QueryTests.swift b/Tests/SQLiteTests/Typed/QueryTests.swift
index f698c621..f018f097 100644
--- a/Tests/SQLiteTests/Typed/QueryTests.swift
+++ b/Tests/SQLiteTests/Typed/QueryTests.swift
@@ -13,19 +13,19 @@ import SQLite3
 class QueryTests: XCTestCase {
 
     let users = Table("users")
-    let id = Expression<Int64>("id")
-    let email = Expression<String>("email")
-    let age = Expression<Int?>("age")
-    let admin = Expression<Bool>("admin")
-    let optionalAdmin = Expression<Bool?>("admin")
+    let id = SQLite.Expression<Int64>("id")
+    let email = SQLite.Expression<String>("email")
+    let age = SQLite.Expression<Int?>("age")
+    let admin = SQLite.Expression<Bool>("admin")
+    let optionalAdmin = SQLite.Expression<Bool?>("admin")
 
     let posts = Table("posts")
-    let userId = Expression<Int64>("user_id")
-    let categoryId = Expression<Int64>("category_id")
-    let published = Expression<Bool>("published")
+    let userId = SQLite.Expression<Int64>("user_id")
+    let categoryId = SQLite.Expression<Int64>("category_id")
+    let published = SQLite.Expression<Bool>("published")
 
     let categories = Table("categories")
-    let tag = Expression<String>("tag")
+    let tag = SQLite.Expression<String>("tag")
 
     func test_select_withExpression_compilesSelectClause() {
         assertSQL("SELECT \"email\" FROM \"users\"", users.select(email))
@@ -217,7 +217,7 @@ class QueryTests: XCTestCase {
     }
 
     func test_alias_aliasesTable() {
-        let managerId = Expression<Int64>("manager_id")
+        let managerId = SQLite.Expression<Int64>("manager_id")
 
         let managers = users.alias("managers")
 
@@ -422,7 +422,7 @@ class QueryTests: XCTestCase {
 
     func test_upsert_encodable() throws {
         let emails = Table("emails")
-        let string = Expression<String>("string")
+        let string = SQLite.Expression<String>("string")
         let value = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4,
                                 date: Date(timeIntervalSince1970: 0), uuid: testUUIDValue, optional: nil, sub: nil)
         let insert = try emails.upsert(value, onConflictOf: string)
diff --git a/Tests/SQLiteTests/Typed/RowTests.swift b/Tests/SQLiteTests/Typed/RowTests.swift
index 14fa373b..1aed00a7 100644
--- a/Tests/SQLiteTests/Typed/RowTests.swift
+++ b/Tests/SQLiteTests/Typed/RowTests.swift
@@ -5,49 +5,49 @@ class RowTests: XCTestCase {
 
     public func test_get_value() throws {
         let row = Row(["\"foo\"": 0], ["value"])
-        let result = try row.get(Expression<String>("foo"))
+        let result = try row.get(SQLite.Expression<String>("foo"))
 
         XCTAssertEqual("value", result)
     }
 
     public func test_get_value_subscript() {
         let row = Row(["\"foo\"": 0], ["value"])
-        let result = row[Expression<String>("foo")]
+        let result = row[SQLite.Expression<String>("foo")]
 
         XCTAssertEqual("value", result)
     }
 
     public func test_get_value_optional() throws {
         let row = Row(["\"foo\"": 0], ["value"])
-        let result = try row.get(Expression<String?>("foo"))
+        let result = try row.get(SQLite.Expression<String?>("foo"))
 
         XCTAssertEqual("value", result)
     }
 
     public func test_get_value_optional_subscript() {
         let row = Row(["\"foo\"": 0], ["value"])
-        let result = row[Expression<String?>("foo")]
+        let result = row[SQLite.Expression<String?>("foo")]
 
         XCTAssertEqual("value", result)
     }
 
     public func test_get_value_optional_nil() throws {
         let row = Row(["\"foo\"": 0], [nil])
-        let result = try row.get(Expression<String?>("foo"))
+        let result = try row.get(SQLite.Expression<String?>("foo"))
 
         XCTAssertNil(result)
     }
 
     public func test_get_value_optional_nil_subscript() {
         let row = Row(["\"foo\"": 0], [nil])
-        let result = row[Expression<String?>("foo")]
+        let result = row[SQLite.Expression<String?>("foo")]
 
         XCTAssertNil(result)
     }
 
     public func test_get_type_mismatch_throws_unexpected_null_value() {
         let row = Row(["\"foo\"": 0], ["value"])
-        XCTAssertThrowsError(try row.get(Expression<Int>("foo"))) { error in
+        XCTAssertThrowsError(try row.get(SQLite.Expression<Int>("foo"))) { error in
             if case QueryError.unexpectedNullValue(let name) = error {
                 XCTAssertEqual("\"foo\"", name)
             } else {
@@ -58,13 +58,13 @@ class RowTests: XCTestCase {
 
     public func test_get_type_mismatch_optional_returns_nil() throws {
         let row = Row(["\"foo\"": 0], ["value"])
-        let result = try row.get(Expression<Int?>("foo"))
+        let result = try row.get(SQLite.Expression<Int?>("foo"))
         XCTAssertNil(result)
     }
 
     public func test_get_non_existent_column_throws_no_such_column() {
         let row = Row(["\"foo\"": 0], ["value"])
-        XCTAssertThrowsError(try row.get(Expression<Int>("bar"))) { error in
+        XCTAssertThrowsError(try row.get(SQLite.Expression<Int>("bar"))) { error in
             if case QueryError.noSuchColumn(let name, let columns) = error {
                 XCTAssertEqual("\"bar\"", name)
                 XCTAssertEqual(["\"foo\""], columns)
@@ -76,7 +76,7 @@ class RowTests: XCTestCase {
 
     public func test_get_ambiguous_column_throws() {
         let row = Row(["table1.\"foo\"": 0, "table2.\"foo\"": 1], ["value"])
-        XCTAssertThrowsError(try row.get(Expression<Int>("foo"))) { error in
+        XCTAssertThrowsError(try row.get(SQLite.Expression<Int>("foo"))) { error in
             if case QueryError.ambiguousColumn(let name, let columns) = error {
                 XCTAssertEqual("\"foo\"", name)
                 XCTAssertEqual(["table1.\"foo\"", "table2.\"foo\""], columns.sorted())
@@ -107,7 +107,7 @@ class RowTests: XCTestCase {
         }
 
         let row = Row(["\"foo\"": 0], [Blob(bytes: [])])
-        XCTAssertThrowsError(try row.get(Expression<MyType>("foo"))) { error in
+        XCTAssertThrowsError(try row.get(SQLite.Expression<MyType>("foo"))) { error in
             if case MyType.MyError.failed = error {
                 XCTAssertTrue(true)
             } else {
diff --git a/Tests/SQLiteTests/Typed/SelectTests.swift b/Tests/SQLiteTests/Typed/SelectTests.swift
index 52d5bb6b..5fa3cd30 100644
--- a/Tests/SQLiteTests/Typed/SelectTests.swift
+++ b/Tests/SQLiteTests/Typed/SelectTests.swift
@@ -24,10 +24,10 @@ class SelectTests: SQLiteTestCase {
         let usersData = Table("users_name")
         let users = Table("users")
 
-        let name = Expression<String>("name")
-        let id = Expression<Int64>("id")
-        let userID = Expression<Int64>("user_id")
-        let email = Expression<String>("email")
+        let name = SQLite.Expression<String>("name")
+        let id = SQLite.Expression<Int64>("id")
+        let userID = SQLite.Expression<Int64>("user_id")
+        let email = SQLite.Expression<String>("email")
 
         try insertUser("Joey")
         try db.run(usersData.insert(
diff --git a/Tests/SQLiteTests/Typed/SetterTests.swift b/Tests/SQLiteTests/Typed/SetterTests.swift
index 938dd013..05da57a4 100644
--- a/Tests/SQLiteTests/Typed/SetterTests.swift
+++ b/Tests/SQLiteTests/Typed/SetterTests.swift
@@ -134,4 +134,7 @@ class SetterTests: XCTestCase {
         assertSQL("\"intOptional\" = (\"intOptional\" - 1)", intOptional--)
     }
 
+    func test_setter_custom_string_convertible() {
+        XCTAssertEqual("\"int\" = \"int\"", (int <- int).description)
+    }
 }
diff --git a/Tests/SQLiteTests/Typed/WindowFunctionsTests.swift b/Tests/SQLiteTests/Typed/WindowFunctionsTests.swift
index 6ded152b..01e88297 100644
--- a/Tests/SQLiteTests/Typed/WindowFunctionsTests.swift
+++ b/Tests/SQLiteTests/Typed/WindowFunctionsTests.swift
@@ -34,13 +34,13 @@ class WindowFunctionsTests: XCTestCase {
     func test_lag_wrapsExpressionWithOverClause() {
         assertSQL("lag(\"int\", 0) OVER (ORDER BY \"int\" DESC)", int.lag(int.desc))
         assertSQL("lag(\"int\", 7) OVER (ORDER BY \"int\" DESC)", int.lag(offset: 7, int.desc))
-        assertSQL("lag(\"int\", 1, 3) OVER (ORDER BY \"int\" DESC)", int.lag(offset: 1, default: Expression<Int>(value: 3), int.desc))
+        assertSQL("lag(\"int\", 1, 3) OVER (ORDER BY \"int\" DESC)", int.lag(offset: 1, default: SQLite.Expression<Int>(value: 3), int.desc))
     }
 
     func test_lead_wrapsExpressionWithOverClause() {
         assertSQL("lead(\"int\", 0) OVER (ORDER BY \"int\" DESC)", int.lead(int.desc))
         assertSQL("lead(\"int\", 7) OVER (ORDER BY \"int\" DESC)", int.lead(offset: 7, int.desc))
-        assertSQL("lead(\"int\", 1, 3) OVER (ORDER BY \"int\" DESC)", int.lead(offset: 1, default: Expression<Int>(value: 3), int.desc))
+        assertSQL("lead(\"int\", 1, 3) OVER (ORDER BY \"int\" DESC)", int.lead(offset: 1, default: SQLite.Expression<Int>(value: 3), int.desc))
     }
 
     func test_firstValue_wrapsExpressionWithOverClause() {

From 4a5eda39a5aa63b4151b54e13ac9cd6ff3390a7e Mon Sep 17 00:00:00 2001
From: Cary Clark <clarkcb@gmail.com>
Date: Fri, 4 Oct 2024 15:54:10 -0700
Subject: [PATCH 285/326] Update oldest supported platform versions

---
 Package.swift | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Package.swift b/Package.swift
index 238661ae..12a2f11c 100644
--- a/Package.swift
+++ b/Package.swift
@@ -4,10 +4,10 @@ import PackageDescription
 let package = Package(
     name: "SQLite.swift",
     platforms: [
-        .iOS(.v11),
+        .iOS(.v12),
         .macOS(.v10_13),
         .watchOS(.v4),
-        .tvOS(.v11),
+        .tvOS(.v12),
         .visionOS(.v1)
     ],
     products: [

From 748dcba3312485151fa91b551a4564238f2b69b9 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Tue, 20 May 2025 18:35:08 +0200
Subject: [PATCH 286/326] Support creating tables in schema changer

---
 Makefile                                      |  2 +-
 Sources/SQLite/Schema/SchemaChanger.swift     | 49 +++++++++++++++++++
 Sources/SQLite/Schema/SchemaDefinitions.swift |  5 +-
 .../Schema/SchemaChangerTests.swift           | 46 +++++++++++++++++
 4 files changed, 100 insertions(+), 2 deletions(-)

diff --git a/Makefile b/Makefile
index 73b33f97..52a25a12 100644
--- a/Makefile
+++ b/Makefile
@@ -35,7 +35,7 @@ lint: $(SWIFTLINT)
 	$< --strict
 
 lint-fix: $(SWIFTLINT)
-	$< lint fix
+	$< --fix
 
 clean:
 	$(XCODEBUILD) $(BUILD_ARGUMENTS) clean
diff --git a/Sources/SQLite/Schema/SchemaChanger.swift b/Sources/SQLite/Schema/SchemaChanger.swift
index af7b5e27..b6ed7312 100644
--- a/Sources/SQLite/Schema/SchemaChanger.swift
+++ b/Sources/SQLite/Schema/SchemaChanger.swift
@@ -43,19 +43,31 @@ public class SchemaChanger: CustomStringConvertible {
 
     public enum Operation {
         case addColumn(ColumnDefinition)
+        case addIndex(IndexDefinition)
         case dropColumn(String)
         case renameColumn(String, String)
         case renameTable(String)
+        case createTable(columns: [ColumnDefinition])
 
         /// Returns non-nil if the operation can be executed with a simple SQL statement
         func toSQL(_ table: String, version: SQLiteVersion) -> String? {
             switch self {
             case .addColumn(let definition):
                 return "ALTER TABLE \(table.quote()) ADD COLUMN \(definition.toSQL())"
+            case .addIndex(let definition):
+                let unique = definition.unique ? "UNIQUE" : ""
+                let columns = definition.columns.joined(separator: ", ")
+                let `where` = definition.where.map { " WHERE " + $0 } ?? ""
+
+                return "CREATE \(unique) INDEX \(definition.name) ON \(definition.table) (\(columns)) \(`where`)"
             case .renameColumn(let from, let to) where SQLiteFeature.renameColumn.isSupported(by: version):
                 return "ALTER TABLE \(table.quote()) RENAME COLUMN \(from.quote()) TO \(to.quote())"
             case .dropColumn(let column) where SQLiteFeature.dropColumn.isSupported(by: version):
                 return "ALTER TABLE \(table.quote()) DROP COLUMN \(column.quote())"
+            case .createTable(let columns):
+                return "CREATE TABLE \(table.quote()) (" +
+                    columns.map { $0.toSQL() }.joined(separator: ", ") +
+                ")"
             default: return nil
             }
         }
@@ -108,12 +120,39 @@ public class SchemaChanger: CustomStringConvertible {
         }
     }
 
+    public class CreateTableDefinition {
+        fileprivate var columnDefinitions: [ColumnDefinition] = []
+        fileprivate var indexDefinitions: [IndexDefinition] = []
+
+        let name: String
+
+        init(name: String) {
+            self.name = name
+        }
+
+        public func add(column: ColumnDefinition) {
+            columnDefinitions.append(column)
+        }
+
+        public func add(index: IndexDefinition) {
+            indexDefinitions.append(index)
+        }
+
+        var operations: [Operation] {
+            precondition(!columnDefinitions.isEmpty)
+            return [
+                .createTable(columns: columnDefinitions)
+            ] + indexDefinitions.map { .addIndex($0) }
+        }
+    }
+
     private let connection: Connection
     private let schemaReader: SchemaReader
     private let version: SQLiteVersion
     static let tempPrefix = "tmp_"
     typealias Block = () throws -> Void
     public typealias AlterTableDefinitionBlock = (AlterTableDefinition) -> Void
+    public typealias CreateTableDefinitionBlock = (CreateTableDefinition) -> Void
 
     struct Options: OptionSet {
         let rawValue: Int
@@ -141,6 +180,15 @@ public class SchemaChanger: CustomStringConvertible {
         }
     }
 
+    public func create(table: String, ifNotExists: Bool = false, block: CreateTableDefinitionBlock) throws {
+        let createTableDefinition = CreateTableDefinition(name: table)
+        block(createTableDefinition)
+
+        for operation in createTableDefinition.operations {
+            try run(table: table, operation: operation)
+        }
+    }
+
     public func drop(table: String, ifExists: Bool = true) throws {
         try dropTable(table, ifExists: ifExists)
     }
@@ -263,6 +311,7 @@ extension TableDefinition {
     func apply(_ operation: SchemaChanger.Operation?) -> TableDefinition {
         switch operation {
         case .none: return self
+        case .createTable, .addIndex: fatalError()
         case .addColumn: fatalError("Use 'ALTER TABLE ADD COLUMN (...)'")
         case .dropColumn(let column):
             return TableDefinition(name: name,
diff --git a/Sources/SQLite/Schema/SchemaDefinitions.swift b/Sources/SQLite/Schema/SchemaDefinitions.swift
index 80f9e199..60837244 100644
--- a/Sources/SQLite/Schema/SchemaDefinitions.swift
+++ b/Sources/SQLite/Schema/SchemaDefinitions.swift
@@ -270,12 +270,15 @@ public struct IndexDefinition: Equatable {
                         return memo2
             }
         }
+
+        let orders = indexSQL.flatMap(orders)
+
         self.init(table: table,
                   name: name,
                   unique: unique,
                   columns: columns,
                   where: indexSQL.flatMap(wherePart),
-                  orders: indexSQL.flatMap(orders))
+                  orders: (orders?.isEmpty ?? false) ? nil : orders)
     }
 
     public let table: String
diff --git a/Tests/SQLiteTests/Schema/SchemaChangerTests.swift b/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
index 2bec6a06..125ef09e 100644
--- a/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
+++ b/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
@@ -154,4 +154,50 @@ class SchemaChangerTests: SQLiteTestCase {
         let users_new = Table("users_new")
         XCTAssertEqual((try db.scalar(users_new.count)) as Int, 1)
     }
+
+    func test_create_table() throws {
+        try schemaChanger.create(table: "foo") { table in
+            table.add(column: .init(name: "id", primaryKey: .init(autoIncrement: true), type: .INTEGER))
+            table.add(column: .init(name: "name", type: .TEXT, nullable: false))
+            table.add(column: .init(name: "age", type: .INTEGER))
+
+            table.add(index: .init(table: table.name,
+                                   name: "nameIndex",
+                                   unique: true,
+                                   columns: ["name"],
+                                   where: nil,
+                                   orders: nil))
+        }
+
+        // make sure new table can be queried
+        let foo = Table("foo")
+        XCTAssertEqual((try db.scalar(foo.count)) as Int, 0)
+
+        let columns = try schema.columnDefinitions(table: "foo")
+        XCTAssertEqual(columns, [
+            ColumnDefinition(name: "id",
+                             primaryKey: .init(autoIncrement: true, onConflict: nil),
+                             type: .INTEGER,
+                             nullable: true,
+                             defaultValue: .NULL,
+                             references: nil),
+            ColumnDefinition(name: "name",
+                             primaryKey: nil,
+                             type: .TEXT,
+                             nullable: false,
+                             defaultValue: .NULL,
+                             references: nil),
+            ColumnDefinition(name: "age",
+                             primaryKey: nil,
+                             type: .INTEGER,
+                             nullable: true,
+                             defaultValue: .NULL,
+                             references: nil)
+        ])
+
+        let indexes = try schema.indexDefinitions(table: "foo")
+        XCTAssertEqual(indexes, [
+            IndexDefinition(table: "foo", name: "nameIndex", unique: true, columns: ["name"], where: nil, orders: nil)
+        ])
+    }
 }

From 261f98ae268f986ec6e54822d45d9bda263bdd50 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Tue, 20 May 2025 22:17:45 +0200
Subject: [PATCH 287/326] Update docs

---
 Documentation/Index.md | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/Documentation/Index.md b/Documentation/Index.md
index fcd2d642..bc62c791 100644
--- a/Documentation/Index.md
+++ b/Documentation/Index.md
@@ -63,6 +63,7 @@
       - [Renaming Columns](#renaming-columns)
       - [Dropping Columns](#dropping-columns)
       - [Renaming/Dropping Tables](#renamingdropping-tables)
+      - [Creating Tables](#creating-tables)      
     - [Indexes](#indexes)
       - [Creating Indexes](#creating-indexes)
       - [Dropping Indexes](#dropping-indexes)
@@ -1583,6 +1584,16 @@ try schemaChanger.rename(table: "users", to: "users_new")
 try schemaChanger.drop(table: "emails", ifExists: false)
 ```
 
+#### Creating Tables
+
+```swift
+let schemaChanger = SchemaChanger(connection: db)
+
+try schemaChanger.create(table: "users") { table in 
+    table.add(column: .init(name: "id", primaryKey: .init(autoIncrement: true), type: .INTEGER))
+    table.add(column: .init(name: "name", type: .TEXT, nullable: false))            
+} 
+
 ### Indexes
 
 

From e21fde28f0f96cc452151aaa17413b198f3718a9 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Tue, 20 May 2025 22:18:37 +0200
Subject: [PATCH 288/326] Revert to standard cocoapods

---
 Gemfile      |  2 +-
 Gemfile.lock | 93 ++++++++++++++++++++++++++--------------------------
 2 files changed, 48 insertions(+), 47 deletions(-)

diff --git a/Gemfile b/Gemfile
index eb6036cd..1f2ebb87 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,3 +1,3 @@
 source "https://rubygems.org"
 
-gem 'cocoapods', :git => 'https://github.com/SagarSDagdu/CocoaPods.git', tag: '1.15.2.1-sagard'
+gem 'cocoapods' 
diff --git a/Gemfile.lock b/Gemfile.lock
index a58783ea..464ef06b 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,27 +1,3 @@
-GIT
-  remote: https://github.com/SagarSDagdu/CocoaPods.git
-  revision: d96f491f79abd2804d1359c5228cce404dd365b7
-  tag: 1.15.2.1-sagard
-  specs:
-    cocoapods (1.15.2.1.pre.sagard)
-      addressable (~> 2.8)
-      claide (>= 1.0.2, < 2.0)
-      cocoapods-core (= 1.15.2)
-      cocoapods-deintegrate (>= 1.0.3, < 2.0)
-      cocoapods-downloader (>= 2.1, < 3.0)
-      cocoapods-plugins (>= 1.0.0, < 2.0)
-      cocoapods-search (>= 1.0.0, < 2.0)
-      cocoapods-trunk (>= 1.6.0, < 2.0)
-      cocoapods-try (>= 1.1.0, < 2.0)
-      colored2 (~> 3.1)
-      escape (~> 0.0.4)
-      fourflusher (>= 2.3.0, < 3.0)
-      gh_inspector (~> 1.0)
-      molinillo (~> 0.8.0)
-      nap (~> 1.0)
-      ruby-macho (>= 2.3.0, < 3.0)
-      xcodeproj (>= 1.23.0, < 2.0)
-
 GEM
   remote: https://rubygems.org/
   specs:
@@ -29,26 +5,47 @@ GEM
       base64
       nkf
       rexml
-    activesupport (7.1.3.2)
+    activesupport (7.2.2.1)
       base64
+      benchmark (>= 0.3)
       bigdecimal
-      concurrent-ruby (~> 1.0, >= 1.0.2)
+      concurrent-ruby (~> 1.0, >= 1.3.1)
       connection_pool (>= 2.2.5)
       drb
       i18n (>= 1.6, < 2)
+      logger (>= 1.4.2)
       minitest (>= 5.1)
-      mutex_m
-      tzinfo (~> 2.0)
-    addressable (2.8.6)
-      public_suffix (>= 2.0.2, < 6.0)
+      securerandom (>= 0.3)
+      tzinfo (~> 2.0, >= 2.0.5)
+    addressable (2.8.7)
+      public_suffix (>= 2.0.2, < 7.0)
     algoliasearch (1.27.5)
       httpclient (~> 2.8, >= 2.8.3)
       json (>= 1.5.1)
     atomos (0.1.3)
     base64 (0.2.0)
-    bigdecimal (3.1.7)
+    benchmark (0.4.0)
+    bigdecimal (3.1.9)
     claide (1.1.0)
-    cocoapods-core (1.15.2)
+    cocoapods (1.16.2)
+      addressable (~> 2.8)
+      claide (>= 1.0.2, < 2.0)
+      cocoapods-core (= 1.16.2)
+      cocoapods-deintegrate (>= 1.0.3, < 2.0)
+      cocoapods-downloader (>= 2.1, < 3.0)
+      cocoapods-plugins (>= 1.0.0, < 2.0)
+      cocoapods-search (>= 1.0.0, < 2.0)
+      cocoapods-trunk (>= 1.6.0, < 2.0)
+      cocoapods-try (>= 1.1.0, < 2.0)
+      colored2 (~> 3.1)
+      escape (~> 0.0.4)
+      fourflusher (>= 2.3.0, < 3.0)
+      gh_inspector (~> 1.0)
+      molinillo (~> 0.8.0)
+      nap (~> 1.0)
+      ruby-macho (>= 2.3.0, < 3.0)
+      xcodeproj (>= 1.27.0, < 2.0)
+    cocoapods-core (1.16.2)
       activesupport (>= 5.0, < 8)
       addressable (~> 2.8)
       algoliasearch (~> 1.0)
@@ -68,48 +65,52 @@ GEM
       netrc (~> 0.11)
     cocoapods-try (1.2.0)
     colored2 (3.1.2)
-    concurrent-ruby (1.2.3)
-    connection_pool (2.4.1)
+    concurrent-ruby (1.3.5)
+    connection_pool (2.5.3)
     drb (2.2.1)
     escape (0.0.4)
     ethon (0.16.0)
       ffi (>= 1.15.0)
-    ffi (1.16.3)
+    ffi (1.17.2)
+    ffi (1.17.2-arm64-darwin)
     fourflusher (2.3.1)
     fuzzy_match (2.0.4)
     gh_inspector (1.1.3)
-    httpclient (2.8.3)
-    i18n (1.14.4)
+    httpclient (2.9.0)
+      mutex_m
+    i18n (1.14.7)
       concurrent-ruby (~> 1.0)
-    json (2.7.2)
-    minitest (5.22.3)
+    json (2.12.0)
+    logger (1.7.0)
+    minitest (5.25.5)
     molinillo (0.8.0)
-    mutex_m (0.2.0)
-    nanaimo (0.3.0)
+    mutex_m (0.3.0)
+    nanaimo (0.4.0)
     nap (1.1.0)
     netrc (0.11.0)
     nkf (0.2.0)
     public_suffix (4.0.7)
-    rexml (3.2.6)
+    rexml (3.4.1)
     ruby-macho (2.5.1)
+    securerandom (0.4.1)
     typhoeus (1.4.1)
       ethon (>= 0.9.0)
     tzinfo (2.0.6)
       concurrent-ruby (~> 1.0)
-    xcodeproj (1.24.0)
+    xcodeproj (1.27.0)
       CFPropertyList (>= 2.3.3, < 4.0)
       atomos (~> 0.1.3)
       claide (>= 1.0.2, < 2.0)
       colored2 (~> 3.1)
-      nanaimo (~> 0.3.0)
-      rexml (~> 3.2.4)
+      nanaimo (~> 0.4.0)
+      rexml (>= 3.3.6, < 4.0)
 
 PLATFORMS
   arm64-darwin-23
   ruby
 
 DEPENDENCIES
-  cocoapods!
+  cocoapods
 
 BUNDLED WITH
    2.5.4

From 5ef20d1887e4b8763df890c4f1ffc256b3f79b99 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Tue, 20 May 2025 22:39:15 +0200
Subject: [PATCH 289/326] Don't lint against visionOS

---
 run-tests.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/run-tests.sh b/run-tests.sh
index 1bf8de96..cb68a022 100755
--- a/run-tests.sh
+++ b/run-tests.sh
@@ -11,7 +11,7 @@ elif [ -n "$VALIDATOR_SUBSPEC" ]; then
     if [ "$VALIDATOR_SUBSPEC" == "none" ]; then
       bundle exec pod lib lint --no-subspecs --fail-fast
     else
-      bundle exec pod lib lint --subspec="${VALIDATOR_SUBSPEC}" --fail-fast
+      bundle exec pod lib lint --subspec="${VALIDATOR_SUBSPEC}" --fail-fast --platforms=macos,ios,tvos,watchos
     fi
 elif [ -n "$CARTHAGE_PLATFORM" ]; then
     cd Tests/Carthage && make test CARTHAGE_PLATFORM="$CARTHAGE_PLATFORM"

From 017929080c146c2085b21adcce95ff67b9e82d2e Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Tue, 20 May 2025 23:14:13 +0200
Subject: [PATCH 290/326] Disable visionos just for standalone

---
 run-tests.sh | 20 +++++++++++++-------
 1 file changed, 13 insertions(+), 7 deletions(-)

diff --git a/run-tests.sh b/run-tests.sh
index cb68a022..94f643e4 100755
--- a/run-tests.sh
+++ b/run-tests.sh
@@ -8,15 +8,21 @@ if [ -n "$BUILD_SCHEME" ]; then
     fi
 elif [ -n "$VALIDATOR_SUBSPEC" ]; then
     bundle install
-    if [ "$VALIDATOR_SUBSPEC" == "none" ]; then
-      bundle exec pod lib lint --no-subspecs --fail-fast
-    else
-      bundle exec pod lib lint --subspec="${VALIDATOR_SUBSPEC}" --fail-fast --platforms=macos,ios,tvos,watchos
-    fi
+    case "$VALIDATOR_SUBSPEC" in
+      none)
+        bundle exec pod lib lint --no-subspecs --fail-fast
+        ;;
+      standalone)
+        bundle exec pod lib lint --subspec="${VALIDATOR_SUBSPEC}" --fail-fast --platforms=macos,ios,tvos,watchos
+        ;;
+      *)
+        bundle exec pod lib lint --subspec="${VALIDATOR_SUBSPEC}" --fail-fast
+        ;;
+      esac
 elif [ -n "$CARTHAGE_PLATFORM" ]; then
     cd Tests/Carthage && make test CARTHAGE_PLATFORM="$CARTHAGE_PLATFORM"
 elif [ -n "$SPM" ]; then
-    cd Tests/SPM && swift ${SPM}
+    cd Tests/SPM && swift "${SPM}"
 elif [ -n "${PACKAGE_MANAGER_COMMAND}" ]; then
-    swift ${PACKAGE_MANAGER_COMMAND}
+    swift "${PACKAGE_MANAGER_COMMAND}"
 fi

From 023c5039780b1c086f1d4bbea1c6d5a683e3041f Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Tue, 20 May 2025 23:18:21 +0200
Subject: [PATCH 291/326] Enable word splitting

---
 run-tests.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/run-tests.sh b/run-tests.sh
index 94f643e4..7d5c26d2 100755
--- a/run-tests.sh
+++ b/run-tests.sh
@@ -24,5 +24,5 @@ elif [ -n "$CARTHAGE_PLATFORM" ]; then
 elif [ -n "$SPM" ]; then
     cd Tests/SPM && swift "${SPM}"
 elif [ -n "${PACKAGE_MANAGER_COMMAND}" ]; then
-    swift "${PACKAGE_MANAGER_COMMAND}"
+    swift ${PACKAGE_MANAGER_COMMAND}
 fi

From 673367b772b04a8df0be64e757b2979da8798939 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Tue, 20 May 2025 23:36:41 +0200
Subject: [PATCH 292/326] Public init

---
 Sources/SQLite/Schema/SchemaDefinitions.swift | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Sources/SQLite/Schema/SchemaDefinitions.swift b/Sources/SQLite/Schema/SchemaDefinitions.swift
index 60837244..897f8557 100644
--- a/Sources/SQLite/Schema/SchemaDefinitions.swift
+++ b/Sources/SQLite/Schema/SchemaDefinitions.swift
@@ -93,7 +93,7 @@ public struct ColumnDefinition: Equatable {
         // swiftlint:disable:next force_try
         static let pattern = try! NSRegularExpression(pattern: "PRIMARY KEY\\s*(?:ASC|DESC)?\\s*(?:ON CONFLICT (\\w+)?)?\\s*(AUTOINCREMENT)?")
 
-        init(autoIncrement: Bool = true, onConflict: OnConflict? = nil) {
+        public init(autoIncrement: Bool = true, onConflict: OnConflict? = nil) {
             self.autoIncrement = autoIncrement
             self.onConflict = onConflict
         }

From 5de1420d58a87e082bb3659da9b59a19687cb867 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Wed, 21 May 2025 14:23:31 +0200
Subject: [PATCH 293/326] Support ifNotExists

---
 Sources/SQLite/Schema/SchemaChanger.swift     | 14 +++++++-----
 .../Schema/SchemaChangerTests.swift           | 22 +++++++++++++++++++
 2 files changed, 30 insertions(+), 6 deletions(-)

diff --git a/Sources/SQLite/Schema/SchemaChanger.swift b/Sources/SQLite/Schema/SchemaChanger.swift
index b6ed7312..e2e3d5fa 100644
--- a/Sources/SQLite/Schema/SchemaChanger.swift
+++ b/Sources/SQLite/Schema/SchemaChanger.swift
@@ -47,7 +47,7 @@ public class SchemaChanger: CustomStringConvertible {
         case dropColumn(String)
         case renameColumn(String, String)
         case renameTable(String)
-        case createTable(columns: [ColumnDefinition])
+        case createTable(columns: [ColumnDefinition], ifNotExists: Bool)
 
         /// Returns non-nil if the operation can be executed with a simple SQL statement
         func toSQL(_ table: String, version: SQLiteVersion) -> String? {
@@ -64,8 +64,8 @@ public class SchemaChanger: CustomStringConvertible {
                 return "ALTER TABLE \(table.quote()) RENAME COLUMN \(from.quote()) TO \(to.quote())"
             case .dropColumn(let column) where SQLiteFeature.dropColumn.isSupported(by: version):
                 return "ALTER TABLE \(table.quote()) DROP COLUMN \(column.quote())"
-            case .createTable(let columns):
-                return "CREATE TABLE \(table.quote()) (" +
+            case .createTable(let columns, let ifNotExists):
+                return "CREATE TABLE \(ifNotExists ? " IF NOT EXISTS " : "") \(table.quote()) (" +
                     columns.map { $0.toSQL() }.joined(separator: ", ") +
                 ")"
             default: return nil
@@ -125,9 +125,11 @@ public class SchemaChanger: CustomStringConvertible {
         fileprivate var indexDefinitions: [IndexDefinition] = []
 
         let name: String
+        let ifNotExists: Bool
 
-        init(name: String) {
+        init(name: String, ifNotExists: Bool) {
             self.name = name
+            self.ifNotExists = ifNotExists
         }
 
         public func add(column: ColumnDefinition) {
@@ -141,7 +143,7 @@ public class SchemaChanger: CustomStringConvertible {
         var operations: [Operation] {
             precondition(!columnDefinitions.isEmpty)
             return [
-                .createTable(columns: columnDefinitions)
+                .createTable(columns: columnDefinitions, ifNotExists: ifNotExists)
             ] + indexDefinitions.map { .addIndex($0) }
         }
     }
@@ -181,7 +183,7 @@ public class SchemaChanger: CustomStringConvertible {
     }
 
     public func create(table: String, ifNotExists: Bool = false, block: CreateTableDefinitionBlock) throws {
-        let createTableDefinition = CreateTableDefinition(name: table)
+        let createTableDefinition = CreateTableDefinition(name: table, ifNotExists: ifNotExists)
         block(createTableDefinition)
 
         for operation in createTableDefinition.operations {
diff --git a/Tests/SQLiteTests/Schema/SchemaChangerTests.swift b/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
index 125ef09e..bba16e08 100644
--- a/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
+++ b/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
@@ -200,4 +200,26 @@ class SchemaChangerTests: SQLiteTestCase {
             IndexDefinition(table: "foo", name: "nameIndex", unique: true, columns: ["name"], where: nil, orders: nil)
         ])
     }
+
+    func test_create_table_if_not_exists() throws {
+        try schemaChanger.create(table: "foo") { table in
+            table.add(column: .init(name: "id", primaryKey: .init(autoIncrement: true), type: .INTEGER))
+        }
+
+        try schemaChanger.create(table: "foo", ifNotExists: true) { table in
+            table.add(column: .init(name: "id", primaryKey: .init(autoIncrement: true), type: .INTEGER))
+        }
+
+        XCTAssertThrowsError(
+            try schemaChanger.create(table: "foo", ifNotExists: false) { table in
+                table.add(column: .init(name: "id", primaryKey: .init(autoIncrement: true), type: .INTEGER))
+            }
+        ) { error in
+            if case Result.error(_, let code, _) = error {
+                XCTAssertEqual(code, 1)
+            } else {
+                XCTFail("unexpected error: \(error)")
+            }
+        }
+    }
 }

From f2a86841f527f756610797d56b6c49a46c947f41 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Wed, 21 May 2025 17:24:57 +0200
Subject: [PATCH 294/326] Add columns via Expressions

---
 Sources/SQLite/Schema/SchemaChanger.swift     | 25 ++++++++++++++++
 .../Schema/SchemaChangerTests.swift           | 30 +++++++++++++++++++
 2 files changed, 55 insertions(+)

diff --git a/Sources/SQLite/Schema/SchemaChanger.swift b/Sources/SQLite/Schema/SchemaChanger.swift
index e2e3d5fa..d3342032 100644
--- a/Sources/SQLite/Schema/SchemaChanger.swift
+++ b/Sources/SQLite/Schema/SchemaChanger.swift
@@ -136,6 +136,14 @@ public class SchemaChanger: CustomStringConvertible {
             columnDefinitions.append(column)
         }
 
+        public func add<T>(expression: Expression<T>) where T: Value {
+            add(column: .init(name: columnName(for: expression), type: .init(expression: expression), nullable: false))
+        }
+
+        public func add<T>(expression: Expression<T?>) where T: Value {
+            add(column: .init(name: columnName(for: expression), type: .init(expression: expression), nullable: true))
+        }
+
         public func add(index: IndexDefinition) {
             indexDefinitions.append(index)
         }
@@ -146,6 +154,13 @@ public class SchemaChanger: CustomStringConvertible {
                 .createTable(columns: columnDefinitions, ifNotExists: ifNotExists)
             ] + indexDefinitions.map { .addIndex($0) }
         }
+
+        private func columnName<T>(for expression: Expression<T>) -> String {
+            switch LiteralValue(expression.template) {
+            case .stringLiteral(let string): return string
+            default: fatalError("expression is not a literal string value")
+            }
+        }
     }
 
     private let connection: Connection
@@ -331,3 +346,13 @@ extension TableDefinition {
         }
     }
 }
+
+extension ColumnDefinition.Affinity {
+    init<T>(expression: Expression<T>) where T: Value {
+        self.init(T.declaredDatatype)
+    }
+
+    init<T>(expression: Expression<T?>) where T: Value {
+        self.init(T.declaredDatatype)
+    }
+}
diff --git a/Tests/SQLiteTests/Schema/SchemaChangerTests.swift b/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
index bba16e08..6b53c9ce 100644
--- a/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
+++ b/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
@@ -201,6 +201,36 @@ class SchemaChangerTests: SQLiteTestCase {
         ])
     }
 
+    func test_create_table_add_column_expression() throws {
+        try schemaChanger.create(table: "foo") { table in
+            table.add(expression: Expression<String>("name"))
+            table.add(expression: Expression<Int>("age"))
+            table.add(expression: Expression<Double?>("salary"))
+        }
+
+        let columns = try schema.columnDefinitions(table: "foo")
+        XCTAssertEqual(columns, [
+            ColumnDefinition(name: "name",
+                             primaryKey: nil,
+                             type: .TEXT,
+                             nullable: false,
+                             defaultValue: .NULL,
+                             references: nil),
+            ColumnDefinition(name: "age",
+                             primaryKey: nil,
+                             type: .INTEGER,
+                             nullable: false,
+                             defaultValue: .NULL,
+                             references: nil),
+            ColumnDefinition(name: "salary",
+                             primaryKey: nil,
+                             type: .REAL,
+                             nullable: true,
+                             defaultValue: .NULL,
+                             references: nil)
+            ])
+    }
+
     func test_create_table_if_not_exists() throws {
         try schemaChanger.create(table: "foo") { table in
             table.add(column: .init(name: "id", primaryKey: .init(autoIncrement: true), type: .INTEGER))

From cdaade15b93a43a5db8038da05da1c30c997ea3d Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Wed, 21 May 2025 18:45:26 +0200
Subject: [PATCH 295/326] Respect ifNotExists for index

---
 Sources/SQLite/Schema/SchemaChanger.swift         | 12 ++++--------
 Tests/SQLiteTests/Schema/SchemaChangerTests.swift | 14 ++++++++++++++
 2 files changed, 18 insertions(+), 8 deletions(-)

diff --git a/Sources/SQLite/Schema/SchemaChanger.swift b/Sources/SQLite/Schema/SchemaChanger.swift
index d3342032..13222dab 100644
--- a/Sources/SQLite/Schema/SchemaChanger.swift
+++ b/Sources/SQLite/Schema/SchemaChanger.swift
@@ -43,7 +43,7 @@ public class SchemaChanger: CustomStringConvertible {
 
     public enum Operation {
         case addColumn(ColumnDefinition)
-        case addIndex(IndexDefinition)
+        case addIndex(IndexDefinition, ifNotExists: Bool)
         case dropColumn(String)
         case renameColumn(String, String)
         case renameTable(String)
@@ -54,12 +54,8 @@ public class SchemaChanger: CustomStringConvertible {
             switch self {
             case .addColumn(let definition):
                 return "ALTER TABLE \(table.quote()) ADD COLUMN \(definition.toSQL())"
-            case .addIndex(let definition):
-                let unique = definition.unique ? "UNIQUE" : ""
-                let columns = definition.columns.joined(separator: ", ")
-                let `where` = definition.where.map { " WHERE " + $0 } ?? ""
-
-                return "CREATE \(unique) INDEX \(definition.name) ON \(definition.table) (\(columns)) \(`where`)"
+            case .addIndex(let definition, let ifNotExists):
+                return definition.toSQL(ifNotExists: ifNotExists)
             case .renameColumn(let from, let to) where SQLiteFeature.renameColumn.isSupported(by: version):
                 return "ALTER TABLE \(table.quote()) RENAME COLUMN \(from.quote()) TO \(to.quote())"
             case .dropColumn(let column) where SQLiteFeature.dropColumn.isSupported(by: version):
@@ -152,7 +148,7 @@ public class SchemaChanger: CustomStringConvertible {
             precondition(!columnDefinitions.isEmpty)
             return [
                 .createTable(columns: columnDefinitions, ifNotExists: ifNotExists)
-            ] + indexDefinitions.map { .addIndex($0) }
+            ] + indexDefinitions.map { .addIndex($0, ifNotExists: ifNotExists) }
         }
 
         private func columnName<T>(for expression: Expression<T>) -> String {
diff --git a/Tests/SQLiteTests/Schema/SchemaChangerTests.swift b/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
index 6b53c9ce..02a82267 100644
--- a/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
+++ b/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
@@ -252,4 +252,18 @@ class SchemaChangerTests: SQLiteTestCase {
             }
         }
     }
+
+    func test_create_table_if_not_exists_with_index() throws {
+        try schemaChanger.create(table: "foo") { table in
+            table.add(column: .init(name: "id", primaryKey: .init(autoIncrement: true), type: .INTEGER))
+            table.add(column: .init(name: "name", type: .TEXT))
+            table.add(index: .init(table: "foo", name: "name_index", unique: true, columns: ["name"], indexSQL: nil))
+        }
+
+        // ifNotExists needs to apply to index creation as well
+        try schemaChanger.create(table: "foo", ifNotExists: true) { table in
+            table.add(column: .init(name: "id", primaryKey: .init(autoIncrement: true), type: .INTEGER))
+            table.add(index: .init(table: "foo", name: "name_index", unique: true, columns: ["name"], indexSQL: nil))
+        }
+    }
 }

From a0136544e9627b941008d7c59836b61feaf1f142 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Wed, 21 May 2025 22:17:39 +0200
Subject: [PATCH 296/326] New visionOS0-compatible pod has been release

---
 run-tests.sh | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/run-tests.sh b/run-tests.sh
index 7d5c26d2..3ffba810 100755
--- a/run-tests.sh
+++ b/run-tests.sh
@@ -12,9 +12,6 @@ elif [ -n "$VALIDATOR_SUBSPEC" ]; then
       none)
         bundle exec pod lib lint --no-subspecs --fail-fast
         ;;
-      standalone)
-        bundle exec pod lib lint --subspec="${VALIDATOR_SUBSPEC}" --fail-fast --platforms=macos,ios,tvos,watchos
-        ;;
       *)
         bundle exec pod lib lint --subspec="${VALIDATOR_SUBSPEC}" --fail-fast
         ;;

From 77b493e661cff1498892dbaa48aa2205b673ce46 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Wed, 21 May 2025 22:42:20 +0200
Subject: [PATCH 297/326] Use inheritance

---
 SQLite.swift.podspec | 43 +++++--------------------------------------
 1 file changed, 5 insertions(+), 38 deletions(-)

diff --git a/SQLite.swift.podspec b/SQLite.swift.podspec
index 184ce23e..4cbd91bb 100644
--- a/SQLite.swift.podspec
+++ b/SQLite.swift.podspec
@@ -18,17 +18,11 @@ Pod::Spec.new do |s|
   s.default_subspec  = 'standard'
   s.swift_versions = ['5']
 
-  ios_deployment_target = '12.0'
-  tvos_deployment_target = '12.0'
-  osx_deployment_target = '10.13'
-  watchos_deployment_target = '4.0'
-  visionos_deployment_target = '1.0'
-
-  s.ios.deployment_target = ios_deployment_target
-  s.tvos.deployment_target = tvos_deployment_target
-  s.osx.deployment_target = osx_deployment_target
-  s.watchos.deployment_target = watchos_deployment_target
-  s.visionos.deployment_target = visionos_deployment_target
+  s.ios.deployment_target = '12.0'
+  s.tvos.deployment_target = '12.0'
+  s.osx.deployment_target = '10.13'
+  s.watchos.deployment_target = '4.0'
+  s.visionos.deployment_target = '1.0'
 
   s.subspec 'standard' do |ss|
     ss.source_files = 'Sources/SQLite/**/*.{c,h,m,swift}'
@@ -36,18 +30,9 @@ Pod::Spec.new do |s|
     ss.library = 'sqlite3'
     ss.resource_bundle = { 'SQLite.swift' => 'Sources/SQLite/PrivacyInfo.xcprivacy' }
     
-    ss.ios.deployment_target = ios_deployment_target
-    ss.tvos.deployment_target = tvos_deployment_target
-    ss.osx.deployment_target = osx_deployment_target
-    ss.watchos.deployment_target = watchos_deployment_target
-    ss.visionos.deployment_target = visionos_deployment_target
-
     ss.test_spec 'tests' do |test_spec|
       test_spec.resources = 'Tests/SQLiteTests/Resources/*'
       test_spec.source_files = 'Tests/SQLiteTests/*.swift'
-      test_spec.ios.deployment_target = ios_deployment_target
-      test_spec.tvos.deployment_target = tvos_deployment_target
-      test_spec.osx.deployment_target = osx_deployment_target
     end
   end
 
@@ -62,18 +47,9 @@ Pod::Spec.new do |s|
     }
     ss.dependency 'sqlite3'
     
-    ss.ios.deployment_target = ios_deployment_target
-    ss.tvos.deployment_target = tvos_deployment_target
-    ss.osx.deployment_target = osx_deployment_target
-    ss.watchos.deployment_target = watchos_deployment_target
-    ss.visionos.deployment_target = visionos_deployment_target
-
     ss.test_spec 'tests' do |test_spec|
       test_spec.resources = 'Tests/SQLiteTests/Resources/*'
       test_spec.source_files = 'Tests/SQLiteTests/*.swift'
-      test_spec.ios.deployment_target = ios_deployment_target
-      test_spec.tvos.deployment_target = tvos_deployment_target
-      test_spec.osx.deployment_target = osx_deployment_target
     end
   end
 
@@ -87,18 +63,9 @@ Pod::Spec.new do |s|
     }
     ss.dependency 'SQLCipher', '>= 4.0.0'
     
-    ss.ios.deployment_target = ios_deployment_target
-    ss.tvos.deployment_target = tvos_deployment_target
-    ss.osx.deployment_target = osx_deployment_target
-    ss.watchos.deployment_target = watchos_deployment_target
-    #ss.visionos.deployment_target = visionos_deployment_target # Not supported by SQLCipher for now
-
     ss.test_spec 'tests' do |test_spec|
       test_spec.resources = 'Tests/SQLiteTests/Resources/*'
       test_spec.source_files = 'Tests/SQLiteTests/*.swift'
-      test_spec.ios.deployment_target = ios_deployment_target
-      test_spec.tvos.deployment_target = tvos_deployment_target
-      test_spec.osx.deployment_target = osx_deployment_target
     end
   end
 end

From 7309b4337a0649d53d8ebd703f582117c1dc9e91 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Wed, 21 May 2025 23:04:30 +0200
Subject: [PATCH 298/326] Use newer Xcode

---
 .github/workflows/build.yml | 8 +-------
 1 file changed, 1 insertion(+), 7 deletions(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 9fbba1c5..5b6b74a6 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -5,15 +5,9 @@ env:
   IOS_VERSION: "17.2"
 jobs:
   build:
-    runs-on: macos-14
+    runs-on: macos-15
     steps:
     - uses: actions/checkout@v2
-    - name: "Select Xcode"
-      # Currently only works with Xcode 14.2:
-      # https://github.com/CocoaPods/CocoaPods/issues/11839
-      run: |
-        xcode-select -p
-        sudo xcode-select -s /Applications/Xcode_15.3.app/Contents/Developer
     - name: "Lint"
       run: make lint
     - name: "Run tests (PACKAGE_MANAGER_COMMAND: test)"

From 4166f86e844f5d1e87e25264eb9a5194dbaf3b08 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Wed, 21 May 2025 23:10:36 +0200
Subject: [PATCH 299/326] iOS 17.5

---
 .github/workflows/build.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 5b6b74a6..a0274321 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -2,7 +2,7 @@ name: Build and test
 on: [push, pull_request]
 env:
   IOS_SIMULATOR: "iPhone 15"
-  IOS_VERSION: "17.2"
+  IOS_VERSION: "17.5"
 jobs:
   build:
     runs-on: macos-15

From f312ab4d12b344f7e541df5cb0363070a0f964ba Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Wed, 21 May 2025 23:29:27 +0200
Subject: [PATCH 300/326] Update bundler

---
 Gemfile.lock | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Gemfile.lock b/Gemfile.lock
index 464ef06b..9f71603b 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -67,7 +67,7 @@ GEM
     colored2 (3.1.2)
     concurrent-ruby (1.3.5)
     connection_pool (2.5.3)
-    drb (2.2.1)
+    drb (2.2.3)
     escape (0.0.4)
     ethon (0.16.0)
       ffi (>= 1.15.0)
@@ -113,4 +113,4 @@ DEPENDENCIES
   cocoapods
 
 BUNDLED WITH
-   2.5.4
+   2.6.9

From 9dc478c9f1f6eaae43f1aa4286e3bf574bb5976d Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Thu, 22 May 2025 10:02:48 +0200
Subject: [PATCH 301/326] SchemaReader: parse and create unique constraints

---
 Sources/SQLite/Schema/SchemaDefinitions.swift | 27 ++++++++--
 Sources/SQLite/Schema/SchemaReader.swift      | 52 ++++++++++++++-----
 .../Schema/SchemaChangerTests.swift           |  9 ++--
 .../Schema/SchemaReaderTests.swift            | 32 ++++++++++--
 4 files changed, 98 insertions(+), 22 deletions(-)

diff --git a/Sources/SQLite/Schema/SchemaDefinitions.swift b/Sources/SQLite/Schema/SchemaDefinitions.swift
index 897f8557..bb4ce82b 100644
--- a/Sources/SQLite/Schema/SchemaDefinitions.swift
+++ b/Sources/SQLite/Schema/SchemaDefinitions.swift
@@ -128,6 +128,7 @@ public struct ColumnDefinition: Equatable {
     public let primaryKey: PrimaryKey?
     public let type: Affinity
     public let nullable: Bool
+    public let unique: Bool
     public let defaultValue: LiteralValue
     public let references: ForeignKey?
 
@@ -135,12 +136,14 @@ public struct ColumnDefinition: Equatable {
                 primaryKey: PrimaryKey? = nil,
                 type: Affinity,
                 nullable: Bool = true,
+                unique: Bool = false,
                 defaultValue: LiteralValue = .NULL,
                 references: ForeignKey? = nil) {
         self.name = name
         self.primaryKey = primaryKey
         self.type = type
         self.nullable = nullable
+        self.unique = unique
         self.defaultValue = defaultValue
         self.references = references
     }
@@ -244,16 +247,18 @@ public struct IndexDefinition: Equatable {
 
     public enum Order: String { case ASC, DESC }
 
-    public init(table: String, name: String, unique: Bool = false, columns: [String], `where`: String? = nil, orders: [String: Order]? = nil) {
+    public init(table: String, name: String, unique: Bool = false, columns: [String], `where`: String? = nil,
+                orders: [String: Order]? = nil, origin: Origin? = nil) {
         self.table = table
         self.name = name
         self.unique = unique
         self.columns = columns
         self.where = `where`
         self.orders = orders
+        self.origin = origin
     }
 
-    init (table: String, name: String, unique: Bool, columns: [String], indexSQL: String?) {
+    init (table: String, name: String, unique: Bool, columns: [String], indexSQL: String?, origin: Origin? = nil) {
         func wherePart(sql: String) -> String? {
             IndexDefinition.whereRe.firstMatch(in: sql, options: [], range: NSRange(location: 0, length: sql.count)).map {
                 (sql as NSString).substring(with: $0.range(at: 1))
@@ -278,7 +283,8 @@ public struct IndexDefinition: Equatable {
                   unique: unique,
                   columns: columns,
                   where: indexSQL.flatMap(wherePart),
-                  orders: (orders?.isEmpty ?? false) ? nil : orders)
+                  orders: (orders?.isEmpty ?? false) ? nil : orders,
+                  origin: origin)
     }
 
     public let table: String
@@ -287,6 +293,13 @@ public struct IndexDefinition: Equatable {
     public let columns: [String]
     public let `where`: String?
     public let orders: [String: Order]?
+    public let origin: Origin?
+
+    public enum Origin: String {
+        case uniqueConstraint = "u" // index created from a "CREATE TABLE (... UNIQUE)" column constraint
+        case createIndex = "c"      // index created explicitly via "CREATE INDEX ..."
+        case primaryKey = "pk"      // index created from a "CREATE TABLE PRIMARY KEY" column constraint
+    }
 
     enum IndexError: LocalizedError {
         case tooLong(String, String)
@@ -300,6 +313,13 @@ public struct IndexDefinition: Equatable {
         }
     }
 
+    // Indices with names of the form "sqlite_autoindex_TABLE_N" that are used to implement UNIQUE and PRIMARY KEY
+    // constraints on ordinary tables.
+    // https://sqlite.org/fileformat2.html#intschema
+    var isInternal: Bool {
+        name.starts(with: "sqlite_autoindex_")
+    }
+
     func validate() throws {
         if name.count > IndexDefinition.maxIndexLength {
             throw IndexError.tooLong(name, table)
@@ -348,6 +368,7 @@ extension ColumnDefinition {
             defaultValue.map { "DEFAULT \($0)" },
             primaryKey.map { $0.toSQL() },
             nullable ? nil : "NOT NULL",
+            unique ? "UNIQUE" : nil,
             references.map { $0.toSQL() }
         ].compactMap { $0 }
          .joined(separator: " ")
diff --git a/Sources/SQLite/Schema/SchemaReader.swift b/Sources/SQLite/Schema/SchemaReader.swift
index 1989ad6f..a67fb94f 100644
--- a/Sources/SQLite/Schema/SchemaReader.swift
+++ b/Sources/SQLite/Schema/SchemaReader.swift
@@ -21,7 +21,7 @@ public class SchemaReader {
         let foreignKeys: [String: [ColumnDefinition.ForeignKey]] =
             Dictionary(grouping: try foreignKeys(table: table), by: { $0.column })
 
-        return try connection.prepareRowIterator("PRAGMA table_info(\(table.quote()))")
+        let columnDefinitions = try connection.prepareRowIterator("PRAGMA table_info(\(table.quote()))")
             .map { (row: Row) -> ColumnDefinition in
                 ColumnDefinition(
                     name: row[TableInfoTable.nameColumn],
@@ -29,10 +29,27 @@ public class SchemaReader {
                         try parsePrimaryKey(column: row[TableInfoTable.nameColumn]) : nil,
                     type: ColumnDefinition.Affinity(row[TableInfoTable.typeColumn]),
                     nullable: row[TableInfoTable.notNullColumn] == 0,
+                    unique: false,
                     defaultValue: LiteralValue(row[TableInfoTable.defaultValueColumn]),
                     references: foreignKeys[row[TableInfoTable.nameColumn]]?.first
                 )
             }
+
+        let internalIndexes = try indexDefinitions(table: table).filter { $0.isInternal }
+        return columnDefinitions.map { definition in
+            if let index = internalIndexes.first(where: { $0.columns.contains(definition.name) }), index.origin == .uniqueConstraint {
+
+                ColumnDefinition(name: definition.name,
+                                 primaryKey: definition.primaryKey,
+                                 type: definition.type,
+                                 nullable: definition.nullable,
+                                 unique: true,
+                                 defaultValue: definition.defaultValue,
+                                 references: definition.references)
+            } else {
+                definition
+            }
+        }
     }
 
     public func objectDefinitions(name: String? = nil,
@@ -66,27 +83,26 @@ public class SchemaReader {
                 .first
         }
 
-        func columns(name: String) throws -> [String] {
+        func indexInfos(name: String) throws -> [IndexInfo] {
             try connection.prepareRowIterator("PRAGMA index_info(\(name.quote()))")
                 .compactMap { row in
-                    row[IndexInfoTable.nameColumn]
+                    IndexInfo(name: row[IndexInfoTable.nameColumn],
+                              columnRank: row[IndexInfoTable.seqnoColumn],
+                              columnRankWithinTable: row[IndexInfoTable.cidColumn])
+
                 }
         }
 
         return try connection.prepareRowIterator("PRAGMA index_list(\(table.quote()))")
             .compactMap { row -> IndexDefinition? in
                 let name = row[IndexListTable.nameColumn]
-                guard !name.starts(with: "sqlite_") else {
-                    // Indexes SQLite creates implicitly for internal use start with "sqlite_".
-                    // See https://www.sqlite.org/fileformat2.html#intschema
-                    return nil
-                }
                 return IndexDefinition(
                     table: table,
                     name: name,
                     unique: row[IndexListTable.uniqueColumn] == 1,
-                    columns: try columns(name: name),
-                    indexSQL: try indexSQL(name: name)
+                    columns: try indexInfos(name: name).compactMap { $0.name },
+                    indexSQL: try indexSQL(name: name),
+                    origin: IndexDefinition.Origin(rawValue: row[IndexListTable.originColumn])
                 )
             }
     }
@@ -123,6 +139,15 @@ public class SchemaReader {
             objectDefinitions(name: name, type: .table, temp: true)
         ).compactMap(\.sql).first
     }
+
+    struct IndexInfo {
+        let name: String?
+        // The rank of the column within the index. (0 means left-most.)
+        let columnRank: Int
+        // The rank of the column within the table being indexed.
+        // A value of -1 means rowid and a value of -2 means that an expression is being used
+        let columnRankWithinTable: Int
+    }
 }
 
 private enum SchemaTable {
@@ -159,11 +184,12 @@ private enum TableInfoTable {
 
 private enum IndexInfoTable {
     // The rank of the column within the index. (0 means left-most.)
-    static let seqnoColumn = Expression<Int64>("seqno")
+    static let seqnoColumn = Expression<Int>("seqno")
     // The rank of the column within the table being indexed.
     // A value of -1 means rowid and a value of -2 means that an expression is being used.
-    static let cidColumn = Expression<Int64>("cid")
-    // The name of the column being indexed. This columns is NULL if the column is the rowid or an expression.
+    static let cidColumn = Expression<Int>("cid")
+    // The name of the column being indexed.
+    // This columns is NULL if the column is the rowid or an expression.
     static let nameColumn = Expression<String?>("name")
 }
 
diff --git a/Tests/SQLiteTests/Schema/SchemaChangerTests.swift b/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
index 02a82267..724c2d0c 100644
--- a/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
+++ b/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
@@ -158,7 +158,7 @@ class SchemaChangerTests: SQLiteTestCase {
     func test_create_table() throws {
         try schemaChanger.create(table: "foo") { table in
             table.add(column: .init(name: "id", primaryKey: .init(autoIncrement: true), type: .INTEGER))
-            table.add(column: .init(name: "name", type: .TEXT, nullable: false))
+            table.add(column: .init(name: "name", type: .TEXT, nullable: false, unique: true))
             table.add(column: .init(name: "age", type: .INTEGER))
 
             table.add(index: .init(table: table.name,
@@ -179,25 +179,28 @@ class SchemaChangerTests: SQLiteTestCase {
                              primaryKey: .init(autoIncrement: true, onConflict: nil),
                              type: .INTEGER,
                              nullable: true,
+                             unique: false,
                              defaultValue: .NULL,
                              references: nil),
             ColumnDefinition(name: "name",
                              primaryKey: nil,
                              type: .TEXT,
                              nullable: false,
+                             unique: true,
                              defaultValue: .NULL,
                              references: nil),
             ColumnDefinition(name: "age",
                              primaryKey: nil,
                              type: .INTEGER,
                              nullable: true,
+                             unique: false,
                              defaultValue: .NULL,
                              references: nil)
         ])
 
-        let indexes = try schema.indexDefinitions(table: "foo")
+        let indexes = try schema.indexDefinitions(table: "foo").filter { !$0.isInternal }
         XCTAssertEqual(indexes, [
-            IndexDefinition(table: "foo", name: "nameIndex", unique: true, columns: ["name"], where: nil, orders: nil)
+            IndexDefinition(table: "foo", name: "nameIndex", unique: true, columns: ["name"], where: nil, orders: nil, origin: .createIndex)
         ])
     }
 
diff --git a/Tests/SQLiteTests/Schema/SchemaReaderTests.swift b/Tests/SQLiteTests/Schema/SchemaReaderTests.swift
index 8c033e41..01047c9c 100644
--- a/Tests/SQLiteTests/Schema/SchemaReaderTests.swift
+++ b/Tests/SQLiteTests/Schema/SchemaReaderTests.swift
@@ -18,41 +18,48 @@ class SchemaReaderTests: SQLiteTestCase {
                              primaryKey: .init(autoIncrement: false, onConflict: nil),
                              type: .INTEGER,
                              nullable: true,
+                             unique: false,
                              defaultValue: .NULL,
                              references: nil),
             ColumnDefinition(name: "email",
                              primaryKey: nil,
                              type: .TEXT,
                              nullable: false,
+                             unique: true,
                              defaultValue: .NULL,
                              references: nil),
             ColumnDefinition(name: "age",
                              primaryKey: nil,
                              type: .INTEGER,
                              nullable: true,
+                             unique: false,
                              defaultValue: .NULL,
                              references: nil),
             ColumnDefinition(name: "salary",
                              primaryKey: nil,
                              type: .REAL,
                              nullable: true,
+                             unique: false,
                              defaultValue: .NULL,
                              references: nil),
             ColumnDefinition(name: "admin",
                              primaryKey: nil,
                              type: .NUMERIC,
                              nullable: false,
+                             unique: false,
                              defaultValue: .numericLiteral("0"),
                              references: nil),
             ColumnDefinition(name: "manager_id",
                              primaryKey: nil, type: .INTEGER,
                              nullable: true,
+                             unique: false,
                              defaultValue: .NULL,
                              references: .init(table: "users", column: "manager_id", primaryKey: "id", onUpdate: nil, onDelete: nil)),
             ColumnDefinition(name: "created_at",
                              primaryKey: nil,
                              type: .NUMERIC,
                              nullable: true,
+                             unique: false,
                              defaultValue: .NULL,
                              references: nil)
         ])
@@ -68,6 +75,24 @@ class SchemaReaderTests: SQLiteTestCase {
                 primaryKey: .init(autoIncrement: true, onConflict: .IGNORE),
                 type: .INTEGER,
                 nullable: true,
+                unique: false,
+                defaultValue: .NULL,
+                references: nil)
+            ]
+        )
+    }
+
+    func test_columnDefinitions_parses_unique() throws {
+        try db.run("CREATE TABLE t (name TEXT UNIQUE)")
+
+        let columns = try schemaReader.columnDefinitions(table: "t")
+        XCTAssertEqual(columns, [
+            ColumnDefinition(
+                name: "name",
+                primaryKey: nil,
+                type: .TEXT,
+                nullable: true,
+                unique: true,
                 defaultValue: .NULL,
                 references: nil)
             ]
@@ -128,13 +153,13 @@ class SchemaReaderTests: SQLiteTestCase {
     }
 
     func test_indexDefinitions_no_index() throws {
-        let indexes = try schemaReader.indexDefinitions(table: "users")
+        let indexes = try schemaReader.indexDefinitions(table: "users").filter { !$0.isInternal }
         XCTAssertTrue(indexes.isEmpty)
     }
 
     func test_indexDefinitions_with_index() throws {
         try db.run("CREATE UNIQUE INDEX index_users ON users (age DESC) WHERE age IS NOT NULL")
-        let indexes = try schemaReader.indexDefinitions(table: "users")
+        let indexes = try schemaReader.indexDefinitions(table: "users").filter { !$0.isInternal }
 
         XCTAssertEqual(indexes, [
             IndexDefinition(
@@ -143,7 +168,8 @@ class SchemaReaderTests: SQLiteTestCase {
                 unique: true,
                 columns: ["age"],
                 where: "age IS NOT NULL",
-                orders: ["age": .DESC]
+                orders: ["age": .DESC],
+                origin: .createIndex
             )
         ])
     }

From 374d99da2107656adf12670feea4f167853321d9 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Thu, 22 May 2025 10:53:36 +0200
Subject: [PATCH 302/326] Use cocoapods with watchOS fix

---
 Gemfile      |  2 +-
 Gemfile.lock | 46 ++++++++++++++++++++++++++--------------------
 2 files changed, 27 insertions(+), 21 deletions(-)

diff --git a/Gemfile b/Gemfile
index 1f2ebb87..2bb803ca 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,3 +1,3 @@
 source "https://rubygems.org"
 
-gem 'cocoapods' 
+gem 'cocoapods', :git => 'https://github.com/jberkel/CocoaPods.git', branch: 'watchos-fourflusher'
diff --git a/Gemfile.lock b/Gemfile.lock
index 9f71603b..20ed2aad 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,3 +1,27 @@
+GIT
+  remote: https://github.com/jberkel/CocoaPods.git
+  revision: 899f273f298ea20de2378687ea55331004b39371
+  branch: watchos-fourflusher
+  specs:
+    cocoapods (1.16.2)
+      addressable (~> 2.8)
+      claide (>= 1.0.2, < 2.0)
+      cocoapods-core (= 1.16.2)
+      cocoapods-deintegrate (>= 1.0.3, < 2.0)
+      cocoapods-downloader (>= 2.1, < 3.0)
+      cocoapods-plugins (>= 1.0.0, < 2.0)
+      cocoapods-search (>= 1.0.0, < 2.0)
+      cocoapods-trunk (>= 1.6.0, < 2.0)
+      cocoapods-try (>= 1.1.0, < 2.0)
+      colored2 (~> 3.1)
+      escape (~> 0.0.4)
+      fourflusher (>= 2.3.0, < 3.0)
+      gh_inspector (~> 1.0)
+      molinillo (~> 0.8.0)
+      nap (~> 1.0)
+      ruby-macho (~> 4.1.0)
+      xcodeproj (>= 1.27.0, < 2.0)
+
 GEM
   remote: https://rubygems.org/
   specs:
@@ -27,24 +51,6 @@ GEM
     benchmark (0.4.0)
     bigdecimal (3.1.9)
     claide (1.1.0)
-    cocoapods (1.16.2)
-      addressable (~> 2.8)
-      claide (>= 1.0.2, < 2.0)
-      cocoapods-core (= 1.16.2)
-      cocoapods-deintegrate (>= 1.0.3, < 2.0)
-      cocoapods-downloader (>= 2.1, < 3.0)
-      cocoapods-plugins (>= 1.0.0, < 2.0)
-      cocoapods-search (>= 1.0.0, < 2.0)
-      cocoapods-trunk (>= 1.6.0, < 2.0)
-      cocoapods-try (>= 1.1.0, < 2.0)
-      colored2 (~> 3.1)
-      escape (~> 0.0.4)
-      fourflusher (>= 2.3.0, < 3.0)
-      gh_inspector (~> 1.0)
-      molinillo (~> 0.8.0)
-      nap (~> 1.0)
-      ruby-macho (>= 2.3.0, < 3.0)
-      xcodeproj (>= 1.27.0, < 2.0)
     cocoapods-core (1.16.2)
       activesupport (>= 5.0, < 8)
       addressable (~> 2.8)
@@ -91,7 +97,7 @@ GEM
     nkf (0.2.0)
     public_suffix (4.0.7)
     rexml (3.4.1)
-    ruby-macho (2.5.1)
+    ruby-macho (4.1.0)
     securerandom (0.4.1)
     typhoeus (1.4.1)
       ethon (>= 0.9.0)
@@ -110,7 +116,7 @@ PLATFORMS
   ruby
 
 DEPENDENCIES
-  cocoapods
+  cocoapods!
 
 BUNDLED WITH
    2.6.9

From 108431244de611050f25c20efd02bcfe68687b28 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Thu, 22 May 2025 11:52:52 +0200
Subject: [PATCH 303/326] Disable visionOS for SQLCipher

---
 SQLite.swift.podspec | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/SQLite.swift.podspec b/SQLite.swift.podspec
index 4cbd91bb..d0ccb136 100644
--- a/SQLite.swift.podspec
+++ b/SQLite.swift.podspec
@@ -54,6 +54,13 @@ Pod::Spec.new do |s|
   end
 
   s.subspec 'SQLCipher' do |ss|
+    # Disable unsupported visionOS
+    # https://github.com/sqlcipher/sqlcipher/issues/483
+    ss.ios.deployment_target = s.deployment_target(:ios)
+    ss.tvos.deployment_target = s.deployment_target(:tvos)
+    ss.osx.deployment_target = s.deployment_target(:osx)
+    ss.watchos.deployment_target = s.deployment_target(:watchos)
+
     ss.source_files = 'Sources/SQLite/**/*.{c,h,m,swift}'
     ss.resource_bundle = { 'SQLite.swift' => 'Sources/SQLite/PrivacyInfo.xcprivacy' }
 

From 718c7988780f803ede87c03ef2cdce58ea762656 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Thu, 22 May 2025 12:21:19 +0200
Subject: [PATCH 304/326] Add comment

---
 Gemfile      | 1 +
 Gemfile.lock | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/Gemfile b/Gemfile
index 2bb803ca..2770e85d 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,3 +1,4 @@
 source "https://rubygems.org"
 
+# https://github.com/CocoaPods/CocoaPods/pull/12816
 gem 'cocoapods', :git => 'https://github.com/jberkel/CocoaPods.git', branch: 'watchos-fourflusher'
diff --git a/Gemfile.lock b/Gemfile.lock
index 20ed2aad..7dac2c18 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,6 +1,6 @@
 GIT
   remote: https://github.com/jberkel/CocoaPods.git
-  revision: 899f273f298ea20de2378687ea55331004b39371
+  revision: 32a90c184bc5dc9ec8b7b9b8ad08e98b7253dec2
   branch: watchos-fourflusher
   specs:
     cocoapods (1.16.2)

From c812caf9c432bc196f87648b56551dff2c0017e3 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Thu, 22 May 2025 13:30:36 +0200
Subject: [PATCH 305/326] Make ForeignKey public, rename fields for clarity

---
 Sources/SQLite/Schema/SchemaDefinitions.swift | 23 ++++++++++---
 Sources/SQLite/Schema/SchemaReader.swift      |  8 ++---
 .../Schema/SchemaChangerTests.swift           | 34 +++++++++++++++++++
 .../Schema/SchemaDefinitionsTests.swift       |  8 ++---
 .../Schema/SchemaReaderTests.swift            |  4 +--
 5 files changed, 62 insertions(+), 15 deletions(-)

diff --git a/Sources/SQLite/Schema/SchemaDefinitions.swift b/Sources/SQLite/Schema/SchemaDefinitions.swift
index bb4ce82b..d7803999 100644
--- a/Sources/SQLite/Schema/SchemaDefinitions.swift
+++ b/Sources/SQLite/Schema/SchemaDefinitions.swift
@@ -117,11 +117,24 @@ public struct ColumnDefinition: Equatable {
     }
 
     public struct ForeignKey: Equatable {
-        let table: String
-        let column: String
-        let primaryKey: String?
+        let fromColumn: String
+        let toTable: String
+        // when null, use primary key of "toTable"
+        let toColumn: String?
         let onUpdate: String?
         let onDelete: String?
+
+        public init(toTable: String, toColumn: String? = nil, onUpdate: String? = nil, onDelete: String? = nil) {
+            self.init(fromColumn: "", toTable: toTable, toColumn: toColumn, onUpdate: onUpdate, onDelete: onDelete)
+        }
+
+        public init(fromColumn: String, toTable: String, toColumn: String? = nil, onUpdate: String? = nil, onDelete: String? = nil) {
+            self.fromColumn = fromColumn
+            self.toTable = toTable
+            self.toColumn = toColumn
+            self.onUpdate = onUpdate
+            self.onDelete = onDelete
+        }
     }
 
     public let name: String
@@ -400,8 +413,8 @@ extension ColumnDefinition.ForeignKey {
     func toSQL() -> String {
         ([
             "REFERENCES",
-            table.quote(),
-            primaryKey.map { "(\($0.quote()))" },
+            toTable.quote(),
+            toColumn.map { "(\($0.quote()))" },
             onUpdate.map { "ON UPDATE \($0)" },
             onDelete.map { "ON DELETE \($0)" }
         ] as [String?]).compactMap { $0 }
diff --git a/Sources/SQLite/Schema/SchemaReader.swift b/Sources/SQLite/Schema/SchemaReader.swift
index a67fb94f..995cd4d7 100644
--- a/Sources/SQLite/Schema/SchemaReader.swift
+++ b/Sources/SQLite/Schema/SchemaReader.swift
@@ -19,7 +19,7 @@ public class SchemaReader {
         }
 
         let foreignKeys: [String: [ColumnDefinition.ForeignKey]] =
-            Dictionary(grouping: try foreignKeys(table: table), by: { $0.column })
+            Dictionary(grouping: try foreignKeys(table: table), by: { $0.fromColumn })
 
         let columnDefinitions = try connection.prepareRowIterator("PRAGMA table_info(\(table.quote()))")
             .map { (row: Row) -> ColumnDefinition in
@@ -111,9 +111,9 @@ public class SchemaReader {
         try connection.prepareRowIterator("PRAGMA foreign_key_list(\(table.quote()))")
             .map { row in
                 ColumnDefinition.ForeignKey(
-                    table: row[ForeignKeyListTable.tableColumn],
-                    column: row[ForeignKeyListTable.fromColumn],
-                    primaryKey: row[ForeignKeyListTable.toColumn],
+                    fromColumn: row[ForeignKeyListTable.fromColumn],
+                    toTable: row[ForeignKeyListTable.tableColumn],
+                    toColumn: row[ForeignKeyListTable.toColumn],
                     onUpdate: row[ForeignKeyListTable.onUpdateColumn] == TableBuilder.Dependency.noAction.rawValue
                         ? nil : row[ForeignKeyListTable.onUpdateColumn],
                     onDelete: row[ForeignKeyListTable.onDeleteColumn] == TableBuilder.Dependency.noAction.rawValue
diff --git a/Tests/SQLiteTests/Schema/SchemaChangerTests.swift b/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
index 724c2d0c..b08344fc 100644
--- a/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
+++ b/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
@@ -269,4 +269,38 @@ class SchemaChangerTests: SQLiteTestCase {
             table.add(index: .init(table: "foo", name: "name_index", unique: true, columns: ["name"], indexSQL: nil))
         }
     }
+
+    func test_create_table_with_foreign_key_reference() throws {
+        try schemaChanger.create(table: "foo") { table in
+            table.add(column: .init(name: "id", primaryKey: .init(autoIncrement: true), type: .INTEGER))
+        }
+
+        try schemaChanger.create(table: "bars") { table in
+            table.add(column: .init(name: "id", primaryKey: .init(autoIncrement: true), type: .INTEGER))
+            table.add(column: .init(name: "foo_id",
+                                    type: .INTEGER,
+                                    nullable: false,
+                                    references: .init(toTable: "foo", toColumn: "id")))
+        }
+
+        let barColumns = try schema.columnDefinitions(table: "bars")
+
+        XCTAssertEqual([
+            ColumnDefinition(name: "id",
+                             primaryKey: .init(autoIncrement: true, onConflict: nil),
+                             type: .INTEGER,
+                             nullable: true,
+                             unique: false,
+                             defaultValue: .NULL,
+                             references: nil),
+
+            ColumnDefinition(name: "foo_id",
+                             primaryKey: nil,
+                             type: .INTEGER,
+                             nullable: false,
+                             unique: false,
+                             defaultValue: .NULL,
+                             references: .init(fromColumn: "foo_id", toTable: "foo", toColumn: "id", onUpdate: nil, onDelete: nil))
+        ], barColumns)
+    }
 }
diff --git a/Tests/SQLiteTests/Schema/SchemaDefinitionsTests.swift b/Tests/SQLiteTests/Schema/SchemaDefinitionsTests.swift
index 8b7e27e3..1c648bd8 100644
--- a/Tests/SQLiteTests/Schema/SchemaDefinitionsTests.swift
+++ b/Tests/SQLiteTests/Schema/SchemaDefinitionsTests.swift
@@ -11,7 +11,7 @@ class ColumnDefinitionTests: XCTestCase {
 
         ("\"other_id\" INTEGER NOT NULL REFERENCES \"other_table\" (\"some_id\")",
         ColumnDefinition(name: "other_id", primaryKey: nil, type: .INTEGER, nullable: false, defaultValue: .NULL,
-                         references: .init(table: "other_table", column: "", primaryKey: "some_id", onUpdate: nil, onDelete: nil))),
+                         references: .init(fromColumn: "", toTable: "other_table", toColumn: "some_id", onUpdate: nil, onDelete: nil))),
 
         ("\"text\" TEXT",
         ColumnDefinition(name: "text", primaryKey: nil, type: .TEXT, nullable: true, defaultValue: .NULL, references: nil)),
@@ -245,9 +245,9 @@ class ForeignKeyDefinitionTests: XCTestCase {
     func test_toSQL() {
         XCTAssertEqual(
             ColumnDefinition.ForeignKey(
-                table: "foo",
-                column: "bar",
-                primaryKey: "bar_id",
+                fromColumn: "bar",
+                toTable: "foo",
+                toColumn: "bar_id",
                 onUpdate: nil,
                 onDelete: "SET NULL"
             ).toSQL(), """
diff --git a/Tests/SQLiteTests/Schema/SchemaReaderTests.swift b/Tests/SQLiteTests/Schema/SchemaReaderTests.swift
index 01047c9c..8963070f 100644
--- a/Tests/SQLiteTests/Schema/SchemaReaderTests.swift
+++ b/Tests/SQLiteTests/Schema/SchemaReaderTests.swift
@@ -54,7 +54,7 @@ class SchemaReaderTests: SQLiteTestCase {
                              nullable: true,
                              unique: false,
                              defaultValue: .NULL,
-                             references: .init(table: "users", column: "manager_id", primaryKey: "id", onUpdate: nil, onDelete: nil)),
+                             references: .init(fromColumn: "manager_id", toTable: "users", toColumn: "id", onUpdate: nil, onDelete: nil)),
             ColumnDefinition(name: "created_at",
                              primaryKey: nil,
                              type: .NUMERIC,
@@ -194,7 +194,7 @@ class SchemaReaderTests: SQLiteTestCase {
 
         let foreignKeys = try schemaReader.foreignKeys(table: "test_links")
         XCTAssertEqual(foreignKeys, [
-            .init(table: "users", column: "test_id", primaryKey: "id", onUpdate: nil, onDelete: nil)
+            .init(fromColumn: "test_id", toTable: "users", toColumn: "id", onUpdate: nil, onDelete: nil)
         ])
     }
 

From 1c440766b22019190a9c78731f0c69b78f5d177b Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Thu, 22 May 2025 17:06:41 +0200
Subject: [PATCH 306/326] Add drop(index:)

---
 Sources/SQLite/Schema/SchemaChanger.swift     | 10 ++++-
 .../Schema/SchemaChangerTests.swift           | 38 +++++++++++++++++++
 2 files changed, 47 insertions(+), 1 deletion(-)

diff --git a/Sources/SQLite/Schema/SchemaChanger.swift b/Sources/SQLite/Schema/SchemaChanger.swift
index 13222dab..7ba458c1 100644
--- a/Sources/SQLite/Schema/SchemaChanger.swift
+++ b/Sources/SQLite/Schema/SchemaChanger.swift
@@ -45,6 +45,7 @@ public class SchemaChanger: CustomStringConvertible {
         case addColumn(ColumnDefinition)
         case addIndex(IndexDefinition, ifNotExists: Bool)
         case dropColumn(String)
+        case dropIndex(String, ifExists: Bool)
         case renameColumn(String, String)
         case renameTable(String)
         case createTable(columns: [ColumnDefinition], ifNotExists: Bool)
@@ -60,6 +61,8 @@ public class SchemaChanger: CustomStringConvertible {
                 return "ALTER TABLE \(table.quote()) RENAME COLUMN \(from.quote()) TO \(to.quote())"
             case .dropColumn(let column) where SQLiteFeature.dropColumn.isSupported(by: version):
                 return "ALTER TABLE \(table.quote()) DROP COLUMN \(column.quote())"
+            case .dropIndex(let name, let ifExists):
+                return "DROP INDEX \(ifExists ? " IF EXISTS " : "") \(name.quote())"
             case .createTable(let columns, let ifNotExists):
                 return "CREATE TABLE \(ifNotExists ? " IF NOT EXISTS " : "") \(table.quote()) (" +
                     columns.map { $0.toSQL() }.joined(separator: ", ") +
@@ -111,6 +114,10 @@ public class SchemaChanger: CustomStringConvertible {
             operations.append(.dropColumn(column))
         }
 
+        public func drop(index: String, ifExists: Bool = false) {
+            operations.append(.dropIndex(index, ifExists: ifExists))
+        }
+
         public func rename(column: String, to: String) {
             operations.append(.renameColumn(column, to))
         }
@@ -324,8 +331,9 @@ extension TableDefinition {
     func apply(_ operation: SchemaChanger.Operation?) -> TableDefinition {
         switch operation {
         case .none: return self
-        case .createTable, .addIndex: fatalError()
+        case .createTable, .addIndex, .dropIndex: fatalError()
         case .addColumn: fatalError("Use 'ALTER TABLE ADD COLUMN (...)'")
+
         case .dropColumn(let column):
             return TableDefinition(name: name,
                 columns: columns.filter { $0.name != column },
diff --git a/Tests/SQLiteTests/Schema/SchemaChangerTests.swift b/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
index b08344fc..75331a97 100644
--- a/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
+++ b/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
@@ -124,6 +124,44 @@ class SchemaChangerTests: SQLiteTestCase {
         }
     }
 
+    func test_drop_index() throws {
+        try db.execute("""
+            CREATE INDEX age_index ON users(age)
+        """)
+
+        try schemaChanger.alter(table: "users") { table in
+            table.drop(index: "age_index")
+        }
+        let indexes = try schema.indexDefinitions(table: "users").filter { !$0.isInternal }
+        XCTAssertEqual(0, indexes.count)
+    }
+
+    func test_drop_index_if_exists() throws {
+        try db.execute("""
+            CREATE INDEX age_index ON users(age)
+        """)
+
+        try schemaChanger.alter(table: "users") { table in
+            table.drop(index: "age_index")
+        }
+
+        try schemaChanger.alter(table: "users") { table in
+            table.drop(index: "age_index", ifExists: true)
+        }
+
+        XCTAssertThrowsError(
+            try schemaChanger.alter(table: "users") { table in
+                table.drop(index: "age_index", ifExists: false)
+            }
+        ) { error in
+            if case Result.error(let message, _, _) =  error {
+                XCTAssertEqual(message, "no such index: age_index")
+            } else {
+                XCTFail("unexpected error \(error)")
+            }
+        }
+    }
+
     func test_drop_table() throws {
         try schemaChanger.drop(table: "users")
         XCTAssertThrowsError(try db.scalar(users.count)) { error in

From 08bed022dc12289d64278cc5ad2e9a63868fb12f Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Thu, 22 May 2025 17:23:02 +0200
Subject: [PATCH 307/326] Implement add(index:)

---
 Sources/SQLite/Schema/SchemaChanger.swift     |  4 +++
 .../Schema/SchemaChangerTests.swift           | 34 +++++++++++++++++++
 2 files changed, 38 insertions(+)

diff --git a/Sources/SQLite/Schema/SchemaChanger.swift b/Sources/SQLite/Schema/SchemaChanger.swift
index 7ba458c1..3badb8d8 100644
--- a/Sources/SQLite/Schema/SchemaChanger.swift
+++ b/Sources/SQLite/Schema/SchemaChanger.swift
@@ -110,6 +110,10 @@ public class SchemaChanger: CustomStringConvertible {
             operations.append(.addColumn(column))
         }
 
+        public func add(index: IndexDefinition, ifNotExists: Bool = false) {
+            operations.append(.addIndex(index, ifNotExists: ifNotExists))
+        }
+
         public func drop(column: String) {
             operations.append(.dropColumn(column))
         }
diff --git a/Tests/SQLiteTests/Schema/SchemaChangerTests.swift b/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
index 75331a97..dafdea54 100644
--- a/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
+++ b/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
@@ -124,6 +124,40 @@ class SchemaChangerTests: SQLiteTestCase {
         }
     }
 
+    func test_add_index() throws {
+        try schemaChanger.alter(table: "users") { table in
+            table.add(index: .init(table: table.name, name: "age_index", unique: false, columns: ["age"], indexSQL: nil))
+        }
+
+        let indexes = try schema.indexDefinitions(table: "users").filter { !$0.isInternal }
+        XCTAssertEqual([
+            IndexDefinition(table: "users",
+                            name: "age_index",
+                            unique: false,
+                            columns: ["age"],
+                            where: nil,
+                            orders: nil,
+                            origin: .createIndex)
+        ], indexes)
+    }
+
+    func test_add_index_if_not_exists() throws {
+        let index = IndexDefinition(table: "users", name: "age_index", unique: false, columns: ["age"], indexSQL: nil)
+        try schemaChanger.alter(table: "users") { table in
+            table.add(index: index)
+        }
+
+        try schemaChanger.alter(table: "users") { table in
+            table.add(index: index, ifNotExists: true)
+        }
+
+        XCTAssertThrowsError(
+            try schemaChanger.alter(table: "users") { table in
+                table.add(index: index, ifNotExists: false)
+            }
+        )
+    }
+
     func test_drop_index() throws {
         try db.execute("""
             CREATE INDEX age_index ON users(age)

From 3def7d60f70b109e0f74f6d42bac61d631de56dd Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Thu, 22 May 2025 17:45:33 +0200
Subject: [PATCH 308/326] Make name public, expose plain SQL run

---
 Sources/SQLite/Schema/SchemaChanger.swift | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/Sources/SQLite/Schema/SchemaChanger.swift b/Sources/SQLite/Schema/SchemaChanger.swift
index 3badb8d8..3e146df7 100644
--- a/Sources/SQLite/Schema/SchemaChanger.swift
+++ b/Sources/SQLite/Schema/SchemaChanger.swift
@@ -100,7 +100,7 @@ public class SchemaChanger: CustomStringConvertible {
     public class AlterTableDefinition {
         fileprivate var operations: [Operation] = []
 
-        let name: String
+        public let name: String
 
         init(name: String) {
             self.name = name
@@ -223,6 +223,11 @@ public class SchemaChanger: CustomStringConvertible {
         try connection.run("ALTER TABLE \(table.quote()) RENAME TO \(to.quote())")
     }
 
+    // Runs arbitrary SQL. Should only be used if no predefined operations exist.
+    public func run(sql: String) throws {
+        try connection.run(sql)
+    }
+
     private func run(table: String, operation: Operation) throws {
         try operation.validate()
 

From 25bd06392f65d7ca75ffc481471de05905ec1c79 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Thu, 22 May 2025 17:53:46 +0200
Subject: [PATCH 309/326] Better API, test

---
 Sources/SQLite/Schema/SchemaChanger.swift         | 5 +++--
 Tests/SQLiteTests/Schema/SchemaChangerTests.swift | 5 +++++
 2 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/Sources/SQLite/Schema/SchemaChanger.swift b/Sources/SQLite/Schema/SchemaChanger.swift
index 3e146df7..6fae532a 100644
--- a/Sources/SQLite/Schema/SchemaChanger.swift
+++ b/Sources/SQLite/Schema/SchemaChanger.swift
@@ -224,8 +224,9 @@ public class SchemaChanger: CustomStringConvertible {
     }
 
     // Runs arbitrary SQL. Should only be used if no predefined operations exist.
-    public func run(sql: String) throws {
-        try connection.run(sql)
+    @discardableResult
+    public func run(_ sql: String, _ bindings: Binding?...) throws -> Statement {
+        return try connection.run(sql, bindings)
     }
 
     private func run(table: String, operation: Operation) throws {
diff --git a/Tests/SQLiteTests/Schema/SchemaChangerTests.swift b/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
index dafdea54..d942fba4 100644
--- a/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
+++ b/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
@@ -375,4 +375,9 @@ class SchemaChangerTests: SQLiteTestCase {
                              references: .init(fromColumn: "foo_id", toTable: "foo", toColumn: "id", onUpdate: nil, onDelete: nil))
         ], barColumns)
     }
+
+    func test_run_arbitrary_sql() throws {
+        try schemaChanger.run("DROP TABLE users")
+        XCTAssertEqual(0, try schema.objectDefinitions(name: "users", type: .table).count)
+    }
 }

From 8938697549dccc21803c5cbef9f2e5fec7ee274a Mon Sep 17 00:00:00 2001
From: ha100 <thastik@gmail.com>
Date: Sun, 8 Jun 2025 05:38:36 +0200
Subject: [PATCH 310/326] change CSQLite dep to SwiftToolchainCSQLite to allow
 swift sdk cross compilation

---
 Package.swift                                 | 69 +++++++++++--------
 Sources/SQLite/Core/Backup.swift              |  2 +-
 .../SQLite/Core/Connection+Aggregation.swift  |  2 +-
 Sources/SQLite/Core/Connection+Attach.swift   |  2 +-
 Sources/SQLite/Core/Connection.swift          |  2 +-
 Sources/SQLite/Core/Result.swift              |  2 +-
 Sources/SQLite/Core/Statement.swift           |  2 +-
 Sources/SQLite/Helpers.swift                  |  2 +-
 .../Core/Connection+AttachTests.swift         |  2 +-
 .../Core/Connection+PragmaTests.swift         |  2 +-
 Tests/SQLiteTests/Core/ConnectionTests.swift  |  2 +-
 Tests/SQLiteTests/Core/ResultTests.swift      |  2 +-
 Tests/SQLiteTests/Core/StatementTests.swift   |  2 +-
 .../Extensions/FTSIntegrationTests.swift      |  2 +-
 .../Typed/CustomAggregationTests.swift        |  2 +-
 .../Typed/QueryIntegrationTests.swift         |  2 +-
 Tests/SQLiteTests/Typed/QueryTests.swift      |  2 +-
 17 files changed, 56 insertions(+), 45 deletions(-)

diff --git a/Package.swift b/Package.swift
index 238661ae..690d41e2 100644
--- a/Package.swift
+++ b/Package.swift
@@ -1,6 +1,38 @@
 // swift-tools-version:5.9
 import PackageDescription
 
+let deps: [Package.Dependency] = [
+    .github("swiftlang/swift-toolchain-sqlite", exact: "1.0.4")
+]
+
+let targets: [Target] = [
+    .target(
+        name: "SQLite",
+        dependencies: [
+            .product(name: "SwiftToolchainCSQLite", package: "swift-toolchain-sqlite")
+        ],
+        exclude: [
+            "Info.plist"
+        ]
+    )
+]
+
+let testTargets: [Target] = [
+    .testTarget(
+        name: "SQLiteTests",
+        dependencies: [
+            "SQLite"
+        ],
+        path: "Tests/SQLiteTests",
+        exclude: [
+            "Info.plist"
+        ],
+        resources: [
+            .copy("Resources")
+        ]
+    )
+]
+
 let package = Package(
     name: "SQLite.swift",
     platforms: [
@@ -16,34 +48,13 @@ let package = Package(
             targets: ["SQLite"]
         )
     ],
-    targets: [
-        .target(
-            name: "SQLite",
-            exclude: [
-                "Info.plist"
-            ]
-        ),
-        .testTarget(
-            name: "SQLiteTests",
-            dependencies: [
-                "SQLite"
-            ],
-            path: "Tests/SQLiteTests",
-            exclude: [
-                "Info.plist"
-            ],
-            resources: [
-                .copy("Resources")
-            ]
-        )
-    ]
+    dependencies: deps,
+    targets: targets + testTargets
 )
 
-#if os(Linux)
-package.dependencies = [
-    .package(url: "https://github.com/stephencelis/CSQLite.git", from: "0.0.3")
-]
-package.targets.first?.dependencies += [
-    .product(name: "CSQLite", package: "CSQLite")
-]
-#endif
+extension Package.Dependency {
+
+    static func github(_ repo: String, exact ver: Version) -> Package.Dependency {
+        .package(url: "https://github.com/\(repo)", exact: ver)
+    }
+}
diff --git a/Sources/SQLite/Core/Backup.swift b/Sources/SQLite/Core/Backup.swift
index 0eebbdd5..5e741ecd 100644
--- a/Sources/SQLite/Core/Backup.swift
+++ b/Sources/SQLite/Core/Backup.swift
@@ -29,7 +29,7 @@ import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
 #elseif os(Linux)
-import CSQLite
+import SwiftToolchainCSQLite
 #else
 import SQLite3
 #endif
diff --git a/Sources/SQLite/Core/Connection+Aggregation.swift b/Sources/SQLite/Core/Connection+Aggregation.swift
index 4eea76c3..bfe253a7 100644
--- a/Sources/SQLite/Core/Connection+Aggregation.swift
+++ b/Sources/SQLite/Core/Connection+Aggregation.swift
@@ -4,7 +4,7 @@ import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
 #elseif os(Linux)
-import CSQLite
+import SwiftToolchainCSQLite
 #else
 import SQLite3
 #endif
diff --git a/Sources/SQLite/Core/Connection+Attach.swift b/Sources/SQLite/Core/Connection+Attach.swift
index 8a25e51d..32461468 100644
--- a/Sources/SQLite/Core/Connection+Attach.swift
+++ b/Sources/SQLite/Core/Connection+Attach.swift
@@ -4,7 +4,7 @@ import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
 #elseif os(Linux)
-import CSQLite
+import SwiftToolchainCSQLite
 #else
 import SQLite3
 #endif
diff --git a/Sources/SQLite/Core/Connection.swift b/Sources/SQLite/Core/Connection.swift
index f2c3b781..188ff80b 100644
--- a/Sources/SQLite/Core/Connection.swift
+++ b/Sources/SQLite/Core/Connection.swift
@@ -29,7 +29,7 @@ import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
 #elseif os(Linux)
-import CSQLite
+import SwiftToolchainCSQLite
 #else
 import SQLite3
 #endif
diff --git a/Sources/SQLite/Core/Result.swift b/Sources/SQLite/Core/Result.swift
index ee59e5d1..2659ad21 100644
--- a/Sources/SQLite/Core/Result.swift
+++ b/Sources/SQLite/Core/Result.swift
@@ -3,7 +3,7 @@ import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
 #elseif os(Linux)
-import CSQLite
+import SwiftToolchainCSQLite
 #else
 import SQLite3
 #endif
diff --git a/Sources/SQLite/Core/Statement.swift b/Sources/SQLite/Core/Statement.swift
index 6cb2e5d3..458e3d9c 100644
--- a/Sources/SQLite/Core/Statement.swift
+++ b/Sources/SQLite/Core/Statement.swift
@@ -27,7 +27,7 @@ import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
 #elseif os(Linux)
-import CSQLite
+import SwiftToolchainCSQLite
 #else
 import SQLite3
 #endif
diff --git a/Sources/SQLite/Helpers.swift b/Sources/SQLite/Helpers.swift
index c27ccf05..00493f21 100644
--- a/Sources/SQLite/Helpers.swift
+++ b/Sources/SQLite/Helpers.swift
@@ -27,7 +27,7 @@ import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
 #elseif os(Linux)
-import CSQLite
+import SwiftToolchainCSQLite
 #else
 import SQLite3
 #endif
diff --git a/Tests/SQLiteTests/Core/Connection+AttachTests.swift b/Tests/SQLiteTests/Core/Connection+AttachTests.swift
index f37300ca..68618596 100644
--- a/Tests/SQLiteTests/Core/Connection+AttachTests.swift
+++ b/Tests/SQLiteTests/Core/Connection+AttachTests.swift
@@ -7,7 +7,7 @@ import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
 #elseif os(Linux)
-import CSQLite
+import SwiftToolchainCSQLite
 #else
 import SQLite3
 #endif
diff --git a/Tests/SQLiteTests/Core/Connection+PragmaTests.swift b/Tests/SQLiteTests/Core/Connection+PragmaTests.swift
index 2bcdb6af..bd29bd98 100644
--- a/Tests/SQLiteTests/Core/Connection+PragmaTests.swift
+++ b/Tests/SQLiteTests/Core/Connection+PragmaTests.swift
@@ -7,7 +7,7 @@ import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
 #elseif os(Linux)
-import CSQLite
+import SwiftToolchainCSQLite
 #else
 import SQLite3
 #endif
diff --git a/Tests/SQLiteTests/Core/ConnectionTests.swift b/Tests/SQLiteTests/Core/ConnectionTests.swift
index 6a1d94ae..61083453 100644
--- a/Tests/SQLiteTests/Core/ConnectionTests.swift
+++ b/Tests/SQLiteTests/Core/ConnectionTests.swift
@@ -8,7 +8,7 @@ import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
 #elseif os(Linux)
-import CSQLite
+import SwiftToolchainCSQLite
 #else
 import SQLite3
 #endif
diff --git a/Tests/SQLiteTests/Core/ResultTests.swift b/Tests/SQLiteTests/Core/ResultTests.swift
index d3c8bb1f..b6a373f7 100644
--- a/Tests/SQLiteTests/Core/ResultTests.swift
+++ b/Tests/SQLiteTests/Core/ResultTests.swift
@@ -7,7 +7,7 @@ import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
 #elseif os(Linux)
-import CSQLite
+import SwiftToolchainCSQLite
 #else
 import SQLite3
 #endif
diff --git a/Tests/SQLiteTests/Core/StatementTests.swift b/Tests/SQLiteTests/Core/StatementTests.swift
index dbf99d7c..ceaa8813 100644
--- a/Tests/SQLiteTests/Core/StatementTests.swift
+++ b/Tests/SQLiteTests/Core/StatementTests.swift
@@ -6,7 +6,7 @@ import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
 #elseif os(Linux)
-import CSQLite
+import SwiftToolchainCSQLite
 #else
 import SQLite3
 #endif
diff --git a/Tests/SQLiteTests/Extensions/FTSIntegrationTests.swift b/Tests/SQLiteTests/Extensions/FTSIntegrationTests.swift
index 1129ae08..8a34e93b 100644
--- a/Tests/SQLiteTests/Extensions/FTSIntegrationTests.swift
+++ b/Tests/SQLiteTests/Extensions/FTSIntegrationTests.swift
@@ -4,7 +4,7 @@ import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
 #elseif os(Linux)
-import CSQLite
+import SwiftToolchainCSQLite
 #else
 import SQLite3
 #endif
diff --git a/Tests/SQLiteTests/Typed/CustomAggregationTests.swift b/Tests/SQLiteTests/Typed/CustomAggregationTests.swift
index 71cbba9c..8b7ec09a 100644
--- a/Tests/SQLiteTests/Typed/CustomAggregationTests.swift
+++ b/Tests/SQLiteTests/Typed/CustomAggregationTests.swift
@@ -8,7 +8,7 @@ import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
 #elseif os(Linux)
-import CSQLite
+import SwiftToolchainCSQLite
 #else
 import SQLite3
 #endif
diff --git a/Tests/SQLiteTests/Typed/QueryIntegrationTests.swift b/Tests/SQLiteTests/Typed/QueryIntegrationTests.swift
index 6be98ca0..899bf354 100644
--- a/Tests/SQLiteTests/Typed/QueryIntegrationTests.swift
+++ b/Tests/SQLiteTests/Typed/QueryIntegrationTests.swift
@@ -4,7 +4,7 @@ import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
 #elseif os(Linux)
-import CSQLite
+import SwiftToolchainCSQLite
 #else
 import SQLite3
 #endif
diff --git a/Tests/SQLiteTests/Typed/QueryTests.swift b/Tests/SQLiteTests/Typed/QueryTests.swift
index f018f097..7fc3dcf5 100644
--- a/Tests/SQLiteTests/Typed/QueryTests.swift
+++ b/Tests/SQLiteTests/Typed/QueryTests.swift
@@ -4,7 +4,7 @@ import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
 #elseif os(Linux)
-import CSQLite
+import SwiftToolchainCSQLite
 #else
 import SQLite3
 #endif

From fc774a2585965a90f60aa034001e6d69aee42382 Mon Sep 17 00:00:00 2001
From: ha100 <thastik@gmail.com>
Date: Sun, 8 Jun 2025 07:08:47 +0200
Subject: [PATCH 311/326] fix macOS build via platform condition

---
 Package.swift | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Package.swift b/Package.swift
index 690d41e2..48085178 100644
--- a/Package.swift
+++ b/Package.swift
@@ -9,7 +9,7 @@ let targets: [Target] = [
     .target(
         name: "SQLite",
         dependencies: [
-            .product(name: "SwiftToolchainCSQLite", package: "swift-toolchain-sqlite")
+            .product(name: "SwiftToolchainCSQLite", package: "swift-toolchain-sqlite", condition: .when(platforms: [.linux, .windows]))
         ],
         exclude: [
             "Info.plist"

From f3cb9105a80d566575c02bb77ac50e74e5895ccc Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Thu, 12 Jun 2025 12:15:34 +0200
Subject: [PATCH 312/326] Fully qualify Expression

---
 Tests/SQLiteTests/Schema/SchemaChangerTests.swift | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/Tests/SQLiteTests/Schema/SchemaChangerTests.swift b/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
index d942fba4..f5a6de42 100644
--- a/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
+++ b/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
@@ -278,9 +278,9 @@ class SchemaChangerTests: SQLiteTestCase {
 
     func test_create_table_add_column_expression() throws {
         try schemaChanger.create(table: "foo") { table in
-            table.add(expression: Expression<String>("name"))
-            table.add(expression: Expression<Int>("age"))
-            table.add(expression: Expression<Double?>("salary"))
+            table.add(expression: SQLite.Expression<String>("name"))
+            table.add(expression: SQLite.Expression<Int>("age"))
+            table.add(expression: SQLite.Expression<Double?>("salary"))
         }
 
         let columns = try schema.columnDefinitions(table: "foo")

From a7d7e8c0b7529f851fd8927071003095f1e87522 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Thu, 12 Jun 2025 12:27:28 +0200
Subject: [PATCH 313/326] 17.5 no longer available

---
 .github/workflows/build.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index a0274321..33c4dba6 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -2,7 +2,7 @@ name: Build and test
 on: [push, pull_request]
 env:
   IOS_SIMULATOR: "iPhone 15"
-  IOS_VERSION: "17.5"
+  IOS_VERSION: "18.0"
 jobs:
   build:
     runs-on: macos-15

From 716394d091618047dfe86b7da5106fb21ede412f Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Thu, 12 Jun 2025 12:34:42 +0200
Subject: [PATCH 314/326] iPhone 16

---
 .github/workflows/build.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 33c4dba6..37f75476 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -1,7 +1,7 @@
 name: Build and test
 on: [push, pull_request]
 env:
-  IOS_SIMULATOR: "iPhone 15"
+  IOS_SIMULATOR: "iPhone 16"
   IOS_VERSION: "18.0"
 jobs:
   build:

From 9831a45edb84d492cd5b68ba6f8d53300808499f Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Thu, 12 Jun 2025 13:17:56 +0200
Subject: [PATCH 315/326] Update Index.md

---
 Documentation/Index.md | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/Documentation/Index.md b/Documentation/Index.md
index bc62c791..bd2a7a77 100644
--- a/Documentation/Index.md
+++ b/Documentation/Index.md
@@ -1592,7 +1592,8 @@ let schemaChanger = SchemaChanger(connection: db)
 try schemaChanger.create(table: "users") { table in 
     table.add(column: .init(name: "id", primaryKey: .init(autoIncrement: true), type: .INTEGER))
     table.add(column: .init(name: "name", type: .TEXT, nullable: false))            
-} 
+}
+``` 
 
 ### Indexes
 

From e3a916637162ece77b0ffd89b9e758669adc62f0 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Fri, 13 Jun 2025 22:49:43 +0200
Subject: [PATCH 316/326] Update CHANGELOG for release

---
 CHANGELOG.md | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index c96724fd..b77a8b07 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,10 @@
+0.15.4 (13-06-2025), [diff][diff-0.15.4]
+========================================
+* Fix cross compilation for linux on macOS fails ([#1317][])
+* Support creating tables in schema changer ([#1315][])
+* Update oldest supported platform versions ([#1280][])
+* Add CustomStringConvertible for Setter ([#1279][])
+
 0.15.3 (19-04-2024), [diff][diff-0.15.3]
 ========================================
 * Update `podspec` to include privacy manifest ([#1265][])
@@ -173,6 +180,8 @@ For breaking changes, see [Upgrading.md](Documentation/Upgrading.md).
 [diff-0.15.0]: https://github.com/stephencelis/SQLite.swift/compare/0.14.0...0.15.0
 [diff-0.15.1]: https://github.com/stephencelis/SQLite.swift/compare/0.15.0...0.15.1
 [diff-0.15.2]: https://github.com/stephencelis/SQLite.swift/compare/0.15.1...0.15.2
+[diff-0.15.3]: https://github.com/stephencelis/SQLite.swift/compare/0.15.2...0.15.3
+[diff-0.15.4]: https://github.com/stephencelis/SQLite.swift/compare/0.15.3...0.15.4
 
 [#30]: https://github.com/stephencelis/SQLite.swift/issues/30
 [#142]: https://github.com/stephencelis/SQLite.swift/issues/142
@@ -259,3 +268,7 @@ For breaking changes, see [Upgrading.md](Documentation/Upgrading.md).
 [#1237]: https://github.com/stephencelis/SQLite.swift/pull/1237
 [#1260]: https://github.com/stephencelis/SQLite.swift/pull/1260
 [#1265]: https://github.com/stephencelis/SQLite.swift/pull/1265
+[#1279]: https://github.com/stephencelis/SQLite.swift/pull/1279
+[#1280]: https://github.com/stephencelis/SQLite.swift/pull/1280
+[#1315]: https://github.com/stephencelis/SQLite.swift/pull/1315
+[#1317]: https://github.com/stephencelis/SQLite.swift/pull/1317
\ No newline at end of file

From 02e055c99c4fa19604a95fea4e82fd085391fdf0 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Fri, 13 Jun 2025 22:51:53 +0200
Subject: [PATCH 317/326] Update README

---
 README.md | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/README.md b/README.md
index a4d713b6..3e782f86 100644
--- a/README.md
+++ b/README.md
@@ -128,7 +128,7 @@ Swift code.
 
   ```swift
   dependencies: [
-      .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.15.3")
+      .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.15.4")
   ]
   ```
 
@@ -152,7 +152,7 @@ install SQLite.swift with Carthage:
  2. Update your Cartfile to include the following:
 
     ```ruby
-    github "stephencelis/SQLite.swift" ~> 0.15.3
+    github "stephencelis/SQLite.swift" ~> 0.15.4
     ```
 
  3. Run `carthage update` and
@@ -183,7 +183,7 @@ SQLite.swift with CocoaPods:
     use_frameworks!
 
     target 'YourAppTargetName' do
-        pod 'SQLite.swift', '~> 0.14.0'
+        pod 'SQLite.swift', '~> 0.15.0'
     end
     ```
 

From bbc5212c2ae9905d17ae581927c06e2cbad61ff7 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Fri, 13 Jun 2025 22:53:36 +0200
Subject: [PATCH 318/326] Project looks dead

---
 README.md | 1 -
 1 file changed, 1 deletion(-)

diff --git a/README.md b/README.md
index 3e782f86..db80fd3a 100644
--- a/README.md
+++ b/README.md
@@ -266,7 +266,6 @@ Looking for something else? Try another Swift wrapper (or [FMDB][]):
 
  - [GRDB](https://github.com/groue/GRDB.swift)
  - [SQLiteDB](https://github.com/FahimF/SQLiteDB)
- - [Squeal](https://github.com/nerdyc/Squeal)
 
 [Swift]: https://swift.org/
 [SQLite3]: https://www.sqlite.org

From 4e289bed6f1ed6737ffa963570beae3359a849e8 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Fri, 13 Jun 2025 23:00:44 +0200
Subject: [PATCH 319/326] No longer maintained

---
 README.md | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/README.md b/README.md
index db80fd3a..0e83504f 100644
--- a/README.md
+++ b/README.md
@@ -226,8 +226,6 @@ device:
 
 ## Communication
 
-[See the planning document] for a roadmap and existing feature requests.
-
 [Read the contributing guidelines][]. The _TL;DR_ (but please; _R_):
 
  - Need **help** or have a **general question**? [Ask on Stack
@@ -235,7 +233,6 @@ device:
  - Found a **bug** or have a **feature request**? [Open an issue][].
  - Want to **contribute**? [Submit a pull request][].
 
-[See the planning document]: /Documentation/Planning.md
 [Read the contributing guidelines]: ./CONTRIBUTING.md#contributing
 [Ask on Stack Overflow]: https://stackoverflow.com/questions/tagged/sqlite.swift
 [Open an issue]: https://github.com/stephencelis/SQLite.swift/issues/new

From 0f4f2c85c5beddeb70f02e1db36785b5f6e2dccf Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Fri, 13 Jun 2025 23:12:38 +0200
Subject: [PATCH 320/326] Bump version to 0.15.4

---
 Documentation/Index.md           | 12 ++++++------
 SQLite.swift.podspec             |  2 +-
 SQLite.xcodeproj/project.pbxproj | 12 ++----------
 Tests/SPM/Package.swift          |  2 +-
 4 files changed, 10 insertions(+), 18 deletions(-)

diff --git a/Documentation/Index.md b/Documentation/Index.md
index bd2a7a77..0e8261f2 100644
--- a/Documentation/Index.md
+++ b/Documentation/Index.md
@@ -109,7 +109,7 @@ process of downloading, compiling, and linking dependencies.
 
   ```swift
   dependencies: [
-    .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.15.3")
+    .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.15.4")
   ]
   ```
 
@@ -130,7 +130,7 @@ install SQLite.swift with Carthage:
  2. Update your Cartfile to include the following:
 
     ```ruby
-    github "stephencelis/SQLite.swift" ~> 0.15.3
+    github "stephencelis/SQLite.swift" ~> 0.15.4
     ```
 
  3. Run `carthage update` and [add the appropriate framework][Carthage Usage].
@@ -160,7 +160,7 @@ install SQLite.swift with Carthage:
     use_frameworks!
 
     target 'YourAppTargetName' do
-        pod 'SQLite.swift', '~> 0.15.3'
+        pod 'SQLite.swift', '~> 0.15.4'
     end
     ```
 
@@ -174,7 +174,7 @@ with the OS you can require the `standalone` subspec:
 
 ```ruby
 target 'YourAppTargetName' do
-  pod 'SQLite.swift/standalone', '~> 0.15.3'
+  pod 'SQLite.swift/standalone', '~> 0.15.4'
 end
 ```
 
@@ -184,7 +184,7 @@ dependency to sqlite3 or one of its subspecs:
 
 ```ruby
 target 'YourAppTargetName' do
-  pod 'SQLite.swift/standalone', '~> 0.15.3'
+  pod 'SQLite.swift/standalone', '~> 0.15.4'
   pod 'sqlite3/fts5', '= 3.15.0'  # SQLite 3.15.0 with FTS5 enabled
 end
 ```
@@ -200,7 +200,7 @@ If you want to use [SQLCipher][] with SQLite.swift you can require the
 target 'YourAppTargetName' do
   # Make sure you only require the subspec, otherwise you app might link against
   # the system SQLite, which means the SQLCipher-specific methods won't work.
-  pod 'SQLite.swift/SQLCipher', '~> 0.15.3'
+  pod 'SQLite.swift/SQLCipher', '~> 0.15.4'
 end
 ```
 
diff --git a/SQLite.swift.podspec b/SQLite.swift.podspec
index d0ccb136..73c6f705 100644
--- a/SQLite.swift.podspec
+++ b/SQLite.swift.podspec
@@ -1,6 +1,6 @@
 Pod::Spec.new do |s|
   s.name             = "SQLite.swift"
-  s.version          = "0.15.3"
+  s.version          = "0.15.4"
   s.summary          = "A type-safe, Swift-language layer over SQLite3."
 
   s.description      = <<-DESC
diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj
index a2d7edbe..ec0423e9 100644
--- a/SQLite.xcodeproj/project.pbxproj
+++ b/SQLite.xcodeproj/project.pbxproj
@@ -1539,7 +1539,6 @@
 				INFOPLIST_FILE = "$(SRCROOT)/Sources/SQLite/Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
-				MARKETING_VERSION = 0.15.3;
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
 				PRODUCT_NAME = SQLite;
 				SDKROOT = appletvos;
@@ -1562,7 +1561,6 @@
 				INFOPLIST_FILE = "$(SRCROOT)/Sources/SQLite/Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
-				MARKETING_VERSION = 0.15.3;
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
 				PRODUCT_NAME = SQLite;
 				SDKROOT = appletvos;
@@ -1614,7 +1612,6 @@
 				INFOPLIST_FILE = "$(SRCROOT)/Sources/SQLite/Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
-				MARKETING_VERSION = 0.15.3;
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
 				PRODUCT_NAME = SQLite;
 				SDKROOT = watchos;
@@ -1639,7 +1636,6 @@
 				INFOPLIST_FILE = "$(SRCROOT)/Sources/SQLite/Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
-				MARKETING_VERSION = 0.15.3;
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
 				PRODUCT_NAME = SQLite;
 				SDKROOT = watchos;
@@ -1664,7 +1660,6 @@
 				INFOPLIST_FILE = "$(SRCROOT)/Sources/SQLite/Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
-				MARKETING_VERSION = 0.15.3;
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
 				PRODUCT_NAME = SQLite;
 				SDKROOT = xros;
@@ -1689,7 +1684,6 @@
 				INFOPLIST_FILE = "$(SRCROOT)/Sources/SQLite/Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
-				MARKETING_VERSION = 0.15.3;
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
 				PRODUCT_NAME = SQLite;
 				SDKROOT = xros;
@@ -1778,6 +1772,7 @@
 				GCC_WARN_UNUSED_VARIABLE = YES;
 				IPHONEOS_DEPLOYMENT_TARGET = 13.0;
 				MACOSX_DEPLOYMENT_TARGET = 10.13;
+				MARKETING_VERSION = 0.15.4;
 				MTL_ENABLE_DEBUG_INFO = YES;
 				ONLY_ACTIVE_ARCH = YES;
 				PRODUCT_NAME = "";
@@ -1837,6 +1832,7 @@
 				GCC_WARN_UNUSED_VARIABLE = YES;
 				IPHONEOS_DEPLOYMENT_TARGET = 13.0;
 				MACOSX_DEPLOYMENT_TARGET = 10.13;
+				MARKETING_VERSION = 0.15.4;
 				MTL_ENABLE_DEBUG_INFO = NO;
 				PRODUCT_NAME = "";
 				SDKROOT = iphoneos;
@@ -1866,7 +1862,6 @@
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
 				IPHONEOS_DEPLOYMENT_TARGET = 12.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
-				MARKETING_VERSION = 0.15.3;
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
 				PRODUCT_NAME = SQLite;
 				SKIP_INSTALL = YES;
@@ -1890,7 +1885,6 @@
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
 				IPHONEOS_DEPLOYMENT_TARGET = 12.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
-				MARKETING_VERSION = 0.15.3;
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
 				PRODUCT_NAME = SQLite;
 				SKIP_INSTALL = YES;
@@ -1940,7 +1934,6 @@
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
 				MACOSX_DEPLOYMENT_TARGET = 10.13;
-				MARKETING_VERSION = 0.15.3;
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
 				PRODUCT_NAME = SQLite;
 				SDKROOT = macosx;
@@ -1966,7 +1959,6 @@
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
 				MACOSX_DEPLOYMENT_TARGET = 10.13;
-				MARKETING_VERSION = 0.15.3;
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
 				PRODUCT_NAME = SQLite;
 				SDKROOT = macosx;
diff --git a/Tests/SPM/Package.swift b/Tests/SPM/Package.swift
index 6521211a..a5a4afc4 100644
--- a/Tests/SPM/Package.swift
+++ b/Tests/SPM/Package.swift
@@ -15,7 +15,7 @@ let package = Package(
         // for testing from same repository
         .package(path: "../..")
         // normally this would be:
-        // .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.15.3")
+        // .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.15.4")
     ],
     targets: [
         .executableTarget(name: "test", dependencies: [.product(name: "SQLite", package: "SQLite.swift")])

From 496a086e5a4d03fb6b0338f63b600dc153be6fa3 Mon Sep 17 00:00:00 2001
From: Alsey Coleman Miller <alseycmiller@gmail.com>
Date: Mon, 14 Jul 2025 15:14:14 -0400
Subject: [PATCH 321/326] Build `SwiftToolchainCSQLite` for Android

---
 Package.swift | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Package.swift b/Package.swift
index cf3a0b60..56925d18 100644
--- a/Package.swift
+++ b/Package.swift
@@ -9,7 +9,7 @@ let targets: [Target] = [
     .target(
         name: "SQLite",
         dependencies: [
-            .product(name: "SwiftToolchainCSQLite", package: "swift-toolchain-sqlite", condition: .when(platforms: [.linux, .windows]))
+            .product(name: "SwiftToolchainCSQLite", package: "swift-toolchain-sqlite", condition: .when(platforms: [.linux, .windows, .android]))
         ],
         exclude: [
             "Info.plist"

From e8cfef658cbdbdd297ed7f549d197ec481b6b7a9 Mon Sep 17 00:00:00 2001
From: Alsey Coleman Miller <alseycmiller@gmail.com>
Date: Mon, 14 Jul 2025 15:14:34 -0400
Subject: [PATCH 322/326] Fix Android build

---
 Sources/SQLite/Core/Backup.swift                       | 2 +-
 Sources/SQLite/Core/Connection+Aggregation.swift       | 2 +-
 Sources/SQLite/Core/Connection+Attach.swift            | 2 +-
 Sources/SQLite/Core/Connection.swift                   | 2 +-
 Sources/SQLite/Core/Result.swift                       | 2 +-
 Sources/SQLite/Core/Statement.swift                    | 2 +-
 Sources/SQLite/Helpers.swift                           | 2 +-
 Tests/SQLiteTests/Core/Connection+AttachTests.swift    | 2 +-
 Tests/SQLiteTests/Core/Connection+PragmaTests.swift    | 2 +-
 Tests/SQLiteTests/Core/ConnectionTests.swift           | 2 +-
 Tests/SQLiteTests/Core/ResultTests.swift               | 2 +-
 Tests/SQLiteTests/Core/StatementTests.swift            | 2 +-
 Tests/SQLiteTests/Extensions/FTSIntegrationTests.swift | 2 +-
 Tests/SQLiteTests/Typed/CustomAggregationTests.swift   | 2 +-
 Tests/SQLiteTests/Typed/QueryIntegrationTests.swift    | 2 +-
 Tests/SQLiteTests/Typed/QueryTests.swift               | 2 +-
 16 files changed, 16 insertions(+), 16 deletions(-)

diff --git a/Sources/SQLite/Core/Backup.swift b/Sources/SQLite/Core/Backup.swift
index 5e741ecd..023acc08 100644
--- a/Sources/SQLite/Core/Backup.swift
+++ b/Sources/SQLite/Core/Backup.swift
@@ -28,7 +28,7 @@ import Dispatch
 import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
-#elseif os(Linux)
+#elseif canImport(SwiftToolchainCSQLite)
 import SwiftToolchainCSQLite
 #else
 import SQLite3
diff --git a/Sources/SQLite/Core/Connection+Aggregation.swift b/Sources/SQLite/Core/Connection+Aggregation.swift
index bfe253a7..a1abb74a 100644
--- a/Sources/SQLite/Core/Connection+Aggregation.swift
+++ b/Sources/SQLite/Core/Connection+Aggregation.swift
@@ -3,7 +3,7 @@ import Foundation
 import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
-#elseif os(Linux)
+#elseif canImport(SwiftToolchainCSQLite)
 import SwiftToolchainCSQLite
 #else
 import SQLite3
diff --git a/Sources/SQLite/Core/Connection+Attach.swift b/Sources/SQLite/Core/Connection+Attach.swift
index 32461468..0c674ee6 100644
--- a/Sources/SQLite/Core/Connection+Attach.swift
+++ b/Sources/SQLite/Core/Connection+Attach.swift
@@ -3,7 +3,7 @@ import Foundation
 import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
-#elseif os(Linux)
+#elseif canImport(SwiftToolchainCSQLite)
 import SwiftToolchainCSQLite
 #else
 import SQLite3
diff --git a/Sources/SQLite/Core/Connection.swift b/Sources/SQLite/Core/Connection.swift
index 188ff80b..57521f83 100644
--- a/Sources/SQLite/Core/Connection.swift
+++ b/Sources/SQLite/Core/Connection.swift
@@ -28,7 +28,7 @@ import Dispatch
 import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
-#elseif os(Linux)
+#elseif canImport(SwiftToolchainCSQLite)
 import SwiftToolchainCSQLite
 #else
 import SQLite3
diff --git a/Sources/SQLite/Core/Result.swift b/Sources/SQLite/Core/Result.swift
index 2659ad21..9a72e4c5 100644
--- a/Sources/SQLite/Core/Result.swift
+++ b/Sources/SQLite/Core/Result.swift
@@ -2,7 +2,7 @@
 import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
-#elseif os(Linux)
+#elseif canImport(SwiftToolchainCSQLite)
 import SwiftToolchainCSQLite
 #else
 import SQLite3
diff --git a/Sources/SQLite/Core/Statement.swift b/Sources/SQLite/Core/Statement.swift
index 458e3d9c..82d535b9 100644
--- a/Sources/SQLite/Core/Statement.swift
+++ b/Sources/SQLite/Core/Statement.swift
@@ -26,7 +26,7 @@
 import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
-#elseif os(Linux)
+#elseif canImport(SwiftToolchainCSQLite)
 import SwiftToolchainCSQLite
 #else
 import SQLite3
diff --git a/Sources/SQLite/Helpers.swift b/Sources/SQLite/Helpers.swift
index 00493f21..e3c84589 100644
--- a/Sources/SQLite/Helpers.swift
+++ b/Sources/SQLite/Helpers.swift
@@ -26,7 +26,7 @@
 import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
-#elseif os(Linux)
+#elseif canImport(SwiftToolchainCSQLite)
 import SwiftToolchainCSQLite
 #else
 import SQLite3
diff --git a/Tests/SQLiteTests/Core/Connection+AttachTests.swift b/Tests/SQLiteTests/Core/Connection+AttachTests.swift
index 68618596..0e185da5 100644
--- a/Tests/SQLiteTests/Core/Connection+AttachTests.swift
+++ b/Tests/SQLiteTests/Core/Connection+AttachTests.swift
@@ -6,7 +6,7 @@ import Foundation
 import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
-#elseif os(Linux)
+#elseif canImport(SwiftToolchainCSQLite)
 import SwiftToolchainCSQLite
 #else
 import SQLite3
diff --git a/Tests/SQLiteTests/Core/Connection+PragmaTests.swift b/Tests/SQLiteTests/Core/Connection+PragmaTests.swift
index bd29bd98..d1d4ab04 100644
--- a/Tests/SQLiteTests/Core/Connection+PragmaTests.swift
+++ b/Tests/SQLiteTests/Core/Connection+PragmaTests.swift
@@ -6,7 +6,7 @@ import Foundation
 import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
-#elseif os(Linux)
+#elseif canImport(SwiftToolchainCSQLite)
 import SwiftToolchainCSQLite
 #else
 import SQLite3
diff --git a/Tests/SQLiteTests/Core/ConnectionTests.swift b/Tests/SQLiteTests/Core/ConnectionTests.swift
index 61083453..da50974a 100644
--- a/Tests/SQLiteTests/Core/ConnectionTests.swift
+++ b/Tests/SQLiteTests/Core/ConnectionTests.swift
@@ -7,7 +7,7 @@ import Dispatch
 import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
-#elseif os(Linux)
+#elseif canImport(SwiftToolchainCSQLite)
 import SwiftToolchainCSQLite
 #else
 import SQLite3
diff --git a/Tests/SQLiteTests/Core/ResultTests.swift b/Tests/SQLiteTests/Core/ResultTests.swift
index b6a373f7..03415f3a 100644
--- a/Tests/SQLiteTests/Core/ResultTests.swift
+++ b/Tests/SQLiteTests/Core/ResultTests.swift
@@ -6,7 +6,7 @@ import Foundation
 import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
-#elseif os(Linux)
+#elseif canImport(SwiftToolchainCSQLite)
 import SwiftToolchainCSQLite
 #else
 import SQLite3
diff --git a/Tests/SQLiteTests/Core/StatementTests.swift b/Tests/SQLiteTests/Core/StatementTests.swift
index ceaa8813..3c90d941 100644
--- a/Tests/SQLiteTests/Core/StatementTests.swift
+++ b/Tests/SQLiteTests/Core/StatementTests.swift
@@ -5,7 +5,7 @@ import XCTest
 import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
-#elseif os(Linux)
+#elseif canImport(SwiftToolchainCSQLite)
 import SwiftToolchainCSQLite
 #else
 import SQLite3
diff --git a/Tests/SQLiteTests/Extensions/FTSIntegrationTests.swift b/Tests/SQLiteTests/Extensions/FTSIntegrationTests.swift
index 8a34e93b..b3b9e617 100644
--- a/Tests/SQLiteTests/Extensions/FTSIntegrationTests.swift
+++ b/Tests/SQLiteTests/Extensions/FTSIntegrationTests.swift
@@ -3,7 +3,7 @@ import XCTest
 import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
-#elseif os(Linux)
+#elseif canImport(SwiftToolchainCSQLite)
 import SwiftToolchainCSQLite
 #else
 import SQLite3
diff --git a/Tests/SQLiteTests/Typed/CustomAggregationTests.swift b/Tests/SQLiteTests/Typed/CustomAggregationTests.swift
index 8b7ec09a..73a0767f 100644
--- a/Tests/SQLiteTests/Typed/CustomAggregationTests.swift
+++ b/Tests/SQLiteTests/Typed/CustomAggregationTests.swift
@@ -7,7 +7,7 @@ import Dispatch
 import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
-#elseif os(Linux)
+#elseif canImport(SwiftToolchainCSQLite)
 import SwiftToolchainCSQLite
 #else
 import SQLite3
diff --git a/Tests/SQLiteTests/Typed/QueryIntegrationTests.swift b/Tests/SQLiteTests/Typed/QueryIntegrationTests.swift
index 899bf354..cde5a0c3 100644
--- a/Tests/SQLiteTests/Typed/QueryIntegrationTests.swift
+++ b/Tests/SQLiteTests/Typed/QueryIntegrationTests.swift
@@ -3,7 +3,7 @@ import XCTest
 import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
-#elseif os(Linux)
+#elseif canImport(SwiftToolchainCSQLite)
 import SwiftToolchainCSQLite
 #else
 import SQLite3
diff --git a/Tests/SQLiteTests/Typed/QueryTests.swift b/Tests/SQLiteTests/Typed/QueryTests.swift
index 7fc3dcf5..257b5245 100644
--- a/Tests/SQLiteTests/Typed/QueryTests.swift
+++ b/Tests/SQLiteTests/Typed/QueryTests.swift
@@ -3,7 +3,7 @@ import XCTest
 import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
-#elseif os(Linux)
+#elseif canImport(SwiftToolchainCSQLite)
 import SwiftToolchainCSQLite
 #else
 import SQLite3

From 91a30a3535e74767372ef605751569299696d8d1 Mon Sep 17 00:00:00 2001
From: NathanFallet <contact@nathanfallet.me>
Date: Tue, 15 Jul 2025 13:54:04 +0200
Subject: [PATCH 323/326] add android ci

---
 .github/workflows/build.yml | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 37f75476..f82a9c59 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -76,3 +76,9 @@ jobs:
       env:
         SPM: run
       run: ./run-tests.sh
+  build-android:
+    runs-on: ubuntu-latest
+    steps:
+    - uses: actions/checkout@v2
+    - name: Run tests
+      uses: skiptools/swift-android-action@v2

From 22d30c885f6ac1161ff5e88e32f054b4672963d2 Mon Sep 17 00:00:00 2001
From: NathanFallet <contact@nathanfallet.me>
Date: Wed, 16 Jul 2025 15:17:33 +0200
Subject: [PATCH 324/326] add SQLite. prefix for Expression in README

---
 README.md | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/README.md b/README.md
index 0e83504f..a52b7ae5 100644
--- a/README.md
+++ b/README.md
@@ -42,9 +42,9 @@ do {
     let db = try Connection("path/to/db.sqlite3")
 
     let users = Table("users")
-    let id = Expression<Int64>("id")
-    let name = Expression<String?>("name")
-    let email = Expression<String>("email")
+    let id = SQLite.Expression<Int64>("id")
+    let name = SQLite.Expression<String?>("name")
+    let email = SQLite.Expression<String>("email")
 
     try db.run(users.create { t in
         t.column(id, primaryKey: true)
@@ -83,6 +83,9 @@ do {
 }
 ```
 
+Note that `Expression` should be written as `SQLite.Expression` to avoid
+conflicts with the `SwiftUI.Expression` if you are using SwiftUI too.
+
 SQLite.swift also works as a lightweight, Swift-friendly wrapper over the C
 API.
 

From fee88af870ccf4f574607b1dc8f2835cb2aa0b36 Mon Sep 17 00:00:00 2001
From: hawk0620 <chenhao@weshot.cc>
Date: Thu, 17 Jul 2025 14:11:06 +0800
Subject: [PATCH 325/326] create index support array as parameters

---
 Sources/SQLite/Typed/Schema.swift | 16 ++++++++++++----
 1 file changed, 12 insertions(+), 4 deletions(-)

diff --git a/Sources/SQLite/Typed/Schema.swift b/Sources/SQLite/Typed/Schema.swift
index e162cf34..02f5b102 100644
--- a/Sources/SQLite/Typed/Schema.swift
+++ b/Sources/SQLite/Typed/Schema.swift
@@ -135,8 +135,8 @@ extension Table {
     }
 
     // MARK: - CREATE INDEX
-
-    public func createIndex(_ columns: Expressible..., unique: Bool = false, ifNotExists: Bool = false) -> String {
+    
+    public func createIndex(_ columns: [Expressible], unique: Bool = false, ifNotExists: Bool = false) -> String {
         let clauses: [Expressible?] = [
             create("INDEX", indexName(columns), unique ? .unique : nil, ifNotExists),
             Expression<Void>(literal: "ON"),
@@ -146,12 +146,20 @@ extension Table {
 
         return " ".join(clauses.compactMap { $0 }).asSQL()
     }
+    
+    public func createIndex(_ columns: Expressible..., unique: Bool = false, ifNotExists: Bool = false) -> String {
+        return createIndex(Array(columns), unique: unique, ifNotExists: ifNotExists)
+    }
 
     // MARK: - DROP INDEX
-
-    public func dropIndex(_ columns: Expressible..., ifExists: Bool = false) -> String {
+    
+    public func dropIndex(_ columns: [Expressible], ifExists: Bool = false) -> String {
         drop("INDEX", indexName(columns), ifExists)
     }
+    
+    public func dropIndex(_ columns: Expressible..., ifExists: Bool = false) -> String {
+        dropIndex(Array(columns), ifExists: ifExists)
+    }
 
     fileprivate func indexName(_ columns: [Expressible]) -> Expressible {
         let string = (["index", clauses.from.name, "on"] + columns.map { $0.expression.template }).joined(separator: " ").lowercased()

From 98d83a223db0d1a51c2d575f108c66b1b770b725 Mon Sep 17 00:00:00 2001
From: NathanFallet <contact@nathanfallet.me>
Date: Thu, 17 Jul 2025 13:42:49 +0200
Subject: [PATCH 326/326] fix linting

---
 Sources/SQLite/Typed/Query.swift    |  2 +-
 Sources/SQLite/Typed/Schema.swift   |  8 ++++----
 Tests/SQLiteTests/TestHelpers.swift | 10 ----------
 3 files changed, 5 insertions(+), 15 deletions(-)

diff --git a/Sources/SQLite/Typed/Query.swift b/Sources/SQLite/Typed/Query.swift
index 2a83665c..6162fcc7 100644
--- a/Sources/SQLite/Typed/Query.swift
+++ b/Sources/SQLite/Typed/Query.swift
@@ -1055,7 +1055,7 @@ extension Connection {
             }
 
             func expandGlob(_ namespace: Bool) -> (QueryType) throws -> Void {
-                { (queryType: QueryType) throws -> Void in
+                { (queryType: QueryType) throws in
                     var query = type(of: queryType).init(queryType.clauses.from.name, database: queryType.clauses.from.database)
                     query.clauses.select = queryType.clauses.select
                     query.clauses.with = strip(queryType.clauses.with)
diff --git a/Sources/SQLite/Typed/Schema.swift b/Sources/SQLite/Typed/Schema.swift
index 02f5b102..919042ab 100644
--- a/Sources/SQLite/Typed/Schema.swift
+++ b/Sources/SQLite/Typed/Schema.swift
@@ -135,7 +135,7 @@ extension Table {
     }
 
     // MARK: - CREATE INDEX
-    
+
     public func createIndex(_ columns: [Expressible], unique: Bool = false, ifNotExists: Bool = false) -> String {
         let clauses: [Expressible?] = [
             create("INDEX", indexName(columns), unique ? .unique : nil, ifNotExists),
@@ -146,17 +146,17 @@ extension Table {
 
         return " ".join(clauses.compactMap { $0 }).asSQL()
     }
-    
+
     public func createIndex(_ columns: Expressible..., unique: Bool = false, ifNotExists: Bool = false) -> String {
         return createIndex(Array(columns), unique: unique, ifNotExists: ifNotExists)
     }
 
     // MARK: - DROP INDEX
-    
+
     public func dropIndex(_ columns: [Expressible], ifExists: Bool = false) -> String {
         drop("INDEX", indexName(columns), ifExists)
     }
-    
+
     public func dropIndex(_ columns: Expressible..., ifExists: Bool = false) -> String {
         dropIndex(Array(columns), ifExists: ifExists)
     }
diff --git a/Tests/SQLiteTests/TestHelpers.swift b/Tests/SQLiteTests/TestHelpers.swift
index 56415a59..e62b2a23 100644
--- a/Tests/SQLiteTests/TestHelpers.swift
+++ b/Tests/SQLiteTests/TestHelpers.swift
@@ -164,14 +164,4 @@ struct TestOptionalCodable: Codable, Equatable {
     let double: Double?
     let date: Date?
     let uuid: UUID?
-
-    init(int: Int?, string: String?, bool: Bool?, float: Float?, double: Double?, date: Date?, uuid: UUID?) {
-        self.int = int
-        self.string = string
-        self.bool = bool
-        self.float = float
-        self.double = double
-        self.date = date
-        self.uuid = uuid
-    }
 }
