Skip to content
Draft
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
16 changes: 13 additions & 3 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
// swift-tools-version:6.1
import PackageDescription

/// This list matches the [supported platforms on the Swift 6.1 release of SPM](https://github.com/swiftlang/swift-package-manager/blob/release/6.1/Sources/PackageDescription/SupportedPlatforms.swift).
/// Don't add new platforms here unless raising the swift-tools-version of this manifest.
let allPlatforms: [Platform] = [.macOS, .macCatalyst, .iOS, .tvOS, .watchOS, .visionOS, .driverKit, .linux, .windows, .android, .wasi, .openbsd]
let nonWASIPlatforms: [Platform] = allPlatforms.filter { $0 != .wasi }

let package = Package(
name: "sql-kit",
platforms: [
Expand All @@ -24,7 +29,11 @@ let package = Package(
dependencies: [
.product(name: "Collections", package: "swift-collections"),
.product(name: "Logging", package: "swift-log"),
.product(name: "NIOCore", package: "swift-nio"),
// SwiftNIO does not support wasm32-unknown-wasip1. Target dependency conditions
// are evaluated per platform, so on WASI NIOCore is simply not linked and the
// `EventLoopFuture` surface drops out via `#if canImport(NIOCore)`; the async
// surface is unaffected.
.product(name: "NIOCore", package: "swift-nio", condition: .when(platforms: nonWASIPlatforms)),
],
swiftSettings: swiftSettings
),
Expand All @@ -38,8 +47,9 @@ let package = Package(
.testTarget(
name: "SQLKitTests",
dependencies: [
.product(name: "NIOCore", package: "swift-nio"),
.product(name: "NIOEmbedded", package: "swift-nio"),
// The test suite exercises the SwiftNIO surface, so it is not built for WASI.
.product(name: "NIOCore", package: "swift-nio", condition: .when(platforms: nonWASIPlatforms)),
.product(name: "NIOEmbedded", package: "swift-nio", condition: .when(platforms: nonWASIPlatforms)),
.target(name: "SQLKit"),
.target(name: "SQLKitBenchmark"),
],
Expand Down
11 changes: 10 additions & 1 deletion Sources/SQLKit/Builders/Prototypes/SQLQueryBuilder.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
// EventLoopFuture is elided on builds without SwiftNIO.
#if canImport(NIOCore)
import class NIOCore.EventLoopFuture
#endif

/// Base definitions for builders which set up queries and execute them against a given database.
///
Expand All @@ -10,25 +13,31 @@ public protocol SQLQueryBuilder: AnyObject {
/// Connection to execute query on.
var database: any SQLDatabase { get }

// N.B.: The `#if` encloses the doc comment; placed between the comment and the declaration it
// would detach the doc from the symbol graph.
#if canImport(NIOCore)
/// Execute the query on the connection, ignoring any results.
///
/// Although it is a protocol requirement for historical reasons, this is considered a legacy interface
/// thanks to its reliance on `EventLoopFuture`. Users should call ``run()-3tldd`` whenever possible.
func run() -> EventLoopFuture<Void>

#endif

/// Execute the query on the connection, ignoring any results.
func run() async throws

}

extension SQLQueryBuilder {
#if canImport(NIOCore)
/// Execute the query associated with the builder on the builder's database, ignoring any results.
///
/// See ``SQLQueryFetcher`` for methods which retrieve results from a query.
@inlinable
public func run() -> EventLoopFuture<Void> {
self.database.execute(sql: self.query) { _ in }
}
#endif

/// Execute the query associated with the builder on the builder's database, ignoring any results.
///
Expand Down
13 changes: 13 additions & 0 deletions Sources/SQLKit/Builders/Prototypes/SQLQueryFetcher.swift
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
// `EventLoopFuture` is unavailable where SwiftNIO is not linked. The `async` `first()`/`all()`/
// `run(_:)` families further down are unconditional and carry the whole surface there.
#if canImport(NIOCore)
import class NIOCore.EventLoopFuture
#endif

/// Common definitions for ``SQLQueryBuilder``s which support retrieving result rows.
public protocol SQLQueryFetcher: SQLQueryBuilder {}

// MARK: - First (EventLoopFuture)

#if canImport(NIOCore)
extension SQLQueryFetcher {
/// Returns the named column from the first output row, if any, decoded as a given type.
///
Expand Down Expand Up @@ -70,6 +75,8 @@ extension SQLQueryFetcher {
}
}

#endif // canImport(NIOCore)

// MARK: - First (async)

extension SQLQueryFetcher {
Expand Down Expand Up @@ -144,6 +151,7 @@ extension SQLQueryFetcher {

// MARK: - All (EventLoopFuture)

#if canImport(NIOCore)
extension SQLQueryFetcher {
/// Returns the named column from each output row, if any, decoded as a given type.
///
Expand Down Expand Up @@ -205,6 +213,8 @@ extension SQLQueryFetcher {
}
}

#endif // canImport(NIOCore)

// MARK: - All (async)

extension SQLQueryFetcher {
Expand Down Expand Up @@ -271,6 +281,7 @@ extension SQLQueryFetcher {

// MARK: - Run (EventLoopFuture)

#if canImport(NIOCore)
extension SQLQueryFetcher {
/// Using a default-configured ``SQLRowDecoder``, call the provided handler closure with the result of decoding
/// each output row, if any, as a given type.
Expand Down Expand Up @@ -337,6 +348,8 @@ extension SQLQueryFetcher {
}
}

#endif // canImport(NIOCore)

// MARK: - Run (async)

extension SQLQueryFetcher {
Expand Down
23 changes: 21 additions & 2 deletions Sources/SQLKit/Database/SQLDatabase.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
// SwiftNIO is not available on every platform SQLKit supports (see Package.swift); the
// EventLoopFuture surface below is elided where it is absent.
#if canImport(NIOCore)
import protocol NIOCore.EventLoop
import class NIOCore.EventLoopFuture
#endif
import struct Logging.Logger

/// The common interface to SQLKit for both drivers and client code.
Expand Down Expand Up @@ -56,14 +60,18 @@ public protocol SQLDatabase: Sendable {
/// The `Logger` used for logging all operations relating to a given database.
var logger: Logger { get }

// N.B.: Every `#if` in this file encloses the doc comment of the declaration it gates; placed
// between the two it would silently detach the doc from the symbol graph.
#if canImport(NIOCore)
/// The `EventLoop` used for asynchronous operations on a given database.
///
/// If there is no specific `EventLoop` which handles the database (such as because it is a connection pool which
/// assigns loops to connections at point of use, or because the underlying implementation is based on Swift
/// Concurrency or some other asynchronous execution technology), a single consistent `EventLoop` must be chosen
/// for the database and returned for this property nonetheless.
var eventLoop: any EventLoop { get }

#endif

/// The version number the database reports for itself.
///
/// The version must be provided via a type conforming to the ``SQLDatabaseReportedVersion`` protocol. If the
Expand Down Expand Up @@ -102,6 +110,7 @@ public protocol SQLDatabase: Sendable {
/// > it's unavoidable, as there are no direct entry points to SQLKit without a driver.
var queryLogLevel: Logger.Level? { get }

#if canImport(NIOCore)
/// Requests that the given generic SQL query be serialized and executed on the database, and that
/// the `onRow` closure be invoked once for each result row the query returns (if any).
///
Expand All @@ -118,6 +127,7 @@ public protocol SQLDatabase: Sendable {
sql query: any SQLExpression,
_ onRow: @escaping @Sendable (any SQLRow) -> ()
) -> EventLoopFuture<Void>
#endif

/// Requests that the given generic SQL query be serialized and executed on the database, and that
/// the `onRow` closure be invoked once for each result row the query returns (if any).
Expand Down Expand Up @@ -200,6 +210,10 @@ extension SQLDatabase {
}

extension SQLDatabase {
// This default bridges the `async` requirement to the legacy `EventLoopFuture` one, so it can
// only exist where the latter does. Without SwiftNIO there is no future overload to forward to
// and conformers implement the `async` `execute` directly.
#if canImport(NIOCore)
/// The default implementation for ``execute(sql:_:)-4eg19``.
@inlinable
public func execute(
Expand All @@ -208,7 +222,8 @@ extension SQLDatabase {
) async throws {
try await self.execute(sql: query, onRow).get()
}

#endif

/// The default implementation for ``withSession(_:)-9b68j``.
@inlinable
public func withSession<R>(
Expand All @@ -227,10 +242,12 @@ private struct CustomLoggerSQLDatabase<D: SQLDatabase>: SQLDatabase {
// See `SQLDatabase.logger`.
let logger: Logger

#if canImport(NIOCore)
// See `SQLDatabase.eventLoop`.
var eventLoop: any EventLoop {
self.database.eventLoop
}
#endif

// See `SQLDatabase.version`.
var version: (any SQLDatabaseReportedVersion)? {
Expand All @@ -247,13 +264,15 @@ private struct CustomLoggerSQLDatabase<D: SQLDatabase>: SQLDatabase {
self.database.queryLogLevel
}

#if canImport(NIOCore)
// See `SQLDatabase.execute(sql:_:)`.
func execute(
sql query: any SQLExpression,
_ onRow: @escaping @Sendable (any SQLRow) -> ()
) -> EventLoopFuture<Void> {
self.database.execute(sql: query, onRow)
}
#endif

// See `SQLDatabase.execute(sql:_:)`.
func execute(
Expand Down
3 changes: 3 additions & 0 deletions Sources/SQLKit/Exports.swift
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// EventLoop/EventLoopFuture are SwiftNIO types, elided where SwiftNIO is unavailable.
#if canImport(NIOCore)
@_documentation(visibility: internal) @_exported import protocol NIOCore.EventLoop
@_documentation(visibility: internal) @_exported import class NIOCore.EventLoopFuture
#endif
@_documentation(visibility: internal) @_exported import struct Logging.Logger
5 changes: 5 additions & 0 deletions Sources/SQLKitBenchmark/SQLBenchmarker.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import Logging
#if canImport(NIOCore)
import NIOCore
#endif
public import SQLKit
import XCTest

Expand All @@ -21,6 +23,8 @@ public final class SQLBenchmarker: Sendable {
}
}

// The deprecated EventLoopFuture bridges are elided where SwiftNIO is unavailable.
#if canImport(NIOCore)
@available(*, deprecated, renamed: "runAllTests()", message: "Use `runAllTests()` instead.")
public func testAll() throws {
try database.eventLoop.makeFutureWithTask { try await self.runAllTests() }.wait()
Expand All @@ -30,6 +34,7 @@ public final class SQLBenchmarker: Sendable {
public func run() throws {
try self.testAll()
}
#endif

func runTest(
_ name: String = #function,
Expand Down