add swiftUI code
This commit is contained in:
+399
@@ -0,0 +1,399 @@
|
||||
import XCTest
|
||||
@testable import GRDB
|
||||
|
||||
class DatabaseQueueSchemaCacheTests : GRDBTestCase {
|
||||
|
||||
func testCache() throws {
|
||||
try makeDatabaseQueue().inDatabase { db in
|
||||
try db.execute(sql: "CREATE TABLE items (id INTEGER PRIMARY KEY)")
|
||||
|
||||
// Assert that cache is empty
|
||||
XCTAssertTrue(db.schemaCache[.main].primaryKey("items") == nil)
|
||||
|
||||
// Warm cache
|
||||
let primaryKey = try db.primaryKey("items")
|
||||
XCTAssertEqual(primaryKey.rowIDColumn, "id")
|
||||
|
||||
// Assert that cache is warmed
|
||||
XCTAssertTrue(db.schemaCache[.main].primaryKey("items") != nil)
|
||||
|
||||
// Empty cache after schema change
|
||||
try db.execute(sql: "DROP TABLE items")
|
||||
|
||||
// Assert that cache is empty
|
||||
XCTAssertTrue(db.schemaCache[.main].primaryKey("items") == nil)
|
||||
|
||||
do {
|
||||
// Assert that cache is used: we expect an error now that
|
||||
// the cache is empty.
|
||||
_ = try db.primaryKey("items")
|
||||
XCTFail()
|
||||
} catch let error as DatabaseError {
|
||||
XCTAssertEqual(error.resultCode, .SQLITE_ERROR)
|
||||
XCTAssertEqual(error.message!, "no such table: items")
|
||||
XCTAssertEqual(error.description, "SQLite error 1: no such table: items")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testCacheInvalidationWithCursor() throws {
|
||||
try makeDatabaseQueue().inDatabase { db in
|
||||
try db.execute(sql: "CREATE TABLE items (id INTEGER PRIMARY KEY)")
|
||||
|
||||
// Assert that cache is empty
|
||||
XCTAssertTrue(db.schemaCache[.main].primaryKey("items") == nil)
|
||||
|
||||
// Warm cache
|
||||
let primaryKey = try db.primaryKey("items")
|
||||
XCTAssertEqual(primaryKey.rowIDColumn, "id")
|
||||
|
||||
// Assert that cache is warmed
|
||||
XCTAssertTrue(db.schemaCache[.main].primaryKey("items") != nil)
|
||||
|
||||
// Assert that cache is still warm until the DROP TABLE statement has been executed
|
||||
let statement = try db.makeStatement(sql: "DROP TABLE items")
|
||||
XCTAssertTrue(db.schemaCache[.main].primaryKey("items") != nil)
|
||||
let cursor = try Row.fetchCursor(statement)
|
||||
XCTAssertTrue(db.schemaCache[.main].primaryKey("items") != nil)
|
||||
_ = try cursor.next()
|
||||
|
||||
// Assert that cache is empty after cursor has run sqlite3_step
|
||||
XCTAssertTrue(db.schemaCache[.main].primaryKey("items") == nil)
|
||||
}
|
||||
}
|
||||
|
||||
func testTempCache() throws {
|
||||
let dbQueue = try makeDatabaseQueue()
|
||||
|
||||
try dbQueue.inDatabase { db in
|
||||
try db.execute(sql: "CREATE TEMPORARY TABLE items (id INTEGER PRIMARY KEY)")
|
||||
}
|
||||
|
||||
dbQueue.inDatabase { db in
|
||||
// Assert that cache is empty
|
||||
XCTAssertTrue(db.schemaCache[.temp].primaryKey("items") == nil)
|
||||
}
|
||||
|
||||
try dbQueue.inDatabase { db in
|
||||
// Warm cache
|
||||
let primaryKey = try db.primaryKey("items")
|
||||
XCTAssertEqual(primaryKey.rowIDColumn, "id")
|
||||
|
||||
// Assert that cache is warmed
|
||||
XCTAssertTrue(db.schemaCache[.temp].primaryKey("items") != nil)
|
||||
}
|
||||
|
||||
try dbQueue.inDatabase { db in
|
||||
// Empty cache after schema change
|
||||
try db.execute(sql: "DROP TABLE items")
|
||||
|
||||
// Assert that cache is empty
|
||||
XCTAssertTrue(db.schemaCache[.temp].primaryKey("items") == nil)
|
||||
}
|
||||
|
||||
try dbQueue.inDatabase { db in
|
||||
do {
|
||||
// Assert that cache is used: we expect an error now that
|
||||
// the cache is empty.
|
||||
_ = try db.primaryKey("items")
|
||||
XCTFail()
|
||||
} catch let error as DatabaseError {
|
||||
XCTAssertEqual(error.resultCode, .SQLITE_ERROR)
|
||||
XCTAssertEqual(error.message!, "no such table: items")
|
||||
XCTAssertEqual(error.description, "SQLite error 1: no such table: items")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testMainShadowedByTemp() throws {
|
||||
let dbQueue = try makeDatabaseQueue()
|
||||
|
||||
try dbQueue.inDatabase { db in
|
||||
do {
|
||||
XCTAssertTrue(db.schemaCache[.main].primaryKey("items") == nil)
|
||||
XCTAssertTrue(db.schemaCache[.temp].primaryKey("items") == nil)
|
||||
}
|
||||
|
||||
do {
|
||||
try db.execute(sql: """
|
||||
CREATE TABLE items (id INTEGER PRIMARY KEY);
|
||||
INSERT INTO items (id) VALUES (1);
|
||||
""")
|
||||
let primaryKey = try db.primaryKey("items")
|
||||
XCTAssertEqual(primaryKey.rowIDColumn, "id")
|
||||
XCTAssertTrue(db.schemaCache[.main].primaryKey("items")!.value != nil)
|
||||
XCTAssertTrue(db.schemaCache[.temp].primaryKey("items") == nil)
|
||||
try XCTAssertEqual(Int.fetchOne(db, sql: "SELECT id FROM items"), 1)
|
||||
}
|
||||
|
||||
do {
|
||||
try db.execute(sql: """
|
||||
CREATE TEMPORARY TABLE items (otherID INTEGER PRIMARY KEY);
|
||||
INSERT INTO items (otherID) VALUES (2);
|
||||
""")
|
||||
let primaryKey = try db.primaryKey("items")
|
||||
XCTAssertEqual(primaryKey.rowIDColumn, "otherID")
|
||||
XCTAssertTrue(db.schemaCache[.main].primaryKey("items") == nil)
|
||||
XCTAssertTrue(db.schemaCache[.temp].primaryKey("items")!.value != nil)
|
||||
try XCTAssertEqual(Int.fetchOne(db, sql: "SELECT otherID FROM items"), 2)
|
||||
}
|
||||
|
||||
do {
|
||||
try db.execute(sql: "DROP TABLE items")
|
||||
let primaryKey = try db.primaryKey("items")
|
||||
XCTAssertEqual(primaryKey.rowIDColumn, "id")
|
||||
XCTAssertTrue(db.schemaCache[.main].primaryKey("items")!.value != nil)
|
||||
XCTAssertTrue(db.schemaCache[.temp].primaryKey("items")!.value == nil)
|
||||
try XCTAssertEqual(Int.fetchOne(db, sql: "SELECT id FROM items"), 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testMainShadowedByAttachedDatabase() throws {
|
||||
#if GRDBCIPHER_USE_ENCRYPTION
|
||||
// Avoid error due to key not being provided:
|
||||
// file is not a database - while executing `ATTACH DATABASE...`
|
||||
throw XCTSkip("This test does not support encrypted databases")
|
||||
#endif
|
||||
|
||||
let attached1 = try makeDatabaseQueue(filename: "attached1")
|
||||
try attached1.write { db in
|
||||
try db.execute(sql: """
|
||||
CREATE TABLE item (attached1Column TEXT);
|
||||
INSERT INTO item VALUES('attached1');
|
||||
""")
|
||||
try XCTAssertEqual(String.fetchOne(db, sql: "SELECT * FROM item"), "attached1")
|
||||
try XCTAssertEqual(db.columns(in: "item").first?.name, "attached1Column")
|
||||
}
|
||||
|
||||
let attached2 = try makeDatabaseQueue(filename: "attached2")
|
||||
try attached2.write { db in
|
||||
try db.execute(sql: """
|
||||
CREATE TABLE item (attached2Column TEXT);
|
||||
INSERT INTO item VALUES('attached2');
|
||||
""")
|
||||
try XCTAssertEqual(String.fetchOne(db, sql: "SELECT * FROM item"), "attached2")
|
||||
try XCTAssertEqual(db.columns(in: "item").first?.name, "attached2Column")
|
||||
}
|
||||
|
||||
let main = try makeDatabaseQueue(filename: "main")
|
||||
try main.writeWithoutTransaction { db in
|
||||
try XCTAssertFalse(db.tableExists("item"))
|
||||
|
||||
try db.execute(literal: "ATTACH DATABASE \(attached1.path) AS attached1")
|
||||
try XCTAssertEqual(String.fetchOne(db, sql: "SELECT * FROM item"), "attached1")
|
||||
try XCTAssertEqual(Row.fetchOne(db, sql: "PRAGMA table_info(item)")?["name"], "attached1Column")
|
||||
try XCTAssertEqual(Row.fetchOne(db, sql: "PRAGMA attached1.table_info(item)")?["name"], "attached1Column")
|
||||
try XCTAssertTrue(db.tableExists("item"))
|
||||
try XCTAssertEqual(db.columns(in: "item").first?.name, "attached1Column")
|
||||
|
||||
// Main shadows attached1
|
||||
try db.execute(sql: """
|
||||
CREATE TABLE item (mainColumn TEXT);
|
||||
INSERT INTO item VALUES('main');
|
||||
""")
|
||||
try XCTAssertEqual(String.fetchOne(db, sql: "SELECT * FROM item"), "main")
|
||||
try XCTAssertEqual(Row.fetchOne(db, sql: "PRAGMA main.table_info(item)")?["name"], "mainColumn")
|
||||
try XCTAssertEqual(Row.fetchOne(db, sql: "PRAGMA attached1.table_info(item)")?["name"], "attached1Column")
|
||||
try XCTAssertEqual(Row.fetchOne(db, sql: "PRAGMA table_info(item)")?["name"], "mainColumn")
|
||||
try XCTAssertTrue(db.tableExists("item"))
|
||||
try XCTAssertEqual(db.columns(in: "item").first?.name, "mainColumn")
|
||||
|
||||
// Main no longer shadows attached1
|
||||
try db.execute(sql: "DROP TABLE item")
|
||||
try XCTAssertEqual(String.fetchOne(db, sql: "SELECT * FROM item"), "attached1")
|
||||
try XCTAssertEqual(Row.fetchOne(db, sql: "PRAGMA attached1.table_info(item)")?["name"], "attached1Column")
|
||||
try XCTAssertEqual(Row.fetchOne(db, sql: "PRAGMA table_info(item)")?["name"], "attached1Column")
|
||||
try XCTAssertTrue(db.tableExists("item"))
|
||||
try XCTAssertEqual(db.columns(in: "item").first?.name, "attached1Column")
|
||||
|
||||
// Attached1 shadows attached2
|
||||
try db.execute(literal: "ATTACH DATABASE \(attached2.path) AS attached2")
|
||||
try XCTAssertEqual(String.fetchOne(db, sql: "SELECT * FROM item"), "attached1")
|
||||
try XCTAssertEqual(Row.fetchOne(db, sql: "PRAGMA attached1.table_info(item)")?["name"], "attached1Column")
|
||||
try XCTAssertEqual(Row.fetchOne(db, sql: "PRAGMA attached2.table_info(item)")?["name"], "attached2Column")
|
||||
try XCTAssertEqual(Row.fetchOne(db, sql: "PRAGMA table_info(item)")?["name"], "attached1Column")
|
||||
try XCTAssertTrue(db.tableExists("item"))
|
||||
try XCTAssertEqual(db.columns(in: "item").first?.name, "attached1Column")
|
||||
|
||||
// Attached1 no longer shadows attached2
|
||||
try db.execute(sql: "DETACH DATABASE attached1")
|
||||
try XCTAssertEqual(String.fetchOne(db, sql: "SELECT * FROM item"), "attached2")
|
||||
try XCTAssertEqual(Row.fetchOne(db, sql: "PRAGMA attached2.table_info(item)")?["name"], "attached2Column")
|
||||
try XCTAssertEqual(Row.fetchOne(db, sql: "PRAGMA table_info(item)")?["name"], "attached2Column")
|
||||
try XCTAssertTrue(db.tableExists("item"))
|
||||
try XCTAssertEqual(db.columns(in: "item").first?.name, "attached2Column")
|
||||
|
||||
// Attached2 no longer shadows main
|
||||
try db.execute(sql: "DETACH DATABASE attached2")
|
||||
try XCTAssertFalse(db.tableExists("item"))
|
||||
}
|
||||
}
|
||||
|
||||
func testTableExistsThrowsWhenUnknownSchema() throws {
|
||||
let dbQueue = try makeDatabaseQueue()
|
||||
try dbQueue.inDatabase { db in
|
||||
try db.execute(sql: """
|
||||
CREATE TABLE t (id INTEGER);
|
||||
CREATE VIEW v AS SELECT * FROM t;
|
||||
CREATE TRIGGER tr AFTER INSERT ON t BEGIN SELECT 1; END;
|
||||
""")
|
||||
do {
|
||||
_ = try db.tableExists("t", in: "invalid")
|
||||
XCTFail("Expected Error")
|
||||
} catch let error as DatabaseError {
|
||||
XCTAssertEqual(error.resultCode, .SQLITE_ERROR)
|
||||
XCTAssertEqual(error.message, "no such schema: invalid")
|
||||
XCTAssertEqual(error.description, "SQLite error 1: no such schema: invalid")
|
||||
}
|
||||
do {
|
||||
_ = try db.viewExists("v", in: "invalid")
|
||||
XCTFail("Expected Error")
|
||||
} catch let error as DatabaseError {
|
||||
XCTAssertEqual(error.resultCode, .SQLITE_ERROR)
|
||||
XCTAssertEqual(error.message, "no such schema: invalid")
|
||||
XCTAssertEqual(error.description, "SQLite error 1: no such schema: invalid")
|
||||
}
|
||||
do {
|
||||
_ = try db.triggerExists("tr", in: "invalid")
|
||||
XCTFail("Expected Error")
|
||||
} catch let error as DatabaseError {
|
||||
XCTAssertEqual(error.resultCode, .SQLITE_ERROR)
|
||||
XCTAssertEqual(error.message, "no such schema: invalid")
|
||||
XCTAssertEqual(error.description, "SQLite error 1: no such schema: invalid")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testExistsWithSpecifiedMainSchema() throws {
|
||||
let dbQueue = try makeDatabaseQueue()
|
||||
try dbQueue.inDatabase { db in
|
||||
try db.execute(sql: """
|
||||
CREATE TABLE t (id INTEGER);
|
||||
CREATE VIEW v AS SELECT * FROM t;
|
||||
CREATE TRIGGER tr AFTER INSERT ON t BEGIN SELECT 1; END;
|
||||
""")
|
||||
let tableExists = try db.tableExists("t", in: "main")
|
||||
let viewExists = try db.viewExists("v", in: "main")
|
||||
let triggerExists = try db.triggerExists("tr", in: "main")
|
||||
XCTAssertTrue(tableExists)
|
||||
XCTAssertTrue(viewExists)
|
||||
XCTAssertTrue(triggerExists)
|
||||
}
|
||||
}
|
||||
|
||||
func testNotExistsWithSpecifiedMainSchema() throws {
|
||||
let dbQueue = try makeDatabaseQueue()
|
||||
try dbQueue.inDatabase { db in
|
||||
let tableExists = try db.tableExists("t", in: "main")
|
||||
let viewExists = try db.viewExists("v", in: "main")
|
||||
let triggerExists = try db.triggerExists("tr", in: "main")
|
||||
XCTAssertFalse(tableExists)
|
||||
XCTAssertFalse(viewExists)
|
||||
XCTAssertFalse(triggerExists)
|
||||
}
|
||||
}
|
||||
|
||||
func testExistsWithSpecifiedSchemaWithEntityNameCollisions() throws {
|
||||
#if GRDBCIPHER_USE_ENCRYPTION
|
||||
// Avoid error due to key not being provided:
|
||||
// file is not a database - while executing `ATTACH DATABASE...`
|
||||
throw XCTSkip("This test does not support encrypted databases")
|
||||
#endif
|
||||
|
||||
let attached = try makeDatabaseQueue(filename: "attached")
|
||||
try attached.inDatabase { db in
|
||||
try db.execute(sql: """
|
||||
CREATE TABLE t (id INTEGER);
|
||||
CREATE VIEW v AS SELECT * FROM t;
|
||||
CREATE TRIGGER tr AFTER INSERT ON t BEGIN SELECT 1; END;
|
||||
""")
|
||||
}
|
||||
let main = try makeDatabaseQueue(filename: "main")
|
||||
try main.inDatabase { db in
|
||||
try db.execute(sql: """
|
||||
CREATE TABLE t (id INTEGER);
|
||||
CREATE VIEW v AS SELECT * FROM t;
|
||||
CREATE TRIGGER tr AFTER INSERT ON t BEGIN SELECT 1; END;
|
||||
""")
|
||||
try db.execute(literal: "ATTACH DATABASE \(attached.path) AS attached")
|
||||
|
||||
let tableExistsInMain = try db.tableExists("t", in: "main")
|
||||
let viewExistsInMain = try db.viewExists("v", in: "main")
|
||||
let triggerExistsInMain = try db.triggerExists("tr", in: "main")
|
||||
XCTAssertTrue(tableExistsInMain)
|
||||
XCTAssertTrue(viewExistsInMain)
|
||||
XCTAssertTrue(triggerExistsInMain)
|
||||
|
||||
let tableExistsInAttached = try db.tableExists("t", in: "attached")
|
||||
let viewExistsInAttached = try db.viewExists("v", in: "attached")
|
||||
let triggerExistsInAttached = try db.triggerExists("tr", in: "attached")
|
||||
XCTAssertTrue(tableExistsInAttached)
|
||||
XCTAssertTrue(viewExistsInAttached)
|
||||
XCTAssertTrue(triggerExistsInAttached)
|
||||
}
|
||||
}
|
||||
|
||||
func testExistsWithUnspecifiedSchemaWithEntityNameCollisions() throws {
|
||||
#if GRDBCIPHER_USE_ENCRYPTION
|
||||
// Avoid error due to key not being provided:
|
||||
// file is not a database - while executing `ATTACH DATABASE...`
|
||||
throw XCTSkip("This test does not support encrypted databases")
|
||||
#endif
|
||||
|
||||
let attached = try makeDatabaseQueue(filename: "attached")
|
||||
try attached.inDatabase { db in
|
||||
try db.execute(sql: """
|
||||
CREATE TABLE t (id INTEGER);
|
||||
CREATE VIEW v AS SELECT * FROM t;
|
||||
CREATE TRIGGER tr AFTER INSERT ON t BEGIN SELECT 1; END;
|
||||
""")
|
||||
}
|
||||
let main = try makeDatabaseQueue(filename: "main")
|
||||
try main.inDatabase { db in
|
||||
try db.execute(sql: """
|
||||
CREATE TABLE t (id INTEGER);
|
||||
CREATE VIEW v AS SELECT * FROM t;
|
||||
CREATE TRIGGER tr AFTER INSERT ON t BEGIN SELECT 1; END;
|
||||
""")
|
||||
try db.execute(literal: "ATTACH DATABASE \(attached.path) AS attached")
|
||||
|
||||
// Some entity with the name exists, but we can't prove from this information which one
|
||||
// it's getting. `true` is still the correct result.
|
||||
let tableExists = try db.tableExists("t")
|
||||
let viewExists = try db.viewExists("v")
|
||||
let triggerExists = try db.triggerExists("tr")
|
||||
XCTAssertTrue(tableExists)
|
||||
XCTAssertTrue(viewExists)
|
||||
XCTAssertTrue(triggerExists)
|
||||
}
|
||||
}
|
||||
|
||||
func testExistsWithUnspecifiedSchemaFindsAttachedDatabase() throws {
|
||||
#if GRDBCIPHER_USE_ENCRYPTION
|
||||
// Avoid error due to key not being provided:
|
||||
// file is not a database - while executing `ATTACH DATABASE...`
|
||||
throw XCTSkip("This test does not support encrypted databases")
|
||||
#endif
|
||||
|
||||
let attached = try makeDatabaseQueue(filename: "attached")
|
||||
try attached.inDatabase { db in
|
||||
try db.execute(sql: """
|
||||
CREATE TABLE t (id INTEGER);
|
||||
CREATE VIEW v AS SELECT * FROM t;
|
||||
CREATE TRIGGER tr AFTER INSERT ON t BEGIN SELECT 1; END;
|
||||
""")
|
||||
}
|
||||
let main = try makeDatabaseQueue(filename: "main")
|
||||
try main.inDatabase { db in
|
||||
try db.execute(literal: "ATTACH DATABASE \(attached.path) AS attached")
|
||||
|
||||
let tableExists = try db.tableExists("t")
|
||||
let viewExists = try db.viewExists("v")
|
||||
let triggerExists = try db.triggerExists("tr")
|
||||
XCTAssertTrue(tableExists)
|
||||
XCTAssertTrue(viewExists)
|
||||
XCTAssertTrue(triggerExists)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user