diff --git a/Package.swift b/Package.swift index d4837f5..4fe0629 100644 --- a/Package.swift +++ b/Package.swift @@ -15,10 +15,10 @@ let package = Package( .package(url: "https://github.com/vapor/core.git", .exact("3.0.0-beta.1")), // Swift ORM framework (queries, models, and relations) for building NoSQL and SQL database integrations. - .package(url: "https://github.com/vapor/fluent.git", .exact("3.0.0-beta.1")), + .package(url: "https://github.com/vapor/fluent.git", .exact("3.0.0-beta.2")), // Pure Swift, async/non-blocking client for PostgreSQL. - .package(url: "https://github.com/vapor/postgresql.git", .exact("1.0.0-beta.1")), + .package(url: "https://github.com/vapor/postgresql.git", .exact("1.0.0-beta.2")), ], targets: [ .target(name: "FluentPostgreSQL", dependencies: ["Async", "CodableKit", "Fluent", "FluentSQL", "PostgreSQL"]), diff --git a/Sources/FluentPostgreSQL/PostgreSQLDatabase+QuerySupporting.swift b/Sources/FluentPostgreSQL/PostgreSQLDatabase+QuerySupporting.swift index 64a5062..d967f74 100644 --- a/Sources/FluentPostgreSQL/PostgreSQLDatabase+QuerySupporting.swift +++ b/Sources/FluentPostgreSQL/PostgreSQLDatabase+QuerySupporting.swift @@ -77,23 +77,27 @@ extension PostgreSQLDatabase: QuerySupporting { } /// See `QuerySupporting.modelEvent` - public static func modelEvent(event: ModelEvent, model: M, on connection: PostgreSQLConnection) -> Future + public static func modelEvent(event: ModelEvent, model: M, on connection: PostgreSQLConnection) -> Future where PostgreSQLDatabase == M.Database, M: Model { switch event { case .willCreate: if M.ID.self == UUID.self { + var model = model model.fluentID = UUID() as? M.ID + return Future(model) } case .didCreate: if M.ID.self == Int.self { - return connection.simpleQuery("SELECT LASTVAL();").map(to: Void.self) { row in - try! model.fluentID = row[0]["lastval"]?.decode(Int.self) as? M.ID + return connection.simpleQuery("SELECT LASTVAL();").map(to: M.self) { row in + var model = model + try model.fluentID = row[0]["lastval"]?.decode(Int.self) as? M.ID + return model } } default: break } - return .done + return Future(model) } } diff --git a/Sources/FluentPostgreSQL/PostgreSQLRowEncoder.swift b/Sources/FluentPostgreSQL/PostgreSQLRowEncoder.swift index 60d94cd..f7e9d1d 100644 --- a/Sources/FluentPostgreSQL/PostgreSQLRowEncoder.swift +++ b/Sources/FluentPostgreSQL/PostgreSQLRowEncoder.swift @@ -1,3 +1,5 @@ +import Foundation + internal final class PostgreSQLRowEncoder: Encoder { var codingPath: [CodingKey] var userInfo: [CodingUserInfoKey : Any] @@ -43,7 +45,7 @@ fileprivate struct PostgreSQLRowKeyedEncodingContainer: KeyedEncodingContaine self.codingPath = [] } - mutating func encodeNil(forKey key: K) throws { encoder.data[key.stringValue] = PostgreSQLData(type: .void) } + mutating func encodeNil(forKey key: K) throws { encoder.data[key.stringValue] = PostgreSQLData(type: .void, data: nil) } mutating func encode(_ value: Bool, forKey key: K) throws { encoder.data[key.stringValue] = try value.convertToPostgreSQLData() } mutating func encode(_ value: Int, forKey key: K) throws { encoder.data[key.stringValue] = try value.convertToPostgreSQLData() } mutating func encode(_ value: Int16, forKey key: K) throws { encoder.data[key.stringValue] = try value.convertToPostgreSQLData() } @@ -59,7 +61,18 @@ fileprivate struct PostgreSQLRowKeyedEncodingContainer: KeyedEncodingContaine mutating func encode(_ value: String, forKey key: K) throws { encoder.data[key.stringValue] = try value.convertToPostgreSQLData() } mutating func superEncoder() -> Encoder { return encoder } mutating func superEncoder(forKey key: K) -> Encoder { return encoder } - mutating func encode(_ value: T, forKey key: K) throws where T : Encodable { + mutating func encodeIfPresent(_ value: T?, forKey key: K) throws where T : Encodable { + if let value = value { + try encode(value, forKey: key) + } else { + if let convertibleType = T.self as? PostgreSQLDataCustomConvertible.Type { + encoder.data[key.stringValue] = PostgreSQLData(type: convertibleType.postgreSQLDataType, data: nil) + } else { + try encodeNil(forKey: key) + } + } + } + mutating func encode(_ value: T, forKey key: K) throws where T: Encodable { guard let convertible = value as? PostgreSQLDataCustomConvertible else { let type = Swift.type(of: value) throw PostgreSQLError( diff --git a/Tests/FluentPostgreSQLTests/FluentPostgreSQLTests.swift b/Tests/FluentPostgreSQLTests/FluentPostgreSQLTests.swift index 1b33cd4..f0c81aa 100644 --- a/Tests/FluentPostgreSQLTests/FluentPostgreSQLTests.swift +++ b/Tests/FluentPostgreSQLTests/FluentPostgreSQLTests.swift @@ -42,6 +42,22 @@ class FluentPostgreSQLTests: XCTestCase { try benchmarker.benchmarkAutoincrement_withSchema() } + func testCache() throws { + try benchmarker.benchmarkCache_withSchema() + } + + func testJoins() throws { + try benchmarker.benchmarkJoins_withSchema() + } + + func testSoftDeletable() throws { + try benchmarker.benchmarkSoftDeletable_withSchema() + } + + func testReferentialActions() throws { + try benchmarker.benchmarkReferentialActions_withSchema() + } + func testNestedStruct() throws { /// Swift runtime does not yet support dynamically querying conditional conformance ('Swift.Array': 'CodableKit.AnyKeyStringDecodable') return; @@ -72,6 +88,10 @@ class FluentPostgreSQLTests: XCTestCase { ("testTransactions", testTransactions), ("testChunking", testChunking), ("testAutoincrement", testAutoincrement), + ("testCache", testCache), + ("testJoins", testJoins), + ("testSoftDeletable", testSoftDeletable), + ("testReferentialActions", testReferentialActions), ] } @@ -80,7 +100,7 @@ struct Pet: PostgreSQLJSONType { } final class User: PostgreSQLModel, Migration { - static let idKey = \User.id + static let idKey: WritableKeyPath = \User.id var id: Int? var name: String var age: Int?