add swiftUI code
This commit is contained in:
@@ -0,0 +1,619 @@
|
||||
import XCTest
|
||||
import GRDB
|
||||
|
||||
private struct CustomValueType : DatabaseValueConvertible {
|
||||
var databaseValue: DatabaseValue { "CustomValueType".databaseValue }
|
||||
static func fromDatabaseValue(_ dbValue: DatabaseValue) -> CustomValueType? {
|
||||
guard let string = String.fromDatabaseValue(dbValue), string == "CustomValueType" else {
|
||||
return nil
|
||||
}
|
||||
return CustomValueType()
|
||||
}
|
||||
}
|
||||
|
||||
class DatabaseAggregateTests: GRDBTestCase {
|
||||
|
||||
// MARK: - Return values
|
||||
|
||||
func testAggregateReturningNull() throws {
|
||||
struct Aggregate : DatabaseAggregate {
|
||||
func step(_ values: [DatabaseValue]) { }
|
||||
func finalize() -> (any DatabaseValueConvertible)? { nil }
|
||||
}
|
||||
let dbQueue = try makeDatabaseQueue()
|
||||
try dbQueue.inDatabase { db in
|
||||
let fn = DatabaseFunction("f", argumentCount: 0, aggregate: Aggregate.self)
|
||||
db.add(function: fn)
|
||||
XCTAssertTrue(try DatabaseValue.fetchOne(db, sql: "SELECT f()")!.isNull)
|
||||
}
|
||||
}
|
||||
|
||||
func testAggregateReturningInt64() throws {
|
||||
struct Aggregate : DatabaseAggregate {
|
||||
func step(_ values: [DatabaseValue]) { }
|
||||
func finalize() -> (any DatabaseValueConvertible)? { Int64(1) }
|
||||
}
|
||||
let dbQueue = try makeDatabaseQueue()
|
||||
try dbQueue.inDatabase { db in
|
||||
let fn = DatabaseFunction("f", argumentCount: 0, aggregate: Aggregate.self)
|
||||
db.add(function: fn)
|
||||
XCTAssertEqual(try Int64.fetchOne(db, sql: "SELECT f()")!, Int64(1))
|
||||
}
|
||||
}
|
||||
|
||||
func testAggregateReturningDouble() throws {
|
||||
let dbQueue = try makeDatabaseQueue()
|
||||
struct Aggregate : DatabaseAggregate {
|
||||
func step(_ values: [DatabaseValue]) { }
|
||||
func finalize() -> (any DatabaseValueConvertible)? { 1e100 }
|
||||
}
|
||||
try dbQueue.inDatabase { db in
|
||||
let fn = DatabaseFunction("f", argumentCount: 0, aggregate: Aggregate.self)
|
||||
db.add(function: fn)
|
||||
XCTAssertEqual(try Double.fetchOne(db, sql: "SELECT f()")!, 1e100)
|
||||
}
|
||||
}
|
||||
|
||||
func testAggregateReturningString() throws {
|
||||
struct Aggregate : DatabaseAggregate {
|
||||
func step(_ values: [DatabaseValue]) { }
|
||||
func finalize() -> (any DatabaseValueConvertible)? { "foo" }
|
||||
}
|
||||
let dbQueue = try makeDatabaseQueue()
|
||||
try dbQueue.inDatabase { db in
|
||||
let fn = DatabaseFunction("f", argumentCount: 0, aggregate: Aggregate.self)
|
||||
db.add(function: fn)
|
||||
XCTAssertEqual(try String.fetchOne(db, sql: "SELECT f()")!, "foo")
|
||||
}
|
||||
}
|
||||
|
||||
func testAggregateReturningData() throws {
|
||||
struct Aggregate : DatabaseAggregate {
|
||||
func step(_ values: [DatabaseValue]) { }
|
||||
func finalize() -> (any DatabaseValueConvertible)? {
|
||||
"foo".data(using: .utf8)
|
||||
}
|
||||
}
|
||||
let dbQueue = try makeDatabaseQueue()
|
||||
try dbQueue.inDatabase { db in
|
||||
let fn = DatabaseFunction("f", argumentCount: 0, aggregate: Aggregate.self)
|
||||
db.add(function: fn)
|
||||
XCTAssertEqual(try Data.fetchOne(db, sql: "SELECT f()")!, "foo".data(using: .utf8))
|
||||
}
|
||||
}
|
||||
|
||||
func testAggregateReturningCustomValueType() throws {
|
||||
struct Aggregate : DatabaseAggregate {
|
||||
func step(_ values: [DatabaseValue]) { }
|
||||
func finalize() -> (any DatabaseValueConvertible)? { CustomValueType() }
|
||||
}
|
||||
let dbQueue = try makeDatabaseQueue()
|
||||
try dbQueue.inDatabase { db in
|
||||
let fn = DatabaseFunction("f", argumentCount: 0, aggregate: Aggregate.self)
|
||||
db.add(function: fn)
|
||||
XCTAssertTrue(try CustomValueType.fetchOne(db, sql: "SELECT f()") != nil)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Argument values
|
||||
|
||||
func testAggregateArgumentNil() throws {
|
||||
struct Aggregate : DatabaseAggregate {
|
||||
var result: (any DatabaseValueConvertible)?
|
||||
mutating func step(_ dbValues: [DatabaseValue]) {
|
||||
result = dbValues[0].isNull
|
||||
}
|
||||
func finalize() -> (any DatabaseValueConvertible)? { result }
|
||||
}
|
||||
let dbQueue = try makeDatabaseQueue()
|
||||
try dbQueue.inDatabase { db in
|
||||
let fn = DatabaseFunction("f", argumentCount: 1, aggregate: Aggregate.self)
|
||||
db.add(function: fn)
|
||||
XCTAssertTrue(try Bool.fetchOne(db, sql: "SELECT f(NULL)")!)
|
||||
XCTAssertFalse(try Bool.fetchOne(db, sql: "SELECT f(1)")!)
|
||||
XCTAssertFalse(try Bool.fetchOne(db, sql: "SELECT f(1.1)")!)
|
||||
XCTAssertFalse(try Bool.fetchOne(db, sql: "SELECT f('foo')")!)
|
||||
XCTAssertFalse(try Bool.fetchOne(db, sql: "SELECT f(?)", arguments: ["foo".data(using: .utf8)])!)
|
||||
XCTAssertFalse(try Bool.fetchOne(db, sql: "SELECT f(?)", arguments: [Data()])!)
|
||||
}
|
||||
}
|
||||
|
||||
func testAggregateArgumentInt64() throws {
|
||||
struct Aggregate : DatabaseAggregate {
|
||||
var result: (any DatabaseValueConvertible)?
|
||||
mutating func step(_ dbValues: [DatabaseValue]) {
|
||||
result = Int64.fromDatabaseValue(dbValues[0])
|
||||
}
|
||||
func finalize() -> (any DatabaseValueConvertible)? { result }
|
||||
}
|
||||
let dbQueue = try makeDatabaseQueue()
|
||||
try dbQueue.inDatabase { db in
|
||||
let fn = DatabaseFunction("f", argumentCount: 1, aggregate: Aggregate.self)
|
||||
db.add(function: fn)
|
||||
XCTAssertTrue(try Int64.fetchOne(db, sql: "SELECT f(NULL)") == nil)
|
||||
XCTAssertEqual(try Int64.fetchOne(db, sql: "SELECT f(1)")!, 1)
|
||||
XCTAssertEqual(try Int64.fetchOne(db, sql: "SELECT f(1.1)")!, 1)
|
||||
}
|
||||
}
|
||||
|
||||
func testAggregateArgumentDouble() throws {
|
||||
struct Aggregate : DatabaseAggregate {
|
||||
var result: (any DatabaseValueConvertible)?
|
||||
mutating func step(_ dbValues: [DatabaseValue]) {
|
||||
result = Double.fromDatabaseValue(dbValues[0])
|
||||
}
|
||||
func finalize() -> (any DatabaseValueConvertible)? { result }
|
||||
}
|
||||
let dbQueue = try makeDatabaseQueue()
|
||||
try dbQueue.inDatabase { db in
|
||||
let fn = DatabaseFunction("f", argumentCount: 1, aggregate: Aggregate.self)
|
||||
db.add(function: fn)
|
||||
XCTAssertTrue(try Double.fetchOne(db, sql: "SELECT f(NULL)") == nil)
|
||||
XCTAssertEqual(try Double.fetchOne(db, sql: "SELECT f(1)")!, 1.0)
|
||||
XCTAssertEqual(try Double.fetchOne(db, sql: "SELECT f(1.1)")!, 1.1)
|
||||
}
|
||||
}
|
||||
|
||||
func testAggregateArgumentString() throws {
|
||||
struct Aggregate : DatabaseAggregate {
|
||||
var result: (any DatabaseValueConvertible)?
|
||||
mutating func step(_ dbValues: [DatabaseValue]) {
|
||||
result = String.fromDatabaseValue(dbValues[0])
|
||||
}
|
||||
func finalize() -> (any DatabaseValueConvertible)? { result }
|
||||
}
|
||||
let dbQueue = try makeDatabaseQueue()
|
||||
try dbQueue.inDatabase { db in
|
||||
let fn = DatabaseFunction("f", argumentCount: 1, aggregate: Aggregate.self)
|
||||
db.add(function: fn)
|
||||
XCTAssertTrue(try String.fetchOne(db, sql: "SELECT f(NULL)") == nil)
|
||||
XCTAssertEqual(try String.fetchOne(db, sql: "SELECT f('foo')")!, "foo")
|
||||
}
|
||||
}
|
||||
|
||||
func testAggregateArgumentBlob() throws {
|
||||
struct Aggregate : DatabaseAggregate {
|
||||
var result: (any DatabaseValueConvertible)?
|
||||
mutating func step(_ dbValues: [DatabaseValue]) {
|
||||
result = Data.fromDatabaseValue(dbValues[0])
|
||||
}
|
||||
func finalize() -> (any DatabaseValueConvertible)? { result }
|
||||
}
|
||||
let dbQueue = try makeDatabaseQueue()
|
||||
try dbQueue.inDatabase { db in
|
||||
let fn = DatabaseFunction("f", argumentCount: 1, aggregate: Aggregate.self)
|
||||
db.add(function: fn)
|
||||
XCTAssertTrue(try Data.fetchOne(db, sql: "SELECT f(NULL)") == nil)
|
||||
XCTAssertEqual(try Data.fetchOne(db, sql: "SELECT f(?)", arguments: ["foo".data(using: .utf8)])!, "foo".data(using: .utf8))
|
||||
XCTAssertEqual(try Data.fetchOne(db, sql: "SELECT f(?)", arguments: [Data()])!, Data())
|
||||
}
|
||||
}
|
||||
|
||||
func testAggregateArgumentCustomValueType() throws {
|
||||
struct Aggregate : DatabaseAggregate {
|
||||
var result: (any DatabaseValueConvertible)?
|
||||
mutating func step(_ dbValues: [DatabaseValue]) {
|
||||
result = CustomValueType.fromDatabaseValue(dbValues[0])
|
||||
}
|
||||
func finalize() -> (any DatabaseValueConvertible)? { result }
|
||||
}
|
||||
let dbQueue = try makeDatabaseQueue()
|
||||
try dbQueue.inDatabase { db in
|
||||
let fn = DatabaseFunction("f", argumentCount: 1, aggregate: Aggregate.self)
|
||||
db.add(function: fn)
|
||||
XCTAssertTrue(try CustomValueType.fetchOne(db, sql: "SELECT f(NULL)") == nil)
|
||||
XCTAssertTrue(try CustomValueType.fetchOne(db, sql: "SELECT f('CustomValueType')") != nil)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Argument count
|
||||
|
||||
func testAggregateWithoutArgument() throws {
|
||||
struct Aggregate : DatabaseAggregate {
|
||||
func step(_ dbValues: [DatabaseValue]) { }
|
||||
func finalize() -> (any DatabaseValueConvertible)? { "foo" }
|
||||
}
|
||||
let dbQueue = try makeDatabaseQueue()
|
||||
try dbQueue.inDatabase { db in
|
||||
let fn = DatabaseFunction("f", argumentCount: 0, aggregate: Aggregate.self)
|
||||
db.add(function: fn)
|
||||
XCTAssertEqual(try String.fetchOne(db, sql: "SELECT f()")!, "foo")
|
||||
do {
|
||||
try db.execute(sql: "SELECT f(1)")
|
||||
XCTFail("Expected error")
|
||||
} catch let error as DatabaseError {
|
||||
XCTAssertEqual(error.resultCode, .SQLITE_ERROR)
|
||||
XCTAssertEqual(error.message!, "wrong number of arguments to function f()")
|
||||
XCTAssertEqual(error.sql!, "SELECT f(1)")
|
||||
XCTAssertEqual(error.description, "SQLite error 1: wrong number of arguments to function f() - while executing `SELECT f(1)`")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testAggregateOfOneArgument() throws {
|
||||
struct Aggregate : DatabaseAggregate {
|
||||
var result: (any DatabaseValueConvertible)?
|
||||
mutating func step(_ dbValues: [DatabaseValue]) {
|
||||
result = String.fromDatabaseValue(dbValues[0])?.uppercased()
|
||||
}
|
||||
func finalize() -> (any DatabaseValueConvertible)? { result }
|
||||
}
|
||||
let dbQueue = try makeDatabaseQueue()
|
||||
try dbQueue.inDatabase { db in
|
||||
let fn = DatabaseFunction("f", argumentCount: 1, aggregate: Aggregate.self)
|
||||
db.add(function: fn)
|
||||
XCTAssertEqual(try String.fetchOne(db, sql: "SELECT upper(?)", arguments: ["Roué"])!, "ROUé")
|
||||
XCTAssertEqual(try String.fetchOne(db, sql: "SELECT f(?)", arguments: ["Roué"])!, "ROUÉ")
|
||||
XCTAssertTrue(try String.fetchOne(db, sql: "SELECT f(NULL)") == nil)
|
||||
do {
|
||||
try db.execute(sql: "SELECT f()")
|
||||
XCTFail("Expected error")
|
||||
} catch let error as DatabaseError {
|
||||
XCTAssertEqual(error.resultCode, .SQLITE_ERROR)
|
||||
XCTAssertEqual(error.message!, "wrong number of arguments to function f()")
|
||||
XCTAssertEqual(error.sql!, "SELECT f()")
|
||||
XCTAssertEqual(error.description, "SQLite error 1: wrong number of arguments to function f() - while executing `SELECT f()`")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testAggregateOfTwoArguments() throws {
|
||||
struct Aggregate : DatabaseAggregate {
|
||||
var result: (any DatabaseValueConvertible)?
|
||||
mutating func step(_ dbValues: [DatabaseValue]) {
|
||||
let ints = dbValues.compactMap { Int.fromDatabaseValue($0) }
|
||||
result = ints.reduce(0, +)
|
||||
}
|
||||
func finalize() -> (any DatabaseValueConvertible)? { result }
|
||||
}
|
||||
let dbQueue = try makeDatabaseQueue()
|
||||
try dbQueue.inDatabase { db in
|
||||
let fn = DatabaseFunction("f", argumentCount: 2, aggregate: Aggregate.self)
|
||||
db.add(function: fn)
|
||||
XCTAssertEqual(try Int.fetchOne(db, sql: "SELECT f(1, 2)")!, 3)
|
||||
do {
|
||||
try db.execute(sql: "SELECT f()")
|
||||
XCTFail("Expected error")
|
||||
} catch let error as DatabaseError {
|
||||
XCTAssertEqual(error.resultCode, .SQLITE_ERROR)
|
||||
XCTAssertEqual(error.message!, "wrong number of arguments to function f()")
|
||||
XCTAssertEqual(error.sql!, "SELECT f()")
|
||||
XCTAssertEqual(error.description, "SQLite error 1: wrong number of arguments to function f() - while executing `SELECT f()`")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testVariadicFunction() throws {
|
||||
struct Aggregate : DatabaseAggregate {
|
||||
var result: (any DatabaseValueConvertible)?
|
||||
mutating func step(_ dbValues: [DatabaseValue]) {
|
||||
result = dbValues.count
|
||||
}
|
||||
func finalize() -> (any DatabaseValueConvertible)? { result }
|
||||
}
|
||||
let dbQueue = try makeDatabaseQueue()
|
||||
try dbQueue.inDatabase { db in
|
||||
let fn = DatabaseFunction("f", aggregate: Aggregate.self)
|
||||
db.add(function: fn)
|
||||
XCTAssertEqual(try Int.fetchOne(db, sql: "SELECT f()")!, 0)
|
||||
XCTAssertEqual(try Int.fetchOne(db, sql: "SELECT f(1)")!, 1)
|
||||
XCTAssertEqual(try Int.fetchOne(db, sql: "SELECT f(1, 1)")!, 2)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Step Errors
|
||||
|
||||
func testAggregateStepThrowingDatabaseErrorWithMessage() throws {
|
||||
struct Aggregate : DatabaseAggregate {
|
||||
func step(_ dbValues: [DatabaseValue]) throws {
|
||||
throw DatabaseError(message: "custom error message")
|
||||
}
|
||||
func finalize() -> (any DatabaseValueConvertible)? { fatalError() }
|
||||
}
|
||||
let dbQueue = try makeDatabaseQueue()
|
||||
try dbQueue.inDatabase { db in
|
||||
let fn = DatabaseFunction("f", aggregate: Aggregate.self)
|
||||
db.add(function: fn)
|
||||
do {
|
||||
try db.execute(sql: "SELECT f()")
|
||||
XCTFail("Expected DatabaseError")
|
||||
} catch let error as DatabaseError {
|
||||
XCTAssertEqual(error.resultCode, .SQLITE_ERROR)
|
||||
XCTAssertEqual(error.message, "custom error message")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testAggregateStepThrowingDatabaseErrorWithCode() throws {
|
||||
struct Aggregate : DatabaseAggregate {
|
||||
func step(_ dbValues: [DatabaseValue]) throws {
|
||||
throw DatabaseError(resultCode: ResultCode(rawValue: 123))
|
||||
}
|
||||
func finalize() -> (any DatabaseValueConvertible)? { fatalError() }
|
||||
}
|
||||
let dbQueue = try makeDatabaseQueue()
|
||||
try dbQueue.inDatabase { db in
|
||||
let fn = DatabaseFunction("f", aggregate: Aggregate.self)
|
||||
db.add(function: fn)
|
||||
do {
|
||||
try db.execute(sql: "SELECT f()")
|
||||
XCTFail("Expected DatabaseError")
|
||||
} catch let error as DatabaseError {
|
||||
XCTAssertEqual(error.resultCode.rawValue, 123)
|
||||
XCTAssertEqual(error.message, "unknown error")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testAggregateStepThrowingDatabaseErrorWithMessageAndCode() throws {
|
||||
struct Aggregate : DatabaseAggregate {
|
||||
func step(_ dbValues: [DatabaseValue]) throws {
|
||||
throw DatabaseError(resultCode: ResultCode(rawValue: 123), message: "custom error message")
|
||||
}
|
||||
func finalize() -> (any DatabaseValueConvertible)? { fatalError() }
|
||||
}
|
||||
let dbQueue = try makeDatabaseQueue()
|
||||
try dbQueue.inDatabase { db in
|
||||
let fn = DatabaseFunction("f", aggregate: Aggregate.self)
|
||||
db.add(function: fn)
|
||||
do {
|
||||
try db.execute(sql: "SELECT f()")
|
||||
XCTFail("Expected DatabaseError")
|
||||
} catch let error as DatabaseError {
|
||||
XCTAssertEqual(error.resultCode.rawValue, 123)
|
||||
XCTAssertEqual(error.message, "custom error message")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testAggregateStepThrowingCustomError() throws {
|
||||
struct Aggregate : DatabaseAggregate {
|
||||
func step(_ dbValues: [DatabaseValue]) throws {
|
||||
throw NSError(domain: "CustomErrorDomain", code: 123, userInfo: [NSLocalizedDescriptionKey: "custom error message"])
|
||||
}
|
||||
func finalize() -> (any DatabaseValueConvertible)? { fatalError() }
|
||||
}
|
||||
let dbQueue = try makeDatabaseQueue()
|
||||
try dbQueue.inDatabase { db in
|
||||
let fn = DatabaseFunction("f", aggregate: Aggregate.self)
|
||||
db.add(function: fn)
|
||||
do {
|
||||
try db.execute(sql: "SELECT f()")
|
||||
XCTFail("Expected DatabaseError")
|
||||
} catch let error as DatabaseError {
|
||||
XCTAssertEqual(error.resultCode, .SQLITE_ERROR)
|
||||
XCTAssertTrue(error.message!.contains("CustomErrorDomain"))
|
||||
XCTAssertTrue(error.message!.contains("123"))
|
||||
XCTAssertTrue(error.message!.contains("custom error message"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Result Errors
|
||||
|
||||
func testAggregateResultThrowingDatabaseErrorWithMessage() throws {
|
||||
struct Aggregate : DatabaseAggregate {
|
||||
func step(_ dbValues: [DatabaseValue]) { }
|
||||
func finalize() throws -> (any DatabaseValueConvertible)? {
|
||||
throw DatabaseError(message: "custom error message")
|
||||
}
|
||||
}
|
||||
let dbQueue = try makeDatabaseQueue()
|
||||
try dbQueue.inDatabase { db in
|
||||
let fn = DatabaseFunction("f", aggregate: Aggregate.self)
|
||||
db.add(function: fn)
|
||||
do {
|
||||
try db.execute(sql: "SELECT f()")
|
||||
XCTFail("Expected DatabaseError")
|
||||
} catch let error as DatabaseError {
|
||||
XCTAssertEqual(error.resultCode, .SQLITE_ERROR)
|
||||
XCTAssertEqual(error.message, "custom error message")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testAggregateResultThrowingDatabaseErrorWithCode() throws {
|
||||
struct Aggregate : DatabaseAggregate {
|
||||
func step(_ dbValues: [DatabaseValue]) { }
|
||||
func finalize() throws -> (any DatabaseValueConvertible)? {
|
||||
throw DatabaseError(resultCode: ResultCode(rawValue: 123))
|
||||
}
|
||||
}
|
||||
let dbQueue = try makeDatabaseQueue()
|
||||
try dbQueue.inDatabase { db in
|
||||
let fn = DatabaseFunction("f", aggregate: Aggregate.self)
|
||||
db.add(function: fn)
|
||||
do {
|
||||
try db.execute(sql: "SELECT f()")
|
||||
XCTFail("Expected DatabaseError")
|
||||
} catch let error as DatabaseError {
|
||||
XCTAssertEqual(error.resultCode.rawValue, 123)
|
||||
XCTAssertEqual(error.message, "unknown error")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testAggregateResultThrowingDatabaseErrorWithMessageAndCode() throws {
|
||||
struct Aggregate : DatabaseAggregate {
|
||||
func step(_ dbValues: [DatabaseValue]) { }
|
||||
func finalize() throws -> (any DatabaseValueConvertible)? {
|
||||
throw DatabaseError(resultCode: ResultCode(rawValue: 123), message: "custom error message")
|
||||
}
|
||||
}
|
||||
let dbQueue = try makeDatabaseQueue()
|
||||
try dbQueue.inDatabase { db in
|
||||
let fn = DatabaseFunction("f", aggregate: Aggregate.self)
|
||||
db.add(function: fn)
|
||||
do {
|
||||
try db.execute(sql: "SELECT f()")
|
||||
XCTFail("Expected DatabaseError")
|
||||
} catch let error as DatabaseError {
|
||||
XCTAssertEqual(error.resultCode.rawValue, 123)
|
||||
XCTAssertEqual(error.message, "custom error message")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testAggregateResultThrowingCustomError() throws {
|
||||
struct Aggregate : DatabaseAggregate {
|
||||
func step(_ dbValues: [DatabaseValue]) { }
|
||||
func finalize() throws -> (any DatabaseValueConvertible)? {
|
||||
throw NSError(domain: "CustomErrorDomain", code: 123, userInfo: [NSLocalizedDescriptionKey: "custom error message"])
|
||||
}
|
||||
}
|
||||
let dbQueue = try makeDatabaseQueue()
|
||||
try dbQueue.inDatabase { db in
|
||||
let fn = DatabaseFunction("f", aggregate: Aggregate.self)
|
||||
db.add(function: fn)
|
||||
do {
|
||||
try db.execute(sql: "SELECT f()")
|
||||
XCTFail("Expected DatabaseError")
|
||||
} catch let error as DatabaseError {
|
||||
XCTAssertEqual(error.resultCode, .SQLITE_ERROR)
|
||||
XCTAssertTrue(error.message!.contains("CustomErrorDomain"))
|
||||
XCTAssertTrue(error.message!.contains("123"))
|
||||
XCTAssertTrue(error.message!.contains("custom error message"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Aggregation
|
||||
|
||||
func testAggregation() throws {
|
||||
struct Aggregate : DatabaseAggregate {
|
||||
var sum: Int?
|
||||
mutating func step(_ dbValues: [DatabaseValue]) {
|
||||
if let int = Int.fromDatabaseValue(dbValues[0]) {
|
||||
sum = (sum ?? 0) + int
|
||||
}
|
||||
}
|
||||
func finalize() throws -> (any DatabaseValueConvertible)? { sum }
|
||||
}
|
||||
let dbQueue = try makeDatabaseQueue()
|
||||
try dbQueue.inDatabase { db in
|
||||
let fn = DatabaseFunction("f", argumentCount: 1, aggregate: Aggregate.self)
|
||||
db.add(function: fn)
|
||||
XCTAssertEqual(try Int.fetchOne(db, sql: "SELECT f(a) FROM (SELECT 1 AS a UNION ALL SELECT 2 UNION ALL SELECT 3)")!, 6)
|
||||
}
|
||||
}
|
||||
|
||||
func testParallelAggregation() throws {
|
||||
struct Aggregate : DatabaseAggregate {
|
||||
var sum: Int?
|
||||
mutating func step(_ dbValues: [DatabaseValue]) {
|
||||
if let int = Int.fromDatabaseValue(dbValues[0]) {
|
||||
sum = (sum ?? 0) + int
|
||||
}
|
||||
}
|
||||
func finalize() throws -> (any DatabaseValueConvertible)? { sum }
|
||||
}
|
||||
let dbQueue = try makeDatabaseQueue()
|
||||
try dbQueue.inDatabase { db in
|
||||
let fn = DatabaseFunction("f", argumentCount: 1, aggregate: Aggregate.self)
|
||||
db.add(function: fn)
|
||||
let row = try Row.fetchOne(db, sql: "SELECT f(a), f(b) FROM (SELECT 1 AS a, 2 AS b UNION ALL SELECT 2, 4 UNION ALL SELECT 3, 6)")!
|
||||
XCTAssertEqual(row[0], 6)
|
||||
XCTAssertEqual(row[1], 12)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Deallocation
|
||||
|
||||
func testDeallocationAfterSuccess() throws {
|
||||
final class Aggregate : DatabaseAggregate {
|
||||
static var onInit: (() -> ())?
|
||||
static var onDeinit: (() -> ())?
|
||||
init() { Aggregate.onInit?() }
|
||||
deinit { Aggregate.onDeinit?() }
|
||||
func step(_ dbValues: [DatabaseValue]) { }
|
||||
func finalize() -> (any DatabaseValueConvertible)? { nil }
|
||||
}
|
||||
var allocationCount = 0
|
||||
var aliveCount = 0
|
||||
Aggregate.onInit = {
|
||||
allocationCount += 1
|
||||
aliveCount += 1
|
||||
}
|
||||
Aggregate.onDeinit = {
|
||||
aliveCount -= 1
|
||||
}
|
||||
|
||||
let dbQueue = try makeDatabaseQueue()
|
||||
try dbQueue.inDatabase { db in
|
||||
let fn = DatabaseFunction("f", argumentCount: 0, aggregate: Aggregate.self)
|
||||
db.add(function: fn)
|
||||
XCTAssertEqual(allocationCount, 0)
|
||||
XCTAssertEqual(aliveCount, 0)
|
||||
try db.execute(sql: "SELECT f()")
|
||||
XCTAssertEqual(allocationCount, 1)
|
||||
XCTAssertEqual(aliveCount, 0)
|
||||
}
|
||||
}
|
||||
|
||||
func testDeallocationAfterStepError() throws {
|
||||
final class Aggregate : DatabaseAggregate {
|
||||
static var onInit: (() -> ())?
|
||||
static var onDeinit: (() -> ())?
|
||||
init() { Aggregate.onInit?() }
|
||||
deinit { Aggregate.onDeinit?() }
|
||||
func step(_ dbValues: [DatabaseValue]) throws {
|
||||
throw DatabaseError(message: "boo")
|
||||
}
|
||||
func finalize() -> (any DatabaseValueConvertible)? { fatalError() }
|
||||
}
|
||||
var allocationCount = 0
|
||||
var aliveCount = 0
|
||||
Aggregate.onInit = {
|
||||
allocationCount += 1
|
||||
aliveCount += 1
|
||||
}
|
||||
Aggregate.onDeinit = {
|
||||
aliveCount -= 1
|
||||
}
|
||||
|
||||
let dbQueue = try makeDatabaseQueue()
|
||||
dbQueue.inDatabase { db in
|
||||
let fn = DatabaseFunction("f", argumentCount: 0, aggregate: Aggregate.self)
|
||||
db.add(function: fn)
|
||||
XCTAssertEqual(allocationCount, 0)
|
||||
XCTAssertEqual(aliveCount, 0)
|
||||
_ = try? db.execute(sql: "SELECT f()")
|
||||
XCTAssertEqual(allocationCount, 1)
|
||||
XCTAssertEqual(aliveCount, 0)
|
||||
}
|
||||
}
|
||||
|
||||
func testDeallocationAfterResultError() throws {
|
||||
final class Aggregate : DatabaseAggregate {
|
||||
static var onInit: (() -> ())?
|
||||
static var onDeinit: (() -> ())?
|
||||
init() { Aggregate.onInit?() }
|
||||
deinit { Aggregate.onDeinit?() }
|
||||
func step(_ dbValues: [DatabaseValue]) { }
|
||||
func finalize() throws -> (any DatabaseValueConvertible)? {
|
||||
throw DatabaseError(message: "boo")
|
||||
}
|
||||
}
|
||||
|
||||
var allocationCount = 0
|
||||
var aliveCount = 0
|
||||
Aggregate.onInit = {
|
||||
allocationCount += 1
|
||||
aliveCount += 1
|
||||
}
|
||||
Aggregate.onDeinit = {
|
||||
aliveCount -= 1
|
||||
}
|
||||
|
||||
let dbQueue = try makeDatabaseQueue()
|
||||
dbQueue.inDatabase { db in
|
||||
let fn = DatabaseFunction("f", argumentCount: 0, aggregate: Aggregate.self)
|
||||
db.add(function: fn)
|
||||
XCTAssertEqual(allocationCount, 0)
|
||||
XCTAssertEqual(aliveCount, 0)
|
||||
_ = try? db.execute(sql: "SELECT f()")
|
||||
XCTAssertEqual(allocationCount, 1)
|
||||
XCTAssertEqual(aliveCount, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user