Skip to content

Commit ef563aa

Browse files
committed
feat: Enable sqlite-nio to compile to wasm targets.
1 parent 2ab6138 commit ef563aa

9 files changed

Lines changed: 57 additions & 21 deletions

File tree

.github/workflows/test.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,4 @@ jobs:
3939
with:
4040
with_musl: true
4141
with_android: true
42+
with_wasm: true

Package.swift

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
// swift-tools-version:5.10
22
import PackageDescription
33

4+
/// 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)
5+
/// Don't add new platforms here unless raising the swift-tools-version of this manifest.
6+
let allPlatforms: [Platform] = [.macOS, .macCatalyst, .iOS, .tvOS, .watchOS, .visionOS, .driverKit, .linux, .windows, .android, .wasi, .openbsd]
7+
let nonWASIPlatforms: [Platform] = allPlatforms.filter { $0 != .wasi }
8+
let wasiPlatform: [Platform] = [.wasi]
9+
410
let package = Package(
511
name: "sqlite-nio",
612
platforms: [
@@ -13,7 +19,9 @@ let package = Package(
1319
.library(name: "SQLiteNIO", targets: ["SQLiteNIO"]),
1420
],
1521
dependencies: [
16-
.package(url: "https://github.com/apple/swift-nio.git", from: "2.65.0"),
22+
// TODO: SM: Update swift-nio version once NIOAsyncRuntime is available from swift-nio
23+
// .package(url: "https://github.com/apple/swift-nio.git", from: "2.89.0"),
24+
.package(url: "https://github.com/PassiveLogic/swift-nio.git", branch: "feat/addNIOAsyncRuntimeForWasm"),
1725
.package(url: "https://github.com/apple/swift-log.git", from: "1.5.4"),
1826
],
1927
targets: [
@@ -38,6 +46,7 @@ let package = Package(
3846
.target(name: "CSQLite"),
3947
.product(name: "Logging", package: "swift-log"),
4048
.product(name: "NIOCore", package: "swift-nio"),
49+
.product(name: "NIOAsyncRuntime", package: "swift-nio", condition: .when(platforms: wasiPlatform)),
4150
.product(name: "NIOPosix", package: "swift-nio"),
4251
.product(name: "NIOFoundationCompat", package: "swift-nio"),
4352
],
@@ -92,7 +101,12 @@ var sqliteCSettings: [CSetting] { [
92101
.define("SQLITE_OMIT_TCL_VARIABLE"),
93102
.define("SQLITE_OMIT_TRACE"),
94103
.define("SQLITE_SECURE_DELETE"),
95-
.define("SQLITE_THREADSAFE", to: "1"),
104+
.define("SQLITE_THREADSAFE", to: "1", .when(platforms: nonWASIPlatforms)),
105+
// For now, we use the single threaded sqlite variation for the WASI platform
106+
// since single-threaded operation is the least common denominator capability
107+
// for Wasm executables and it is considered unreliable to use canImport(wasi_pthread)
108+
// in a manifest file to distinguish between the two capabilities.
109+
.define("SQLITE_THREADSAFE", to: "0", .when(platforms: wasiPlatform)),
96110
.define("SQLITE_UNTESTABLE"),
97111
.define("SQLITE_USE_URI"),
98112
] }

Sources/SQLiteNIO/Exports.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,16 @@
11
@_documentation(visibility: internal) @_exported import struct NIOCore.ByteBuffer
2+
3+
#if canImport(NIOAsyncRuntime)
4+
@_documentation(visibility: internal) @_exported import class NIOAsyncRuntime.NIOThreadPool
5+
#elseif canImport(NIOPosix)
26
@_documentation(visibility: internal) @_exported import class NIOPosix.NIOThreadPool
7+
#endif
8+
39
@_documentation(visibility: internal) @_exported import protocol NIOCore.EventLoop
410
@_documentation(visibility: internal) @_exported import protocol NIOCore.EventLoopGroup
11+
12+
#if canImport(NIOAsyncRuntime)
13+
@_documentation(visibility: internal) @_exported import class NIOAsyncRuntime.MultiThreadedEventLoopGroup
14+
#elseif canImport(NIOPosix)
515
@_documentation(visibility: internal) @_exported import class NIOPosix.MultiThreadedEventLoopGroup
16+
#endif

Sources/SQLiteNIO/SQLiteConnection.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
import NIOConcurrencyHelpers
22
import NIOCore
3+
#if canImport(NIOAsyncRuntime)
4+
import NIOAsyncRuntime
5+
typealias MultiThreadedEventLoopGroup = AsyncEventLoopGroup
6+
typealias NIOThreadPool = AsyncThreadPool
7+
#endif
38
import NIOPosix
49
import CSQLite
510
import Logging

Sources/SQLiteNIO/SQLiteData.swift

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,25 @@
11
import CSQLite
22
import NIOCore
33

4+
#if _pointerBitWidth(_64)
5+
/// We use `Int` on 64-bit systems due to public API breakage concerns.
6+
public typealias SQLiteInt64 = Int // 64-bit platform, Int = 64 bits
7+
#elseif _pointerBitWidth(_32)
8+
public typealias SQLiteInt64 = Int64 // On 32-bit platforms, we want to use 64 bit integers.
9+
#else
10+
/// If you hit errors here, you may simply need to add a new architectural bit size above (e.g. _128)
11+
/// when that exists. Or, if [this proposal for pointerBitWidth](https://forums.swift.org/t/pitch-pointer-bit-width-compile-time-conditional/59572)
12+
/// ever lands in a published Swift version, then the above conditionals may need to be adjusted:
13+
#error("Unsupported integer size")
14+
#endif
15+
416
/// Encapsulates a single data item provided by or to SQLite.
517
///
618
/// SQLite supports four data type "affinities" - INTEGER, REAL, TEXT, and BLOB - plus the `NULL` value, which has no
719
/// innate affinity.
820
public enum SQLiteData: Equatable, Encodable, CustomStringConvertible, Sendable {
921
/// `INTEGER` affinity, represented in Swift by `Int`.
10-
case integer(Int)
22+
case integer(SQLiteInt64)
1123

1224
/// `REAL` affinity, represented in Swift by `Double`.
1325
case float(Double)
@@ -25,16 +37,12 @@ public enum SQLiteData: Equatable, Encodable, CustomStringConvertible, Sendable
2537
///
2638
/// If the data has `REAL` or `TEXT` affinity, an attempt is made to interpret the value as an integer. `BLOB`
2739
/// and `NULL` values always return `nil`.
28-
public var integer: Int? {
40+
public var integer: SQLiteInt64? {
2941
switch self {
30-
case .integer(let integer):
31-
return integer
32-
case .float(let double):
33-
return Int(double)
34-
case .text(let string):
35-
return Int(string)
36-
case .blob, .null:
37-
return nil
42+
case .integer(let integer): integer
43+
case .float(let double): .init(double)
44+
case .text(let string): .init(string)
45+
case .blob, .null: nil
3846
}
3947
}
4048

@@ -137,7 +145,7 @@ extension SQLiteData {
137145
case SQLITE_NULL:
138146
self = .null
139147
case SQLITE_INTEGER:
140-
self = .integer(Int(sqlite_nio_sqlite3_value_int64(sqliteValue)))
148+
self = .integer(.init(sqlite_nio_sqlite3_value_int64(sqliteValue)))
141149
case SQLITE_FLOAT:
142150
self = .float(sqlite_nio_sqlite3_value_double(sqliteValue))
143151
case SQLITE_TEXT:

Sources/SQLiteNIO/SQLiteDatabase.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import NIOCore
2-
import NIOPosix
32
import CSQLite
43
import Logging
54

Sources/SQLiteNIO/SQLiteStatement.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ struct SQLiteStatement {
9292
private func data(at offset: Int32) throws -> SQLiteData {
9393
switch sqlite_nio_sqlite3_column_type(self.handle, offset) {
9494
case SQLITE_INTEGER:
95-
return .integer(Int(sqlite_nio_sqlite3_column_int64(self.handle, offset)))
95+
return .integer(.init(sqlite_nio_sqlite3_column_int64(self.handle, offset)))
9696
case SQLITE_FLOAT:
9797
return .float(Double(sqlite_nio_sqlite3_column_double(self.handle, offset)))
9898
case SQLITE_TEXT:

Tests/SQLiteNIOTests/SQLiteCustomFunctionTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ final class DatabaseFunctionTests: XCTestCase {
4646
try await withOpenedConnection { conn in
4747
let fn = SQLiteCustomFunction("f", argumentCount: 0) { values in 1 }
4848
try await conn.install(customFunction: fn)
49-
await XCTAssertEqualAsync(Int(1), try await conn.query("SELECT f() as result").first?.column("result")?.integer)
49+
await XCTAssertEqualAsync(.init(1), try await conn.query("SELECT f() as result").first?.column("result")?.integer)
5050
}
5151
}
5252

Tests/SQLiteNIOTests/SQLiteNIOTests.swift

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import XCTest
22
import SQLiteNIO
33
import Logging
4-
import NIOCore
5-
import NIOPosix
64
import NIOFoundationCompat
75

86
/// Run the provided closure with an opened ``SQLiteConnection`` using an in-memory database and the singleton thread
@@ -100,7 +98,7 @@ final class SQLiteNIOTests: XCTestCase {
10098
_ = try await conn.query(#"INSERT INTO test (date) VALUES (?)"#, [date.sqliteData!])
10199
let rows = try await conn.query("SELECT * FROM test")
102100

103-
XCTAssertTrue(rows.first?.column("date") == .float(date.timeIntervalSince1970) || rows.first?.column("date") == .integer(Int(date.timeIntervalSince1970)))
101+
XCTAssertTrue(rows.first?.column("date") == .float(date.timeIntervalSince1970) || rows.first?.column("date") == .integer(.init(date.timeIntervalSince1970)))
104102
XCTAssertEqual(rows.first?.column("date").flatMap(Date.init(sqliteData:))?.description, date.description)
105103
}
106104
}
@@ -109,7 +107,7 @@ final class SQLiteNIOTests: XCTestCase {
109107
try await withOpenedConnection { conn in
110108
let rows = try await conn.query("SELECT 1 as foo, 2 as foo")
111109
let row0 = try XCTUnwrap(rows.first)
112-
var i = 0
110+
var i: SQLiteInt64 = 0
113111
for column in row0.columns {
114112
XCTAssertEqual(column.name, "foo")
115113
i += column.data.integer ?? 0
@@ -127,7 +125,7 @@ final class SQLiteNIOTests: XCTestCase {
127125
_ = try await conn.query(#"INSERT INTO scores (score) VALUES (?), (?), (?)"#, [.integer(3), .integer(4), .integer(5)])
128126

129127
struct MyAggregate: SQLiteCustomAggregate {
130-
var sum: Int = 0
128+
var sum: SQLiteInt64 = 0
131129
mutating func step(_ values: [SQLiteData]) throws {
132130
self.sum += (values.first?.integer ?? 0)
133131
}

0 commit comments

Comments
 (0)