GRDB.swift: DatabaseValueConvertible use throws methods instead of properties
Currently, since DatabaseValueConvertible uses a property or a non throwing method, any errors in the conversion process cannot be propagated and there is no way to cancel a current transaction.
Unless I am missing something, the only options available to the implementor is to log an error and return some valid value (e.g. database null) or use some horrible overreach such as fatalError.
Switching this protocol to use throwing methods for both the to & from direction would allow errors during this phase to propagate and successfully fail any in-flight transactions.
A proposed implementation would be:
public protocol DatabaseValueConvertible : SQLExpressible {
/// Returns a value that can be stored in the database.
func databaseValue() throws -> DatabaseValue
/// Returns a value initialized from *databaseValue*, if possible.
static func fromDatabaseValue(_ databaseValue: DatabaseValue) throws -> Self?
}
If you are open to this change, I’d be more than happy to submit a PR implement it as such.
About this issue
- Original URL
- State: closed
- Created 8 years ago
- Comments: 15 (15 by maintainers)
Commits related to this issue
- Introduce DatabaseCursor (addresses #147) let cursor = try Row.fetchCursor(db, "SELECT * FROM persons) while let row = try cursor.step() { ... } — committed to groue/GRDB.swift by groue 8 years ago
- Add DatabaseCursor to CHANGELOG, fix FTS5, and some TODO (addresses #147) — committed to groue/GRDB.swift by groue 8 years ago
- DatabaseCursor.enumerated, filter, forEach, map, reduce (addresses #147) — committed to groue/GRDB.swift by groue 8 years ago
- Base DatabaseSequence on DatabaseCursor (addresses #147) — committed to groue/GRDB.swift by groue 8 years ago
- DatabaseCursor.enumerated, filter, forEach, map, reduce are public. Once DatabaseCursor.step() has returned nil, all subsequent calls return nil. (addresses #147) — committed to groue/GRDB.swift by groue 8 years ago
- #147: Rename DatabaseCursor.step() into next — committed to groue/GRDB.swift by groue 8 years ago
- #147: Database.tableExists(_:) and Database.indexes(on:) can now throw database errors — committed to groue/GRDB.swift by groue 8 years ago
- #147: use cursor — committed to groue/GRDB.swift by groue 8 years ago
- #147: tests for DatabaseValueConvertible.fetchCursor() methods — committed to groue/GRDB.swift by groue 8 years ago
- #147: Array(cursor) initialiser; base all fetchAll and fetchOne methods on fetchCursor. — committed to groue/GRDB.swift by groue 8 years ago
- #147: tests for StatementColumnConvertible.fetchCursor() methods — committed to groue/GRDB.swift by groue 8 years ago
- #147: tests for RowConvertible.fetchCursor() methods — committed to groue/GRDB.swift by groue 8 years ago
- #147: tests for RowConvertible.fetchCursor(_:keys:) methods — committed to groue/GRDB.swift by groue 8 years ago
- #147: tests for QueryInterfaceRequest.fetchCursor(_:) and (RowConvertible & TableMapping).fetchCursor(_:) — committed to groue/GRDB.swift by groue 8 years ago
- #147: record.exists(_:) can now throw a database error — committed to groue/GRDB.swift by groue 8 years ago
- #147: tests for DatabaseCursor.filter, forEach, map, reduce; dropped DatabaseCursor.enumerated since it can't be implemented yet — committed to groue/GRDB.swift by groue 8 years ago
- #147: DatabaseCursor.enumerated() — committed to groue/GRDB.swift by groue 8 years ago
- #147: DatabaseCursor.flatMap() — committed to groue/GRDB.swift by groue 8 years ago
- #147: The `fetch` method have been removed, along with `DatabaseSequence` and `DatabaseIterator`. — committed to groue/GRDB.swift by groue 8 years ago
- #147: fetchAll() and fetchOne() can now throw database errors. — committed to groue/GRDB.swift by groue 8 years ago
@kdubb Your questions did ring a bell in me. Although I still stand by non-failable DatabaseValueConvertible and RowConvertible conversion protocols, I have to admit that the state of handling errors that happen when reading from the database lacks a lot:
That’s why I’ve pushed to the DatabaseCursor branch support for a new type: DatabaseCursor.
Unlike DatabaseSequence which can not throw (because of Swift), DatabaseCursor throws whenever it can.
The code above can now read:
Of course, with records that can always initialize from rows, it’s even shorter:
The fetchCursor() methods completes the list of fetching methods:
I think this branch may help you.