Compare commits

...

1 Commits

Author SHA1 Message Date
Gwendal Roué
1081515035 Support for Xcode 10.2 2019-03-16 20:17:46 +01:00
26 changed files with 128 additions and 66 deletions

View File

@ -611,7 +611,7 @@ struct SchemaInfo: Equatable {
var sql: String?
var isTemporary: Bool
#if !swift(>=4.2)
#if !swift(>=4.1.50)
var hashValue: Int {
var hash = type.hashValue
hash ^= name.hashValue

View File

@ -290,13 +290,13 @@ struct StatementCache {
}
mutating func remove(_ statement: SelectStatement) {
if let index = selectStatements.index(where: { $0.1 === statement }) {
if let index = selectStatements.firstIndex(where: { $0.1 === statement }) {
selectStatements.remove(at: index)
}
}
mutating func remove(_ statement: UpdateStatement) {
if let index = updateStatements.index(where: { $0.1 === statement }) {
if let index = updateStatements.firstIndex(where: { $0.1 === statement }) {
updateStatements.remove(at: index)
}
}

View File

@ -39,7 +39,7 @@ extension DatabaseCollation: Hashable {
// implies hash equality) is thus non trivial. But it's not that
// important, since this hashValue is only used when one adds
// or removes a collation from a database connection.
#if swift(>=4.2)
#if swift(>=4.1.50)
/// :nodoc:
public func hash(into hasher: inout Hasher) {
hasher.combine(0)

View File

@ -294,9 +294,15 @@ public final class DatabaseFunction: Hashable {
case .string(let string):
sqlite3_result_text(sqliteContext, string, -1, SQLITE_TRANSIENT)
case .blob(let data):
data.withUnsafeBytes { bytes in
sqlite3_result_blob(sqliteContext, bytes, Int32(data.count), SQLITE_TRANSIENT)
#if swift(>=5.0)
data.withUnsafeBytes {
sqlite3_result_blob(sqliteContext, $0.baseAddress, Int32($0.count), SQLITE_TRANSIENT)
}
#else
data.withUnsafeBytes {
sqlite3_result_blob(sqliteContext, $0, Int32(data.count), SQLITE_TRANSIENT)
}
#endif
}
}
@ -313,7 +319,7 @@ public final class DatabaseFunction: Hashable {
}
extension DatabaseFunction {
#if swift(>=4.2)
#if swift(>=4.1.50)
/// :nodoc:
public func hash(into hasher: inout Hasher) {
hasher.combine(identity)

View File

@ -149,7 +149,7 @@ public struct DatabaseValue: Hashable, CustomStringConvertible, DatabaseValueCon
// Hashable
extension DatabaseValue {
#if swift(>=4.2)
#if swift(>=4.1.50)
/// :nodoc:
public func hash(into hasher: inout Hasher) {
switch storage {

View File

@ -994,7 +994,7 @@ extension Row {
// Hashable
extension Row {
#if swift(>=4.2)
#if swift(>=4.1.50)
/// :nodoc:
public func hash(into hasher: inout Hasher) {
hasher.combine(count)
@ -1317,7 +1317,7 @@ private struct ArrayRowImpl : RowImpl {
func index(ofColumn name: String) -> Int? {
let lowercaseName = name.lowercased()
return columns.index { (column, _) in column.lowercased() == lowercaseName }
return columns.firstIndex { (column, _) in column.lowercased() == lowercaseName }
}
func copiedRow(_ row: Row) -> Row {
@ -1356,7 +1356,7 @@ private struct StatementCopyRowImpl : RowImpl {
func index(ofColumn name: String) -> Int? {
let lowercaseName = name.lowercased()
return columnNames.index { $0.lowercased() == lowercaseName }
return columnNames.firstIndex { $0.lowercased() == lowercaseName }
}
func copiedRow(_ row: Row) -> Row {

View File

@ -44,7 +44,7 @@ final class SchedulingWatchdog {
}
static func preconditionValidQueue(_ db: Database, _ message: @autoclosure() -> String = "Database was not used on the correct thread.", file: StaticString = #file, line: UInt = #line) {
GRDBPrecondition(current?.allows(db) ?? false, message, file: file, line: line)
GRDBPrecondition(current?.allows(db) ?? false, message(), file: file, line: line)
}
static var current: SchedulingWatchdog? {

View File

@ -189,11 +189,11 @@ final class SerializedDatabase {
/// Fatal error if current dispatch queue is not valid.
func preconditionValidQueue(_ message: @autoclosure() -> String = "Database was not used on the correct thread.", file: StaticString = #file, line: UInt = #line) {
SchedulingWatchdog.preconditionValidQueue(db, message, file: file, line: line)
SchedulingWatchdog.preconditionValidQueue(db, message(), file: file, line: line)
}
/// Fatal error if a transaction has been left opened.
private func preconditionNoUnsafeTransactionLeft(_ db: Database, _ message: @autoclosure() -> String = "A transaction has been left opened at the end of a database access", file: StaticString = #file, line: UInt = #line) {
GRDBPrecondition(configuration.allowsUnsafeTransactions || !db.isInsideTransaction, message, file: file, line: line)
GRDBPrecondition(configuration.allowsUnsafeTransactions || !db.isInsideTransaction, message(), file: file, line: line)
}
}

View File

@ -176,9 +176,15 @@ public class Statement {
case .string(let string):
code = sqlite3_bind_text(sqliteStatement, index, string, -1, SQLITE_TRANSIENT)
case .blob(let data):
code = data.withUnsafeBytes { bytes in
sqlite3_bind_blob(sqliteStatement, index, bytes, Int32(data.count), SQLITE_TRANSIENT)
#if swift(>=5.0)
code = data.withUnsafeBytes {
sqlite3_bind_blob(sqliteStatement, index, $0.baseAddress, Int32($0.count), SQLITE_TRANSIENT)
}
#else
code = data.withUnsafeBytes {
sqlite3_bind_blob(sqliteStatement, index, $0, Int32(data.count), SQLITE_TRANSIENT)
}
#endif
}
// It looks like sqlite3_bind_xxx() functions do not access the file system.

View File

@ -26,14 +26,14 @@ extension DatabaseValueConvertible where Self: ReferenceConvertible, Self.Refere
}
}
public extension DatabaseValueConvertible where Self: Decodable & ReferenceConvertible, Self.ReferenceType: DatabaseValueConvertible {
extension DatabaseValueConvertible where Self: Decodable & ReferenceConvertible, Self.ReferenceType: DatabaseValueConvertible {
public static func fromDatabaseValue(_ databaseValue: DatabaseValue) -> Self? {
// Preserve custom database decoding
return ReferenceType.fromDatabaseValue(databaseValue).flatMap { cast($0) }
}
}
public extension DatabaseValueConvertible where Self: Encodable & ReferenceConvertible, Self.ReferenceType: DatabaseValueConvertible {
extension DatabaseValueConvertible where Self: Encodable & ReferenceConvertible, Self.ReferenceType: DatabaseValueConvertible {
public var databaseValue: DatabaseValue {
// Preserve custom database encoding
return (self as! ReferenceType).databaseValue

View File

@ -53,9 +53,15 @@ extension UUID: DatabaseValueConvertible {
public static func fromDatabaseValue(_ dbValue: DatabaseValue) -> UUID? {
switch dbValue.storage {
case .blob(let data) where data.count == 16:
#if swift(>=5.0)
return data.withUnsafeBytes {
UUID(uuid: $0.bindMemory(to: uuid_t.self).first!)
}
#else
return data.withUnsafeBytes {
UUID(uuid: $0.pointee)
}
#endif
case .string(let string):
return UUID(uuidString: string)
default:

View File

@ -178,13 +178,13 @@ private struct DatabaseValueDecoder: Decoder {
}
}
public extension DatabaseValueConvertible where Self: Decodable {
extension DatabaseValueConvertible where Self: Decodable {
public static func fromDatabaseValue(_ databaseValue: DatabaseValue) -> Self? {
return try? self.init(from: DatabaseValueDecoder(dbValue: databaseValue, codingPath: []))
}
}
public extension DatabaseValueConvertible where Self: Decodable & RawRepresentable, Self.RawValue: DatabaseValueConvertible {
extension DatabaseValueConvertible where Self: Decodable & RawRepresentable, Self.RawValue: DatabaseValueConvertible {
public static func fromDatabaseValue(_ databaseValue: DatabaseValue) -> Self? {
// Preserve custom database decoding
return RawValue.fromDatabaseValue(databaseValue).flatMap { self.init(rawValue: $0) }

View File

@ -89,7 +89,7 @@ private struct DatabaseValueEncoder : Encoder {
}
}
public extension DatabaseValueConvertible where Self: Encodable {
extension DatabaseValueConvertible where Self: Encodable {
public var databaseValue: DatabaseValue {
var dbValue: DatabaseValue! = nil
let encoder = DatabaseValueEncoder(encode: { dbValue = $0 })
@ -98,7 +98,7 @@ public extension DatabaseValueConvertible where Self: Encodable {
}
}
public extension DatabaseValueConvertible where Self: Encodable & RawRepresentable, Self.RawValue: DatabaseValueConvertible {
extension DatabaseValueConvertible where Self: Encodable & RawRepresentable, Self.RawValue: DatabaseValueConvertible {
public var databaseValue: DatabaseValue {
// Preserve custom database encoding
return rawValue.databaseValue

View File

@ -205,8 +205,8 @@ extension TableRequest where Self: FilteredRequest {
return key
// Preserve ordering of columns in the unique index
.sorted { (kv1, kv2) in
let index1 = lowercaseColumns.index(of: kv1.key.lowercased())!
let index2 = lowercaseColumns.index(of: kv2.key.lowercased())!
let index1 = lowercaseColumns.firstIndex(of: kv1.key.lowercased())!
let index2 = lowercaseColumns.firstIndex(of: kv2.key.lowercased())!
return index1 < index2
}
.map { (column, value) in Column(column) == value }

View File

@ -213,7 +213,7 @@ public struct SQLBinaryOperator : Hashable {
return SQLBinaryOperator(negatedSQL, negated: sql)
}
#if !swift(>=4.2)
#if !swift(>=4.1.50)
/// :nodoc:
public var hashValue: Int {
return sql.hashValue ^ (negatedSQL?.hashValue ?? 0)

View File

@ -271,7 +271,7 @@ public class TableAlias: Hashable {
return expression.qualifiedExpression(with: self)
}
#if swift(>=4.2)
#if swift(>=4.1.50)
/// :nodoc:
public func hash(into hasher: inout Hasher) {
hasher.combine(ObjectIdentifier(root))

View File

@ -439,7 +439,7 @@ public final class TableDefinition {
let primaryKeyColumns: [String]
if let (columns, _) = primaryKeyConstraint {
primaryKeyColumns = columns
} else if let index = columns.index(where: { $0.primaryKey != nil }) {
} else if let index = columns.firstIndex(where: { $0.primaryKey != nil }) {
primaryKeyColumns = [columns[index].name]
} else {
// WITHOUT ROWID optimization requires a primary key. If the

View File

@ -887,7 +887,7 @@ extension FetchedRecordsController where Record: MutablePersistableRecord {
/// if record could not be found.
public func indexPath(for record: Record) -> IndexPath? {
let item = Item<Record>(row: Row(record))
guard let fetchedItems = fetchedItems, let index = fetchedItems.index(where: { itemsAreIdentical($0, item) }) else {
guard let fetchedItems = fetchedItems, let index = fetchedItems.firstIndex(where: { itemsAreIdentical($0, item) }) else {
return nil
}
return IndexPath(indexes: [0, index])

View File

@ -1263,7 +1263,7 @@ private struct InsertQuery: Hashable {
let tableName: String
let insertedColumns: [String]
#if !swift(>=4.2)
#if !swift(>=4.1.50)
var hashValue: Int { return tableName.hashValue }
#endif
}
@ -1297,7 +1297,7 @@ private struct UpdateQuery: Hashable {
let updatedColumns: [String]
let conditionColumns: [String]
#if !swift(>=4.2)
#if !swift(>=4.1.50)
var hashValue: Int { return tableName.hashValue }
#endif
}

View File

@ -73,7 +73,7 @@ struct OrderedDictionary<Key: Hashable, Value> {
guard let value = dictionary.removeValue(forKey: key) else {
return nil
}
let index = keys.index { $0 == key }!
let index = keys.firstIndex { $0 == key }!
keys.remove(at: index)
return value
}

View File

@ -52,7 +52,7 @@ func GRDBPrecondition(_ condition: @autoclosure() -> Bool, _ message: @autoclosu
/// https://bugs.swift.org/browse/SR-905 and
/// https://github.com/groue/GRDB.swift/issues/37
if !condition() {
fatalError(message, file: file, line: line)
fatalError(message(), file: file, line: line)
}
}
@ -64,7 +64,7 @@ func cast<T, U>(_ value: T) -> U? {
extension Array {
/// Removes the first object that matches *predicate*.
mutating func removeFirst(_ predicate: (Element) throws -> Bool) rethrows {
if let index = try index(where: predicate) {
if let index = try firstIndex(where: predicate) {
remove(at: index)
}
}
@ -81,3 +81,17 @@ extension DispatchQueue {
return DispatchQueue.getSpecific(key: mainKey) != nil
}
}
#if !swift(>=4.2)
extension Collection {
func firstIndex(where predicate:(Element) throws -> Bool) rethrows -> Index? {
return try index(where: predicate)
}
}
extension Collection where Element: Equatable {
func firstIndex(of element: Element) -> Index? {
return index(of: element)
}
}
#endif

View File

@ -17,56 +17,55 @@ class DataMemoryTests: GRDBTestCase {
func testMemoryBehavior() throws {
let dbQueue = try makeDatabaseQueue()
try dbQueue.inDatabase { db in
try db.execute("CREATE TABLE datas (data BLOB)")
// Make sure Data is on the heap (15 bytes is enough)
// For more context, see:
// https://forums.swift.org/t/swift-5-how-to-test-data-bytesnocopydeallocator/20299/2?u=gwendal.roue
let data = Data(repeating: 0xaa, count: 15)
let data = "foo".data(using: .utf8)
try db.execute("INSERT INTO datas (data) VALUES (?)", arguments: [data])
let rows = try Row.fetchCursor(db, "SELECT * FROM datas")
let rows = try Row.fetchCursor(db, "SELECT ?", arguments: [data])
while let row = try rows.next() {
let sqliteStatement = row.sqliteStatement
let sqliteBytes = sqlite3_column_blob(sqliteStatement, 0)
let blobPointer = sqlite3_column_blob(row.sqliteStatement, 0)
do {
// This data should be copied:
let copiedData: Data = row[0]
copiedData.withUnsafeBytes { copiedBytes in
XCTAssertNotEqual(copiedBytes, sqliteBytes)
}
XCTAssertEqual(copiedData, data)
copiedData.withBaseAddress { dataPointer in
XCTAssertNotEqual(dataPointer, blobPointer)
}
}
do {
// This data should not be copied
let nonCopiedData = row.dataNoCopy(atIndex: 0)!
nonCopiedData.withUnsafeBytes { nonCopiedBytes in
XCTAssertEqual(nonCopiedBytes, sqliteBytes)
}
XCTAssertEqual(nonCopiedData, data)
nonCopiedData.withBaseAddress { dataPointer in
XCTAssertEqual(dataPointer, blobPointer)
}
}
}
let row = try Row.fetchOne(db, "SELECT * FROM datas")!
let dbValue = row.first!.1
let row = try Row.fetchOne(db, "SELECT ?", arguments: [data])!
let dbValue = row.first!.1 // TODO: think about exposing a (column:,databaseValue:) tuple
switch dbValue.storage {
case .blob(let data):
data.withUnsafeBytes { (dataBytes: UnsafePointer<UInt8>) -> Void in
data.withBaseAddress { dataPointer in
do {
// This data should not be copied:
let nonCopiedData: Data = row[0]
nonCopiedData.withUnsafeBytes { nonCopiedBytes in
XCTAssertEqual(nonCopiedBytes, dataBytes)
}
XCTAssertEqual(nonCopiedData, data)
nonCopiedData.withBaseAddress { nonCopiedBytes in
XCTAssertEqual(nonCopiedBytes, dataPointer)
}
}
do {
// This data should not be copied:
let nonCopiedData = row.dataNoCopy(atIndex: 0)!
nonCopiedData.withUnsafeBytes { nonCopiedBytes in
XCTAssertEqual(nonCopiedBytes, dataBytes)
}
XCTAssertEqual(nonCopiedData, data)
nonCopiedData.withBaseAddress { nonCopiedBytes in
XCTAssertEqual(nonCopiedBytes, dataPointer)
}
}
}
default:
@ -75,3 +74,18 @@ class DataMemoryTests: GRDBTestCase {
}
}
}
extension Data {
// Helper for comparing data heap pointers, depending on the Swift version
fileprivate func withBaseAddress(_ body: (UnsafeRawPointer?) -> Void) {
#if swift(>=5.0)
withUnsafeBytes {
body($0.baseAddress)
}
#else
withUnsafeBytes {
body(UnsafeRawPointer($0))
}
#endif
}
}

View File

@ -31,7 +31,11 @@ private extension DatabaseValue {
private let emojiString = "'fooéı👨👨🏿🇫🇷🇨🇮'"
private let emojiData = emojiString.data(using: .utf8)
#if swift(>=5.0)
private let nonUTF8Data = Data([0x80])
#else
private let nonUTF8Data = Data(bytes: [0x80])
#endif
private let invalidString = "\u{FFFD}" // decoded from nonUTF8Data
// Until SPM tests can load resources, disable this test for SPM.
#if !SWIFT_PACKAGE

View File

@ -167,10 +167,22 @@ class GRDBTestCase: XCTestCase {
}
}
#if !swift(>=4.2)
#if !swift(>=4.1.5)
extension Sequence {
func allSatisfy(_ predicate: (Self.Element) throws -> Bool) rethrows -> Bool {
return try !contains(where: { try !predicate($0) })
}
}
extension Collection {
func firstIndex(where predicate:(Element) throws -> Bool) rethrows -> Index? {
return try index(where: predicate)
}
}
extension Collection where Element: Equatable {
func firstIndex(of element: Element) -> Index? {
return index(of: element)
}
}
#endif

View File

@ -36,14 +36,14 @@ class IndexInfoTests: GRDBTestCase {
let indexes = try db.indexes(on: "citizenships")
XCTAssertEqual(indexes.count, 2)
if let i = indexes.index(where: { $0.columns == ["year"] }) {
if let i = indexes.firstIndex(where: { $0.columns == ["year"] }) {
XCTAssertEqual(indexes[i].name, "citizenshipsOnYear")
XCTAssertEqual(indexes[i].columns, ["year"])
XCTAssertFalse(indexes[i].isUnique)
} else {
XCTFail()
}
if let i = indexes.index(where: { $0.columns == ["personId", "countryIsoCode"] }) {
if let i = indexes.firstIndex(where: { $0.columns == ["personId", "countryIsoCode"] }) {
XCTAssertEqual(indexes[i].name, "sqlite_autoindex_citizenships_1")
XCTAssertEqual(indexes[i].columns, ["personId", "countryIsoCode"])
XCTAssertTrue(indexes[i].isUnique)

View File

@ -48,9 +48,9 @@ class RowFromDictionaryLiteralTests : RowTestCase {
func testRowValueAtIndex() {
let row: Row = ["a": 0, "b": 1, "c": 2]
let aIndex = row.distance(from: row.startIndex, to: row.index(where: { (column, value) in column == "a" })!)
let bIndex = row.distance(from: row.startIndex, to: row.index(where: { (column, value) in column == "b" })!)
let cIndex = row.distance(from: row.startIndex, to: row.index(where: { (column, value) in column == "c" })!)
let aIndex = row.distance(from: row.startIndex, to: row.firstIndex(where: { (column, value) in column == "a" })!)
let bIndex = row.distance(from: row.startIndex, to: row.firstIndex(where: { (column, value) in column == "b" })!)
let cIndex = row.distance(from: row.startIndex, to: row.firstIndex(where: { (column, value) in column == "c" })!)
// Raw extraction
assertRowRawValueEqual(row, index: aIndex, value: 0 as Int64)
@ -140,11 +140,11 @@ class RowFromDictionaryLiteralTests : RowTestCase {
func testRowDatabaseValueAtIndex() throws {
let row: Row = ["null": nil, "int64": 1, "double": 1.1, "string": "foo", "blob": "SQLite".data(using: .utf8)]
let nullIndex = row.distance(from: row.startIndex, to: row.index(where: { (column, value) in column == "null" })!)
let int64Index = row.distance(from: row.startIndex, to: row.index(where: { (column, value) in column == "int64" })!)
let doubleIndex = row.distance(from: row.startIndex, to: row.index(where: { (column, value) in column == "double" })!)
let stringIndex = row.distance(from: row.startIndex, to: row.index(where: { (column, value) in column == "string" })!)
let blobIndex = row.distance(from: row.startIndex, to: row.index(where: { (column, value) in column == "blob" })!)
let nullIndex = row.distance(from: row.startIndex, to: row.firstIndex(where: { (column, value) in column == "null" })!)
let int64Index = row.distance(from: row.startIndex, to: row.firstIndex(where: { (column, value) in column == "int64" })!)
let doubleIndex = row.distance(from: row.startIndex, to: row.firstIndex(where: { (column, value) in column == "double" })!)
let stringIndex = row.distance(from: row.startIndex, to: row.firstIndex(where: { (column, value) in column == "string" })!)
let blobIndex = row.distance(from: row.startIndex, to: row.firstIndex(where: { (column, value) in column == "blob" })!)
guard case .null = (row[nullIndex] as DatabaseValue).storage else { XCTFail(); return }
guard case .int64(let int64) = (row[int64Index] as DatabaseValue).storage, int64 == 1 else { XCTFail(); return }