Skip to content
Open
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
1 change: 1 addition & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,4 @@ jobs:
with:
with_musl: true
with_android: true
with_wasm: true
18 changes: 16 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -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: [
Expand All @@ -13,7 +19,9 @@ let package = Package(
.library(name: "SQLiteNIO", targets: ["SQLiteNIO"]),
],
dependencies: [
.package(url: "https://github.com/apple/swift-nio.git", from: "2.65.0"),
// TODO: SM: Update swift-nio version once NIOAsyncRuntime is available from swift-nio
// .package(url: "https://github.com/apple/swift-nio.git", from: "2.89.0"),
.package(url: "https://github.com/PassiveLogic/swift-nio.git", branch: "feat/addNIOAsyncRuntimeForWasm"),
.package(url: "https://github.com/apple/swift-log.git", from: "1.5.4"),
],
targets: [
Expand All @@ -38,6 +46,7 @@ let package = Package(
.target(name: "CSQLite"),
.product(name: "Logging", package: "swift-log"),
.product(name: "NIOCore", package: "swift-nio"),
.product(name: "NIOAsyncRuntime", package: "swift-nio", condition: .when(platforms: wasiPlatform)),
.product(name: "NIOPosix", package: "swift-nio"),
.product(name: "NIOFoundationCompat", package: "swift-nio"),
],
Expand Down Expand Up @@ -92,7 +101,12 @@ 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)),
// For now, we use the single threaded sqlite variation for the WASI platform
// since single-threaded operation is the least common denominator capability
// for Wasm executables and it is considered unreliable to use canImport(wasi_pthread)
// in a manifest file to distinguish between the two capabilities.
.define("SQLITE_THREADSAFE", to: "0", .when(platforms: wasiPlatform)),
.define("SQLITE_UNTESTABLE"),
.define("SQLITE_USE_URI"),
] }
11 changes: 11 additions & 0 deletions Sources/SQLiteNIO/Exports.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
@_documentation(visibility: internal) @_exported import struct NIOCore.ByteBuffer

#if canImport(NIOAsyncRuntime)
@_documentation(visibility: internal) @_exported import class NIOAsyncRuntime.AsyncThreadPool
#elseif canImport(NIOPosix)
@_documentation(visibility: internal) @_exported import class NIOPosix.NIOThreadPool
#endif

@_documentation(visibility: internal) @_exported import protocol NIOCore.EventLoop
@_documentation(visibility: internal) @_exported import protocol NIOCore.EventLoopGroup

#if canImport(NIOAsyncRuntime)
@_documentation(visibility: internal) @_exported import class NIOAsyncRuntime.AsyncEventLoopGroup
#elseif canImport(NIOPosix)
@_documentation(visibility: internal) @_exported import class NIOPosix.MultiThreadedEventLoopGroup
#endif
5 changes: 5 additions & 0 deletions Sources/SQLiteNIO/SQLiteConnection.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import NIOConcurrencyHelpers
import NIOCore
#if canImport(NIOAsyncRuntime)
import NIOAsyncRuntime
typealias MultiThreadedEventLoopGroup = AsyncEventLoopGroup
public typealias NIOThreadPool = AsyncThreadPool
#endif
import NIOPosix
import CSQLite
import Logging
Expand Down
30 changes: 19 additions & 11 deletions Sources/SQLiteNIO/SQLiteData.swift
Original file line number Diff line number Diff line change
@@ -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)
Expand All @@ -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
}
}

Expand Down Expand Up @@ -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:
Expand Down
1 change: 0 additions & 1 deletion Sources/SQLiteNIO/SQLiteDatabase.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import NIOCore
import NIOPosix
import CSQLite
import Logging

Expand Down
2 changes: 1 addition & 1 deletion Sources/SQLiteNIO/SQLiteStatement.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
2 changes: 1 addition & 1 deletion Tests/SQLiteNIOTests/SQLiteCustomFunctionTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ final class DatabaseFunctionTests: XCTestCase {
try await withOpenedConnection { conn in
let fn = SQLiteCustomFunction("f", argumentCount: 0) { values in 1 }
try await conn.install(customFunction: fn)
await XCTAssertEqualAsync(Int(1), try await conn.query("SELECT f() as result").first?.column("result")?.integer)
await XCTAssertEqualAsync(.init(1), try await conn.query("SELECT f() as result").first?.column("result")?.integer)
}
}

Expand Down
8 changes: 3 additions & 5 deletions Tests/SQLiteNIOTests/SQLiteNIOTests.swift
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -100,7 +98,7 @@ final class SQLiteNIOTests: XCTestCase {
_ = try await conn.query(#"INSERT INTO test (date) VALUES (?)"#, [date.sqliteData!])
let rows = try await conn.query("SELECT * FROM test")

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

struct MyAggregate: SQLiteCustomAggregate {
var sum: Int = 0
var sum: SQLiteInt64 = 0
mutating func step(_ values: [SQLiteData]) throws {
self.sum += (values.first?.integer ?? 0)
}
Expand Down