diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index af6666ba..51654fba 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -61,3 +61,13 @@ jobs: env: CARTHAGE_PLATFORM: tvOS run: ./run-tests.sh + build-linux: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Install + run: | + sudo apt-get update -qq + sudo apt-get install -y libsqlite3-dev + - name: Test + run: swift test diff --git a/Documentation/Linux.md b/Documentation/Linux.md new file mode 100644 index 00000000..640ced6f --- /dev/null +++ b/Documentation/Linux.md @@ -0,0 +1,30 @@ +# Linux + +## Limitations + +* Custom functions are currently not supported and crash, caused by a bug in Swift. +See [#1071](https://github.com/stephencelis/SQLite.swift/issues/1071). +* FTS5 might not work, see [#1007](https://github.com/stephencelis/SQLite.swift/issues/1007) + +## Debugging + +### Create and launch docker container + +```shell +$ docker container create swift:focal +$ docker run --cap-add=SYS_PTRACE \ + --security-opt seccomp=unconfined \ + --security-opt apparmor=unconfined \ + -i -t swift:focal bash +``` + +### Compile and run tests in debugger + +```shell +$ apt-get update && apt-get install libsqlite3-dev +$ git clone https://github.com/stephencelis/SQLite.swift.git +$ swift test +$ lldb .build/x86_64-unknown-linux-gnu/debug/SQLite.swiftPackageTests.xctest +(lldb) target create ".build/x86_64-unknown-linux-gnu/debug/SQLite.swiftPackageTests.xctest" +(lldb) run +``` diff --git a/README.md b/README.md index ac044dc0..9242b200 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ syntax _and_ intent. - [Well-documented][See Documentation] - Extensively tested - [SQLCipher][] support via CocoaPods + - Works on [Linux](Documentation/Linux.md) (with some limitations) - Active support at [StackOverflow](https://stackoverflow.com/questions/tagged/sqlite.swift), and [Gitter Chat Room](https://gitter.im/stephencelis/SQLite.swift) diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj index fcdb521c..6aff8045 100644 --- a/SQLite.xcodeproj/project.pbxproj +++ b/SQLite.xcodeproj/project.pbxproj @@ -227,6 +227,7 @@ 19A17399EA9E61235D5D77BF /* CipherTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CipherTests.swift; sourceTree = ""; }; 19A175C1F9CB3BBAB8FCEC7B /* RowTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RowTests.swift; sourceTree = ""; }; 19A178A39ACA9667A62663CC /* Cipher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Cipher.swift; sourceTree = ""; }; + 19A1794B7972D14330A65BBD /* Linux.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = Linux.md; sourceTree = ""; }; 19A1794CC4D7827E997E32A7 /* FoundationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FoundationTests.swift; sourceTree = ""; }; 19A17B93B48B5560E6E51791 /* Fixtures.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Fixtures.swift; sourceTree = ""; }; 19A17BA55DABB480F9020C8A /* DateAndTimeFunctions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DateAndTimeFunctions.swift; sourceTree = ""; }; @@ -498,6 +499,7 @@ EE247B8F1C3F822500AE3E12 /* Index.md */, EE247B901C3F822500AE3E12 /* Resources */, 19A17EA3A313F129011B3FA0 /* Release.md */, + 19A1794B7972D14330A65BBD /* Linux.md */, ); path = Documentation; sourceTree = ""; diff --git a/Sources/SQLite/Core/Connection.swift b/Sources/SQLite/Core/Connection.swift index b2bc2cbd..2c8c408c 100644 --- a/Sources/SQLite/Core/Connection.swift +++ b/Sources/SQLite/Core/Connection.swift @@ -629,12 +629,22 @@ public final class Connection { flags |= SQLITE_DETERMINISTIC } #endif - sqlite3_create_function_v2(handle, function, Int32(argc), flags, - unsafeBitCast(box, to: UnsafeMutableRawPointer.self), { context, argc, value in + let resultCode = sqlite3_create_function_v2(handle, + function, + Int32(argc), + flags, + unsafeBitCast(box, to: UnsafeMutableRawPointer.self), { context, argc, value in let function = unsafeBitCast(sqlite3_user_data(context), to: Function.self) function(context, argc, value) }, nil, nil, nil) - if functions[function] == nil { functions[function] = [:] } + + if let result = Result(errorCode: resultCode, connection: self, statement: nil) { + fatalError("Error creating function: \(result)") + } + + if functions[function] == nil { + functions[function] = [:] // fails on Linux, https://github.com/stephencelis/SQLite.swift/issues/1071 + } functions[function]?[argc] = box } diff --git a/Tests/LinuxMain.swift b/Tests/LinuxMain.swift deleted file mode 100644 index 59796fde..00000000 --- a/Tests/LinuxMain.swift +++ /dev/null @@ -1,6 +0,0 @@ -import XCTest -@testable import SQLiteTests - -XCTMain([ -testCase([ -])]) diff --git a/Tests/SQLiteTests/ConnectionTests.swift b/Tests/SQLiteTests/ConnectionTests.swift index 37b765ab..5bc4d42f 100644 --- a/Tests/SQLiteTests/ConnectionTests.swift +++ b/Tests/SQLiteTests/ConnectionTests.swift @@ -343,6 +343,8 @@ class ConnectionTests: SQLiteTestCase { } } + // https://github.com/stephencelis/SQLite.swift/issues/1071 + #if !os(Linux) func test_createFunction_withArrayArguments() { db.createFunction("hello") { $0[0].map { "Hello, \($0)!" } } @@ -390,6 +392,7 @@ class ConnectionTests: SQLiteTestCase { } } } + #endif func test_concurrent_access_single_connection() { // test can fail on iOS/tvOS 9.x: SQLite compile-time differences? diff --git a/Tests/SQLiteTests/CustomAggregationTests.swift b/Tests/SQLiteTests/CustomAggregationTests.swift index d4569eef..d5232260 100644 --- a/Tests/SQLiteTests/CustomAggregationTests.swift +++ b/Tests/SQLiteTests/CustomAggregationTests.swift @@ -13,6 +13,9 @@ import CSQLite import SQLite3 #endif +// https://github.com/stephencelis/SQLite.swift/issues/1071 +#if !os(Linux) + class CustomAggregationTests: SQLiteTestCase { override func setUp() { super.setUp() @@ -139,6 +142,7 @@ class CustomAggregationTests: SQLiteTestCase { XCTAssertEqual(TestObject.deinits, 3) // the initial value is still retained by the aggregate's state block, so deinits is one less than inits } } +#endif /// This class is used to test that aggregation state variables /// can be reference types and are properly memory managed when diff --git a/Tests/SQLiteTests/CustomFunctionsTests.swift b/Tests/SQLiteTests/CustomFunctionsTests.swift index 8e702e9c..3d897f8e 100644 --- a/Tests/SQLiteTests/CustomFunctionsTests.swift +++ b/Tests/SQLiteTests/CustomFunctionsTests.swift @@ -1,6 +1,9 @@ import XCTest import SQLite +// https://github.com/stephencelis/SQLite.swift/issues/1071 +#if !os(Linux) + class CustomFunctionNoArgsTests: SQLiteTestCase { typealias FunctionNoOptional = () -> Expression typealias FunctionResultOptional = () -> Expression @@ -144,3 +147,5 @@ class CustomFunctionTruncation: SQLiteTestCase { XCTAssertEqual("töl-aa 12", result) } } + +#endif diff --git a/Tests/SQLiteTests/QueryTests.swift b/Tests/SQLiteTests/QueryTests.swift index 3a58e2fc..c80dd0c7 100644 --- a/Tests/SQLiteTests/QueryTests.swift +++ b/Tests/SQLiteTests/QueryTests.swift @@ -290,6 +290,7 @@ class QueryTests: XCTestCase { ) } + #if !os(Linux) // depends on exact JSON serialization func test_insert_encodable_with_nested_encodable() throws { let emails = Table("emails") let value1 = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4, @@ -307,6 +308,7 @@ class QueryTests: XCTestCase { insert ) } + #endif func test_upsert_withOnConflict_compilesInsertOrOnConflictExpression() { assertSQL(