From 44097d32651d093e826e16ce5d2fa13d4c774c3a Mon Sep 17 00:00:00 2001 From: Dave DeLong Date: Mon, 16 Apr 2018 16:13:04 +0200 Subject: [PATCH 01/52] Add `and` and `or` functions When you're programmatically building a query to execute, you have times where you build a variable number of clauses by which you filter a table. There isn't a great way to turn an `Array>` into an `Expression`, short of making a large `(a AND (b AND (c AND (d AND e))))` expression. `and` and `or` fix this by allowing you to turn an `Array>` directly into a lower-complexity clause: `(a AND b AND c AND d AND e)` --- Sources/SQLite/Helpers.swift | 6 +++++- Sources/SQLite/Typed/Operators.swift | 12 ++++++++++++ Tests/SQLiteTests/OperatorsTests.swift | 20 ++++++++++++++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/Sources/SQLite/Helpers.swift b/Sources/SQLite/Helpers.swift index ac831667..b4ff360e 100644 --- a/Sources/SQLite/Helpers.swift +++ b/Sources/SQLite/Helpers.swift @@ -73,7 +73,11 @@ extension String { } func infix(_ lhs: Expressible, _ rhs: Expressible, wrap: Bool = true) -> Expression { - let expression = Expression(" \(self) ".join([lhs, rhs]).expression) + return infix([lhs, rhs]) + } + + func infix(_ terms: [Expressible], wrap: Bool = true) -> Expression { + let expression = Expression(" \(self) ".join(terms).expression) guard wrap else { return expression } diff --git a/Sources/SQLite/Typed/Operators.swift b/Sources/SQLite/Typed/Operators.swift index d97e52b9..a6df717f 100644 --- a/Sources/SQLite/Typed/Operators.swift +++ b/Sources/SQLite/Typed/Operators.swift @@ -516,6 +516,12 @@ public func ~=(lhs: PartialRangeFrom, rhs: Expression) -> Expr // MARK: - +public func and(_ terms: Expression...) -> Expression { + return "AND".infix(terms) +} +public func and(_ terms: [Expression]) -> Expression { + return "AND".infix(terms) +} public func &&(lhs: Expression, rhs: Expression) -> Expression { return "AND".infix(lhs, rhs) } @@ -541,6 +547,12 @@ public func &&(lhs: Bool, rhs: Expression) -> Expression { return "AND".infix(lhs, rhs) } +public func or(_ terms: Expression...) -> Expression { + return "OR".infix(terms) +} +public func or(_ terms: [Expression]) -> Expression { + return "OR".infix(terms) +} public func ||(lhs: Expression, rhs: Expression) -> Expression { return "OR".infix(lhs, rhs) } diff --git a/Tests/SQLiteTests/OperatorsTests.swift b/Tests/SQLiteTests/OperatorsTests.swift index 948eb0a4..050b1e39 100644 --- a/Tests/SQLiteTests/OperatorsTests.swift +++ b/Tests/SQLiteTests/OperatorsTests.swift @@ -290,6 +290,16 @@ class OperatorsTests : XCTestCase { AssertSQL("(1 AND \"bool\")", true && bool) AssertSQL("(1 AND \"boolOptional\")", true && boolOptional) } + + func test_andFunction_withBooleanExpressions_buildsCompoundExpression() { + AssertSQL("(\"bool\" AND \"bool\" AND \"bool\")", and([bool, bool, bool])) + AssertSQL("(\"bool\" AND \"bool\")", and([bool, bool])) + AssertSQL("(\"bool\")", and([bool])) + + AssertSQL("(\"bool\" AND \"bool\" AND \"bool\")", and(bool, bool, bool)) + AssertSQL("(\"bool\" AND \"bool\")", and(bool, bool)) + AssertSQL("(\"bool\")", and(bool)) + } func test_doubleOrOperator_withBooleanExpressions_buildsCompoundExpression() { AssertSQL("(\"bool\" OR \"bool\")", bool || bool) @@ -301,6 +311,16 @@ class OperatorsTests : XCTestCase { AssertSQL("(1 OR \"bool\")", true || bool) AssertSQL("(1 OR \"boolOptional\")", true || boolOptional) } + + func test_orFunction_withBooleanExpressions_buildsCompoundExpression() { + AssertSQL("(\"bool\" OR \"bool\" OR \"bool\")", or([bool, bool, bool])) + AssertSQL("(\"bool\" OR \"bool\")", or([bool, bool])) + AssertSQL("(\"bool\")", or([bool])) + + AssertSQL("(\"bool\" OR \"bool\" OR \"bool\")", or(bool, bool, bool)) + AssertSQL("(\"bool\" OR \"bool\")", or(bool, bool)) + AssertSQL("(\"bool\")", or(bool)) + } func test_unaryNotOperator_withBooleanExpressions_buildsNotExpression() { AssertSQL("NOT (\"bool\")", !bool) From e080a3ac0d2dc8fea15dde5681939bbbc0848b3e Mon Sep 17 00:00:00 2001 From: Dave DeLong Date: Mon, 16 Apr 2018 16:15:09 +0200 Subject: [PATCH 02/52] Forgot to pass on a parameter --- Sources/SQLite/Helpers.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/SQLite/Helpers.swift b/Sources/SQLite/Helpers.swift index b4ff360e..4b755078 100644 --- a/Sources/SQLite/Helpers.swift +++ b/Sources/SQLite/Helpers.swift @@ -73,7 +73,7 @@ extension String { } func infix(_ lhs: Expressible, _ rhs: Expressible, wrap: Bool = true) -> Expression { - return infix([lhs, rhs]) + return infix([lhs, rhs], wrap: wrap) } func infix(_ terms: [Expressible], wrap: Bool = true) -> Expression { From 1f84745513aa56bbd38e428e0a5db5d55de3381e Mon Sep 17 00:00:00 2001 From: Serg Date: Sun, 15 Jul 2018 23:36:52 +0700 Subject: [PATCH 03/52] Support Int64 coding --- Sources/SQLite/Typed/Coding.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/SQLite/Typed/Coding.swift b/Sources/SQLite/Typed/Coding.swift index 7c70db33..3e4b5226 100644 --- a/Sources/SQLite/Typed/Coding.swift +++ b/Sources/SQLite/Typed/Coding.swift @@ -152,7 +152,7 @@ fileprivate class SQLiteEncoder: Encoder { } func encode(_ value: Int64, forKey key: Key) throws { - throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: self.codingPath, debugDescription: "encoding an Int64 is not supported")) + self.encoder.setters.append(Expression(key.stringValue) <- value) } func encode(_ value: UInt, forKey key: Key) throws { @@ -249,7 +249,7 @@ fileprivate class SQLiteDecoder : Decoder { } func decode(_ type: Int64.Type, forKey key: Key) throws -> Int64 { - throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: self.codingPath, debugDescription: "decoding an UInt64 is not supported")) + return try self.row.get(Expression(key.stringValue)) } func decode(_ type: UInt.Type, forKey key: Key) throws -> UInt { From ad63c37cc044af3d4bc1588faa6c41c5600b71b0 Mon Sep 17 00:00:00 2001 From: Madalin Mamuleanu Date: Tue, 2 Oct 2018 00:21:03 +0300 Subject: [PATCH 04/52] Documentation update. Fixes #841 --- Documentation/Index.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/Index.md b/Documentation/Index.md index 628e7464..f214b50a 100644 --- a/Documentation/Index.md +++ b/Documentation/Index.md @@ -266,8 +266,8 @@ var path = NSSearchPathForDirectoriesInDomains( ).first! + "/" + Bundle.main.bundleIdentifier! // create parent directory iff it doesn’t exist -try FileManager.default.createDirectoryAtPath( - path, withIntermediateDirectories: true, attributes: nil +try FileManager.default.createDirectory( +atPath: path, withIntermediateDirectories: true, attributes: nil ) let db = try Connection("\(path)/db.sqlite3") From 7c0bdfd01fd3461d273d24cb4f0205614567d4c9 Mon Sep 17 00:00:00 2001 From: Christian Roman Date: Fri, 11 Jan 2019 09:05:41 -0600 Subject: [PATCH 05/52] Codable - Add Date support --- Documentation/Index.md | 2 +- Sources/SQLite/Typed/Coding.swift | 7 +++++++ Tests/SQLiteTests/QueryTests.swift | 27 +++++++++++++++------------ Tests/SQLiteTests/TestHelpers.swift | 5 ++++- 4 files changed, 27 insertions(+), 14 deletions(-) diff --git a/Documentation/Index.md b/Documentation/Index.md index 628e7464..7fc74ea0 100644 --- a/Documentation/Index.md +++ b/Documentation/Index.md @@ -1557,7 +1557,7 @@ Both of the above methods also have the following optional parameter: There are a few restrictions on using Codable types: - The encodable and decodable objects can only use the following types: - - Int, Bool, Float, Double, String + - Int, Bool, Float, Double, String, Date - Nested Codable types that will be encoded as JSON to a single column - These methods will not handle object relationships for you. You must write your own Codable and Decodable implementations if you wish to support this. diff --git a/Sources/SQLite/Typed/Coding.swift b/Sources/SQLite/Typed/Coding.swift index c3fb931b..0eb02a55 100644 --- a/Sources/SQLite/Typed/Coding.swift +++ b/Sources/SQLite/Typed/Coding.swift @@ -132,6 +132,9 @@ fileprivate class SQLiteEncoder: Encoder { if let data = value as? Data { self.encoder.setters.append(Expression(key.stringValue) <- data) } + else if let date = value as? Date { + self.encoder.setters.append(Expression(key.stringValue) <- date.datatypeValue) + } else { let encoded = try JSONEncoder().encode(value) let string = String(data: encoded, encoding: .utf8) @@ -290,6 +293,10 @@ fileprivate class SQLiteDecoder : Decoder { let data = try self.row.get(Expression(key.stringValue)) return data as! T } + else if type == Date.self { + let date = try self.row.get(Expression(key.stringValue)) + return date as! T + } guard let JSONString = try self.row.get(Expression(key.stringValue)) else { throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: self.codingPath, debugDescription: "an unsupported type was found")) } diff --git a/Tests/SQLiteTests/QueryTests.swift b/Tests/SQLiteTests/QueryTests.swift index 2a9e4ecb..8f48264f 100644 --- a/Tests/SQLiteTests/QueryTests.swift +++ b/Tests/SQLiteTests/QueryTests.swift @@ -249,23 +249,23 @@ class QueryTests : XCTestCase { func test_insert_encodable() throws { let emails = Table("emails") - let value = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4, optional: nil, sub: nil) + let value = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4, date: Date(timeIntervalSince1970: 0), optional: nil, sub: nil) let insert = try emails.insert(value) AssertSQL( - "INSERT INTO \"emails\" (\"int\", \"string\", \"bool\", \"float\", \"double\") VALUES (1, '2', 1, 3.0, 4.0)", + "INSERT INTO \"emails\" (\"int\", \"string\", \"bool\", \"float\", \"double\", \"date\") VALUES (1, '2', 1, 3.0, 4.0, '1970-01-01T00:00:00.000')", insert ) } func test_insert_encodable_with_nested_encodable() throws { let emails = Table("emails") - let value1 = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4, optional: nil, sub: nil) - let value = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4, optional: "optional", sub: value1) + let value1 = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4, date: Date(timeIntervalSince1970: 0), optional: nil, sub: nil) + let value = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4, date: Date(timeIntervalSince1970: 0), optional: "optional", sub: value1) let insert = try emails.insert(value) let encodedJSON = try JSONEncoder().encode(value1) let encodedJSONString = String(data: encodedJSON, encoding: .utf8)! AssertSQL( - "INSERT INTO \"emails\" (\"int\", \"string\", \"bool\", \"float\", \"double\", \"optional\", \"sub\") VALUES (1, '2', 1, 3.0, 4.0, 'optional', '\(encodedJSONString)')", + "INSERT INTO \"emails\" (\"int\", \"string\", \"bool\", \"float\", \"double\", \"date\", \"optional\", \"sub\") VALUES (1, '2', 1, 3.0, 4.0, '1970-01-01T00:00:00.000', 'optional', '\(encodedJSONString)')", insert ) } @@ -286,23 +286,23 @@ class QueryTests : XCTestCase { func test_update_encodable() throws { let emails = Table("emails") - let value = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4, optional: nil, sub: nil) + let value = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4, date: Date(timeIntervalSince1970: 0), optional: nil, sub: nil) let update = try emails.update(value) AssertSQL( - "UPDATE \"emails\" SET \"int\" = 1, \"string\" = '2', \"bool\" = 1, \"float\" = 3.0, \"double\" = 4.0", + "UPDATE \"emails\" SET \"int\" = 1, \"string\" = '2', \"bool\" = 1, \"float\" = 3.0, \"double\" = 4.0, \"date\" = '1970-01-01T00:00:00.000'", update ) } func test_update_encodable_with_nested_encodable() throws { let emails = Table("emails") - let value1 = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4, optional: nil, sub: nil) - let value = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4, optional: nil, sub: value1) + let value1 = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4, date: Date(timeIntervalSince1970: 0), optional: nil, sub: nil) + let value = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4, date: Date(timeIntervalSince1970: 0), optional: nil, sub: value1) let update = try emails.update(value) let encodedJSON = try JSONEncoder().encode(value1) let encodedJSONString = String(data: encodedJSON, encoding: .utf8)! AssertSQL( - "UPDATE \"emails\" SET \"int\" = 1, \"string\" = '2', \"bool\" = 1, \"float\" = 3.0, \"double\" = 4.0, \"sub\" = '\(encodedJSONString)'", + "UPDATE \"emails\" SET \"int\" = 1, \"string\" = '2', \"bool\" = 1, \"float\" = 3.0, \"double\" = 4.0, \"date\" = '1970-01-01T00:00:00.000', \"sub\" = '\(encodedJSONString)'", update ) } @@ -438,12 +438,13 @@ class QueryIntegrationTests : SQLiteTestCase { builder.column(Expression("bool")) builder.column(Expression("float")) builder.column(Expression("double")) + builder.column(Expression("date")) builder.column(Expression("optional")) builder.column(Expression("sub")) }) - let value1 = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4, optional: nil, sub: nil) - let value = TestCodable(int: 5, string: "6", bool: true, float: 7, double: 8, optional: "optional", sub: value1) + let value1 = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4, date: Date(timeIntervalSince1970: 0), optional: nil, sub: nil) + let value = TestCodable(int: 5, string: "6", bool: true, float: 7, double: 8, date: Date(timeIntervalSince1970: 5000), optional: "optional", sub: value1) try db.run(table.insert(value)) @@ -455,12 +456,14 @@ class QueryIntegrationTests : SQLiteTestCase { XCTAssertEqual(values[0].bool, true) XCTAssertEqual(values[0].float, 7) XCTAssertEqual(values[0].double, 8) + XCTAssertEqual(values[0].date, Date(timeIntervalSince1970: 5000)) XCTAssertEqual(values[0].optional, "optional") XCTAssertEqual(values[0].sub?.int, 1) XCTAssertEqual(values[0].sub?.string, "2") XCTAssertEqual(values[0].sub?.bool, true) XCTAssertEqual(values[0].sub?.float, 3) XCTAssertEqual(values[0].sub?.double, 4) + XCTAssertEqual(values[0].sub?.date, Date(timeIntervalSince1970: 0)) XCTAssertNil(values[0].sub?.optional) XCTAssertNil(values[0].sub?.sub) } diff --git a/Tests/SQLiteTests/TestHelpers.swift b/Tests/SQLiteTests/TestHelpers.swift index 2e491b01..247013b4 100644 --- a/Tests/SQLiteTests/TestHelpers.swift +++ b/Tests/SQLiteTests/TestHelpers.swift @@ -26,6 +26,7 @@ class SQLiteTestCase : XCTestCase { salary REAL, admin BOOLEAN NOT NULL DEFAULT 0 CHECK (admin IN (0, 1)), manager_id INTEGER, + created_at DATETIME, FOREIGN KEY(manager_id) REFERENCES users(id) ) """ @@ -111,15 +112,17 @@ class TestCodable: Codable { let bool: Bool let float: Float let double: Double + let date: Date let optional: String? let sub: TestCodable? - init(int: Int, string: String, bool: Bool, float: Float, double: Double, optional: String?, sub: TestCodable?) { + init(int: Int, string: String, bool: Bool, float: Float, double: Double, date: Date, optional: String?, sub: TestCodable?) { self.int = int self.string = string self.bool = bool self.float = float self.double = double + self.date = date self.optional = optional self.sub = sub } From 0fa531d1118bf88e83ffb893d89b8898bfdd85b8 Mon Sep 17 00:00:00 2001 From: Andrew Finnell Date: Sun, 24 Feb 2019 13:54:35 -0500 Subject: [PATCH 06/52] Implement Upsert [#482] # Problem Apps need the ability to insert or, if the row already exists, update the existing row. This is commonly called "upsert" and is [documented for SQLite here](https://www.sqlite.org/lang_UPSERT.html). The existing "replace" on conflict clause for `insert()` often does not have the desired behavior. It will cascade (with deletes, if specified) along foreign keys if the value exists and has to be replaced ([see Issue 842](https://github.com/stephencelis/SQLite.swift/issues/842)). # Solution Create an `upsert()` method for queries. The basic form allows for the fallback setters to be manually specified. A slightly more ergonomic version will compute the setters from the inserted values by removing the "conflicting" column, and then using the inserted values via the "excluded" qualifier. Additionally, an `Encodable` version of `upsert` is provided. --- Documentation/Index.md | 28 +++++++++++++++++++++ Sources/SQLite/Typed/Coding.swift | 23 ++++++++++++++++++ Sources/SQLite/Typed/Query.swift | 39 ++++++++++++++++++++++++++++++ Sources/SQLite/Typed/Setter.swift | 5 ++++ Tests/SQLiteTests/QueryTests.swift | 35 ++++++++++++++++++++++++++- 5 files changed, 129 insertions(+), 1 deletion(-) diff --git a/Documentation/Index.md b/Documentation/Index.md index 628e7464..61c287d6 100644 --- a/Documentation/Index.md +++ b/Documentation/Index.md @@ -35,6 +35,7 @@ - [Sorting Rows](#sorting-rows) - [Limiting and Paging Results](#limiting-and-paging-results) - [Aggregation](#aggregation) + - [Upserting Rows](#upserting-rows) - [Updating Rows](#updating-rows) - [Deleting Rows](#deleting-rows) - [Transactions and Savepoints](#transactions-and-savepoints) @@ -1098,6 +1099,33 @@ let count = try db.scalar(users.filter(name != nil).count) > // SELECT count(DISTINCT "name") FROM "users" > ``` +## Upserting Rows + +We can upsert rows into a table by calling a [query’s](#queries) `upsert` +function with a list of [setters](#setters)—typically [typed column +expressions](#expressions) and values (which can also be expressions)—each +joined by the `<-` operator. Upserting is like inserting, except if there is a +conflict on the specified column value, SQLite will perform an update on the row instead. + +```swift +try db.run(users.upsert(email <- "alice@mac.com", name <- "Alice"), onConflictOf: email) +// INSERT INTO "users" ("email", "name") VALUES ('alice@mac.com', 'Alice') ON CONFLICT (\"email\") DO UPDATE SET \"name\" = \"excluded\".\"name\" +``` + +The `upsert` function, when run successfully, returns an `Int64` representing +the inserted row’s [`ROWID`][ROWID]. + +```swift +do { + let rowid = try db.run(users.upsert(email <- "alice@mac.com", name <- "Alice", onConflictOf: email)) + print("inserted id: \(rowid)") +} catch { + print("insertion failed: \(error)") +} +``` + +The [`insert`](#inserting-rows), [`update`](#updating-rows), and [`delete`](#deleting-rows) functions +follow similar patterns. ## Updating Rows diff --git a/Sources/SQLite/Typed/Coding.swift b/Sources/SQLite/Typed/Coding.swift index c3fb931b..6ed8c987 100644 --- a/Sources/SQLite/Typed/Coding.swift +++ b/Sources/SQLite/Typed/Coding.swift @@ -45,6 +45,29 @@ extension QueryType { return self.insert(encoder.setters + otherSetters) } + + /// Creates an `INSERT ON CONFLICT DO UPDATE` statement, aka upsert, by encoding the given object + /// This method converts any custom nested types to JSON data and does not handle any sort + /// of object relationships. If you want to support relationships between objects you will + /// have to provide your own Encodable implementations that encode the correct ids. + /// + /// - Parameters: + /// + /// - encodable: An encodable object to insert + /// + /// - userInfo: User info to be passed to encoder + /// + /// - otherSetters: Any other setters to include in the insert + /// + /// - onConflictOf: The column that if conflicts should trigger an update instead of insert. + /// + /// - Returns: An `INSERT` statement fort the encodable object + public func upsert(_ encodable: Encodable, userInfo: [CodingUserInfoKey:Any] = [:], otherSetters: [Setter] = [], onConflictOf conflicting: Expressible) throws -> Insert { + let encoder = SQLiteEncoder(userInfo: userInfo) + try encodable.encode(to: encoder) + return self.upsert(encoder.setters + otherSetters, onConflictOf: conflicting) + } + /// Creates an `UPDATE` statement by encoding the given object /// This method converts any custom nested types to JSON data and does not handle any sort /// of object relationships. If you want to support relationships between objects you will diff --git a/Sources/SQLite/Typed/Query.swift b/Sources/SQLite/Typed/Query.swift index f6ef6df8..8793acae 100644 --- a/Sources/SQLite/Typed/Query.swift +++ b/Sources/SQLite/Typed/Query.swift @@ -672,6 +672,45 @@ extension QueryType { query.expression ]).expression) } + + // MARK: UPSERT + + public func upsert(_ insertValues: Setter..., onConflictOf conflicting: Expressible) -> Insert { + return upsert(insertValues, onConflictOf: conflicting) + } + + public func upsert(_ insertValues: [Setter], onConflictOf conflicting: Expressible) -> Insert { + let setValues = insertValues.filter { $0.column.asSQL() != conflicting.asSQL() } + .map { Setter(excluded: $0.column) } + return upsert(insertValues, onConflictOf: conflicting, set: setValues) + } + + public func upsert(_ insertValues: Setter..., onConflictOf conflicting: Expressible, set setValues: [Setter]) -> Insert { + return upsert(insertValues, onConflictOf: conflicting, set: setValues) + } + + public func upsert(_ insertValues: [Setter], onConflictOf conflicting: Expressible, set setValues: [Setter]) -> Insert { + let insert = insertValues.reduce((columns: [Expressible](), values: [Expressible]())) { insert, setter in + (insert.columns + [setter.column], insert.values + [setter.value]) + } + + let clauses: [Expressible?] = [ + Expression(literal: "INSERT"), + Expression(literal: "INTO"), + tableName(), + "".wrap(insert.columns) as Expression, + Expression(literal: "VALUES"), + "".wrap(insert.values) as Expression, + whereClause, + Expression(literal: "ON CONFLICT"), + "".wrap(conflicting) as Expression, + Expression(literal: "DO UPDATE SET"), + ", ".join(setValues.map { $0.expression }) + ] + + return Insert(" ".join(clauses.compactMap { $0 }).expression) + } + // MARK: UPDATE diff --git a/Sources/SQLite/Typed/Setter.swift b/Sources/SQLite/Typed/Setter.swift index 86f16fca..3d3170f6 100644 --- a/Sources/SQLite/Typed/Setter.swift +++ b/Sources/SQLite/Typed/Setter.swift @@ -60,6 +60,11 @@ public struct Setter { self.value = Expression(value: value) } + init(excluded column: Expressible) { + let excluded = Expression("excluded") + self.column = column + self.value = ".".join([excluded, column.expression]) + } } extension Setter : Expressible { diff --git a/Tests/SQLiteTests/QueryTests.swift b/Tests/SQLiteTests/QueryTests.swift index 2a9e4ecb..694e7f26 100644 --- a/Tests/SQLiteTests/QueryTests.swift +++ b/Tests/SQLiteTests/QueryTests.swift @@ -270,6 +270,24 @@ class QueryTests : XCTestCase { ) } + func test_upsert_withOnConflict_compilesInsertOrOnConflictExpression() { + AssertSQL( + "INSERT INTO \"users\" (\"email\", \"age\") VALUES ('alice@example.com', 30) ON CONFLICT (\"email\") DO UPDATE SET \"age\" = \"excluded\".\"age\"", + users.upsert(email <- "alice@example.com", age <- 30, onConflictOf: email) + ) + } + + func test_upsert_encodable() throws { + let emails = Table("emails") + let string = Expression("string") + let value = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4, optional: nil, sub: nil) + let insert = try emails.upsert(value, onConflictOf: string) + AssertSQL( + "INSERT INTO \"emails\" (\"int\", \"string\", \"bool\", \"float\", \"double\") VALUES (1, '2', 1, 3.0, 4.0) ON CONFLICT (\"string\") DO UPDATE SET \"int\" = \"excluded\".\"int\", \"bool\" = \"excluded\".\"bool\", \"float\" = \"excluded\".\"float\", \"double\" = \"excluded\".\"double\"", + insert + ) + } + func test_update_compilesUpdateExpression() { AssertSQL( "UPDATE \"users\" SET \"age\" = 30, \"admin\" = 1 WHERE (\"id\" = 1)", @@ -378,7 +396,8 @@ class QueryIntegrationTests : SQLiteTestCase { let id = Expression("id") let email = Expression("email") - + let age = Expression("age") + override func setUp() { super.setUp() @@ -483,6 +502,20 @@ class QueryIntegrationTests : SQLiteTestCase { XCTAssertEqual(1, id) } + func test_upsert() throws { + let fetchAge = { () throws -> Int? in + return try self.db.pluck(self.users.filter(self.email == "alice@example.com")).flatMap { $0[self.age] } + } + + let id = try db.run(users.upsert(email <- "alice@example.com", age <- 30, onConflictOf: email)) + XCTAssertEqual(1, id) + XCTAssertEqual(30, try fetchAge()) + + let nextId = try db.run(users.upsert(email <- "alice@example.com", age <- 42, onConflictOf: email)) + XCTAssertEqual(1, nextId) + XCTAssertEqual(42, try fetchAge()) + } + func test_update() { let changes = try! db.run(users.update(email <- "alice@example.com")) XCTAssertEqual(0, changes) From 78089e2bdebbf1f634eaf85876d719981ed3c147 Mon Sep 17 00:00:00 2001 From: Stephan Heilner Date: Thu, 31 May 2018 15:14:02 -0600 Subject: [PATCH 07/52] Fixed bug not allowing columns from multiple tables in the .select --- SQLite.xcodeproj/project.pbxproj | 8 +++++ Sources/SQLite/Typed/Query.swift | 1 - Tests/SQLiteTests/SelectTests.swift | 45 +++++++++++++++++++++++++++++ 3 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 Tests/SQLiteTests/SelectTests.swift diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj index 2eb11fd9..9143d1b2 100644 --- a/SQLite.xcodeproj/project.pbxproj +++ b/SQLite.xcodeproj/project.pbxproj @@ -108,6 +108,9 @@ 49EB68C51F7B3CB400D89D40 /* Coding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49EB68C31F7B3CB400D89D40 /* Coding.swift */; }; 49EB68C61F7B3CB400D89D40 /* Coding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49EB68C31F7B3CB400D89D40 /* Coding.swift */; }; 49EB68C71F7B3CB400D89D40 /* Coding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49EB68C31F7B3CB400D89D40 /* Coding.swift */; }; + D4DB368C20C09CFB00D5A58E /* SelectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4DB368A20C09C9B00D5A58E /* SelectTests.swift */; }; + D4DB368D20C09CFC00D5A58E /* SelectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4DB368A20C09C9B00D5A58E /* SelectTests.swift */; }; + D4DB368E20C09CFD00D5A58E /* SelectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4DB368A20C09C9B00D5A58E /* SelectTests.swift */; }; EE247AD71C3F04ED00AE3E12 /* SQLite.h in Headers */ = {isa = PBXBuildFile; fileRef = EE247AD61C3F04ED00AE3E12 /* SQLite.h */; settings = {ATTRIBUTES = (Public, ); }; }; EE247ADE1C3F04ED00AE3E12 /* SQLite.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EE247AD31C3F04ED00AE3E12 /* SQLite.framework */; }; EE247B031C3F06E900AE3E12 /* Blob.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AEE1C3F06E900AE3E12 /* Blob.swift */; }; @@ -228,6 +231,7 @@ 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; }; 49EB68C31F7B3CB400D89D40 /* Coding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Coding.swift; sourceTree = ""; }; A121AC451CA35C79005A31D1 /* SQLite.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SQLite.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + D4DB368A20C09C9B00D5A58E /* SelectTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectTests.swift; sourceTree = ""; }; EE247AD31C3F04ED00AE3E12 /* SQLite.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SQLite.framework; sourceTree = BUILT_PRODUCTS_DIR; }; EE247AD61C3F04ED00AE3E12 /* SQLite.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SQLite.h; sourceTree = ""; }; EE247AD81C3F04ED00AE3E12 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -418,6 +422,7 @@ 19A17B93B48B5560E6E51791 /* Fixtures.swift */, 19A175C1F9CB3BBAB8FCEC7B /* RowTests.swift */, 19A1729B75C33F9A0B9A89C1 /* DateAndTimeFunctionTests.swift */, + D4DB368A20C09C9B00D5A58E /* SelectTests.swift */, ); name = SQLiteTests; path = Tests/SQLiteTests; @@ -847,6 +852,7 @@ 19A17F60B685636D1F83C2DD /* Fixtures.swift in Sources */, 19A1785195182AF8731A8BDA /* RowTests.swift in Sources */, 19A1769C1F3A7542BECF50FF /* DateAndTimeFunctionTests.swift in Sources */, + D4DB368E20C09CFD00D5A58E /* SelectTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -935,6 +941,7 @@ 19A17408007B182F884E3A53 /* Fixtures.swift in Sources */, 19A1720B67ED13E6150C6A3D /* RowTests.swift in Sources */, 19A17C80076860CF7751A056 /* DateAndTimeFunctionTests.swift in Sources */, + D4DB368C20C09CFB00D5A58E /* SelectTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -993,6 +1000,7 @@ 19A1709C3E7A406E62293B2A /* Fixtures.swift in Sources */, 19A171967CC511C4F6F773C9 /* RowTests.swift in Sources */, 19A172EB202970561E5C4245 /* DateAndTimeFunctionTests.swift in Sources */, + D4DB368D20C09CFC00D5A58E /* SelectTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Sources/SQLite/Typed/Query.swift b/Sources/SQLite/Typed/Query.swift index f6ef6df8..7ec5e581 100644 --- a/Sources/SQLite/Typed/Query.swift +++ b/Sources/SQLite/Typed/Query.swift @@ -963,7 +963,6 @@ extension Connection { try expandGlob(true)(q) continue column } - throw QueryError.noSuchTable(name: namespace) } throw QueryError.noSuchTable(name: namespace) } diff --git a/Tests/SQLiteTests/SelectTests.swift b/Tests/SQLiteTests/SelectTests.swift new file mode 100644 index 00000000..bca01092 --- /dev/null +++ b/Tests/SQLiteTests/SelectTests.swift @@ -0,0 +1,45 @@ +import XCTest +@testable import SQLite + +class SelectTests: SQLiteTestCase { + + override func setUp() { + super.setUp() + CreateUsersTable() + CreateUsersDataTable() + } + + func CreateUsersDataTable() { + try! db.execute(""" + CREATE TABLE users_name ( + id INTEGER, + user_id INTEGER REFERENCES users(id), + name TEXT + ) + """ + ) + } + + func test_select_columns_from_multiple_tables() { + let usersData = Table("users_name") + let users = Table("users") + + let name = Expression("name") + let id = Expression("id") + let userID = Expression("user_id") + let email = Expression("email") + + try! InsertUser("Joey") + try! db.run(usersData.insert( + id <- 1, + userID <- 1, + name <- "Joey" + )) + + try! db.prepare(users.select(name, email).join(usersData, on: userID == users[id])).forEach { + XCTAssertEqual($0[name], "Joey") + XCTAssertEqual($0[email], "Joey@example.com") + } + } + +} From 4de5dc458788bc09111d26f97c4ea46c8f08d946 Mon Sep 17 00:00:00 2001 From: Guillaume Algis Date: Wed, 14 Aug 2019 12:44:15 +0200 Subject: [PATCH 08/52] Add @discardableResult to all FTSConfig methods returning Self for chaining --- Sources/SQLite/Extensions/FTS4.swift | 18 +++++++++--------- Sources/SQLite/Extensions/FTS5.swift | 6 +++--- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Sources/SQLite/Extensions/FTS4.swift b/Sources/SQLite/Extensions/FTS4.swift index 5ef84dd7..f0184565 100644 --- a/Sources/SQLite/Extensions/FTS4.swift +++ b/Sources/SQLite/Extensions/FTS4.swift @@ -194,25 +194,25 @@ open class FTSConfig { } /// [Tokenizers](https://www.sqlite.org/fts3.html#tokenizer) - open func tokenizer(_ tokenizer: Tokenizer?) -> Self { + @discardableResult open func tokenizer(_ tokenizer: Tokenizer?) -> Self { self.tokenizer = tokenizer return self } /// [The prefix= option](https://www.sqlite.org/fts3.html#section_6_6) - open func prefix(_ prefix: [Int]) -> Self { + @discardableResult open func prefix(_ prefix: [Int]) -> Self { self.prefixes += prefix return self } /// [The content= option](https://www.sqlite.org/fts3.html#section_6_2) - open func externalContent(_ schema: SchemaType) -> Self { + @discardableResult open func externalContent(_ schema: SchemaType) -> Self { self.externalContentSchema = schema return self } /// [Contentless FTS4 Tables](https://www.sqlite.org/fts3.html#section_6_2_1) - open func contentless() -> Self { + @discardableResult open func contentless() -> Self { self.isContentless = true return self } @@ -308,31 +308,31 @@ open class FTS4Config : FTSConfig { } /// [The compress= and uncompress= options](https://www.sqlite.org/fts3.html#section_6_1) - open func compress(_ functionName: String) -> Self { + @discardableResult open func compress(_ functionName: String) -> Self { self.compressFunction = functionName return self } /// [The compress= and uncompress= options](https://www.sqlite.org/fts3.html#section_6_1) - open func uncompress(_ functionName: String) -> Self { + @discardableResult open func uncompress(_ functionName: String) -> Self { self.uncompressFunction = functionName return self } /// [The languageid= option](https://www.sqlite.org/fts3.html#section_6_3) - open func languageId(_ columnName: String) -> Self { + @discardableResult open func languageId(_ columnName: String) -> Self { self.languageId = columnName return self } /// [The matchinfo= option](https://www.sqlite.org/fts3.html#section_6_4) - open func matchInfo(_ matchInfo: MatchInfo) -> Self { + @discardableResult open func matchInfo(_ matchInfo: MatchInfo) -> Self { self.matchInfo = matchInfo return self } /// [FTS4 options](https://www.sqlite.org/fts3.html#fts4_options) - open func order(_ order: Order) -> Self { + @discardableResult open func order(_ order: Order) -> Self { self.order = order return self } diff --git a/Sources/SQLite/Extensions/FTS5.swift b/Sources/SQLite/Extensions/FTS5.swift index 763927ff..cf13f3d8 100644 --- a/Sources/SQLite/Extensions/FTS5.swift +++ b/Sources/SQLite/Extensions/FTS5.swift @@ -58,19 +58,19 @@ open class FTS5Config : FTSConfig { } /// [External Content Tables](https://www.sqlite.org/fts5.html#section_4_4_2) - open func contentRowId(_ column: Expressible) -> Self { + @discardableResult open func contentRowId(_ column: Expressible) -> Self { self.contentRowId = column return self } /// [The Columnsize Option](https://www.sqlite.org/fts5.html#section_4_5) - open func columnSize(_ size: Int) -> Self { + @discardableResult open func columnSize(_ size: Int) -> Self { self.columnSize = size return self } /// [The Detail Option](https://www.sqlite.org/fts5.html#section_4_6) - open func detail(_ detail: Detail) -> Self { + @discardableResult open func detail(_ detail: Detail) -> Self { self.detail = detail return self } From 115ad41835b9de8c2f69fa510c222ce85e526a71 Mon Sep 17 00:00:00 2001 From: Johannes Ebeling Date: Sat, 17 Aug 2019 00:15:34 +0200 Subject: [PATCH 09/52] Enable the encodable api to pass the onConflict parameter to the QueryType.insert function --- Sources/SQLite/Typed/Coding.swift | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/Sources/SQLite/Typed/Coding.swift b/Sources/SQLite/Typed/Coding.swift index c3fb931b..d4f99e6e 100644 --- a/Sources/SQLite/Typed/Coding.swift +++ b/Sources/SQLite/Typed/Coding.swift @@ -44,6 +44,30 @@ extension QueryType { try encodable.encode(to: encoder) return self.insert(encoder.setters + otherSetters) } + + /// Creates an `INSERT` statement by encoding the given object + /// This method converts any custom nested types to JSON data and does not handle any sort + /// of object relationships. If you want to support relationships between objects you will + /// have to provide your own Encodable implementations that encode the correct ids. + /// The onConflict will be passed to the actual insert function to define what should happen + /// when an error occurs during the insert operation. + /// + /// - Parameters: + /// + /// - onConlict: Define what happens when an insert operation fails + /// + /// - encodable: An encodable object to insert + /// + /// - userInfo: User info to be passed to encoder + /// + /// - otherSetters: Any other setters to include in the insert + /// + /// - Returns: An `INSERT` statement fort the encodable object + public func insert(or onConflict: OnConflict, encodable: Encodable, userInfo: [CodingUserInfoKey:Any] = [:], otherSetters: [Setter] = []) throws -> Insert { + let encoder = SQLiteEncoder(userInfo: userInfo) + try encodable.encode(to: encoder) + return self.insert(or: onConflict, encoder.setters + otherSetters) + } /// Creates an `UPDATE` statement by encoding the given object /// This method converts any custom nested types to JSON data and does not handle any sort From d8193fb00255186802fe5182cd84fa4de041f917 Mon Sep 17 00:00:00 2001 From: Bartek Date: Tue, 5 Nov 2019 08:40:20 +0100 Subject: [PATCH 10/52] closing brackets/aposthrophes few things wasnt closed properly --- Documentation/Index.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/Index.md b/Documentation/Index.md index 70d67c2d..c6c54437 100644 --- a/Documentation/Index.md +++ b/Documentation/Index.md @@ -1792,12 +1792,12 @@ let config = FTS5Config() .column(subject) .column(body, [.unindexed]) -try db.run(emails.create(.FTS5(config)) +try db.run(emails.create(.FTS5(config))) // CREATE VIRTUAL TABLE "emails" USING fts5("subject", "body" UNINDEXED) // Note that FTS5 uses a different syntax to select columns, so we need to rewrite // the last FTS4 query above as: -let replies = emails.filter(emails.match("subject:\"Re:\"*)) +let replies = emails.filter(emails.match("subject:\"Re:\"*")) // SELECT * FROM "emails" WHERE "emails" MATCH 'subject:"Re:"*' // https://www.sqlite.org/fts5.html#_changes_to_select_statements_ From ef3f88f7d02116c95088d7c5dae9a0af42f4dbfc Mon Sep 17 00:00:00 2001 From: Bradley Walters Date: Fri, 15 Nov 2019 09:52:51 -0700 Subject: [PATCH 11/52] Fix building with standalone sqlite3 >= 3.30.0 SQLite 3.30.0 changed the definition of SQLITE_DETERMINISTIC: `-#define SQLITE_DETERMINISTIC 0x800` `+#define SQLITE_DETERMINISTIC 0x000000800` Meaning that the (older) system sqlite3 library and the pod have different definitions, even though they're the same value. We've been importing the system sqlite3 module in SQLiteObjc.h even when linking against standalone sqlite. I added a check to SQLiteObjc.h to import the sqlite3 pod when we're using it, instead of always importing the system module. This leads to there being only one definition in scope. --- SQLite.swift.podspec | 3 ++- Sources/SQLiteObjc/include/SQLiteObjc.h | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/SQLite.swift.podspec b/SQLite.swift.podspec index 2341cfc4..712ddc36 100644 --- a/SQLite.swift.podspec +++ b/SQLite.swift.podspec @@ -50,7 +50,8 @@ Pod::Spec.new do |s| ss.private_header_files = 'Sources/SQLiteObjc/*.h' ss.xcconfig = { - 'OTHER_SWIFT_FLAGS' => '$(inherited) -DSQLITE_SWIFT_STANDALONE' + 'OTHER_SWIFT_FLAGS' => '$(inherited) -DSQLITE_SWIFT_STANDALONE', + 'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) SQLITE_SWIFT_STANDALONE=1' } ss.dependency 'sqlite3' diff --git a/Sources/SQLiteObjc/include/SQLiteObjc.h b/Sources/SQLiteObjc/include/SQLiteObjc.h index f8c2a3b3..e8ba9a7d 100644 --- a/Sources/SQLiteObjc/include/SQLiteObjc.h +++ b/Sources/SQLiteObjc/include/SQLiteObjc.h @@ -23,7 +23,11 @@ // @import Foundation; +#if defined(SQLITE_SWIFT_STANDALONE) +@import sqlite3; +#else @import SQLite3; +#endif NS_ASSUME_NONNULL_BEGIN typedef NSString * _Nullable (^_SQLiteTokenizerNextCallback)(const char *input, int *inputOffset, int *inputLength); From 3b57e6963303adbca4cbc435e8ef1c43dee02726 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Luthi?= Date: Wed, 22 Jan 2020 09:13:54 +0100 Subject: [PATCH 12/52] Add support for storing and retrieving UUID objects Storing them as string for better readability in the database --- Sources/SQLite/Foundation.swift | 16 ++++++++++++++++ Tests/SQLiteTests/FoundationTests.swift | 12 ++++++++++++ 2 files changed, 28 insertions(+) diff --git a/Sources/SQLite/Foundation.swift b/Sources/SQLite/Foundation.swift index cfb79bec..9986f581 100644 --- a/Sources/SQLite/Foundation.swift +++ b/Sources/SQLite/Foundation.swift @@ -68,3 +68,19 @@ public var dateFormatter: DateFormatter = { formatter.timeZone = TimeZone(secondsFromGMT: 0) return formatter }() + +extension UUID : Value { + + public static var declaredDatatype: String { + return String.declaredDatatype + } + + public static func fromDatatypeValue(_ stringValue: String) -> UUID { + return UUID(uuidString: stringValue)! + } + + public var datatypeValue: String { + return self.uuidString + } + +} diff --git a/Tests/SQLiteTests/FoundationTests.swift b/Tests/SQLiteTests/FoundationTests.swift index dd80afc1..ba9685b7 100644 --- a/Tests/SQLiteTests/FoundationTests.swift +++ b/Tests/SQLiteTests/FoundationTests.swift @@ -13,4 +13,16 @@ class FoundationTests : XCTestCase { let data = Data.fromDatatypeValue(blob) XCTAssertEqual(Data([1, 2, 3]), data) } + + func testStringFromUUID() { + let uuid = UUID(uuidString: "4ABE10C9-FF12-4CD4-90C1-4B429001BAD3")! + let string = uuid.datatypeValue + XCTAssertEqual("4ABE10C9-FF12-4CD4-90C1-4B429001BAD3", string) + } + + func testUUIDFromString() { + let string = "4ABE10C9-FF12-4CD4-90C1-4B429001BAD3" + let uuid = UUID.fromDatatypeValue(string) + XCTAssertEqual(UUID(uuidString: "4ABE10C9-FF12-4CD4-90C1-4B429001BAD3"), uuid) + } } From 806f03c91f473d5003372a19083e27ab5e8897a6 Mon Sep 17 00:00:00 2001 From: Nathan Fallet Date: Wed, 15 Apr 2020 11:29:23 +0200 Subject: [PATCH 13/52] Improving README.md Because of #988 and #983, I added a do...catch in the usage section, and a production implementation example in the related section. --- README.md | 119 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 66 insertions(+), 53 deletions(-) diff --git a/README.md b/README.md index b7a18e0b..8b0972f6 100644 --- a/README.md +++ b/README.md @@ -34,67 +34,79 @@ syntax _and_ intent. ```swift import SQLite -let db = try Connection("path/to/db.sqlite3") - -let users = Table("users") -let id = Expression("id") -let name = Expression("name") -let email = Expression("email") - -try db.run(users.create { t in - t.column(id, primaryKey: true) - t.column(name) - t.column(email, unique: true) -}) -// CREATE TABLE "users" ( -// "id" INTEGER PRIMARY KEY NOT NULL, -// "name" TEXT, -// "email" TEXT NOT NULL UNIQUE -// ) - -let insert = users.insert(name <- "Alice", email <- "alice@mac.com") -let rowid = try db.run(insert) -// INSERT INTO "users" ("name", "email") VALUES ('Alice', 'alice@mac.com') - -for user in try db.prepare(users) { - print("id: \(user[id]), name: \(user[name]), email: \(user[email])") - // id: 1, name: Optional("Alice"), email: alice@mac.com +// Wrap everything in a do...catch to handle errors +do { + let db = try Connection("path/to/db.sqlite3") + + let users = Table("users") + let id = Expression("id") + let name = Expression("name") + let email = Expression("email") + + try db.run(users.create { t in + t.column(id, primaryKey: true) + t.column(name) + t.column(email, unique: true) + }) + // CREATE TABLE "users" ( + // "id" INTEGER PRIMARY KEY NOT NULL, + // "name" TEXT, + // "email" TEXT NOT NULL UNIQUE + // ) + + let insert = users.insert(name <- "Alice", email <- "alice@mac.com") + let rowid = try db.run(insert) + // INSERT INTO "users" ("name", "email") VALUES ('Alice', 'alice@mac.com') + + for user in try db.prepare(users) { + print("id: \(user[id]), name: \(user[name]), email: \(user[email])") + // id: 1, name: Optional("Alice"), email: alice@mac.com + } + // SELECT * FROM "users" + + let alice = users.filter(id == rowid) + + try db.run(alice.update(email <- email.replace("mac.com", with: "me.com"))) + // UPDATE "users" SET "email" = replace("email", 'mac.com', 'me.com') + // WHERE ("id" = 1) + + try db.run(alice.delete()) + // DELETE FROM "users" WHERE ("id" = 1) + + try db.scalar(users.count) // 0 + // SELECT count(*) FROM "users" +} catch { + print (error) } -// SELECT * FROM "users" - -let alice = users.filter(id == rowid) - -try db.run(alice.update(email <- email.replace("mac.com", with: "me.com"))) -// UPDATE "users" SET "email" = replace("email", 'mac.com', 'me.com') -// WHERE ("id" = 1) - -try db.run(alice.delete()) -// DELETE FROM "users" WHERE ("id" = 1) - -try db.scalar(users.count) // 0 -// SELECT count(*) FROM "users" ``` SQLite.swift also works as a lightweight, Swift-friendly wrapper over the C API. ```swift -let stmt = try db.prepare("INSERT INTO users (email) VALUES (?)") -for email in ["betty@icloud.com", "cathy@icloud.com"] { - try stmt.run(email) +// Wrap everything in a do...catch to handle errors +do { + // ... + + let stmt = try db.prepare("INSERT INTO users (email) VALUES (?)") + for email in ["betty@icloud.com", "cathy@icloud.com"] { + try stmt.run(email) + } + + db.totalChanges // 3 + db.changes // 1 + db.lastInsertRowid // 3 + + for row in try db.prepare("SELECT id, email FROM users") { + print("id: \(row[0]), email: \(row[1])") + // id: Optional(2), email: Optional("betty@icloud.com") + // id: Optional(3), email: Optional("cathy@icloud.com") + } + + try db.scalar("SELECT count(*) FROM users") // 2 +} catch { + print (error) } - -db.totalChanges // 3 -db.changes // 1 -db.lastInsertRowid // 3 - -for row in try db.prepare("SELECT id, email FROM users") { - print("id: \(row[0]), email: \(row[1])") - // id: Optional(2), email: Optional("betty@icloud.com") - // id: Optional(3), email: Optional("cathy@icloud.com") -} - -try db.scalar("SELECT count(*) FROM users") // 2 ``` [Read the documentation][See Documentation] or explore more, @@ -253,6 +265,7 @@ These projects enhance or use SQLite.swift: - [SQLiteMigrationManager.swift][] (inspired by [FMDBMigrationManager][]) + - [Delta: Math helper](https://apps.apple.com/app/delta-math-helper/id1436506800) (see [Delta/Utils/Database.swift](https://github.com/GroupeMINASTE/Delta-iOS/blob/master/Delta/Utils/Database.swift) for production implementation example) ## Alternatives From f040e145d328042a4a0f042086ac09b28f4b1b41 Mon Sep 17 00:00:00 2001 From: Daniel Shelley Date: Fri, 17 Jul 2020 10:59:47 -0600 Subject: [PATCH 14/52] set deployment target to iOS 9 to fix Archiving with Xcode 12 --- SQLite.xcodeproj/project.pbxproj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj index 30a40214..80719c33 100644 --- a/SQLite.xcodeproj/project.pbxproj +++ b/SQLite.xcodeproj/project.pbxproj @@ -1173,7 +1173,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MACOSX_DEPLOYMENT_TARGET = 10.9; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; @@ -1229,7 +1229,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MACOSX_DEPLOYMENT_TARGET = 10.9; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_NAME = ""; @@ -1255,7 +1255,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = "$(SRCROOT)/Sources/SQLite/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite; PRODUCT_NAME = SQLite; @@ -1276,7 +1276,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = "$(SRCROOT)/Sources/SQLite/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite; PRODUCT_NAME = SQLite; From 135b2fc8b854f8b528956a56d8af8a89f6708d17 Mon Sep 17 00:00:00 2001 From: Jake-B Date: Thu, 13 Aug 2020 07:09:38 -0400 Subject: [PATCH 15/52] =?UTF-8?q?Added=20=3D=3D=3D=20as=20explicit=20?= =?UTF-8?q?=E2=80=9CIS=E2=80=9D=20operator=20for=20expressions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added === as explicit “IS” operator for expressions. Added tests for === operator. --- Sources/SQLite/Typed/Operators.swift | 27 ++++++++++++++++++++++++++ Tests/SQLiteTests/OperatorsTests.swift | 14 +++++++++++++ 2 files changed, 41 insertions(+) diff --git a/Sources/SQLite/Typed/Operators.swift b/Sources/SQLite/Typed/Operators.swift index b5637ea3..0a323b90 100644 --- a/Sources/SQLite/Typed/Operators.swift +++ b/Sources/SQLite/Typed/Operators.swift @@ -378,6 +378,33 @@ public func ==(lhs: V?, rhs: Expression) -> Expression whe return Operator.eq.infix(lhs, rhs) } +public func ===(lhs: Expression, rhs: Expression) -> Expression where V.Datatype : Equatable { + return "IS".infix(lhs, rhs) +} +public func ===(lhs: Expression, rhs: Expression) -> Expression where V.Datatype : Equatable { + return "IS".infix(lhs, rhs) +} +public func ===(lhs: Expression, rhs: Expression) -> Expression where V.Datatype : Equatable { + return "IS".infix(lhs, rhs) +} +public func ===(lhs: Expression, rhs: Expression) -> Expression where V.Datatype : Equatable { + return "IS".infix(lhs, rhs) +} +public func ===(lhs: Expression, rhs: V) -> Expression where V.Datatype : Equatable { + return "IS".infix(lhs, rhs) +} +public func ===(lhs: Expression, rhs: V?) -> Expression where V.Datatype : Equatable { + guard let rhs = rhs else { return "IS".infix(lhs, Expression(value: nil)) } + return "IS".infix(lhs, rhs) +} +public func ===(lhs: V, rhs: Expression) -> Expression where V.Datatype : Equatable { + return "IS".infix(lhs, rhs) +} +public func ===(lhs: V?, rhs: Expression) -> Expression where V.Datatype : Equatable { + guard let lhs = lhs else { return "IS".infix(Expression(value: nil), rhs) } + return "IS".infix(lhs, rhs) +} + public func !=(lhs: Expression, rhs: Expression) -> Expression where V.Datatype : Equatable { return Operator.neq.infix(lhs, rhs) } diff --git a/Tests/SQLiteTests/OperatorsTests.swift b/Tests/SQLiteTests/OperatorsTests.swift index 948eb0a4..18a1f52e 100644 --- a/Tests/SQLiteTests/OperatorsTests.swift +++ b/Tests/SQLiteTests/OperatorsTests.swift @@ -187,6 +187,20 @@ class OperatorsTests : XCTestCase { AssertSQL("(NULL IS \"boolOptional\")", nil == boolOptional) } + func test_isOperator_withEquatableExpressions_buildsBooleanExpression() { + AssertSQL("(\"bool\" IS \"bool\")", bool === bool) + AssertSQL("(\"bool\" IS \"boolOptional\")", bool === boolOptional) + AssertSQL("(\"boolOptional\" IS \"bool\")", boolOptional === bool) + AssertSQL("(\"boolOptional\" IS \"boolOptional\")", boolOptional === boolOptional) + AssertSQL("(\"bool\" IS 1)", bool === true) + AssertSQL("(\"boolOptional\" IS 1)", boolOptional === true) + AssertSQL("(1 IS \"bool\")", true === bool) + AssertSQL("(1 IS \"boolOptional\")", true === boolOptional) + + AssertSQL("(\"boolOptional\" IS NULL)", boolOptional === nil) + AssertSQL("(NULL IS \"boolOptional\")", nil === boolOptional) + } + func test_inequalityOperator_withEquatableExpressions_buildsBooleanExpression() { AssertSQL("(\"bool\" != \"bool\")", bool != bool) AssertSQL("(\"bool\" != \"boolOptional\")", bool != boolOptional) From 113872d8d01b2b0c927d9b450abfb5fe6a8f312a Mon Sep 17 00:00:00 2001 From: Jake-B Date: Thu, 13 Aug 2020 08:17:00 -0400 Subject: [PATCH 16/52] =?UTF-8?q?Add=20!=3D=3D=20as=20explicit=20=E2=80=9C?= =?UTF-8?q?IS=20NOT=E2=80=9D=20operator=20for=20exprsns?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added !== as explicit “IS” operator for expressions. Added tests for !== operator. --- Sources/SQLite/Typed/Operators.swift | 28 ++++++++++++++++++++++++++ Tests/SQLiteTests/OperatorsTests.swift | 14 +++++++++++++ 2 files changed, 42 insertions(+) diff --git a/Sources/SQLite/Typed/Operators.swift b/Sources/SQLite/Typed/Operators.swift index 0a323b90..594f08a0 100644 --- a/Sources/SQLite/Typed/Operators.swift +++ b/Sources/SQLite/Typed/Operators.swift @@ -432,6 +432,34 @@ public func !=(lhs: V?, rhs: Expression) -> Expression whe return Operator.neq.infix(lhs, rhs) } +public func !==(lhs: Expression, rhs: Expression) -> Expression where V.Datatype : Equatable { + return "IS NOT".infix(lhs, rhs) +} +public func !==(lhs: Expression, rhs: Expression) -> Expression where V.Datatype : Equatable { + return "IS NOT".infix(lhs, rhs) +} +public func !==(lhs: Expression, rhs: Expression) -> Expression where V.Datatype : Equatable { + return "IS NOT".infix(lhs, rhs) +} +public func !==(lhs: Expression, rhs: Expression) -> Expression where V.Datatype : Equatable { + return "IS NOT".infix(lhs, rhs) +} +public func !==(lhs: Expression, rhs: V) -> Expression where V.Datatype : Equatable { + return "IS NOT".infix(lhs, rhs) +} +public func !==(lhs: Expression, rhs: V?) -> Expression where V.Datatype : Equatable { + guard let rhs = rhs else { return "IS NOT".infix(lhs, Expression(value: nil)) } + return "IS NOT".infix(lhs, rhs) +} +public func !==(lhs: V, rhs: Expression) -> Expression where V.Datatype : Equatable { + return "IS NOT".infix(lhs, rhs) +} +public func !==(lhs: V?, rhs: Expression) -> Expression where V.Datatype : Equatable { + guard let lhs = lhs else { return "IS NOT".infix(Expression(value: nil), rhs) } + return "IS NOT".infix(lhs, rhs) +} + + public func >(lhs: Expression, rhs: Expression) -> Expression where V.Datatype : Comparable { return Operator.gt.infix(lhs, rhs) } diff --git a/Tests/SQLiteTests/OperatorsTests.swift b/Tests/SQLiteTests/OperatorsTests.swift index 18a1f52e..c2416844 100644 --- a/Tests/SQLiteTests/OperatorsTests.swift +++ b/Tests/SQLiteTests/OperatorsTests.swift @@ -200,6 +200,20 @@ class OperatorsTests : XCTestCase { AssertSQL("(\"boolOptional\" IS NULL)", boolOptional === nil) AssertSQL("(NULL IS \"boolOptional\")", nil === boolOptional) } + + func test_isNotOperator_withEquatableExpressions_buildsBooleanExpression() { + AssertSQL("(\"bool\" IS NOT \"bool\")", bool !== bool) + AssertSQL("(\"bool\" IS NOT \"boolOptional\")", bool !== boolOptional) + AssertSQL("(\"boolOptional\" IS NOT \"bool\")", boolOptional !== bool) + AssertSQL("(\"boolOptional\" IS NOT \"boolOptional\")", boolOptional !== boolOptional) + AssertSQL("(\"bool\" IS NOT 1)", bool !== true) + AssertSQL("(\"boolOptional\" IS NOT 1)", boolOptional !== true) + AssertSQL("(1 IS NOT \"bool\")", true !== bool) + AssertSQL("(1 IS NOT \"boolOptional\")", true !== boolOptional) + + AssertSQL("(\"boolOptional\" IS NOT NULL)", boolOptional !== nil) + AssertSQL("(NULL IS NOT \"boolOptional\")", nil !== boolOptional) + } func test_inequalityOperator_withEquatableExpressions_buildsBooleanExpression() { AssertSQL("(\"bool\" != \"bool\")", bool != bool) From e3e031df66fe6f8a35f50a6d2f3cfcd1a3439656 Mon Sep 17 00:00:00 2001 From: Denis Date: Thu, 5 Nov 2020 16:37:47 -0300 Subject: [PATCH 17/52] Removed the force unwrap in the FailableIterator extension for the next () method Although the method accepts an optional return, the function forces unwrap --- SQLite.xcodeproj/project.pbxproj | 2 ++ Sources/SQLite/Core/Statement.swift | 2 +- Sources/SQLite/Info.plist | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj index 30a40214..9972b3f7 100644 --- a/SQLite.xcodeproj/project.pbxproj +++ b/SQLite.xcodeproj/project.pbxproj @@ -1257,6 +1257,7 @@ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MARKETING_VERSION = 0.12.3; PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite; PRODUCT_NAME = SQLite; SKIP_INSTALL = YES; @@ -1278,6 +1279,7 @@ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MARKETING_VERSION = 0.12.3; PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite; PRODUCT_NAME = SQLite; SKIP_INSTALL = YES; diff --git a/Sources/SQLite/Core/Statement.swift b/Sources/SQLite/Core/Statement.swift index dc91d3d8..237d4812 100644 --- a/Sources/SQLite/Core/Statement.swift +++ b/Sources/SQLite/Core/Statement.swift @@ -208,7 +208,7 @@ public protocol FailableIterator : IteratorProtocol { extension FailableIterator { public func next() -> Element? { - return try! failableNext() + return try? failableNext() } } diff --git a/Sources/SQLite/Info.plist b/Sources/SQLite/Info.plist index 2d956da2..ca23c84f 100644 --- a/Sources/SQLite/Info.plist +++ b/Sources/SQLite/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 0.12.2 + $(MARKETING_VERSION) CFBundleSignature ???? CFBundleVersion From 12ac2fd4928e690957e2d3cad57884d450a8cea7 Mon Sep 17 00:00:00 2001 From: turtlemaster19 <46784000+UInt2048@users.noreply.github.com> Date: Thu, 3 Dec 2020 14:29:36 -0500 Subject: [PATCH 18/52] Fix #920 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b7a18e0b..a12fe539 100644 --- a/README.md +++ b/README.md @@ -112,7 +112,7 @@ and the [companion repository][SQLiteDataAccessLayer2]. ## Installation -> _Note:_ Version 0.12 requires Swift 5 (and [Xcode](https://developer.apple.com/xcode/downloads/) 10.2) or greater. Version 0.11.6 requires Swift 4.2 (and [Xcode](https://developer.apple.com/xcode/downloads/) 10.1) or greater. +> _Note:_ Version 0.11.6 and later requires Swift 5 (and [Xcode](https://developer.apple.com/xcode/downloads/) 10.2) or greater. Version 0.11.5 requires Swift 4.2 (and [Xcode](https://developer.apple.com/xcode/downloads/) 10.1) or greater. ### Carthage From 140374a1f25df9a50c9e014679844bb2de70542d Mon Sep 17 00:00:00 2001 From: Geoff MacDonald Date: Fri, 30 Apr 2021 11:09:45 -0700 Subject: [PATCH 19/52] implement batch insert, insertMany() --- Sources/SQLite/Typed/Coding.swift | 25 +++++++++++++++++++- Sources/SQLite/Typed/Query.swift | 37 ++++++++++++++++++++++++++++++ Tests/SQLiteTests/QueryTests.swift | 37 ++++++++++++++++++++++++++++++ 3 files changed, 98 insertions(+), 1 deletion(-) diff --git a/Sources/SQLite/Typed/Coding.swift b/Sources/SQLite/Typed/Coding.swift index c3fb931b..34edbc49 100644 --- a/Sources/SQLite/Typed/Coding.swift +++ b/Sources/SQLite/Typed/Coding.swift @@ -38,13 +38,36 @@ extension QueryType { /// /// - otherSetters: Any other setters to include in the insert /// - /// - Returns: An `INSERT` statement fort the encodable object + /// - Returns: An `INSERT` statement for the encodable object public func insert(_ encodable: Encodable, userInfo: [CodingUserInfoKey:Any] = [:], otherSetters: [Setter] = []) throws -> Insert { let encoder = SQLiteEncoder(userInfo: userInfo) try encodable.encode(to: encoder) return self.insert(encoder.setters + otherSetters) } + /// Creates a batch `INSERT` statement by encoding the array of given objects + /// This method converts any custom nested types to JSON data and does not handle any sort + /// of object relationships. If you want to support relationships between objects you will + /// have to provide your own Encodable implementations that encode the correct ids. + /// + /// - Parameters: + /// + /// - encodables: Encodable objects to insert + /// + /// - userInfo: User info to be passed to encoder + /// + /// - otherSetters: Any other setters to include in the inserts, per row/object. + /// + /// - Returns: An `INSERT` statement for the encodable objects + public func insertMany(_ encodables: [Encodable], userInfo: [CodingUserInfoKey:Any] = [:], otherSetters: [Setter] = []) throws -> Insert { + let combinedSetters = try encodables.map { encodable -> [Setter] in + let encoder = SQLiteEncoder(userInfo: userInfo) + try encodable.encode(to: encoder) + return encoder.setters + otherSetters + } + return self.insertMany(combinedSetters) + } + /// Creates an `UPDATE` statement by encoding the given object /// This method converts any custom nested types to JSON data and does not handle any sort /// of object relationships. If you want to support relationships between objects you will diff --git a/Sources/SQLite/Typed/Query.swift b/Sources/SQLite/Typed/Query.swift index f6ef6df8..61deaa1f 100644 --- a/Sources/SQLite/Typed/Query.swift +++ b/Sources/SQLite/Typed/Query.swift @@ -631,6 +631,18 @@ extension QueryType { return insert(onConflict, values) } + public func insertMany( _ values: [[Setter]]) -> Insert { + return insertMany(nil, values) + } + + public func insertMany(or onConflict: OnConflict, _ values: [[Setter]]) -> Insert { + return insertMany(onConflict, values) + } + + public func insertMany(or onConflict: OnConflict, _ values: [Setter]...) -> Insert { + return insertMany(onConflict, values) + } + fileprivate func insert(_ or: OnConflict?, _ values: [Setter]) -> Insert { let insert = values.reduce((columns: [Expressible](), values: [Expressible]())) { insert, setter in (insert.columns + [setter.column], insert.values + [setter.value]) @@ -650,6 +662,29 @@ extension QueryType { return Insert(" ".join(clauses.compactMap { $0 }).expression) } + fileprivate func insertMany(_ or: OnConflict?, _ values: [[Setter]]) -> Insert { + guard values.count > 0 else { + return insert() + } + let insertRows = values.map { rowValues in + rowValues.reduce((columns: [Expressible](), values: [Expressible]())) { insert, setter in + (insert.columns + [setter.column], insert.values + [setter.value]) + } + } + + let clauses: [Expressible?] = [ + Expression(literal: "INSERT"), + or.map { Expression(literal: "OR \($0.rawValue)") }, + Expression(literal: "INTO"), + tableName(), + "".wrap(insertRows[0].columns) as Expression, + Expression(literal: "VALUES"), + ", ".join(insertRows.map(\.values).map({ "".wrap($0) as Expression })), + whereClause + ] + return Insert(" ".join(clauses.compactMap { $0 }).expression) + } + /// Runs an `INSERT` statement against the query with `DEFAULT VALUES`. public func insert() -> Insert { return Insert(" ".join([ @@ -1010,6 +1045,8 @@ extension Connection { /// - SeeAlso: `QueryType.insert(value:_:)` /// - SeeAlso: `QueryType.insert(values:)` /// - SeeAlso: `QueryType.insert(or:_:)` + /// - SeeAlso: `QueryType.insertMany(values:)` + /// - SeeAlso: `QueryType.insertMany(or:_:)` /// - SeeAlso: `QueryType.insert()` /// /// - Parameter query: An insert query. diff --git a/Tests/SQLiteTests/QueryTests.swift b/Tests/SQLiteTests/QueryTests.swift index 2a9e4ecb..f48f49b9 100644 --- a/Tests/SQLiteTests/QueryTests.swift +++ b/Tests/SQLiteTests/QueryTests.swift @@ -247,6 +247,26 @@ class QueryTests : XCTestCase { ) } + func test_insert_many_compilesInsertManyExpression() { + AssertSQL( + "INSERT INTO \"users\" (\"email\", \"age\") VALUES ('alice@example.com', 30), ('geoff@example.com', 32), ('alex@example.com', 83)", + users.insertMany([[email <- "alice@example.com", age <- 30], [email <- "geoff@example.com", age <- 32], [email <- "alex@example.com", age <- 83]]) + ) + } + func test_insert_many_compilesInsertManyNoneExpression() { + AssertSQL( + "INSERT INTO \"users\" DEFAULT VALUES", + users.insertMany([]) + ) + } + + func test_insert_many_withOnConflict_compilesInsertManyOrOnConflictExpression() { + AssertSQL( + "INSERT OR REPLACE INTO \"users\" (\"email\", \"age\") VALUES ('alice@example.com', 30), ('geoff@example.com', 32), ('alex@example.com', 83)", + users.insertMany(or: .replace, [[email <- "alice@example.com", age <- 30], [email <- "geoff@example.com", age <- 32], [email <- "alex@example.com", age <- 83]]) + ) + } + func test_insert_encodable() throws { let emails = Table("emails") let value = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4, optional: nil, sub: nil) @@ -270,6 +290,18 @@ class QueryTests : XCTestCase { ) } + func test_insert_many_encodable() throws { + let emails = Table("emails") + let value1 = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4, optional: nil, sub: nil) + let value2 = TestCodable(int: 2, string: "3", bool: true, float: 3, double: 5, optional: nil, sub: nil) + let value3 = TestCodable(int: 3, string: "4", bool: true, float: 3, double: 6, optional: nil, sub: nil) + let insert = try emails.insertMany([value1, value2, value3]) + AssertSQL( + "INSERT INTO \"emails\" (\"int\", \"string\", \"bool\", \"float\", \"double\") VALUES (1, '2', 1, 3.0, 4.0), (2, '3', 1, 3.0, 5.0), (3, '4', 1, 3.0, 6.0)", + insert + ) + } + func test_update_compilesUpdateExpression() { AssertSQL( "UPDATE \"users\" SET \"age\" = 30, \"admin\" = 1 WHERE (\"id\" = 1)", @@ -483,6 +515,11 @@ class QueryIntegrationTests : SQLiteTestCase { XCTAssertEqual(1, id) } + func test_insert_many() { + let id = try! db.run(users.insertMany([[email <- "alice@example.com"], [email <- "geoff@example.com"]])) + XCTAssertEqual(2, id) + } + func test_update() { let changes = try! db.run(users.update(email <- "alice@example.com")) XCTAssertEqual(0, changes) From 087264792a7c75c446106ef4090d9fd669b37e8a Mon Sep 17 00:00:00 2001 From: Geoff MacDonald Date: Fri, 30 Apr 2021 11:27:42 -0700 Subject: [PATCH 20/52] Update Index.md --- Documentation/Index.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Documentation/Index.md b/Documentation/Index.md index 70d67c2d..2f441c8c 100644 --- a/Documentation/Index.md +++ b/Documentation/Index.md @@ -638,6 +638,18 @@ do { } ``` +Multiple rows can be inserted at once by similarily calling `insertMany` with an array of per-row [setters](#setters). + +```swift +do { + let rowid = try db.run(users.insertMany([mail <- "alice@mac.com"], [email <- "geoff@mac.com"])) + print("inserted id: \(rowid)") +} catch { + print("insertion failed: \(error)") +} +``` + + The [`update`](#updating-rows) and [`delete`](#deleting-rows) functions follow similar patterns. From 05c404fcec8df8043d68d38617a9c69f54772f50 Mon Sep 17 00:00:00 2001 From: Geoff MacDonald Date: Fri, 30 Apr 2021 11:37:22 -0700 Subject: [PATCH 21/52] cleanup sql creation --- Sources/SQLite/Typed/Query.swift | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/Sources/SQLite/Typed/Query.swift b/Sources/SQLite/Typed/Query.swift index 61deaa1f..54e44fa5 100644 --- a/Sources/SQLite/Typed/Query.swift +++ b/Sources/SQLite/Typed/Query.swift @@ -663,12 +663,14 @@ extension QueryType { } fileprivate func insertMany(_ or: OnConflict?, _ values: [[Setter]]) -> Insert { - guard values.count > 0 else { + guard let firstInsert = values.first else { + // must be at least 1 object or else we don't know columns. Default to default inserts. return insert() } - let insertRows = values.map { rowValues in - rowValues.reduce((columns: [Expressible](), values: [Expressible]())) { insert, setter in - (insert.columns + [setter.column], insert.values + [setter.value]) + let columns = firstInsert.map { $0.column } + let insertValues = values.map { rowValues in + rowValues.reduce([Expressible]()) { insert, setter in + insert + [setter.value] } } @@ -677,9 +679,9 @@ extension QueryType { or.map { Expression(literal: "OR \($0.rawValue)") }, Expression(literal: "INTO"), tableName(), - "".wrap(insertRows[0].columns) as Expression, + "".wrap(columns) as Expression, Expression(literal: "VALUES"), - ", ".join(insertRows.map(\.values).map({ "".wrap($0) as Expression })), + ", ".join(insertValues.map({ "".wrap($0) as Expression })), whereClause ] return Insert(" ".join(clauses.compactMap { $0 }).expression) From 4fde8dba065b213ae0a5a067a1d8d31680091b5a Mon Sep 17 00:00:00 2001 From: Geoff MacDonald Date: Fri, 30 Apr 2021 14:52:52 -0700 Subject: [PATCH 22/52] try and fix travis? --- .travis.yml | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index c8fc5feb..ea60b063 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,11 +1,11 @@ language: objective-c -rvm: 2.3 +rvm: 2.7.3 # https://docs.travis-ci.com/user/reference/osx -osx_image: xcode10.2 +osx_image: xcode12.2 env: global: - - IOS_SIMULATOR="iPhone XS" - - IOS_VERSION="12.2" + - IOS_SIMULATOR="iPhone 11" + - IOS_VERSION="14.2" matrix: include: - env: BUILD_SCHEME="SQLite iOS" @@ -25,7 +25,4 @@ before_install: - brew update - brew outdated carthage || brew upgrade carthage script: -# Workaround for Xcode 10.2/tvOS 9.1 bug -# See https://stackoverflow.com/questions/55389080/xcode-10-2-failed-to-run-app-on-simulator-with-ios-10 - - sudo mkdir /Library/Developer/CoreSimulator/Profiles/Runtimes/tvOS\ 9.1.simruntime/Contents/Resources/RuntimeRoot/usr/lib/swift - ./run-tests.sh From 183a43948f40637643c15e6e19a4b983d2df8325 Mon Sep 17 00:00:00 2001 From: Geoff MacDonald Date: Fri, 30 Apr 2021 15:32:43 -0700 Subject: [PATCH 23/52] Update Planning.md --- Documentation/Planning.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/Documentation/Planning.md b/Documentation/Planning.md index 5f885de8..62df1f24 100644 --- a/Documentation/Planning.md +++ b/Documentation/Planning.md @@ -33,6 +33,3 @@ be referred to when it comes time to add the corresponding feature._ _Features that are not actively being considered, perhaps because of no clean type-safe way to implement them with the current Swift, or bugs, or just general uncertainty._ - - * provide a mechanism for INSERT INTO multiple values, per - [#168](https://github.com/stephencelis/SQLite.swift/issues/168) From b260b0a4aa1754b609ac48c637578ef1cea0898c Mon Sep 17 00:00:00 2001 From: Geoff MacDonald Date: Fri, 30 Apr 2021 15:35:12 -0700 Subject: [PATCH 24/52] upgrade test deps to force it working --- .travis.yml | 2 +- Tests/CocoaPods/Gemfile.lock | 46 ++++++++++++++++++------------------ 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/.travis.yml b/.travis.yml index ea60b063..2a0460bb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,7 +20,7 @@ matrix: - env: CARTHAGE_PLATFORM="tvOS" - env: PACKAGE_MANAGER_COMMAND="test" before_install: - - gem update bundler + - gem install bundler - gem install xcpretty --no-document - brew update - brew outdated carthage || brew upgrade carthage diff --git a/Tests/CocoaPods/Gemfile.lock b/Tests/CocoaPods/Gemfile.lock index b5172144..e5c53ea3 100644 --- a/Tests/CocoaPods/Gemfile.lock +++ b/Tests/CocoaPods/Gemfile.lock @@ -1,18 +1,18 @@ GEM remote: https://rubygems.org/ specs: - CFPropertyList (3.0.0) - activesupport (4.2.11) + CFPropertyList (3.0.3) + activesupport (4.2.11.3) i18n (~> 0.7) minitest (~> 5.1) thread_safe (~> 0.3, >= 0.3.4) tzinfo (~> 1.1) atomos (0.1.3) - claide (1.0.2) - cocoapods (1.6.0.beta.2) + claide (1.0.3) + cocoapods (1.6.2) activesupport (>= 4.0.2, < 5) claide (>= 1.0.2, < 2.0) - cocoapods-core (= 1.6.0.beta.2) + cocoapods-core (= 1.6.2) cocoapods-deintegrate (>= 1.0.2, < 2.0) cocoapods-downloader (>= 1.2.2, < 2.0) cocoapods-plugins (>= 1.0.0, < 2.0) @@ -22,56 +22,56 @@ GEM cocoapods-try (>= 1.1.0, < 2.0) colored2 (~> 3.1) escape (~> 0.0.4) - fourflusher (~> 2.0.1) + fourflusher (>= 2.2.0, < 3.0) gh_inspector (~> 1.0) molinillo (~> 0.6.6) nap (~> 1.0) - ruby-macho (~> 1.3, >= 1.3.1) - xcodeproj (>= 1.7.0, < 2.0) - cocoapods-core (1.6.0.beta.2) + ruby-macho (~> 1.4) + xcodeproj (>= 1.8.1, < 2.0) + cocoapods-core (1.6.2) activesupport (>= 4.0.2, < 6) fuzzy_match (~> 2.0.4) nap (~> 1.0) - cocoapods-deintegrate (1.0.2) - cocoapods-downloader (1.2.2) + cocoapods-deintegrate (1.0.4) + cocoapods-downloader (1.4.0) cocoapods-plugins (1.0.0) nap cocoapods-search (1.0.0) - cocoapods-stats (1.0.0) - cocoapods-trunk (1.3.1) + cocoapods-stats (1.1.0) + cocoapods-trunk (1.5.0) nap (>= 0.8, < 2.0) netrc (~> 0.11) - cocoapods-try (1.1.0) + cocoapods-try (1.2.0) colored2 (3.1.2) - concurrent-ruby (1.1.4) + concurrent-ruby (1.1.8) escape (0.0.4) - fourflusher (2.0.1) + fourflusher (2.3.1) fuzzy_match (2.0.4) gh_inspector (1.1.3) i18n (0.9.5) concurrent-ruby (~> 1.0) minitest (5.11.3) molinillo (0.6.6) - nanaimo (0.2.6) + nanaimo (0.3.0) nap (1.1.0) netrc (0.11.0) - ruby-macho (1.3.1) + ruby-macho (1.4.0) thread_safe (0.3.6) - tzinfo (1.2.5) + tzinfo (1.2.9) thread_safe (~> 0.1) - xcodeproj (1.7.0) + xcodeproj (1.19.0) CFPropertyList (>= 2.3.3, < 4.0) atomos (~> 0.1.3) claide (>= 1.0.2, < 2.0) colored2 (~> 3.1) - nanaimo (~> 0.2.6) + nanaimo (~> 0.3.0) PLATFORMS ruby DEPENDENCIES - cocoapods (~> 1.6.0beta2) + cocoapods (~> 1.6.1) minitest BUNDLED WITH - 1.17.1 + 1.17.2 From e40e3369c14319df540bbac476c419cfea20294e Mon Sep 17 00:00:00 2001 From: Geoff MacDonald Date: Fri, 30 Apr 2021 17:43:16 -0700 Subject: [PATCH 25/52] update deps --- Tests/CocoaPods/Gemfile | 2 +- Tests/CocoaPods/Gemfile.lock | 51 +++++++++++++++++++---------- Tests/CocoaPods/integration_test.rb | 2 +- 3 files changed, 36 insertions(+), 19 deletions(-) diff --git a/Tests/CocoaPods/Gemfile b/Tests/CocoaPods/Gemfile index 77d90eec..19db2b36 100644 --- a/Tests/CocoaPods/Gemfile +++ b/Tests/CocoaPods/Gemfile @@ -1,4 +1,4 @@ source 'https://rubygems.org' -gem 'cocoapods', '~> 1.6.1' +gem 'cocoapods', '~> 1.10.1' gem 'minitest' diff --git a/Tests/CocoaPods/Gemfile.lock b/Tests/CocoaPods/Gemfile.lock index e5c53ea3..d99ac49a 100644 --- a/Tests/CocoaPods/Gemfile.lock +++ b/Tests/CocoaPods/Gemfile.lock @@ -2,42 +2,51 @@ GEM remote: https://rubygems.org/ specs: CFPropertyList (3.0.3) - activesupport (4.2.11.3) - i18n (~> 0.7) + activesupport (5.2.5) + concurrent-ruby (~> 1.0, >= 1.0.2) + i18n (>= 0.7, < 2) minitest (~> 5.1) - thread_safe (~> 0.3, >= 0.3.4) tzinfo (~> 1.1) + addressable (2.7.0) + public_suffix (>= 2.0.2, < 5.0) + algoliasearch (1.27.5) + httpclient (~> 2.8, >= 2.8.3) + json (>= 1.5.1) atomos (0.1.3) claide (1.0.3) - cocoapods (1.6.2) - activesupport (>= 4.0.2, < 5) + cocoapods (1.10.1) + addressable (~> 2.6) claide (>= 1.0.2, < 2.0) - cocoapods-core (= 1.6.2) - cocoapods-deintegrate (>= 1.0.2, < 2.0) - cocoapods-downloader (>= 1.2.2, < 2.0) + cocoapods-core (= 1.10.1) + cocoapods-deintegrate (>= 1.0.3, < 2.0) + cocoapods-downloader (>= 1.4.0, < 2.0) cocoapods-plugins (>= 1.0.0, < 2.0) cocoapods-search (>= 1.0.0, < 2.0) - cocoapods-stats (>= 1.0.0, < 2.0) - cocoapods-trunk (>= 1.3.1, < 2.0) + cocoapods-trunk (>= 1.4.0, < 2.0) cocoapods-try (>= 1.1.0, < 2.0) colored2 (~> 3.1) escape (~> 0.0.4) - fourflusher (>= 2.2.0, < 3.0) + fourflusher (>= 2.3.0, < 3.0) gh_inspector (~> 1.0) molinillo (~> 0.6.6) nap (~> 1.0) ruby-macho (~> 1.4) - xcodeproj (>= 1.8.1, < 2.0) - cocoapods-core (1.6.2) - activesupport (>= 4.0.2, < 6) + xcodeproj (>= 1.19.0, < 2.0) + cocoapods-core (1.10.1) + activesupport (> 5.0, < 6) + addressable (~> 2.6) + algoliasearch (~> 1.0) + concurrent-ruby (~> 1.1) fuzzy_match (~> 2.0.4) nap (~> 1.0) + netrc (~> 0.11) + public_suffix + typhoeus (~> 1.0) cocoapods-deintegrate (1.0.4) cocoapods-downloader (1.4.0) cocoapods-plugins (1.0.0) nap cocoapods-search (1.0.0) - cocoapods-stats (1.1.0) cocoapods-trunk (1.5.0) nap (>= 0.8, < 2.0) netrc (~> 0.11) @@ -45,18 +54,26 @@ GEM colored2 (3.1.2) concurrent-ruby (1.1.8) escape (0.0.4) + ethon (0.14.0) + ffi (>= 1.15.0) + ffi (1.15.0) fourflusher (2.3.1) fuzzy_match (2.0.4) gh_inspector (1.1.3) - i18n (0.9.5) + httpclient (2.8.3) + i18n (1.8.10) concurrent-ruby (~> 1.0) + json (2.5.1) minitest (5.11.3) molinillo (0.6.6) nanaimo (0.3.0) nap (1.1.0) netrc (0.11.0) + public_suffix (4.0.6) ruby-macho (1.4.0) thread_safe (0.3.6) + typhoeus (1.4.0) + ethon (>= 0.9.0) tzinfo (1.2.9) thread_safe (~> 0.1) xcodeproj (1.19.0) @@ -70,7 +87,7 @@ PLATFORMS ruby DEPENDENCIES - cocoapods (~> 1.6.1) + cocoapods (~> 1.10.1) minitest BUNDLED WITH diff --git a/Tests/CocoaPods/integration_test.rb b/Tests/CocoaPods/integration_test.rb index 98a539b5..2ce2997c 100755 --- a/Tests/CocoaPods/integration_test.rb +++ b/Tests/CocoaPods/integration_test.rb @@ -40,7 +40,7 @@ def test_pod super unless consumer.platform_name == :watchos end - def xcodebuild(action, scheme, configuration) + def xcodebuild(action, scheme, configuration, _) require 'fourflusher' command = %W(#{action} -workspace #{File.join(validation_dir, 'App.xcworkspace')} -scheme #{scheme} -configuration #{configuration}) case consumer.platform_name From 0742ee04976029504502b6212b60462017519887 Mon Sep 17 00:00:00 2001 From: Geoff MacDonald Date: Fri, 30 Apr 2021 17:46:47 -0700 Subject: [PATCH 26/52] apparently carthage doesnt support xcode 12 out of box --- .travis.yml | 8 ++++---- Makefile | 2 +- Tests/Carthage/Makefile | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2a0460bb..b8b2eaba 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,11 +1,11 @@ language: objective-c rvm: 2.7.3 # https://docs.travis-ci.com/user/reference/osx -osx_image: xcode12.2 +osx_image: xcode11.6 env: global: - - IOS_SIMULATOR="iPhone 11" - - IOS_VERSION="14.2" + - IOS_SIMULATOR="iPhone XS" + - IOS_VERSION="13.6" matrix: include: - env: BUILD_SCHEME="SQLite iOS" @@ -20,7 +20,7 @@ matrix: - env: CARTHAGE_PLATFORM="tvOS" - env: PACKAGE_MANAGER_COMMAND="test" before_install: - - gem install bundler + - gem install bundler - gem install xcpretty --no-document - brew update - brew outdated carthage || brew upgrade carthage diff --git a/Makefile b/Makefile index 50d07148..b38b90e0 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ BUILD_TOOL = xcodebuild BUILD_SCHEME = SQLite Mac IOS_SIMULATOR = iPhone XS -IOS_VERSION = 12.2 +IOS_VERSION = 13.6 ifeq ($(BUILD_SCHEME),SQLite iOS) BUILD_ARGUMENTS = -scheme "$(BUILD_SCHEME)" -destination "platform=iOS Simulator,name=$(IOS_SIMULATOR),OS=$(IOS_VERSION)" else diff --git a/Tests/Carthage/Makefile b/Tests/Carthage/Makefile index f28eb25b..7f068985 100644 --- a/Tests/Carthage/Makefile +++ b/Tests/Carthage/Makefile @@ -3,7 +3,7 @@ CARTHAGE_PLATFORM := iOS CARTHAGE_CONFIGURATION := Release CARTHAGE_DIR := Carthage CARTHAGE_ARGS := --no-use-binaries -CARTHAGE_TOOLCHAIN := com.apple.dt.toolchain.Swift_3_0 +CARTHAGE_TOOLCHAIN := com.apple.dt.toolchain.XcodeDefault CARTHAGE_CMDLINE := --configuration $(CARTHAGE_CONFIGURATION) --platform $(CARTHAGE_PLATFORM) --toolchain $(CARTHAGE_TOOLCHAIN) $(CARTHAGE_ARGS) test: $(CARTHAGE) Cartfile From a78d8c4ae303c7d4875a29a01bc307e48095f7d6 Mon Sep 17 00:00:00 2001 From: Geoff MacDonald Date: Fri, 30 Apr 2021 18:04:29 -0700 Subject: [PATCH 27/52] try 2.6 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index b8b2eaba..e9d5ec2a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ language: objective-c -rvm: 2.7.3 +rvm: 2.6 # https://docs.travis-ci.com/user/reference/osx osx_image: xcode11.6 env: From 5052cbbd6d881396c87a9ede9de2e30a8f86a706 Mon Sep 17 00:00:00 2001 From: Geoff MacDonald Date: Fri, 30 Apr 2021 18:13:28 -0700 Subject: [PATCH 28/52] use iphone 11 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index e9d5ec2a..7bff9ce2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ rvm: 2.6 osx_image: xcode11.6 env: global: - - IOS_SIMULATOR="iPhone XS" + - IOS_SIMULATOR="iPhone 11" - IOS_VERSION="13.6" matrix: include: From f961db5344375ea667f3f5d44c8df202a5cfc5aa Mon Sep 17 00:00:00 2001 From: Bennett Smith Date: Mon, 17 May 2021 18:23:32 -0700 Subject: [PATCH 29/52] Updates for Xcode 12.5 * Updates ruby version used by Travis-CI. * Updates Xcode image to 12.5 for Travis-CI. * Removes workaround in run-tests.sh for older simulators. * Updates Carthage toolchain. * Updates Package.swift tool version. * Fixes up SPM build for SQLiteObjc module. * Updated Xcode build settings. --- .travis.yml | 10 ++--- CHANGELOG.md | 8 ++++ Makefile | 2 +- Package.swift | 45 ++++++++++++++++--- SQLite.xcodeproj/project.pbxproj | 30 +++++-------- .../xcschemes/SQLite Mac.xcscheme | 24 +++++----- .../xcschemes/SQLite iOS.xcscheme | 24 +++++----- .../xcschemes/SQLite tvOS.xcscheme | 24 +++++----- .../xcschemes/SQLite watchOS.xcscheme | 6 +-- Sources/SQLiteObjc/SQLiteObjc.h | 36 +++++++++++++++ Sources/SQLiteObjc/include/README.md | 7 +++ Tests/Carthage/Makefile | 5 ++- 12 files changed, 141 insertions(+), 80 deletions(-) create mode 100644 Sources/SQLiteObjc/SQLiteObjc.h create mode 100644 Sources/SQLiteObjc/include/README.md diff --git a/.travis.yml b/.travis.yml index c8fc5feb..98987db1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,11 +1,11 @@ language: objective-c -rvm: 2.3 +rvm: 2.6 # https://docs.travis-ci.com/user/reference/osx -osx_image: xcode10.2 +osx_image: xcode12.5 env: global: - IOS_SIMULATOR="iPhone XS" - - IOS_VERSION="12.2" + - IOS_VERSION="12.4" matrix: include: - env: BUILD_SCHEME="SQLite iOS" @@ -25,7 +25,7 @@ before_install: - brew update - brew outdated carthage || brew upgrade carthage script: -# Workaround for Xcode 10.2/tvOS 9.1 bug +# Workaround for Xcode 10.2/tvOS 9.1 bug (This is an outdated workaround; commenting out.) # See https://stackoverflow.com/questions/55389080/xcode-10-2-failed-to-run-app-on-simulator-with-ios-10 - - sudo mkdir /Library/Developer/CoreSimulator/Profiles/Runtimes/tvOS\ 9.1.simruntime/Contents/Resources/RuntimeRoot/usr/lib/swift +# - sudo mkdir /Library/Developer/CoreSimulator/Profiles/Runtimes/tvOS\ 9.1.simruntime/Contents/Resources/RuntimeRoot/usr/lib/swift - ./run-tests.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index 027611ee..1f29afeb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +0.12.3 (18-05-2021), [diff][diff-0.12.2] +======================================== + +* Swift 5.3 support. +* Xcode 12.5 support. +* Bumps minimum deployment versions. +* Fixes up Package.swift to build SQLiteObjc module. + 0.11.6 (xxx), [diff][diff-0.11.6] ======================================== diff --git a/Makefile b/Makefile index 50d07148..be426dc1 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ BUILD_TOOL = xcodebuild BUILD_SCHEME = SQLite Mac IOS_SIMULATOR = iPhone XS -IOS_VERSION = 12.2 +IOS_VERSION = 12.4 ifeq ($(BUILD_SCHEME),SQLite iOS) BUILD_ARGUMENTS = -scheme "$(BUILD_SCHEME)" -destination "platform=iOS Simulator,name=$(IOS_SIMULATOR),OS=$(IOS_VERSION)" else diff --git a/Package.swift b/Package.swift index 430ae6f2..73339b7b 100644 --- a/Package.swift +++ b/Package.swift @@ -1,15 +1,46 @@ -// swift-tools-version:4.0 +// swift-tools-version:5.3 import PackageDescription let package = Package( name: "SQLite.swift", - products: [.library(name: "SQLite", targets: ["SQLite"])], + products: [ + .library( + name: "SQLite", + targets: ["SQLite"] + ) + ], targets: [ - .target(name: "SQLite", dependencies: ["SQLiteObjc"]), - .target(name: "SQLiteObjc"), - .testTarget(name: "SQLiteTests", dependencies: ["SQLite"], path: "Tests/SQLiteTests") - ], - swiftLanguageVersions: [4, 5] + .target( + name: "SQLite", + dependencies: ["SQLiteObjc"], + exclude: [ + "Info.plist" + ] + ), + .target( + name: "SQLiteObjc", + dependencies: [], + exclude: [ + "SQLiteObjc.h", + "fts3_tokenizer.h", + "include/README.md" + ] + ), + .testTarget( + name: "SQLiteTests", + dependencies: [ + "SQLite" + ], + path: "Tests/SQLiteTests", + exclude: [ + "Info.plist" + ], + resources: [ + .copy("fixtures/encrypted-3.x.sqlite"), + .copy("fixtures/encrypted-4.x.sqlite") + ] + ) + ] ) #if os(Linux) diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj index 30a40214..2e8807ed 100644 --- a/SQLite.xcodeproj/project.pbxproj +++ b/SQLite.xcodeproj/project.pbxproj @@ -277,7 +277,7 @@ EE247B911C3F822500AE3E12 /* installation@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "installation@2x.png"; sourceTree = ""; }; EE247B921C3F822600AE3E12 /* playground@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "playground@2x.png"; sourceTree = ""; }; EE247B931C3F826100AE3E12 /* SQLite.swift.podspec */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; path = SQLite.swift.podspec; sourceTree = ""; }; - EE91808D1C46E5230038162A /* SQLiteObjc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SQLiteObjc.h; path = ../../SQLiteObjc/include/SQLiteObjc.h; sourceTree = ""; }; + EE91808D1C46E5230038162A /* SQLiteObjc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SQLiteObjc.h; path = ../../SQLiteObjc/SQLiteObjc.h; sourceTree = ""; }; EE9180911C46E9D30038162A /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/usr/lib/libsqlite3.tbd; sourceTree = DEVELOPER_DIR; }; EE9180931C46EA210038162A /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = usr/lib/libsqlite3.tbd; sourceTree = SDKROOT; }; /* End PBXFileReference section */ @@ -680,7 +680,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0720; - LastUpgradeCheck = 1020; + LastUpgradeCheck = 1250; TargetAttributes = { 03A65E591C6BB0F50062603F = { CreatedOnToolsVersion = 7.2; @@ -1033,7 +1033,7 @@ PRODUCT_NAME = SQLite; SDKROOT = appletvos; SKIP_INSTALL = YES; - TVOS_DEPLOYMENT_TARGET = 9.1; + TVOS_DEPLOYMENT_TARGET = 12.0; }; name = Debug; }; @@ -1053,7 +1053,7 @@ PRODUCT_NAME = SQLite; SDKROOT = appletvos; SKIP_INSTALL = YES; - TVOS_DEPLOYMENT_TARGET = 9.1; + TVOS_DEPLOYMENT_TARGET = 12.0; }; name = Release; }; @@ -1065,7 +1065,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLiteTests; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = appletvos; - TVOS_DEPLOYMENT_TARGET = 9.1; + TVOS_DEPLOYMENT_TARGET = 12.0; }; name = Debug; }; @@ -1077,7 +1077,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLiteTests; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = appletvos; - TVOS_DEPLOYMENT_TARGET = 9.1; + TVOS_DEPLOYMENT_TARGET = 12.0; }; name = Release; }; @@ -1148,6 +1148,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -1173,8 +1174,8 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - MACOSX_DEPLOYMENT_TARGET = 10.9; + IPHONEOS_DEPLOYMENT_TARGET = 12.3; + MACOSX_DEPLOYMENT_TARGET = 10.15; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; PRODUCT_NAME = ""; @@ -1210,6 +1211,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -1229,8 +1231,8 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - MACOSX_DEPLOYMENT_TARGET = 10.9; + IPHONEOS_DEPLOYMENT_TARGET = 12.3; + MACOSX_DEPLOYMENT_TARGET = 10.15; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_NAME = ""; SDKROOT = iphoneos; @@ -1255,7 +1257,6 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = "$(SRCROOT)/Sources/SQLite/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite; PRODUCT_NAME = SQLite; @@ -1276,7 +1277,6 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = "$(SRCROOT)/Sources/SQLite/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite; PRODUCT_NAME = SQLite; @@ -1288,7 +1288,6 @@ isa = XCBuildConfiguration; buildSettings = { INFOPLIST_FILE = Tests/SQLiteTests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLiteTests; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1299,7 +1298,6 @@ isa = XCBuildConfiguration; buildSettings = { INFOPLIST_FILE = Tests/SQLiteTests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLiteTests; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1320,7 +1318,6 @@ INFOPLIST_FILE = "$(SRCROOT)/Sources/SQLite/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.10; PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite; PRODUCT_NAME = SQLite; SDKROOT = macosx; @@ -1343,7 +1340,6 @@ INFOPLIST_FILE = "$(SRCROOT)/Sources/SQLite/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.10; PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite; PRODUCT_NAME = SQLite; SDKROOT = macosx; @@ -1359,7 +1355,6 @@ COMBINE_HIDPI_IMAGES = YES; INFOPLIST_FILE = Tests/SQLiteTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.10; PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLiteTests; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; @@ -1373,7 +1368,6 @@ COMBINE_HIDPI_IMAGES = YES; INFOPLIST_FILE = Tests/SQLiteTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.10; PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLiteTests; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; diff --git a/SQLite.xcodeproj/xcshareddata/xcschemes/SQLite Mac.xcscheme b/SQLite.xcodeproj/xcshareddata/xcschemes/SQLite Mac.xcscheme index 4e94e80a..a0db21a2 100644 --- a/SQLite.xcodeproj/xcshareddata/xcschemes/SQLite Mac.xcscheme +++ b/SQLite.xcodeproj/xcshareddata/xcschemes/SQLite Mac.xcscheme @@ -1,6 +1,6 @@ + + + + @@ -39,17 +48,6 @@ - - - - - - - - + + + + @@ -39,17 +48,6 @@ - - - - - - - - + + + + @@ -39,17 +48,6 @@ - - - - - - - - - - - - Date: Thu, 24 Jun 2021 01:24:18 +0200 Subject: [PATCH 30/52] - Set iOS deployment target to 9.0 - Bump watchOS deployment version to 3.0 #971 - Fix SQLCipher compilation #1024 --- .travis.yml | 3 - SQLite.swift.podspec | 14 ++--- SQLite.xcodeproj/project.pbxproj | 8 +-- Sources/SQLiteObjc/SQLiteObjc.h | 2 + Sources/SQLiteObjc/include/README.md | 7 --- Sources/SQLiteObjc/include/SQLiteObjc.h | 36 ----------- Tests/CocoaPods/Gemfile | 2 +- Tests/CocoaPods/Gemfile.lock | 83 +++++++++++++++---------- Tests/CocoaPods/integration_test.rb | 37 +---------- Tests/SQLiteTests/ConnectionTests.swift | 6 +- 10 files changed, 69 insertions(+), 129 deletions(-) delete mode 100644 Sources/SQLiteObjc/include/README.md delete mode 100644 Sources/SQLiteObjc/include/SQLiteObjc.h diff --git a/.travis.yml b/.travis.yml index 98987db1..90ff205c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,7 +25,4 @@ before_install: - brew update - brew outdated carthage || brew upgrade carthage script: -# Workaround for Xcode 10.2/tvOS 9.1 bug (This is an outdated workaround; commenting out.) -# See https://stackoverflow.com/questions/55389080/xcode-10-2-failed-to-run-app-on-simulator-with-ios-10 -# - sudo mkdir /Library/Developer/CoreSimulator/Profiles/Runtimes/tvOS\ 9.1.simruntime/Contents/Resources/RuntimeRoot/usr/lib/swift - ./run-tests.sh diff --git a/SQLite.swift.podspec b/SQLite.swift.podspec index 712ddc36..bcefa5fe 100644 --- a/SQLite.swift.podspec +++ b/SQLite.swift.podspec @@ -19,10 +19,10 @@ Pod::Spec.new do |s| s.swift_versions = ['4.2', '5'] - ios_deployment_target = '8.0' + ios_deployment_target = '9.0' tvos_deployment_target = '9.1' osx_deployment_target = '10.10' - watchos_deployment_target = '2.2' + watchos_deployment_target = '3.0' s.ios.deployment_target = ios_deployment_target s.tvos.deployment_target = tvos_deployment_target @@ -32,7 +32,7 @@ Pod::Spec.new do |s| s.subspec 'standard' do |ss| ss.source_files = 'Sources/{SQLite,SQLiteObjc}/**/*.{c,h,m,swift}' ss.exclude_files = 'Sources/**/Cipher.swift' - ss.private_header_files = 'Sources/SQLiteObjc/*.h' + ss.private_header_files = 'Sources/SQLiteObjc/fts3_tokenizer.h' ss.library = 'sqlite3' ss.test_spec 'tests' do |test_spec| @@ -47,7 +47,7 @@ Pod::Spec.new do |s| s.subspec 'standalone' do |ss| ss.source_files = 'Sources/{SQLite,SQLiteObjc}/**/*.{c,h,m,swift}' ss.exclude_files = 'Sources/**/Cipher.swift' - ss.private_header_files = 'Sources/SQLiteObjc/*.h' + ss.private_header_files = 'Sources/SQLiteObjc/fts3_tokenizer.h' ss.xcconfig = { 'OTHER_SWIFT_FLAGS' => '$(inherited) -DSQLITE_SWIFT_STANDALONE', @@ -66,12 +66,12 @@ Pod::Spec.new do |s| s.subspec 'SQLCipher' do |ss| ss.source_files = 'Sources/{SQLite,SQLiteObjc}/**/*.{c,h,m,swift}' - ss.private_header_files = 'Sources/SQLiteObjc/*.h' + ss.private_header_files = 'Sources/SQLiteObjc/fts3_tokenizer.h' ss.xcconfig = { 'OTHER_SWIFT_FLAGS' => '$(inherited) -DSQLITE_SWIFT_SQLCIPHER', - 'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) SQLITE_HAS_CODEC=1' + 'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) SQLITE_HAS_CODEC=1 SQLITE_SWIFT_SQLCIPHER=1' } - ss.dependency 'SQLCipher', '>= 3.4.0' + ss.dependency 'SQLCipher', '>= 4.0.0' ss.test_spec 'tests' do |test_spec| test_spec.resources = 'Tests/SQLiteTests/fixtures/*' diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj index 2e8807ed..92795299 100644 --- a/SQLite.xcodeproj/project.pbxproj +++ b/SQLite.xcodeproj/project.pbxproj @@ -1099,7 +1099,7 @@ SDKROOT = watchos; SKIP_INSTALL = YES; TARGETED_DEVICE_FAMILY = 4; - WATCHOS_DEPLOYMENT_TARGET = 2.2; + WATCHOS_DEPLOYMENT_TARGET = 3.0; }; name = Debug; }; @@ -1121,7 +1121,7 @@ SDKROOT = watchos; SKIP_INSTALL = YES; TARGETED_DEVICE_FAMILY = 4; - WATCHOS_DEPLOYMENT_TARGET = 2.2; + WATCHOS_DEPLOYMENT_TARGET = 3.0; }; name = Release; }; @@ -1174,7 +1174,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.3; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MACOSX_DEPLOYMENT_TARGET = 10.15; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; @@ -1231,7 +1231,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.3; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MACOSX_DEPLOYMENT_TARGET = 10.15; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_NAME = ""; diff --git a/Sources/SQLiteObjc/SQLiteObjc.h b/Sources/SQLiteObjc/SQLiteObjc.h index e8ba9a7d..610cdf10 100644 --- a/Sources/SQLiteObjc/SQLiteObjc.h +++ b/Sources/SQLiteObjc/SQLiteObjc.h @@ -25,6 +25,8 @@ @import Foundation; #if defined(SQLITE_SWIFT_STANDALONE) @import sqlite3; +#elif defined(SQLITE_SWIFT_SQLCIPHER) +@import SQLCipher; #else @import SQLite3; #endif diff --git a/Sources/SQLiteObjc/include/README.md b/Sources/SQLiteObjc/include/README.md deleted file mode 100644 index 54b375e5..00000000 --- a/Sources/SQLiteObjc/include/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# README - -This folder contains the public interface for the SQLiteObjc module. It is a duplicate -copy of the header file found in the Sources/SQLiteObjc folder. If you change one, change -the other too! - - diff --git a/Sources/SQLiteObjc/include/SQLiteObjc.h b/Sources/SQLiteObjc/include/SQLiteObjc.h deleted file mode 100644 index e8ba9a7d..00000000 --- a/Sources/SQLiteObjc/include/SQLiteObjc.h +++ /dev/null @@ -1,36 +0,0 @@ -// -// SQLite.swift -// https://github.com/stephencelis/SQLite.swift -// Copyright © 2014-2015 Stephen Celis. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -@import Foundation; -#if defined(SQLITE_SWIFT_STANDALONE) -@import sqlite3; -#else -@import SQLite3; -#endif - -NS_ASSUME_NONNULL_BEGIN -typedef NSString * _Nullable (^_SQLiteTokenizerNextCallback)(const char *input, int *inputOffset, int *inputLength); -int _SQLiteRegisterTokenizer(sqlite3 *db, const char *module, const char *tokenizer, _Nullable _SQLiteTokenizerNextCallback callback); -NS_ASSUME_NONNULL_END - diff --git a/Tests/CocoaPods/Gemfile b/Tests/CocoaPods/Gemfile index 77d90eec..19db2b36 100644 --- a/Tests/CocoaPods/Gemfile +++ b/Tests/CocoaPods/Gemfile @@ -1,4 +1,4 @@ source 'https://rubygems.org' -gem 'cocoapods', '~> 1.6.1' +gem 'cocoapods', '~> 1.10.1' gem 'minitest' diff --git a/Tests/CocoaPods/Gemfile.lock b/Tests/CocoaPods/Gemfile.lock index b5172144..0cf77eda 100644 --- a/Tests/CocoaPods/Gemfile.lock +++ b/Tests/CocoaPods/Gemfile.lock @@ -1,77 +1,94 @@ GEM remote: https://rubygems.org/ specs: - CFPropertyList (3.0.0) - activesupport (4.2.11) - i18n (~> 0.7) + CFPropertyList (3.0.3) + activesupport (5.2.6) + concurrent-ruby (~> 1.0, >= 1.0.2) + i18n (>= 0.7, < 2) minitest (~> 5.1) - thread_safe (~> 0.3, >= 0.3.4) tzinfo (~> 1.1) + addressable (2.7.0) + public_suffix (>= 2.0.2, < 5.0) + algoliasearch (1.27.5) + httpclient (~> 2.8, >= 2.8.3) + json (>= 1.5.1) atomos (0.1.3) - claide (1.0.2) - cocoapods (1.6.0.beta.2) - activesupport (>= 4.0.2, < 5) + claide (1.0.3) + cocoapods (1.10.1) + addressable (~> 2.6) claide (>= 1.0.2, < 2.0) - cocoapods-core (= 1.6.0.beta.2) - cocoapods-deintegrate (>= 1.0.2, < 2.0) - cocoapods-downloader (>= 1.2.2, < 2.0) + cocoapods-core (= 1.10.1) + cocoapods-deintegrate (>= 1.0.3, < 2.0) + cocoapods-downloader (>= 1.4.0, < 2.0) cocoapods-plugins (>= 1.0.0, < 2.0) cocoapods-search (>= 1.0.0, < 2.0) - cocoapods-stats (>= 1.0.0, < 2.0) - cocoapods-trunk (>= 1.3.1, < 2.0) + cocoapods-trunk (>= 1.4.0, < 2.0) cocoapods-try (>= 1.1.0, < 2.0) colored2 (~> 3.1) escape (~> 0.0.4) - fourflusher (~> 2.0.1) + fourflusher (>= 2.3.0, < 3.0) gh_inspector (~> 1.0) molinillo (~> 0.6.6) nap (~> 1.0) - ruby-macho (~> 1.3, >= 1.3.1) - xcodeproj (>= 1.7.0, < 2.0) - cocoapods-core (1.6.0.beta.2) - activesupport (>= 4.0.2, < 6) + ruby-macho (~> 1.4) + xcodeproj (>= 1.19.0, < 2.0) + cocoapods-core (1.10.1) + activesupport (> 5.0, < 6) + addressable (~> 2.6) + algoliasearch (~> 1.0) + concurrent-ruby (~> 1.1) fuzzy_match (~> 2.0.4) nap (~> 1.0) - cocoapods-deintegrate (1.0.2) - cocoapods-downloader (1.2.2) + netrc (~> 0.11) + public_suffix + typhoeus (~> 1.0) + cocoapods-deintegrate (1.0.4) + cocoapods-downloader (1.4.0) cocoapods-plugins (1.0.0) nap cocoapods-search (1.0.0) - cocoapods-stats (1.0.0) - cocoapods-trunk (1.3.1) + cocoapods-trunk (1.5.0) nap (>= 0.8, < 2.0) netrc (~> 0.11) - cocoapods-try (1.1.0) + cocoapods-try (1.2.0) colored2 (3.1.2) - concurrent-ruby (1.1.4) + concurrent-ruby (1.1.9) escape (0.0.4) - fourflusher (2.0.1) + ethon (0.14.0) + ffi (>= 1.15.0) + ffi (1.15.3) + fourflusher (2.3.1) fuzzy_match (2.0.4) gh_inspector (1.1.3) - i18n (0.9.5) + httpclient (2.8.3) + i18n (1.8.10) concurrent-ruby (~> 1.0) - minitest (5.11.3) + json (2.5.1) + minitest (5.14.4) molinillo (0.6.6) - nanaimo (0.2.6) + nanaimo (0.3.0) nap (1.1.0) netrc (0.11.0) - ruby-macho (1.3.1) + public_suffix (4.0.6) + ruby-macho (1.4.0) thread_safe (0.3.6) - tzinfo (1.2.5) + typhoeus (1.4.0) + ethon (>= 0.9.0) + tzinfo (1.2.9) thread_safe (~> 0.1) - xcodeproj (1.7.0) + xcodeproj (1.19.0) CFPropertyList (>= 2.3.3, < 4.0) atomos (~> 0.1.3) claide (>= 1.0.2, < 2.0) colored2 (~> 3.1) - nanaimo (~> 0.2.6) + nanaimo (~> 0.3.0) PLATFORMS ruby DEPENDENCIES - cocoapods (~> 1.6.0beta2) + cocoapods (~> 1.10.1) minitest BUNDLED WITH - 1.17.1 + 1.17.3 diff --git a/Tests/CocoaPods/integration_test.rb b/Tests/CocoaPods/integration_test.rb index 98a539b5..67b13385 100755 --- a/Tests/CocoaPods/integration_test.rb +++ b/Tests/CocoaPods/integration_test.rb @@ -13,7 +13,7 @@ def test_validate_project private def validator - @validator ||= CustomValidator.new(podspec, ['https://github.com/CocoaPods/Specs.git']).tap do |validator| + @validator ||= Pod::Validator.new(podspec, ['https://github.com/CocoaPods/Specs.git']).tap do |validator| validator.config.verbose = true validator.no_clean = true validator.use_frameworks = true @@ -32,39 +32,4 @@ def validator def podspec File.expand_path(File.dirname(__FILE__) + '/../../SQLite.swift.podspec') end - - - class CustomValidator < Pod::Validator - def test_pod - # https://github.com/CocoaPods/CocoaPods/issues/7009 - super unless consumer.platform_name == :watchos - end - - def xcodebuild(action, scheme, configuration) - require 'fourflusher' - command = %W(#{action} -workspace #{File.join(validation_dir, 'App.xcworkspace')} -scheme #{scheme} -configuration #{configuration}) - case consumer.platform_name - when :osx, :macos - command += %w(CODE_SIGN_IDENTITY=) - when :ios - command += %w(CODE_SIGN_IDENTITY=- -sdk iphonesimulator) - command += Fourflusher::SimControl.new.destination(nil, 'iOS', deployment_target) - when :watchos - command += %w(CODE_SIGN_IDENTITY=- -sdk watchsimulator) - command += Fourflusher::SimControl.new.destination(:oldest, 'watchOS', deployment_target) - when :tvos - command += %w(CODE_SIGN_IDENTITY=- -sdk appletvsimulator) - command += Fourflusher::SimControl.new.destination(:oldest, 'tvOS', deployment_target) - end - - begin - _xcodebuild(command, true) - rescue => e - message = 'Returned an unsuccessful exit code.' - message += ' You can use `--verbose` for more information.' unless config.verbose? - error('xcodebuild', message) - e.message - end - end - end end diff --git a/Tests/SQLiteTests/ConnectionTests.swift b/Tests/SQLiteTests/ConnectionTests.swift index eab3cf00..fdfc9637 100644 --- a/Tests/SQLiteTests/ConnectionTests.swift +++ b/Tests/SQLiteTests/ConnectionTests.swift @@ -38,12 +38,14 @@ class ConnectionTests : SQLiteTestCase { func test_init_withURI_returnsURIConnection() { let db = try! Connection(.uri("\(NSTemporaryDirectory())/SQLite.swift Tests.sqlite3")) - XCTAssertEqual("\(NSTemporaryDirectory())/SQLite.swift Tests.sqlite3", db.description) + let url = URL(fileURLWithPath: db.description) + XCTAssertEqual(url.lastPathComponent, "SQLite.swift Tests.sqlite3") } func test_init_withString_returnsURIConnection() { let db = try! Connection("\(NSTemporaryDirectory())/SQLite.swift Tests.sqlite3") - XCTAssertEqual("\(NSTemporaryDirectory())/SQLite.swift Tests.sqlite3", db.description) + let url = URL(fileURLWithPath: db.description) + XCTAssertEqual(url.lastPathComponent, "SQLite.swift Tests.sqlite3") } func test_readonly_returnsFalseOnReadWriteConnections() { From 3976805368faf2d5ba9fbf00a397ec8b3a4d81c8 Mon Sep 17 00:00:00 2001 From: Nathan Fallet Date: Wed, 18 Aug 2021 17:39:36 +0200 Subject: [PATCH 31/52] Moving to GitHub Actions --- .../template.md} | 0 .github/workflows/build.yml | 60 +++++++++++++++++++ .travis.yml | 31 ---------- 3 files changed, 60 insertions(+), 31 deletions(-) rename .github/{ISSUE_TEMPLATE.md => ISSUE_TEMPLATE/template.md} (100%) create mode 100644 .github/workflows/build.yml delete mode 100644 .travis.yml diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE/template.md similarity index 100% rename from .github/ISSUE_TEMPLATE.md rename to .github/ISSUE_TEMPLATE/template.md diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 00000000..462233d4 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,60 @@ +name: Build and test +on: [push, pull_request] +env: + IOS_SIMULATOR: iPhone XS + IOS_VERSION: 12.4 +jobs: + build: + runs-on: macos-latest + steps: + - uses: actions/checkout@v2 + - name: Install + run: | + gem update bundler + gem install xcpretty --no-document + brew update + brew outdated carthage || brew upgrade carthage + - name: "Run tests (BUILD_SCHEME: SQLite iOS)" + env: + BUILD_SCHEME: SQLite iOS + run: ./run-tests.sh + - name: "Run tests (BUILD_SCHEME: SQLite Mac)" + env: + BUILD_SCHEME: SQLite Mac + run: ./run-tests.sh + - name: "Run tests (VALIDATOR_SUBSPEC: none)" + env: + VALIDATOR_SUBSPEC: none + run: ./run-tests.sh + - name: "Run tests (VALIDATOR_SUBSPEC: standard)" + env: + VALIDATOR_SUBSPEC: standard + run: ./run-tests.sh + - name: "Run tests (VALIDATOR_SUBSPEC: standalone)" + env: + VALIDATOR_SUBSPEC: standalone + run: ./run-tests.sh + - name: "Run tests (VALIDATOR_SUBSPEC: SQLCipher)" + env: + VALIDATOR_SUBSPEC: SQLCipher + run: ./run-tests.sh + - name: "Run tests (CARTHAGE_PLATFORM: iOS)" + env: + CARTHAGE_PLATFORM: iOS + run: ./run-tests.sh + - name: "Run tests (CARTHAGE_PLATFORM: Mac)" + env: + CARTHAGE_PLATFORM: Mac + run: ./run-tests.sh + - name: "Run tests (CARTHAGE_PLATFORM: watchOS)" + env: + CARTHAGE_PLATFORM: watchOS + run: ./run-tests.sh + - name: "Run tests (CARTHAGE_PLATFORM: tvOS)" + env: + CARTHAGE_PLATFORM: tvOS + run: ./run-tests.sh + - name: "Run tests (PACKAGE_MANAGER_COMMAND: test)" + env: + PACKAGE_MANAGER_COMMAND: test + run: ./run-tests.sh diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 98987db1..00000000 --- a/.travis.yml +++ /dev/null @@ -1,31 +0,0 @@ -language: objective-c -rvm: 2.6 -# https://docs.travis-ci.com/user/reference/osx -osx_image: xcode12.5 -env: - global: - - IOS_SIMULATOR="iPhone XS" - - IOS_VERSION="12.4" -matrix: - include: - - env: BUILD_SCHEME="SQLite iOS" - - env: BUILD_SCHEME="SQLite Mac" - - env: VALIDATOR_SUBSPEC="none" - - env: VALIDATOR_SUBSPEC="standard" - - env: VALIDATOR_SUBSPEC="standalone" - - env: VALIDATOR_SUBSPEC="SQLCipher" - - env: CARTHAGE_PLATFORM="iOS" - - env: CARTHAGE_PLATFORM="Mac" - - env: CARTHAGE_PLATFORM="watchOS" - - env: CARTHAGE_PLATFORM="tvOS" - - env: PACKAGE_MANAGER_COMMAND="test" -before_install: - - gem update bundler - - gem install xcpretty --no-document - - brew update - - brew outdated carthage || brew upgrade carthage -script: -# Workaround for Xcode 10.2/tvOS 9.1 bug (This is an outdated workaround; commenting out.) -# See https://stackoverflow.com/questions/55389080/xcode-10-2-failed-to-run-app-on-simulator-with-ios-10 -# - sudo mkdir /Library/Developer/CoreSimulator/Profiles/Runtimes/tvOS\ 9.1.simruntime/Contents/Resources/RuntimeRoot/usr/lib/swift - - ./run-tests.sh From 4b91f07fe16d6d4ca42f81d845aa37181913b2d8 Mon Sep 17 00:00:00 2001 From: Nathan Fallet Date: Wed, 18 Aug 2021 17:46:22 +0200 Subject: [PATCH 32/52] Updating iOS test device --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 462233d4..1c47e3a6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,8 +1,8 @@ name: Build and test on: [push, pull_request] env: - IOS_SIMULATOR: iPhone XS - IOS_VERSION: 12.4 + IOS_SIMULATOR: iPhone 12 + IOS_VERSION: 14.4 jobs: build: runs-on: macos-latest From de2059b37a4da218367685368fa01093a7817d7a Mon Sep 17 00:00:00 2001 From: Nathan Fallet Date: Wed, 18 Aug 2021 18:03:26 +0200 Subject: [PATCH 33/52] Update Cocoapods version --- Tests/CocoaPods/Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/CocoaPods/Gemfile b/Tests/CocoaPods/Gemfile index 77d90eec..da52ec89 100644 --- a/Tests/CocoaPods/Gemfile +++ b/Tests/CocoaPods/Gemfile @@ -1,4 +1,4 @@ source 'https://rubygems.org' -gem 'cocoapods', '~> 1.6.1' +gem 'cocoapods', '~> 1.10.2' gem 'minitest' From 1e4a1f8092980b93cef68f64edba92dc2b65c573 Mon Sep 17 00:00:00 2001 From: Nathan Fallet Date: Wed, 18 Aug 2021 18:16:27 +0200 Subject: [PATCH 34/52] Updating Cocoapods gemfile lock --- Tests/CocoaPods/Gemfile.lock | 85 ++++++++++++++++++++++-------------- 1 file changed, 52 insertions(+), 33 deletions(-) diff --git a/Tests/CocoaPods/Gemfile.lock b/Tests/CocoaPods/Gemfile.lock index b5172144..e0383a67 100644 --- a/Tests/CocoaPods/Gemfile.lock +++ b/Tests/CocoaPods/Gemfile.lock @@ -1,77 +1,96 @@ GEM remote: https://rubygems.org/ specs: - CFPropertyList (3.0.0) - activesupport (4.2.11) - i18n (~> 0.7) + CFPropertyList (3.0.3) + activesupport (5.2.6) + concurrent-ruby (~> 1.0, >= 1.0.2) + i18n (>= 0.7, < 2) minitest (~> 5.1) - thread_safe (~> 0.3, >= 0.3.4) tzinfo (~> 1.1) + addressable (2.8.0) + public_suffix (>= 2.0.2, < 5.0) + algoliasearch (1.27.5) + httpclient (~> 2.8, >= 2.8.3) + json (>= 1.5.1) atomos (0.1.3) - claide (1.0.2) - cocoapods (1.6.0.beta.2) - activesupport (>= 4.0.2, < 5) + claide (1.0.3) + cocoapods (1.10.2) + addressable (~> 2.6) claide (>= 1.0.2, < 2.0) - cocoapods-core (= 1.6.0.beta.2) - cocoapods-deintegrate (>= 1.0.2, < 2.0) - cocoapods-downloader (>= 1.2.2, < 2.0) + cocoapods-core (= 1.10.2) + cocoapods-deintegrate (>= 1.0.3, < 2.0) + cocoapods-downloader (>= 1.4.0, < 2.0) cocoapods-plugins (>= 1.0.0, < 2.0) cocoapods-search (>= 1.0.0, < 2.0) - cocoapods-stats (>= 1.0.0, < 2.0) - cocoapods-trunk (>= 1.3.1, < 2.0) + cocoapods-trunk (>= 1.4.0, < 2.0) cocoapods-try (>= 1.1.0, < 2.0) colored2 (~> 3.1) escape (~> 0.0.4) - fourflusher (~> 2.0.1) + fourflusher (>= 2.3.0, < 3.0) gh_inspector (~> 1.0) molinillo (~> 0.6.6) nap (~> 1.0) - ruby-macho (~> 1.3, >= 1.3.1) - xcodeproj (>= 1.7.0, < 2.0) - cocoapods-core (1.6.0.beta.2) - activesupport (>= 4.0.2, < 6) + ruby-macho (~> 1.4) + xcodeproj (>= 1.19.0, < 2.0) + cocoapods-core (1.10.2) + activesupport (> 5.0, < 6) + addressable (~> 2.6) + algoliasearch (~> 1.0) + concurrent-ruby (~> 1.1) fuzzy_match (~> 2.0.4) nap (~> 1.0) - cocoapods-deintegrate (1.0.2) - cocoapods-downloader (1.2.2) + netrc (~> 0.11) + public_suffix + typhoeus (~> 1.0) + cocoapods-deintegrate (1.0.5) + cocoapods-downloader (1.4.0) cocoapods-plugins (1.0.0) nap - cocoapods-search (1.0.0) - cocoapods-stats (1.0.0) - cocoapods-trunk (1.3.1) + cocoapods-search (1.0.1) + cocoapods-trunk (1.5.0) nap (>= 0.8, < 2.0) netrc (~> 0.11) - cocoapods-try (1.1.0) + cocoapods-try (1.2.0) colored2 (3.1.2) - concurrent-ruby (1.1.4) + concurrent-ruby (1.1.9) escape (0.0.4) - fourflusher (2.0.1) + ethon (0.14.0) + ffi (>= 1.15.0) + ffi (1.15.3) + fourflusher (2.3.1) fuzzy_match (2.0.4) gh_inspector (1.1.3) - i18n (0.9.5) + httpclient (2.8.3) + i18n (1.8.10) concurrent-ruby (~> 1.0) + json (2.5.1) minitest (5.11.3) molinillo (0.6.6) - nanaimo (0.2.6) + nanaimo (0.3.0) nap (1.1.0) netrc (0.11.0) - ruby-macho (1.3.1) + public_suffix (4.0.6) + rexml (3.2.5) + ruby-macho (1.4.0) thread_safe (0.3.6) - tzinfo (1.2.5) + typhoeus (1.4.0) + ethon (>= 0.9.0) + tzinfo (1.2.9) thread_safe (~> 0.1) - xcodeproj (1.7.0) + xcodeproj (1.21.0) CFPropertyList (>= 2.3.3, < 4.0) atomos (~> 0.1.3) claide (>= 1.0.2, < 2.0) colored2 (~> 3.1) - nanaimo (~> 0.2.6) + nanaimo (~> 0.3.0) + rexml (~> 3.2.4) PLATFORMS ruby DEPENDENCIES - cocoapods (~> 1.6.0beta2) + cocoapods (~> 1.10.2) minitest BUNDLED WITH - 1.17.1 + 2.2.22 From 9016160ae16bc0376f00dd0d50132319b996c750 Mon Sep 17 00:00:00 2001 From: Nathan Fallet Date: Wed, 18 Aug 2021 18:41:08 +0200 Subject: [PATCH 35/52] Fixed Carthage Makefil for GitHub Actions --- Tests/Carthage/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/Carthage/Makefile b/Tests/Carthage/Makefile index f5185854..b44d85b9 100644 --- a/Tests/Carthage/Makefile +++ b/Tests/Carthage/Makefile @@ -11,7 +11,7 @@ test: $(CARTHAGE) Cartfile $< bootstrap $(CARTHAGE_CMDLINE) Cartfile: - echo 'git "$(TRAVIS_BUILD_DIR)" "HEAD"' > $@ + echo 'git "$(GITHUB_WORKSPACE)" "HEAD"' > $@ clean: @rm -f Cartfile Cartfile.resolved From 6ccf8a8e7f03d1909bb0452b344151091d664bf5 Mon Sep 17 00:00:00 2001 From: Nathan Fallet Date: Wed, 18 Aug 2021 20:01:29 +0200 Subject: [PATCH 36/52] Totally removing Travis from repository --- README.md | 5 ++--- SQLite.xcodeproj/project.pbxproj | 2 -- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 8b0972f6..c3c89ce5 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # SQLite.swift -[![Build Status][TravisBadge]][TravisLink] [![CocoaPods Version][CocoaPodsVersionBadge]][CocoaPodsVersionLink] [![Swift5 compatible][Swift5Badge]][Swift5Link] [![Platform][PlatformBadge]][PlatformLink] [![Carthage compatible][CartagheBadge]][CarthageLink] [![Join the chat at https://gitter.im/stephencelis/SQLite.swift][GitterBadge]][GitterLink] +![Build Status][GitHubActionBadge] [![CocoaPods Version][CocoaPodsVersionBadge]][CocoaPodsVersionLink] [![Swift5 compatible][Swift5Badge]][Swift5Link] [![Platform][PlatformBadge]][PlatformLink] [![Carthage compatible][CartagheBadge]][CarthageLink] [![Join the chat at https://gitter.im/stephencelis/SQLite.swift][GitterBadge]][GitterLink] A type-safe, [Swift][]-language layer over [SQLite3][]. @@ -283,8 +283,7 @@ Looking for something else? Try another Swift wrapper (or [FMDB][]): [SQLite3]: http://www.sqlite.org [SQLite.swift]: https://github.com/stephencelis/SQLite.swift -[TravisBadge]: https://img.shields.io/travis/stephencelis/SQLite.swift/master.svg?style=flat -[TravisLink]: https://travis-ci.org/stephencelis/SQLite.swift +[GitHubActionBadge]: https://img.shields.io/github/workflow/status/stephencelis/SQLite.swift/Build%20and%20test [CocoaPodsVersionBadge]: https://cocoapod-badges.herokuapp.com/v/SQLite.swift/badge.png [CocoaPodsVersionLink]: http://cocoadocs.org/docsets/SQLite.swift diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj index 92795299..1db9c2b7 100644 --- a/SQLite.xcodeproj/project.pbxproj +++ b/SQLite.xcodeproj/project.pbxproj @@ -271,7 +271,6 @@ EE247B451C3F3ED000AE3E12 /* SQLiteTests Mac.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "SQLiteTests Mac.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; EE247B771C3F40D700AE3E12 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; EE247B8B1C3F820300AE3E12 /* CONTRIBUTING.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = CONTRIBUTING.md; sourceTree = ""; }; - EE247B8C1C3F821200AE3E12 /* .travis.yml */ = {isa = PBXFileReference; lastKnownFileType = text; path = .travis.yml; sourceTree = ""; }; EE247B8D1C3F821200AE3E12 /* Makefile */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = ""; }; EE247B8F1C3F822500AE3E12 /* Index.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = Index.md; sourceTree = ""; }; EE247B911C3F822500AE3E12 /* installation@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "installation@2x.png"; sourceTree = ""; }; @@ -473,7 +472,6 @@ EE247B771C3F40D700AE3E12 /* README.md */, EE247B8B1C3F820300AE3E12 /* CONTRIBUTING.md */, EE247B931C3F826100AE3E12 /* SQLite.swift.podspec */, - EE247B8C1C3F821200AE3E12 /* .travis.yml */, EE247B8D1C3F821200AE3E12 /* Makefile */, EE9180931C46EA210038162A /* libsqlite3.tbd */, EE9180911C46E9D30038162A /* libsqlite3.tbd */, From 65b0989dbefdb1163ac419d7121a176335a90a85 Mon Sep 17 00:00:00 2001 From: Jake-B Date: Wed, 18 Aug 2021 14:56:18 -0400 Subject: [PATCH 37/52] Updated documentation for `===` and `!==` operators. --- Documentation/Index.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Documentation/Index.md b/Documentation/Index.md index c6c54437..76a69206 100644 --- a/Documentation/Index.md +++ b/Documentation/Index.md @@ -955,8 +955,10 @@ equate or compare different types will prevent compilation. | `~=` | `(Interval, Comparable) -> Bool` | `BETWEEN` | | `&&` | `Bool -> Bool` | `AND` | | `\|\|`| `Bool -> Bool` | `OR` | +| `===` | `Equatable -> Bool` | `IS` | +| `!==` | `Equatable -> Bool` | `IS NOT` | -> *When comparing against `nil`, SQLite.swift will use `IS` and `IS NOT` +> * When comparing against `nil`, SQLite.swift will use `IS` and `IS NOT` > accordingly. From b7302f6ed19b1994ef9d68882d1cb447be8fd238 Mon Sep 17 00:00:00 2001 From: Nathan Fallet Date: Wed, 18 Aug 2021 23:21:01 +0200 Subject: [PATCH 38/52] SPM as first installation method --- README.md | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 009167b0..81fb4b5d 100644 --- a/README.md +++ b/README.md @@ -126,6 +126,27 @@ and the [companion repository][SQLiteDataAccessLayer2]. > _Note:_ Version 0.11.6 and later requires Swift 5 (and [Xcode](https://developer.apple.com/xcode/downloads/) 10.2) or greater. Version 0.11.5 requires Swift 4.2 (and [Xcode](https://developer.apple.com/xcode/downloads/) 10.1) or greater. +### Swift Package Manager + +The [Swift Package Manager][] is a tool for managing the distribution of +Swift code. + +1. Add the following to your `Package.swift` file: + + ```swift + dependencies: [ + .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.12.0") + ] + ``` + +2. Build your project: + + ```sh + $ swift build + ``` + +[Swift Package Manager]: https://swift.org/package-manager + ### Carthage [Carthage][] is a simple, decentralized dependency manager for Cocoa. To @@ -177,27 +198,6 @@ SQLite.swift with CocoaPods: [CocoaPods]: https://cocoapods.org [CocoaPods Installation]: https://guides.cocoapods.org/using/getting-started.html#getting-started -### Swift Package Manager - -The [Swift Package Manager][] is a tool for managing the distribution of -Swift code. - -1. Add the following to your `Package.swift` file: - - ```swift - dependencies: [ - .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.12.0") - ] - ``` - -2. Build your project: - - ```sh - $ swift build - ``` - -[Swift Package Manager]: https://swift.org/package-manager - ### Manual To install SQLite.swift as an Xcode sub-project: From c5b7258ac175751b966a25bc6ba06f1be4ae2caa Mon Sep 17 00:00:00 2001 From: Nathan Fallet Date: Thu, 19 Aug 2021 00:03:18 +0200 Subject: [PATCH 39/52] Fixing missing date in upsert test --- Tests/SQLiteTests/QueryTests.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/SQLiteTests/QueryTests.swift b/Tests/SQLiteTests/QueryTests.swift index 9a19945f..fc47ab27 100644 --- a/Tests/SQLiteTests/QueryTests.swift +++ b/Tests/SQLiteTests/QueryTests.swift @@ -280,10 +280,10 @@ class QueryTests : XCTestCase { func test_upsert_encodable() throws { let emails = Table("emails") let string = Expression("string") - let value = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4, optional: nil, sub: nil) + let value = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4, date: Date(timeIntervalSince1970: 0), optional: nil, sub: nil) let insert = try emails.upsert(value, onConflictOf: string) AssertSQL( - "INSERT INTO \"emails\" (\"int\", \"string\", \"bool\", \"float\", \"double\") VALUES (1, '2', 1, 3.0, 4.0) ON CONFLICT (\"string\") DO UPDATE SET \"int\" = \"excluded\".\"int\", \"bool\" = \"excluded\".\"bool\", \"float\" = \"excluded\".\"float\", \"double\" = \"excluded\".\"double\"", + "INSERT INTO \"emails\" (\"int\", \"string\", \"bool\", \"float\", \"double\", \"date\") VALUES (1, '2', 1, 3.0, 4.0, '1970-01-01T00:00:00.000') ON CONFLICT (\"string\") DO UPDATE SET \"int\" = \"excluded\".\"int\", \"bool\" = \"excluded\".\"bool\", \"float\" = \"excluded\".\"float\", \"double\" = \"excluded\".\"double\"", insert ) } From 5c49d0ed90279d5f90a22997c95ca4c6bfb5bb9f Mon Sep 17 00:00:00 2001 From: Nathan Fallet Date: Thu, 19 Aug 2021 00:09:34 +0200 Subject: [PATCH 40/52] Oups --- Tests/SQLiteTests/QueryTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/SQLiteTests/QueryTests.swift b/Tests/SQLiteTests/QueryTests.swift index fc47ab27..9b845a97 100644 --- a/Tests/SQLiteTests/QueryTests.swift +++ b/Tests/SQLiteTests/QueryTests.swift @@ -283,7 +283,7 @@ class QueryTests : XCTestCase { let value = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4, date: Date(timeIntervalSince1970: 0), optional: nil, sub: nil) let insert = try emails.upsert(value, onConflictOf: string) AssertSQL( - "INSERT INTO \"emails\" (\"int\", \"string\", \"bool\", \"float\", \"double\", \"date\") VALUES (1, '2', 1, 3.0, 4.0, '1970-01-01T00:00:00.000') ON CONFLICT (\"string\") DO UPDATE SET \"int\" = \"excluded\".\"int\", \"bool\" = \"excluded\".\"bool\", \"float\" = \"excluded\".\"float\", \"double\" = \"excluded\".\"double\"", + "INSERT INTO \"emails\" (\"int\", \"string\", \"bool\", \"float\", \"double\", \"date\") VALUES (1, '2', 1, 3.0, 4.0, '1970-01-01T00:00:00.000') ON CONFLICT (\"string\") DO UPDATE SET \"int\" = \"excluded\".\"int\", \"bool\" = \"excluded\".\"bool\", \"float\" = \"excluded\".\"float\", \"double\" = \"excluded\".\"double\", \"date\" = \"excluded\".\"date\"", insert ) } From b20d6baca11636ac28de51aa797312811d50c2b5 Mon Sep 17 00:00:00 2001 From: Jan Berkel Date: Thu, 19 Aug 2021 00:11:54 +0200 Subject: [PATCH 41/52] Fix spm package structure --- Package.swift | 78 +++++++++---------- SQLite.xcodeproj/project.pbxproj | 20 ++--- Sources/SQLiteObjc/{ => include}/SQLiteObjc.h | 0 3 files changed, 48 insertions(+), 50 deletions(-) rename Sources/SQLiteObjc/{ => include}/SQLiteObjc.h (100%) diff --git a/Package.swift b/Package.swift index 73339b7b..7522fa45 100644 --- a/Package.swift +++ b/Package.swift @@ -4,52 +4,50 @@ import PackageDescription let package = Package( name: "SQLite.swift", products: [ - .library( - name: "SQLite", - targets: ["SQLite"] - ) - ], + .library( + name: "SQLite", + targets: ["SQLite"] + ) + ], targets: [ .target( - name: "SQLite", - dependencies: ["SQLiteObjc"], - exclude: [ - "Info.plist" - ] - ), + name: "SQLite", + dependencies: ["SQLiteObjc"], + exclude: [ + "Info.plist" + ] + ), .target( - name: "SQLiteObjc", - dependencies: [], - exclude: [ - "SQLiteObjc.h", - "fts3_tokenizer.h", - "include/README.md" - ] - ), + name: "SQLiteObjc", + dependencies: [], + exclude: [ + "fts3_tokenizer.h" + ] + ), .testTarget( - name: "SQLiteTests", - dependencies: [ - "SQLite" - ], - path: "Tests/SQLiteTests", - exclude: [ - "Info.plist" - ], - resources: [ - .copy("fixtures/encrypted-3.x.sqlite"), - .copy("fixtures/encrypted-4.x.sqlite") - ] - ) + name: "SQLiteTests", + dependencies: [ + "SQLite" + ], + path: "Tests/SQLiteTests", + exclude: [ + "Info.plist" + ], + resources: [ + .copy("fixtures/encrypted-3.x.sqlite"), + .copy("fixtures/encrypted-4.x.sqlite") + ] + ) ] ) #if os(Linux) - package.dependencies = [.package(url: "https://github.com/stephencelis/CSQLite.git", from: "0.0.3")] - package.targets = [ - .target(name: "SQLite", exclude: ["Extensions/FTS4.swift", "Extensions/FTS5.swift"]), - .testTarget(name: "SQLiteTests", dependencies: ["SQLite"], path: "Tests/SQLiteTests", exclude: [ - "FTS4Tests.swift", - "FTS5Tests.swift" - ]) - ] +package.dependencies = [.package(url: "https://github.com/stephencelis/CSQLite.git", from: "0.0.3")] +package.targets = [ + .target(name: "SQLite", exclude: ["Extensions/FTS4.swift", "Extensions/FTS5.swift"]), + .testTarget(name: "SQLiteTests", dependencies: ["SQLite"], path: "Tests/SQLiteTests", exclude: [ + "FTS4Tests.swift", + "FTS5Tests.swift" + ]) +] #endif diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj index 1b5bb0f4..a730284d 100644 --- a/SQLite.xcodeproj/project.pbxproj +++ b/SQLite.xcodeproj/project.pbxproj @@ -11,7 +11,6 @@ 03A65E721C6BB2D30062603F /* SQLite.h in Headers */ = {isa = PBXBuildFile; fileRef = EE247AD61C3F04ED00AE3E12 /* SQLite.h */; settings = {ATTRIBUTES = (Public, ); }; }; 03A65E731C6BB2D80062603F /* Foundation.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AF71C3F06E900AE3E12 /* Foundation.swift */; }; 03A65E741C6BB2DA0062603F /* Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AF81C3F06E900AE3E12 /* Helpers.swift */; }; - 03A65E751C6BB2DF0062603F /* SQLiteObjc.h in Headers */ = {isa = PBXBuildFile; fileRef = EE91808D1C46E5230038162A /* SQLiteObjc.h */; settings = {ATTRIBUTES = (Public, ); }; }; 03A65E761C6BB2E60062603F /* Blob.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AEE1C3F06E900AE3E12 /* Blob.swift */; }; 03A65E771C6BB2E60062603F /* Connection.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AEF1C3F06E900AE3E12 /* Connection.swift */; }; 03A65E781C6BB2EA0062603F /* fts3_tokenizer.h in Headers */ = {isa = PBXBuildFile; fileRef = EE247AF01C3F06E900AE3E12 /* fts3_tokenizer.h */; }; @@ -101,9 +100,12 @@ 3D67B3F71DB246D700A4F4C6 /* Foundation.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AF71C3F06E900AE3E12 /* Foundation.swift */; }; 3D67B3F81DB246D700A4F4C6 /* Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AF81C3F06E900AE3E12 /* Helpers.swift */; }; 3D67B3F91DB246E700A4F4C6 /* SQLiteObjc.m in Sources */ = {isa = PBXBuildFile; fileRef = EE247AF11C3F06E900AE3E12 /* SQLiteObjc.m */; }; - 3D67B3FB1DB2470600A4F4C6 /* SQLiteObjc.h in Headers */ = {isa = PBXBuildFile; fileRef = EE91808D1C46E5230038162A /* SQLiteObjc.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3D67B3FC1DB2471B00A4F4C6 /* SQLite.h in Headers */ = {isa = PBXBuildFile; fileRef = EE247AD61C3F04ED00AE3E12 /* SQLite.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3D67B3FD1DB2472D00A4F4C6 /* fts3_tokenizer.h in Headers */ = {isa = PBXBuildFile; fileRef = EE247AF01C3F06E900AE3E12 /* fts3_tokenizer.h */; }; + 3DDC112F26CDBA0200CE369F /* SQLiteObjc.h in Headers */ = {isa = PBXBuildFile; fileRef = 3DDC112E26CDBA0200CE369F /* SQLiteObjc.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 3DDC113626CDBE1900CE369F /* SQLiteObjc.h in Headers */ = {isa = PBXBuildFile; fileRef = 3DDC112E26CDBA0200CE369F /* SQLiteObjc.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 3DDC113726CDBE1900CE369F /* SQLiteObjc.h in Headers */ = {isa = PBXBuildFile; fileRef = 3DDC112E26CDBA0200CE369F /* SQLiteObjc.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 3DDC113826CDBE1C00CE369F /* SQLiteObjc.h in Headers */ = {isa = PBXBuildFile; fileRef = 3DDC112E26CDBA0200CE369F /* SQLiteObjc.h */; settings = {ATTRIBUTES = (Public, ); }; }; 49EB68C41F7B3CB400D89D40 /* Coding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49EB68C31F7B3CB400D89D40 /* Coding.swift */; }; 49EB68C51F7B3CB400D89D40 /* Coding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49EB68C31F7B3CB400D89D40 /* Coding.swift */; }; 49EB68C61F7B3CB400D89D40 /* Coding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49EB68C31F7B3CB400D89D40 /* Coding.swift */; }; @@ -183,8 +185,6 @@ EE247B731C3F3FEC00AE3E12 /* Query.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B001C3F06E900AE3E12 /* Query.swift */; }; EE247B741C3F3FEC00AE3E12 /* Schema.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B011C3F06E900AE3E12 /* Schema.swift */; }; EE247B751C3F3FEC00AE3E12 /* Setter.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B021C3F06E900AE3E12 /* Setter.swift */; }; - EE91808E1C46E5230038162A /* SQLiteObjc.h in Headers */ = {isa = PBXBuildFile; fileRef = EE91808D1C46E5230038162A /* SQLiteObjc.h */; settings = {ATTRIBUTES = (Public, ); }; }; - EE91808F1C46E76D0038162A /* SQLiteObjc.h in Headers */ = {isa = PBXBuildFile; fileRef = EE91808D1C46E5230038162A /* SQLiteObjc.h */; settings = {ATTRIBUTES = (Public, ); }; }; EE9180941C46EA210038162A /* libsqlite3.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = EE9180931C46EA210038162A /* libsqlite3.tbd */; }; EE9180951C46EBCC0038162A /* libsqlite3.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = EE9180911C46E9D30038162A /* libsqlite3.tbd */; }; /* End PBXBuildFile section */ @@ -229,6 +229,7 @@ 19A17BA55DABB480F9020C8A /* DateAndTimeFunctions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DateAndTimeFunctions.swift; sourceTree = ""; }; 19A17E2695737FAB5D6086E3 /* fixtures */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = folder; path = fixtures; sourceTree = ""; }; 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; }; + 3DDC112E26CDBA0200CE369F /* SQLiteObjc.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SQLiteObjc.h; path = ../SQLiteObjc/include/SQLiteObjc.h; sourceTree = ""; }; 49EB68C31F7B3CB400D89D40 /* Coding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Coding.swift; sourceTree = ""; }; A121AC451CA35C79005A31D1 /* SQLite.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SQLite.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D4DB368A20C09C9B00D5A58E /* SelectTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectTests.swift; sourceTree = ""; }; @@ -280,7 +281,6 @@ EE247B911C3F822500AE3E12 /* installation@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "installation@2x.png"; sourceTree = ""; }; EE247B921C3F822600AE3E12 /* playground@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "playground@2x.png"; sourceTree = ""; }; EE247B931C3F826100AE3E12 /* SQLite.swift.podspec */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; path = SQLite.swift.podspec; sourceTree = ""; }; - EE91808D1C46E5230038162A /* SQLiteObjc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SQLiteObjc.h; path = ../../SQLiteObjc/SQLiteObjc.h; sourceTree = ""; }; EE9180911C46E9D30038162A /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/usr/lib/libsqlite3.tbd; sourceTree = DEVELOPER_DIR; }; EE9180931C46EA210038162A /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = usr/lib/libsqlite3.tbd; sourceTree = SDKROOT; }; /* End PBXFileReference section */ @@ -384,6 +384,7 @@ isa = PBXGroup; children = ( EE247AD61C3F04ED00AE3E12 /* SQLite.h */, + 3DDC112E26CDBA0200CE369F /* SQLiteObjc.h */, EE247AF71C3F06E900AE3E12 /* Foundation.swift */, EE247AF81C3F06E900AE3E12 /* Helpers.swift */, EE247AD81C3F04ED00AE3E12 /* Info.plist */, @@ -430,7 +431,6 @@ EE247AED1C3F06E900AE3E12 /* Core */ = { isa = PBXGroup; children = ( - EE91808D1C46E5230038162A /* SQLiteObjc.h */, EE247AEE1C3F06E900AE3E12 /* Blob.swift */, EE247AEF1C3F06E900AE3E12 /* Connection.swift */, EE247AF01C3F06E900AE3E12 /* fts3_tokenizer.h */, @@ -512,7 +512,7 @@ buildActionMask = 2147483647; files = ( 03A65E781C6BB2EA0062603F /* fts3_tokenizer.h in Headers */, - 03A65E751C6BB2DF0062603F /* SQLiteObjc.h in Headers */, + 3DDC113626CDBE1900CE369F /* SQLiteObjc.h in Headers */, 03A65E721C6BB2D30062603F /* SQLite.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; @@ -521,8 +521,8 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( - 3D67B3FB1DB2470600A4F4C6 /* SQLiteObjc.h in Headers */, 3D67B3FC1DB2471B00A4F4C6 /* SQLite.h in Headers */, + 3DDC113726CDBE1900CE369F /* SQLiteObjc.h in Headers */, 3D67B3FD1DB2472D00A4F4C6 /* fts3_tokenizer.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; @@ -531,8 +531,8 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( - EE91808E1C46E5230038162A /* SQLiteObjc.h in Headers */, EE247B051C3F06E900AE3E12 /* fts3_tokenizer.h in Headers */, + 3DDC113826CDBE1C00CE369F /* SQLiteObjc.h in Headers */, EE247AD71C3F04ED00AE3E12 /* SQLite.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; @@ -542,8 +542,8 @@ buildActionMask = 2147483647; files = ( EE247B671C3F3FEC00AE3E12 /* fts3_tokenizer.h in Headers */, + 3DDC112F26CDBA0200CE369F /* SQLiteObjc.h in Headers */, EE247B621C3F3FDB00AE3E12 /* SQLite.h in Headers */, - EE91808F1C46E76D0038162A /* SQLiteObjc.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Sources/SQLiteObjc/SQLiteObjc.h b/Sources/SQLiteObjc/include/SQLiteObjc.h similarity index 100% rename from Sources/SQLiteObjc/SQLiteObjc.h rename to Sources/SQLiteObjc/include/SQLiteObjc.h From 4ddcb1cc6fdec3e4dd24d89e35a1f31630d81632 Mon Sep 17 00:00:00 2001 From: Jan Berkel Date: Thu, 19 Aug 2021 00:12:59 +0200 Subject: [PATCH 42/52] Fix query tests --- Tests/SQLiteTests/QueryTests.swift | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Tests/SQLiteTests/QueryTests.swift b/Tests/SQLiteTests/QueryTests.swift index 9b845a97..29cbd7e4 100644 --- a/Tests/SQLiteTests/QueryTests.swift +++ b/Tests/SQLiteTests/QueryTests.swift @@ -397,7 +397,7 @@ class QueryIntegrationTests : SQLiteTestCase { let id = Expression("id") let email = Expression("email") let age = Expression("age") - + override func setUp() { super.setUp() @@ -509,16 +509,16 @@ class QueryIntegrationTests : SQLiteTestCase { let fetchAge = { () throws -> Int? in return try self.db.pluck(self.users.filter(self.email == "alice@example.com")).flatMap { $0[self.age] } } - + let id = try db.run(users.upsert(email <- "alice@example.com", age <- 30, onConflictOf: email)) XCTAssertEqual(1, id) XCTAssertEqual(30, try fetchAge()) - + let nextId = try db.run(users.upsert(email <- "alice@example.com", age <- 42, onConflictOf: email)) XCTAssertEqual(1, nextId) XCTAssertEqual(42, try fetchAge()) } - + func test_update() { let changes = try! db.run(users.update(email <- "alice@example.com")) XCTAssertEqual(0, changes) @@ -528,24 +528,24 @@ class QueryIntegrationTests : SQLiteTestCase { let changes = try! db.run(users.delete()) XCTAssertEqual(0, changes) } - + func test_union() throws { let expectedIDs = [ try db.run(users.insert(email <- "alice@example.com")), try db.run(users.insert(email <- "sally@example.com")) ] - + let query1 = users.filter(email == "alice@example.com") let query2 = users.filter(email == "sally@example.com") - + let actualIDs = try db.prepare(query1.union(query2)).map { $0[id] } XCTAssertEqual(expectedIDs, actualIDs) - + let query3 = users.select(users[*], Expression(literal: "1 AS weight")).filter(email == "sally@example.com") let query4 = users.select(users[*], Expression(literal: "2 AS weight")).filter(email == "alice@example.com") - + print(query3.union(query4).order(Expression(literal: "weight")).asSQL()) - + let orderedIDs = try db.prepare(query3.union(query4).order(Expression(literal: "weight"), email)).map { $0[id] } XCTAssertEqual(Array(expectedIDs.reversed()), orderedIDs) } From 75a177a688ada617cef0efbc910f2fc3bdfbd1ff Mon Sep 17 00:00:00 2001 From: Nathan Fallet Date: Sat, 21 Aug 2021 18:03:32 +0200 Subject: [PATCH 43/52] Fixed codable insert many --- Tests/SQLiteTests/QueryTests.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Tests/SQLiteTests/QueryTests.swift b/Tests/SQLiteTests/QueryTests.swift index 9e6e9f7e..79e6871e 100644 --- a/Tests/SQLiteTests/QueryTests.swift +++ b/Tests/SQLiteTests/QueryTests.swift @@ -310,12 +310,12 @@ class QueryTests : XCTestCase { func test_insert_many_encodable() throws { let emails = Table("emails") - let value1 = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4, optional: nil, sub: nil) - let value2 = TestCodable(int: 2, string: "3", bool: true, float: 3, double: 5, optional: nil, sub: nil) - let value3 = TestCodable(int: 3, string: "4", bool: true, float: 3, double: 6, optional: nil, sub: nil) + let value1 = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4, date: Date(timeIntervalSince1970: 0), optional: nil, sub: nil) + let value2 = TestCodable(int: 2, string: "3", bool: true, float: 3, double: 5, date: Date(timeIntervalSince1970: 0), optional: nil, sub: nil) + let value3 = TestCodable(int: 3, string: "4", bool: true, float: 3, double: 6, date: Date(timeIntervalSince1970: 0), optional: nil, sub: nil) let insert = try emails.insertMany([value1, value2, value3]) AssertSQL( - "INSERT INTO \"emails\" (\"int\", \"string\", \"bool\", \"float\", \"double\") VALUES (1, '2', 1, 3.0, 4.0), (2, '3', 1, 3.0, 5.0), (3, '4', 1, 3.0, 6.0)", + "INSERT INTO \"emails\" (\"int\", \"string\", \"bool\", \"float\", \"double\", \"date\") VALUES (1, '2', 1, 3.0, 4.0, '1970-01-01T00:00:00.000'), (2, '3', 1, 3.0, 5.0, '1970-01-01T00:00:00.000'), (3, '4', 1, 3.0, 6.0, '1970-01-01T00:00:00.000')", insert ) } From 449edebfd6c91c2dd376ee61359b4c4b65032032 Mon Sep 17 00:00:00 2001 From: Nathan Fallet Date: Sat, 21 Aug 2021 19:24:45 +0200 Subject: [PATCH 44/52] Updating to 0.13.0 --- .swift-version | 2 +- SQLite.swift.podspec | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.swift-version b/.swift-version index bf77d549..819e07a2 100644 --- a/.swift-version +++ b/.swift-version @@ -1 +1 @@ -4.2 +5.0 diff --git a/SQLite.swift.podspec b/SQLite.swift.podspec index bcefa5fe..499012c1 100644 --- a/SQLite.swift.podspec +++ b/SQLite.swift.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "SQLite.swift" - s.version = "0.12.2" + s.version = "0.13.0" s.summary = "A type-safe, Swift-language layer over SQLite3 for iOS and macOS." s.description = <<-DESC @@ -16,12 +16,12 @@ Pod::Spec.new do |s| s.module_name = 'SQLite' s.default_subspec = 'standard' - s.swift_versions = ['4.2', '5'] + s.swift_versions = ['5'] ios_deployment_target = '9.0' tvos_deployment_target = '9.1' - osx_deployment_target = '10.10' + osx_deployment_target = '10.15' watchos_deployment_target = '3.0' s.ios.deployment_target = ios_deployment_target From 8096e9b9c5cc694f373d2176f96889d3e4afe053 Mon Sep 17 00:00:00 2001 From: Nathan Fallet Date: Sat, 21 Aug 2021 20:22:10 +0200 Subject: [PATCH 45/52] Deleted .swift-version --- .swift-version | 1 - 1 file changed, 1 deletion(-) delete mode 100644 .swift-version diff --git a/.swift-version b/.swift-version deleted file mode 100644 index 819e07a2..00000000 --- a/.swift-version +++ /dev/null @@ -1 +0,0 @@ -5.0 From 31f667f71f5e6b401a4ec2391d8e44d835e5d7fa Mon Sep 17 00:00:00 2001 From: Jan Berkel Date: Sun, 22 Aug 2021 03:13:48 +0200 Subject: [PATCH 46/52] Pod lint fixes --- Sources/SQLite/Core/Connection.swift | 82 +++++++++++++--------------- Tests/SQLiteTests/QueryTests.swift | 15 ++++- 2 files changed, 50 insertions(+), 47 deletions(-) diff --git a/Sources/SQLite/Core/Connection.swift b/Sources/SQLite/Core/Connection.swift index 1bbf7f73..f4a9e8cb 100644 --- a/Sources/SQLite/Core/Connection.swift +++ b/Sources/SQLite/Core/Connection.swift @@ -415,17 +415,17 @@ public final class Connection { /// /// db.trace { SQL in print(SQL) } public func trace(_ callback: ((String) -> Void)?) { - #if SQLITE_SWIFT_SQLCIPHER || os(Linux) + if #available(iOS 10.0, OSX 10.12, tvOS 10.0, watchOS 3.0, *) { + trace_v2(callback) + } else { trace_v1(callback) - #else - if #available(iOS 10.0, OSX 10.12, tvOS 10.0, watchOS 3.0, *) { - trace_v2(callback) - } else { - trace_v1(callback) - } - #endif + } } + @available(OSX, deprecated: 10.2) + @available(iOS, deprecated: 10.0) + @available(watchOS, deprecated: 3.0) + @available(tvOS, deprecated: 10.0) fileprivate func trace_v1(_ callback: ((String) -> Void)?) { guard let callback = callback else { sqlite3_trace(handle, nil /* xCallback */, nil /* pCtx */) @@ -447,8 +447,36 @@ public final class Connection { trace = box } + @available(iOS 10.0, OSX 10.12, tvOS 10.0, watchOS 3.0, *) + fileprivate func trace_v2(_ callback: ((String) -> Void)?) { + guard let callback = callback else { + // If the X callback is NULL or if the M mask is zero, then tracing is disabled. + sqlite3_trace_v2(handle, 0 /* mask */, nil /* xCallback */, nil /* pCtx */) + trace = nil + return + } - + let box: Trace = { (pointer: UnsafeRawPointer) in + callback(String(cString: pointer.assumingMemoryBound(to: UInt8.self))) + } + sqlite3_trace_v2(handle, UInt32(SQLITE_TRACE_STMT) /* mask */, + { + // A trace callback is invoked with four arguments: callback(T,C,P,X). + // The T argument is one of the SQLITE_TRACE constants to indicate why the + // callback was invoked. The C argument is a copy of the context pointer. + // The P and X arguments are pointers whose meanings depend on T. + (T: UInt32, C: UnsafeMutableRawPointer?, P: UnsafeMutableRawPointer?, X: UnsafeMutableRawPointer?) in + if let P = P, + let expandedSQL = sqlite3_expanded_sql(OpaquePointer(P)) { + unsafeBitCast(C, to: Trace.self)(expandedSQL) + sqlite3_free(expandedSQL) + } + return Int32(0) // currently ignored + }, + unsafeBitCast(box, to: UnsafeMutableRawPointer.self) /* pCtx */ + ) + trace = box + } fileprivate typealias Trace = @convention(block) (UnsafeRawPointer) -> Void fileprivate var trace: Trace? @@ -712,39 +740,3 @@ extension Result : CustomStringConvertible { } } } - -#if !SQLITE_SWIFT_SQLCIPHER && !os(Linux) -@available(iOS 10.0, OSX 10.12, tvOS 10.0, watchOS 3.0, *) -extension Connection { - fileprivate func trace_v2(_ callback: ((String) -> Void)?) { - guard let callback = callback else { - // If the X callback is NULL or if the M mask is zero, then tracing is disabled. - sqlite3_trace_v2(handle, 0 /* mask */, nil /* xCallback */, nil /* pCtx */) - trace = nil - return - } - - let box: Trace = { (pointer: UnsafeRawPointer) in - callback(String(cString: pointer.assumingMemoryBound(to: UInt8.self))) - } - sqlite3_trace_v2(handle, - UInt32(SQLITE_TRACE_STMT) /* mask */, - { - // A trace callback is invoked with four arguments: callback(T,C,P,X). - // The T argument is one of the SQLITE_TRACE constants to indicate why the - // callback was invoked. The C argument is a copy of the context pointer. - // The P and X arguments are pointers whose meanings depend on T. - (T: UInt32, C: UnsafeMutableRawPointer?, P: UnsafeMutableRawPointer?, X: UnsafeMutableRawPointer?) in - if let P = P, - let expandedSQL = sqlite3_expanded_sql(OpaquePointer(P)) { - unsafeBitCast(C, to: Trace.self)(expandedSQL) - sqlite3_free(expandedSQL) - } - return Int32(0) // currently ignored - }, - unsafeBitCast(box, to: UnsafeMutableRawPointer.self) /* pCtx */ - ) - trace = box - } -} -#endif diff --git a/Tests/SQLiteTests/QueryTests.swift b/Tests/SQLiteTests/QueryTests.swift index 79e6871e..041b7d0c 100644 --- a/Tests/SQLiteTests/QueryTests.swift +++ b/Tests/SQLiteTests/QueryTests.swift @@ -541,10 +541,11 @@ class QueryIntegrationTests : SQLiteTestCase { let id = try! db.run(users.insertMany([[email <- "alice@example.com"], [email <- "geoff@example.com"]])) XCTAssertEqual(2, id) } - + func test_upsert() throws { + guard db.satisfiesMinimumVersion(minor: 24) else { return } let fetchAge = { () throws -> Int? in - return try self.db.pluck(self.users.filter(self.email == "alice@example.com")).flatMap { $0[self.age] } + try self.db.pluck(self.users.filter(self.email == "alice@example.com")).flatMap { $0[self.age] } } let id = try db.run(users.upsert(email <- "alice@example.com", age <- 30, onConflictOf: email)) @@ -613,3 +614,13 @@ class QueryIntegrationTests : SQLiteTestCase { } } } + +private extension Connection { + func satisfiesMinimumVersion(minor: Int, patch: Int = 0) -> Bool { + guard let version = try? scalar("SELECT sqlite_version()") as? String else { return false } + let components = version.split(separator: ".", maxSplits: 3).compactMap { Int($0) } + guard components.count == 3 else { return false } + + return components[1] >= minor && components[2] >= patch + } +} From af4801483adf9fa26fa4e80df120b1913b380652 Mon Sep 17 00:00:00 2001 From: Nathan Fallet Date: Sun, 22 Aug 2021 17:11:01 +0200 Subject: [PATCH 47/52] Adding a `vacuum()` fonction to `Connection` --- Sources/SQLite/Core/Connection.swift | 11 +++++++++++ Tests/SQLiteTests/ConnectionTests.swift | 4 ++++ 2 files changed, 15 insertions(+) diff --git a/Sources/SQLite/Core/Connection.swift b/Sources/SQLite/Core/Connection.swift index f4a9e8cb..8fd07cc1 100644 --- a/Sources/SQLite/Core/Connection.swift +++ b/Sources/SQLite/Core/Connection.swift @@ -252,6 +252,17 @@ public final class Connection { @discardableResult public func run(_ statement: String, _ bindings: [String: Binding?]) throws -> Statement { return try prepare(statement).run(bindings) } + + // MARK: - VACUUM + + /// Run a vacuum on the database + /// + /// - Throws: `Result.Error` if query execution fails. + /// + /// - Returns: The statement. + @discardableResult public func vacuum() throws -> Statement { + return try run("VACUUM") + } // MARK: - Scalar diff --git a/Tests/SQLiteTests/ConnectionTests.swift b/Tests/SQLiteTests/ConnectionTests.swift index fdfc9637..a05cceb5 100644 --- a/Tests/SQLiteTests/ConnectionTests.swift +++ b/Tests/SQLiteTests/ConnectionTests.swift @@ -111,6 +111,10 @@ class ConnectionTests : SQLiteTestCase { try! db.run("SELECT * FROM users WHERE admin = $admin", ["$admin": 0]) AssertSQL("SELECT * FROM users WHERE admin = 0", 4) } + + func test_vacuum() { + try! db.vacuum() + } func test_scalar_preparesRunsAndReturnsScalarValues() { XCTAssertEqual(0, try! db.scalar("SELECT count(*) FROM users WHERE admin = 0") as? Int64) From e48d1469f7fda8175de4abdc45638a25e3409f6b Mon Sep 17 00:00:00 2001 From: Nathan Fallet Date: Sun, 22 Aug 2021 17:28:37 +0200 Subject: [PATCH 48/52] SPM as first installation method --- Documentation/Index.md | 51 +++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/Documentation/Index.md b/Documentation/Index.md index bc8bb52e..14b29b37 100644 --- a/Documentation/Index.md +++ b/Documentation/Index.md @@ -1,9 +1,9 @@ # SQLite.swift Documentation - [Installation](#installation) + - [Swift Package Manager](#swift-package-manager) - [Carthage](#carthage) - [CocoaPods](#cocoapods) - - [Swift Package Manager](#swift-package-manager) - [Manual](#manual) - [Getting Started](#getting-started) - [Connecting to a Database](#connecting-to-a-database) @@ -71,6 +71,30 @@ > _Note:_ SQLite.swift requires Swift 5 (and > [Xcode 10.2](https://developer.apple.com/xcode/downloads/)) or greater. +### Swift Package Manager + +The [Swift Package Manager][] is a tool for managing the distribution of +Swift code. It’s integrated with the Swift build system to automate the +process of downloading, compiling, and linking dependencies. + +It is the recommended approach for using SQLite.swift in OSX CLI +applications. + + 1. Add the following to your `Package.swift` file: + + ```swift + dependencies: [ + .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.12.0") + ] + ``` + + 2. Build your project: + + ```sh + $ swift build + ``` + +[Swift Package Manager]: https://swift.org/package-manager ### Carthage @@ -169,31 +193,6 @@ try db.rekey("another secret") [sqlite3pod]: https://github.com/clemensg/sqlite3pod [SQLCipher]: https://www.zetetic.net/sqlcipher/ -### Swift Package Manager - -The [Swift Package Manager][] is a tool for managing the distribution of -Swift code. It’s integrated with the Swift build system to automate the -process of downloading, compiling, and linking dependencies. - -It is the recommended approach for using SQLite.swift in OSX CLI -applications. - - 1. Add the following to your `Package.swift` file: - - ```swift - dependencies: [ - .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.12.0") - ] - ``` - - 2. Build your project: - - ```sh - $ swift build - ``` - -[Swift Package Manager]: https://swift.org/package-manager - ### Manual To install SQLite.swift as an Xcode sub-project: From c648408e467e84d9d2e191fe354080fde23b980c Mon Sep 17 00:00:00 2001 From: Jan Berkel Date: Sun, 22 Aug 2021 21:14:27 +0200 Subject: [PATCH 49/52] Ensure file can be cleaned up --- Tests/SQLiteTests/CipherTests.swift | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Tests/SQLiteTests/CipherTests.swift b/Tests/SQLiteTests/CipherTests.swift index abecffba..a27ac17d 100644 --- a/Tests/SQLiteTests/CipherTests.swift +++ b/Tests/SQLiteTests/CipherTests.swift @@ -88,6 +88,11 @@ class CipherTests: XCTestCase { try! FileManager.default.setAttributes([FileAttributeKey.immutable : 1], ofItemAtPath: encryptedFile) XCTAssertFalse(FileManager.default.isWritableFile(atPath: encryptedFile)) + defer { + // ensure file can be cleaned up afterwards + try! FileManager.default.setAttributes([FileAttributeKey.immutable : 0], ofItemAtPath: encryptedFile) + } + let conn = try! Connection(encryptedFile) try! conn.key("sqlcipher-test") XCTAssertEqual(1, try! conn.scalar("SELECT count(*) FROM foo") as? Int64) From 368c873e75cc3de9e39464ff82a09fc65649607e Mon Sep 17 00:00:00 2001 From: Jan Berkel Date: Sun, 22 Aug 2021 21:42:01 +0200 Subject: [PATCH 50/52] Update CHANGELOG --- CHANGELOG.md | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f29afeb..465438bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,22 @@ -0.12.3 (18-05-2021), [diff][diff-0.12.2] +0.13.0 (22-08-2021), [diff][diff-0.13.0] ======================================== -* Swift 5.3 support. -* Xcode 12.5 support. -* Bumps minimum deployment versions. -* Fixes up Package.swift to build SQLiteObjc module. +* Swift 5.3 support +* Xcode 12.5 support +* Bumps minimum deployment versions +* Fixes up Package.swift to build SQLiteObjc module -0.11.6 (xxx), [diff][diff-0.11.6] +0.12.1, 0.12.2 (21-06-2019) [diff][diff-0.12.2] +======================================== + +* CocoaPods modular headers support + +0.12.0 (24-04-2019) [diff][diff-0.12.0] +======================================== + +* Version with Swift 5 Support + +0.11.6 (19-04-2019), [diff][diff-0.11.6] ======================================== * Swift 4.2, SQLCipher 4.x ([#866][]) @@ -71,6 +81,9 @@ [diff-0.11.4]: https://github.com/stephencelis/SQLite.swift/compare/0.11.3...0.11.4 [diff-0.11.5]: https://github.com/stephencelis/SQLite.swift/compare/0.11.4...0.11.5 [diff-0.11.6]: https://github.com/stephencelis/SQLite.swift/compare/0.11.5...0.11.6 +[diff-0.12.0]: https://github.com/stephencelis/SQLite.swift/compare/0.11.6...0.12.0 +[diff-0.12.2]: https://github.com/stephencelis/SQLite.swift/compare/0.12.0...0.12.2 +[diff-0.13.0]: https://github.com/stephencelis/SQLite.swift/compare/0.12.2...0.13.0 [#142]: https://github.com/stephencelis/SQLite.swift/issues/142 [#315]: https://github.com/stephencelis/SQLite.swift/issues/315 From d27a078f24164c58827f3adba612415b10016190 Mon Sep 17 00:00:00 2001 From: Jan Berkel Date: Sun, 22 Aug 2021 21:53:16 +0200 Subject: [PATCH 51/52] Update version --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 81fb4b5d..98a625c4 100644 --- a/README.md +++ b/README.md @@ -135,7 +135,7 @@ Swift code. ```swift dependencies: [ - .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.12.0") + .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.13.0") ] ``` @@ -157,7 +157,7 @@ install SQLite.swift with Carthage: 2. Update your Cartfile to include the following: ```ruby - github "stephencelis/SQLite.swift" ~> 0.12.0 + github "stephencelis/SQLite.swift" ~> 0.13.0 ``` 3. Run `carthage update` and @@ -189,7 +189,7 @@ SQLite.swift with CocoaPods: use_frameworks! target 'YourAppTargetName' do - pod 'SQLite.swift', '~> 0.12.0' + pod 'SQLite.swift', '~> 0.13.0' end ``` From 9af51e2edf491c0ea632e369a6566e09b65aa333 Mon Sep 17 00:00:00 2001 From: Jan Berkel Date: Sun, 22 Aug 2021 22:09:47 +0200 Subject: [PATCH 52/52] Bump version --- Documentation/Index.md | 12 ++++++------ SQLite.xcodeproj/project.pbxproj | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Documentation/Index.md b/Documentation/Index.md index 14b29b37..21b1c995 100644 --- a/Documentation/Index.md +++ b/Documentation/Index.md @@ -84,7 +84,7 @@ applications. ```swift dependencies: [ - .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.12.0") + .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.13.0") ] ``` @@ -105,7 +105,7 @@ install SQLite.swift with Carthage: 2. Update your Cartfile to include the following: ```ruby - github "stephencelis/SQLite.swift" ~> 0.12.0 + github "stephencelis/SQLite.swift" ~> 0.13.0 ``` 3. Run `carthage update` and [add the appropriate framework][Carthage Usage]. @@ -135,7 +135,7 @@ install SQLite.swift with Carthage: use_frameworks! target 'YourAppTargetName' do - pod 'SQLite.swift', '~> 0.12.0' + pod 'SQLite.swift', '~> 0.13.0' end ``` @@ -149,7 +149,7 @@ with the OS you can require the `standalone` subspec: ```ruby target 'YourAppTargetName' do - pod 'SQLite.swift/standalone', '~> 0.12.0' + pod 'SQLite.swift/standalone', '~> 0.13.0' end ``` @@ -159,7 +159,7 @@ dependency to sqlite3 or one of its subspecs: ```ruby target 'YourAppTargetName' do - pod 'SQLite.swift/standalone', '~> 0.12.0' + pod 'SQLite.swift/standalone', '~> 0.13.0' pod 'sqlite3/fts5', '= 3.15.0' # SQLite 3.15.0 with FTS5 enabled end ``` @@ -173,7 +173,7 @@ If you want to use [SQLCipher][] with SQLite.swift you can require the ```ruby target 'YourAppTargetName' do - pod 'SQLite.swift/SQLCipher', '~> 0.12.0' + pod 'SQLite.swift/SQLCipher', '~> 0.13.0' end ``` diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj index a730284d..3056bd74 100644 --- a/SQLite.xcodeproj/project.pbxproj +++ b/SQLite.xcodeproj/project.pbxproj @@ -1265,7 +1265,7 @@ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MARKETING_VERSION = 0.12.3; + MARKETING_VERSION = 0.13.0; PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite; PRODUCT_NAME = SQLite; SKIP_INSTALL = YES; @@ -1287,7 +1287,7 @@ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MARKETING_VERSION = 0.12.3; + MARKETING_VERSION = 0.13.0; PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite; PRODUCT_NAME = SQLite; SKIP_INSTALL = YES;