-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Adding public method to reset a prepared statement #1145
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
Changes from 1 commit
4442ad0
ac1988c
012194b
58fd9dc
7b3de3d
5a30f29
efa5fcb
da0cd09
210303f
d970a11
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. unnecessary test |
||
try db.run("PRAGMA journal_mode=WAL;") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the test also passes with just let db = try Connection(.inMemory) looks like the exclusive lock is obtained even when WAL mode is not active There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ah interesting, i wouldn't have expected a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. After looking at this a bit more, I would change the test to not depend on any WAL specifics at all. Another way the db can end up in a locked state is when you have an open reader and then try to drop the table from the same connection: let statement = try db.prepare("SELECT email FROM users")
_ = try statement.step()
// verify that the transaction is not closed, which prevents schema changes
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
statement.reset()
// DROP table succeeds now
try db.run("DROP TABLE users") The db should be opened in-memory. Additionally, can you simplify the db schema? Most of the fields defined are not relevant. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. good idea to use the drop table - i've just pushed up a commit that uses the |
||
|
||
// 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 (?, ?, ?)", | ||
"[email protected]", 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("[email protected]", String(bytes: blob.bytes, encoding: .utf8)!) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. probably not relevant to the test |
||
|
||
// 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 | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
// reset the prepared statement, allowing the implicit transaction to close | ||
statement.reset() | ||
|
||
// truncate succeeds | ||
try db.run("pragma wal_checkpoint(truncate)") | ||
} | ||
|
||
} |
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's better to create new and unique temporary file instead: see
temporaryFile()
inFixtures
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you don't need to remove these files since they are created each time