Skip to content

Commit 9064717

Browse files
authored
Merge pull request groue#1764 from groue/dev/fts-pattern-initializers
Make it possible to create a raw FTS5Pattern without any database connection
2 parents 0aaa2a4 + 00f4622 commit 9064717

File tree

2 files changed

+39
-5
lines changed

2 files changed

+39
-5
lines changed

GRDB/FTS/FTS5Pattern.swift

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@
77
///
88
/// ### Creating Raw FTS5 Patterns
99
///
10+
/// - ``init(rawPattern:allowedColumns:)``
1011
/// - ``Database/makeFTS5Pattern(rawPattern:forTable:)``
11-
///
12+
///
1213
/// ### Creating FTS5 Patterns from User Input
1314
///
1415
/// - ``init(matchingAllPrefixesIn:)``
@@ -123,7 +124,25 @@ public struct FTS5Pattern: Sendable {
123124
try? self.init(rawPattern: "^\"" + tokens.joined(separator: " ") + "\"")
124125
}
125126

126-
init(rawPattern: String, allowedColumns: [String] = []) throws {
127+
/// Creates a pattern from a raw pattern string.
128+
///
129+
/// The pattern syntax is documented at <https://www.sqlite.org/fts5.html#full_text_query_syntax>
130+
///
131+
/// For example:
132+
///
133+
/// ```swift
134+
/// // OK
135+
/// let pattern = try FTS5Pattern(rawPattern: "and")
136+
///
137+
/// // Throws an error: malformed MATCH expression: [AND]
138+
/// let pattern = try FTS5Pattern(rawPattern: "AND")
139+
/// ```
140+
///
141+
/// If the pattern tests for specific columns, you must pass those
142+
/// columns in the `allowedColumns` parameter.
143+
///
144+
/// - throws: A ``DatabaseError`` if the pattern has an invalid syntax.
145+
public init(rawPattern: String, allowedColumns: [String] = []) throws {
127146
// Correctness above all: use SQLite to validate the pattern.
128147
//
129148
// Invalid patterns have SQLite return an error on the first

Tests/GRDBTests/FTS5PatternTests.swift

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,16 @@ class FTS5PatternTests: GRDBTestCase {
4343
("title:brest", 1)
4444
]
4545
for (rawPattern, expectedCount) in validRawPatterns {
46-
let pattern = try db.makeFTS5Pattern(rawPattern: rawPattern, forTable: "books")
47-
let count = try Int.fetchOne(db, sql: "SELECT COUNT(*) FROM books WHERE books MATCH ?", arguments: [pattern])!
48-
XCTAssertEqual(count, expectedCount, "Expected pattern \(String(reflecting: rawPattern)) to yield \(expectedCount) results")
46+
do {
47+
let pattern = try FTS5Pattern(rawPattern: rawPattern, allowedColumns: ["title"])
48+
let count = try Int.fetchOne(db, sql: "SELECT COUNT(*) FROM books WHERE books MATCH ?", arguments: [pattern])!
49+
XCTAssertEqual(count, expectedCount, "Expected pattern \(String(reflecting: rawPattern)) to yield \(expectedCount) results")
50+
}
51+
do {
52+
let pattern = try db.makeFTS5Pattern(rawPattern: rawPattern, forTable: "books")
53+
let count = try Int.fetchOne(db, sql: "SELECT COUNT(*) FROM books WHERE books MATCH ?", arguments: [pattern])!
54+
XCTAssertEqual(count, expectedCount, "Expected pattern \(String(reflecting: rawPattern)) to yield \(expectedCount) results")
55+
}
4956
}
5057
}
5158
}
@@ -55,6 +62,14 @@ class FTS5PatternTests: GRDBTestCase {
5562
dbQueue.inDatabase { db in
5663
let invalidRawPatterns = ["", "?!", "^", "NOT", "(", "AND", "OR", "\"", "missing:foo"]
5764
for rawPattern in invalidRawPatterns {
65+
do {
66+
_ = try FTS5Pattern(rawPattern: rawPattern, allowedColumns: ["title"])
67+
XCTFail("Expected pattern to be invalid: \(String(reflecting: rawPattern))")
68+
} catch is DatabaseError {
69+
} catch {
70+
XCTFail("Expected DatabaseError, not \(error)")
71+
}
72+
5873
do {
5974
_ = try db.makeFTS5Pattern(rawPattern: rawPattern, forTable: "books")
6075
XCTFail("Expected pattern to be invalid: \(String(reflecting: rawPattern))")

0 commit comments

Comments
 (0)