Skip to content

Commit 87b236d

Browse files
committed
selectID
This reverts 7ffc332 At the time, selectID was replaced with selectPrimaryKey(as:) because: > Since Optional is a database value like others, selectID would be a request of optionals for records with an optional ID type: > > struct Player: Identifiable, MutablePersistableRecord { > var id: Int64? > } > > Player.selectID() // QueryInterface<Int64?> > > This is not good, because primary keys are not null. And those requests would fetch impractical types such as [Int64?], Set<Int64?>, Int64??. This problem is reintroduced, but we now recommend against optional ids in https://swiftpackageindex.com/groue/grdb.swift/documentation/grdb/recordrecommendedpractices. And record with optional ids can use selectPrimaryKey(as:).
1 parent ea6d8e0 commit 87b236d

10 files changed

+140
-11
lines changed

GRDB/Documentation.docc/RecordRecommendedPractices.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -393,16 +393,16 @@ Extensions to the `DerivableRequest` protocol can not change the type of request
393393
394394
```swift
395395
extension QueryInterfaceRequest<Author> {
396-
// Selects author ids
397-
func selectId() -> QueryInterfaceRequest<Int64> {
398-
selectPrimaryKey(as: Int64.self)
396+
// Selects authors' name
397+
func selectName() -> QueryInterfaceRequest<String> {
398+
select(Author.Columns.name)
399399
}
400400
}
401401
402-
// The ids of Japanese authors
403-
let ids: Set<Int64> = try Author.all()
402+
// The names of Japanese authors
403+
let names: Set<String> = try Author.all()
404404
.filter(countryCode: "JP")
405-
.selectId()
405+
.selectName()
406406
.fetchSet(db)
407407
```
408408

GRDB/QueryInterface/Request/QueryInterfaceRequest.swift

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
/// - ``select(_:as:)-3o8qw``
5757
/// - ``select(literal:as:)``
5858
/// - ``select(sql:arguments:as:)``
59+
/// - ``selectID()``
5960
/// - ``selectPrimaryKey(as:)``
6061
///
6162
/// ### Batch Delete
@@ -237,7 +238,7 @@ extension QueryInterfaceRequest: SelectionRequest {
237238
select(sqlLiteral, as: type)
238239
}
239240

240-
/// Selects the primary key.
241+
/// Returns a request that selects the primary key.
241242
///
242243
/// All primary keys are supported:
243244
///
@@ -281,6 +282,26 @@ extension QueryInterfaceRequest: SelectionRequest {
281282
.asRequest(of: PrimaryKey.self)
282283
}
283284

285+
/// Returns a request that selects the primary key.
286+
///
287+
/// For example:
288+
///
289+
/// ```swift
290+
/// // SELECT id FROM player WHERE ...
291+
/// let request = try Player.filter(...).selectID()
292+
/// ```
293+
public func selectID() -> QueryInterfaceRequest<RowDecoder.ID>
294+
where RowDecoder: Identifiable
295+
{
296+
selectWhenConnected { db in
297+
let primaryKey = try db.primaryKey(self.databaseTableName)
298+
GRDBPrecondition(
299+
primaryKey.columns.count == 1,
300+
"selectID requires a single-column primary key in the table \(self.databaseTableName)")
301+
return [Column(primaryKey.columns[0])]
302+
}.asRequest(of: RowDecoder.ID.self)
303+
}
304+
284305
public func annotatedWhenConnected(
285306
with selection: @escaping @Sendable (Database) throws -> [any SQLSelectable])
286307
-> Self

GRDB/QueryInterface/SQL/Table.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@
9494
/// - ``select(literal:as:)``
9595
/// - ``select(sql:arguments:)``
9696
/// - ``select(sql:arguments:as:)``
97+
/// - ``selectID()``
9798
/// - ``selectPrimaryKey(as:)``
9899
/// - ``with(_:)``
99100
///
@@ -424,6 +425,22 @@ extension Table {
424425
all().selectPrimaryKey(as: type)
425426
}
426427

428+
/// Returns a request that selects the primary key.
429+
///
430+
/// For example:
431+
///
432+
/// ```swift
433+
/// let table = Table<Player>("player")
434+
///
435+
/// // SELECT id FROM player
436+
/// let request = try table.selectID()
437+
/// ```
438+
public func selectID() -> QueryInterfaceRequest<RowDecoder.ID>
439+
where RowDecoder: Identifiable
440+
{
441+
all().selectID()
442+
}
443+
427444
/// Returns a request with the provided result columns appended to the
428445
/// table columns.
429446
///

GRDB/QueryInterface/TableRecord+QueryInterfaceRequest.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,20 @@ extension TableRecord {
251251
all().selectPrimaryKey(as: type)
252252
}
253253

254+
/// Returns a request that selects the primary key.
255+
///
256+
/// For example:
257+
///
258+
/// ```swift
259+
/// // SELECT id FROM player
260+
/// let request = try Player.selectID()
261+
/// ```
262+
public static func selectID() -> QueryInterfaceRequest<Self.ID>
263+
where Self: Identifiable
264+
{
265+
all().selectID()
266+
}
267+
254268
/// Returns a request with the provided result columns appended to the
255269
/// record selection.
256270
///

GRDB/Record/TableRecord.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ import Foundation
9898
/// - ``select(literal:as:)``
9999
/// - ``select(sql:arguments:)``
100100
/// - ``select(sql:arguments:as:)``
101+
/// - ``selectID()``
101102
/// - ``selectPrimaryKey(as:)``
102103
/// - ``with(_:)``
103104
///

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3341,6 +3341,16 @@ You can now build requests with the following methods: `all`, `none`, `select`,
33413341
Player.select(nameColumn, as: String.self)
33423342
```
33433343

3344+
- [`selectID()`](https://swiftpackageindex.com/groue/GRDB.swift/documentation/grdb/queryinterfacerequest/selectID()) is available on [Identifiable Records]. It supports all tables that have a single-column primary key:
3345+
3346+
```swift
3347+
// SELECT id FROM player
3348+
Player.selectID()
3349+
3350+
// SELECT id FROM player WHERE name IS NOT NULL
3351+
Player.filter(nameColumn != nil).selectID()
3352+
```
3353+
33443354
- [`annotated(with: expression...)`](https://swiftpackageindex.com/groue/GRDB.swift/documentation/grdb/selectionrequest/annotated(with:)-6ehs4) extends the selection.
33453355

33463356
```swift

Tests/GRDBTests/RecordMinimalNonOptionalPrimaryKeySingleTests.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -800,6 +800,16 @@ class RecordMinimalNonOptionalPrimaryKeySingleTests: GRDBTestCase {
800800
}
801801
}
802802

803+
func test_static_selectID() throws {
804+
let dbQueue = try makeDatabaseQueue()
805+
try dbQueue.inDatabase { db in
806+
let record = MinimalNonOptionalPrimaryKeySingle(id: "theUUID")
807+
try record.insert(db)
808+
let ids: [String] = try MinimalSingle.selectID().fetchAll(db)
809+
XCTAssertEqual(ids, ["theUUID"])
810+
}
811+
}
812+
803813
func test_request_selectPrimaryKey() throws {
804814
let dbQueue = try makeDatabaseQueue()
805815
try dbQueue.inDatabase { db in
@@ -811,4 +821,14 @@ class RecordMinimalNonOptionalPrimaryKeySingleTests: GRDBTestCase {
811821
XCTAssertEqual(rows, [["id": "theUUID"]])
812822
}
813823
}
824+
825+
func test_request_selectID() throws {
826+
let dbQueue = try makeDatabaseQueue()
827+
try dbQueue.inDatabase { db in
828+
let record = MinimalNonOptionalPrimaryKeySingle(id: "theUUID")
829+
try record.insert(db)
830+
let ids: [String] = try MinimalSingle.all().selectID().fetchAll(db)
831+
XCTAssertEqual(ids, ["theUUID"])
832+
}
833+
}
814834
}

Tests/GRDBTests/RecordMinimalPrimaryKeyRowIDTests.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -844,6 +844,16 @@ class RecordMinimalPrimaryKeyRowIDTests : GRDBTestCase {
844844
}
845845
}
846846

847+
func test_static_selectID() throws {
848+
let dbQueue = try makeDatabaseQueue()
849+
try dbQueue.inDatabase { db in
850+
let record = MinimalRowID()
851+
try record.insert(db)
852+
let ids: [Int64?] = try MinimalRowID.selectID().fetchAll(db)
853+
XCTAssertEqual(ids, [1])
854+
}
855+
}
856+
847857
func test_request_selectPrimaryKey() throws {
848858
let dbQueue = try makeDatabaseQueue()
849859
try dbQueue.inDatabase { db in
@@ -855,4 +865,14 @@ class RecordMinimalPrimaryKeyRowIDTests : GRDBTestCase {
855865
XCTAssertEqual(rows, [["id": 1]])
856866
}
857867
}
868+
869+
func test_request_selectID() throws {
870+
let dbQueue = try makeDatabaseQueue()
871+
try dbQueue.inDatabase { db in
872+
let record = MinimalRowID()
873+
try record.insert(db)
874+
let ids: [Int64?] = try MinimalRowID.all().selectID().fetchAll(db)
875+
XCTAssertEqual(ids, [1])
876+
}
877+
}
858878
}

Tests/GRDBTests/RecordMinimalPrimaryKeySingleTests.swift

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -375,7 +375,7 @@ class RecordMinimalPrimaryKeySingleTests: GRDBTestCase {
375375
XCTAssertEqual(lastSQLQuery, "SELECT * FROM \"minimalSingles\" WHERE \"UUID\" = '\(record.UUID!)'")
376376
}
377377
}
378-
378+
379379

380380
// MARK: - Fetch With Key Request
381381

@@ -661,7 +661,7 @@ class RecordMinimalPrimaryKeySingleTests: GRDBTestCase {
661661
_ = try MinimalSingle.find(db, key: id)
662662
XCTFail("Expected RecordError")
663663
} catch RecordError.recordNotFound(databaseTableName: "minimalSingles", key: ["UUID": .null]) { }
664-
664+
665665
do {
666666
let fetchedRecord = try MinimalSingle.find(db, key: record.UUID)
667667
XCTAssertTrue(fetchedRecord.UUID == record.UUID)
@@ -680,7 +680,7 @@ class RecordMinimalPrimaryKeySingleTests: GRDBTestCase {
680680
}
681681
}
682682
}
683-
683+
684684

685685
// MARK: - Fetch With Primary Key Request
686686

@@ -861,7 +861,7 @@ class RecordMinimalPrimaryKeySingleTests: GRDBTestCase {
861861
}
862862

863863
// MARK: Select PrimaryKey
864-
864+
865865
func test_static_selectPrimaryKey() throws {
866866
let dbQueue = try makeDatabaseQueue()
867867
try dbQueue.inDatabase { db in
@@ -875,6 +875,17 @@ class RecordMinimalPrimaryKeySingleTests: GRDBTestCase {
875875
}
876876
}
877877

878+
func test_static_selectID() throws {
879+
let dbQueue = try makeDatabaseQueue()
880+
try dbQueue.inDatabase { db in
881+
let record = MinimalSingle()
882+
record.UUID = "theUUID"
883+
try record.insert(db)
884+
let ids: [String] = try MinimalSingle.selectID().fetchAll(db)
885+
XCTAssertEqual(ids, ["theUUID"])
886+
}
887+
}
888+
878889
func test_request_selectPrimaryKey() throws {
879890
let dbQueue = try makeDatabaseQueue()
880891
try dbQueue.inDatabase { db in
@@ -887,4 +898,15 @@ class RecordMinimalPrimaryKeySingleTests: GRDBTestCase {
887898
XCTAssertEqual(rows, [["UUID": "theUUID"]])
888899
}
889900
}
901+
902+
func test_request_selectID() throws {
903+
let dbQueue = try makeDatabaseQueue()
904+
try dbQueue.inDatabase { db in
905+
let record = MinimalSingle()
906+
record.UUID = "theUUID"
907+
try record.insert(db)
908+
let ids: [String] = try MinimalSingle.all().selectID().fetchAll(db)
909+
XCTAssertEqual(ids, ["theUUID"])
910+
}
911+
}
890912
}

Tests/GRDBTests/TableTests.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,8 @@ class TableTests: GRDBTestCase {
133133
try assertEqualSQL(db, t.filter(ids: [1, 2, 3]), """
134134
SELECT * FROM "player" WHERE "id" IN (1, 2, 3)
135135
""")
136+
137+
try XCTAssertEqual(t.selectID().fetchOne(db), 1)
136138
}
137139

138140
do {
@@ -148,6 +150,8 @@ class TableTests: GRDBTestCase {
148150
try assertEqualSQL(db, t.filter(ids: [1, 2, 3]), """
149151
SELECT * FROM "player" WHERE "id" IN (1, 2, 3)
150152
""")
153+
154+
try XCTAssertEqual(t.selectID().fetchOne(db), 1)
151155
}
152156
}
153157
}

0 commit comments

Comments
 (0)