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
25 changes: 15 additions & 10 deletions Sources/SQLiteNIO/Exports.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,6 @@
@_documentation(visibility: internal) @_exported import protocol NIOCore.EventLoopGroup
@_documentation(visibility: internal) @_exported import class NIOPosix.MultiThreadedEventLoopGroup
#else // !canImport(NIOCore)
#if canImport(FoundationEssentials)
import FoundationEssentials
#else
import Foundation
#endif

/// `BLOB` values are carried by `[UInt8]` on platforms where SwiftNIO is not available.
///
/// SwiftNIO does not support `wasm32-unknown-wasip1`, so `NIOCore.ByteBuffer` cannot be re-exported
Expand All @@ -29,10 +23,6 @@ extension ByteBuffer {
self.init(bytes)
}

init(data: Data) {
self.init(data)
}

var readableBytes: Int {
self.count
}
Expand All @@ -46,6 +36,21 @@ extension ByteBuffer {
}
}

// The `Data` bridge needs a Foundation flavor, and Embedded Swift has neither.
#if canImport(FoundationEssentials) || canImport(Foundation)
#if canImport(FoundationEssentials)
import FoundationEssentials
#else
import Foundation
#endif

extension ByteBuffer {
init(data: Data) {
self.init(data)
}
}
#endif // canImport(FoundationEssentials) || canImport(Foundation)

/// A single-threaded stand-in for `NIOConcurrencyHelpers.NIOLockedValueBox`.
///
/// The one supported SwiftNIO-free target is single-threaded, so no lock is needed and none is
Expand Down
7 changes: 7 additions & 0 deletions Sources/SQLiteNIO/SQLiteConnection+Hooks.swift
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
// N.B.: This is elided wholesale, rather than narrowed, because the hook API's *public* surface is
// Foundation-shaped: `SQLiteCommitEvent.date` and `SQLiteRollbackEvent.date` are `Date`, and
// `SQLiteHookToken.id` is a `UUID`. Embedded Swift has neither type in either Foundation flavor,
// so keeping the API here would mean either shipping stand-ins for both or giving Embedded a
// different public API than every other target. Neither belongs in this commit.
#if canImport(FoundationEssentials) || canImport(Foundation)
#if canImport(FoundationEssentials)
import FoundationEssentials
#else
Expand Down Expand Up @@ -871,3 +877,4 @@ extension SQLiteConnection {
self.observerBuckets.withLockedValue { $0 = .init() }
}
}
#endif // canImport(FoundationEssentials) || canImport(Foundation)
6 changes: 6 additions & 0 deletions Sources/SQLiteNIO/SQLiteConnection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -253,8 +253,11 @@ public final class SQLiteConnection: SQLiteDatabase, Sendable {
/// The underlying `sqlite3` connection handle.
let handle: SQLiteConnectionHandle

// The hook API needs a Foundation flavor; see `SQLiteConnection+Hooks.swift`.
#if canImport(FoundationEssentials) || canImport(Foundation)
/// Container for storing multiple observers per hook type.
let observerBuckets = NIOLockedValueBox<ObserverBuckets>(.init())
#endif

#if canImport(NIOCore)
/// The thread pool used by this connection when calling libsqlite3 APIs.
Expand Down Expand Up @@ -499,7 +502,10 @@ extension SQLiteConnection {
/// No further operations may be performed on the connection after calling this method.
public func close() async throws {
try await self.withBlockingIO {
// The hook API needs a Foundation flavor; see `SQLiteConnection+Hooks.swift`.
#if canImport(FoundationEssentials) || canImport(Foundation)
self.clearAllHooks()
#endif
sqlite_nio_sqlite3_close(self.handle.raw)
self.handle.raw = nil
}
Expand Down
5 changes: 5 additions & 0 deletions Sources/SQLiteNIO/SQLiteCustomFunction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,12 @@ public final class SQLiteCustomFunction: Hashable {
sqlite_nio_sqlite3_result_error(sqliteContext, error.message, -1)
sqlite_nio_sqlite3_result_error_code(sqliteContext, error.reason.statusCode)
} else {
#if hasFeature(Embedded)
// Interpolating an `any Error` needs reflection, unavailable in Embedded Swift.
sqlite_nio_sqlite3_result_error(sqliteContext, "custom function error", -1)
#else
sqlite_nio_sqlite3_result_error(sqliteContext, "\(error)", -1)
#endif
}
}
}
Expand Down
13 changes: 11 additions & 2 deletions Sources/SQLiteNIO/SQLiteData.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ public typealias SQLiteInt64 = Int
///
/// SQLite supports four data type "affinities" - INTEGER, REAL, TEXT, and BLOB - plus the `NULL` value, which has no
/// innate affinity.
public enum SQLiteData: Equatable, Encodable, CustomStringConvertible, Sendable {
///
/// > Note: The `Encodable` conformance is declared in a conditional extension below, because
/// > `Encoder` is unavailable in Embedded Swift.
public enum SQLiteData: Equatable, CustomStringConvertible, Sendable {
/// `INTEGER` affinity, represented in Swift by `Int`.
case integer(SQLiteInt64)

Expand Down Expand Up @@ -112,7 +115,8 @@ public enum SQLiteData: Equatable, Encodable, CustomStringConvertible, Sendable
}
}

// See `Encodable.encode(to:)`.
// See `Encodable.encode(to:)`. `Encoder` is unavailable in Embedded Swift.
#if !hasFeature(Embedded)
public func encode(to encoder: any Encoder) throws {
var container = encoder.singleValueContainer()
switch self {
Expand All @@ -123,8 +127,13 @@ public enum SQLiteData: Equatable, Encodable, CustomStringConvertible, Sendable
case .null: try container.encodeNil()
}
}
#endif // !hasFeature(Embedded)
}

#if !hasFeature(Embedded)
extension SQLiteData: Encodable {}
#endif

extension SQLiteData {
/// Attempt to interpret an `sqlite3_value` as an equivalent ``SQLiteData``.
init(sqliteValue: OpaquePointer) throws {
Expand Down
8 changes: 7 additions & 1 deletion Sources/SQLiteNIO/SQLiteDataConvertible.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import NIOCore
import NIOFoundationEssentialsCompat
#endif
import FoundationEssentials
#else
#elseif canImport(Foundation)
#if canImport(NIOCore)
import NIOFoundationCompat
#endif
Expand Down Expand Up @@ -112,6 +112,8 @@ extension ByteBuffer: SQLiteDataConvertible {
}
}

// The `Data` bridge needs a Foundation flavor, and Embedded Swift has neither.
#if canImport(FoundationEssentials) || canImport(Foundation)
extension Data: SQLiteDataConvertible {
public init?(sqliteData: SQLiteData) {
guard case .blob(let value) = sqliteData else {
Expand All @@ -126,6 +128,7 @@ extension Data: SQLiteDataConvertible {
.blob(.init(data: self))
}
}
#endif // canImport(FoundationEssentials) || canImport(Foundation)

extension Bool: SQLiteDataConvertible {
public init?(sqliteData: SQLiteData) {
Expand All @@ -140,6 +143,8 @@ extension Bool: SQLiteDataConvertible {
}
}

// The `Date` bridge needs a Foundation flavor, and Embedded Swift has neither.
#if canImport(FoundationEssentials) || canImport(Foundation)
extension Date: SQLiteDataConvertible {
public init?(sqliteData: SQLiteData) {
let value: Double
Expand Down Expand Up @@ -203,3 +208,4 @@ extension Date: SQLiteDataConvertible {
.float(self.timeIntervalSince1970)
}
}
#endif // canImport(FoundationEssentials) || canImport(Foundation)
4 changes: 4 additions & 0 deletions Sources/SQLiteNIO/SQLiteDataType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ public enum SQLiteDataType {
/// `NULL`.
case null

// `any Encodable` requires runtime existentials that Embedded Swift does not provide. The
// method is deprecated and unused, so it is simply elided there.
#if !hasFeature(Embedded)
public func serialize(_ binds: inout [any Encodable]) -> String {
switch self {
case .integer: return "INTEGER"
Expand All @@ -25,4 +28,5 @@ public enum SQLiteDataType {
case .null: return "NULL"
}
}
#endif // !hasFeature(Embedded)
}
7 changes: 7 additions & 0 deletions Sources/SQLiteNIO/SQLiteDatabase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@ public protocol SQLiteDatabase: Sendable {
) -> EventLoopFuture<T>
#endif // canImport(NIOCore)

// Unavailable in Embedded Swift: a generic method requirement cannot be placed in a witness
// table, so keeping it would make `any SQLiteDatabase` unusable there.
// ``SQLiteConnection/withConnection(_:)`` remains available on the concrete type.
#if !hasFeature(Embedded)
/// Call the provided closure with a concrete ``SQLiteConnection`` instance, concurrency version.
///
/// This method is required to provide a connection object which executes all queries directed to it in the
Expand All @@ -92,6 +96,7 @@ public protocol SQLiteDatabase: Sendable {
func withConnection<T>(
_ closure: @escaping @Sendable (SQLiteConnection) async throws -> T
) async throws -> T
#endif // !hasFeature(Embedded)
}

/// Convenience helpers and Concurrency-aware variants.
Expand Down Expand Up @@ -202,10 +207,12 @@ private struct SQLiteDatabaseCustomLogger<D: SQLiteDatabase>: SQLiteDatabase {
self.database.withConnection(closure)
}
#endif // canImport(NIOCore)
#if !hasFeature(Embedded)
// See `SQLiteDatabase.withConnection(_:)`.
func withConnection<T: Sendable>(_ closure: @escaping @Sendable (SQLiteConnection) async throws -> T) async throws -> T {
try await self.database.withConnection(closure)
}
#endif

#if canImport(NIOCore)
// See `SQLiteDatabase.query(_:_:_:)`.
Expand Down
10 changes: 8 additions & 2 deletions Sources/SQLiteNIO/SQLiteError.swift
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import VaporCSQLite
#if canImport(FoundationEssentials)
import FoundationEssentials
#else
#elseif canImport(Foundation)
import Foundation
#endif

public struct SQLiteError: Error, CustomStringConvertible, LocalizedError {
// `LocalizedError` needs one of the Foundation flavors, and Embedded Swift has neither. The
// `errorDescription` witness stays in the type body; only the conformance is conditional.
#if canImport(FoundationEssentials) || canImport(Foundation)
extension SQLiteError: LocalizedError {}
#endif

public struct SQLiteError: Error, CustomStringConvertible {
public let reason: Reason
public let message: String

Expand Down
5 changes: 5 additions & 0 deletions Sources/SQLiteNIO/SQLiteRow.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,12 @@ public struct SQLiteRow: CustomStringConvertible, Sendable {
}

public var description: String {
#if hasFeature(Embedded)
// `Array.description` goes through reflection, which Embedded Swift does not provide.
"[" + self.columns.map { $0.description }.joined(separator: ", ") + "]"
#else
self.columns.description
#endif
}
}

Expand Down