Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 16 additions & 25 deletions Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

28 changes: 14 additions & 14 deletions SharingGRDB.xcworkspace/xcshareddata/swiftpm/Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

53 changes: 47 additions & 6 deletions Sources/StructuredQueriesGRDBCore/QueryCursor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,29 @@ public class QueryCursor<Element>: DatabaseCursor {
public func _element(sqliteStatement _: SQLiteStatement) throws -> Element {
fatalError("Abstract method should be overridden in subclass")
}

@usableFromInline
struct DecodingError: Error, CustomStringConvertible {
let columnIndex: Int
let columnName: String
let sql: String

@usableFromInline
init(columnIndex: Int, columnName: String, sql: String) {
self.columnIndex = columnIndex
self.columnName = columnName
self.sql = sql
}

@usableFromInline
var description: String {
"""
Expected column \(columnIndex) (\(columnName.debugDescription)) to not be NULL: …

\(sql)
"""
}
}
}

@usableFromInline
Expand All @@ -40,9 +63,18 @@ final class QueryValueCursor<QueryValue: QueryRepresentable>: QueryCursor<QueryV

@inlinable
public override func _element(sqliteStatement _: SQLiteStatement) throws -> Element {
let element = try QueryValue(decoder: &decoder).queryOutput
decoder.next()
return element
do {
let element = try QueryValue(decoder: &decoder).queryOutput
decoder.next()
return element
} catch QueryDecodingError.missingRequiredColumn {
let columnIndex = Int(decoder.currentIndex) - 1
throw DecodingError(
columnIndex: columnIndex,
columnName: _statement.columnNames[columnIndex],
sql: _statement.sql
)
}
}
}

Expand All @@ -62,9 +94,18 @@ final class QueryPackCursor<

@inlinable
public override func _element(sqliteStatement _: SQLiteStatement) throws -> Element {
let element = try decoder.decodeColumns((repeat each QueryValue).self)
decoder.next()
return element
do {
let element = try decoder.decodeColumns((repeat each QueryValue).self)
decoder.next()
return element
} catch QueryDecodingError.missingRequiredColumn {
let columnIndex = Int(decoder.currentIndex) - 1
throw DecodingError(
columnIndex: columnIndex,
columnName: _statement.columnNames[columnIndex],
sql: _statement.sql
)
}
}
}

Expand Down
34 changes: 27 additions & 7 deletions Tests/SharingGRDBTests/FetchAllTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ struct FetchAllTests {
for index in 1...count {
group.addTask {
try await database.write { db in
try Record.insert(Record(id: index)).execute(db)
try Record.insert { Record(id: index) }.execute(db)
}
}
}
Expand All @@ -31,7 +31,7 @@ struct FetchAllTests {
#expect(records == (1...count).map { Record(id: $0) })

await withThrowingTaskGroup { group in
for index in 1...(count/2) {
for index in 1...(count / 2) {
group.addTask {
try await database.write { db in
try Record.find(index * 2).delete().execute(db)
Expand All @@ -41,7 +41,27 @@ struct FetchAllTests {
}

try await $records.load()
#expect(records == (0...(count/2-1)).map { Record(id: $0 * 2 + 1) })
#expect(records == (0...(count / 2 - 1)).map { Record(id: $0 * 2 + 1) })
}

@Test func fetchFailure() {
do {
try database.read { db in
_ =
try Record
.select { ($0.id, $0.date, #sql("\($0.optionalDate)", as: Date.self)) }
.fetchAll(db)
}
Issue.record()
} catch {
#expect(
"\(error)".contains(
"""
Expected column 2 ("optionalDate") to not be NULL
"""
)
)
}
}
}

Expand All @@ -60,15 +80,15 @@ extension DatabaseWriter where Self == DatabaseQueue {
try #sql(
"""
CREATE TABLE "records" (
"id" INTEGER PRIMARY KEY AUTOINCREMENT
, "date" INTEGER NOT NULL DEFAULT 42
, "optionalDate" INTEGER
"id" INTEGER PRIMARY KEY AUTOINCREMENT,
"date" INTEGER NOT NULL DEFAULT 42,
"optionalDate" INTEGER
)
"""
)
.execute(db)
for _ in 1...3 {
_ = try Record.insert(Record.Draft()).execute(db)
_ = try Record.insert { Record.Draft() }.execute(db)
}
}
return database
Expand Down
2 changes: 1 addition & 1 deletion Tests/SharingGRDBTests/FetchTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ extension DatabaseWriter where Self == DatabaseQueue {
)
.execute(db)
for _ in 1...3 {
_ = try Record.insert(Record.Draft()).execute(db)
_ = try Record.insert { Record.Draft() }.execute(db)
}
}
try migrator.migrate(database)
Expand Down
12 changes: 6 additions & 6 deletions Tests/SharingGRDBTests/IntegrationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,19 @@ struct IntegrationTests {
#expect(syncUps == [])

try await database.write { db in
_ = try SyncUp.insert(SyncUp.Draft(isActive: true, title: "Engineering"))
_ = try SyncUp.insert { SyncUp.Draft(isActive: true, title: "Engineering") }
.execute(db)
}
try await $syncUps.load()
#expect(syncUps == [SyncUp(id: 1, isActive: true, title: "Engineering")])
try await database.write { db in
_ = try SyncUp.upsert(SyncUp.Draft(id: 1, isActive: false, title: "Engineering"))
_ = try SyncUp.upsert { SyncUp.Draft(id: 1, isActive: false, title: "Engineering") }
.execute(db)
}
try await $syncUps.load()
#expect(syncUps == [])
try await database.write { db in
_ = try SyncUp.upsert(SyncUp.Draft(id: 1, isActive: true, title: "Engineering"))
_ = try SyncUp.upsert { SyncUp.Draft(id: 1, isActive: true, title: "Engineering") }
.execute(db)
}
try await $syncUps.load()
Expand All @@ -40,19 +40,19 @@ struct IntegrationTests {
#expect(syncUps == [])

try await database.write { db in
_ = try SyncUp.insert(SyncUp.Draft(isActive: true, title: "Engineering"))
_ = try SyncUp.insert { SyncUp.Draft(isActive: true, title: "Engineering") }
.execute(db)
}
try await $syncUps.load()
#expect(syncUps == [SyncUp(id: 1, isActive: true, title: "Engineering")])
try await database.write { db in
_ = try SyncUp.upsert(SyncUp.Draft(id: 1, isActive: false, title: "Engineering"))
_ = try SyncUp.upsert { SyncUp.Draft(id: 1, isActive: false, title: "Engineering") }
.execute(db)
}
try await $syncUps.load()
#expect(syncUps == [])
try await database.write { db in
_ = try SyncUp.upsert(SyncUp.Draft(id: 1, isActive: true, title: "Engineering"))
_ = try SyncUp.upsert { SyncUp.Draft(id: 1, isActive: true, title: "Engineering") }
.execute(db)
}
try await $syncUps.load()
Expand Down