Files
UUVPN/iOS-SwiftUI-Code/GRDB.swift-6.29.3/Tests/GRDBTests/QueryInterfaceExpressionsTests.swift
T

1847 lines
81 KiB
Swift

import XCTest
@testable 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"
}
private let tableRequest = Reader.all()
class QueryInterfaceExpressionsTests: GRDBTestCase {
let collation = DatabaseCollation("localized_case_insensitive") { (lhs, rhs) in
return (lhs as NSString).localizedCaseInsensitiveCompare(rhs)
}
let customFunction = DatabaseFunction("avgOf", pure: true) { dbValues in
let sum = dbValues.compactMap { Int.fromDatabaseValue($0) }.reduce(0, +)
return Double(sum) / Double(dbValues.count)
}
override func setUp() {
super.setUp()
dbConfiguration.prepareDatabase { db in
db.add(collation: self.collation)
db.add(function: self.customFunction)
}
}
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: - Boolean expressions
func testContains() throws {
let dbQueue = try makeDatabaseQueue()
// emptyArray.contains(): 0
XCTAssertEqual(
sql(dbQueue, tableRequest.filter([Int]().contains(Col.id))),
"SELECT * FROM \"readers\" WHERE 0")
// !emptyArray.contains(): 0
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(![Int]().contains(Col.id))),
"SELECT * FROM \"readers\" WHERE 1")
// Array.contains(): = operator
XCTAssertEqual(
sql(dbQueue, tableRequest.filter([1].contains(Col.id))),
"SELECT * FROM \"readers\" WHERE \"id\" = 1")
// Array.contains(): IN operator
XCTAssertEqual(
sql(dbQueue, tableRequest.filter([1,2,3].contains(Col.id))),
"SELECT * FROM \"readers\" WHERE \"id\" IN (1, 2, 3)")
// Array.contains(): IN operator
XCTAssertEqual(
sql(dbQueue, tableRequest.filter([Col.id].contains(Col.id))),
"SELECT * FROM \"readers\" WHERE \"id\" = \"id\"")
// EmptyCollection.contains(): 0
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(EmptyCollection<Int>().contains(Col.id))),
"SELECT * FROM \"readers\" WHERE 0")
// !EmptyCollection.contains(): 1
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(!EmptyCollection<Int>().contains(Col.id))),
"SELECT * FROM \"readers\" WHERE 1")
// Sequence.contains(): = operator
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(AnySequence([1]).contains(Col.id))),
"SELECT * FROM \"readers\" WHERE \"id\" = 1")
// Sequence.contains(): IN operator
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(AnySequence([1,2,3]).contains(Col.id))),
"SELECT * FROM \"readers\" WHERE \"id\" IN (1, 2, 3)")
// Sequence.contains(): IN operator
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(AnySequence([Col.id]).contains(Col.id))),
"SELECT * FROM \"readers\" WHERE \"id\" = \"id\"")
// !Sequence.contains(): <> operator
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(![1].contains(Col.id))),
"SELECT * FROM \"readers\" WHERE \"id\" <> 1")
// !Sequence.contains(): NOT IN operator
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(![1,2,3].contains(Col.id))),
"SELECT * FROM \"readers\" WHERE \"id\" NOT IN (1, 2, 3)")
// !!Sequence.contains(): = operator
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(!(![1].contains(Col.id)))),
"SELECT * FROM \"readers\" WHERE \"id\" = 1")
// !!Sequence.contains(): IN operator
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(!(![1,2,3].contains(Col.id)))),
"SELECT * FROM \"readers\" WHERE \"id\" IN (1, 2, 3)")
// CountableRange.contains(): min <= x < max
XCTAssertEqual(
sql(dbQueue, tableRequest.filter((1..<10).contains(Col.id))),
"SELECT * FROM \"readers\" WHERE (\"id\" >= 1) AND (\"id\" < 10)")
// CountableClosedRange.contains(): BETWEEN operator
XCTAssertEqual(
sql(dbQueue, tableRequest.filter((1...10).contains(Col.id))),
"SELECT * FROM \"readers\" WHERE \"id\" BETWEEN 1 AND 10")
// !CountableClosedRange.contains(): BETWEEN operator
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(!(1...10).contains(Col.id))),
"SELECT * FROM \"readers\" WHERE \"id\" NOT BETWEEN 1 AND 10")
// Range.contains(): min <= x < max
XCTAssertEqual(
sql(dbQueue, tableRequest.filter((1.1..<10.9).contains(Col.id))),
"SELECT * FROM \"readers\" WHERE (\"id\" >= 1.1) AND (\"id\" < 10.9)")
// Range.contains(): min <= x < max
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(("A"..<"z").contains(Col.name))),
"SELECT * FROM \"readers\" WHERE (\"name\" >= 'A') AND (\"name\" < 'z')")
// ClosedRange.contains(): BETWEEN operator
XCTAssertEqual(
sql(dbQueue, tableRequest.filter((1.1...10.9).contains(Col.id))),
"SELECT * FROM \"readers\" WHERE \"id\" BETWEEN 1.1 AND 10.9")
// ClosedRange.contains(): BETWEEN operator
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(("A"..."z").contains(Col.name))),
"SELECT * FROM \"readers\" WHERE \"name\" BETWEEN 'A' AND 'z'")
// !ClosedRange.contains(): NOT BETWEEN operator
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(!("A"..."z").contains(Col.name))),
"SELECT * FROM \"readers\" WHERE \"name\" NOT BETWEEN 'A' AND 'z'")
}
func testContainsCollated() throws {
let dbQueue = try makeDatabaseQueue()
try dbQueue.read { db in
// Reminder of the SQLite behavior
// https://sqlite.org/datatype3.html#assigning_collating_sequences_from_sql
// > If an explicit collating sequence is required on an IN operator
// > it should be applied to the left operand, like this:
// > "x COLLATE nocase IN (y,z, ...)".
try XCTAssertFalse(Bool.fetchOne(db, sql: "SELECT 'arthur' IN ('ARTHUR') COLLATE NOCASE")!)
try XCTAssertTrue(Bool.fetchOne(db, sql: "SELECT 'arthur' COLLATE NOCASE IN ('ARTHUR')")!)
}
// Array.contains(): IN operator
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(["arthur", "barbara"].contains(Col.name.collating(.nocase)))),
"SELECT * FROM \"readers\" WHERE (\"name\" COLLATE NOCASE) IN ('arthur', 'barbara')")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(!["arthur", "barbara"].contains(Col.name.collating(.nocase)))),
"SELECT * FROM \"readers\" WHERE (\"name\" COLLATE NOCASE) NOT IN ('arthur', 'barbara')")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter((["arthur", "barbara"] as [any SQLExpressible]).contains(Col.name.collating(.nocase)))),
"SELECT * FROM \"readers\" WHERE (\"name\" COLLATE NOCASE) IN ('arthur', 'barbara')")
// Sequence.contains(): IN operator
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(AnySequence(["arthur", "barbara"]).contains(Col.name.collating(.nocase)))),
"SELECT * FROM \"readers\" WHERE (\"name\" COLLATE NOCASE) IN ('arthur', 'barbara')")
// Sequence.contains(): = operator
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(AnySequence([Col.name]).contains(Col.name.collating(.nocase)))),
"SELECT * FROM \"readers\" WHERE \"name\" = \"name\" COLLATE NOCASE")
// Sequence.contains(): false
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(EmptyCollection<Int>().contains(Col.name.collating(.nocase)))),
"SELECT * FROM \"readers\" WHERE 0")
// ClosedInterval: BETWEEN operator
let closedInterval = "A"..."z"
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(closedInterval.contains(Col.name.collating(.nocase)))),
"SELECT * FROM \"readers\" WHERE \"name\" BETWEEN 'A' AND 'z' COLLATE NOCASE")
// HalfOpenInterval: min <= x < max
let halfOpenInterval = "A"..<"z"
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(halfOpenInterval.contains(Col.name.collating(.nocase)))),
"SELECT * FROM \"readers\" WHERE (\"name\" >= 'A' COLLATE NOCASE) AND (\"name\" < 'z' COLLATE NOCASE)")
}
func testCollatedContains() throws {
let dbQueue = try makeDatabaseQueue()
try dbQueue.read { db in
// Reminder of the SQLite behavior
// https://sqlite.org/datatype3.html#assigning_collating_sequences_from_sql
// > If an explicit collating sequence is required on an IN operator
// > it should be applied to the left operand, like this:
// > "x COLLATE nocase IN (y,z, ...)".
try XCTAssertFalse(Bool.fetchOne(db, sql: "SELECT 'arthur' IN ('ARTHUR') COLLATE NOCASE")!)
try XCTAssertTrue(Bool.fetchOne(db, sql: "SELECT 'arthur' COLLATE NOCASE IN ('ARTHUR')")!)
}
// Array.contains(): IN operator
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(["arthur", "barbara"].contains(Col.name).collating(.nocase))),
"SELECT * FROM \"readers\" WHERE (\"name\" COLLATE NOCASE) IN ('arthur', 'barbara')")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(!["arthur", "barbara"].contains(Col.name).collating(.nocase))),
"SELECT * FROM \"readers\" WHERE (\"name\" COLLATE NOCASE) NOT IN ('arthur', 'barbara')")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter((["arthur", "barbara"] as [any SQLExpressible]).contains(Col.name).collating(.nocase))),
"SELECT * FROM \"readers\" WHERE (\"name\" COLLATE NOCASE) IN ('arthur', 'barbara')")
// Sequence.contains(): IN operator
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(AnySequence(["arthur", "barbara"]).contains(Col.name).collating(.nocase))),
"SELECT * FROM \"readers\" WHERE (\"name\" COLLATE NOCASE) IN ('arthur', 'barbara')")
// Sequence.contains(): = operator
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(AnySequence([Col.name]).contains(Col.name).collating(.nocase))),
"SELECT * FROM \"readers\" WHERE \"name\" = \"name\" COLLATE NOCASE")
// Sequence.contains(): false
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(EmptyCollection<Int>().contains(Col.name).collating(.nocase))),
"SELECT * FROM \"readers\" WHERE 0 COLLATE NOCASE")
// ClosedInterval: BETWEEN operator
let closedInterval = "A"..."z"
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(closedInterval.contains(Col.name).collating(.nocase))),
"SELECT * FROM \"readers\" WHERE \"name\" BETWEEN 'A' AND 'z' COLLATE NOCASE")
// HalfOpenInterval: min <= x < max
let halfOpenInterval = "A"..<"z"
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(halfOpenInterval.contains(Col.name).collating(.nocase))),
"SELECT * FROM \"readers\" WHERE (\"name\" >= 'A' COLLATE NOCASE) AND (\"name\" < 'z' COLLATE NOCASE)")
}
func testSubqueryContains() throws {
let dbQueue = try makeDatabaseQueue()
do {
let subquery = tableRequest.select(Col.age).filter(Col.name != nil).distinct()
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(subquery.contains(Col.age))),
"""
SELECT * FROM "readers" WHERE "age" IN \
(SELECT DISTINCT "age" FROM "readers" WHERE "name" IS NOT NULL)
""")
}
do {
let subquery = SQLRequest<Int>(sql: "SELECT ? UNION SELECT ?", arguments: [1, 2])
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(subquery.contains(Col.age + 1))),
"""
SELECT * FROM "readers" WHERE ("age" + 1) IN (SELECT 1 UNION SELECT 2)
""")
}
do {
let subquery1 = tableRequest.select(max(Col.age))
let subquery2 = tableRequest.filter(Col.age == subquery1)
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(subquery2.select(Col.id).contains(Col.id))),
"""
SELECT * FROM "readers" WHERE "id" IN (\
SELECT "id" FROM "readers" WHERE "age" = (\
SELECT MAX("age") FROM "readers"))
""")
}
}
func testSubqueryExists() throws {
let dbQueue = try makeDatabaseQueue()
do {
try dbQueue.write { db in
try db.create(table: "team") { t in
t.autoIncrementedPrimaryKey("id")
}
try db.create(table: "player") { t in
t.column("teamID", .integer).references("team")
}
struct Player: TableRecord { }
struct Team: TableRecord { }
let teamAlias = TableAlias()
let player = Player.filter(Column("teamID") == teamAlias[Column("id")])
let teams = Team.aliased(teamAlias).filter(player.exists())
try assertEqualSQL(db, teams, """
SELECT * FROM "team" WHERE EXISTS (SELECT * FROM "player" WHERE "teamID" = "team"."id")
""")
}
}
do {
let alias = TableAlias(name: "r")
let subquery = tableRequest.filter(Col.age > alias[Col.age])
XCTAssertEqual(
sql(dbQueue, tableRequest.aliased(alias).filter(subquery.exists())),
"""
SELECT "r".* FROM "readers" "r" WHERE EXISTS (SELECT * FROM "readers" WHERE "age" > "r"."age")
""")
}
do {
let alias = TableAlias(name: "r")
let subquery = tableRequest.filter(Col.age > alias[Col.age])
XCTAssertEqual(
sql(dbQueue, tableRequest.aliased(alias).filter(!subquery.exists())),
"""
SELECT "r".* FROM "readers" "r" WHERE NOT EXISTS (SELECT * FROM "readers" WHERE "age" > "r"."age")
""")
}
do {
let alias = TableAlias(name: "r")
let subquery = SQLRequest<Row>("SELECT * FROM readers WHERE age > \(alias[Col.age])")
XCTAssertEqual(
sql(dbQueue, tableRequest.aliased(alias).filter(subquery.exists())),
"""
SELECT "r".* FROM "readers" "r" WHERE EXISTS (SELECT * FROM readers WHERE age > "r"."age")
""")
}
}
func testGreaterThan() throws {
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.age > 10)),
"SELECT * FROM \"readers\" WHERE \"age\" > 10")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(10 > Col.age)),
"SELECT * FROM \"readers\" WHERE 10 > \"age\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.filterWhenConnected { _ in 10 > 10 }),
"SELECT * FROM \"readers\" WHERE 0")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.age > Col.age)),
"SELECT * FROM \"readers\" WHERE \"age\" > \"age\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.name > "B")),
"SELECT * FROM \"readers\" WHERE \"name\" > 'B'")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter("B" > Col.name)),
"SELECT * FROM \"readers\" WHERE 'B' > \"name\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.filterWhenConnected { _ in "B" > "B" }),
"SELECT * FROM \"readers\" WHERE 0")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.name > Col.name)),
"SELECT * FROM \"readers\" WHERE \"name\" > \"name\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.group(Col.name).having(average(Col.age) + 10 > 20)),
"SELECT * FROM \"readers\" GROUP BY \"name\" HAVING (AVG(\"age\") + 10) > 20")
}
func testGreaterThanWithCollation() throws {
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.name.collating(.nocase) > "fOo")),
"SELECT * FROM \"readers\" WHERE \"name\" > 'fOo' COLLATE NOCASE")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.name.collating(collation) > "fOo")),
"SELECT * FROM \"readers\" WHERE \"name\" > 'fOo' COLLATE localized_case_insensitive")
}
func testGreaterThanOrEqual() throws {
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.age >= 10)),
"SELECT * FROM \"readers\" WHERE \"age\" >= 10")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(10 >= Col.age)),
"SELECT * FROM \"readers\" WHERE 10 >= \"age\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.filterWhenConnected { _ in 10 >= 10 }),
"SELECT * FROM \"readers\" WHERE 1")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.age >= Col.age)),
"SELECT * FROM \"readers\" WHERE \"age\" >= \"age\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.name >= "B")),
"SELECT * FROM \"readers\" WHERE \"name\" >= 'B'")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter("B" >= Col.name)),
"SELECT * FROM \"readers\" WHERE 'B' >= \"name\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.filterWhenConnected { _ in "B" >= "B" }),
"SELECT * FROM \"readers\" WHERE 1")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.name >= Col.name)),
"SELECT * FROM \"readers\" WHERE \"name\" >= \"name\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.group(Col.name).having(average(Col.age) + 10 >= 20)),
"SELECT * FROM \"readers\" GROUP BY \"name\" HAVING (AVG(\"age\") + 10) >= 20")
}
func testGreaterThanOrEqualWithCollation() throws {
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.name.collating(.nocase) >= "fOo")),
"SELECT * FROM \"readers\" WHERE \"name\" >= 'fOo' COLLATE NOCASE")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.name.collating(collation) >= "fOo")),
"SELECT * FROM \"readers\" WHERE \"name\" >= 'fOo' COLLATE localized_case_insensitive")
}
func testLessThan() throws {
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.age < 10)),
"SELECT * FROM \"readers\" WHERE \"age\" < 10")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(10 < Col.age)),
"SELECT * FROM \"readers\" WHERE 10 < \"age\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.filterWhenConnected { _ in 10 < 10 }),
"SELECT * FROM \"readers\" WHERE 0")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.age < Col.age)),
"SELECT * FROM \"readers\" WHERE \"age\" < \"age\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.name < "B")),
"SELECT * FROM \"readers\" WHERE \"name\" < 'B'")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter("B" < Col.name)),
"SELECT * FROM \"readers\" WHERE 'B' < \"name\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.filterWhenConnected { _ in "B" < "B" }),
"SELECT * FROM \"readers\" WHERE 0")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.name < Col.name)),
"SELECT * FROM \"readers\" WHERE \"name\" < \"name\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.group(Col.name).having(average(Col.age) + 10 < 20)),
"SELECT * FROM \"readers\" GROUP BY \"name\" HAVING (AVG(\"age\") + 10) < 20")
}
func testLessThanWithCollation() throws {
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.name.collating(.nocase) < "fOo")),
"SELECT * FROM \"readers\" WHERE \"name\" < 'fOo' COLLATE NOCASE")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.name.collating(collation) < "fOo")),
"SELECT * FROM \"readers\" WHERE \"name\" < 'fOo' COLLATE localized_case_insensitive")
}
func testLessThanOrEqual() throws {
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.age <= 10)),
"SELECT * FROM \"readers\" WHERE \"age\" <= 10")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(10 <= Col.age)),
"SELECT * FROM \"readers\" WHERE 10 <= \"age\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.filterWhenConnected { _ in 10 <= 10 }),
"SELECT * FROM \"readers\" WHERE 1")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.age <= Col.age)),
"SELECT * FROM \"readers\" WHERE \"age\" <= \"age\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.name <= "B")),
"SELECT * FROM \"readers\" WHERE \"name\" <= 'B'")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter("B" <= Col.name)),
"SELECT * FROM \"readers\" WHERE 'B' <= \"name\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.filterWhenConnected { _ in "B" <= "B" }),
"SELECT * FROM \"readers\" WHERE 1")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.name <= Col.name)),
"SELECT * FROM \"readers\" WHERE \"name\" <= \"name\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.group(Col.name).having(average(Col.age) + 10 <= 20)),
"SELECT * FROM \"readers\" GROUP BY \"name\" HAVING (AVG(\"age\") + 10) <= 20")
}
func testLessThanOrEqualWithCollation() throws {
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.name.collating(.nocase) <= "fOo")),
"SELECT * FROM \"readers\" WHERE \"name\" <= 'fOo' COLLATE NOCASE")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.name.collating(collation) <= "fOo")),
"SELECT * FROM \"readers\" WHERE \"name\" <= 'fOo' COLLATE localized_case_insensitive")
}
func testEqual() throws {
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.age == 10)),
"SELECT * FROM \"readers\" WHERE \"age\" = 10")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.age == (10 as Int?))),
"SELECT * FROM \"readers\" WHERE \"age\" = 10")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(10 == Col.age)),
"SELECT * FROM \"readers\" WHERE 10 = \"age\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter((10 as Int?) == Col.age)),
"SELECT * FROM \"readers\" WHERE 10 = \"age\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.filterWhenConnected { _ in 10 == 10 }),
"SELECT * FROM \"readers\" WHERE 1")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.age == Col.age)),
"SELECT * FROM \"readers\" WHERE \"age\" = \"age\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.age == nil)),
"SELECT * FROM \"readers\" WHERE \"age\" IS NULL")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.age == DatabaseValue.null)),
"SELECT * FROM \"readers\" WHERE \"age\" IS NULL")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(nil == Col.age)),
"SELECT * FROM \"readers\" WHERE \"age\" IS NULL")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(DatabaseValue.null == Col.age)),
"SELECT * FROM \"readers\" WHERE \"age\" IS NULL")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.age == Col.age)),
"SELECT * FROM \"readers\" WHERE \"age\" = \"age\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.name == "B")),
"SELECT * FROM \"readers\" WHERE \"name\" = 'B'")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter("B" == Col.name)),
"SELECT * FROM \"readers\" WHERE 'B' = \"name\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.filterWhenConnected { _ in "B" == "B" }),
"SELECT * FROM \"readers\" WHERE 1")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.name == Col.name)),
"SELECT * FROM \"readers\" WHERE \"name\" = \"name\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.age == true)),
"SELECT * FROM \"readers\" WHERE \"age\" = 1")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(true == Col.age)),
"SELECT * FROM \"readers\" WHERE \"age\" = 1")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.age == false)),
"SELECT * FROM \"readers\" WHERE \"age\" = 0")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(false == Col.age)),
"SELECT * FROM \"readers\" WHERE \"age\" = 0")
XCTAssertEqual(
sql(dbQueue, tableRequest.filterWhenConnected { _ in true == true }),
"SELECT * FROM \"readers\" WHERE 1")
XCTAssertEqual(
sql(dbQueue, tableRequest.filterWhenConnected { _ in false == false }),
"SELECT * FROM \"readers\" WHERE 1")
XCTAssertEqual(
sql(dbQueue, tableRequest.filterWhenConnected { _ in true == false }),
"SELECT * FROM \"readers\" WHERE 0")
}
func testEqualWithCollation() throws {
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.name.collating(.nocase) == "fOo")),
"SELECT * FROM \"readers\" WHERE \"name\" = 'fOo' COLLATE NOCASE")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.name.collating(.nocase) == ("fOo" as String?))),
"SELECT * FROM \"readers\" WHERE \"name\" = 'fOo' COLLATE NOCASE")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.name.collating(.nocase) == nil)),
"SELECT * FROM \"readers\" WHERE \"name\" IS NULL COLLATE NOCASE")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.name.collating(.nocase) == DatabaseValue.null)),
"SELECT * FROM \"readers\" WHERE \"name\" IS NULL COLLATE NOCASE")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.name.collating(collation) == "fOo")),
"SELECT * FROM \"readers\" WHERE \"name\" = 'fOo' COLLATE localized_case_insensitive")
}
func testSubqueryEqual() throws {
let dbQueue = try makeDatabaseQueue()
do {
let subquery = tableRequest.select(max(Col.age))
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.age == subquery)),
"""
SELECT * FROM "readers" WHERE "age" = (SELECT MAX("age") FROM "readers")
""")
}
do {
let subquery = SQLRequest<Int>(sql: "SELECT MAX(age + ?) FROM readers", arguments: [1])
XCTAssertEqual(
sql(dbQueue, tableRequest.filter((Col.age + 2) == subquery)),
"""
SELECT * FROM "readers" WHERE ("age" + 2) = (SELECT MAX(age + 1) FROM readers)
""")
}
}
func testSubqueryWithOuterAlias() throws {
let dbQueue = try makeDatabaseQueue()
try dbQueue.write { db in
try db.create(table: "parent") { t in
t.autoIncrementedPrimaryKey("id")
t.belongsTo("parent")
}
try db.create(table: "child") { t in
t.column("childParentId", .integer).references("parent")
}
}
struct Parent: TableRecord {
static let parent = belongsTo(Parent.self)
}
struct Child: TableRecord { }
do {
let parentAlias = TableAlias()
// Some ugly subquery whose only purpose is to use a table alias
// which requires disambiguation in the parent query.
let subquery = Child.select(sql: "COUNT(*)").filter(Column("childParentId") == parentAlias[Column("id")])
let request = Parent
.joining(optional: Parent.parent.aliased(parentAlias))
.filter(subquery > 1)
XCTAssertEqual(
sql(dbQueue, request),
"""
SELECT "parent1".* FROM "parent" "parent1" \
LEFT JOIN "parent" "parent2" ON "parent2"."id" = "parent1"."parentId" \
WHERE (SELECT COUNT(*) FROM "child" WHERE "childParentId" = "parent2"."id") > 1
""")
}
}
func testNotEqual() throws {
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.age != 10)),
"SELECT * FROM \"readers\" WHERE \"age\" <> 10")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.age != (10 as Int?))),
"SELECT * FROM \"readers\" WHERE \"age\" <> 10")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(10 != Col.age)),
"SELECT * FROM \"readers\" WHERE 10 <> \"age\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter((10 as Int?) != Col.age)),
"SELECT * FROM \"readers\" WHERE 10 <> \"age\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.filterWhenConnected { _ in 10 != 10 }),
"SELECT * FROM \"readers\" WHERE 0")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.age != Col.age)),
"SELECT * FROM \"readers\" WHERE \"age\" <> \"age\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(!(Col.age == 10))),
"SELECT * FROM \"readers\" WHERE \"age\" <> 10")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(!(Col.age == (10 as Int?)))),
"SELECT * FROM \"readers\" WHERE \"age\" <> 10")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(!(10 == Col.age))),
"SELECT * FROM \"readers\" WHERE 10 <> \"age\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(!((10 as Int?) == Col.age))),
"SELECT * FROM \"readers\" WHERE 10 <> \"age\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.filterWhenConnected { _ in !(10 == 10) }),
"SELECT * FROM \"readers\" WHERE 0")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(!(Col.age == Col.age))),
"SELECT * FROM \"readers\" WHERE \"age\" <> \"age\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.age != nil)),
"SELECT * FROM \"readers\" WHERE \"age\" IS NOT NULL")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.age != DatabaseValue.null)),
"SELECT * FROM \"readers\" WHERE \"age\" IS NOT NULL")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(nil != Col.age)),
"SELECT * FROM \"readers\" WHERE \"age\" IS NOT NULL")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(DatabaseValue.null != Col.age)),
"SELECT * FROM \"readers\" WHERE \"age\" IS NOT NULL")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.age != Col.age)),
"SELECT * FROM \"readers\" WHERE \"age\" <> \"age\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(!(Col.age == nil))),
"SELECT * FROM \"readers\" WHERE \"age\" IS NOT NULL")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(!(Col.age == DatabaseValue.null))),
"SELECT * FROM \"readers\" WHERE \"age\" IS NOT NULL")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(!(nil == Col.age))),
"SELECT * FROM \"readers\" WHERE \"age\" IS NOT NULL")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(!(DatabaseValue.null == Col.age))),
"SELECT * FROM \"readers\" WHERE \"age\" IS NOT NULL")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(!(Col.age == Col.age))),
"SELECT * FROM \"readers\" WHERE \"age\" <> \"age\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.name != "B")),
"SELECT * FROM \"readers\" WHERE \"name\" <> 'B'")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter("B" != Col.name)),
"SELECT * FROM \"readers\" WHERE 'B' <> \"name\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.filterWhenConnected { _ in "B" != "B" }),
"SELECT * FROM \"readers\" WHERE 0")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.name != Col.name)),
"SELECT * FROM \"readers\" WHERE \"name\" <> \"name\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(!(Col.name == "B"))),
"SELECT * FROM \"readers\" WHERE \"name\" <> 'B'")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(!("B" == Col.name))),
"SELECT * FROM \"readers\" WHERE 'B' <> \"name\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.filterWhenConnected { _ in !("B" == "B") }),
"SELECT * FROM \"readers\" WHERE 0")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(!(Col.name == Col.name))),
"SELECT * FROM \"readers\" WHERE \"name\" <> \"name\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.age != true)),
"SELECT * FROM \"readers\" WHERE \"age\" <> 1")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(true != Col.age)),
"SELECT * FROM \"readers\" WHERE \"age\" <> 1")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.age != false)),
"SELECT * FROM \"readers\" WHERE \"age\" <> 0")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(false != Col.age)),
"SELECT * FROM \"readers\" WHERE \"age\" <> 0")
XCTAssertEqual(
sql(dbQueue, tableRequest.filterWhenConnected { _ in true != true }),
"SELECT * FROM \"readers\" WHERE 0")
XCTAssertEqual(
sql(dbQueue, tableRequest.filterWhenConnected { _ in false != false }),
"SELECT * FROM \"readers\" WHERE 0")
XCTAssertEqual(
sql(dbQueue, tableRequest.filterWhenConnected { _ in true != false }),
"SELECT * FROM \"readers\" WHERE 1")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(!(Col.age == true))),
"SELECT * FROM \"readers\" WHERE \"age\" <> 1")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(!(true == Col.age))),
"SELECT * FROM \"readers\" WHERE \"age\" <> 1")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(!(Col.age == false))),
"SELECT * FROM \"readers\" WHERE \"age\" <> 0")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(!(false == Col.age))),
"SELECT * FROM \"readers\" WHERE \"age\" <> 0")
XCTAssertEqual(
sql(dbQueue, tableRequest.filterWhenConnected { _ in !(true == true) }),
"SELECT * FROM \"readers\" WHERE 0")
XCTAssertEqual(
sql(dbQueue, tableRequest.filterWhenConnected { _ in !(false == false) }),
"SELECT * FROM \"readers\" WHERE 0")
XCTAssertEqual(
sql(dbQueue, tableRequest.filterWhenConnected { _ in !(true == false) }),
"SELECT * FROM \"readers\" WHERE 1")
}
func testNotEqualWithCollation() throws {
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.name.collating(.nocase) != "fOo")),
"SELECT * FROM \"readers\" WHERE \"name\" <> 'fOo' COLLATE NOCASE")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.name.collating(.nocase) != ("fOo" as String?))),
"SELECT * FROM \"readers\" WHERE \"name\" <> 'fOo' COLLATE NOCASE")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.name.collating(.nocase) != nil)),
"SELECT * FROM \"readers\" WHERE \"name\" IS NOT NULL COLLATE NOCASE")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.name.collating(.nocase) != DatabaseValue.null)),
"SELECT * FROM \"readers\" WHERE \"name\" IS NOT NULL COLLATE NOCASE")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.name.collating(collation) != "fOo")),
"SELECT * FROM \"readers\" WHERE \"name\" <> 'fOo' COLLATE localized_case_insensitive")
}
func testNotEqualWithSwiftNotOperator() throws {
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(!(Col.age == 10))),
"SELECT * FROM \"readers\" WHERE \"age\" <> 10")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(!(10 == Col.age))),
"SELECT * FROM \"readers\" WHERE 10 <> \"age\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.filterWhenConnected { _ in !(10 == 10) }),
"SELECT * FROM \"readers\" WHERE 0")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(!(Col.age == Col.age))),
"SELECT * FROM \"readers\" WHERE \"age\" <> \"age\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(!(Col.age == nil))),
"SELECT * FROM \"readers\" WHERE \"age\" IS NOT NULL")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(!(Col.age == DatabaseValue.null))),
"SELECT * FROM \"readers\" WHERE \"age\" IS NOT NULL")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(!(nil == Col.age))),
"SELECT * FROM \"readers\" WHERE \"age\" IS NOT NULL")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(!(DatabaseValue.null == Col.age))),
"SELECT * FROM \"readers\" WHERE \"age\" IS NOT NULL")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(!(Col.age == Col.age))),
"SELECT * FROM \"readers\" WHERE \"age\" <> \"age\"")
}
func testIs() throws {
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.age === 10)),
"SELECT * FROM \"readers\" WHERE \"age\" IS 10")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(10 === Col.age)),
"SELECT * FROM \"readers\" WHERE 10 IS \"age\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.age === Col.age)),
"SELECT * FROM \"readers\" WHERE \"age\" IS \"age\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.age === nil)),
"SELECT * FROM \"readers\" WHERE \"age\" IS NULL")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(nil === Col.age)),
"SELECT * FROM \"readers\" WHERE \"age\" IS NULL")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.age === Col.age)),
"SELECT * FROM \"readers\" WHERE \"age\" IS \"age\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.name === "B")),
"SELECT * FROM \"readers\" WHERE \"name\" IS 'B'")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter("B" === Col.name)),
"SELECT * FROM \"readers\" WHERE 'B' IS \"name\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.name === Col.name)),
"SELECT * FROM \"readers\" WHERE \"name\" IS \"name\"")
}
func testIsWithCollation() throws {
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.name.collating(.nocase) === "fOo")),
"SELECT * FROM \"readers\" WHERE \"name\" IS 'fOo' COLLATE NOCASE")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.name.collating(collation) === "fOo")),
"SELECT * FROM \"readers\" WHERE \"name\" IS 'fOo' COLLATE localized_case_insensitive")
}
func testIsNot() throws {
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.age !== 10)),
"SELECT * FROM \"readers\" WHERE \"age\" IS NOT 10")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(10 !== Col.age)),
"SELECT * FROM \"readers\" WHERE 10 IS NOT \"age\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.age !== Col.age)),
"SELECT * FROM \"readers\" WHERE \"age\" IS NOT \"age\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.age !== nil)),
"SELECT * FROM \"readers\" WHERE \"age\" IS NOT NULL")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(nil !== Col.age)),
"SELECT * FROM \"readers\" WHERE \"age\" IS NOT NULL")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.age !== Col.age)),
"SELECT * FROM \"readers\" WHERE \"age\" IS NOT \"age\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.name !== "B")),
"SELECT * FROM \"readers\" WHERE \"name\" IS NOT 'B'")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter("B" !== Col.name)),
"SELECT * FROM \"readers\" WHERE 'B' IS NOT \"name\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.name !== Col.name)),
"SELECT * FROM \"readers\" WHERE \"name\" IS NOT \"name\"")
}
func testIsNotWithCollation() throws {
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.name.collating(.nocase) !== "fOo")),
"SELECT * FROM \"readers\" WHERE \"name\" IS NOT 'fOo' COLLATE NOCASE")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.name.collating(collation) !== "fOo")),
"SELECT * FROM \"readers\" WHERE \"name\" IS NOT 'fOo' COLLATE localized_case_insensitive")
}
func testIsNotWithSwiftNotOperator() throws {
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(!(Col.age === 10))),
"SELECT * FROM \"readers\" WHERE \"age\" IS NOT 10")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(!(10 === Col.age))),
"SELECT * FROM \"readers\" WHERE 10 IS NOT \"age\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(!(Col.age === Col.age))),
"SELECT * FROM \"readers\" WHERE \"age\" IS NOT \"age\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(!(Col.age === nil))),
"SELECT * FROM \"readers\" WHERE \"age\" IS NOT NULL")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(!(nil === Col.age))),
"SELECT * FROM \"readers\" WHERE \"age\" IS NOT NULL")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(!(Col.age === Col.age))),
"SELECT * FROM \"readers\" WHERE \"age\" IS NOT \"age\"")
}
func testLogicalOperators() throws {
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(!Col.age)),
"SELECT * FROM \"readers\" WHERE NOT \"age\"")
// Make sure NOT NOT "hack" is available in order to produce 0 or 1
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(!(!Col.age))),
"SELECT * FROM \"readers\" WHERE NOT (NOT \"age\")")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.age && true)),
"SELECT * FROM \"readers\" WHERE \"age\" AND 1")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(true && Col.age)),
"SELECT * FROM \"readers\" WHERE 1 AND \"age\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.age || false)),
"SELECT * FROM \"readers\" WHERE \"age\" OR 0")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(false || Col.age)),
"SELECT * FROM \"readers\" WHERE 0 OR \"age\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.age != nil || Col.name == nil)),
"SELECT * FROM \"readers\" WHERE (\"age\" IS NOT NULL) OR (\"name\" IS NULL)")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.age != nil || Col.name != nil && Col.id != nil)),
"SELECT * FROM \"readers\" WHERE (\"age\" IS NOT NULL) OR ((\"name\" IS NOT NULL) AND (\"id\" IS NOT NULL))")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter((Col.age != nil || Col.name != nil) && Col.id != nil)),
"SELECT * FROM \"readers\" WHERE ((\"age\" IS NOT NULL) OR (\"name\" IS NOT NULL)) AND (\"id\" IS NOT NULL)")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(!(Col.age > 18) && !(Col.name > "foo"))),
"SELECT * FROM \"readers\" WHERE (NOT (\"age\" > 18)) AND (NOT (\"name\" > 'foo'))")
}
func testJoinedOperatorAnd() throws {
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, tableRequest.filter([].joined(operator: .and))),
"SELECT * FROM \"readers\" WHERE 1")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter([].joined(operator: .and) || false)),
"SELECT * FROM \"readers\" WHERE 1 OR 0")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter([false.databaseValue].joined(operator: .and))),
"SELECT * FROM \"readers\" WHERE 0")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter([false.databaseValue].joined(operator: .and) || false)),
"SELECT * FROM \"readers\" WHERE 0 OR 0")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter([Col.id == 1].joined(operator: .and))),
"SELECT * FROM \"readers\" WHERE \"id\" = 1")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter([Col.id == 1].joined(operator: .and) || false)),
"SELECT * FROM \"readers\" WHERE (\"id\" = 1) OR 0")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter([Col.id == 1, Col.name != nil].joined(operator: .and))),
"SELECT * FROM \"readers\" WHERE (\"id\" = 1) AND (\"name\" IS NOT NULL)")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter([Col.id == 1, Col.name != nil].joined(operator: .and) || false)),
"SELECT * FROM \"readers\" WHERE ((\"id\" = 1) AND (\"name\" IS NOT NULL)) OR 0")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter([Col.id == 1, Col.name != nil, Col.age == nil].joined(operator: .and))),
"SELECT * FROM \"readers\" WHERE (\"id\" = 1) AND (\"name\" IS NOT NULL) AND (\"age\" IS NULL)")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter([Col.id == 1, Col.name != nil, Col.age == nil].joined(operator: .and) || false)),
"SELECT * FROM \"readers\" WHERE ((\"id\" = 1) AND (\"name\" IS NOT NULL) AND (\"age\" IS NULL)) OR 0")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(![Col.id == 1, Col.name != nil, Col.age == nil].joined(operator: .and))),
"SELECT * FROM \"readers\" WHERE NOT ((\"id\" = 1) AND (\"name\" IS NOT NULL) AND (\"age\" IS NULL))")
}
func testJoinedOperatorOr() throws {
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, tableRequest.filter([].joined(operator: .or))),
"SELECT * FROM \"readers\" WHERE 0")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter([].joined(operator: .or) && true)),
"SELECT * FROM \"readers\" WHERE 0 AND 1")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter([true.databaseValue].joined(operator: .or))),
"SELECT * FROM \"readers\" WHERE 1")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter([true.databaseValue].joined(operator: .or) && true)),
"SELECT * FROM \"readers\" WHERE 1 AND 1")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter([Col.id == 1].joined(operator: .or))),
"SELECT * FROM \"readers\" WHERE \"id\" = 1")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter([Col.id == 1].joined(operator: .or) && true)),
"SELECT * FROM \"readers\" WHERE (\"id\" = 1) AND 1")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter([Col.id == 1, Col.name != nil].joined(operator: .or))),
"SELECT * FROM \"readers\" WHERE (\"id\" = 1) OR (\"name\" IS NOT NULL)")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter([Col.id == 1, Col.name != nil].joined(operator: .or) && true)),
"SELECT * FROM \"readers\" WHERE ((\"id\" = 1) OR (\"name\" IS NOT NULL)) AND 1")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter([Col.id == 1, Col.name != nil, Col.age == nil].joined(operator: .or))),
"SELECT * FROM \"readers\" WHERE (\"id\" = 1) OR (\"name\" IS NOT NULL) OR (\"age\" IS NULL)")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter([Col.id == 1, Col.name != nil, Col.age == nil].joined(operator: .or) && true)),
"SELECT * FROM \"readers\" WHERE ((\"id\" = 1) OR (\"name\" IS NOT NULL) OR (\"age\" IS NULL)) AND 1")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(![Col.id == 1, Col.name != nil, Col.age == nil].joined(operator: .or))),
"SELECT * FROM \"readers\" WHERE NOT ((\"id\" = 1) OR (\"name\" IS NOT NULL) OR (\"age\" IS NULL))")
}
// MARK: - String functions
func testStringFunctions() throws {
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, tableRequest.select(Col.name.capitalized)),
"SELECT swiftCapitalizedString(\"name\") FROM \"readers\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.select(Col.name.lowercased)),
"SELECT swiftLowercaseString(\"name\") FROM \"readers\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.select(Col.name.uppercased)),
"SELECT swiftUppercaseString(\"name\") FROM \"readers\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.select(Col.name.localizedCapitalized)),
"SELECT swiftLocalizedCapitalizedString(\"name\") FROM \"readers\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.select(Col.name.localizedLowercased)),
"SELECT swiftLocalizedLowercaseString(\"name\") FROM \"readers\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.select(Col.name.localizedUppercased)),
"SELECT swiftLocalizedUppercaseString(\"name\") FROM \"readers\"")
}
// MARK: - Arithmetic expressions
func testPrefixMinusOperator() throws {
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(-Col.age)),
"SELECT * FROM \"readers\" WHERE -\"age\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(-(Col.age + 1))),
"SELECT * FROM \"readers\" WHERE -(\"age\" + 1)")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(-(-Col.age + 1))),
"SELECT * FROM \"readers\" WHERE -((-\"age\") + 1)")
}
func testInfixSubtractOperator() throws {
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.age - 2)),
"SELECT * FROM \"readers\" WHERE \"age\" - 2")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(2 - Col.age)),
"SELECT * FROM \"readers\" WHERE 2 - \"age\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.filterWhenConnected { _ in 2 - 2 }),
"SELECT * FROM \"readers\" WHERE 0")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.age - Col.age)),
"SELECT * FROM \"readers\" WHERE \"age\" - \"age\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter((Col.age - Col.age) - 1)),
"SELECT * FROM \"readers\" WHERE (\"age\" - \"age\") - 1")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(1 - [Col.age > 1, Col.age == nil].joined(operator: .and))),
"SELECT * FROM \"readers\" WHERE 1 - ((\"age\" > 1) AND (\"age\" IS NULL))")
}
func testInfixAddOperator() throws {
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.age + 2)),
"SELECT * FROM \"readers\" WHERE \"age\" + 2")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(2 + Col.age)),
"SELECT * FROM \"readers\" WHERE 2 + \"age\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.filterWhenConnected { _ in 2 + 2 }),
"SELECT * FROM \"readers\" WHERE 4")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.age + Col.age)),
"SELECT * FROM \"readers\" WHERE \"age\" + \"age\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(1 + (Col.age + Col.age))),
"SELECT * FROM \"readers\" WHERE 1 + (\"age\" + \"age\")")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(1 + [Col.age > 1, Col.age == nil].joined(operator: .and))),
"SELECT * FROM \"readers\" WHERE 1 + ((\"age\" > 1) AND (\"age\" IS NULL))")
}
func testJoinedAddOperator() throws {
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, tableRequest.select([].joined(operator: .add))),
"SELECT 0 FROM \"readers\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.select([Col.age, Col.age].joined(operator: .add))),
"SELECT \"age\" + \"age\" FROM \"readers\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.select([Col.age, 2.databaseValue, Col.age].joined(operator: .add))),
"SELECT \"age\" + 2 + \"age\" FROM \"readers\"")
// Not flattened
XCTAssertEqual(
sql(dbQueue, tableRequest.select([
[Col.age, 1.databaseValue].joined(operator: .add),
[2.databaseValue, Col.age].joined(operator: .add),
].joined(operator: .add))),
"SELECT (\"age\" + 1) + (2 + \"age\") FROM \"readers\"")
}
func testInfixMultiplyOperator() throws {
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.age * 2)),
"SELECT * FROM \"readers\" WHERE \"age\" * 2")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(2 * Col.age)),
"SELECT * FROM \"readers\" WHERE 2 * \"age\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.filterWhenConnected { _ in 2 * 2 }),
"SELECT * FROM \"readers\" WHERE 4")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.age * Col.age)),
"SELECT * FROM \"readers\" WHERE \"age\" * \"age\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(2 * (Col.age * Col.age))),
"SELECT * FROM \"readers\" WHERE 2 * (\"age\" * \"age\")")
}
func testJoinedMultiplyOperator() throws {
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, tableRequest.select([].joined(operator: .multiply))),
"SELECT 1 FROM \"readers\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.select([Col.age, Col.age].joined(operator: .multiply))),
"SELECT \"age\" * \"age\" FROM \"readers\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.select([Col.age, 2.databaseValue, Col.age].joined(operator: .multiply))),
"SELECT \"age\" * 2 * \"age\" FROM \"readers\"")
// Not flattened
XCTAssertEqual(
sql(dbQueue, tableRequest.select([
[Col.age, 1.databaseValue].joined(operator: .multiply),
[2.databaseValue, Col.age].joined(operator: .multiply),
].joined(operator: .multiply))),
"SELECT (\"age\" * 1) * (2 * \"age\") FROM \"readers\"")
}
func testInfixDivideOperator() throws {
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.age / 2)),
"SELECT * FROM \"readers\" WHERE \"age\" / 2")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(2 / Col.age)),
"SELECT * FROM \"readers\" WHERE 2 / \"age\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.filterWhenConnected { _ in 2 / 2 }),
"SELECT * FROM \"readers\" WHERE 1")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.age / Col.age)),
"SELECT * FROM \"readers\" WHERE \"age\" / \"age\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(1 / (Col.age / Col.age))),
"SELECT * FROM \"readers\" WHERE 1 / (\"age\" / \"age\")")
}
func testCompoundArithmeticExpression() throws {
let dbQueue = try makeDatabaseQueue()
// Int / Double
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.age / 2.0)),
"SELECT * FROM \"readers\" WHERE \"age\" / 2.0")
// Double / Int
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(2.0 / Col.age)),
"SELECT * FROM \"readers\" WHERE 2.0 / \"age\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.age + 2 * 5)),
"SELECT * FROM \"readers\" WHERE \"age\" + 10")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.age * 2 + 5)),
"SELECT * FROM \"readers\" WHERE (\"age\" * 2) + 5")
}
// MARK: - Bitwise expressions
func testPrefixBitwiseNotOperator() throws {
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(~Col.age)),
"SELECT * FROM \"readers\" WHERE ~\"age\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(~(Col.age + 1))),
"SELECT * FROM \"readers\" WHERE ~(\"age\" + 1)")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(~(~Col.age + 1))),
"SELECT * FROM \"readers\" WHERE ~((~\"age\") + 1)")
}
func testInfixLeftShiftOperator() throws {
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.age << 2)),
"SELECT * FROM \"readers\" WHERE \"age\" << 2")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(2 << Col.age)),
"SELECT * FROM \"readers\" WHERE 2 << \"age\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.filterWhenConnected { _ in 2 << 2 }),
"SELECT * FROM \"readers\" WHERE 8")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.age << Col.age)),
"SELECT * FROM \"readers\" WHERE \"age\" << \"age\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter((Col.age << Col.age) << 1)),
"SELECT * FROM \"readers\" WHERE (\"age\" << \"age\") << 1")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(1 << [Col.age > 1, Col.age == nil].joined(operator: .and))),
"SELECT * FROM \"readers\" WHERE 1 << ((\"age\" > 1) AND (\"age\" IS NULL))")
}
func testInfixRightShiftOperator() throws {
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.age >> 2)),
"SELECT * FROM \"readers\" WHERE \"age\" >> 2")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(2 >> Col.age)),
"SELECT * FROM \"readers\" WHERE 2 >> \"age\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.filterWhenConnected { _ in 8 >> 2 }),
"SELECT * FROM \"readers\" WHERE 2")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.age >> Col.age)),
"SELECT * FROM \"readers\" WHERE \"age\" >> \"age\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter((Col.age >> Col.age) >> 1)),
"SELECT * FROM \"readers\" WHERE (\"age\" >> \"age\") >> 1")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(1 >> [Col.age > 1, Col.age == nil].joined(operator: .and))),
"SELECT * FROM \"readers\" WHERE 1 >> ((\"age\" > 1) AND (\"age\" IS NULL))")
}
func testInfixBitwiseAndOperator() throws {
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.age & 2)),
"SELECT * FROM \"readers\" WHERE \"age\" & 2")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(2 & Col.age)),
"SELECT * FROM \"readers\" WHERE 2 & \"age\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.filterWhenConnected { _ in 2 & 2 }),
"SELECT * FROM \"readers\" WHERE 2")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.age & Col.age)),
"SELECT * FROM \"readers\" WHERE \"age\" & \"age\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(2 & (Col.age & Col.age))),
"SELECT * FROM \"readers\" WHERE 2 & \"age\" & \"age\"")
}
func testJoinedBitwiseAndOperator() throws {
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, tableRequest.select([].joined(operator: .bitwiseAnd))),
"SELECT -1 FROM \"readers\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.select([Col.age, Col.age].joined(operator: .bitwiseAnd))),
"SELECT \"age\" & \"age\" FROM \"readers\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.select([Col.age, 2.databaseValue, Col.age].joined(operator: .bitwiseAnd))),
"SELECT \"age\" & 2 & \"age\" FROM \"readers\"")
// Flattened
XCTAssertEqual(
sql(dbQueue, tableRequest.select([
[Col.age, 1.databaseValue].joined(operator: .bitwiseAnd),
[2.databaseValue, Col.age].joined(operator: .bitwiseAnd),
].joined(operator: .bitwiseAnd))),
"SELECT \"age\" & 1 & 2 & \"age\" FROM \"readers\"")
}
func testInfixBitwiseOrOperator() throws {
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.age | 2)),
"SELECT * FROM \"readers\" WHERE \"age\" | 2")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(2 | Col.age)),
"SELECT * FROM \"readers\" WHERE 2 | \"age\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.filterWhenConnected { _ in 2 | 2 }),
"SELECT * FROM \"readers\" WHERE 2")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.age | Col.age)),
"SELECT * FROM \"readers\" WHERE \"age\" | \"age\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(2 | (Col.age | Col.age))),
"SELECT * FROM \"readers\" WHERE 2 | \"age\" | \"age\"")
}
func testJoinedBitwiseOrOperator() throws {
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, tableRequest.select([].joined(operator: .bitwiseOr))),
"SELECT 0 FROM \"readers\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.select([Col.age, Col.age].joined(operator: .bitwiseOr))),
"SELECT \"age\" | \"age\" FROM \"readers\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.select([Col.age, 2.databaseValue, Col.age].joined(operator: .bitwiseOr))),
"SELECT \"age\" | 2 | \"age\" FROM \"readers\"")
// Flattened
XCTAssertEqual(
sql(dbQueue, tableRequest.select([
[Col.age, 1.databaseValue].joined(operator: .bitwiseOr),
[2.databaseValue, Col.age].joined(operator: .bitwiseOr),
].joined(operator: .bitwiseOr))),
"SELECT \"age\" | 1 | 2 | \"age\" FROM \"readers\"")
}
// MARK: - IFNULL expression
func testIfNull() throws {
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.age ?? 2)),
"SELECT * FROM \"readers\" WHERE IFNULL(\"age\", 2)")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.age ?? Col.age)),
"SELECT * FROM \"readers\" WHERE IFNULL(\"age\", \"age\")")
}
// MARK: - Aggregated expressions
func testCountExpression() throws {
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, tableRequest.select(count(Col.age))),
"SELECT COUNT(\"age\") FROM \"readers\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.select(count(Col.age ?? 0))),
"SELECT COUNT(IFNULL(\"age\", 0)) FROM \"readers\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.select(count(distinct: Col.age))),
"SELECT COUNT(DISTINCT \"age\") FROM \"readers\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.select(count(distinct: Col.age / Col.age))),
"SELECT COUNT(DISTINCT \"age\" / \"age\") FROM \"readers\"")
}
func testAvgExpression() throws {
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, tableRequest.select(average(Col.age))),
"SELECT AVG(\"age\") FROM \"readers\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.select(average(Col.age / 2))),
"SELECT AVG(\"age\" / 2) FROM \"readers\"")
}
func testAvgExpression_filter() throws {
#if GRDBCUSTOMSQLITE || GRDBCIPHER
// Prevent SQLCipher failures
guard sqlite3_libversion_number() >= 3030000 else {
throw XCTSkip("FILTER clause on aggregate functions is not available")
}
#else
guard #available(iOS 14, macOS 10.16, tvOS 14, watchOS 7, *) else {
throw XCTSkip("FILTER clause on aggregate functions is not available")
}
#endif
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, tableRequest.select(average(Col.age, filter: Col.age > 0))),
"SELECT AVG(\"age\") FILTER (WHERE \"age\" > 0) FROM \"readers\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.select(average(Col.age / 2, filter: Col.age > 0))),
"SELECT AVG(\"age\" / 2) FILTER (WHERE \"age\" > 0) FROM \"readers\"")
}
func testCastExpression() throws {
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, tableRequest.select(cast(Col.name, as: .blob))),
"SELECT CAST(\"name\" AS BLOB) FROM \"readers\"")
}
func testLengthExpression() throws {
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, tableRequest.select(length(Col.name))),
"SELECT LENGTH(\"name\") FROM \"readers\"")
}
func testMinExpression() throws {
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, tableRequest.select(min(Col.age))),
"SELECT MIN(\"age\") FROM \"readers\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.select(min(Col.age / 2))),
"SELECT MIN(\"age\" / 2) FROM \"readers\"")
}
func testMinExpression_filter() throws {
#if GRDBCUSTOMSQLITE || GRDBCIPHER
// Prevent SQLCipher failures
guard sqlite3_libversion_number() >= 3030000 else {
throw XCTSkip("FILTER clause on aggregate functions is not available")
}
#else
guard #available(iOS 14, macOS 10.16, tvOS 14, watchOS 7, *) else {
throw XCTSkip("FILTER clause on aggregate functions is not available")
}
#endif
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, tableRequest.select(min(Col.age, filter: Col.age > 0))),
"SELECT MIN(\"age\") FILTER (WHERE \"age\" > 0) FROM \"readers\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.select(min(Col.age / 2, filter: Col.age > 0))),
"SELECT MIN(\"age\" / 2) FILTER (WHERE \"age\" > 0) FROM \"readers\"")
}
func testMaxExpression() throws {
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, tableRequest.select(max(Col.age))),
"SELECT MAX(\"age\") FROM \"readers\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.select(max(Col.age / 2))),
"SELECT MAX(\"age\" / 2) FROM \"readers\"")
}
func testMaxExpression_filter() throws {
#if GRDBCUSTOMSQLITE || GRDBCIPHER
// Prevent SQLCipher failures
guard sqlite3_libversion_number() >= 3030000 else {
throw XCTSkip("FILTER clause on aggregate functions is not available")
}
#else
guard #available(iOS 14, macOS 10.16, tvOS 14, watchOS 7, *) else {
throw XCTSkip("FILTER clause on aggregate functions is not available")
}
#endif
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, tableRequest.select(max(Col.age, filter: Col.age < 0))),
"SELECT MAX(\"age\") FILTER (WHERE \"age\" < 0) FROM \"readers\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.select(max(Col.age / 2, filter: Col.age < 0))),
"SELECT MAX(\"age\" / 2) FILTER (WHERE \"age\" < 0) FROM \"readers\"")
}
func testSumExpression() throws {
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, tableRequest.select(sum(Col.age))),
"SELECT SUM(\"age\") FROM \"readers\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.select(sum(Col.age / 2))),
"SELECT SUM(\"age\" / 2) FROM \"readers\"")
}
func testSumExpression_filter() throws {
#if GRDBCUSTOMSQLITE || GRDBCIPHER
// Prevent SQLCipher failures
guard sqlite3_libversion_number() >= 3030000 else {
throw XCTSkip("FILTER clause on aggregate functions is not available")
}
#else
guard #available(iOS 14, macOS 10.16, tvOS 14, watchOS 7, *) else {
throw XCTSkip("FILTER clause on aggregate functions is not available")
}
#endif
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, tableRequest.select(sum(Col.age, filter: Col.age > 0))),
"SELECT SUM(\"age\") FILTER (WHERE \"age\" > 0) FROM \"readers\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.select(sum(Col.age / 2, filter: Col.age > 0))),
"SELECT SUM(\"age\" / 2) FILTER (WHERE \"age\" > 0) FROM \"readers\"")
}
#if GRDBCUSTOMSQLITE || GRDBCIPHER
func testSumExpression_order() throws {
// Prevent SQLCipher failures
guard sqlite3_libversion_number() >= 3044000 else {
throw XCTSkip("ORDER BY clause on aggregate functions is not available")
}
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, tableRequest.select(sum(Col.age, orderBy: Col.age))),
"SELECT SUM(\"age\" ORDER BY \"age\") FROM \"readers\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.select(sum(Col.age / 2, orderBy: Col.age.desc))),
"SELECT SUM(\"age\" / 2 ORDER BY \"age\" DESC) FROM \"readers\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.select(sum(Col.age, orderBy: Col.age, filter: Col.age > 0))),
"SELECT SUM(\"age\" ORDER BY \"age\") FILTER (WHERE \"age\" > 0) FROM \"readers\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.select(sum(Col.age / 2, orderBy: Col.age.desc, filter: Col.age > 0))),
"SELECT SUM(\"age\" / 2 ORDER BY \"age\" DESC) FILTER (WHERE \"age\" > 0) FROM \"readers\"")
}
#endif
func testTotalExpression() throws {
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, tableRequest.select(total(Col.age))),
"SELECT TOTAL(\"age\") FROM \"readers\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.select(total(Col.age / 2))),
"SELECT TOTAL(\"age\" / 2) FROM \"readers\"")
}
func testTotalExpression_filter() throws {
#if GRDBCUSTOMSQLITE || GRDBCIPHER
// Prevent SQLCipher failures
guard sqlite3_libversion_number() >= 3030000 else {
throw XCTSkip("FILTER clause on aggregate functions is not available")
}
#else
guard #available(iOS 14, macOS 10.16, tvOS 14, watchOS 7, *) else {
throw XCTSkip("FILTER clause on aggregate functions is not available")
}
#endif
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, tableRequest.select(total(Col.age, filter: Col.age > 0))),
"SELECT TOTAL(\"age\") FILTER (WHERE \"age\" > 0) FROM \"readers\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.select(total(Col.age / 2, filter: Col.age > 0))),
"SELECT TOTAL(\"age\" / 2) FILTER (WHERE \"age\" > 0) FROM \"readers\"")
}
#if GRDBCUSTOMSQLITE || GRDBCIPHER
func testTotalExpression_order() throws {
// Prevent SQLCipher failures
guard sqlite3_libversion_number() >= 3044000 else {
throw XCTSkip("ORDER BY clause on aggregate functions is not available")
}
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, tableRequest.select(total(Col.age, orderBy: Col.age))),
"SELECT TOTAL(\"age\" ORDER BY \"age\") FROM \"readers\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.select(total(Col.age / 2, orderBy: Col.age.desc))),
"SELECT TOTAL(\"age\" / 2 ORDER BY \"age\" DESC) FROM \"readers\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.select(total(Col.age, orderBy: Col.age, filter: Col.age > 0))),
"SELECT TOTAL(\"age\" ORDER BY \"age\") FILTER (WHERE \"age\" > 0) FROM \"readers\"")
XCTAssertEqual(
sql(dbQueue, tableRequest.select(total(Col.age / 2, orderBy: Col.age.desc, filter: Col.age > 0))),
"SELECT TOTAL(\"age\" / 2 ORDER BY \"age\" DESC) FILTER (WHERE \"age\" > 0) FROM \"readers\"")
}
#endif
// MARK: - LIKE operator
func testLikeOperator() throws {
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.name.like("%foo"))),
"SELECT * FROM \"readers\" WHERE \"name\" LIKE '%foo'")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(!Col.name.like("%foo"))),
"SELECT * FROM \"readers\" WHERE \"name\" NOT LIKE '%foo'")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.name.like("%foo") == true)),
"SELECT * FROM \"readers\" WHERE (\"name\" LIKE '%foo') = 1")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.name.like("%foo") == false)),
"SELECT * FROM \"readers\" WHERE (\"name\" LIKE '%foo') = 0")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.name.like("%foo", escape: "\\"))),
"SELECT * FROM \"readers\" WHERE \"name\" LIKE '%foo' ESCAPE '\\'")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(!Col.name.like("%foo", escape: "\\"))),
"SELECT * FROM \"readers\" WHERE \"name\" NOT LIKE '%foo' ESCAPE '\\'")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.name.like("%foo", escape: "\\") == true)),
"SELECT * FROM \"readers\" WHERE (\"name\" LIKE '%foo' ESCAPE '\\') = 1")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter(Col.name.like("%foo", escape: "\\") == false)),
"SELECT * FROM \"readers\" WHERE (\"name\" LIKE '%foo' ESCAPE '\\') = 0")
}
// MARK: - || concat operator
func testConcatOperator() throws {
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, tableRequest.select([Col.name, Col.name].joined(operator: .concat))),
"""
SELECT "name" || "name" FROM "readers"
""")
XCTAssertEqual(
sql(dbQueue, tableRequest.filter([Col.name, Col.name].joined(operator: .concat) == "foo")),
"""
SELECT * FROM "readers" WHERE ("name" || "name") = 'foo'
""")
XCTAssertEqual(
sql(dbQueue, tableRequest.select([Col.name, " ".databaseValue, Col.name].joined(operator: .concat))),
"""
SELECT "name" || ' ' || "name" FROM "readers"
""")
// Flattened
XCTAssertEqual(
sql(dbQueue, tableRequest.select([
[Col.name, "a".databaseValue].joined(operator: .concat),
["b".databaseValue, Col.name].joined(operator: .concat),
].joined(operator: .concat))),
"""
SELECT "name" || 'a' || 'b' || "name" FROM "readers"
""")
}
// MARK: - Function
func testCustomFunction() throws {
let dbQueue = try makeDatabaseQueue()
XCTAssertEqual(
sql(dbQueue, tableRequest.select(customFunction(Col.age, 1, 2))),
"SELECT avgOf(\"age\", 1, 2) FROM \"readers\"")
}
// MARK: - SQLExpressionFastPrimaryKey
func testFastPrimaryKeyExpression() throws {
struct IntegerPrimaryKeyRecord: TableRecord { }
struct UUIDRecord: TableRecord { }
struct UUIDRecordWithoutRowID: TableRecord { }
struct RowIDRecord: TableRecord { }
struct CompoundPrimaryKeyRecord: TableRecord { }
let dbQueue = try makeDatabaseQueue()
try dbQueue.write { db in
try db.execute(sql: """
CREATE TABLE integerPrimaryKeyRecord (id INTEGER PRIMARY KEY);
CREATE TABLE uuidRecord (uuid TEXT PRIMARY KEY);
CREATE TABLE uuidRecordWithoutRowID (uuid TEXT PRIMARY KEY) WITHOUT ROWID;
CREATE TABLE rowIDRecord (name TEXT);
CREATE TABLE compoundPrimaryKeyRecord (a INTEGER, b INTEGER, PRIMARY KEY (a, b));
""")
try assertEqualSQL(db, IntegerPrimaryKeyRecord.select(SQLExpression.fastPrimaryKey), """
SELECT "id" FROM "integerPrimaryKeyRecord"
""")
try assertEqualSQL(db, UUIDRecord.select(SQLExpression.fastPrimaryKey), """
SELECT "rowid" FROM "uuidRecord"
""")
try assertEqualSQL(db, UUIDRecordWithoutRowID.select(SQLExpression.fastPrimaryKey), """
SELECT "uuid" FROM "uuidRecordWithoutRowID"
""")
try assertEqualSQL(db, RowIDRecord.select(SQLExpression.fastPrimaryKey), """
SELECT "rowid" FROM "rowIDRecord"
""")
try assertEqualSQL(db, CompoundPrimaryKeyRecord.select(SQLExpression.fastPrimaryKey), """
SELECT "rowid" FROM "compoundPrimaryKeyRecord"
""")
}
}
}