Skip to content

Commit f3acfec

Browse files
committed
DatabaseCollation no longer uses a deprecated String initializer
1 parent aa2cdb7 commit f3acfec

File tree

3 files changed

+44
-40
lines changed

3 files changed

+44
-40
lines changed

GRDB/Core/Database.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -806,7 +806,7 @@ public final class Database: CustomStringConvertible, CustomDebugStringConvertib
806806
collationPointer,
807807
{ (collationPointer, length1, buffer1, length2, buffer2) in
808808
let collation = Unmanaged<DatabaseCollation>.fromOpaque(collationPointer!).takeUnretainedValue()
809-
return CInt(collation.function(length1, buffer1, length2, buffer2).rawValue)
809+
return collation.xCompare(length1, buffer1.unsafelyUnwrapped, length2, buffer2.unsafelyUnwrapped)
810810
}, nil)
811811
guard code == SQLITE_OK else {
812812
// Assume a GRDB bug: there is no point throwing any error.

GRDB/Core/DatabaseCollation.swift

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,21 @@ public final class DatabaseCollation: Identifiable, Sendable {
5353
}
5454
}
5555

56+
/// Feeds the `xCompare` parameter of sqlite3_create_collation_v2
57+
/// <https://www.sqlite.org/c3ref/create_collation.html>
58+
typealias XCompare = @Sendable (
59+
_ length1: CInt,
60+
_ buffer1: UnsafeRawPointer,
61+
_ length2: CInt,
62+
_ buffer2: UnsafeRawPointer
63+
) -> CInt
64+
5665
/// The identifier of the collation.
5766
public var id: ID { ID(name: name) }
5867

5968
/// The name of the collation.
6069
public let name: String
61-
let function: @Sendable (CInt, UnsafeRawPointer?, CInt, UnsafeRawPointer?) -> ComparisonResult
70+
let xCompare: XCompare
6271

6372
/// Creates a collation.
6473
///
@@ -75,21 +84,16 @@ public final class DatabaseCollation: Identifiable, Sendable {
7584
/// - parameters:
7685
/// - name: The collation name.
7786
/// - function: A function that compares two strings.
78-
public init(_ name: String, function: @escaping @Sendable (String, String) -> ComparisonResult) {
87+
public convenience init(_ name: String, function: @escaping @Sendable (String, String) -> ComparisonResult) {
88+
self.init(name, xCompare: { (length1, buffer1, length2, buffer2) in
89+
let string1 = String(decoding: UnsafeRawBufferPointer(start: buffer1, count: Int(length1)), as: UTF8.self)
90+
let string2 = String(decoding: UnsafeRawBufferPointer(start: buffer2, count: Int(length2)), as: UTF8.self)
91+
return CInt(function(string1, string2).rawValue)
92+
})
93+
}
94+
95+
init(_ name: String, xCompare: @escaping XCompare) {
7996
self.name = name
80-
self.function = { (length1, buffer1, length2, buffer2) in
81-
// Buffers are not C strings: they do not end with \0.
82-
let string1 = String(
83-
bytesNoCopy: UnsafeMutableRawPointer(mutating: buffer1.unsafelyUnwrapped),
84-
length: Int(length1),
85-
encoding: .utf8,
86-
freeWhenDone: false)!
87-
let string2 = String(
88-
bytesNoCopy: UnsafeMutableRawPointer(mutating: buffer2.unsafelyUnwrapped),
89-
length: Int(length2),
90-
encoding: .utf8,
91-
freeWhenDone: false)!
92-
return function(string1, string2)
93-
}
97+
self.xCompare = xCompare
9498
}
9599
}

Tests/GRDBTests/DatabaseCollationTests.swift

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,38 +6,38 @@ class DatabaseCollationTests: GRDBTestCase {
66
func testDefaultCollations() throws {
77
let dbQueue = try makeDatabaseQueue()
88
try dbQueue.inDatabase { db in
9-
try db.execute(sql: "CREATE TABLE strings (id INTEGER PRIMARY KEY, name TEXT)")
10-
try db.execute(sql: "INSERT INTO strings VALUES (1, '1')")
11-
try db.execute(sql: "INSERT INTO strings VALUES (2, '2')")
12-
try db.execute(sql: "INSERT INTO strings VALUES (3, '10')")
13-
try db.execute(sql: "INSERT INTO strings VALUES (4, 'a')")
14-
try db.execute(sql: "INSERT INTO strings VALUES (5, 'à')")
15-
try db.execute(sql: "INSERT INTO strings VALUES (6, 'A')")
16-
try db.execute(sql: "INSERT INTO strings VALUES (7, 'Z')")
17-
try db.execute(sql: "INSERT INTO strings VALUES (8, 'z')")
9+
try db.execute(sql: """
10+
CREATE TABLE strings (id INTEGER PRIMARY KEY, name TEXT);
11+
INSERT INTO strings VALUES (1, '1');
12+
INSERT INTO strings VALUES (2, '2');
13+
INSERT INTO strings VALUES (3, '10');
14+
INSERT INTO strings VALUES (4, 'a');
15+
INSERT INTO strings VALUES (5, 'à');
16+
INSERT INTO strings VALUES (6, 'A');
17+
INSERT INTO strings VALUES (7, 'Z');
18+
INSERT INTO strings VALUES (8, 'z');
19+
INSERT INTO strings VALUES (9, '');
20+
INSERT INTO strings VALUES (10, NULL);
21+
INSERT INTO strings VALUES (11, x'42FF'); -- Invalid UTF8 "B�"
22+
""")
1823

19-
// Swift 4.2 and Swift 4.1 don't sort strings in the same way
20-
if "z" < "à" {
21-
XCTAssertEqual(
22-
try Int.fetchAll(db, sql: "SELECT id FROM strings ORDER BY name COLLATE \(DatabaseCollation.unicodeCompare.name), id"),
23-
[1,3,2,6,7,4,8,5])
24-
} else {
25-
XCTAssertEqual(
26-
try Int.fetchAll(db, sql: "SELECT id FROM strings ORDER BY name COLLATE \(DatabaseCollation.unicodeCompare.name), id"),
27-
[1,3,2,6,7,4,5,8])
28-
}
24+
// Note that "B�" is always last. We can observe that SQLite
25+
// does not invoke the collation for invalid UTF8 strings.
26+
XCTAssertEqual(
27+
try Int.fetchAll(db, sql: "SELECT id FROM strings ORDER BY name COLLATE \(DatabaseCollation.unicodeCompare.name), id"),
28+
[10,9,1,3,2,6,7,4,8,5,11])
2929
XCTAssertEqual(
3030
try Int.fetchAll(db, sql: "SELECT id FROM strings ORDER BY name COLLATE \(DatabaseCollation.caseInsensitiveCompare.name), id"),
31-
[1,3,2,4,6,5,7,8])
31+
[10,9,1,3,2,4,6,5,7,8,11])
3232
XCTAssertEqual(
3333
try Int.fetchAll(db, sql: "SELECT id FROM strings ORDER BY name COLLATE \(DatabaseCollation.localizedCaseInsensitiveCompare.name), id"),
34-
[1,3,2,4,6,5,7,8])
34+
[10,9,1,3,2,4,6,5,7,8,11])
3535
XCTAssertEqual(
3636
try Int.fetchAll(db, sql: "SELECT id FROM strings ORDER BY name COLLATE \(DatabaseCollation.localizedCompare.name), id"),
37-
[1,3,2,4,6,5,8,7])
37+
[10,9,1,3,2,4,6,5,8,7,11])
3838
XCTAssertEqual(
3939
try Int.fetchAll(db, sql: "SELECT id FROM strings ORDER BY name COLLATE \(DatabaseCollation.localizedStandardCompare.name), id"),
40-
[1,2,3,4,6,5,8,7])
40+
[10,9,1,2,3,4,6,5,8,7,11])
4141
}
4242
}
4343

0 commit comments

Comments
 (0)