Skip to content
Merged
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
2 changes: 2 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ jobs:
contents: read
uses: vapor/ci/.github/workflows/run-unit-tests.yml@main
secrets: inherit
with:
with_wasm: true

# Make sure downstream dependents still work
dependents-check:
Expand Down
16 changes: 12 additions & 4 deletions Package@swift-5.9.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
// swift-tools-version:5.9
import PackageDescription

let allPlatforms: [Platform] = [.macOS, .macCatalyst, .iOS, .tvOS, .watchOS, .visionOS, .driverKit, .linux, .windows, .android, .wasi, .openbsd]
let nonWASIPlatforms: [Platform] = allPlatforms.filter { $0 != .wasi }

let package = Package(
name: "sqlite-kit",
platforms: [
Expand All @@ -13,16 +16,21 @@ let package = Package(
.library(name: "SQLiteKit", targets: ["SQLiteKit"]),
],
dependencies: [
.package(url: "https://github.com/apple/swift-nio.git", from: "2.65.0"),
.package(url: "https://github.com/vapor/sqlite-nio.git", from: "1.9.0"),
.package(url: "https://github.com/vapor/sql-kit.git", from: "3.29.3"),
.package(url: "https://github.com/vapor/async-kit.git", from: "1.19.0"),
// WASM: upstream swift-nio + standalone nio-async-runtime; wasm-safe vapor forks.
// Pin to tagged fork releases once available.
.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/PassiveLogic/sqlite-nio.git", from: "1.9.0"),
.package(url: "https://github.com/vapor/sql-kit.git", from: "3.33.1"),
.package(url: "https://github.com/PassiveLogic/async-kit.git", from: "1.19.0"),
],
targets: [
.target(
name: "SQLiteKit",
dependencies: [
.product(name: "NIOFoundationCompat", package: "swift-nio"),
.product(name: "NIOAsyncRuntime", package: "nio-async-runtime", condition: .when(platforms: [.wasi])),
.product(name: "NIOPosix", package: "swift-nio", condition: .when(platforms: nonWASIPlatforms)),
.product(name: "AsyncKit", package: "async-kit"),
.product(name: "SQLiteNIO", package: "sqlite-nio"),
.product(name: "SQLKit", package: "sql-kit"),
Expand Down
19 changes: 19 additions & 0 deletions Sources/SQLiteKit/SQLiteConnectionSource.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,20 @@ import Foundation
#endif
import Logging
import AsyncKit
// Use NIOPosix on every host platform and substitute NIOAsyncRuntime on
// WASI. Static `os(WASI)` is the right gate:
// - `canImport(NIOAsyncRuntime)` matches on macOS / Linux too when the
// module is in the dep graph as a transitive, but its types are
// `@available(macOS 15, *)` and break consumers with older deployment
// targets.
// - `canImport(NIOPosix)` matches on WASI as a partial-module stub that
// doesn't actually expose `NIOThreadPool` there, so the import
// link-fails.
#if os(WASI)
import NIOAsyncRuntime
#else
import NIOPosix
#endif
import SQLiteNIO
import NIOCore

Expand All @@ -16,7 +29,13 @@ public struct SQLiteConnectionSource: ConnectionPoolSource, Sendable {
private let threadPool: NIOThreadPool

private var connectionStorage: SQLiteConnection.Storage {
#if os(WASI)
// NOTE: For WASI platforms, file urls and paths cause runtime errors currently. Using
// in-memory connection only as a workaround.
.memory
#else
.file(path: self.actualURL.absoluteString)
#endif
}

/// Create a new ``SQLiteConnectionSource``.
Expand Down
18 changes: 13 additions & 5 deletions Tests/SQLiteKitTests/SQLiteKitTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -105,10 +105,11 @@ final class SQLiteKitTests: XCTestCase {
)

let conn2 = try await source.makeConnection(logger: self.connection.logger, on: MultiThreadedEventLoopGroup.singleton.any()).get()
defer { try! conn2.close().wait() }

let res2 = try await conn2.query("PRAGMA foreign_keys").get()
XCTAssertEqual(res2[0].column("foreign_keys"), .integer(0))

try! await conn2.close().get()
}

func testJSONStringColumn() async throws {
Expand All @@ -123,6 +124,11 @@ final class SQLiteKitTests: XCTestCase {
XCTAssertEqual(bar.baz, "qux")
}

// NOTE: The following test doesn't work in a runtime environment
// due to reliance on temp files. The test is elided for now
// for WASI targets, but could be used in the future once
// a persistence solution is working for WASI platforms.
#if !os(WASI)
func testMultipleInMemoryDatabases() async throws {
let a = SQLiteConnectionSource(
configuration: .init(storage: .memory, enableForeignKeys: true),
Expand All @@ -134,19 +140,21 @@ final class SQLiteKitTests: XCTestCase {
)

let a1 = try await a.makeConnection(logger: .init(label: "test"), on: MultiThreadedEventLoopGroup.singleton.any()).get()
defer { try! a1.close().wait() }
let a2 = try await a.makeConnection(logger: .init(label: "test"), on: MultiThreadedEventLoopGroup.singleton.any()).get()
defer { try! a2.close().wait() }
let b1 = try await b.makeConnection(logger: .init(label: "test"), on: MultiThreadedEventLoopGroup.singleton.any()).get()
defer { try! b1.close().wait() }
let b2 = try await b.makeConnection(logger: .init(label: "test"), on: MultiThreadedEventLoopGroup.singleton.any()).get()
defer { try! b2.close().wait() }

_ = try await a1.query("CREATE TABLE foo (bar INTEGER)").get()
_ = try await a2.query("SELECT * FROM foo").get()
_ = try await b1.query("CREATE TABLE foo (bar INTEGER)").get()
_ = try await b2.query("SELECT * FROM foo").get()

try! await b2.close().get()
try! await b1.close().get()
try! await a2.close().get()
try! await a1.close().get()
}
#endif // !os(WASI)

// https://github.com/vapor/sqlite-kit/issues/56
func testDoubleConstraintError() async throws {
Expand Down