diff --git a/.travis.yml b/.travis.yml index 833c67d5..98975aba 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,6 +11,7 @@ matrix: - env: VALIDATOR_SUBSPEC="none" - env: VALIDATOR_SUBSPEC="standard" - env: VALIDATOR_SUBSPEC="standalone" + - env: VALIDATOR_SUBSPEC="SQLCipher" before_install: - gem update bundler - gem install xcpretty --no-document diff --git a/CHANGELOG.md b/CHANGELOG.md index ca8da53e..394f651f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,15 +1,21 @@ -0.11.1 (unreleased, [diff][diff-0.11.1]) + +0.11.1 (06-12-2016), [diff][diff-0.11.1] ======================================== -* Made lastInsertRowid consistent with other SQLite wrappers (#532) +* Integrate SQLCipher via CocoaPods ([#546][], [#553][]) +* Made lastInsertRowid consistent with other SQLite wrappers ([#532][]) * Fix for ~= operator used with Double ranges * Various documentation updates -0.11.0 (19-10-2016) +0.11.0 (10-19-2016) =================== * Swift3 migration ([diff][diff-0.11.0]) -[diff-0.11.1]: https://github.com/stephencelis/SQLite.swift/compare/0.11.1...0.11.0 +[diff-0.11.1]: https://github.com/stephencelis/SQLite.swift/compare/0.11.0...0.11.1 [diff-0.11.0]: https://github.com/stephencelis/SQLite.swift/compare/0.10.1...0.11.0 + +[#532]: https://github.com/stephencelis/SQLite.swift/issues/532 +[#546]: https://github.com/stephencelis/SQLite.swift/issues/546 +[#553]: https://github.com/stephencelis/SQLite.swift/pull/553 diff --git a/Documentation/Index.md b/Documentation/Index.md index 190c1d2d..ae3a8a0b 100644 --- a/Documentation/Index.md +++ b/Documentation/Index.md @@ -114,7 +114,7 @@ install SQLite.swift with Carthage: 4. Run `pod install --repo-update`. - #### Requiring a specific version of SQLite +#### Requiring a specific version of SQLite If you want to use a more recent version of SQLite than what is provided with the OS you can require the `standalone` subspec: @@ -131,10 +131,30 @@ By default this will use the most recent version of SQLite without any extras. I See the [sqlite3 podspec][sqlite3pod] for more details. +#### Using SQLite.swift with SQLCipher + +If you want to use [SQLCipher][] with SQLite.swift you can require the `SQLCipher` +subspec in your Podfile: + +``` ruby + pod 'SQLite.swift/SQLCipher', '~> 0.11.1' +``` + +This will automatically add a dependency to the SQLCipher pod as well as extend +`Connection` with methods to change the database key: + +``` swift +import SQLite + +let db = try Connection("path/to/db.sqlite3") +try db.key("secret") +try db.rekey("another secret") +``` + [CocoaPods]: https://cocoapods.org [CocoaPods Installation]: https://guides.cocoapods.org/using/getting-started.html#getting-started [sqlite3pod]: https://github.com/clemensg/sqlite3pod - +[SQLCipher]: https://www.zetetic.net/sqlcipher/ ### Manual diff --git a/Documentation/Planning.md b/Documentation/Planning.md index 12555703..a0c6d049 100644 --- a/Documentation/Planning.md +++ b/Documentation/Planning.md @@ -1,24 +1,19 @@ # SQLite.swift Planning -This document captures both near term steps (aka Roadmap) and feature requests. +This document captures both near term steps (aka Roadmap) and feature requests. The goal is to add some visibility and guidance for future additions and Pull Requests, as well as to keep the Issues list clear of enhancement requests so that bugs are more visible. ## Roadmap _Lists agreed upon next steps in approximate priority order._ - * ~~publish to the CocoaPods directory at https://cocoapods.org/, per [#257](https://github.com/stephencelis/SQLite.swift/issues/257)~~ _jan 7, 2016_) - * add SQLCipher back into the product as a separate repo, per [#311](https://github.com/stephencelis/SQLite.swift/issues/311), _in progress jan 6, 2016_ - - ## Feature Requests _A gathering point for ideas for new features. In general, the corresponding issue will be closed once it is added here, with the assumption that it will be referred to when it comes time to add the corresponding feature._ ### Packaging - * add TV OS support, per [#272](https://github.com/stephencelis/SQLite.swift/issues/272) - currently pending SQLiteCipher merge and/or adding ability for user to pick their preferred [SQLCipher](https://github.com/sqlcipher/sqlcipher) branch - * linux support via Swift Package Manager, per [#315](https://github.com/stephencelis/SQLite.swift/issues/315) + * linux support via Swift Package Manager, per [#315](https://github.com/stephencelis/SQLite.swift/issues/315), _in progress_: [#548](https://github.com/stephencelis/SQLite.swift/pull/548) ### Features diff --git a/README.md b/README.md index 14c0f6f6..a9b44abe 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ syntax _and_ intent. - [Full-text search][] support - [Well-documented][See Documentation] - Extensively tested - - Companion project has [SQLCipher support](https://github.com/stephencelis/SQLiteCipher.swift) + - SQLCipher support via CocoaPods - Active support at [StackOverflow](http://stackoverflow.com/questions/tagged/sqlite.swift), and [Gitter Chat Room](https://gitter.im/stephencelis/SQLite.swift) (_experimental_) [Full-text search]: Documentation/Index.md#full-text-search @@ -224,7 +224,6 @@ file](./LICENSE.txt) for more information. These projects enhance or use SQLite.swift: - - [SQLiteCipher.swift](https://github.com/stephencelis/SQLiteCipher.swift) - [SQLiteMigrationManager.swift](https://github.com/garriguv/SQLiteMigrationManager.swift) (inspired by [FMDBMigrationManager](https://github.com/layerhq/FMDBMigrationManager)) diff --git a/SQLite.swift.podspec b/SQLite.swift.podspec index ac7ed8bb..ad423298 100644 --- a/SQLite.swift.podspec +++ b/SQLite.swift.podspec @@ -26,6 +26,7 @@ Pod::Spec.new do |s| s.subspec 'standard' do |ss| ss.source_files = 'SQLite/**/*.{c,h,m,swift}' + ss.exclude_files = 'SQLite/Extensions/Cipher.swift' ss.private_header_files = 'SQLite/Core/fts3_tokenizer.h' ss.library = 'sqlite3' @@ -47,9 +48,23 @@ Pod::Spec.new do |s| s.subspec 'standalone' do |ss| ss.source_files = 'SQLite/**/*.{c,h,m,swift}' + ss.exclude_files = 'SQLite/Extensions/Cipher.swift' ss.private_header_files = 'SQLite/Core/fts3_tokenizer.h' - ss.xcconfig = { 'OTHER_SWIFT_FLAGS' => '$(inherited) -DSQLITE_SWIFT_STANDALONE' } + ss.xcconfig = { + 'OTHER_SWIFT_FLAGS' => '$(inherited) -DSQLITE_SWIFT_STANDALONE' + } + + ss.dependency 'sqlite3', '>= 3.14.0' + end + + s.subspec 'SQLCipher' do |ss| + ss.source_files = 'SQLite/**/*.{c,h,m,swift}' + ss.private_header_files = 'SQLite/Core/fts3_tokenizer.h' + ss.xcconfig = { + 'OTHER_SWIFT_FLAGS' => '$(inherited) -DSQLITE_SWIFT_SQLCIPHER', + 'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) SQLITE_HAS_CODEC=1' + } - ss.dependency 'sqlite3' + ss.dependency 'SQLCipher', '>= 3.4.0' end end diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj index 7b33eac6..531d5713 100644 --- a/SQLite.xcodeproj/project.pbxproj +++ b/SQLite.xcodeproj/project.pbxproj @@ -48,10 +48,17 @@ 03A65E971C6BB3210062603F /* libsqlite3.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 03A65E961C6BB3210062603F /* libsqlite3.tbd */; }; 19A1717B10CC941ACB5533D6 /* FTS5.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1730E4390C775C25677D1 /* FTS5.swift */; }; 19A171E6FA242F72A308C594 /* FTS5Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1721B8984686B9963B45D /* FTS5Tests.swift */; }; + 19A171F12AB8B07F2FD7201A /* Cipher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A178A39ACA9667A62663CC /* Cipher.swift */; }; 19A17254FBA7894891F7297B /* FTS5Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1721B8984686B9963B45D /* FTS5Tests.swift */; }; 19A174D78559CD30679BCCCB /* FTS5Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1721B8984686B9963B45D /* FTS5Tests.swift */; }; 19A1750CEE9B05267995CF3D /* FTS5.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1730E4390C775C25677D1 /* FTS5.swift */; }; + 19A177CC33F2E6A24AF90B02 /* CipherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17399EA9E61235D5D77BF /* CipherTests.swift */; }; 19A178072B371489E6A1E839 /* FoundationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1794CC4D7827E997E32A7 /* FoundationTests.swift */; }; + 19A17835FD5886FDC5A3228F /* Cipher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A178A39ACA9667A62663CC /* Cipher.swift */; }; + 19A179A0C45377CB09BB358C /* CipherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17399EA9E61235D5D77BF /* CipherTests.swift */; }; + 19A179CCF9671E345E5A9811 /* Cipher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A178A39ACA9667A62663CC /* Cipher.swift */; }; + 19A179E76EA6207669B60C1B /* Cipher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A178A39ACA9667A62663CC /* Cipher.swift */; }; + 19A17C4B951CB054EE48AB1C /* CipherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17399EA9E61235D5D77BF /* CipherTests.swift */; }; 19A17E04C4C0956715C5676A /* FoundationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1794CC4D7827E997E32A7 /* FoundationTests.swift */; }; 19A17EC0D68BA8C03288ADF7 /* FTS5.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1730E4390C775C25677D1 /* FTS5.swift */; }; 19A17FB80B94E882050AA908 /* FoundationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1794CC4D7827E997E32A7 /* FoundationTests.swift */; }; @@ -189,6 +196,8 @@ 03A65E961C6BB3210062603F /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS.sdk/usr/lib/libsqlite3.tbd; sourceTree = DEVELOPER_DIR; }; 19A1721B8984686B9963B45D /* FTS5Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FTS5Tests.swift; sourceTree = ""; }; 19A1730E4390C775C25677D1 /* FTS5.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FTS5.swift; sourceTree = ""; }; + 19A17399EA9E61235D5D77BF /* CipherTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CipherTests.swift; sourceTree = ""; }; + 19A178A39ACA9667A62663CC /* Cipher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Cipher.swift; sourceTree = ""; }; 19A1794CC4D7827E997E32A7 /* FoundationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FoundationTests.swift; sourceTree = ""; }; 39548A631CA63C740003E3B5 /* module.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; path = module.modulemap; sourceTree = ""; }; 39548A651CA63C740003E3B5 /* module.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; path = module.modulemap; sourceTree = ""; }; @@ -455,6 +464,7 @@ EE247AE41C3F04ED00AE3E12 /* Info.plist */, 19A1721B8984686B9963B45D /* FTS5Tests.swift */, 19A1794CC4D7827E997E32A7 /* FoundationTests.swift */, + 19A17399EA9E61235D5D77BF /* CipherTests.swift */, ); path = SQLiteTests; sourceTree = ""; @@ -479,6 +489,7 @@ EE247AF51C3F06E900AE3E12 /* FTS4.swift */, EE247AF61C3F06E900AE3E12 /* RTree.swift */, 19A1730E4390C775C25677D1 /* FTS5.swift */, + 19A178A39ACA9667A62663CC /* Cipher.swift */, ); path = Extensions; sourceTree = ""; @@ -845,6 +856,7 @@ 03A65E771C6BB2E60062603F /* Connection.swift in Sources */, 03A65E7E1C6BB2FB0062603F /* AggregateFunctions.swift in Sources */, 19A17EC0D68BA8C03288ADF7 /* FTS5.swift in Sources */, + 19A179E76EA6207669B60C1B /* Cipher.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -869,6 +881,7 @@ 03A65E951C6BB3030062603F /* TestHelpers.swift in Sources */, 19A17254FBA7894891F7297B /* FTS5Tests.swift in Sources */, 19A17E04C4C0956715C5676A /* FoundationTests.swift in Sources */, + 19A179A0C45377CB09BB358C /* CipherTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -895,6 +908,7 @@ 3D67B3F61DB246D100A4F4C6 /* Setter.swift in Sources */, 3D67B3E71DB246BA00A4F4C6 /* Blob.swift in Sources */, 3D67B3E81DB246BA00A4F4C6 /* Connection.swift in Sources */, + 19A179CCF9671E345E5A9811 /* Cipher.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -921,6 +935,7 @@ EE247B071C3F06E900AE3E12 /* Statement.swift in Sources */, EE247B0D1C3F06E900AE3E12 /* AggregateFunctions.swift in Sources */, 19A1717B10CC941ACB5533D6 /* FTS5.swift in Sources */, + 19A171F12AB8B07F2FD7201A /* Cipher.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -945,6 +960,7 @@ EE247B251C3F137700AE3E12 /* ConnectionTests.swift in Sources */, 19A171E6FA242F72A308C594 /* FTS5Tests.swift in Sources */, 19A17FB80B94E882050AA908 /* FoundationTests.swift in Sources */, + 19A177CC33F2E6A24AF90B02 /* CipherTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -971,6 +987,7 @@ EE247B661C3F3FEC00AE3E12 /* Connection.swift in Sources */, EE247B6D1C3F3FEC00AE3E12 /* AggregateFunctions.swift in Sources */, 19A1750CEE9B05267995CF3D /* FTS5.swift in Sources */, + 19A17835FD5886FDC5A3228F /* Cipher.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -995,6 +1012,7 @@ EE247B5B1C3F3FC700AE3E12 /* QueryTests.swift in Sources */, 19A174D78559CD30679BCCCB /* FTS5Tests.swift in Sources */, 19A178072B371489E6A1E839 /* FoundationTests.swift in Sources */, + 19A17C4B951CB054EE48AB1C /* CipherTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/SQLite/Core/Connection.swift b/SQLite/Core/Connection.swift index 6b447305..cad25996 100644 --- a/SQLite/Core/Connection.swift +++ b/SQLite/Core/Connection.swift @@ -26,6 +26,8 @@ import Foundation.NSUUID import Dispatch #if SQLITE_SWIFT_STANDALONE import sqlite3 +#elseif SQLITE_SWIFT_SQLCIPHER +import SQLCipher #elseif COCOAPODS import CSQLite #endif @@ -411,11 +413,15 @@ public final class Connection { /// /// db.trace { SQL in print(SQL) } public func trace(_ callback: ((String) -> Void)?) { - if #available(iOS 10.0, OSX 10.12, tvOS 10.0, watchOS 3.0, *) { - trace_v2(callback) - } else { + #if SQLITE_SWIFT_SQLCIPHER trace_v1(callback) - } + #else + if #available(iOS 10.0, OSX 10.12, tvOS 10.0, watchOS 3.0, *) { + trace_v2(callback) + } else { + trace_v1(callback) + } + #endif } fileprivate func trace_v1(_ callback: ((String) -> Void)?) { @@ -439,37 +445,8 @@ public final class Connection { trace = box } - @available(iOS 10.0, OSX 10.12, tvOS 10.0, watchOS 3.0, *) - fileprivate func trace_v2(_ callback: ((String) -> Void)?) { - guard let callback = callback else { - // If the X callback is NULL or if the M mask is zero, then tracing is disabled. - sqlite3_trace_v2(handle, 0 /* mask */, nil /* xCallback */, nil /* pCtx */) - trace = nil - return - } - let box: Trace = { (pointer: UnsafeRawPointer) in - callback(String(cString: pointer.assumingMemoryBound(to: UInt8.self))) - } - sqlite3_trace_v2(handle, - UInt32(SQLITE_TRACE_STMT) /* mask */, - { - // A trace callback is invoked with four arguments: callback(T,C,P,X). - // The T argument is one of the SQLITE_TRACE constants to indicate why the - // callback was invoked. The C argument is a copy of the context pointer. - // The P and X arguments are pointers whose meanings depend on T. - (T: UInt32, C: UnsafeMutableRawPointer?, P: UnsafeMutableRawPointer?, X: UnsafeMutableRawPointer?) in - if let P = P, - let expandedSQL = sqlite3_expanded_sql(OpaquePointer(P)) { - unsafeBitCast(C, to: Trace.self)(expandedSQL) - sqlite3_free(expandedSQL) - } - return Int32(0) // currently ignored - }, - unsafeBitCast(box, to: UnsafeMutableRawPointer.self) /* pCtx */ - ) - trace = box - } + fileprivate typealias Trace = @convention(block) (UnsafeRawPointer) -> Void fileprivate var trace: Trace? @@ -674,7 +651,7 @@ public final class Connection { return success! } - @discardableResult public func check(_ resultCode: Int32, statement: Statement? = nil) throws -> Int32 { + @discardableResult func check(_ resultCode: Int32, statement: Statement? = nil) throws -> Int32 { guard let error = Result(errorCode: resultCode, connection: self, statement: statement) else { return resultCode } @@ -740,3 +717,39 @@ extension Result : CustomStringConvertible { } } + +#if !SQLITE_SWIFT_SQLCIPHER +@available(iOS 10.0, OSX 10.12, tvOS 10.0, watchOS 3.0, *) +extension Connection { + fileprivate func trace_v2(_ callback: ((String) -> Void)?) { + guard let callback = callback else { + // If the X callback is NULL or if the M mask is zero, then tracing is disabled. + sqlite3_trace_v2(handle, 0 /* mask */, nil /* xCallback */, nil /* pCtx */) + trace = nil + return + } + + let box: Trace = { (pointer: UnsafeRawPointer) in + callback(String(cString: pointer.assumingMemoryBound(to: UInt8.self))) + } + sqlite3_trace_v2(handle, + UInt32(SQLITE_TRACE_STMT) /* mask */, + { + // A trace callback is invoked with four arguments: callback(T,C,P,X). + // The T argument is one of the SQLITE_TRACE constants to indicate why the + // callback was invoked. The C argument is a copy of the context pointer. + // The P and X arguments are pointers whose meanings depend on T. + (T: UInt32, C: UnsafeMutableRawPointer?, P: UnsafeMutableRawPointer?, X: UnsafeMutableRawPointer?) in + if let P = P, + let expandedSQL = sqlite3_expanded_sql(OpaquePointer(P)) { + unsafeBitCast(C, to: Trace.self)(expandedSQL) + sqlite3_free(expandedSQL) + } + return Int32(0) // currently ignored + }, + unsafeBitCast(box, to: UnsafeMutableRawPointer.self) /* pCtx */ + ) + trace = box + } +} +#endif diff --git a/SQLite/Core/Statement.swift b/SQLite/Core/Statement.swift index 64b9017f..1dc3e93d 100644 --- a/SQLite/Core/Statement.swift +++ b/SQLite/Core/Statement.swift @@ -24,6 +24,8 @@ #if SQLITE_SWIFT_STANDALONE import sqlite3 +#elseif SQLITE_SWIFT_SQLCIPHER +import SQLCipher #elseif COCOAPODS import CSQLite #endif diff --git a/SQLite/Extensions/Cipher.swift b/SQLite/Extensions/Cipher.swift new file mode 100644 index 00000000..e0b53390 --- /dev/null +++ b/SQLite/Extensions/Cipher.swift @@ -0,0 +1,34 @@ +#if SQLITE_SWIFT_SQLCIPHER +import SQLCipher + +extension Connection { + public func key(_ key: String) throws { + try _key(keyPointer: key, keySize: key.utf8.count) + } + + public func key(_ key: Blob) throws { + try _key(keyPointer: key.bytes, keySize: key.bytes.count) + } + + public func rekey(_ key: String) throws { + try _rekey(keyPointer: key, keySize: key.utf8.count) + } + + public func rekey(_ key: Blob) throws { + try _rekey(keyPointer: key.bytes, keySize: key.bytes.count) + } + + // MARK: - private + private func _key(keyPointer: UnsafePointer, keySize: Int) throws { + try check(sqlite3_key(handle, keyPointer, Int32(keySize))) + try execute( + "CREATE TABLE \"__SQLCipher.swift__\" (\"cipher key check\");\n" + + "DROP TABLE \"__SQLCipher.swift__\";" + ) + } + + private func _rekey(keyPointer: UnsafePointer, keySize: Int) throws { + try check(sqlite3_rekey(handle, keyPointer, Int32(keySize))) + } +} +#endif diff --git a/SQLite/Helpers.swift b/SQLite/Helpers.swift index febdc949..08c9a144 100644 --- a/SQLite/Helpers.swift +++ b/SQLite/Helpers.swift @@ -24,6 +24,8 @@ #if SQLITE_SWIFT_STANDALONE import sqlite3 +#elseif SQLITE_SWIFT_SQLCIPHER +import SQLCipher #elseif COCOAPODS import CSQLite #endif diff --git a/SQLiteTests/CipherTests.swift b/SQLiteTests/CipherTests.swift new file mode 100644 index 00000000..89b67f76 --- /dev/null +++ b/SQLiteTests/CipherTests.swift @@ -0,0 +1,74 @@ +#if SQLITE_SWIFT_SQLCIPHER +import XCTest +import SQLite +import SQLCipher + +class CipherTests: XCTestCase { + + let db = try! Connection() + let db2 = try! Connection() + + override func setUp() { + // db + try! db.key("hello") + + try! db.run("CREATE TABLE foo (bar TEXT)") + try! db.run("INSERT INTO foo (bar) VALUES ('world')") + + // db2 + let keyData = NSMutableData(length: 64)! + let _ = SecRandomCopyBytes(kSecRandomDefault, 64, + keyData.mutableBytes.assumingMemoryBound(to: UInt8.self)) + try! db2.key(Blob(bytes: keyData.bytes, length: keyData.length)) + + try! db2.run("CREATE TABLE foo (bar TEXT)") + try! db2.run("INSERT INTO foo (bar) VALUES ('world')") + + super.setUp() + } + + func test_key() { + XCTAssertEqual(1, try! db.scalar("SELECT count(*) FROM foo") as? Int64) + } + + func test_rekey() { + try! db.rekey("goodbye") + XCTAssertEqual(1, try! db.scalar("SELECT count(*) FROM foo") as? Int64) + } + + func test_data_key() { + XCTAssertEqual(1, try! db2.scalar("SELECT count(*) FROM foo") as? Int64) + } + + func test_data_rekey() { + let keyData = NSMutableData(length: 64)! + let _ = SecRandomCopyBytes(kSecRandomDefault, 64, + keyData.mutableBytes.assumingMemoryBound(to: UInt8.self)) + + try! db2.rekey(Blob(bytes: keyData.bytes, length: keyData.length)) + XCTAssertEqual(1, try! db2.scalar("SELECT count(*) FROM foo") as? Int64) + } + + func test_keyFailure() { + let path = "\(NSTemporaryDirectory())/db.sqlite3" + _ = try? FileManager.default.removeItem(atPath: path) + + let connA = try! Connection(path) + defer { try! FileManager.default.removeItem(atPath: path) } + + try! connA.key("hello") + + let connB = try! Connection(path) + + var rc: Int32? + do { + try connB.key("world") + } catch Result.error(_, let code, _) { + rc = code + } catch { + XCTFail() + } + XCTAssertEqual(SQLITE_NOTADB, rc) + } +} +#endif diff --git a/SQLiteTests/ConnectionTests.swift b/SQLiteTests/ConnectionTests.swift index 6455d30a..3b255fd0 100644 --- a/SQLiteTests/ConnectionTests.swift +++ b/SQLiteTests/ConnectionTests.swift @@ -2,6 +2,8 @@ import XCTest import SQLite #if SQLITE_SWIFT_STANDALONE import sqlite3 +#elseif SQLITE_SWIFT_SQLCIPHER +import SQLCipher #elseif COCOAPODS import CSQLite #endif @@ -338,11 +340,4 @@ class ConnectionTests : SQLiteTestCase { AssertThrows(try stmt.run()) } - func test_check_with_successCode_returnsCode() { - try! XCTAssertEqual(SQLITE_OK, db.check(SQLITE_OK)) - } - - func test_check_with_errorCode_throws() { - XCTAssertThrowsError(try db.check(SQLITE_CONSTRAINT)) - } } diff --git a/SQLiteTests/FTS4Tests.swift b/SQLiteTests/FTS4Tests.swift index b2ccc86c..4373bf8b 100644 --- a/SQLiteTests/FTS4Tests.swift +++ b/SQLiteTests/FTS4Tests.swift @@ -176,7 +176,7 @@ class FTS4ConfigTests : XCTestCase { } class FTS4IntegrationTests : SQLiteTestCase { -#if !SQLITE_SWIFT_STANDALONE +#if !SQLITE_SWIFT_STANDALONE && !SQLITE_SWIFT_SQLCIPHER func test_registerTokenizer_registersTokenizer() { let emails = VirtualTable("emails") let subject = Expression("subject")