584 lines
24 KiB
Swift
584 lines
24 KiB
Swift
import XCTest
|
|
import GRDB
|
|
|
|
class StatementArgumentsTests: GRDBTestCase {
|
|
|
|
override func setup(_ dbWriter: some DatabaseWriter) throws {
|
|
var migrator = DatabaseMigrator()
|
|
migrator.registerMigration("createPersons") { db in
|
|
try db.execute(sql: """
|
|
CREATE TABLE persons (
|
|
id INTEGER PRIMARY KEY,
|
|
firstName TEXT,
|
|
lastName TEXT,
|
|
age INT)
|
|
""")
|
|
}
|
|
try migrator.migrate(dbWriter)
|
|
}
|
|
|
|
func testPositionalStatementArgumentsValidation() throws {
|
|
let dbQueue = try makeDatabaseQueue()
|
|
try dbQueue.inDatabase { db in
|
|
let statement = try db.makeStatement(sql: "INSERT INTO persons (firstName, age) VALUES (?, ?)")
|
|
|
|
do {
|
|
// Correct number of arguments
|
|
try statement.validateArguments(["foo", 1])
|
|
} catch {
|
|
XCTFail("Unexpected error: \(error)")
|
|
}
|
|
|
|
do {
|
|
// Missing arguments
|
|
try statement.validateArguments([])
|
|
XCTFail("Expected error")
|
|
} catch DatabaseError.SQLITE_MISUSE {
|
|
} catch {
|
|
XCTFail("Unexpected error: \(error)")
|
|
}
|
|
|
|
do {
|
|
// Two few arguments
|
|
try statement.validateArguments(["foo"])
|
|
XCTFail("Expected error")
|
|
} catch DatabaseError.SQLITE_MISUSE {
|
|
} catch {
|
|
XCTFail("Unexpected error: \(error)")
|
|
}
|
|
|
|
do {
|
|
// Two many arguments
|
|
try statement.validateArguments(["foo", 1, "bar"])
|
|
XCTFail("Expected error")
|
|
} catch DatabaseError.SQLITE_MISUSE {
|
|
} catch {
|
|
XCTFail("Unexpected error: \(error)")
|
|
}
|
|
|
|
do {
|
|
// Missing arguments
|
|
try statement.validateArguments([:])
|
|
XCTFail("Expected error")
|
|
} catch DatabaseError.SQLITE_MISUSE {
|
|
} catch {
|
|
XCTFail("Unexpected error: \(error)")
|
|
}
|
|
|
|
do {
|
|
// Unmappable arguments
|
|
try statement.validateArguments(["firstName": "foo", "age": 1])
|
|
XCTFail("Expected error")
|
|
} catch DatabaseError.SQLITE_MISUSE {
|
|
} catch {
|
|
XCTFail("Unexpected error: \(error)")
|
|
}
|
|
}
|
|
}
|
|
|
|
func testPositionalStatementArguments() throws {
|
|
let dbQueue = try makeDatabaseQueue()
|
|
try dbQueue.inDatabase { db in
|
|
let name = "Arthur"
|
|
let age = 42
|
|
let arguments = StatementArguments([name, age] as [(any DatabaseValueConvertible)?])
|
|
|
|
let updateStatement = try db.makeStatement(sql: "INSERT INTO persons (firstName, age) VALUES (?, ?)")
|
|
updateStatement.arguments = arguments
|
|
try updateStatement.execute()
|
|
|
|
let selectStatement = try db.makeStatement(sql: "SELECT * FROM persons WHERE firstName = ? AND age = ?")
|
|
selectStatement.arguments = arguments
|
|
let row = try Row.fetchOne(selectStatement)!
|
|
|
|
XCTAssertEqual(row["firstName"] as String, name)
|
|
XCTAssertEqual(row["age"] as Int, age)
|
|
}
|
|
}
|
|
|
|
func testCheckedPositionalStatementArguments() throws {
|
|
let dbQueue = try makeDatabaseQueue()
|
|
try dbQueue.inDatabase { db in
|
|
let name = "Arthur"
|
|
let age = 42
|
|
let arguments = StatementArguments([name, age] as [(any DatabaseValueConvertible)?])
|
|
|
|
let updateStatement = try db.makeStatement(sql: "INSERT INTO persons (firstName, age) VALUES (?, ?)")
|
|
try updateStatement.setArguments(arguments)
|
|
try updateStatement.execute()
|
|
|
|
let selectStatement = try db.makeStatement(sql: "SELECT * FROM persons WHERE firstName = ? AND age = ?")
|
|
try selectStatement.setArguments(arguments)
|
|
let row = try Row.fetchOne(selectStatement)!
|
|
|
|
XCTAssertEqual(row["firstName"] as String, name)
|
|
XCTAssertEqual(row["age"] as Int, age)
|
|
|
|
do {
|
|
try updateStatement.setArguments([1])
|
|
XCTFail("Expected error")
|
|
} catch DatabaseError.SQLITE_MISUSE {
|
|
XCTAssertEqual(updateStatement.arguments, arguments)
|
|
} catch {
|
|
XCTFail("Unexpected error: \(error)")
|
|
}
|
|
|
|
do {
|
|
try selectStatement.setArguments([1])
|
|
XCTFail("Expected error")
|
|
} catch DatabaseError.SQLITE_MISUSE {
|
|
XCTAssertEqual(selectStatement.arguments, arguments)
|
|
} catch {
|
|
XCTFail("Unexpected error: \(error)")
|
|
}
|
|
}
|
|
}
|
|
|
|
func testUncheckedPositionalStatementArguments() throws {
|
|
let dbQueue = try makeDatabaseQueue()
|
|
try dbQueue.inDatabase { db in
|
|
let name = "Arthur"
|
|
let age = 42
|
|
let arguments = StatementArguments([name, age] as [(any DatabaseValueConvertible)?])
|
|
|
|
let updateStatement = try db.makeStatement(sql: "INSERT INTO persons (firstName, age) VALUES (?, ?)")
|
|
updateStatement.setUncheckedArguments(arguments)
|
|
try updateStatement.execute()
|
|
|
|
let selectStatement = try db.makeStatement(sql: "SELECT * FROM persons WHERE firstName = ? AND age = ?")
|
|
selectStatement.setUncheckedArguments(arguments)
|
|
let row = try Row.fetchOne(selectStatement)!
|
|
|
|
XCTAssertEqual(row["firstName"] as String, name)
|
|
XCTAssertEqual(row["age"] as Int, age)
|
|
}
|
|
}
|
|
|
|
func testNamedStatementArgumentsValidation() throws {
|
|
let dbQueue = try makeDatabaseQueue()
|
|
try dbQueue.inDatabase { db in
|
|
let statement = try db.makeStatement(sql: "INSERT INTO persons (firstName, age) VALUES (:firstName, :age)")
|
|
|
|
do {
|
|
// Correct number of arguments
|
|
try statement.validateArguments(["foo", 1])
|
|
} catch {
|
|
XCTFail("Unexpected error: \(error)")
|
|
}
|
|
|
|
do {
|
|
// All arguments are mapped
|
|
try statement.validateArguments(["firstName": "foo", "age": 1])
|
|
} catch {
|
|
XCTFail("Unexpected error: \(error)")
|
|
}
|
|
|
|
do {
|
|
// All arguments are mapped
|
|
try statement.validateArguments(["firstName": "foo", "age": 1, "bar": "baz"])
|
|
} catch {
|
|
XCTFail("Unexpected error: \(error)")
|
|
}
|
|
|
|
do {
|
|
// Missing arguments
|
|
try statement.validateArguments([])
|
|
XCTFail("Expected error")
|
|
} catch DatabaseError.SQLITE_MISUSE {
|
|
} catch {
|
|
XCTFail("Unexpected error: \(error)")
|
|
}
|
|
|
|
do {
|
|
// Missing arguments
|
|
try statement.validateArguments(["foo"])
|
|
XCTFail("Expected error")
|
|
} catch DatabaseError.SQLITE_MISUSE {
|
|
} catch {
|
|
XCTFail("Unexpected error: \(error)")
|
|
}
|
|
|
|
do {
|
|
// Too many arguments
|
|
try statement.validateArguments(["foo", 1, "baz"])
|
|
XCTFail("Expected error")
|
|
} catch DatabaseError.SQLITE_MISUSE {
|
|
} catch {
|
|
XCTFail("Unexpected error: \(error)")
|
|
}
|
|
|
|
do {
|
|
// Missing arguments
|
|
try statement.validateArguments([:])
|
|
XCTFail("Expected error")
|
|
} catch DatabaseError.SQLITE_MISUSE {
|
|
} catch {
|
|
XCTFail("Unexpected error: \(error)")
|
|
}
|
|
|
|
do {
|
|
// Missing arguments
|
|
try statement.validateArguments(["firstName": "foo"])
|
|
XCTFail("Expected error")
|
|
} catch DatabaseError.SQLITE_MISUSE {
|
|
} catch {
|
|
XCTFail("Unexpected error: \(error)")
|
|
}
|
|
}
|
|
}
|
|
|
|
func testNamedStatementArguments() throws {
|
|
let dbQueue = try makeDatabaseQueue()
|
|
try dbQueue.inDatabase { db in
|
|
let name = "Arthur"
|
|
let age = 42
|
|
let arguments = StatementArguments(["name": name, "age": age] as [String: (any DatabaseValueConvertible)?])
|
|
|
|
let updateStatement = try db.makeStatement(sql: "INSERT INTO persons (firstName, age) VALUES (:name, :age)")
|
|
updateStatement.arguments = arguments
|
|
try updateStatement.execute()
|
|
|
|
let selectStatement = try db.makeStatement(sql: "SELECT * FROM persons WHERE firstName = :name AND age = :age")
|
|
selectStatement.arguments = arguments
|
|
let row = try Row.fetchOne(selectStatement)!
|
|
|
|
XCTAssertEqual(row["firstName"] as String, name)
|
|
XCTAssertEqual(row["age"] as Int, age)
|
|
}
|
|
}
|
|
|
|
func testCheckedNamedStatementArguments() throws {
|
|
let dbQueue = try makeDatabaseQueue()
|
|
try dbQueue.inDatabase { db in
|
|
let name = "Arthur"
|
|
let age = 42
|
|
let arguments = StatementArguments(["name": name, "age": age] as [String: (any DatabaseValueConvertible)?])
|
|
|
|
let updateStatement = try db.makeStatement(sql: "INSERT INTO persons (firstName, age) VALUES (:name, :age)")
|
|
try updateStatement.setArguments(arguments)
|
|
try updateStatement.execute()
|
|
|
|
let selectStatement = try db.makeStatement(sql: "SELECT * FROM persons WHERE firstName = :name AND age = :age")
|
|
try selectStatement.setArguments(arguments)
|
|
let row = try Row.fetchOne(selectStatement)!
|
|
|
|
XCTAssertEqual(row["firstName"] as String, name)
|
|
XCTAssertEqual(row["age"] as Int, age)
|
|
|
|
do {
|
|
try updateStatement.setArguments(["name": name])
|
|
XCTFail("Expected error")
|
|
} catch DatabaseError.SQLITE_MISUSE {
|
|
XCTAssertEqual(updateStatement.arguments, arguments)
|
|
} catch {
|
|
XCTFail("Unexpected error: \(error)")
|
|
}
|
|
|
|
do {
|
|
try selectStatement.setArguments(["name": name])
|
|
XCTFail("Expected error")
|
|
} catch DatabaseError.SQLITE_MISUSE {
|
|
XCTAssertEqual(selectStatement.arguments, arguments)
|
|
} catch {
|
|
XCTFail("Unexpected error: \(error)")
|
|
}
|
|
}
|
|
}
|
|
|
|
func testUncheckedNamedStatementArguments() throws {
|
|
let dbQueue = try makeDatabaseQueue()
|
|
try dbQueue.inDatabase { db in
|
|
let name = "Arthur"
|
|
let age = 42
|
|
let arguments = StatementArguments(["name": name, "age": age] as [String: (any DatabaseValueConvertible)?])
|
|
|
|
let updateStatement = try db.makeStatement(sql: "INSERT INTO persons (firstName, age) VALUES (:name, :age)")
|
|
updateStatement.setUncheckedArguments(arguments)
|
|
try updateStatement.execute()
|
|
|
|
let selectStatement = try db.makeStatement(sql: "SELECT * FROM persons WHERE firstName = :name AND age = :age")
|
|
selectStatement.setUncheckedArguments(arguments)
|
|
let row = try Row.fetchOne(selectStatement)!
|
|
|
|
XCTAssertEqual(row["firstName"] as String, name)
|
|
XCTAssertEqual(row["age"] as Int, age)
|
|
}
|
|
}
|
|
|
|
func testReusedNamedStatementArgumentsValidation() throws {
|
|
let dbQueue = try makeDatabaseQueue()
|
|
try dbQueue.inDatabase { db in
|
|
let statement = try db.makeStatement(sql: "INSERT INTO persons (firstName, lastName, age) VALUES (:name, :name, :age)")
|
|
|
|
do {
|
|
try statement.execute(arguments: ["name": "foo", "age": 1])
|
|
let row = try Row.fetchOne(db, sql: "SELECT * FROM persons")!
|
|
XCTAssertEqual(row["firstName"] as String, "foo")
|
|
XCTAssertEqual(row["lastName"] as String, "foo")
|
|
XCTAssertEqual(row["age"] as Int, 1)
|
|
}
|
|
|
|
do {
|
|
// Correct number of arguments
|
|
try statement.validateArguments(["foo", 1])
|
|
} catch {
|
|
XCTFail("Unexpected error: \(error)")
|
|
}
|
|
|
|
do {
|
|
// All arguments are mapped
|
|
try statement.validateArguments(["name": "foo", "age": 1])
|
|
} catch {
|
|
XCTFail("Unexpected error: \(error)")
|
|
}
|
|
|
|
do {
|
|
// All arguments are mapped
|
|
try statement.validateArguments(["name": "foo", "age": 1, "bar": "baz"])
|
|
} catch {
|
|
XCTFail("Unexpected error: \(error)")
|
|
}
|
|
|
|
do {
|
|
// Missing arguments
|
|
try statement.validateArguments([])
|
|
XCTFail("Expected error")
|
|
} catch DatabaseError.SQLITE_MISUSE {
|
|
} catch {
|
|
XCTFail("Unexpected error: \(error)")
|
|
}
|
|
|
|
do {
|
|
// Missing arguments
|
|
try statement.validateArguments(["foo"])
|
|
XCTFail("Expected error")
|
|
} catch DatabaseError.SQLITE_MISUSE {
|
|
} catch {
|
|
XCTFail("Unexpected error: \(error)")
|
|
}
|
|
|
|
do {
|
|
// Too many arguments
|
|
try statement.validateArguments(["foo", 1, "baz"])
|
|
XCTFail("Expected error")
|
|
} catch DatabaseError.SQLITE_MISUSE {
|
|
} catch {
|
|
XCTFail("Unexpected error: \(error)")
|
|
}
|
|
|
|
do {
|
|
// Missing arguments
|
|
try statement.validateArguments([:])
|
|
XCTFail("Expected error")
|
|
} catch DatabaseError.SQLITE_MISUSE {
|
|
} catch {
|
|
XCTFail("Unexpected error: \(error)")
|
|
}
|
|
|
|
do {
|
|
// Missing arguments
|
|
try statement.validateArguments(["name": "foo"])
|
|
XCTFail("Expected error")
|
|
} catch DatabaseError.SQLITE_MISUSE {
|
|
} catch {
|
|
XCTFail("Unexpected error: \(error)")
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
func testReusedNamedStatementArguments() throws {
|
|
let dbQueue = try makeDatabaseQueue()
|
|
try dbQueue.inDatabase { db in
|
|
let name = "Arthur"
|
|
let age = 42
|
|
let arguments = StatementArguments(["name": name, "age": age] as [String: (any DatabaseValueConvertible)?])
|
|
|
|
let updateStatement = try db.makeStatement(sql: "INSERT INTO persons (firstName, lastName, age) VALUES (:name, :name, :age)")
|
|
updateStatement.arguments = arguments
|
|
try updateStatement.execute()
|
|
|
|
let selectStatement = try db.makeStatement(sql: "SELECT * FROM persons WHERE firstName = :name AND lastName = :name AND age = :age")
|
|
selectStatement.arguments = arguments
|
|
let row = try Row.fetchOne(selectStatement)!
|
|
|
|
XCTAssertEqual(row["firstName"] as String, name)
|
|
XCTAssertEqual(row["age"] as Int, age)
|
|
}
|
|
}
|
|
|
|
func testCheckedReusedNamedStatementArguments() throws {
|
|
let dbQueue = try makeDatabaseQueue()
|
|
try dbQueue.inDatabase { db in
|
|
let name = "Arthur"
|
|
let age = 42
|
|
let arguments = StatementArguments(["name": name, "age": age] as [String: (any DatabaseValueConvertible)?])
|
|
|
|
let updateStatement = try db.makeStatement(sql: "INSERT INTO persons (firstName, lastName, age) VALUES (:name, :name, :age)")
|
|
try updateStatement.setArguments(arguments)
|
|
try updateStatement.execute()
|
|
|
|
let selectStatement = try db.makeStatement(sql: "SELECT * FROM persons WHERE firstName = :name AND lastName = :name AND age = :age")
|
|
try selectStatement.setArguments(arguments)
|
|
let row = try Row.fetchOne(selectStatement)!
|
|
|
|
XCTAssertEqual(row["firstName"] as String, name)
|
|
XCTAssertEqual(row["age"] as Int, age)
|
|
|
|
do {
|
|
try updateStatement.setArguments(["name": name])
|
|
XCTFail("Expected error")
|
|
} catch DatabaseError.SQLITE_MISUSE {
|
|
XCTAssertEqual(updateStatement.arguments, arguments)
|
|
} catch {
|
|
XCTFail("Unexpected error: \(error)")
|
|
}
|
|
|
|
do {
|
|
try selectStatement.setArguments(["name": name])
|
|
XCTFail("Expected error")
|
|
} catch DatabaseError.SQLITE_MISUSE {
|
|
XCTAssertEqual(selectStatement.arguments, arguments)
|
|
} catch {
|
|
XCTFail("Unexpected error: \(error)")
|
|
}
|
|
}
|
|
}
|
|
|
|
func testUncheckedReusedNamedStatementArguments() throws {
|
|
let dbQueue = try makeDatabaseQueue()
|
|
try dbQueue.inDatabase { db in
|
|
let name = "Arthur"
|
|
let age = 42
|
|
let arguments = StatementArguments(["name": name, "age": age] as [String: (any DatabaseValueConvertible)?])
|
|
|
|
let updateStatement = try db.makeStatement(sql: "INSERT INTO persons (firstName, lastName, age) VALUES (:name, :name, :age)")
|
|
updateStatement.setUncheckedArguments(arguments)
|
|
try updateStatement.execute()
|
|
|
|
let selectStatement = try db.makeStatement(sql: "SELECT * FROM persons WHERE firstName = :name AND lastName = :name AND age = :age")
|
|
selectStatement.setUncheckedArguments(arguments)
|
|
let row = try Row.fetchOne(selectStatement)!
|
|
|
|
XCTAssertEqual(row["firstName"] as String, name)
|
|
XCTAssertEqual(row["age"] as Int, age)
|
|
}
|
|
}
|
|
|
|
func testMixedArguments() throws {
|
|
let dbQueue = try makeDatabaseQueue()
|
|
try dbQueue.inDatabase { db in
|
|
let sql = "SELECT ?2 AS two, :foo AS foo, ?1 AS one, :foo AS foo2, :bar AS bar"
|
|
let row = try Row.fetchOne(db, sql: sql, arguments: [1, 2, "bar"] + ["foo": "foo"])!
|
|
XCTAssertEqual(row, ["two": 2, "foo": "foo", "one": 1, "foo2": "foo", "bar": "bar"])
|
|
}
|
|
}
|
|
|
|
func testAppendContentsOf() throws {
|
|
let dbQueue = try makeDatabaseQueue()
|
|
try dbQueue.inDatabase { db in
|
|
do {
|
|
var arguments: StatementArguments = [1, 2]
|
|
let replacedValues = arguments.append(contentsOf: [3, 4])
|
|
XCTAssert(replacedValues.isEmpty)
|
|
|
|
let row = try Row.fetchOne(db, sql: "SELECT ?, ?, ?, ?", arguments: arguments)!
|
|
XCTAssertEqual(row, ["?": 1, "?": 2, "?": 3, "?": 4])
|
|
}
|
|
|
|
do {
|
|
var arguments: StatementArguments = ["foo": "foo", "bar": "bar", "toto": "titi"]
|
|
let replacedValues = arguments.append(contentsOf: ["foo": "qux", "bar": "baz", "tata": "tutu"])
|
|
XCTAssertEqual(replacedValues, ["foo": "foo".databaseValue, "bar": "bar".databaseValue])
|
|
|
|
let row = try Row.fetchOne(db, sql: "SELECT :foo, :bar, :toto, :tata", arguments: arguments)!
|
|
XCTAssertEqual(row, [":foo": "qux", ":bar": "baz", ":toto": "titi", ":tata": "tutu"])
|
|
}
|
|
|
|
do {
|
|
var arguments: StatementArguments = [1, 2]
|
|
let replacedValues = arguments.append(contentsOf: ["foo": "qux", "bar": "baz", "tata": "tutu"])
|
|
XCTAssert(replacedValues.isEmpty)
|
|
|
|
let row = try Row.fetchOne(db, sql: "SELECT ?, :foo, :bar, ?, :tata", arguments: arguments)!
|
|
XCTAssertEqual(row, ["?": 1, ":foo": "qux", ":bar": "baz", "?": 2, ":tata": "tutu"])
|
|
}
|
|
}
|
|
}
|
|
|
|
func testPlusOperator() throws {
|
|
let dbQueue = try makeDatabaseQueue()
|
|
try dbQueue.inDatabase { db in
|
|
do {
|
|
let arguments: StatementArguments = [1, 2] + [3, 4]
|
|
let row = try Row.fetchOne(db, sql: "SELECT ?, ?, ?, ?", arguments: arguments)!
|
|
XCTAssertEqual(row, ["?": 1, "?": 2, "?": 3, "?": 4])
|
|
}
|
|
|
|
do {
|
|
// + does not allow overrides
|
|
let arguments: StatementArguments = ["foo": "foo", "bar": "bar", "toto": "titi"] + ["tata": "tutu"]
|
|
let row = try Row.fetchOne(db, sql: "SELECT :foo, :bar, :toto, :tata", arguments: arguments)!
|
|
XCTAssertEqual(row, [":foo": "foo", ":bar": "bar", ":toto": "titi", ":tata": "tutu"])
|
|
}
|
|
|
|
do {
|
|
let arguments: StatementArguments = [1, 2] + ["foo": "qux", "bar": "baz", "tata": "tutu"]
|
|
let row = try Row.fetchOne(db, sql: "SELECT ?, :foo, :bar, ?, :tata", arguments: arguments)!
|
|
XCTAssertEqual(row, ["?": 1, ":foo": "qux", ":bar": "baz", "?": 2, ":tata": "tutu"])
|
|
}
|
|
}
|
|
}
|
|
|
|
func testOverflowPlusOperator() throws {
|
|
let dbQueue = try makeDatabaseQueue()
|
|
try dbQueue.inDatabase { db in
|
|
do {
|
|
let arguments: StatementArguments = [1, 2] &+ [3, 4]
|
|
let row = try Row.fetchOne(db, sql: "SELECT ?, ?, ?, ?", arguments: arguments)!
|
|
XCTAssertEqual(row, ["?": 1, "?": 2, "?": 3, "?": 4])
|
|
}
|
|
|
|
do {
|
|
// &+ does not allow overrides
|
|
let arguments: StatementArguments = ["foo": "foo", "bar": "bar", "toto": "titi"] &+ ["foo": "qux", "bar": "baz", "tata": "tutu"]
|
|
let row = try Row.fetchOne(db, sql: "SELECT :foo, :bar, :toto, :tata", arguments: arguments)!
|
|
XCTAssertEqual(row, [":foo": "qux", ":bar": "baz", ":toto": "titi", ":tata": "tutu"])
|
|
}
|
|
|
|
do {
|
|
let arguments: StatementArguments = [1, 2] &+ ["foo": "qux", "bar": "baz", "tata": "tutu"]
|
|
let row = try Row.fetchOne(db, sql: "SELECT ?, :foo, :bar, ?, :tata", arguments: arguments)!
|
|
XCTAssertEqual(row, ["?": 1, ":foo": "qux", ":bar": "baz", "?": 2, ":tata": "tutu"])
|
|
}
|
|
}
|
|
}
|
|
|
|
func testPlusEqualOperator() throws {
|
|
let dbQueue = try makeDatabaseQueue()
|
|
try dbQueue.inDatabase { db in
|
|
do {
|
|
var arguments: StatementArguments = [1, 2]
|
|
arguments += [3, 4]
|
|
let row = try Row.fetchOne(db, sql: "SELECT ?, ?, ?, ?", arguments: arguments)!
|
|
XCTAssertEqual(row, ["?": 1, "?": 2, "?": 3, "?": 4])
|
|
}
|
|
|
|
do {
|
|
// += does not allow overrides
|
|
var arguments: StatementArguments = ["foo": "foo", "bar": "bar", "toto": "titi"]
|
|
arguments += ["tata": "tutu"]
|
|
let row = try Row.fetchOne(db, sql: "SELECT :foo, :bar, :toto, :tata", arguments: arguments)!
|
|
XCTAssertEqual(row, [":foo": "foo", ":bar": "bar", ":toto": "titi", ":tata": "tutu"])
|
|
}
|
|
|
|
do {
|
|
var arguments: StatementArguments = [1, 2]
|
|
arguments += ["foo": "qux", "bar": "baz", "tata": "tutu"]
|
|
let row = try Row.fetchOne(db, sql: "SELECT ?, :foo, :bar, ?, :tata", arguments: arguments)!
|
|
XCTAssertEqual(row, ["?": 1, ":foo": "qux", ":bar": "baz", "?": 2, ":tata": "tutu"])
|
|
}
|
|
}
|
|
}
|
|
}
|