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
8 changes: 8 additions & 0 deletions Sources/SQLiteKit/SQLiteConfiguration.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
#if canImport(Foundation)
import struct Foundation.UUID
#endif

/// Describes a configuration for an SQLite database connection.
public struct SQLiteConfiguration: Sendable {
Expand All @@ -8,7 +10,13 @@ public struct SQLiteConfiguration: Sendable {
///
/// See ``memory(identifier:)``.
public static var memory: Self {
#if canImport(Foundation)
.memory(identifier: UUID().uuidString)
#else
// Foundation.UUID is unavailable in Embedded Swift; derive a unique identifier from
// system randomness instead.
.memory(identifier: "memory-\(UInt64.random(in: .min ... .max))")
#endif
}

/// Specify an SQLite database stored in memory, using a given identifier string.
Expand Down
9 changes: 9 additions & 0 deletions Sources/SQLiteKit/SQLiteConnection+SQLKit.swift
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,9 @@ struct SQLiteDatabaseVersion: SQLDatabaseReportedVersion {
Self.components(of: self.intValue).patch
}

// `as? Self` is a cast to a generic type, which Embedded Swift does not support. There, the
// `SQLDatabaseReportedVersion` protocol's default `stringValue`-based implementations apply.
#if !hasFeature(Embedded)
// See `SQLDatabaseReportedVersion.isEqual(to:)`.
func isEqual(to otherVersion: any SQLDatabaseReportedVersion) -> Bool {
(otherVersion as? Self).map { $0.intValue == self.intValue } ?? false
Expand All @@ -156,6 +159,7 @@ struct SQLiteDatabaseVersion: SQLDatabaseReportedVersion {
(self.patchVersion < $0.patchVersion)))
} ?? false
}
#endif
}

/// Wraps a `SQLiteDatabase` with the `SQLDatabase` protocol.
Expand Down Expand Up @@ -250,7 +254,12 @@ struct SQLiteDatabaseVersion: SQLDatabaseReportedVersion {
let (sql, rawBinds) = self.serialize(query)

if let queryLogLevel = self.queryLogLevel {
#if hasFeature(Embedded)
// Embedded Swift has no reflection, so bound values cannot be interpolated for logging.
self.logger.log(level: queryLogLevel, "Executing query", metadata: ["sql": .string(sql)])
#else
self.logger.log(level: queryLogLevel, "Executing query", metadata: ["sql": .string(sql), "binds": .array(rawBinds.map { .string("\($0)") })])
#endif
}

try await self.database.query(
Expand Down
15 changes: 15 additions & 0 deletions Sources/SQLiteKit/SQLiteDataDecoder.swift
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
#if hasFeature(Embedded)
import SQLiteNIO

/// Placeholder ``SQLiteDataDecoder`` for the Embedded Swift build.
///
/// `Decodable`-based row decoding — the only thing this type does elsewhere — is unavailable in
/// Embedded Swift, where `SQLKit/SQLRow` exposes no generic `decode(column:as:)`. Typed access is
/// performed directly against the concrete `SQLiteRow` via `SQLiteDataConvertible` there. This stub
/// exists only so the ``SQLiteDataDecoder`` API surface (e.g. `sql(decoder:)`) stays uniform.
public struct SQLiteDataDecoder: Sendable {
/// Initialize a ``SQLiteDataDecoder``.
public init() {}
}
#else
import Foundation
import SQLiteNIO
@_spi(CodableUtilities) import SQLKit
Expand Down Expand Up @@ -115,3 +129,4 @@ public struct SQLiteDataDecoder: Sendable {
}
}
}
#endif // hasFeature(Embedded)
29 changes: 29 additions & 0 deletions Sources/SQLiteKit/SQLiteDataEncoder.swift
Original file line number Diff line number Diff line change
@@ -1,3 +1,31 @@
#if hasFeature(Embedded)
import SQLKit
import SQLiteNIO

/// Translates a bound query parameter (an `SQLKit/SQLBindValue`) into an `SQLiteData` value.
///
/// The Codable/JSON path used elsewhere is unavailable in Embedded Swift, so values are mapped
/// directly from their driver-neutral `SQLKit/SQLDataValue` representation there.
public struct SQLiteDataEncoder: Sendable {
/// Initialize a ``SQLiteDataEncoder``.
public init() {}

/// Convert the given bound value to an `SQLiteData` value.
///
/// - Parameter value: The value to convert.
/// - Returns: The converted `SQLiteData` value.
public func encode(_ value: any SQLBindValue & Sendable) throws -> SQLiteData {
switch value.sqlDataValue {
case .integer(let int): return .integer(SQLiteInt64(int))
case .double(let double): return .float(double)
case .string(let string): return .text(string)
case .blob(let bytes): return .blob(bytes)
case .bool(let bool): return .integer(bool ? 1 : 0)
case .null: return .null
}
}
}
#else
#if canImport(NIOCore)
import NIOCore
#endif
Expand Down Expand Up @@ -177,3 +205,4 @@ public struct SQLiteDataEncoder: Sendable {
}
}
}
#endif // hasFeature(Embedded)
6 changes: 6 additions & 0 deletions Sources/SQLiteKit/SQLiteRow+SQLRow.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,17 @@ private struct SQLiteSQLRow: SQLRow {
return data == .null
}

// `SQLRow.decode(column:as:)` is gated out of the SQLKit protocol in Embedded Swift (generic
// requirement + Decodable).
#if !hasFeature(Embedded)
// See `SQLRow.decode(column:as:)`.
func decode<D: Decodable>(column: String, as: D.Type) throws -> D {
guard let data = self.row.column(column) else {
throw MissingColumn(column: column)
}
return try self.decoder.decode(D.self, from: data)
}
#endif
}

/// A legacy deprecated conformance of `SQLiteRow` directly to `SQLRow`. This interface exists solely
Expand All @@ -68,6 +72,7 @@ extension SQLiteNIO.SQLiteRow: SQLKit.SQLRow {
// See `SQLRow.decodeNil(column:)`.
public func decodeNil(column: String) throws -> Bool { (self.column(column) ?? .null) == .null }

#if !hasFeature(Embedded) // Decodable-based row decoding is unavailable in Embedded Swift.
// See `SQLRow.decode(column:as:)`.
public func decode<D: Decodable>(column: String, as: D.Type) throws -> D {
guard let data = self.column(column) else { throw MissingColumn(column: column) }
Expand All @@ -87,4 +92,5 @@ extension SQLiteNIO.SQLiteRow: SQLKit.SQLRow {

// See `SQLRow.decode(model:with:)`.
public func decode<D: Decodable>(model: D.Type, with: SQLRowDecoder) throws -> D { try with.decode(D.self, from: self) }
#endif // !hasFeature(Embedded)
}