Skip to content

Commit 8e59ea0

Browse files
committed
WIP 2
Signed-off-by: Stephen Celis <stephen@stephencelis.com>
1 parent d446df4 commit 8e59ea0

File tree

10 files changed

+421
-86
lines changed

10 files changed

+421
-86
lines changed

SQLite.xcodeproj/project.pbxproj

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
archiveVersion = 1;
44
classes = {
55
};
6-
objectVersion = 46;
6+
objectVersion = 47;
77
objects = {
88

99
/* Begin PBXBuildFile section */
@@ -426,7 +426,7 @@
426426
};
427427
};
428428
buildConfigurationList = DC2B29DC1B26F718001C60EA /* Build configuration list for PBXProject "SQLite" */;
429-
compatibilityVersion = "Xcode 3.2";
429+
compatibilityVersion = "Xcode 6.3";
430430
developmentRegion = English;
431431
hasScannedForEncodings = 0;
432432
knownRegions = (
@@ -777,7 +777,7 @@
777777
buildSettings = {
778778
CLANG_ENABLE_MODULES = YES;
779779
COMBINE_HIDPI_IMAGES = YES;
780-
INFOPLIST_FILE = "SQLite MacTests/Info.plist";
780+
INFOPLIST_FILE = Tests/Info.plist;
781781
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
782782
MACOSX_DEPLOYMENT_TARGET = 10.11;
783783
PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLiteTests;
@@ -792,7 +792,7 @@
792792
buildSettings = {
793793
CLANG_ENABLE_MODULES = YES;
794794
COMBINE_HIDPI_IMAGES = YES;
795-
INFOPLIST_FILE = "SQLite MacTests/Info.plist";
795+
INFOPLIST_FILE = Tests/Info.plist;
796796
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
797797
MACOSX_DEPLOYMENT_TARGET = 10.11;
798798
PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLiteTests;

SQLite.xcodeproj/xcshareddata/xcschemes/SQLite Mac.xcscheme

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,29 @@
2626
buildConfiguration = "Debug"
2727
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
2828
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
29-
shouldUseLaunchSchemeArgsEnv = "YES">
29+
shouldUseLaunchSchemeArgsEnv = "YES"
30+
codeCoverageEnabled = "YES">
3031
<Testables>
32+
<TestableReference
33+
skipped = "NO">
34+
<BuildableReference
35+
BuildableIdentifier = "primary"
36+
BlueprintIdentifier = "DC7253B41B52ADEC009B38B1"
37+
BuildableName = "SQLite Mac Tests.xctest"
38+
BlueprintName = "SQLite Mac Tests"
39+
ReferencedContainer = "container:SQLite.xcodeproj">
40+
</BuildableReference>
41+
</TestableReference>
3142
</Testables>
43+
<MacroExpansion>
44+
<BuildableReference
45+
BuildableIdentifier = "primary"
46+
BlueprintIdentifier = "DC7253AB1B52ADEB009B38B1"
47+
BuildableName = "SQLite.framework"
48+
BlueprintName = "SQLite Mac"
49+
ReferencedContainer = "container:SQLite.xcodeproj">
50+
</BuildableReference>
51+
</MacroExpansion>
3252
<AdditionalOptions>
3353
</AdditionalOptions>
3454
</TestAction>

Source/Core/Connection.swift

Lines changed: 47 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ public final class Connection {
4343
public init(_ location: Location = .InMemory, readonly: Bool = false) throws {
4444
let flags = readonly ? SQLITE_OPEN_READONLY : SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE
4545
try check(sqlite3_open_v2(location.description, &handle, flags | SQLITE_OPEN_FULLMUTEX, nil))
46+
47+
dispatch_queue_set_specific(queue, Connection.queueKey, queueContext, nil)
4648
}
4749

4850
// TODO: add 'Throws:' documentation here and so on
@@ -257,7 +259,7 @@ public final class Connection {
257259
/// The transaction will be committed when the block returns. The block
258260
/// must throw to roll the transaction back.
259261
// TODO: consider not requiring a throw to roll back?
260-
public func transaction(mode: TransactionMode = .Deferred, @noescape block: () throws -> Void) throws {
262+
public func transaction(mode: TransactionMode = .Deferred, block: () throws -> Void) throws {
261263
try transaction("BEGIN \(mode.rawValue) TRANSACTION", block, "COMMIT TRANSACTION", or: "ROLLBACK TRANSACTION")
262264
}
263265

@@ -275,19 +277,24 @@ public final class Connection {
275277
/// The block must throw to roll the savepoint back.
276278
// TODO: consider not requiring a throw to roll back?
277279
// TODO: consider removing ability to set a name?
278-
public func savepoint(name: String = NSUUID().UUIDString, @noescape block: () throws -> Void) throws {
279-
let savepoint = "SAVEPOINT \(name.quote())"
280+
public func savepoint(name: String = NSUUID().UUIDString, block: () throws -> Void) throws {
281+
let name = name.quote("'")
282+
let savepoint = "SAVEPOINT \(name)"
283+
280284
try transaction(savepoint, block, "RELEASE \(savepoint)", or: "ROLLBACK TO \(savepoint)")
281285
}
282286

283-
private func transaction(begin: String, @noescape _ block: () throws -> Void, _ commit: String, or rollback: String) throws {
284-
try run(begin)
285-
do {
286-
try block()
287-
try run(commit)
288-
} catch {
289-
try run(rollback)
290-
throw error
287+
// FIXME: rdar://problem/21389236
288+
private func transaction(begin: String, _ block: () throws -> Void, _ commit: String, or rollback: String) throws {
289+
return try sync {
290+
try self.run(begin)
291+
do {
292+
try block()
293+
try self.run(commit)
294+
} catch {
295+
try self.run(rollback)
296+
throw error
297+
}
291298
}
292299
}
293300

@@ -478,8 +485,37 @@ public final class Connection {
478485
return resultCode
479486
}
480487

488+
func sync<T>(block: () throws -> T) rethrows -> T {
489+
var success: T?
490+
var failure: ErrorType?
491+
492+
let wrapped: () -> Void = {
493+
do {
494+
success = try block()
495+
} catch {
496+
failure = error
497+
}
498+
}
499+
500+
if dispatch_get_specific(Connection.queueKey) == queueContext {
501+
wrapped()
502+
} else {
503+
dispatch_sync(queue, wrapped)
504+
}
505+
506+
if let failure = failure {
507+
try { () -> Void in throw failure }()
508+
}
509+
510+
return success!
511+
}
512+
481513
private var queue = dispatch_queue_create("SQLite.Database", DISPATCH_QUEUE_SERIAL)
482514

515+
private static let queueKey = unsafeBitCast(Connection.self, UnsafePointer<Void>.self)
516+
517+
private lazy var queueContext: UnsafeMutablePointer<Void> = unsafeBitCast(self, UnsafeMutablePointer<Void>.self)
518+
483519
}
484520

485521
extension Connection : CustomStringConvertible {

Source/Extensions/FTS4.swift

Lines changed: 72 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,18 @@ extension Module {
1515

1616
extension VirtualTable {
1717

18+
/// Builds an expression appended with a `MATCH` query against the given
19+
/// pattern.
20+
///
21+
/// let emails = VirtualTable("emails")
22+
///
23+
/// emails.filter(emails.match("Hello"))
24+
/// // SELECT * FROM "emails" WHERE "emails" MATCH 'Hello'
25+
///
26+
/// - Parameter pattern: A pattern to match.
27+
///
28+
/// - Returns: An expression appended with a `MATCH` query against the given
29+
/// pattern.
1830
@warn_unused_result public func match(pattern: String) -> Expression<Bool> {
1931
return "MATCH".infix(tableName(), pattern)
2032
}
@@ -27,31 +39,77 @@ extension VirtualTable {
2739
return "MATCH".infix(tableName(), pattern)
2840
}
2941

42+
/// Builds a copy of the query with a `WHERE … MATCH` clause.
43+
///
44+
/// let emails = VirtualTable("emails")
45+
///
46+
/// emails.match("Hello")
47+
/// // SELECT * FROM "emails" WHERE "emails" MATCH 'Hello'
48+
///
49+
/// - Parameter pattern: A pattern to match.
50+
///
51+
/// - Returns: A query with the given `WHERE … MATCH` clause applied.
52+
@warn_unused_result public func match(pattern: String) -> QueryType {
53+
return filter(match(pattern))
54+
}
55+
56+
@warn_unused_result public func match(pattern: Expression<String>) -> QueryType {
57+
return filter(match(pattern))
58+
}
59+
60+
@warn_unused_result public func match(pattern: Expression<String?>) -> QueryType {
61+
return filter(match(pattern))
62+
}
63+
3064
}
3165

32-
public enum Tokenizer {
66+
public struct Tokenizer {
3367

34-
internal static let moduleName = "SQLite.swift"
68+
public static let Simple = Tokenizer("simple")
3569

36-
case Simple
70+
public static let Porter = Tokenizer("porter")
3771

38-
case Porter
72+
@warn_unused_result public static func Unicode61(removeDiacritics removeDiacritics: Bool? = nil, tokenchars: Set<Character> = [], separators: Set<Character> = []) -> Tokenizer {
73+
var arguments = [String]()
3974

40-
case Custom(String)
75+
if let removeDiacritics = removeDiacritics {
76+
arguments.append("removeDiacritics=\(removeDiacritics ? 1 : 0)".quote())
77+
}
78+
79+
if !tokenchars.isEmpty {
80+
let joined = "".join(tokenchars.map { String($0) })
81+
arguments.append("tokenchars=\(joined)".quote())
82+
}
83+
84+
if !separators.isEmpty {
85+
let joined = "".join(separators.map { String($0) })
86+
arguments.append("separators=\(joined)".quote())
87+
}
88+
89+
return Tokenizer("unicode61", arguments)
90+
}
91+
92+
@warn_unused_result public static func Custom(name: String) -> Tokenizer {
93+
return Tokenizer(Tokenizer.moduleName.quote(), [name.quote()])
94+
}
95+
96+
public let name: String
97+
98+
public let arguments: [String]
99+
100+
private init(_ name: String, _ arguments: [String] = []) {
101+
self.name = name
102+
self.arguments = arguments
103+
}
104+
105+
private static let moduleName = "SQLite.swift"
41106

42107
}
43108

44-
extension Tokenizer: CustomStringConvertible {
109+
extension Tokenizer : CustomStringConvertible {
45110

46111
public var description: String {
47-
switch self {
48-
case .Simple:
49-
return "simple"
50-
case .Porter:
51-
return "porter"
52-
case .Custom(let tokenizer):
53-
return " ".join([Tokenizer.moduleName.quote(), tokenizer.quote("'")])
54-
}
112+
return " ".join([name] + arguments)
55113
}
56114

57115
}

0 commit comments

Comments
 (0)