From b9489a7cc11f18a15e744193e29de1430ddd35b2 Mon Sep 17 00:00:00 2001 From: Nathan Fallet Date: Mon, 17 Jan 2022 17:08:36 +0100 Subject: [PATCH 1/8] 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 Date: Mon, 17 Jan 2022 17:30:00 +0100 Subject: [PATCH 2/8] 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 Date: Tue, 18 Jan 2022 13:28:31 +0100 Subject: [PATCH 3/8] 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 Date: Tue, 25 Jan 2022 13:14:00 +0100 Subject: [PATCH 4/8] 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 Date: Tue, 25 Jan 2022 14:09:11 +0100 Subject: [PATCH 5/8] 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 Date: Tue, 25 Jan 2022 14:50:37 +0100 Subject: [PATCH 6/8] 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 Date: Tue, 25 Jan 2022 15:05:28 +0100 Subject: [PATCH 7/8] 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 Date: Tue, 25 Jan 2022 15:19:40 +0100 Subject: [PATCH 8/8] 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 {