Skip to content

Commit 9d0c0b9

Browse files
committed
Add support for ATTACH/DETACH (#30)
1 parent 642d4fa commit 9d0c0b9

File tree

10 files changed

+330
-140
lines changed

10 files changed

+330
-140
lines changed

Package.swift

+2-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ let package = Package(
4141
],
4242
resources: [
4343
.copy("fixtures/encrypted-3.x.sqlite"),
44-
.copy("fixtures/encrypted-4.x.sqlite")
44+
.copy("fixtures/encrypted-4.x.sqlite"),
45+
.copy("fixtures/test.sqlite")
4546
]
4647
)
4748
]

SQLite.xcodeproj/project.pbxproj

+28
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,17 @@
126126
3DDC113626CDBE1900CE369F /* SQLiteObjc.h in Headers */ = {isa = PBXBuildFile; fileRef = 3DDC112E26CDBA0200CE369F /* SQLiteObjc.h */; settings = {ATTRIBUTES = (Public, ); }; };
127127
3DDC113726CDBE1900CE369F /* SQLiteObjc.h in Headers */ = {isa = PBXBuildFile; fileRef = 3DDC112E26CDBA0200CE369F /* SQLiteObjc.h */; settings = {ATTRIBUTES = (Public, ); }; };
128128
3DDC113826CDBE1C00CE369F /* SQLiteObjc.h in Headers */ = {isa = PBXBuildFile; fileRef = 3DDC112E26CDBA0200CE369F /* SQLiteObjc.h */; settings = {ATTRIBUTES = (Public, ); }; };
129+
3DF7B78828842972005DD8CA /* Connection+Attach.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DF7B78728842972005DD8CA /* Connection+Attach.swift */; };
130+
3DF7B78928842972005DD8CA /* Connection+Attach.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DF7B78728842972005DD8CA /* Connection+Attach.swift */; };
131+
3DF7B78A28842972005DD8CA /* Connection+Attach.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DF7B78728842972005DD8CA /* Connection+Attach.swift */; };
132+
3DF7B78B28842972005DD8CA /* Connection+Attach.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DF7B78728842972005DD8CA /* Connection+Attach.swift */; };
133+
3DF7B78D28842C23005DD8CA /* ResultTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DF7B78C28842C23005DD8CA /* ResultTests.swift */; };
134+
3DF7B78E28842C23005DD8CA /* ResultTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DF7B78C28842C23005DD8CA /* ResultTests.swift */; };
135+
3DF7B78F28842C23005DD8CA /* ResultTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DF7B78C28842C23005DD8CA /* ResultTests.swift */; };
136+
3DF7B791288449BA005DD8CA /* URIQueryParameter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DF7B790288449BA005DD8CA /* URIQueryParameter.swift */; };
137+
3DF7B792288449BA005DD8CA /* URIQueryParameter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DF7B790288449BA005DD8CA /* URIQueryParameter.swift */; };
138+
3DF7B793288449BA005DD8CA /* URIQueryParameter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DF7B790288449BA005DD8CA /* URIQueryParameter.swift */; };
139+
3DF7B794288449BA005DD8CA /* URIQueryParameter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DF7B790288449BA005DD8CA /* URIQueryParameter.swift */; };
129140
49EB68C41F7B3CB400D89D40 /* Coding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49EB68C31F7B3CB400D89D40 /* Coding.swift */; };
130141
49EB68C51F7B3CB400D89D40 /* Coding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49EB68C31F7B3CB400D89D40 /* Coding.swift */; };
131142
49EB68C61F7B3CB400D89D40 /* Coding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49EB68C31F7B3CB400D89D40 /* Coding.swift */; };
@@ -263,6 +274,9 @@
263274
3D3C3CCB26E5568800759140 /* SQLite.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = SQLite.playground; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
264275
3D67B3E51DB2469200A4F4C6 /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = Platforms/WatchOS.platform/Developer/SDKs/WatchOS3.0.sdk/usr/lib/libsqlite3.tbd; sourceTree = DEVELOPER_DIR; };
265276
3DDC112E26CDBA0200CE369F /* SQLiteObjc.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SQLiteObjc.h; path = ../SQLiteObjc/include/SQLiteObjc.h; sourceTree = "<group>"; };
277+
3DF7B78728842972005DD8CA /* Connection+Attach.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Connection+Attach.swift"; sourceTree = "<group>"; };
278+
3DF7B78C28842C23005DD8CA /* ResultTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResultTests.swift; sourceTree = "<group>"; };
279+
3DF7B790288449BA005DD8CA /* URIQueryParameter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URIQueryParameter.swift; sourceTree = "<group>"; };
266280
49EB68C31F7B3CB400D89D40 /* Coding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Coding.swift; sourceTree = "<group>"; };
267281
997DF2AD287FC06D00F8DF95 /* Query+with.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Query+with.swift"; sourceTree = "<group>"; };
268282
A121AC451CA35C79005A31D1 /* SQLite.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SQLite.framework; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -461,6 +475,7 @@
461475
D4DB368A20C09C9B00D5A58E /* SelectTests.swift */,
462476
19A17BA6B4E282C1315A115C /* QueryIntegrationTests.swift */,
463477
19A171FDE4D67879B14ACBDF /* FTSIntegrationTests.swift */,
478+
3DF7B78C28842C23005DD8CA /* ResultTests.swift */,
464479
);
465480
name = SQLiteTests;
466481
path = Tests/SQLiteTests;
@@ -479,6 +494,8 @@
479494
02A43A9722738CF100FEC494 /* Backup.swift */,
480495
19A17E723300E5ED3771DCB5 /* Result.swift */,
481496
19A175A9CB446640AE6F2200 /* Connection+Aggregation.swift */,
497+
3DF7B78728842972005DD8CA /* Connection+Attach.swift */,
498+
3DF7B790288449BA005DD8CA /* URIQueryParameter.swift */,
482499
);
483500
path = Core;
484501
sourceTree = "<group>";
@@ -847,12 +864,14 @@
847864
49EB68C61F7B3CB400D89D40 /* Coding.swift in Sources */,
848865
03A65E761C6BB2E60062603F /* Blob.swift in Sources */,
849866
03A65E7D1C6BB2F70062603F /* RTree.swift in Sources */,
867+
3DF7B793288449BA005DD8CA /* URIQueryParameter.swift in Sources */,
850868
03A65E791C6BB2EF0062603F /* SQLiteObjc.m in Sources */,
851869
03A65E7B1C6BB2F70062603F /* Value.swift in Sources */,
852870
03A65E821C6BB2FB0062603F /* Expression.swift in Sources */,
853871
03A65E731C6BB2D80062603F /* Foundation.swift in Sources */,
854872
03A65E7F1C6BB2FB0062603F /* Collation.swift in Sources */,
855873
03A65E861C6BB2FB0062603F /* Setter.swift in Sources */,
874+
3DF7B78A28842972005DD8CA /* Connection+Attach.swift in Sources */,
856875
03A65E811C6BB2FB0062603F /* CustomFunctions.swift in Sources */,
857876
03A65E7A1C6BB2F70062603F /* Statement.swift in Sources */,
858877
03A65E741C6BB2DA0062603F /* Helpers.swift in Sources */,
@@ -877,6 +896,7 @@
877896
isa = PBXSourcesBuildPhase;
878897
buildActionMask = 2147483647;
879898
files = (
899+
3DF7B78F28842C23005DD8CA /* ResultTests.swift in Sources */,
880900
03A65E881C6BB3030062603F /* BlobTests.swift in Sources */,
881901
03A65E901C6BB3030062603F /* RTreeTests.swift in Sources */,
882902
03A65E941C6BB3030062603F /* ValueTests.swift in Sources */,
@@ -910,6 +930,7 @@
910930
buildActionMask = 2147483647;
911931
files = (
912932
3D67B3F91DB246E700A4F4C6 /* SQLiteObjc.m in Sources */,
933+
3DF7B78B28842972005DD8CA /* Connection+Attach.swift in Sources */,
913934
49EB68C71F7B3CB400D89D40 /* Coding.swift in Sources */,
914935
997DF2B1287FC06D00F8DF95 /* Query+with.swift in Sources */,
915936
3D67B3F71DB246D700A4F4C6 /* Foundation.swift in Sources */,
@@ -925,6 +946,7 @@
925946
3D67B3F11DB246D100A4F4C6 /* CustomFunctions.swift in Sources */,
926947
3D67B3F21DB246D100A4F4C6 /* Expression.swift in Sources */,
927948
3D67B3F31DB246D100A4F4C6 /* Operators.swift in Sources */,
949+
3DF7B794288449BA005DD8CA /* URIQueryParameter.swift in Sources */,
928950
3D67B3F41DB246D100A4F4C6 /* Query.swift in Sources */,
929951
3D67B3F51DB246D100A4F4C6 /* Schema.swift in Sources */,
930952
3D67B3F61DB246D100A4F4C6 /* Setter.swift in Sources */,
@@ -946,12 +968,14 @@
946968
49EB68C41F7B3CB400D89D40 /* Coding.swift in Sources */,
947969
EE247B0A1C3F06E900AE3E12 /* RTree.swift in Sources */,
948970
EE247B031C3F06E900AE3E12 /* Blob.swift in Sources */,
971+
3DF7B791288449BA005DD8CA /* URIQueryParameter.swift in Sources */,
949972
EE247B0B1C3F06E900AE3E12 /* Foundation.swift in Sources */,
950973
EE247B041C3F06E900AE3E12 /* Connection.swift in Sources */,
951974
EE247B111C3F06E900AE3E12 /* Expression.swift in Sources */,
952975
EE247B0C1C3F06E900AE3E12 /* Helpers.swift in Sources */,
953976
EE247B0E1C3F06E900AE3E12 /* Collation.swift in Sources */,
954977
EE247B151C3F06E900AE3E12 /* Setter.swift in Sources */,
978+
3DF7B78828842972005DD8CA /* Connection+Attach.swift in Sources */,
955979
EE247B101C3F06E900AE3E12 /* CustomFunctions.swift in Sources */,
956980
EE247B091C3F06E900AE3E12 /* FTS4.swift in Sources */,
957981
EE247B081C3F06E900AE3E12 /* Value.swift in Sources */,
@@ -976,6 +1000,7 @@
9761000
isa = PBXSourcesBuildPhase;
9771001
buildActionMask = 2147483647;
9781002
files = (
1003+
3DF7B78D28842C23005DD8CA /* ResultTests.swift in Sources */,
9791004
EE247B261C3F137700AE3E12 /* CoreFunctionsTests.swift in Sources */,
9801005
EE247B291C3F137700AE3E12 /* FTS4Tests.swift in Sources */,
9811006
EE247B191C3F134A00AE3E12 /* SetterTests.swift in Sources */,
@@ -1012,12 +1037,14 @@
10121037
49EB68C51F7B3CB400D89D40 /* Coding.swift in Sources */,
10131038
EE247B651C3F3FEC00AE3E12 /* Blob.swift in Sources */,
10141039
EE247B6C1C3F3FEC00AE3E12 /* RTree.swift in Sources */,
1040+
3DF7B792288449BA005DD8CA /* URIQueryParameter.swift in Sources */,
10151041
EE247B681C3F3FEC00AE3E12 /* SQLiteObjc.m in Sources */,
10161042
EE247B6A1C3F3FEC00AE3E12 /* Value.swift in Sources */,
10171043
EE247B711C3F3FEC00AE3E12 /* Expression.swift in Sources */,
10181044
EE247B631C3F3FDB00AE3E12 /* Foundation.swift in Sources */,
10191045
EE247B6E1C3F3FEC00AE3E12 /* Collation.swift in Sources */,
10201046
EE247B751C3F3FEC00AE3E12 /* Setter.swift in Sources */,
1047+
3DF7B78928842972005DD8CA /* Connection+Attach.swift in Sources */,
10211048
EE247B701C3F3FEC00AE3E12 /* CustomFunctions.swift in Sources */,
10221049
EE247B691C3F3FEC00AE3E12 /* Statement.swift in Sources */,
10231050
EE247B641C3F3FDB00AE3E12 /* Helpers.swift in Sources */,
@@ -1042,6 +1069,7 @@
10421069
isa = PBXSourcesBuildPhase;
10431070
buildActionMask = 2147483647;
10441071
files = (
1072+
3DF7B78E28842C23005DD8CA /* ResultTests.swift in Sources */,
10451073
EE247B561C3F3FC700AE3E12 /* CoreFunctionsTests.swift in Sources */,
10461074
EE247B5A1C3F3FC700AE3E12 /* OperatorsTests.swift in Sources */,
10471075
EE247B541C3F3FC700AE3E12 /* BlobTests.swift in Sources */,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import Foundation
2+
#if SQLITE_SWIFT_STANDALONE
3+
import sqlite3
4+
#elseif SQLITE_SWIFT_SQLCIPHER
5+
import SQLCipher
6+
#elseif os(Linux)
7+
import CSQLite
8+
#else
9+
import SQLite3
10+
#endif
11+
12+
extension Connection {
13+
14+
/// See https://www3.sqlite.org/lang_attach.html
15+
public func attach(_ location: Location, as schemaName: String) throws {
16+
try run("ATTACH DATABASE ? AS ?", location.description, schemaName)
17+
}
18+
19+
/// See https://www3.sqlite.org/lang_detach.html
20+
public func detach(_ schemaName: String) throws {
21+
try run("DETACH DATABASE ?", schemaName)
22+
}
23+
}

Sources/SQLite/Core/Connection.swift

+13-3
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@ public final class Connection {
5555
/// See: <https://www.sqlite.org/uri.html>
5656
///
5757
/// - Parameter filename: A URI filename
58-
case uri(String)
58+
/// - Parameter parameters: optional query parameters
59+
case uri(String, parameters: [URIQueryParameter] = [])
5960
}
6061

6162
/// An SQL operation passed to update callbacks.
@@ -727,8 +728,17 @@ extension Connection.Location: CustomStringConvertible {
727728
return ":memory:"
728729
case .temporary:
729730
return ""
730-
case .uri(let URI):
731-
return URI
731+
case let .uri(URI, parameters):
732+
guard parameters.count > 0,
733+
var components = URLComponents(string: URI) else {
734+
return URI
735+
}
736+
components.queryItems =
737+
(components.queryItems ?? []) + parameters.map(\.queryItem)
738+
if components.scheme == nil {
739+
components.scheme = "file"
740+
}
741+
return components.description
732742
}
733743
}
734744

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import Foundation
2+
3+
public enum URIQueryParameter: CustomStringConvertible {
4+
public enum FileMode: String {
5+
case readOnly = "ro", readWrite = "rw", readWriteCreate = "rwc", memory
6+
}
7+
8+
public enum CacheMode: String {
9+
case shared, `private`
10+
}
11+
12+
/// The cache query parameter determines if the new database is opened using shared cache mode or with a private cache.
13+
case cache(CacheMode)
14+
15+
/// The immutable query parameter is a boolean that signals to SQLite that the underlying database file is held on read-only media
16+
/// and cannot be modified, even by another process with elevated privileges.
17+
case immutable(Bool)
18+
19+
/// When creating a new database file during `sqlite3_open_v2()` on unix systems, SQLite will try to set the permissions of the new database
20+
/// file to match the existing file "filename".
21+
case modeOf(String)
22+
23+
/// The mode query parameter determines if the new database is opened read-only, read-write, read-write and created if it does not exist,
24+
/// or that the database is a pure in-memory database that never interacts with disk, respectively.
25+
case mode(FileMode)
26+
27+
/// The nolock query parameter is a boolean that disables all calls to the `xLock`, ` xUnlock`, and `xCheckReservedLock` methods
28+
/// of the VFS when true.
29+
case nolock(Bool)
30+
31+
/// The psow query parameter overrides the `powersafe_overwrite` property of the database file being opened.
32+
case psow(Bool)
33+
34+
/// The vfs query parameter causes the database connection to be opened using the VFS called NAME.
35+
case vfs(String)
36+
37+
public var description: String {
38+
queryItem.description
39+
}
40+
41+
var queryItem: URLQueryItem {
42+
switch self {
43+
case .cache(let mode): return .init(name: "cache", value: mode.rawValue)
44+
case .immutable(let bool): return .init(name: "immutable", value: NSNumber(value: bool).description)
45+
case .modeOf(let filename): return .init(name: "modeOf", value: filename)
46+
case .mode(let fileMode): return .init(name: "mode", value: fileMode.rawValue)
47+
case .nolock(let bool): return .init(name: "nolock", value: NSNumber(value: bool).description)
48+
case .psow(let bool): return .init(name: "psow", value: NSNumber(value: bool).description)
49+
case .vfs(let name): return .init(name: "vfs", value: name)
50+
}
51+
}
52+
}

0 commit comments

Comments
 (0)