Skip to content

Commit 1cd6c7c

Browse files
committed
Merge branch 'development'
2 parents 78448cf + bbbe0d8 commit 1cd6c7c

File tree

8 files changed

+138
-9
lines changed

8 files changed

+138
-9
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ GRDB adheres to [Semantic Versioning](https://semver.org/), with one exception:
77

88
#### 6.x Releases
99

10+
- `6.29.x` Releases - [6.29.0](#6290)
1011
- `6.28.x` Releases - [6.28.0](#6280)
1112
- `6.27.x` Releases - [6.27.0](#6270)
1213
- `6.26.x` Releases - [6.26.0](#6260)
@@ -126,6 +127,13 @@ GRDB adheres to [Semantic Versioning](https://semver.org/), with one exception:
126127

127128
---
128129

130+
## 6.29.0
131+
132+
Released July 20, 2024
133+
134+
- **New**: [#1574](https://github.com/groue/GRDB.swift/pull/1574) by [@sroebert](https://github.com/sroebert): Support for single value decoding (the complement of [#1570](https://github.com/groue/GRDB.swift/pull/1570) shipped in 6.28.0)
135+
- **New**: [#1575](https://github.com/groue/GRDB.swift/pull/1575) by [@Jason-Abbott](https://github.com/Jason-Abbott): Show comments when tracing expanded statements
136+
129137
## 6.28.0
130138

131139
Released July 11, 2024

Documentation/ReleaseProcess.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@ To release a new GRDB version:
1818
- README.md
1919
- Support/Info.plist
2020
- Commit and tag
21-
- Check tag authors: `git for-each-ref --format '%(refname) %(authorname)' refs/tags`
22-
- Push to the master & development branch
21+
- Look for undesired tags: `git for-each-ref --format '%(refname) %(authorname)' refs/tags`
22+
- Push to the `master` branch
23+
- Push to the `development` branch
24+
- Push to the `GRDB6` branch
2325
- `pod trunk push --allow-warnings GRDB.swift.podspec`
2426
- Update [performance comparison](https://github.com/groue/GRDB.swift/wiki/Performance):
2527

GRDB.swift.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Pod::Spec.new do |s|
22
s.name = 'GRDB.swift'
3-
s.version = '6.28.0'
3+
s.version = '6.29.0'
44

55
s.license = { :type => 'MIT', :file => 'LICENSE' }
66
s.summary = 'A toolkit for SQLite databases, with a focus on application development.'

GRDB/Core/Database.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2083,7 +2083,8 @@ extension Database {
20832083
/// ```
20842084
public var sql: String {
20852085
if let unexpandedSQL {
2086-
return String(cString: unexpandedSQL).trimmedSQLStatement
2086+
let sql = String(cString: unexpandedSQL)
2087+
return sql.hasPrefix("--") ? sql : sql.trimmedSQLStatement
20872088
} else {
20882089
return String(cString: sqlite3_sql(sqliteStatement)).trimmedSQLStatement
20892090
}
@@ -2101,6 +2102,10 @@ extension Database {
21012102
/// information from leaking in unexpected locations, so use this
21022103
/// property with care.
21032104
public var expandedSQL: String {
2105+
if let unexpandedSQL {
2106+
let sql = String(cString: unexpandedSQL)
2107+
if sql.hasPrefix("--") { return sql }
2108+
}
21042109
guard let cString = sqlite3_expanded_sql(sqliteStatement) else {
21052110
return ""
21062111
}

GRDB/Record/FetchableRecord+Decodable.swift

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,8 +108,12 @@ private struct _RowDecoder<R: FetchableRecord>: Decoder {
108108

109109
func singleValueContainer() throws -> SingleValueDecodingContainer {
110110
guard let key = codingPath.last else {
111-
// Decoding an array of scalars from rows: pick the first column
112-
return ColumnDecoder<R>(row: row, columnIndex: 0, codingPath: codingPath)
111+
// Not yet sure what we are decoding, this will be decided in the SingleValueDecodingContainer functions.
112+
// For decoding an array of scalars (in case of prefetched rows) we pick the first column.
113+
return SingleValueRowDecoder(
114+
columnDecoder: ColumnDecoder<R>(row: row, columnIndex: 0, codingPath: codingPath),
115+
columnDecodingStrategy: columnDecodingStrategy
116+
)
113117
}
114118
guard let index = row.index(forColumn: key.stringValue) else {
115119
// Don't use DecodingError.keyNotFound:
@@ -346,7 +350,7 @@ private struct _RowDecoder<R: FetchableRecord>: Decoder {
346350

347351
// Unknown key
348352
//
349-
// Should be throw an error? Well... The use case is the following:
353+
// Should we throw an error? Well... The use case is the following:
350354
//
351355
// // SELECT book.*, author.* FROM book
352356
// // JOIN author ON author.id = book.authorId
@@ -487,6 +491,50 @@ private struct _RowDecoder<R: FetchableRecord>: Decoder {
487491
}
488492
}
489493

494+
private struct SingleValueRowDecoder<R: FetchableRecord>: SingleValueDecodingContainer {
495+
var columnDecoder: ColumnDecoder<R>
496+
var columnDecodingStrategy: DatabaseColumnDecodingStrategy
497+
let codingPath: [any CodingKey] = []
498+
499+
func decodeNil() -> Bool { columnDecoder.decodeNil() }
500+
func decode(_ type: Bool.Type) throws -> Bool { try columnDecoder.decode(type) }
501+
func decode(_ type: String.Type) throws -> String { try columnDecoder.decode(type) }
502+
func decode(_ type: Double.Type) throws -> Double { try columnDecoder.decode(type) }
503+
func decode(_ type: Float.Type) throws -> Float { try columnDecoder.decode(type) }
504+
func decode(_ type: Int.Type) throws -> Int { try columnDecoder.decode(type) }
505+
func decode(_ type: Int8.Type) throws -> Int8 { try columnDecoder.decode(type) }
506+
func decode(_ type: Int16.Type) throws -> Int16 { try columnDecoder.decode(type) }
507+
func decode(_ type: Int32.Type) throws -> Int32 { try columnDecoder.decode(type) }
508+
func decode(_ type: Int64.Type) throws -> Int64 { try columnDecoder.decode(type) }
509+
#if compiler(>=6)
510+
@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *)
511+
func decode(_ type: Int128.Type) throws -> Int128 { try columnDecoder.decode(type) }
512+
#endif
513+
func decode(_ type: UInt.Type) throws -> UInt { try columnDecoder.decode(type) }
514+
func decode(_ type: UInt8.Type) throws -> UInt8 { try columnDecoder.decode(type) }
515+
func decode(_ type: UInt16.Type) throws -> UInt16 { try columnDecoder.decode(type) }
516+
func decode(_ type: UInt32.Type) throws -> UInt32 { try columnDecoder.decode(type) }
517+
func decode(_ type: UInt64.Type) throws -> UInt64 { try columnDecoder.decode(type) }
518+
#if compiler(>=6)
519+
@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *)
520+
func decode(_ type: UInt128.Type) throws -> UInt128 { try columnDecoder.decode(type) }
521+
#endif
522+
523+
func decode<T>(_ type: T.Type) throws -> T where T: Decodable {
524+
if let type = T.self as? any FetchableRecord.Type {
525+
// Prefer FetchableRecord decoding over Decodable.
526+
return try type.init(row: columnDecoder.row) as! T
527+
} else {
528+
let decoder = _RowDecoder<R>(
529+
row: columnDecoder.row,
530+
codingPath: [],
531+
columnDecodingStrategy: columnDecodingStrategy
532+
)
533+
return try T(from: decoder)
534+
}
535+
}
536+
}
537+
490538
// MARK: - PrefetchedRowsDecoder
491539

492540
private struct PrefetchedRowsDecoder<R: FetchableRecord>: Decoder {

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
<a href="https://github.com/groue/GRDB.swift/actions/workflows/CI.yml"><img alt="CI Status" src="https://github.com/groue/GRDB.swift/actions/workflows/CI.yml/badge.svg?branch=master"></a>
1616
</p>
1717

18-
**Latest release**: July 11, 2024 • [version 6.28.0](https://github.com/groue/GRDB.swift/tree/v6.28.0) • [CHANGELOG](CHANGELOG.md) • [Migrating From GRDB 5 to GRDB 6](Documentation/GRDB6MigrationGuide.md)
18+
**Latest release**: July 20, 2024 • [version 6.29.0](https://github.com/groue/GRDB.swift/tree/v6.29.0) • [CHANGELOG](CHANGELOG.md) • [Migrating From GRDB 5 to GRDB 6](Documentation/GRDB6MigrationGuide.md)
1919

2020
**Requirements**: iOS 11.0+ / macOS 10.13+ / tvOS 11.0+ / watchOS 4.0+ &bull; SQLite 3.19.3+ &bull; Swift 5.7+ / Xcode 14+
2121

Support/Info.plist

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
<key>CFBundlePackageType</key>
1616
<string>FMWK</string>
1717
<key>CFBundleShortVersionString</key>
18-
<string>6.28.0</string>
18+
<string>6.29.0</string>
1919
<key>CFBundleSignature</key>
2020
<string>????</string>
2121
<key>CFBundleVersion</key>

Tests/GRDBTests/FetchableRecordDecodableTests.swift

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1913,4 +1913,70 @@ extension FetchableRecordDecodableTests {
19131913
}
19141914
}
19151915
}
1916+
1917+
// Regression test for <https://github.com/groue/GRDB.swift/issues/1572>
1918+
func testSingleValueContainer() throws {
1919+
struct Struct: Decodable {
1920+
let value: String
1921+
}
1922+
1923+
struct Wrapper<Model: Decodable>: FetchableRecord, Decodable {
1924+
var model: Model
1925+
var otherValue: String
1926+
1927+
enum CodingKeys: String, CodingKey {
1928+
case otherValue
1929+
}
1930+
1931+
init(from decoder: any Decoder) throws {
1932+
let container = try decoder.container(keyedBy: CodingKeys.self)
1933+
otherValue = try container.decode(String.self, forKey: . otherValue)
1934+
1935+
let singleValueContainer = try decoder.singleValueContainer()
1936+
model = try singleValueContainer.decode(Model.self)
1937+
}
1938+
}
1939+
1940+
let row = Row(["value": "foo", "otherValue": "bar"])
1941+
1942+
let wrapper = try Wrapper<Struct>(row: row)
1943+
XCTAssertEqual(wrapper.model.value, "foo")
1944+
XCTAssertEqual(wrapper.otherValue, "bar")
1945+
}
1946+
1947+
// Regression test for <https://github.com/groue/GRDB.swift/issues/1572>
1948+
// Here we test that `FetchableRecord` takes precedence over `Decodable`
1949+
// when a record is encoded with a `SingleValueEncodingContainer`.
1950+
func testSingleValueContainerWithFetchableRecord() throws {
1951+
struct Struct: Decodable, FetchableRecord {
1952+
let value: String
1953+
1954+
init(row: Row) throws {
1955+
value = row["actualValue"]
1956+
}
1957+
}
1958+
1959+
struct Wrapper<Model: Decodable>: FetchableRecord, Decodable {
1960+
var model: Model
1961+
var otherValue: String
1962+
1963+
enum CodingKeys: String, CodingKey {
1964+
case otherValue
1965+
}
1966+
1967+
init(from decoder: any Decoder) throws {
1968+
let container = try decoder.container(keyedBy: CodingKeys.self)
1969+
otherValue = try container.decode(String.self, forKey: . otherValue)
1970+
1971+
let singleValueContainer = try decoder.singleValueContainer()
1972+
model = try singleValueContainer.decode(Model.self)
1973+
}
1974+
}
1975+
1976+
let row = Row(["value": "foo", "otherValue": "bar", "actualValue": "test"])
1977+
1978+
let wrapper = try Wrapper<Struct>(row: row)
1979+
XCTAssertEqual(wrapper.model.value, "test")
1980+
XCTAssertEqual(wrapper.otherValue, "bar")
1981+
}
19161982
}

0 commit comments

Comments
 (0)