diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ac7a45f..5ff3d44 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -39,3 +39,4 @@ jobs: with: with_musl: true with_android: true + with_wasm: true diff --git a/Package.swift b/Package.swift index e8b1eb7..81dc20d 100644 --- a/Package.swift +++ b/Package.swift @@ -1,6 +1,12 @@ // swift-tools-version:5.10 import PackageDescription +/// This list matches the [supported platforms on the Swift 5.10 release of SPM](https://github.com/swiftlang/swift-package-manager/blob/release/5.10/Sources/PackageDescription/SupportedPlatforms.swift#L34-L71) +/// 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 wasiPlatform: [Platform] = [.wasi] + let package = Package( name: "sqlite-nio", platforms: [ @@ -13,7 +19,8 @@ let package = Package( .library(name: "SQLiteNIO", targets: ["SQLiteNIO"]), ], dependencies: [ - .package(url: "https://github.com/apple/swift-nio.git", from: "2.65.0"), + .package(url: "https://github.com/apple/swift-nio.git", from: "2.89.0"), + .package(url: "https://github.com/PassiveLogic/nio-async-runtime.git", from: "1.0.0"), .package(url: "https://github.com/apple/swift-log.git", from: "1.5.4"), ], targets: [ @@ -38,7 +45,8 @@ let package = Package( .target(name: "CSQLite"), .product(name: "Logging", package: "swift-log"), .product(name: "NIOCore", package: "swift-nio"), - .product(name: "NIOPosix", package: "swift-nio"), + .product(name: "NIOAsyncRuntime", package: "nio-async-runtime", condition: .when(platforms: wasiPlatform)), + .product(name: "NIOPosix", package: "swift-nio", condition: .when(platforms: nonWASIPlatforms)), .product(name: "NIOFoundationCompat", package: "swift-nio"), ], swiftSettings: swiftSettings @@ -92,7 +100,9 @@ var sqliteCSettings: [CSetting] { [ .define("SQLITE_OMIT_TCL_VARIABLE"), .define("SQLITE_OMIT_TRACE"), .define("SQLITE_SECURE_DELETE"), - .define("SQLITE_THREADSAFE", to: "1"), + .define("SQLITE_THREADSAFE", to: "1", .when(platforms: nonWASIPlatforms)), + // WASI is single threaded and lacks pthread support in official SDKs. + .define("SQLITE_THREADSAFE", to: "0", .when(platforms: wasiPlatform)), .define("SQLITE_UNTESTABLE"), .define("SQLITE_USE_URI"), ] } diff --git a/Sources/SQLiteNIO/Exports.swift b/Sources/SQLiteNIO/Exports.swift index 0542622..b7d59df 100644 --- a/Sources/SQLiteNIO/Exports.swift +++ b/Sources/SQLiteNIO/Exports.swift @@ -1,5 +1,16 @@ @_documentation(visibility: internal) @_exported import struct NIOCore.ByteBuffer + +#if canImport(NIOPosix) @_documentation(visibility: internal) @_exported import class NIOPosix.NIOThreadPool +#elseif canImport(NIOAsyncRuntime) +@_documentation(visibility: internal) @_exported import class NIOAsyncRuntime.NIOThreadPool +#endif + @_documentation(visibility: internal) @_exported import protocol NIOCore.EventLoop @_documentation(visibility: internal) @_exported import protocol NIOCore.EventLoopGroup + +#if canImport(NIOPosix) @_documentation(visibility: internal) @_exported import class NIOPosix.MultiThreadedEventLoopGroup +#elseif canImport(NIOAsyncRuntime) +@_documentation(visibility: internal) @_exported import class NIOAsyncRuntime.MultiThreadedEventLoopGroup +#endif diff --git a/Sources/SQLiteNIO/SQLiteConnection.swift b/Sources/SQLiteNIO/SQLiteConnection.swift index a728806..9cb6598 100644 --- a/Sources/SQLiteNIO/SQLiteConnection.swift +++ b/Sources/SQLiteNIO/SQLiteConnection.swift @@ -1,6 +1,10 @@ import NIOConcurrencyHelpers import NIOCore +#if canImport(NIOAsyncRuntime) +import NIOAsyncRuntime +#elseif canImport(NIOPosix) import NIOPosix +#endif import CSQLite import Logging diff --git a/Sources/SQLiteNIO/SQLiteData.swift b/Sources/SQLiteNIO/SQLiteData.swift index adf7588..8455a15 100644 --- a/Sources/SQLiteNIO/SQLiteData.swift +++ b/Sources/SQLiteNIO/SQLiteData.swift @@ -1,13 +1,25 @@ import CSQLite import NIOCore +#if _pointerBitWidth(_64) +/// We use `Int` on 64-bit systems due to public API breakage concerns. +public typealias SQLiteInt64 = Int // 64-bit platform, Int = 64 bits +#elseif _pointerBitWidth(_32) +public typealias SQLiteInt64 = Int64 // On 32-bit platforms, we want to use 64 bit integers. +#else +/// If you hit errors here, you may simply need to add a new architectural bit size above (e.g. _128) +/// when that exists. Or, if [this proposal for pointerBitWidth](https://forums.swift.org/t/pitch-pointer-bit-width-compile-time-conditional/59572) +/// ever lands in a published Swift version, then the above conditionals may need to be adjusted: +#error("Unsupported integer size") +#endif + /// Encapsulates a single data item provided by or to SQLite. /// /// 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 { /// `INTEGER` affinity, represented in Swift by `Int`. - case integer(Int) + case integer(SQLiteInt64) /// `REAL` affinity, represented in Swift by `Double`. case float(Double) @@ -25,16 +37,12 @@ public enum SQLiteData: Equatable, Encodable, CustomStringConvertible, Sendable /// /// If the data has `REAL` or `TEXT` affinity, an attempt is made to interpret the value as an integer. `BLOB` /// and `NULL` values always return `nil`. - public var integer: Int? { + public var integer: SQLiteInt64? { switch self { - case .integer(let integer): - return integer - case .float(let double): - return Int(double) - case .text(let string): - return Int(string) - case .blob, .null: - return nil + case .integer(let integer): integer + case .float(let double): .init(double) + case .text(let string): .init(string) + case .blob, .null: nil } } @@ -137,7 +145,7 @@ extension SQLiteData { case SQLITE_NULL: self = .null case SQLITE_INTEGER: - self = .integer(Int(sqlite_nio_sqlite3_value_int64(sqliteValue))) + self = .integer(.init(sqlite_nio_sqlite3_value_int64(sqliteValue))) case SQLITE_FLOAT: self = .float(sqlite_nio_sqlite3_value_double(sqliteValue)) case SQLITE_TEXT: diff --git a/Sources/SQLiteNIO/SQLiteDatabase.swift b/Sources/SQLiteNIO/SQLiteDatabase.swift index e236822..8fc3518 100644 --- a/Sources/SQLiteNIO/SQLiteDatabase.swift +++ b/Sources/SQLiteNIO/SQLiteDatabase.swift @@ -1,5 +1,4 @@ import NIOCore -import NIOPosix import CSQLite import Logging diff --git a/Sources/SQLiteNIO/SQLiteStatement.swift b/Sources/SQLiteNIO/SQLiteStatement.swift index e32c897..21912b4 100644 --- a/Sources/SQLiteNIO/SQLiteStatement.swift +++ b/Sources/SQLiteNIO/SQLiteStatement.swift @@ -92,7 +92,7 @@ struct SQLiteStatement { private func data(at offset: Int32) throws -> SQLiteData { switch sqlite_nio_sqlite3_column_type(self.handle, offset) { case SQLITE_INTEGER: - return .integer(Int(sqlite_nio_sqlite3_column_int64(self.handle, offset))) + return .integer(.init(sqlite_nio_sqlite3_column_int64(self.handle, offset))) case SQLITE_FLOAT: return .float(Double(sqlite_nio_sqlite3_column_double(self.handle, offset))) case SQLITE_TEXT: diff --git a/Tests/SQLiteNIOTests/SQLiteNIOTests.swift b/Tests/SQLiteNIOTests/SQLiteNIOTests.swift index 602ef90..766a453 100644 --- a/Tests/SQLiteNIOTests/SQLiteNIOTests.swift +++ b/Tests/SQLiteNIOTests/SQLiteNIOTests.swift @@ -1,8 +1,6 @@ import XCTest import SQLiteNIO import Logging -import NIOCore -import NIOPosix import NIOFoundationCompat /// Run the provided closure with an opened ``SQLiteConnection`` using an in-memory database and the singleton thread