Files
UUVPN/iOS-SwiftUI-Code/GRDB.swift-6.29.3/Tests/GRDBTests/TableRecord+QueryInterfaceRequestTests.swift
T

410 lines
16 KiB
Swift

import XCTest
import GRDB
private struct Col {
static let id = Column("id")
static let name = Column("name")
static let age = Column("age")
static let readerId = Column("readerId")
}
private struct Reader : TableRecord {
static let databaseTableName = "readers"
}
class TableRecordQueryInterfaceRequestTests: GRDBTestCase {
override func setup(_ dbWriter: some DatabaseWriter) throws {
var migrator = DatabaseMigrator()
migrator.registerMigration("createReaders") { db in
try db.execute(sql: """
CREATE TABLE readers (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
age INT)
""")
}
try migrator.migrate(dbWriter)
}
// MARK: - Count
func testFetchCount() throws {
let dbQueue = try makeDatabaseQueue()
try dbQueue.inDatabase { db in
XCTAssertEqual(try Reader.fetchCount(db), 0)
XCTAssertEqual(lastSQLQuery, "SELECT COUNT(*) FROM \"readers\"")
XCTAssertEqual(try Reader.all().reversed().fetchCount(db), 0)
XCTAssertEqual(lastSQLQuery, "SELECT COUNT(*) FROM \"readers\"")
XCTAssertEqual(try Reader.order(Col.name).fetchCount(db), 0)
XCTAssertEqual(lastSQLQuery, "SELECT COUNT(*) FROM \"readers\"")
XCTAssertEqual(try Reader.limit(10).fetchCount(db), 0)
XCTAssertEqual(lastSQLQuery, "SELECT COUNT(*) FROM (SELECT * FROM \"readers\" LIMIT 10)")
XCTAssertEqual(try Reader.filter(Col.age == 42).fetchCount(db), 0)
XCTAssertEqual(lastSQLQuery, "SELECT COUNT(*) FROM \"readers\" WHERE \"age\" = 42")
XCTAssertEqual(try Reader.all().distinct().fetchCount(db), 0)
XCTAssertEqual(lastSQLQuery, "SELECT COUNT(*) FROM (SELECT DISTINCT * FROM \"readers\")")
XCTAssertEqual(try Reader.select(Col.name).fetchCount(db), 0)
XCTAssertEqual(lastSQLQuery, "SELECT COUNT(*) FROM \"readers\"")
XCTAssertEqual(try Reader.select(Col.name).distinct().fetchCount(db), 0)
XCTAssertEqual(lastSQLQuery, "SELECT COUNT(DISTINCT \"name\") FROM \"readers\"")
XCTAssertEqual(try Reader.select(Col.age * 2).distinct().fetchCount(db), 0)
XCTAssertEqual(lastSQLQuery, "SELECT COUNT(DISTINCT \"age\" * 2) FROM \"readers\"")
XCTAssertEqual(try Reader.select((Col.age * 2).forKey("ignored")).distinct().fetchCount(db), 0)
XCTAssertEqual(lastSQLQuery, "SELECT COUNT(DISTINCT \"age\" * 2) FROM \"readers\"")
XCTAssertEqual(try Reader.select(Col.name, Col.age).fetchCount(db), 0)
XCTAssertEqual(lastSQLQuery, "SELECT COUNT(*) FROM \"readers\"")
XCTAssertEqual(try Reader.select(Col.name, Col.age).distinct().fetchCount(db), 0)
XCTAssertEqual(lastSQLQuery, "SELECT COUNT(*) FROM (SELECT DISTINCT \"name\", \"age\" FROM \"readers\")")
XCTAssertEqual(try Reader.select(max(Col.age)).group(Col.name).fetchCount(db), 0)
XCTAssertEqual(lastSQLQuery, "SELECT COUNT(*) FROM (SELECT MAX(\"age\") FROM \"readers\" GROUP BY \"name\")")
}
}
// MARK: - Select
func testSelectLiteral() throws {
let dbQueue = try makeDatabaseQueue()
try dbQueue.inDatabase { db in
try db.execute(sql: "INSERT INTO readers (name, age) VALUES (?, ?)", arguments: ["Arthur", 42])
try db.execute(sql: "INSERT INTO readers (name, age) VALUES (?, ?)", arguments: ["Barbara", 36])
let request = Reader.select(sql: "name, id - 1")
let rows = try Row.fetchAll(db, request)
XCTAssertEqual(lastSQLQuery, "SELECT name, id - 1 FROM \"readers\"")
XCTAssertEqual(rows.count, 2)
XCTAssertEqual(rows[0][0] as String, "Arthur")
XCTAssertEqual(rows[0][1] as Int64, 0)
XCTAssertEqual(rows[1][0] as String, "Barbara")
XCTAssertEqual(rows[1][1] as Int64, 1)
}
}
func testSelectLiteralWithPositionalArguments() throws {
let dbQueue = try makeDatabaseQueue()
try dbQueue.inDatabase { db in
try db.execute(sql: "INSERT INTO readers (name, age) VALUES (?, ?)", arguments: ["Arthur", 42])
try db.execute(sql: "INSERT INTO readers (name, age) VALUES (?, ?)", arguments: ["Barbara", 36])
let request = Reader.select(sql: "name, id - ?", arguments: [1])
let rows = try Row.fetchAll(db, request)
XCTAssertEqual(lastSQLQuery, "SELECT name, id - 1 FROM \"readers\"")
XCTAssertEqual(rows.count, 2)
XCTAssertEqual(rows[0][0] as String, "Arthur")
XCTAssertEqual(rows[0][1] as Int64, 0)
XCTAssertEqual(rows[1][0] as String, "Barbara")
XCTAssertEqual(rows[1][1] as Int64, 1)
}
}
func testSelectLiteralWithNamedArguments() throws {
let dbQueue = try makeDatabaseQueue()
try dbQueue.inDatabase { db in
try db.execute(sql: "INSERT INTO readers (name, age) VALUES (?, ?)", arguments: ["Arthur", 42])
try db.execute(sql: "INSERT INTO readers (name, age) VALUES (?, ?)", arguments: ["Barbara", 36])
let request = Reader.select(sql: "name, id - :n", arguments: ["n": 1])
let rows = try Row.fetchAll(db, request)
XCTAssertEqual(lastSQLQuery, "SELECT name, id - 1 FROM \"readers\"")
XCTAssertEqual(rows.count, 2)
XCTAssertEqual(rows[0][0] as String, "Arthur")
XCTAssertEqual(rows[0][1] as Int64, 0)
XCTAssertEqual(rows[1][0] as String, "Barbara")
XCTAssertEqual(rows[1][1] as Int64, 1)
}
}
func testSelectSQLLiteral() throws {
let dbQueue = try makeDatabaseQueue()
try dbQueue.inDatabase { db in
try db.execute(sql: "INSERT INTO readers (name, age) VALUES (?, ?)", arguments: ["Arthur", 42])
try db.execute(sql: "INSERT INTO readers (name, age) VALUES (?, ?)", arguments: ["Barbara", 36])
func test(_ request: QueryInterfaceRequest<Reader>) throws {
let rows = try Row.fetchAll(db, request)
XCTAssertEqual(rows.count, 2)
XCTAssertEqual(rows[0][0] as String, "O'Brien")
XCTAssertEqual(rows[0][1] as Int64, 0)
XCTAssertEqual(rows[1][0] as String, "O'Brien")
XCTAssertEqual(rows[1][1] as Int64, 1)
}
try test(Reader.select(literal: SQL(sql: ":name, id - :value", arguments: ["name": "O'Brien", "value": 1])))
// Interpolation
try test(Reader.select(literal: "\("O'Brien"), id - \(1)"))
}
}
func testSelect() throws {
let dbQueue = try makeDatabaseQueue()
try dbQueue.inDatabase { db in
try db.execute(sql: "INSERT INTO readers (name, age) VALUES (?, ?)", arguments: ["Arthur", 42])
try db.execute(sql: "INSERT INTO readers (name, age) VALUES (?, ?)", arguments: ["Barbara", 36])
let request = Reader.select(Col.name, Col.id - 1)
let rows = try Row.fetchAll(db, request)
XCTAssertEqual(lastSQLQuery, "SELECT \"name\", \"id\" - 1 FROM \"readers\"")
XCTAssertEqual(rows.count, 2)
XCTAssertEqual(rows[0][0] as String, "Arthur")
XCTAssertEqual(rows[0][1] as Int64, 0)
XCTAssertEqual(rows[1][0] as String, "Barbara")
XCTAssertEqual(rows[1][1] as Int64, 1)
}
}
func testMultipleSelect() throws {
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, Reader.select(Col.age).select(Col.name)),
"SELECT \"name\" FROM \"readers\"")
}
// MARK: - Filter
func testFilterLiteral() throws {
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, Reader.filter(sql: "id <> 1")),
"SELECT * FROM \"readers\" WHERE id <> 1")
}
func testFilterLiteralWithPositionalArguments() throws {
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, Reader.filter(sql: "id <> ?", arguments: [1])),
"SELECT * FROM \"readers\" WHERE id <> 1")
}
func testFilterLiteralWithNamedArguments() throws {
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, Reader.filter(sql: "id <> :id", arguments: ["id": 1])),
"SELECT * FROM \"readers\" WHERE id <> 1")
}
func testFilter() throws {
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, Reader.filter(true.databaseValue)),
"SELECT * FROM \"readers\" WHERE 1")
}
func testMultipleFilter() throws {
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, Reader.filter(true.databaseValue).filter(false.databaseValue)),
"SELECT * FROM \"readers\" WHERE 1 AND 0")
}
// MARK: - Sort
func testSortLiteral() throws {
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, Reader.order(sql: "lower(name) desc")),
"SELECT * FROM \"readers\" ORDER BY lower(name) desc")
}
func testSortLiteralWithPositionalArguments() throws {
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, Reader.order(sql: "age + ?", arguments: [1])),
"SELECT * FROM \"readers\" ORDER BY age + 1")
}
func testSortLiteralWithNamedArguments() throws {
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, Reader.order(sql: "age + :age", arguments: ["age": 1])),
"SELECT * FROM \"readers\" ORDER BY age + 1")
}
func testSort() throws {
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, Reader.order(Col.age)),
"SELECT * FROM \"readers\" ORDER BY \"age\"")
XCTAssertEqual(
sql(dbQueue, Reader.order(Col.age.asc)),
"SELECT * FROM \"readers\" ORDER BY \"age\" ASC")
XCTAssertEqual(
sql(dbQueue, Reader.order(Col.age.desc)),
"SELECT * FROM \"readers\" ORDER BY \"age\" DESC")
XCTAssertEqual(
sql(dbQueue, Reader.order(Col.age, Col.name.desc)),
"SELECT * FROM \"readers\" ORDER BY \"age\", \"name\" DESC")
XCTAssertEqual(
sql(dbQueue, Reader.order(abs(Col.age))),
"SELECT * FROM \"readers\" ORDER BY ABS(\"age\")")
#if GRDBCUSTOMSQLITE
XCTAssertEqual(
sql(dbQueue, Reader.order(Col.age.ascNullsLast)),
"SELECT * FROM \"readers\" ORDER BY \"age\" ASC NULLS LAST")
XCTAssertEqual(
sql(dbQueue, Reader.order(Col.age.descNullsFirst)),
"SELECT * FROM \"readers\" ORDER BY \"age\" DESC NULLS FIRST")
#elseif !GRDBCIPHER
if #available(iOS 14, macOS 10.16, tvOS 14, watchOS 7, *) {
XCTAssertEqual(
sql(dbQueue, Reader.order(Col.age.ascNullsLast)),
"SELECT * FROM \"readers\" ORDER BY \"age\" ASC NULLS LAST")
XCTAssertEqual(
sql(dbQueue, Reader.order(Col.age.descNullsFirst)),
"SELECT * FROM \"readers\" ORDER BY \"age\" DESC NULLS FIRST")
}
#endif
}
func testMultipleSort() throws {
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, Reader.order(Col.age).order(Col.name)),
"SELECT * FROM \"readers\" ORDER BY \"name\"")
}
// MARK: - Limit
func testLimit() throws {
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, Reader.limit(1)),
"SELECT * FROM \"readers\" LIMIT 1")
XCTAssertEqual(
sql(dbQueue, Reader.limit(1, offset: 2)),
"SELECT * FROM \"readers\" LIMIT 1 OFFSET 2")
}
func testMultipleLimit() throws {
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, Reader.limit(1, offset: 2).limit(3)),
"SELECT * FROM \"readers\" LIMIT 3")
}
// MARK: - Exists
func testExists() throws {
let dbQueue = try makeDatabaseQueue()
try dbQueue.inTransaction { db in
struct Player: TableRecord { }
try db.create(table: "player") { t in
t.column("a").unique()
t.column("b")
t.column("c")
t.uniqueKey(["b", "c"])
}
try XCTAssertFalse(Player.exists(db, key: 1))
XCTAssertEqual(lastSQLQuery, "SELECT EXISTS (SELECT * FROM \"player\" WHERE \"rowid\" = 1)")
try XCTAssertFalse(Player.exists(db, key: ["a": 1]))
XCTAssertEqual(lastSQLQuery, "SELECT EXISTS (SELECT * FROM \"player\" WHERE \"a\" = 1)")
try XCTAssertFalse(Player.exists(db, key: ["a": 1, "b": 2]))
XCTAssertEqual(lastSQLQuery, "SELECT EXISTS (SELECT * FROM \"player\" WHERE (\"a\" = 1) AND (\"b\" = 2))")
try XCTAssertFalse(Player.exists(db, key: ["b": 1, "c": 2]))
XCTAssertEqual(lastSQLQuery, "SELECT EXISTS (SELECT * FROM \"player\" WHERE (\"b\" = 1) AND (\"c\" = 2))")
return .rollback
}
try dbQueue.inTransaction { db in
struct Player: TableRecord { }
try db.create(table: "player") { t in
t.primaryKey("a", .integer)
}
try XCTAssertFalse(Player.exists(db, key: 1))
XCTAssertEqual(lastSQLQuery, "SELECT EXISTS (SELECT * FROM \"player\" WHERE \"a\" = 1)")
try XCTAssertFalse(Player.exists(db, key: ["a": 2]))
XCTAssertEqual(lastSQLQuery, "SELECT EXISTS (SELECT * FROM \"player\" WHERE \"a\" = 2)")
return .rollback
}
try dbQueue.inTransaction { db in
struct Player: TableRecord { }
try db.create(table: "player") { t in
t.primaryKey("id", .text)
}
try XCTAssertFalse(Player.exists(db, key: "foo"))
XCTAssertEqual(lastSQLQuery, "SELECT EXISTS (SELECT * FROM \"player\" WHERE \"id\" = 'foo')")
try XCTAssertFalse(Player.exists(db, key: ["id": "bar"]))
XCTAssertEqual(lastSQLQuery, "SELECT EXISTS (SELECT * FROM \"player\" WHERE \"id\" = 'bar')")
return .rollback
}
}
func testExistsIdentifiable() throws {
guard #available(iOS 13, macOS 10.15, tvOS 13, watchOS 6, *) else {
throw XCTSkip("Identifiable is not available")
}
let dbQueue = try makeDatabaseQueue()
try dbQueue.inTransaction { db in
struct Player: TableRecord, Identifiable {
var id: Int64
}
try db.create(table: "player") { t in
t.autoIncrementedPrimaryKey("id")
}
try XCTAssertFalse(Player.exists(db, id: 1))
XCTAssertEqual(lastSQLQuery, "SELECT EXISTS (SELECT * FROM \"player\" WHERE \"id\" = 1)")
return .rollback
}
try dbQueue.inTransaction { db in
struct Player: TableRecord, Identifiable {
var id: Int64?
}
try db.create(table: "player") { t in
t.autoIncrementedPrimaryKey("id")
}
try XCTAssertFalse(Player.exists(db, id: 1))
XCTAssertEqual(lastSQLQuery, "SELECT EXISTS (SELECT * FROM \"player\" WHERE \"id\" = 1)")
sqlQueries.removeAll()
try XCTAssertFalse(Player.exists(db, id: nil))
XCTAssertNil(lastSQLQuery) // Database not hit
return .rollback
}
try dbQueue.inTransaction { db in
struct Player: TableRecord, Identifiable {
var id: String
}
try db.create(table: "player") { t in
t.primaryKey("id", .text)
}
try XCTAssertFalse(Player.exists(db, id: "foo"))
XCTAssertEqual(lastSQLQuery, "SELECT EXISTS (SELECT * FROM \"player\" WHERE \"id\" = 'foo')")
return .rollback
}
}
}