Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Native user_version support in Connection #1105

Merged
merged 8 commits into from
Jan 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 2 additions & 12 deletions Documentation/Index.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
13 changes: 13 additions & 0 deletions Sources/SQLite/Core/Connection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,19 @@ 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 {
if let userVersion = newValue {
_ = try? run("PRAGMA user_version = \(userVersion)")
}
nathanfallet marked this conversation as resolved.
Show resolved Hide resolved
}
}

// MARK: - Execute

/// Executes a batch of SQL statements.
Expand Down
5 changes: 5 additions & 0 deletions Tests/SQLiteTests/ConnectionTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,11 @@ class ConnectionTests: SQLiteTestCase {
XCTAssertEqual(2, db.totalChanges)
}

func test_userVersion() {
db.userVersion = 2
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)
Expand Down
26 changes: 19 additions & 7 deletions Tests/SQLiteTests/QueryTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -391,15 +391,27 @@ 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(

// 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\" = '\(encodedJSONString)'
""".replacingOccurrences(of: "\n", with: ""),
update
)
\"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 decodedJSON = try JSONDecoder().decode(TestCodable.self, from: extractedJSON.data(using: .utf8)!)
XCTAssertEqual(decodedJSON, value1)
}

func test_delete_compilesDeleteExpression() {
Expand Down
13 changes: 12 additions & 1 deletion Tests/SQLiteTests/TestHelpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -125,4 +125,15 @@ class TestCodable: Codable {
self.optional = optional
self.sub = sub
}

static func == (lhs: TestCodable, rhs: TestCodable) -> Bool {
nathanfallet marked this conversation as resolved.
Show resolved Hide resolved
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
}
}