diff --git a/GRDB/Record/EncodableRecord+Encodable.swift b/GRDB/Record/EncodableRecord+Encodable.swift index 500a4814d4..4ecbd5f803 100644 --- a/GRDB/Record/EncodableRecord+Encodable.swift +++ b/GRDB/Record/EncodableRecord+Encodable.swift @@ -224,7 +224,11 @@ extension RecordEncoder: SingleValueEncodingContainer { } func encode(_ value: T) throws where T : Encodable { - try value.encode(to: self) + if let record = value as? EncodableRecord { + try record.encode(to: &_persistenceContainer) + } else { + try value.encode(to: self) + } } } diff --git a/Tests/GRDBTests/MutablePersistableRecordEncodableTests.swift b/Tests/GRDBTests/MutablePersistableRecordEncodableTests.swift index 9ccd862e18..339a4a4973 100644 --- a/Tests/GRDBTests/MutablePersistableRecordEncodableTests.swift +++ b/Tests/GRDBTests/MutablePersistableRecordEncodableTests.swift @@ -124,6 +124,56 @@ extension MutablePersistableRecordEncodableTests { XCTAssertEqual(row[1], "bar") } } + + // Regression test for + // Here we test that `EncodableRecord` takes precedence over `Encodable` + // when a record is encoded with a `SingleValueEncodingContainer`. + func testSingleValueContainerWithEncodableRecord() throws { + struct Struct: Encodable, EncodableRecord { + let value: String + + func encode(to container: inout PersistenceContainer) throws { + container["column1"] = "test" + container["column2"] = 12 + } + } + + struct Wrapper: MutablePersistableRecord, Encodable { + static var databaseTableName: String { "t1" } + var model: Model + var otherValue: String + + enum CodingKeys: String, CodingKey { + case otherValue + } + + func encode(to encoder: any Encoder) throws { + var modelContainer = encoder.singleValueContainer() + try modelContainer.encode(model) + + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(otherValue, forKey: .otherValue) + } + } + + let dbQueue = try makeDatabaseQueue() + try dbQueue.inDatabase { db in + try db.create(table: "t1") { t in + t.column("column1", .text) + t.column("column2", .integer) + t.column("otherValue", .text) + } + + var value = Wrapper(model: Struct(value: "foo"), otherValue: "bar") + try assert(value, isEncodedIn: ["column1": "test", "column2": 12, "otherValue": "bar"]) + + try value.insert(db) + let row = try Row.fetchOne(db, sql: "SELECT column1, column2, otherValue FROM t1")! + XCTAssertEqual(row[0], "test") + XCTAssertEqual(row[1], 12) + XCTAssertEqual(row[2], "bar") + } + } } // MARK: - Different kinds of single-value properties