Skip to content
Open
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
4 changes: 0 additions & 4 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,13 @@ let package = Package(
dependencies: [
.package(url: "https://github.com/apple/swift-collections.git", from: "1.1.0"),
.package(url: "https://github.com/apple/swift-log.git", from: "1.5.4"),
.package(url: "https://github.com/apple/swift-nio.git", from: "2.84.0"),
],
targets: [
.target(
name: "SQLKit",
dependencies: [
.product(name: "Collections", package: "swift-collections"),
.product(name: "Logging", package: "swift-log"),
.product(name: "NIOCore", package: "swift-nio"),
],
swiftSettings: swiftSettings
),
Expand All @@ -38,8 +36,6 @@ let package = Package(
.testTarget(
name: "SQLKitTests",
dependencies: [
.product(name: "NIOCore", package: "swift-nio"),
.product(name: "NIOEmbedded", package: "swift-nio"),
.target(name: "SQLKit"),
.target(name: "SQLKitBenchmark"),
],
Expand Down
16 changes: 0 additions & 16 deletions Sources/SQLKit/Builders/Prototypes/SQLQueryBuilder.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import class NIOCore.EventLoopFuture

/// Base definitions for builders which set up queries and execute them against a given database.
///
/// Almost all concrete builders conform to this protocol.
Expand All @@ -9,27 +7,13 @@ public protocol SQLQueryBuilder: AnyObject {

/// Connection to execute query on.
var database: any SQLDatabase { get }

/// 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>

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

}

extension SQLQueryBuilder {
/// 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 }
}

/// 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.
Expand Down
206 changes: 3 additions & 203 deletions Sources/SQLKit/Builders/Prototypes/SQLQueryFetcher.swift
Original file line number Diff line number Diff line change
@@ -1,76 +1,7 @@
import class NIOCore.EventLoopFuture

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

// MARK: - First (EventLoopFuture)

extension SQLQueryFetcher {
/// Returns the named column from the first output row, if any, decoded as a given type.
///
/// - Parameters:
/// - column: The name of the column to decode.
/// - type: The type of the desired value.
/// - Returns: A future containing the decoded value, if any.
@inlinable
public func first<D: Decodable & _SQLKitSendableMetatype>(decodingColumn column: String, as type: D.Type) -> EventLoopFuture<D?> {
self.first().flatMapThrowing { try $0?.decode(column: column, as: D.self) }
}

/// Using a default-configured ``SQLRowDecoder``, returns the first output row, if any, decoded as a given type.
///
/// - Parameter type: The type of the desired value.
/// - Returns: A future containing the decoded value, if any.
@inlinable
public func first<D: Decodable & _SQLKitSendableMetatype>(decoding type: D.Type) -> EventLoopFuture<D?> {
self.first(decoding: D.self, with: .init())
}

/// Configure a new ``SQLRowDecoder`` as specified and use it to decode and return the first output row, if any,
/// as a given type.
///
/// - Parameters:
/// - type: The type of the desired value.
/// - prefix: See ``SQLRowDecoder/prefix``.
/// - keyDecodingStrategy: See ``SQLRowDecoder/keyDecodingStrategy-swift.property``.
/// - userInfo: See ``SQLRowDecoder/userInfo``.
/// - Returns: A future containing the decoded value, if any.
@inlinable
public func first<D: Decodable & _SQLKitSendableMetatype>(
decoding type: D.Type,
prefix: String? = nil,
keyDecodingStrategy: SQLRowDecoder.KeyDecodingStrategy = .useDefaultKeys,
userInfo: [CodingUserInfoKey: any Sendable] = [:]
) -> EventLoopFuture<D?> {
self.first(decoding: D.self, with: .init(prefix: prefix, keyDecodingStrategy: keyDecodingStrategy, userInfo: userInfo))
}

/// Using the given ``SQLRowDecoder``, returns the first output row, if any, decoded as a given type.
///
/// - Parameters:
/// - type: The type of the desired value.
/// - decoder: A configured ``SQLRowDecoder`` to use.
/// - Returns: A future containing the decoded value, if any.
@inlinable
public func first<D: Decodable & _SQLKitSendableMetatype>(decoding type: D.Type, with decoder: SQLRowDecoder) -> EventLoopFuture<D?> {
self.first().flatMapThrowing { try $0?.decode(model: D.self, with: decoder) }
}

/// Returns the first output row, if any.
///
/// If `self` conforms to ``SQLPartialResultBuilder``, ``SQLPartialResultBuilder/limit(_:)`` is used to avoid
/// loading more rows than necessary from the database.
///
/// - Returns: A future containing the first output row, if any.
@inlinable
public func first() -> EventLoopFuture<(any SQLRow)?> {
(self as? any SQLPartialResultBuilder)?.limit(1)
nonisolated(unsafe) var rows = [any SQLRow]()
return self.run { if rows.isEmpty { rows.append($0) } }.map { rows.first }
}
}

// MARK: - First (async)
// MARK: - First

extension SQLQueryFetcher {
/// Returns the named column from the first output row, if any, decoded as a given type.
Expand Down Expand Up @@ -142,70 +73,7 @@ extension SQLQueryFetcher {
}
}

// MARK: - All (EventLoopFuture)

extension SQLQueryFetcher {
/// Returns the named column from each output row, if any, decoded as a given type.
///
/// - Parameters:
/// - column: The name of the column to decode.
/// - type: The type of the desired values.
/// - Returns: A future containing the decoded values, if any.
@inlinable
public func all<D: Decodable & _SQLKitSendableMetatype>(decodingColumn column: String, as type: D.Type) -> EventLoopFuture<[D]> {
self.all().flatMapThrowing { try $0.map { try $0.decode(column: column, as: D.self) } }
}

/// Using a default-configured ``SQLRowDecoder``, returns all output rows, if any, decoded as a given type.
///
/// - Parameter type: The type of the desired values.
/// - Returns: A future containing the decoded values, if any.
@inlinable
public func all<D: Decodable & _SQLKitSendableMetatype>(decoding type: D.Type) -> EventLoopFuture<[D]> {
self.all(decoding: D.self, with: .init())
}

/// Configure a new ``SQLRowDecoder`` as specified and use it to decode and return the output rows, if any,
/// as a given type.
///
/// - Parameters:
/// - type: The type of the desired values.
/// - prefix: See ``SQLRowDecoder/prefix``.
/// - keyDecodingStrategy: See ``SQLRowDecoder/keyDecodingStrategy-swift.property``.
/// - userInfo: See ``SQLRowDecoder/userInfo``.
/// - Returns: A future containing the decoded values, if any.
@inlinable
public func all<D: Decodable & _SQLKitSendableMetatype>(
decoding type: D.Type,
prefix: String? = nil,
keyDecodingStrategy: SQLRowDecoder.KeyDecodingStrategy = .useDefaultKeys,
userInfo: [CodingUserInfoKey: any Sendable] = [:]
) -> EventLoopFuture<[D]> {
self.all(decoding: D.self, with: .init(prefix: prefix, keyDecodingStrategy: keyDecodingStrategy, userInfo: userInfo))
}

/// Using the given ``SQLRowDecoder``, returns the output rows, if any, decoded as a given type.
///
/// - Parameters:
/// - type: The type of the desired values.
/// - decoder: A configured ``SQLRowDecoder`` to use.
/// - Returns: A future containing the decoded values, if any.
@inlinable
public func all<D: Decodable & _SQLKitSendableMetatype>(decoding type: D.Type, with decoder: SQLRowDecoder) -> EventLoopFuture<[D]> {
self.all().flatMapThrowing { try $0.map { try $0.decode(model: D.self, with: decoder) } }
}

/// Returns all output rows, if any.
///
/// - Returns: A future containing the output rows, if any.
@inlinable
public func all() -> EventLoopFuture<[any SQLRow]> {
nonisolated(unsafe) var rows = [any SQLRow]()
return self.run { row in rows.append(row) }.map { rows }
}
}

// MARK: - All (async)
// MARK: - All

extension SQLQueryFetcher {
/// Returns the named column from each output row, if any, decoded as a given type.
Expand Down Expand Up @@ -269,75 +137,7 @@ extension SQLQueryFetcher {
}
}

// MARK: - Run (EventLoopFuture)

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.
///
/// - Parameters:
/// - type: The type of the desired values.
/// - handler: A closure which receives the result of each decoding operation, row by row.
/// - Returns: A completion future.
@preconcurrency
@inlinable
public func run<D: Decodable & _SQLKitSendableMetatype>(decoding type: D.Type, _ handler: @escaping @Sendable (Result<D, any Error>) -> ()) -> EventLoopFuture<Void> {
self.run(decoding: D.self, with: .init(), handler)
}

/// Configure a new ``SQLRowDecoder`` as specified, use it to to decode each output row, if any, as a given type,
/// and call the provided handler closure with each decoding result.
///
/// - Parameters:
/// - type: The type of the desired values.
/// - prefix: See ``SQLRowDecoder/prefix``.
/// - keyDecodingStrategy: See ``SQLRowDecoder/keyDecodingStrategy-swift.property``.
/// - userInfo: See ``SQLRowDecoder/userInfo``.
/// - handler: A closure which receives the result of each decoding operation, row by row.
/// - Returns: A completion future.
@preconcurrency
@inlinable
public func run<D: Decodable & _SQLKitSendableMetatype>(
decoding type: D.Type,
prefix: String? = nil,
keyDecodingStrategy: SQLRowDecoder.KeyDecodingStrategy = .useDefaultKeys,
userInfo: [CodingUserInfoKey: any Sendable] = [:],
_ handler: @escaping @Sendable (Result<D, any Error>) -> ()
) -> EventLoopFuture<Void> {
self.run(decoding: D.self, with: .init(prefix: prefix, keyDecodingStrategy: keyDecodingStrategy, userInfo: userInfo), handler)
}

/// Using the given ``SQLRowDecoder``, call the provided handler closure with the result of decoding each output
/// row, if any, as a given type.
///
/// - Parameters:
/// - type: The type of the desired values.
/// - decoder: A configured ``SQLRowDecoder`` to use.
/// - handler: A closure which receives the result of each decoding operation, row by row.
/// - Returns: A completion future.
@preconcurrency
@inlinable
public func run<D: Decodable & _SQLKitSendableMetatype>(
decoding type: D.Type,
with decoder: SQLRowDecoder,
_ handler: @escaping @Sendable (Result<D, any Error>) -> ()
) -> EventLoopFuture<Void> {
self.run { row in handler(.init { try row.decode(model: D.self, with: decoder) }) }
}

/// Run the query specified by the builder, calling the provided handler closure with each output row, if any, as
/// it is received.
///
/// - Parameter handler: A closure which receives each output row one at a time.
/// - Returns: A completion future.
@preconcurrency
@inlinable
public func run(_ handler: @escaping @Sendable (any SQLRow) -> ()) -> EventLoopFuture<Void> {
self.database.execute(sql: self.query, handler)
}
}

// MARK: - Run (async)
// MARK: - Run

extension SQLQueryFetcher {
/// Using a default-configured ``SQLRowDecoder``, call the provided handler closure with the result of decoding
Expand Down
53 changes: 0 additions & 53 deletions Sources/SQLKit/Database/SQLDatabase.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import protocol NIOCore.EventLoop
import class NIOCore.EventLoopFuture
import struct Logging.Logger

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

/// 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 }

/// 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 @@ -105,27 +95,6 @@ public protocol SQLDatabase: Sendable {
/// 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).
///
/// Although it is a protocol requirement for historical reasons, this is considered a legacy interface thanks
/// to its reliance on `EventLoopFuture`. Implementers should implement both this method and
/// ``execute(sql:_:)-7trgm`` if they can, and users should use ``execute(sql:_:)-7trgm`` whenever possible.
///
/// - Parameters:
/// - query: An ``SQLExpression`` representing a complete query to execute.
/// - onRow: A closure which is invoked once for each result row returned by the query (if any).
/// - Returns: An `EventLoopFuture`.
@preconcurrency
func execute(
sql query: any SQLExpression,
_ onRow: @escaping @Sendable (any SQLRow) -> ()
) -> EventLoopFuture<Void>

/// 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).
///
/// If a concrete type conforming to ``SQLDatabase`` can provide a more efficient Concurrency-based implementation
/// than forwarding the invocation through the legacy `EventLoopFuture`-based API, it should override this method
/// in order to do so.
///
/// - Parameters:
/// - query: An ``SQLExpression`` representing a complete query to execute.
/// - onRow: A closure which is invoked once for each result row returned by the query (if any).
Expand Down Expand Up @@ -200,15 +169,6 @@ extension SQLDatabase {
}

extension SQLDatabase {
/// The default implementation for ``execute(sql:_:)-4eg19``.
@inlinable
public func execute(
sql query: any SQLExpression,
_ onRow: @escaping @Sendable (any SQLRow) -> ()
) async throws {
try await self.execute(sql: query, onRow).get()
}

/// The default implementation for ``withSession(_:)-9b68j``.
@inlinable
public func withSession<R>(
Expand All @@ -226,11 +186,6 @@ private struct CustomLoggerSQLDatabase<D: SQLDatabase>: SQLDatabase {

// See `SQLDatabase.logger`.
let logger: Logger

// See `SQLDatabase.eventLoop`.
var eventLoop: any EventLoop {
self.database.eventLoop
}

// See `SQLDatabase.version`.
var version: (any SQLDatabaseReportedVersion)? {
Expand All @@ -246,14 +201,6 @@ private struct CustomLoggerSQLDatabase<D: SQLDatabase>: SQLDatabase {
var queryLogLevel: Logger.Level? {
self.database.queryLogLevel
}

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

// See `SQLDatabase.execute(sql:_:)`.
func execute(
Expand Down
5 changes: 0 additions & 5 deletions Sources/SQLKit/Docs.docc/SQLDatabase+ExtensionDocs.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
### Properties

- ``SQLDatabase/logger``
- ``SQLDatabase/eventLoop``
- ``SQLDatabase/version``
- ``SQLDatabase/dialect``
- ``SQLDatabase/queryLogLevel``
Expand Down Expand Up @@ -61,7 +60,3 @@
### Logging

- ``SQLDatabase/logging(to:)``

### Legacy query interface

- ``SQLDatabase/execute(sql:_:)->_``
Loading
Loading