Skip to content

Commit

Permalink
Merge pull request #1510 from groue/dev/trivial-sendable-conformances
Browse files Browse the repository at this point in the history
Add Sendable conformances and unavailabilities
  • Loading branch information
groue committed Mar 17, 2024
2 parents 62e7ea1 + 77da5f4 commit 9b95dd5
Show file tree
Hide file tree
Showing 52 changed files with 207 additions and 57 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ GRDB adheres to [Semantic Versioning](https://semver.org/), with one exception:
## Next Release

- **New**: [#1503](https://github.com/groue/GRDB.swift/pull/1503) by [@simba909](https://github.com/simba909): Conform Database.ColumnType to Sendable
- **New**: [#1510](https://github.com/groue/GRDB.swift/pull/1510) by [@groue](https://github.com/groue): Add Sendable conformances and unavailabilities
- **Fixed**: [#1508](https://github.com/groue/GRDB.swift/pull/1508) by [@groue](https://github.com/groue): Fix ValueObservation mishandling of database schema modification

## 6.25.0
Expand Down
2 changes: 1 addition & 1 deletion GRDB/Core/Configuration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ public struct Configuration {
/// connection is opened.
///
/// Related SQLite documentation: <https://www.sqlite.org/pragma.html#pragma_journal_mode>
public enum JournalModeConfiguration {
public enum JournalModeConfiguration: Sendable {
/// The default setup has ``DatabaseQueue`` perform no specific
/// configuration of the journal mode, and ``DatabasePool``
/// configure the database for the WAL mode (just like the
Expand Down
40 changes: 40 additions & 0 deletions GRDB/Core/Cursor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -733,6 +733,11 @@ public final class AnyCursor<Element>: Cursor {
}
}

// Explicit non-conformance to Sendable: a type-erased cursor can't be more
// sendable than non-sendable cursors (such as `DatabaseCursor`).
@available(*, unavailable)
extension AnyCursor: Sendable { }

/// A `Cursor` that consumes and drops n elements from an underlying `Base`
/// cursor before possibly returning the first available element.
public final class DropFirstCursor<Base: Cursor> {
Expand All @@ -747,6 +752,11 @@ public final class DropFirstCursor<Base: Cursor> {
}
}

// Explicit non-conformance to Sendable: `DropFirstCursor` is a mutable
// class and there is no known reason for making it thread-safe.
@available(*, unavailable)
extension DropFirstCursor: Sendable { }

extension DropFirstCursor: Cursor {
public func next() throws -> Base.Element? {
while dropped < limit {
Expand All @@ -773,6 +783,11 @@ public final class DropWhileCursor<Base: Cursor> {
}
}

// Explicit non-conformance to Sendable: `DropWhileCursor` is a mutable
// class and there is no known reason for making it thread-safe.
@available(*, unavailable)
extension DropWhileCursor: Sendable { }

extension DropWhileCursor: Cursor {
public func next() throws -> Base.Element? {
if predicateHasFailed {
Expand Down Expand Up @@ -818,6 +833,11 @@ public final class EnumeratedCursor<Base: Cursor> {
}
}

// Explicit non-conformance to Sendable: `EnumeratedCursor` is a mutable
// class and there is no known reason for making it thread-safe.
@available(*, unavailable)
extension EnumeratedCursor: Sendable { }

extension EnumeratedCursor: Cursor {
public func next() throws -> (Int, Base.Element)? {
guard let element = try base.next() else { return nil }
Expand Down Expand Up @@ -875,6 +895,11 @@ public final class FlattenCursor<Base: Cursor> where Base.Element: Cursor {
}
}

// Explicit non-conformance to Sendable: `EnumeratedCursor` is a mutable
// class and there is no known reason for making it thread-safe.
@available(*, unavailable)
extension FlattenCursor: Sendable { }

extension FlattenCursor: Cursor {
public func next() throws -> Base.Element.Element? {
while true {
Expand All @@ -901,6 +926,11 @@ public final class MapCursor<Base: Cursor, Element> {
}
}

// Explicit non-conformance to Sendable: There is no known reason for making
// it thread-safe (`transform` a Sendable closure).
@available(*, unavailable)
extension MapCursor: Sendable { }

extension MapCursor: Cursor {
public func next() throws -> Element? {
guard let element = try base.next() else { return nil }
Expand All @@ -927,6 +957,11 @@ public final class PrefixCursor<Base: Cursor> {
}
}

// Explicit non-conformance to Sendable: `PrefixCursor` is a mutable
// class and there is no known reason for making it thread-safe.
@available(*, unavailable)
extension PrefixCursor: Sendable { }

extension PrefixCursor: Cursor {
public func next() throws -> Base.Element? {
if taken >= maxLength { return nil }
Expand Down Expand Up @@ -954,6 +989,11 @@ public final class PrefixWhileCursor<Base: Cursor> {
}
}

// Explicit non-conformance to Sendable: `PrefixCursor` is a mutable
// class and there is no known reason for making it thread-safe.
@available(*, unavailable)
extension PrefixWhileCursor: Sendable { }

extension PrefixWhileCursor: Cursor {
public func next() throws -> Base.Element? {
if !predicateHasFailed, let nextElement = try base.next() {
Expand Down
12 changes: 6 additions & 6 deletions GRDB/Core/Database+Schema.swift
Original file line number Diff line number Diff line change
Expand Up @@ -997,7 +997,7 @@ extension Database {
///
/// - [pragma `table_info`](https://www.sqlite.org/pragma.html#pragma_table_info)
/// - [pragma `table_xinfo`](https://www.sqlite.org/pragma.html#pragma_table_xinfo)
public struct ColumnInfo: FetchableRecord {
public struct ColumnInfo: FetchableRecord, Sendable {
let cid: Int
let hidden: Int?

Expand Down Expand Up @@ -1083,9 +1083,9 @@ public struct ColumnInfo: FetchableRecord {
///
/// - [pragma `index_list`](https://www.sqlite.org/pragma.html#pragma_index_list)
/// - [pragma `index_info`](https://www.sqlite.org/pragma.html#pragma_index_info)
public struct IndexInfo {
public struct IndexInfo: Sendable{
/// The origin of an index.
public struct Origin: RawRepresentable, Equatable, DatabaseValueConvertible {
public struct Origin: RawRepresentable, Equatable, DatabaseValueConvertible, Sendable {
public var rawValue: String

public init(rawValue: String) {
Expand Down Expand Up @@ -1158,7 +1158,7 @@ public struct IndexInfo {
/// ```
///
/// Related SQLite documentation: <https://www.sqlite.org/pragma.html#pragma_foreign_key_check>
public struct ForeignKeyViolation {
public struct ForeignKeyViolation: Sendable {
/// The name of the table that contains the foreign key.
public var originTable: String

Expand Down Expand Up @@ -1307,7 +1307,7 @@ extension ForeignKeyViolation: CustomStringConvertible {
/// pk.rowIDColumn // nil
/// pk.isRowID // false
/// ```
public struct PrimaryKeyInfo {
public struct PrimaryKeyInfo: Sendable {
private enum Impl {
/// The hidden rowID.
case hiddenRowID
Expand Down Expand Up @@ -1433,7 +1433,7 @@ public struct PrimaryKeyInfo {
/// `Database` method.
///
/// Related SQLite documentation: [pragma `foreign_key_list`](https://www.sqlite.org/pragma.html#pragma_foreign_key_list).
public struct ForeignKeyInfo {
public struct ForeignKeyInfo: Sendable {
/// The first column in the output of the `foreign_key_list` pragma.
public var id: Int

Expand Down
5 changes: 5 additions & 0 deletions GRDB/Core/Database+Statements.swift
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,11 @@ public class SQLStatementCursor {
}
}

// Explicit non-conformance to Sendable: database cursors must be used from
// a serialized database access dispatch queue.
@available(*, unavailable)
extension SQLStatementCursor: Sendable { }

extension SQLStatementCursor: Cursor {
public func next() throws -> Statement? {
guard offset < cString.count - 1 /* trailing \0 */ else {
Expand Down
29 changes: 22 additions & 7 deletions GRDB/Core/Database.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1729,6 +1729,11 @@ public final class Database: CustomStringConvertible, CustomDebugStringConvertib
}
}

// Explicit non-conformance to Sendable: `Database` must be used from a
// serialized database access dispatch queue (see `SerializedDatabase`).
@available(*, unavailable)
extension Database: Sendable { }

#if SQLITE_HAS_CODEC
extension Database {

Expand Down Expand Up @@ -1854,7 +1859,7 @@ extension Database {
/// The available checkpoint modes.
///
/// Related SQLite documentation: <https://www.sqlite.org/c3ref/wal_checkpoint_v2.html>
public enum CheckpointMode: CInt {
public enum CheckpointMode: CInt, Sendable {
/// The `SQLITE_CHECKPOINT_PASSIVE` mode.
case passive = 0

Expand All @@ -1873,7 +1878,7 @@ extension Database {
/// Related SQLite documentation:
/// - <https://www.sqlite.org/datatype3.html#collating_sequences>
/// - <https://www.sqlite.org/datatype3.html#collation>
public struct CollationName: RawRepresentable, Hashable {
public struct CollationName: RawRepresentable, Hashable, Sendable {
public let rawValue: String

/// Creates a collation name.
Expand Down Expand Up @@ -1962,7 +1967,7 @@ extension Database {
/// An SQLite conflict resolution.
///
/// Related SQLite documentation: <https://www.sqlite.org/lang_conflict.html>
public enum ConflictResolution: String {
public enum ConflictResolution: String, Sendable {
/// The `ROLLBACK` conflict resolution.
case rollback = "ROLLBACK"

Expand All @@ -1982,7 +1987,7 @@ extension Database {
/// A foreign key action.
///
/// Related SQLite documentation: <https://www.sqlite.org/foreignkeys.html>
public enum ForeignKeyAction: String {
public enum ForeignKeyAction: String, Sendable {
/// The `CASCADE` foreign key action.
case cascade = "CASCADE"

Expand All @@ -2005,7 +2010,7 @@ extension Database {
/// ``Database/trace(options:_:)`` method.
///
/// Related SQLite documentation: <https://www.sqlite.org/c3ref/c_trace.html>
public struct TracingOptions: OptionSet {
public struct TracingOptions: OptionSet, Sendable {
/// The raw trace event code.
public let rawValue: CInt

Expand Down Expand Up @@ -2138,15 +2143,15 @@ extension Database {
///
/// Related SQLite documentation: <https://www.sqlite.org/lang_transaction.html>.
@frozen
public enum TransactionCompletion {
public enum TransactionCompletion: Sendable {
case commit
case rollback
}

/// A transaction kind.
///
/// Related SQLite documentation: <https://www.sqlite.org/lang_transaction.html>.
public enum TransactionKind: String {
public enum TransactionKind: String, Sendable {
/// The `DEFERRED` transaction kind.
case deferred = "DEFERRED"

Expand Down Expand Up @@ -2179,3 +2184,13 @@ extension Database {
}
}
}

// Explicit non-conformance to Sendable: a trace event contains transient
// information.
@available(*, unavailable)
extension Database.TraceEvent: Sendable { }

// Explicit non-conformance to Sendable: a trace event contains transient
// information.
@available(*, unavailable)
extension Database.TraceEvent.Statement: Sendable { }
2 changes: 1 addition & 1 deletion GRDB/Core/DatabaseBackupProgress.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/// Describe the progress of a database backup.
///
/// Related SQLite documentation: <https://www.sqlite.org/c3ref/backup_finish.html>
public struct DatabaseBackupProgress {
public struct DatabaseBackupProgress: Sendable {
/// The number of pages still to be backed up.
///
/// It is the result of the `sqlite3_backup_remaining` function.
Expand Down
2 changes: 1 addition & 1 deletion GRDB/Core/DatabaseRegion.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
///
/// - ``isModified(byEventsOfKind:)``
/// - ``isModified(by:)``
public struct DatabaseRegion {
public struct DatabaseRegion: Sendable {
private let tableRegions: [CaseInsensitiveIdentifier: TableRegion]?

private init(tableRegions: [CaseInsensitiveIdentifier: TableRegion]?) {
Expand Down
5 changes: 5 additions & 0 deletions GRDB/Core/DatabaseValueConvertible.swift
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,11 @@ public final class DatabaseValueCursor<Value: DatabaseValueConvertible>: Databas
}
}

// Explicit non-conformance to Sendable: database cursors must be used from
// a serialized database access dispatch queue.
@available(*, unavailable)
extension DatabaseValueCursor: Sendable { }

/// DatabaseValueConvertible comes with built-in methods that allow to fetch
/// cursors, arrays, or single values:
///
Expand Down
5 changes: 5 additions & 0 deletions GRDB/Core/FetchRequest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,11 @@ public struct PreparedRequest {
}
}

// Explicit non-conformance to Sendable: `PreparedRequest` contains
// a statement.
@available(*, unavailable)
extension PreparedRequest: Sendable { }

extension PreparedRequest: Refinable { }

// MARK: - AdaptedFetchRequest
Expand Down
13 changes: 12 additions & 1 deletion GRDB/Core/Row.swift
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,12 @@ public final class Row {
}
}

// Explicit non-conformance to Sendable: a row contains transient
// information. TODO GRDB7: split non sendable statement rows from sendable
// copied rows.
@available(*, unavailable)
extension Row: Sendable { }

extension Row {

// MARK: - Columns
Expand Down Expand Up @@ -1377,6 +1383,11 @@ public final class RowCursor: DatabaseCursor {
public func _element(sqliteStatement: SQLiteStatement) -> Row { _row }
}

// Explicit non-conformance to Sendable: database cursors must be used from
// a serialized database access dispatch queue.
@available(*, unavailable)
extension RowCursor: Sendable { }

extension Row {

// MARK: - Fetching From Prepared Statement
Expand Down Expand Up @@ -2059,7 +2070,7 @@ typealias RowIndex = Row.Index

extension Row {
/// An index to a (column, value) pair in a ``Row``.
public struct Index {
public struct Index: Sendable {
let index: Int
init(_ index: Int) { self.index = index }
}
Expand Down
8 changes: 4 additions & 4 deletions GRDB/Core/RowAdapter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,7 @@ extension RowAdapter {
///
/// This limit adapter may turn out useful in some narrow use cases. You'll
/// be happy to find it when you need it.
public struct EmptyRowAdapter: RowAdapter {
public struct EmptyRowAdapter: RowAdapter, Sendable {
/// Creates an `EmptyRowAdapter`.
public init() { }

Expand All @@ -403,7 +403,7 @@ public struct EmptyRowAdapter: RowAdapter {
///
/// Note that columns that are not present in the dictionary are not present
/// in the resulting adapted row.
public struct ColumnMapping: RowAdapter {
public struct ColumnMapping: RowAdapter, Sendable {
/// A dictionary from mapped column names to column names in a base row.
let mapping: [String: String]

Expand Down Expand Up @@ -444,7 +444,7 @@ public struct ColumnMapping: RowAdapter {
/// // [c:2, d: 3]
/// try Row.fetchOne(db, sql: sql, adapter: adapter)!
/// ```
public struct SuffixRowAdapter: RowAdapter {
public struct SuffixRowAdapter: RowAdapter, Sendable {
/// The suffix index
let index: Int

Expand Down Expand Up @@ -473,7 +473,7 @@ public struct SuffixRowAdapter: RowAdapter {
/// // [b:1 c:2]
/// try Row.fetchOne(db, sql: sql, adapter: adapter)
/// ```
public struct RangeRowAdapter: RowAdapter {
public struct RangeRowAdapter: RowAdapter, Sendable {
/// The range
let range: CountableRange<Int>

Expand Down
5 changes: 5 additions & 0 deletions GRDB/Core/Statement.swift
Original file line number Diff line number Diff line change
Expand Up @@ -623,6 +623,11 @@ public final class Statement {
}
}

// Explicit non-conformance to Sendable: statements must be used from
// a serialized database access dispatch queue.
@available(*, unavailable)
extension Statement: Sendable { }

extension Statement: CustomStringConvertible {
public var description: String {
SchedulingWatchdog.allows(database) ? sql : "Statement"
Expand Down
5 changes: 5 additions & 0 deletions GRDB/Core/StatementColumnConvertible.swift
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,11 @@ where Value: DatabaseValueConvertible & StatementColumnConvertible
}
}

// Explicit non-conformance to Sendable: database cursors must be used from
// a serialized database access dispatch queue.
@available(*, unavailable)
extension FastDatabaseValueCursor: Sendable { }

/// Types that adopt both DatabaseValueConvertible and
/// StatementColumnConvertible can be efficiently initialized from
/// database values.
Expand Down
Loading

0 comments on commit 9b95dd5

Please sign in to comment.