Files

636 lines
28 KiB
Swift

import XCTest
import GRDB
class TruncateOptimizationTests: GRDBTestCase {
// https://www.sqlite.org/c3ref/update_hook.html
//
// > In the current implementation, the update hook is not invoked [...]
// > when rows are deleted using the truncate optimization.
//
// https://www.sqlite.org/lang_delete.html#truncateopt
//
// > When the WHERE is omitted from a DELETE statement and the table
// > being deleted has no triggers, SQLite uses an optimization to erase
// > the entire table content without having to visit each row of the
// > table individually.
//
// We will thus test that the truncate optimization does not prevent
// transaction observers from observing individual deletions.
//
// But that's not enough: preventing the truncate optimization requires GRDB
// to fiddle with sqlite3_set_authorizer. When badly done, this can prevent
// DROP TABLE statements from dropping tables. SQLite3 authorizers are
// invoked during both compilation and execution of SQL statements. We will
// thus test that DROP TABLE statements perform as expected when compilation
// and execution are grouped, and when they are performed separately.
class DeletionObserver : TransactionObserver {
private var notify: ([String: Int]) -> Void
private var deletionEvents: [String: Int] = [:]
// Notifies table names with the number of deleted rows
init(_ notify: @escaping ([String: Int]) -> Void) {
self.notify = notify
}
func observes(eventsOfKind eventKind: DatabaseEventKind) -> Bool { true }
func databaseDidChange(with event: DatabaseEvent) {
if case .delete = event.kind {
deletionEvents[event.tableName, default: 0] += 1
}
}
func databaseDidCommit(_ db: Database) {
if !deletionEvents.isEmpty {
notify(deletionEvents)
}
deletionEvents = [:]
}
func databaseDidRollback(_ db: Database) {
deletionEvents = [:]
}
}
class UniversalObserver : TransactionObserver {
func observes(eventsOfKind eventKind: DatabaseEventKind) -> Bool { true }
func databaseDidChange(with event: DatabaseEvent) { }
func databaseDidCommit(_ db: Database) { }
func databaseDidRollback(_ db: Database) { }
}
func testExecuteDelete() throws {
let dbQueue = try makeDatabaseQueue()
var deletionEvents: [[String: Int]] = []
let observer = DeletionObserver { deletionEvents.append($0) }
dbQueue.add(transactionObserver: observer, extent: .databaseLifetime)
try dbQueue.writeWithoutTransaction { db in
try db.execute(sql: "CREATE TABLE t(a)")
try db.execute(sql: "INSERT INTO t VALUES (NULL)")
try db.execute(sql: "INSERT INTO t VALUES (NULL)")
deletionEvents = []
try db.execute(sql: "DELETE FROM t")
XCTAssertEqual(deletionEvents.count, 1)
XCTAssertEqual(deletionEvents[0], ["t": 2])
try db.execute(sql: "INSERT INTO t VALUES (NULL)")
deletionEvents = []
try db.execute(sql: "DELETE FROM t")
XCTAssertEqual(deletionEvents.count, 1)
XCTAssertEqual(deletionEvents[0], ["t": 1])
}
}
func testExecuteDeleteCaseSensitivity() throws {
let dbQueue = try makeDatabaseQueue()
class DeletionObserver : TransactionObserver {
var deletionCount = 0
func observes(eventsOfKind eventKind: DatabaseEventKind) -> Bool {
if case .delete(tableName: "pLaYeR") = eventKind {
return true
} else {
return false
}
}
func databaseDidChange(with event: DatabaseEvent) {
if case .delete = event.kind {
deletionCount += 1
}
}
func databaseDidCommit(_ db: Database) { }
func databaseDidRollback(_ db: Database) { }
}
let observer = DeletionObserver()
dbQueue.add(transactionObserver: observer, extent: .databaseLifetime)
try dbQueue.writeWithoutTransaction { db in
observer.deletionCount = 0
try db.execute(sql: "CREATE TABLE pLaYeR(a)")
try db.execute(sql: "INSERT INTO pLaYeR VALUES (NULL)")
try db.execute(sql: "INSERT INTO pLaYeR VALUES (NULL)")
try db.execute(sql: "DELETE FROM pLaYeR")
XCTAssertEqual(observer.deletionCount, 2)
observer.deletionCount = 0
try db.execute(sql: "INSERT INTO pLaYeR VALUES (NULL)")
try db.execute(sql: "DELETE FROM PLAYER")
XCTAssertEqual(observer.deletionCount, 1)
observer.deletionCount = 0
try db.execute(sql: "INSERT INTO pLaYeR VALUES (NULL)")
try db.execute(sql: "DELETE FROM player")
XCTAssertEqual(observer.deletionCount, 1)
}
}
func testExecuteDeleteWithPreparedStatement() throws {
let dbQueue = try makeDatabaseQueue()
var deletionEvents: [[String: Int]] = []
let observer = DeletionObserver { deletionEvents.append($0) }
dbQueue.add(transactionObserver: observer, extent: .databaseLifetime)
try dbQueue.writeWithoutTransaction { db in
try db.execute(sql: "CREATE TABLE t(a)")
let deleteStatement = try db.makeStatement(sql: "DELETE FROM t")
try db.execute(sql: "INSERT INTO t VALUES (NULL)")
try db.execute(sql: "INSERT INTO t VALUES (NULL)")
deletionEvents = []
try deleteStatement.execute()
XCTAssertEqual(deletionEvents.count, 1)
XCTAssertEqual(deletionEvents[0], ["t": 2])
try db.execute(sql: "INSERT INTO t VALUES (NULL)")
deletionEvents = []
try deleteStatement.execute()
XCTAssertEqual(deletionEvents.count, 1)
XCTAssertEqual(deletionEvents[0], ["t": 1])
}
}
func testExecuteDeleteWithCachedPreparedStatement() throws {
let dbQueue = try makeDatabaseQueue()
var deletionEvents: [[String: Int]] = []
try dbQueue.writeWithoutTransaction { db in
try db.execute(sql: "CREATE TABLE t(a)")
try db.cachedStatement(sql: "DELETE FROM t").execute()
// Start observing after statement has been cached: it does not
// prevent the truncate optimization.
let observer = DeletionObserver { deletionEvents.append($0) }
db.add(transactionObserver: observer, extent: .databaseLifetime)
try db.execute(sql: "INSERT INTO t VALUES (NULL)")
try db.execute(sql: "INSERT INTO t VALUES (NULL)")
deletionEvents = []
// This must be a recompiled statement, so that the truncate
// optimization is prevented.
try db.cachedStatement(sql: "DELETE FROM t").execute()
XCTAssertEqual(deletionEvents.count, 1)
XCTAssertEqual(deletionEvents[0], ["t": 2])
try db.execute(sql: "INSERT INTO t VALUES (NULL)")
deletionEvents = []
try db.cachedStatement(sql: "DELETE FROM t").execute()
XCTAssertEqual(deletionEvents.count, 1)
XCTAssertEqual(deletionEvents[0], ["t": 1])
}
}
func testDropTable() throws {
let dbQueue = try makeDatabaseQueue()
try dbQueue.writeWithoutTransaction { db in
try db.execute(sql: "CREATE TABLE t(a)")
try db.execute(sql: "INSERT INTO t VALUES (NULL)")
try XCTAssertTrue(db.tableExists("t"))
try db.execute(sql: "DROP TABLE t") // compile + execute
try XCTAssertFalse(db.tableExists("t"))
}
}
func testObservedDropTable() throws {
let dbQueue = try makeDatabaseQueue()
dbQueue.add(transactionObserver: UniversalObserver(), extent: .databaseLifetime)
try dbQueue.writeWithoutTransaction { db in
try db.execute(sql: "CREATE TABLE t(a)")
try db.execute(sql: "INSERT INTO t VALUES (NULL)")
try XCTAssertTrue(db.tableExists("t"))
try db.execute(sql: "DROP TABLE t") // compile + execute
try XCTAssertFalse(db.tableExists("t"))
}
}
func testDropTableWithPreparedStatement() throws {
let dbQueue = try makeDatabaseQueue()
try dbQueue.writeWithoutTransaction { db in
try db.execute(sql: "CREATE TABLE t(a)")
try db.execute(sql: "INSERT INTO t VALUES (NULL)")
try XCTAssertTrue(db.tableExists("t"))
let dropStatement = try db.makeStatement(sql: "DROP TABLE t") // compile...
try dropStatement.execute() // ... then execute
try XCTAssertFalse(db.tableExists("t"))
}
}
func testObservedDropTableWithPreparedStatement() throws {
let dbQueue = try makeDatabaseQueue()
dbQueue.add(transactionObserver: UniversalObserver(), extent: .databaseLifetime)
try dbQueue.writeWithoutTransaction { db in
try db.execute(sql: "CREATE TABLE t(a)")
try db.execute(sql: "INSERT INTO t VALUES (NULL)")
try XCTAssertTrue(db.tableExists("t"))
let dropStatement = try db.makeStatement(sql: "DROP TABLE t") // compile...
try dropStatement.execute() // ... then execute
try XCTAssertFalse(db.tableExists("t"))
}
}
func testDropTemporaryTable() throws {
let dbQueue = try makeDatabaseQueue()
try dbQueue.writeWithoutTransaction { db in
try db.execute(sql: "CREATE TEMPORARY TABLE t(a)")
try db.execute(sql: "INSERT INTO t VALUES (NULL)")
try XCTAssertTrue(db.tableExists("t"))
try db.execute(sql: "DROP TABLE t") // compile + execute
try XCTAssertFalse(db.tableExists("t"))
}
}
func testObservedDropTemporaryTable() throws {
let dbQueue = try makeDatabaseQueue()
dbQueue.add(transactionObserver: UniversalObserver(), extent: .databaseLifetime)
try dbQueue.writeWithoutTransaction { db in
try db.execute(sql: "CREATE TEMPORARY TABLE t(a)")
try db.execute(sql: "INSERT INTO t VALUES (NULL)")
try XCTAssertTrue(db.tableExists("t"))
try db.execute(sql: "DROP TABLE t") // compile + execute
try XCTAssertFalse(db.tableExists("t"))
}
}
func testDropTemporaryTableWithPreparedStatement() throws {
let dbQueue = try makeDatabaseQueue()
try dbQueue.writeWithoutTransaction { db in
try db.execute(sql: "CREATE TEMPORARY TABLE t(a)")
try db.execute(sql: "INSERT INTO t VALUES (NULL)")
try XCTAssertTrue(db.tableExists("t"))
let dropStatement = try db.makeStatement(sql: "DROP TABLE t") // compile...
try dropStatement.execute() // ... then execute
try XCTAssertFalse(db.tableExists("t"))
}
}
func testObservedDropTemporaryTableWithPreparedStatement() throws {
let dbQueue = try makeDatabaseQueue()
dbQueue.add(transactionObserver: UniversalObserver(), extent: .databaseLifetime)
try dbQueue.writeWithoutTransaction { db in
try db.execute(sql: "CREATE TEMPORARY TABLE t(a)")
try db.execute(sql: "INSERT INTO t VALUES (NULL)")
try XCTAssertTrue(db.tableExists("t"))
let dropStatement = try db.makeStatement(sql: "DROP TABLE t") // compile...
try dropStatement.execute() // ... then execute
try XCTAssertFalse(db.tableExists("t"))
}
}
func testDropVirtualTable() throws {
let dbQueue = try makeDatabaseQueue()
try dbQueue.writeWithoutTransaction { db in
try db.execute(sql: "CREATE VIRTUAL TABLE t USING fts3(a)")
try db.execute(sql: "INSERT INTO t VALUES (NULL)")
try XCTAssertTrue(db.tableExists("t"))
try db.execute(sql: "DROP TABLE t") // compile + execute
try XCTAssertFalse(db.tableExists("t"))
}
}
func testObservedDropVirtualTable() throws {
let dbQueue = try makeDatabaseQueue()
dbQueue.add(transactionObserver: UniversalObserver(), extent: .databaseLifetime)
try dbQueue.writeWithoutTransaction { db in
try db.execute(sql: "CREATE VIRTUAL TABLE t USING fts3(a)")
try db.execute(sql: "INSERT INTO t VALUES (NULL)")
try XCTAssertTrue(db.tableExists("t"))
try db.execute(sql: "DROP TABLE t") // compile + execute
try XCTAssertFalse(db.tableExists("t"))
}
}
func testDropVirtualTableWithPreparedStatement() throws {
let dbQueue = try makeDatabaseQueue()
try dbQueue.writeWithoutTransaction { db in
try db.execute(sql: "CREATE VIRTUAL TABLE t USING fts3(a)")
try db.execute(sql: "INSERT INTO t VALUES (NULL)")
try XCTAssertTrue(db.tableExists("t"))
let dropStatement = try db.makeStatement(sql: "DROP TABLE t") // compile...
try dropStatement.execute() // ... then execute
try XCTAssertFalse(db.tableExists("t"))
}
}
func testObservedDropVirtualTableWithPreparedStatement() throws {
let dbQueue = try makeDatabaseQueue()
dbQueue.add(transactionObserver: UniversalObserver(), extent: .databaseLifetime)
try dbQueue.writeWithoutTransaction { db in
try db.execute(sql: "CREATE VIRTUAL TABLE t USING fts3(a)")
try db.execute(sql: "INSERT INTO t VALUES (NULL)")
try XCTAssertTrue(db.tableExists("t"))
let dropStatement = try db.makeStatement(sql: "DROP TABLE t") // compile...
try dropStatement.execute() // ... then execute
try XCTAssertFalse(db.tableExists("t"))
}
}
func testDropView() throws {
let dbQueue = try makeDatabaseQueue()
try dbQueue.writeWithoutTransaction { db in
try db.execute(sql: "CREATE TABLE t(a)")
try db.execute(sql: "CREATE VIEW v AS SELECT * FROM t")
try db.execute(sql: "INSERT INTO t VALUES (NULL)")
try XCTAssertTrue(db.viewExists("v"))
try db.execute(sql: "DROP VIEW v") // compile + execute
try XCTAssertFalse(db.viewExists("v"))
}
}
func testObservedDropView() throws {
let dbQueue = try makeDatabaseQueue()
dbQueue.add(transactionObserver: UniversalObserver(), extent: .databaseLifetime)
try dbQueue.writeWithoutTransaction { db in
try db.execute(sql: "CREATE TABLE t(a)")
try db.execute(sql: "CREATE VIEW v AS SELECT * FROM t")
try db.execute(sql: "INSERT INTO t VALUES (NULL)")
try XCTAssertTrue(db.viewExists("v"))
try db.execute(sql: "DROP VIEW v") // compile + execute
try XCTAssertFalse(db.viewExists("v"))
}
}
func testDropViewWithPreparedStatement() throws {
let dbQueue = try makeDatabaseQueue()
try dbQueue.writeWithoutTransaction { db in
try db.execute(sql: "CREATE TABLE t(a)")
try db.execute(sql: "CREATE VIEW v AS SELECT * FROM t")
try db.execute(sql: "INSERT INTO t VALUES (NULL)")
try XCTAssertTrue(db.viewExists("v"))
let dropStatement = try db.makeStatement(sql: "DROP VIEW v") // compile...
try dropStatement.execute() // ... then execute
try XCTAssertFalse(db.viewExists("v"))
}
}
func testObservedDropViewWithPreparedStatement() throws {
let dbQueue = try makeDatabaseQueue()
dbQueue.add(transactionObserver: UniversalObserver(), extent: .databaseLifetime)
try dbQueue.writeWithoutTransaction { db in
try db.execute(sql: "CREATE TABLE t(a)")
try db.execute(sql: "CREATE VIEW v AS SELECT * FROM t")
try db.execute(sql: "INSERT INTO t VALUES (NULL)")
try XCTAssertTrue(db.viewExists("v"))
let dropStatement = try db.makeStatement(sql: "DROP VIEW v") // compile...
try dropStatement.execute() // ... then execute
try XCTAssertFalse(db.viewExists("v"))
}
}
func testDropTemporaryView() throws {
let dbQueue = try makeDatabaseQueue()
try dbQueue.writeWithoutTransaction { db in
try db.execute(sql: "CREATE TABLE t(a)")
try db.execute(sql: "CREATE TEMPORARY VIEW v AS SELECT * FROM t")
try db.execute(sql: "INSERT INTO t VALUES (NULL)")
try XCTAssertTrue(db.viewExists("v"))
try db.execute(sql: "DROP VIEW v") // compile + execute
try XCTAssertFalse(db.viewExists("v"))
}
}
func testObservedDropTemporaryView() throws {
let dbQueue = try makeDatabaseQueue()
dbQueue.add(transactionObserver: UniversalObserver(), extent: .databaseLifetime)
try dbQueue.writeWithoutTransaction { db in
try db.execute(sql: "CREATE TABLE t(a)")
try db.execute(sql: "CREATE TEMPORARY VIEW v AS SELECT * FROM t")
try db.execute(sql: "INSERT INTO t VALUES (NULL)")
try XCTAssertTrue(db.viewExists("v"))
try db.execute(sql: "DROP VIEW v") // compile + execute
try XCTAssertFalse(db.viewExists("v"))
}
}
func testDropTemporaryViewWithPreparedStatement() throws {
let dbQueue = try makeDatabaseQueue()
try dbQueue.writeWithoutTransaction { db in
try db.execute(sql: "CREATE TABLE t(a)")
try db.execute(sql: "CREATE TEMPORARY VIEW v AS SELECT * FROM t")
try db.execute(sql: "INSERT INTO t VALUES (NULL)")
try XCTAssertTrue(db.viewExists("v"))
let dropStatement = try db.makeStatement(sql: "DROP VIEW v") // compile...
try dropStatement.execute() // ... then execute
try XCTAssertFalse(db.viewExists("v"))
}
}
func testObservedDropTemporaryViewWithPreparedStatement() throws {
let dbQueue = try makeDatabaseQueue()
dbQueue.add(transactionObserver: UniversalObserver(), extent: .databaseLifetime)
try dbQueue.writeWithoutTransaction { db in
try db.execute(sql: "CREATE TABLE t(a)")
try db.execute(sql: "CREATE TEMPORARY VIEW v AS SELECT * FROM t")
try db.execute(sql: "INSERT INTO t VALUES (NULL)")
try XCTAssertTrue(db.viewExists("v"))
let dropStatement = try db.makeStatement(sql: "DROP VIEW v") // compile...
try dropStatement.execute() // ... then execute
try XCTAssertFalse(db.viewExists("v"))
}
}
func testDropIndex() throws {
let dbQueue = try makeDatabaseQueue()
try dbQueue.writeWithoutTransaction { db in
try db.execute(sql: "CREATE TABLE t(a)")
try db.execute(sql: "CREATE INDEX i ON t(a)")
try XCTAssertFalse(db.indexes(on: "t").isEmpty)
try db.execute(sql: "DROP INDEX i") // compile + execute
try XCTAssertTrue(db.indexes(on: "t").isEmpty)
}
}
func testObservedDropIndex() throws {
let dbQueue = try makeDatabaseQueue()
dbQueue.add(transactionObserver: UniversalObserver(), extent: .databaseLifetime)
try dbQueue.writeWithoutTransaction { db in
try db.execute(sql: "CREATE TABLE t(a)")
try db.execute(sql: "CREATE INDEX i ON t(a)")
try XCTAssertFalse(db.indexes(on: "t").isEmpty)
try db.execute(sql: "DROP INDEX i") // compile + execute
try XCTAssertTrue(db.indexes(on: "t").isEmpty)
}
}
func testDropIndexViewWithPreparedStatement() throws {
let dbQueue = try makeDatabaseQueue()
try dbQueue.writeWithoutTransaction { db in
try db.execute(sql: "CREATE TABLE t(a)")
try db.execute(sql: "CREATE INDEX i ON t(a)")
try db.execute(sql: "INSERT INTO t VALUES (1)")
try XCTAssertFalse(db.indexes(on: "t").isEmpty)
let dropStatement = try db.makeStatement(sql: "DROP INDEX i") // compile...
try dropStatement.execute() // ... then execute
try XCTAssertTrue(db.indexes(on: "t").isEmpty)
}
}
func testObservedDropIndexViewWithPreparedStatement() throws {
let dbQueue = try makeDatabaseQueue()
dbQueue.add(transactionObserver: UniversalObserver(), extent: .databaseLifetime)
try dbQueue.writeWithoutTransaction { db in
try db.execute(sql: "CREATE TABLE t(a)")
try db.execute(sql: "CREATE INDEX i ON t(a)")
try db.execute(sql: "INSERT INTO t VALUES (1)")
try XCTAssertFalse(db.indexes(on: "t").isEmpty)
let dropStatement = try db.makeStatement(sql: "DROP INDEX i") // compile...
try dropStatement.execute() // ... then execute
try XCTAssertTrue(db.indexes(on: "t").isEmpty)
}
}
func testDropTemporaryIndex() throws {
let dbQueue = try makeDatabaseQueue()
try dbQueue.writeWithoutTransaction { db in
try db.execute(sql: "CREATE TEMPORARY TABLE t(a)")
try db.execute(sql: "CREATE INDEX i ON t(a)")
try XCTAssertFalse(db.indexes(on: "t").isEmpty)
try db.execute(sql: "DROP INDEX i") // compile + execute
try XCTAssertTrue(db.indexes(on: "t").isEmpty)
}
}
func testObservedDropTemporaryIndex() throws {
let dbQueue = try makeDatabaseQueue()
dbQueue.add(transactionObserver: UniversalObserver(), extent: .databaseLifetime)
try dbQueue.writeWithoutTransaction { db in
try db.execute(sql: "CREATE TEMPORARY TABLE t(a)")
try db.execute(sql: "CREATE INDEX i ON t(a)")
try XCTAssertFalse(db.indexes(on: "t").isEmpty)
try db.execute(sql: "DROP INDEX i") // compile + execute
try XCTAssertTrue(db.indexes(on: "t").isEmpty)
}
}
func testDropTemporaryIndexViewWithPreparedStatement() throws {
let dbQueue = try makeDatabaseQueue()
try dbQueue.writeWithoutTransaction { db in
try db.execute(sql: "CREATE TEMPORARY TABLE t(a)")
try db.execute(sql: "CREATE INDEX i ON t(a)")
try db.execute(sql: "INSERT INTO t VALUES (1)")
try XCTAssertFalse(db.indexes(on: "t").isEmpty)
let dropStatement = try db.makeStatement(sql: "DROP INDEX i") // compile...
try dropStatement.execute() // ... then execute
try XCTAssertTrue(db.indexes(on: "t").isEmpty)
}
}
func testObservedDropTemporaryIndexViewWithPreparedStatement() throws {
let dbQueue = try makeDatabaseQueue()
dbQueue.add(transactionObserver: UniversalObserver(), extent: .databaseLifetime)
try dbQueue.writeWithoutTransaction { db in
try db.execute(sql: "CREATE TEMPORARY TABLE t(a)")
try db.execute(sql: "CREATE INDEX i ON t(a)")
try db.execute(sql: "INSERT INTO t VALUES (1)")
try XCTAssertFalse(db.indexes(on: "t").isEmpty)
let dropStatement = try db.makeStatement(sql: "DROP INDEX i") // compile...
try dropStatement.execute() // ... then execute
try XCTAssertTrue(db.indexes(on: "t").isEmpty)
}
}
func testDropTrigger() throws {
let dbQueue = try makeDatabaseQueue()
try dbQueue.writeWithoutTransaction { db in
try db.execute(sql: "CREATE TABLE t(a)")
try db.execute(sql: "CREATE TRIGGER r INSERT ON t BEGIN DELETE FROM t; END")
try XCTAssertTrue(db.triggerExists("r"))
try db.execute(sql: "DROP TRIGGER r") // compile + execute
try XCTAssertFalse(db.triggerExists("r"))
}
}
func testObservedDropTrigger() throws {
let dbQueue = try makeDatabaseQueue()
dbQueue.add(transactionObserver: UniversalObserver(), extent: .databaseLifetime)
try dbQueue.writeWithoutTransaction { db in
try db.execute(sql: "CREATE TABLE t(a)")
try db.execute(sql: "CREATE TRIGGER r INSERT ON t BEGIN DELETE FROM t; END")
try XCTAssertTrue(db.triggerExists("r"))
try db.execute(sql: "DROP TRIGGER r") // compile + execute
try XCTAssertFalse(db.triggerExists("r"))
}
}
func testDropTriggerWithPreparedStatement() throws {
let dbQueue = try makeDatabaseQueue()
try dbQueue.writeWithoutTransaction { db in
try db.execute(sql: "CREATE TABLE t(a)")
try db.execute(sql: "CREATE TRIGGER r INSERT ON t BEGIN DELETE FROM t; END")
try XCTAssertTrue(db.triggerExists("r"))
let dropStatement = try db.makeStatement(sql: "DROP TRIGGER r") // compile...
try dropStatement.execute() // ... then execute
try XCTAssertFalse(db.triggerExists("r"))
}
}
func testObservedDropTriggerWithPreparedStatement() throws {
let dbQueue = try makeDatabaseQueue()
dbQueue.add(transactionObserver: UniversalObserver(), extent: .databaseLifetime)
try dbQueue.writeWithoutTransaction { db in
try db.execute(sql: "CREATE TABLE t(a)")
try db.execute(sql: "CREATE TRIGGER r INSERT ON t BEGIN DELETE FROM t; END")
try XCTAssertTrue(db.triggerExists("r"))
let dropStatement = try db.makeStatement(sql: "DROP TRIGGER r") // compile...
try dropStatement.execute() // ... then execute
try XCTAssertFalse(db.triggerExists("r"))
}
}
func testDropTemporaryTrigger() throws {
let dbQueue = try makeDatabaseQueue()
try dbQueue.writeWithoutTransaction { db in
try db.execute(sql: "CREATE TABLE t(a)")
try db.execute(sql: "CREATE TEMPORARY TRIGGER r INSERT ON t BEGIN DELETE FROM t; END")
try XCTAssertTrue(db.triggerExists("r"))
try db.execute(sql: "DROP TRIGGER r") // compile + execute
try XCTAssertFalse(db.triggerExists("r"))
}
}
func testObservedDropTemporaryTrigger() throws {
let dbQueue = try makeDatabaseQueue()
dbQueue.add(transactionObserver: UniversalObserver(), extent: .databaseLifetime)
try dbQueue.writeWithoutTransaction { db in
try db.execute(sql: "CREATE TABLE t(a)")
try db.execute(sql: "CREATE TEMPORARY TRIGGER r INSERT ON t BEGIN DELETE FROM t; END")
try XCTAssertTrue(db.triggerExists("r"))
try db.execute(sql: "DROP TRIGGER r") // compile + execute
try XCTAssertFalse(db.triggerExists("r"))
}
}
func testDropTemporaryTriggerWithPreparedStatement() throws {
let dbQueue = try makeDatabaseQueue()
try dbQueue.writeWithoutTransaction { db in
try db.execute(sql: "CREATE TABLE t(a)")
try db.execute(sql: "CREATE TEMPORARY TRIGGER r INSERT ON t BEGIN DELETE FROM t; END")
try XCTAssertTrue(db.triggerExists("r"))
let dropStatement = try db.makeStatement(sql: "DROP TRIGGER r") // compile...
try dropStatement.execute() // ... then execute
try XCTAssertFalse(db.triggerExists("r"))
}
}
func testObservedDropTemporaryTriggerWithPreparedStatement() throws {
let dbQueue = try makeDatabaseQueue()
dbQueue.add(transactionObserver: UniversalObserver(), extent: .databaseLifetime)
try dbQueue.writeWithoutTransaction { db in
try db.execute(sql: "CREATE TABLE t(a)")
try db.execute(sql: "CREATE TEMPORARY TRIGGER r INSERT ON t BEGIN DELETE FROM t; END")
try XCTAssertTrue(db.triggerExists("r"))
let dropStatement = try db.makeStatement(sql: "DROP TRIGGER r") // compile...
try dropStatement.execute() // ... then execute
try XCTAssertFalse(db.triggerExists("r"))
}
}
}