import XCTest import GRDB class ColumnExpressionTests: GRDBTestCase { func testRawColumnExpression() throws { struct Player: TableRecord, FetchableRecord, PersistableRecord { var id: Int64 var name: String var score: Int struct Column: ColumnExpression { var name: String } enum Columns { static let id = Player.Column(name: "id") static let name = Player.Column(name: "name") static let score = Player.Column(name: "score") } // Test databaseSelection static let databaseSelection: [any SQLSelectable] = [Columns.id, Columns.name, Columns.score] init(row: Row) { // Test row subscript id = row[Columns.id] name = row[Columns.name] score = row[Columns.score] } func encode(to container: inout PersistenceContainer) { // Test container subscript container[Columns.id] = id container[Columns.name] = name container[Columns.score] = score } static var testRequest: QueryInterfaceRequest { // Test expression derivation return filter(Columns.name != nil).order(Columns.score.desc) } } let dbQueue = try makeDatabaseQueue() try dbQueue.inDatabase { db in try db.create(table: "player") { t in t.autoIncrementedPrimaryKey("id") t.column("name") t.column("score") } // Test rowId column identification try XCTAssertEqual(Player.filter(key: 1).databaseRegion(db).description, "player(id,name,score)[1]") try XCTAssertEqual(Player.filter(Player.Columns.id == 1).databaseRegion(db).description, "player(id,name,score)[1]") try XCTAssertEqual(Player.filter(1 == Player.Columns.id).databaseRegion(db).description, "player(id,name,score)[1]") try XCTAssertEqual(Player.filter(Player.Columns.id == 1 || Player.Columns.id == 2).databaseRegion(db).description, "player(id,name,score)[1,2]") try XCTAssertEqual(Player.filter([1, 2, 3].contains(Player.Columns.id)).databaseRegion(db).description, "player(id,name,score)[1,2,3]") // Test specific column updates let player = Player(row: ["id": 1, "name": "Arthur", "score": 1000]) try? player.update(db, columns: [Player.Columns.name, Player.Columns.score]) XCTAssertEqual(lastSQLQuery, "UPDATE \"player\" SET \"name\"=\'Arthur\', \"score\"=1000 WHERE \"id\"=1") // Test compound expression let expression = Player.Columns.name == "foo" let request = Player.select(expression) try assertEqualSQL(db, request, "SELECT \"name\" = 'foo' FROM \"player\"") } } func testRawRepresentableColumnExpression() throws { struct Player: TableRecord, FetchableRecord, PersistableRecord { var id: Int64 var name: String var score: Int enum Columns: String, ColumnExpression { case id, name, score } // Test databaseSelection static let databaseSelection: [any SQLSelectable] = [Columns.id, Columns.name, Columns.score] init(row: Row) { // Test row subscript id = row[Columns.id] name = row[Columns.name] score = row[Columns.score] } func encode(to container: inout PersistenceContainer) { // Test container subscript container[Columns.id] = id container[Columns.name] = name container[Columns.score] = score } static var testRequest: QueryInterfaceRequest { // Test expression derivation return filter(Columns.name != nil).order(Columns.score.desc) } } let dbQueue = try makeDatabaseQueue() try dbQueue.inDatabase { db in try db.create(table: "player") { t in t.autoIncrementedPrimaryKey("id") t.column("name") t.column("score") } // Test rowId column identification try XCTAssertEqual(Player.filter(key: 1).databaseRegion(db).description, "player(id,name,score)[1]") try XCTAssertEqual(Player.filter(Player.Columns.id == 1).databaseRegion(db).description, "player(id,name,score)[1]") try XCTAssertEqual(Player.filter(1 == Player.Columns.id).databaseRegion(db).description, "player(id,name,score)[1]") try XCTAssertEqual(Player.filter(Player.Columns.id == 1 || Player.Columns.id == 2).databaseRegion(db).description, "player(id,name,score)[1,2]") try XCTAssertEqual(Player.filter([1, 2, 3].contains(Player.Columns.id)).databaseRegion(db).description, "player(id,name,score)[1,2,3]") // Test specific column updates let player = Player(row: ["id": 1, "name": "Arthur", "score": 1000]) try? player.update(db, columns: [Player.Columns.name, Player.Columns.score]) XCTAssertEqual(lastSQLQuery, "UPDATE \"player\" SET \"name\"=\'Arthur\', \"score\"=1000 WHERE \"id\"=1") // Test compound expression let expression = Player.Columns.name == "foo" let request = Player.select(expression) try assertEqualSQL(db, request, "SELECT \"name\" = 'foo' FROM \"player\"") } } func testColumnsDerivedFromCodingKeys() throws { struct Player: Codable, TableRecord, FetchableRecord, PersistableRecord { var id: Int64 var name: String var score: Int enum CodingKeys: String, CodingKey { case id case name = "full_name" case score } enum Columns { static let id = Column(CodingKeys.id) static let name = Column(CodingKeys.name) static let score = Column(CodingKeys.score) } // Test databaseSelection static let databaseSelection: [any SQLSelectable] = [Columns.id, Columns.name, Columns.score] static var testRequest: QueryInterfaceRequest { // Test expression derivation return filter(Columns.name != nil).order(Columns.score.desc) } } let dbQueue = try makeDatabaseQueue() try dbQueue.inDatabase { db in try db.create(table: "player") { t in t.autoIncrementedPrimaryKey("id") t.column("full_name") t.column("score") } // Test rowId column identification try XCTAssertEqual(Player.filter(key: 1).databaseRegion(db).description, "player(full_name,id,score)[1]") try XCTAssertEqual(Player.filter(Player.Columns.id == 1).databaseRegion(db).description, "player(full_name,id,score)[1]") try XCTAssertEqual(Player.filter(1 == Player.Columns.id).databaseRegion(db).description, "player(full_name,id,score)[1]") try XCTAssertEqual(Player.filter(Player.Columns.id == 1 || Player.Columns.id == 2).databaseRegion(db).description, "player(full_name,id,score)[1,2]") try XCTAssertEqual(Player.filter([1, 2, 3].contains(Player.Columns.id)).databaseRegion(db).description, "player(full_name,id,score)[1,2,3]") // Test specific column updates let player = try Player(row: ["id": 1, "full_name": "Arthur", "score": 1000]) try? player.update(db, columns: [Player.Columns.name, Player.Columns.score]) XCTAssertEqual(lastSQLQuery, "UPDATE \"player\" SET \"full_name\"=\'Arthur\', \"score\"=1000 WHERE \"id\"=1") // Test compound expression let expression = Player.Columns.name == "foo" let request = Player.select(expression) try assertEqualSQL(db, request, "SELECT \"full_name\" = 'foo' FROM \"player\"") } } func testCodingKeysAsColumnExpression() throws { struct Player: Codable, TableRecord, FetchableRecord, PersistableRecord { var id: Int64 var name: String var score: Int enum CodingKeys: String, CodingKey, ColumnExpression { case id case name = "full_name" case score } // Test databaseSelection static let databaseSelection: [any SQLSelectable] = [CodingKeys.id, CodingKeys.name, CodingKeys.score] static var testRequest: QueryInterfaceRequest { // Test expression derivation return filter(CodingKeys.name != nil).order(CodingKeys.score.desc) } } let dbQueue = try makeDatabaseQueue() try dbQueue.inDatabase { db in try db.create(table: "player") { t in t.autoIncrementedPrimaryKey("id") t.column("full_name") t.column("score") } // Test rowId column identification try XCTAssertEqual(Player.filter(key: 1).databaseRegion(db).description, "player(full_name,id,score)[1]") try XCTAssertEqual(Player.filter(Player.CodingKeys.id == 1).databaseRegion(db).description, "player(full_name,id,score)[1]") try XCTAssertEqual(Player.filter(1 == Player.CodingKeys.id).databaseRegion(db).description, "player(full_name,id,score)[1]") try XCTAssertEqual(Player.filter(Player.CodingKeys.id == 1 || Player.CodingKeys.id == 2).databaseRegion(db).description, "player(full_name,id,score)[1,2]") try XCTAssertEqual(Player.filter([1, 2, 3].contains(Player.CodingKeys.id)).databaseRegion(db).description, "player(full_name,id,score)[1,2,3]") // Test specific column updates let player = try Player(row: ["id": 1, "full_name": "Arthur", "score": 1000]) try? player.update(db, columns: [Player.CodingKeys.name, Player.CodingKeys.score]) XCTAssertEqual(lastSQLQuery, "UPDATE \"player\" SET \"full_name\"=\'Arthur\', \"score\"=1000 WHERE \"id\"=1") // Test compound expression let expression = Player.CodingKeys.name == "foo" let request = Player.select(expression) try assertEqualSQL(db, request, "SELECT \"full_name\" = 'foo' FROM \"player\"") } } func testDetachedColumn() throws { let dbQueue = try makeDatabaseQueue() try dbQueue.write { db in try db.create(table: "t") { t in t.column("a") } // Regular columns are qualified do { let alias = TableAlias(name: "custom") let request = Table("t") .aliased(alias) .order(Column("a")) let sql = try request.makePreparedRequest(db).statement.sql XCTAssertEqual(sql, """ SELECT "custom".* FROM "t" "custom" ORDER BY "custom"."a" """) } // Detached columns are NOT qualified do { let alias = TableAlias(name: "custom") let request = Table("t") .aliased(alias) .order(Column("a").detached) let sql = try request.makePreparedRequest(db).statement.sql XCTAssertEqual(sql, """ SELECT "custom".* FROM "t" "custom" ORDER BY "a" """) } // Detached columns are quoted do { let alias = TableAlias(name: "custom") let request = Table("t") .aliased(alias) .select(Column("a").forKey("order")) .order(Column("order").detached) let sql = try request.makePreparedRequest(db).statement.sql XCTAssertEqual(sql, """ SELECT "custom"."a" AS "order" FROM "t" "custom" ORDER BY "order" """) } } } }