import XCTest import GRDB // A type that adopts DatabaseValueConvertible but does not adopt StatementColumnConvertible private struct Fetched: DatabaseValueConvertible, Hashable { let int: Int init(int: Int) { self.int = int } var databaseValue: DatabaseValue { int.databaseValue } static func fromDatabaseValue(_ dbValue: DatabaseValue) -> Fetched? { guard let int = Int.fromDatabaseValue(dbValue) else { return nil } return Fetched(int: int) } } class DatabaseValueConvertibleFetchTests: GRDBTestCase { // MARK: - DatabaseValueConvertible.fetch func testFetchCursor() throws { let dbQueue = try makeDatabaseQueue() try dbQueue.inDatabase { db in func test(_ cursor: DatabaseValueCursor) throws { XCTAssertEqual(try cursor.next()!.int, 1) XCTAssertEqual(try cursor.next()!.int, 2) XCTAssertTrue(try cursor.next() == nil) // end XCTAssertTrue(try cursor.next() == nil) // past the end } do { let sql = "SELECT 1 UNION ALL SELECT 2" let statement = try db.makeStatement(sql: sql) try test(Fetched.fetchCursor(db, sql: sql)) try test(Fetched.fetchCursor(statement)) try test(Fetched.fetchCursor(db, SQLRequest(sql: sql))) try test(SQLRequest(sql: sql).fetchCursor(db)) } do { let sql = "SELECT 0, 1 UNION ALL SELECT 0, 2" let statement = try db.makeStatement(sql: sql) let adapter = SuffixRowAdapter(fromIndex: 1) try test(Fetched.fetchCursor(db, sql: sql, adapter: adapter)) try test(Fetched.fetchCursor(statement, adapter: adapter)) try test(Fetched.fetchCursor(db, SQLRequest(sql: sql, adapter: adapter))) try test(SQLRequest(sql: sql, adapter: adapter).fetchCursor(db)) } } } func testFetchCursorWithInterpolation() throws { let dbQueue = try makeDatabaseQueue() try dbQueue.inDatabase { db in let request: SQLRequest = "SELECT \(42)" let cursor = try request.fetchCursor(db) let fetched = try cursor.next()! XCTAssertEqual(fetched.int, 42) } } func testFetchCursorStepFailure() throws { let dbQueue = try makeDatabaseQueue() try dbQueue.inDatabase { db in let customError = NSError(domain: "Custom", code: 0xDEAD) db.add(function: DatabaseFunction("throw", argumentCount: 0, pure: true) { _ in throw customError }) func test(_ cursor: DatabaseValueCursor, sql: String) throws { do { _ = try cursor.next() XCTFail() } catch let error as DatabaseError { XCTAssertEqual(error.resultCode, .SQLITE_ERROR) XCTAssertEqual(error.message, "\(customError)") XCTAssertEqual(error.sql!, sql) XCTAssertEqual(error.description, "SQLite error 1: \(customError) - while executing `\(sql)`") } do { _ = try cursor.next() XCTFail() } catch is DatabaseError { // Various SQLite and SQLCipher versions don't emit the same // error. What we care about is that there is an error. } } do { let sql = "SELECT throw()" try test(Fetched.fetchCursor(db, sql: sql), sql: sql) try test(Fetched.fetchCursor(db.makeStatement(sql: sql)), sql: sql) try test(Fetched.fetchCursor(db, SQLRequest(sql: sql)), sql: sql) try test(SQLRequest(sql: sql).fetchCursor(db), sql: sql) } do { let sql = "SELECT 0, throw()" let adapter = SuffixRowAdapter(fromIndex: 1) try test(Fetched.fetchCursor(db, sql: sql, adapter: adapter), sql: sql) try test(Fetched.fetchCursor(db.makeStatement(sql: sql), adapter: adapter), sql: sql) try test(Fetched.fetchCursor(db, SQLRequest(sql: sql, adapter: adapter)), sql: sql) try test(SQLRequest(sql: sql, adapter: adapter).fetchCursor(db), sql: sql) } } } func testFetchCursorCompilationFailure() throws { let dbQueue = try makeDatabaseQueue() try dbQueue.inDatabase { db in func test(_ cursor: @autoclosure () throws -> DatabaseValueCursor, sql: String) throws { do { _ = try cursor() XCTFail() } catch let error as DatabaseError { XCTAssertEqual(error.resultCode, .SQLITE_ERROR) XCTAssertEqual(error.message, "no such table: nonExistingTable") XCTAssertEqual(error.sql!, sql) XCTAssertEqual(error.description, "SQLite error 1: no such table: nonExistingTable - while executing `\(sql)`") } } do { let sql = "SELECT * FROM nonExistingTable" try test(Fetched.fetchCursor(db, sql: sql), sql: sql) try test(Fetched.fetchCursor(db.makeStatement(sql: sql)), sql: sql) try test(Fetched.fetchCursor(db, SQLRequest(sql: sql)), sql: sql) try test(SQLRequest(sql: sql).fetchCursor(db), sql: sql) } do { let sql = "SELECT * FROM nonExistingTable" let adapter = SuffixRowAdapter(fromIndex: 1) try test(Fetched.fetchCursor(db, sql: sql, adapter: adapter), sql: sql) try test(Fetched.fetchCursor(db.makeStatement(sql: sql), adapter: adapter), sql: sql) try test(Fetched.fetchCursor(db, SQLRequest(sql: sql, adapter: adapter)), sql: sql) try test(SQLRequest(sql: sql, adapter: adapter).fetchCursor(db), sql: sql) } } } func testFetchAll() throws { let dbQueue = try makeDatabaseQueue() try dbQueue.inDatabase { db in func test(_ array: [Fetched]) { XCTAssertEqual(array.map(\.int), [1,2]) } do { let sql = "SELECT 1 UNION ALL SELECT 2" let statement = try db.makeStatement(sql: sql) try test(Fetched.fetchAll(db, sql: sql)) try test(Fetched.fetchAll(statement)) try test(Fetched.fetchAll(db, SQLRequest(sql: sql))) try test(SQLRequest(sql: sql).fetchAll(db)) } do { let sql = "SELECT 0, 1 UNION ALL SELECT 0, 2" let statement = try db.makeStatement(sql: sql) let adapter = SuffixRowAdapter(fromIndex: 1) try test(Fetched.fetchAll(db, sql: sql, adapter: adapter)) try test(Fetched.fetchAll(statement, adapter: adapter)) try test(Fetched.fetchAll(db, SQLRequest(sql: sql, adapter: adapter))) try test(SQLRequest(sql: sql, adapter: adapter).fetchAll(db)) } } } func testFetchAllWithInterpolation() throws { let dbQueue = try makeDatabaseQueue() try dbQueue.inDatabase { db in let request: SQLRequest = "SELECT \(42)" let array = try request.fetchAll(db) XCTAssertEqual(array[0].int, 42) } } func testFetchAllStepFailure() throws { let dbQueue = try makeDatabaseQueue() try dbQueue.inDatabase { db in let customError = NSError(domain: "Custom", code: 0xDEAD) db.add(function: DatabaseFunction("throw", argumentCount: 0, pure: true) { _ in throw customError }) func test(_ array: @autoclosure () throws -> [Fetched], sql: String) throws { do { _ = try array() XCTFail() } catch let error as DatabaseError { XCTAssertEqual(error.resultCode, .SQLITE_ERROR) XCTAssertEqual(error.message, "\(customError)") XCTAssertEqual(error.sql!, sql) XCTAssertEqual(error.description, "SQLite error 1: \(customError) - while executing `\(sql)`") } } do { let sql = "SELECT throw()" try test(Fetched.fetchAll(db, sql: sql), sql: sql) try test(Fetched.fetchAll(db.makeStatement(sql: sql)), sql: sql) try test(Fetched.fetchAll(db, SQLRequest(sql: sql)), sql: sql) try test(SQLRequest(sql: sql).fetchAll(db), sql: sql) } do { let sql = "SELECT 0, throw()" let adapter = SuffixRowAdapter(fromIndex: 1) try test(Fetched.fetchAll(db, sql: sql, adapter: adapter), sql: sql) try test(Fetched.fetchAll(db.makeStatement(sql: sql), adapter: adapter), sql: sql) try test(Fetched.fetchAll(db, SQLRequest(sql: sql, adapter: adapter)), sql: sql) try test(SQLRequest(sql: sql, adapter: adapter).fetchAll(db), sql: sql) } } } func testFetchAllCompilationFailure() throws { let dbQueue = try makeDatabaseQueue() try dbQueue.inDatabase { db in func test(_ array: @autoclosure () throws -> [Fetched], sql: String) throws { do { _ = try array() XCTFail() } catch let error as DatabaseError { XCTAssertEqual(error.resultCode, .SQLITE_ERROR) XCTAssertEqual(error.message, "no such table: nonExistingTable") XCTAssertEqual(error.sql!, sql) XCTAssertEqual(error.description, "SQLite error 1: no such table: nonExistingTable - while executing `\(sql)`") } } do { let sql = "SELECT * FROM nonExistingTable" try test(Fetched.fetchAll(db, sql: sql), sql: sql) try test(Fetched.fetchAll(db.makeStatement(sql: sql)), sql: sql) try test(Fetched.fetchAll(db, SQLRequest(sql: sql)), sql: sql) try test(SQLRequest(sql: sql).fetchAll(db), sql: sql) } do { let sql = "SELECT * FROM nonExistingTable" let adapter = SuffixRowAdapter(fromIndex: 1) try test(Fetched.fetchAll(db, sql: sql, adapter: adapter), sql: sql) try test(Fetched.fetchAll(db.makeStatement(sql: sql), adapter: adapter), sql: sql) try test(Fetched.fetchAll(db, SQLRequest(sql: sql, adapter: adapter)), sql: sql) try test(SQLRequest(sql: sql, adapter: adapter).fetchAll(db), sql: sql) } } } func testFetchSet() throws { let dbQueue = try makeDatabaseQueue() try dbQueue.inDatabase { db in func test(_ set: Set) { XCTAssertEqual(Set(set.map(\.int)), [1,2]) } do { let sql = "SELECT 1 UNION ALL SELECT 2" let statement = try db.makeStatement(sql: sql) try test(Fetched.fetchSet(db, sql: sql)) try test(Fetched.fetchSet(statement)) try test(Fetched.fetchSet(db, SQLRequest(sql: sql))) try test(SQLRequest(sql: sql).fetchSet(db)) } do { let sql = "SELECT 0, 1 UNION ALL SELECT 0, 2" let statement = try db.makeStatement(sql: sql) let adapter = SuffixRowAdapter(fromIndex: 1) try test(Fetched.fetchSet(db, sql: sql, adapter: adapter)) try test(Fetched.fetchSet(statement, adapter: adapter)) try test(Fetched.fetchSet(db, SQLRequest(sql: sql, adapter: adapter))) try test(SQLRequest(sql: sql, adapter: adapter).fetchSet(db)) } } } func testFetchSetWithInterpolation() throws { let dbQueue = try makeDatabaseQueue() try dbQueue.inDatabase { db in let request: SQLRequest = "SELECT \(42)" let set = try request.fetchSet(db) XCTAssertEqual(set.first!.int, 42) } } func testFetchSetStepFailure() throws { let dbQueue = try makeDatabaseQueue() try dbQueue.inDatabase { db in let customError = NSError(domain: "Custom", code: 0xDEAD) db.add(function: DatabaseFunction("throw", argumentCount: 0, pure: true) { _ in throw customError }) func test(_ set: @autoclosure () throws -> Set, sql: String) throws { do { _ = try set() XCTFail() } catch let error as DatabaseError { XCTAssertEqual(error.resultCode, .SQLITE_ERROR) XCTAssertEqual(error.message, "\(customError)") XCTAssertEqual(error.sql!, sql) XCTAssertEqual(error.description, "SQLite error 1: \(customError) - while executing `\(sql)`") } } do { let sql = "SELECT throw()" try test(Fetched.fetchSet(db, sql: sql), sql: sql) try test(Fetched.fetchSet(db.makeStatement(sql: sql)), sql: sql) try test(Fetched.fetchSet(db, SQLRequest(sql: sql)), sql: sql) try test(SQLRequest(sql: sql).fetchSet(db), sql: sql) } do { let sql = "SELECT 0, throw()" let adapter = SuffixRowAdapter(fromIndex: 1) try test(Fetched.fetchSet(db, sql: sql, adapter: adapter), sql: sql) try test(Fetched.fetchSet(db.makeStatement(sql: sql), adapter: adapter), sql: sql) try test(Fetched.fetchSet(db, SQLRequest(sql: sql, adapter: adapter)), sql: sql) try test(SQLRequest(sql: sql, adapter: adapter).fetchSet(db), sql: sql) } } } func testFetchSetCompilationFailure() throws { let dbQueue = try makeDatabaseQueue() try dbQueue.inDatabase { db in func test(_ set: @autoclosure () throws -> Set, sql: String) throws { do { _ = try set() XCTFail() } catch let error as DatabaseError { XCTAssertEqual(error.resultCode, .SQLITE_ERROR) XCTAssertEqual(error.message, "no such table: nonExistingTable") XCTAssertEqual(error.sql!, sql) XCTAssertEqual(error.description, "SQLite error 1: no such table: nonExistingTable - while executing `\(sql)`") } } do { let sql = "SELECT * FROM nonExistingTable" try test(Fetched.fetchSet(db, sql: sql), sql: sql) try test(Fetched.fetchSet(db.makeStatement(sql: sql)), sql: sql) try test(Fetched.fetchSet(db, SQLRequest(sql: sql)), sql: sql) try test(SQLRequest(sql: sql).fetchSet(db), sql: sql) } do { let sql = "SELECT * FROM nonExistingTable" let adapter = SuffixRowAdapter(fromIndex: 1) try test(Fetched.fetchSet(db, sql: sql, adapter: adapter), sql: sql) try test(Fetched.fetchSet(db.makeStatement(sql: sql), adapter: adapter), sql: sql) try test(Fetched.fetchSet(db, SQLRequest(sql: sql, adapter: adapter)), sql: sql) try test(SQLRequest(sql: sql, adapter: adapter).fetchSet(db), sql: sql) } } } func testFetchOne() throws { let dbQueue = try makeDatabaseQueue() try dbQueue.inDatabase { db in do { func test(_ nilBecauseMissingRow: Fetched?) { XCTAssertTrue(nilBecauseMissingRow == nil) } do { let sql = "SELECT 1 WHERE 0" let statement = try db.makeStatement(sql: sql) try test(Fetched.fetchOne(db, sql: sql)) try test(Fetched.fetchOne(statement)) try test(Fetched.fetchOne(db, SQLRequest(sql: sql))) try test(SQLRequest(sql: sql).fetchOne(db)) } do { let sql = "SELECT 0, 1 WHERE 0" let statement = try db.makeStatement(sql: sql) let adapter = SuffixRowAdapter(fromIndex: 1) try test(Fetched.fetchOne(db, sql: sql, adapter: adapter)) try test(Fetched.fetchOne(statement, adapter: adapter)) try test(Fetched.fetchOne(db, SQLRequest(sql: sql, adapter: adapter))) try test(SQLRequest(sql: sql, adapter: adapter).fetchOne(db)) } } do { func test(_ nilBecauseNull: Fetched?) { XCTAssertTrue(nilBecauseNull == nil) } do { let sql = "SELECT NULL" let statement = try db.makeStatement(sql: sql) try test(Fetched.fetchOne(db, sql: sql)) try test(Fetched.fetchOne(statement)) try test(Fetched.fetchOne(db, SQLRequest(sql: sql))) try test(SQLRequest(sql: sql).fetchOne(db)) } do { let sql = "SELECT 0, NULL" let statement = try db.makeStatement(sql: sql) let adapter = SuffixRowAdapter(fromIndex: 1) try test(Fetched.fetchOne(db, sql: sql, adapter: adapter)) try test(Fetched.fetchOne(statement, adapter: adapter)) try test(Fetched.fetchOne(db, SQLRequest(sql: sql, adapter: adapter))) try test(SQLRequest(sql: sql, adapter: adapter).fetchOne(db)) } } do { func test(_ value: Fetched?) { XCTAssertEqual(value!.int, 1) } do { let sql = "SELECT 1" let statement = try db.makeStatement(sql: sql) try test(Fetched.fetchOne(db, sql: sql)) try test(Fetched.fetchOne(statement)) try test(Fetched.fetchOne(db, SQLRequest(sql: sql))) try test(SQLRequest(sql: sql).fetchOne(db)) } do { let sql = "SELECT 0, 1" let statement = try db.makeStatement(sql: sql) let adapter = SuffixRowAdapter(fromIndex: 1) try test(Fetched.fetchOne(db, sql: sql, adapter: adapter)) try test(Fetched.fetchOne(statement, adapter: adapter)) try test(Fetched.fetchOne(db, SQLRequest(sql: sql, adapter: adapter))) try test(SQLRequest(sql: sql, adapter: adapter).fetchOne(db)) } } } } func testFetchOneWithInterpolation() throws { let dbQueue = try makeDatabaseQueue() try dbQueue.inDatabase { db in let request: SQLRequest = "SELECT \(42)" let fetched = try request.fetchOne(db) XCTAssertEqual(fetched!.int, 42) } } func testFetchOneStepFailure() throws { let dbQueue = try makeDatabaseQueue() try dbQueue.inDatabase { db in let customError = NSError(domain: "Custom", code: 0xDEAD) db.add(function: DatabaseFunction("throw", argumentCount: 0, pure: true) { _ in throw customError }) func test(_ value: @autoclosure () throws -> Fetched?, sql: String) throws { do { _ = try value() XCTFail() } catch let error as DatabaseError { XCTAssertEqual(error.resultCode, .SQLITE_ERROR) XCTAssertEqual(error.message, "\(customError)") XCTAssertEqual(error.sql!, sql) XCTAssertEqual(error.description, "SQLite error 1: \(customError) - while executing `\(sql)`") } } do { let sql = "SELECT throw()" try test(Fetched.fetchOne(db, sql: sql), sql: sql) try test(Fetched.fetchOne(db.makeStatement(sql: sql)), sql: sql) try test(Fetched.fetchOne(db, SQLRequest(sql: sql)), sql: sql) try test(SQLRequest(sql: sql).fetchOne(db), sql: sql) } do { let sql = "SELECT 0, throw()" let adapter = SuffixRowAdapter(fromIndex: 1) try test(Fetched.fetchOne(db, sql: sql, adapter: adapter), sql: sql) try test(Fetched.fetchOne(db.makeStatement(sql: sql), adapter: adapter), sql: sql) try test(Fetched.fetchOne(db, SQLRequest(sql: sql, adapter: adapter)), sql: sql) try test(SQLRequest(sql: sql, adapter: adapter).fetchOne(db), sql: sql) } } } func testFetchOneCompilationFailure() throws { let dbQueue = try makeDatabaseQueue() try dbQueue.inDatabase { db in func test(_ value: @autoclosure () throws -> Fetched?, sql: String) throws { do { _ = try value() XCTFail() } catch let error as DatabaseError { XCTAssertEqual(error.resultCode, .SQLITE_ERROR) XCTAssertEqual(error.message, "no such table: nonExistingTable") XCTAssertEqual(error.sql!, sql) XCTAssertEqual(error.description, "SQLite error 1: no such table: nonExistingTable - while executing `\(sql)`") } } do { let sql = "SELECT * FROM nonExistingTable" try test(Fetched.fetchOne(db, sql: sql), sql: sql) try test(Fetched.fetchOne(db.makeStatement(sql: sql)), sql: sql) try test(Fetched.fetchOne(db, SQLRequest(sql: sql)), sql: sql) try test(SQLRequest(sql: sql).fetchOne(db), sql: sql) } do { let sql = "SELECT * FROM nonExistingTable" let adapter = SuffixRowAdapter(fromIndex: 1) try test(Fetched.fetchOne(db, sql: sql, adapter: adapter), sql: sql) try test(Fetched.fetchOne(db.makeStatement(sql: sql), adapter: adapter), sql: sql) try test(Fetched.fetchOne(db, SQLRequest(sql: sql, adapter: adapter)), sql: sql) try test(SQLRequest(sql: sql, adapter: adapter).fetchOne(db), sql: sql) } } } // MARK: - Optional.fetch func testOptionalFetchCursor() throws { let dbQueue = try makeDatabaseQueue() try dbQueue.inDatabase { db in func test(_ cursor: DatabaseValueCursor) throws { XCTAssertEqual(try cursor.next()!!.int, 1) XCTAssertTrue(try cursor.next()! == nil) XCTAssertTrue(try cursor.next() == nil) // end XCTAssertTrue(try cursor.next() == nil) // past the end } do { let sql = "SELECT 1 UNION ALL SELECT NULL" let statement = try db.makeStatement(sql: sql) try test(Optional.fetchCursor(db, sql: sql)) try test(Optional.fetchCursor(statement)) try test(Optional.fetchCursor(db, SQLRequest(sql: sql))) try test(SQLRequest(sql: sql).fetchCursor(db)) } do { let sql = "SELECT 0, 1 UNION ALL SELECT 0, NULL" let statement = try db.makeStatement(sql: sql) let adapter = SuffixRowAdapter(fromIndex: 1) try test(Optional.fetchCursor(db, sql: sql, adapter: adapter)) try test(Optional.fetchCursor(statement, adapter: adapter)) try test(Optional.fetchCursor(db, SQLRequest(sql: sql, adapter: adapter))) try test(SQLRequest(sql: sql, adapter: adapter).fetchCursor(db)) } } } func testOptionalFetchCursorWithInterpolation() throws { let dbQueue = try makeDatabaseQueue() try dbQueue.inDatabase { db in let request: SQLRequest = "SELECT \(42)" let cursor = try request.fetchCursor(db) let fetched = try cursor.next()! XCTAssertEqual(fetched!.int, 42) } } func testOptionalFetchCursorStepFailure() throws { let dbQueue = try makeDatabaseQueue() try dbQueue.inDatabase { db in let customError = NSError(domain: "Custom", code: 0xDEAD) db.add(function: DatabaseFunction("throw", argumentCount: 0, pure: true) { _ in throw customError }) func test(_ cursor: DatabaseValueCursor, sql: String) throws { do { _ = try cursor.next() XCTFail() } catch let error as DatabaseError { XCTAssertEqual(error.resultCode, .SQLITE_ERROR) XCTAssertEqual(error.message, "\(customError)") XCTAssertEqual(error.sql!, sql) XCTAssertEqual(error.description, "SQLite error 1: \(customError) - while executing `\(sql)`") } do { _ = try cursor.next() XCTFail() } catch is DatabaseError { // Various SQLite and SQLCipher versions don't emit the same // error. What we care about is that there is an error. } } do { let sql = "SELECT throw()" try test(Optional.fetchCursor(db, sql: sql), sql: sql) try test(Optional.fetchCursor(db.makeStatement(sql: sql)), sql: sql) try test(Optional.fetchCursor(db, SQLRequest(sql: sql)), sql: sql) try test(SQLRequest(sql: sql).fetchCursor(db), sql: sql) } do { let sql = "SELECT 0, throw()" let adapter = SuffixRowAdapter(fromIndex: 1) try test(Optional.fetchCursor(db, sql: sql, adapter: adapter), sql: sql) try test(Optional.fetchCursor(db.makeStatement(sql: sql), adapter: adapter), sql: sql) try test(Optional.fetchCursor(db, SQLRequest(sql: sql, adapter: adapter)), sql: sql) try test(SQLRequest(sql: sql, adapter: adapter).fetchCursor(db), sql: sql) } } } func testOptionalFetchCursorCompilationFailure() throws { let dbQueue = try makeDatabaseQueue() try dbQueue.inDatabase { db in func test(_ cursor: @autoclosure () throws -> DatabaseValueCursor, sql: String) throws { do { _ = try cursor() XCTFail() } catch let error as DatabaseError { XCTAssertEqual(error.resultCode, .SQLITE_ERROR) XCTAssertEqual(error.message, "no such table: nonExistingTable") XCTAssertEqual(error.sql!, sql) XCTAssertEqual(error.description, "SQLite error 1: no such table: nonExistingTable - while executing `\(sql)`") } } do { let sql = "SELECT * FROM nonExistingTable" try test(Optional.fetchCursor(db, sql: sql), sql: sql) try test(Optional.fetchCursor(db.makeStatement(sql: sql)), sql: sql) try test(Optional.fetchCursor(db, SQLRequest(sql: sql)), sql: sql) try test(SQLRequest(sql: sql).fetchCursor(db), sql: sql) } do { let sql = "SELECT * FROM nonExistingTable" let adapter = SuffixRowAdapter(fromIndex: 1) try test(Optional.fetchCursor(db, sql: sql, adapter: adapter), sql: sql) try test(Optional.fetchCursor(db.makeStatement(sql: sql), adapter: adapter), sql: sql) try test(Optional.fetchCursor(db, SQLRequest(sql: sql, adapter: adapter)), sql: sql) try test(SQLRequest(sql: sql, adapter: adapter).fetchCursor(db), sql: sql) } } } func testOptionalFetchAll() throws { let dbQueue = try makeDatabaseQueue() try dbQueue.inDatabase { db in func test(_ array: [Fetched?]) { XCTAssertEqual(array.count, 2) XCTAssertEqual(array[0]!.int, 1) XCTAssertTrue(array[1] == nil) } do { let sql = "SELECT 1 UNION ALL SELECT NULL" let statement = try db.makeStatement(sql: sql) try test(Optional.fetchAll(db, sql: sql)) try test(Optional.fetchAll(statement)) try test(Optional.fetchAll(db, SQLRequest(sql: sql))) try test(SQLRequest(sql: sql).fetchAll(db)) } do { let sql = "SELECT 0, 1 UNION ALL SELECT 0, NULL" let statement = try db.makeStatement(sql: sql) let adapter = SuffixRowAdapter(fromIndex: 1) try test(Optional.fetchAll(db, sql: sql, adapter: adapter)) try test(Optional.fetchAll(statement, adapter: adapter)) try test(Optional.fetchAll(db, SQLRequest(sql: sql, adapter: adapter))) try test(SQLRequest(sql: sql, adapter: adapter).fetchAll(db)) } } } func testOptionalFetchAllWithInterpolation() throws { let dbQueue = try makeDatabaseQueue() try dbQueue.inDatabase { db in let request: SQLRequest = "SELECT \(42)" let array = try request.fetchAll(db) XCTAssertEqual(array[0]!.int, 42) } } func testOptionalFetchAllStepFailure() throws { let dbQueue = try makeDatabaseQueue() try dbQueue.inDatabase { db in let customError = NSError(domain: "Custom", code: 0xDEAD) db.add(function: DatabaseFunction("throw", argumentCount: 0, pure: true) { _ in throw customError }) func test(_ array: @autoclosure () throws -> [Fetched?], sql: String) throws { do { _ = try array() XCTFail() } catch let error as DatabaseError { XCTAssertEqual(error.resultCode, .SQLITE_ERROR) XCTAssertEqual(error.message, "\(customError)") XCTAssertEqual(error.sql!, sql) XCTAssertEqual(error.description, "SQLite error 1: \(customError) - while executing `\(sql)`") } } do { let sql = "SELECT throw()" try test(Optional.fetchAll(db, sql: sql), sql: sql) try test(Optional.fetchAll(db.makeStatement(sql: sql)), sql: sql) try test(Optional.fetchAll(db, SQLRequest(sql: sql)), sql: sql) try test(SQLRequest(sql: sql).fetchAll(db), sql: sql) } do { let sql = "SELECT 0, throw()" let adapter = SuffixRowAdapter(fromIndex: 1) try test(Optional.fetchAll(db, sql: sql, adapter: adapter), sql: sql) try test(Optional.fetchAll(db.makeStatement(sql: sql), adapter: adapter), sql: sql) try test(Optional.fetchAll(db, SQLRequest(sql: sql, adapter: adapter)), sql: sql) try test(SQLRequest(sql: sql, adapter: adapter).fetchAll(db), sql: sql) } } } func testOptionalFetchAllCompilationFailure() throws { let dbQueue = try makeDatabaseQueue() try dbQueue.inDatabase { db in func test(_ array: @autoclosure () throws -> [Fetched?], sql: String) throws { do { _ = try array() XCTFail() } catch let error as DatabaseError { XCTAssertEqual(error.resultCode, .SQLITE_ERROR) XCTAssertEqual(error.message, "no such table: nonExistingTable") XCTAssertEqual(error.sql!, sql) XCTAssertEqual(error.description, "SQLite error 1: no such table: nonExistingTable - while executing `\(sql)`") } } do { let sql = "SELECT * FROM nonExistingTable" try test(Optional.fetchAll(db, sql: sql), sql: sql) try test(Optional.fetchAll(db.makeStatement(sql: sql)), sql: sql) try test(Optional.fetchAll(db, SQLRequest(sql: sql)), sql: sql) try test(SQLRequest(sql: sql).fetchAll(db), sql: sql) } do { let sql = "SELECT * FROM nonExistingTable" let adapter = SuffixRowAdapter(fromIndex: 1) try test(Optional.fetchAll(db, sql: sql, adapter: adapter), sql: sql) try test(Optional.fetchAll(db.makeStatement(sql: sql), adapter: adapter), sql: sql) try test(Optional.fetchAll(db, SQLRequest(sql: sql, adapter: adapter)), sql: sql) try test(SQLRequest(sql: sql, adapter: adapter).fetchAll(db), sql: sql) } } } func testOptionalFetchSet() throws { let dbQueue = try makeDatabaseQueue() try dbQueue.inDatabase { db in func test(_ set: Set) { XCTAssertEqual(Set(set.map { $0?.int }), [1, nil]) } do { let sql = "SELECT 1 UNION ALL SELECT NULL" let statement = try db.makeStatement(sql: sql) try test(Optional.fetchSet(db, sql: sql)) try test(Optional.fetchSet(statement)) try test(Optional.fetchSet(db, SQLRequest(sql: sql))) try test(SQLRequest(sql: sql).fetchSet(db)) } do { let sql = "SELECT 0, 1 UNION ALL SELECT 0, NULL" let statement = try db.makeStatement(sql: sql) let adapter = SuffixRowAdapter(fromIndex: 1) try test(Optional.fetchSet(db, sql: sql, adapter: adapter)) try test(Optional.fetchSet(statement, adapter: adapter)) try test(Optional.fetchSet(db, SQLRequest(sql: sql, adapter: adapter))) try test(SQLRequest(sql: sql, adapter: adapter).fetchSet(db)) } } } func testOptionalFetchSetWithInterpolation() throws { let dbQueue = try makeDatabaseQueue() try dbQueue.inDatabase { db in let request: SQLRequest = "SELECT \(42)" let set = try request.fetchSet(db) XCTAssertEqual(set.first!!.int, 42) } } func testOptionalFetchSetStepFailure() throws { let dbQueue = try makeDatabaseQueue() try dbQueue.inDatabase { db in let customError = NSError(domain: "Custom", code: 0xDEAD) db.add(function: DatabaseFunction("throw", argumentCount: 0, pure: true) { _ in throw customError }) func test(_ set: @autoclosure () throws -> Set, sql: String) throws { do { _ = try set() XCTFail() } catch let error as DatabaseError { XCTAssertEqual(error.resultCode, .SQLITE_ERROR) XCTAssertEqual(error.message, "\(customError)") XCTAssertEqual(error.sql!, sql) XCTAssertEqual(error.description, "SQLite error 1: \(customError) - while executing `\(sql)`") } } do { let sql = "SELECT throw()" try test(Optional.fetchSet(db, sql: sql), sql: sql) try test(Optional.fetchSet(db.makeStatement(sql: sql)), sql: sql) try test(Optional.fetchSet(db, SQLRequest(sql: sql)), sql: sql) try test(SQLRequest(sql: sql).fetchSet(db), sql: sql) } do { let sql = "SELECT 0, throw()" let adapter = SuffixRowAdapter(fromIndex: 1) try test(Optional.fetchSet(db, sql: sql, adapter: adapter), sql: sql) try test(Optional.fetchSet(db.makeStatement(sql: sql), adapter: adapter), sql: sql) try test(Optional.fetchSet(db, SQLRequest(sql: sql, adapter: adapter)), sql: sql) try test(SQLRequest(sql: sql, adapter: adapter).fetchSet(db), sql: sql) } } } func testOptionalFetchSetCompilationFailure() throws { let dbQueue = try makeDatabaseQueue() try dbQueue.inDatabase { db in func test(_ set: @autoclosure () throws -> Set, sql: String) throws { do { _ = try set() XCTFail() } catch let error as DatabaseError { XCTAssertEqual(error.resultCode, .SQLITE_ERROR) XCTAssertEqual(error.message, "no such table: nonExistingTable") XCTAssertEqual(error.sql!, sql) XCTAssertEqual(error.description, "SQLite error 1: no such table: nonExistingTable - while executing `\(sql)`") } } do { let sql = "SELECT * FROM nonExistingTable" try test(Optional.fetchSet(db, sql: sql), sql: sql) try test(Optional.fetchSet(db.makeStatement(sql: sql)), sql: sql) try test(Optional.fetchSet(db, SQLRequest(sql: sql)), sql: sql) try test(SQLRequest(sql: sql).fetchSet(db), sql: sql) } do { let sql = "SELECT * FROM nonExistingTable" let adapter = SuffixRowAdapter(fromIndex: 1) try test(Optional.fetchSet(db, sql: sql, adapter: adapter), sql: sql) try test(Optional.fetchSet(db.makeStatement(sql: sql), adapter: adapter), sql: sql) try test(Optional.fetchSet(db, SQLRequest(sql: sql, adapter: adapter)), sql: sql) try test(SQLRequest(sql: sql, adapter: adapter).fetchSet(db), sql: sql) } } } func testOptionalFetchOne() throws { let dbQueue = try makeDatabaseQueue() try dbQueue.inDatabase { db in do { func test(_ nilBecauseMissingRow: Fetched??) { switch nilBecauseMissingRow { case .none: break default: XCTFail("Expected nil because missing row") } } do { let sql = "SELECT 1 WHERE 0" let statement = try db.makeStatement(sql: sql) try test(Optional.fetchOne(db, sql: sql)) try test(Optional.fetchOne(statement)) try test(Optional.fetchOne(db, SQLRequest(sql: sql))) try test(SQLRequest(sql: sql).fetchOne(db)) } do { let sql = "SELECT 0, 1 WHERE 0" let statement = try db.makeStatement(sql: sql) let adapter = SuffixRowAdapter(fromIndex: 1) try test(Optional.fetchOne(db, sql: sql, adapter: adapter)) try test(Optional.fetchOne(statement, adapter: adapter)) try test(Optional.fetchOne(db, SQLRequest(sql: sql, adapter: adapter))) try test(SQLRequest(sql: sql, adapter: adapter).fetchOne(db)) } } do { func test(_ nilBecauseNull: Fetched??) { switch nilBecauseNull { case .some(.none): break default: XCTFail("Expected .some(nil) because NULL") } } do { let sql = "SELECT NULL" let statement = try db.makeStatement(sql: sql) try test(Optional.fetchOne(db, sql: sql)) try test(Optional.fetchOne(statement)) try test(Optional.fetchOne(db, SQLRequest(sql: sql))) try test(SQLRequest(sql: sql).fetchOne(db)) } do { let sql = "SELECT 0, NULL" let statement = try db.makeStatement(sql: sql) let adapter = SuffixRowAdapter(fromIndex: 1) try test(Optional.fetchOne(db, sql: sql, adapter: adapter)) try test(Optional.fetchOne(statement, adapter: adapter)) try test(Optional.fetchOne(db, SQLRequest(sql: sql, adapter: adapter))) try test(SQLRequest(sql: sql, adapter: adapter).fetchOne(db)) } } do { func test(_ value: Fetched??) { XCTAssertEqual(value!!.int, 1) } do { let sql = "SELECT 1" let statement = try db.makeStatement(sql: sql) try test(Optional.fetchOne(db, sql: sql)) try test(Optional.fetchOne(statement)) try test(Optional.fetchOne(db, SQLRequest(sql: sql))) try test(SQLRequest(sql: sql).fetchOne(db)) } do { let sql = "SELECT 0, 1" let statement = try db.makeStatement(sql: sql) let adapter = SuffixRowAdapter(fromIndex: 1) try test(Optional.fetchOne(db, sql: sql, adapter: adapter)) try test(Optional.fetchOne(statement, adapter: adapter)) try test(Optional.fetchOne(db, SQLRequest(sql: sql, adapter: adapter))) try test(SQLRequest(sql: sql, adapter: adapter).fetchOne(db)) } } } } func testOptionalFetchOneWithInterpolation() throws { let dbQueue = try makeDatabaseQueue() try dbQueue.inDatabase { db in let request: SQLRequest = "SELECT \(42)" let fetched = try request.fetchOne(db) XCTAssertEqual(fetched!!.int, 42) } } func testOptionalFetchOneStepFailure() throws { let dbQueue = try makeDatabaseQueue() try dbQueue.inDatabase { db in let customError = NSError(domain: "Custom", code: 0xDEAD) db.add(function: DatabaseFunction("throw", argumentCount: 0, pure: true) { _ in throw customError }) func test(_ value: @autoclosure () throws -> Fetched??, sql: String) throws { do { _ = try value() XCTFail() } catch let error as DatabaseError { XCTAssertEqual(error.resultCode, .SQLITE_ERROR) XCTAssertEqual(error.message, "\(customError)") XCTAssertEqual(error.sql!, sql) XCTAssertEqual(error.description, "SQLite error 1: \(customError) - while executing `\(sql)`") } } do { let sql = "SELECT throw()" try test(Optional.fetchOne(db, sql: sql), sql: sql) try test(Optional.fetchOne(db.makeStatement(sql: sql)), sql: sql) try test(Optional.fetchOne(db, SQLRequest(sql: sql)), sql: sql) try test(SQLRequest(sql: sql).fetchOne(db), sql: sql) } do { let sql = "SELECT 0, throw()" let adapter = SuffixRowAdapter(fromIndex: 1) try test(Optional.fetchOne(db, sql: sql, adapter: adapter), sql: sql) try test(Optional.fetchOne(db.makeStatement(sql: sql), adapter: adapter), sql: sql) try test(Optional.fetchOne(db, SQLRequest(sql: sql, adapter: adapter)), sql: sql) try test(SQLRequest(sql: sql, adapter: adapter).fetchOne(db), sql: sql) } } } func testOptionalFetchOneCompilationFailure() throws { let dbQueue = try makeDatabaseQueue() try dbQueue.inDatabase { db in func test(_ value: @autoclosure () throws -> Fetched??, sql: String) throws { do { _ = try value() XCTFail() } catch let error as DatabaseError { XCTAssertEqual(error.resultCode, .SQLITE_ERROR) XCTAssertEqual(error.message, "no such table: nonExistingTable") XCTAssertEqual(error.sql!, sql) XCTAssertEqual(error.description, "SQLite error 1: no such table: nonExistingTable - while executing `\(sql)`") } } do { let sql = "SELECT * FROM nonExistingTable" try test(Optional.fetchOne(db, sql: sql), sql: sql) try test(Optional.fetchOne(db.makeStatement(sql: sql)), sql: sql) try test(Optional.fetchOne(db, SQLRequest(sql: sql)), sql: sql) try test(SQLRequest(sql: sql).fetchOne(db), sql: sql) } do { let sql = "SELECT * FROM nonExistingTable" let adapter = SuffixRowAdapter(fromIndex: 1) try test(Optional.fetchOne(db, sql: sql, adapter: adapter), sql: sql) try test(Optional.fetchOne(db.makeStatement(sql: sql), adapter: adapter), sql: sql) try test(Optional.fetchOne(db, SQLRequest(sql: sql, adapter: adapter)), sql: sql) try test(SQLRequest(sql: sql, adapter: adapter).fetchOne(db), sql: sql) } } } }