Skip to content

Commit

Permalink
Merge branch 'development'
Browse files Browse the repository at this point in the history
  • Loading branch information
groue committed Jul 20, 2024
2 parents 78448cf + bbbe0d8 commit 1cd6c7c
Show file tree
Hide file tree
Showing 8 changed files with 138 additions and 9 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ GRDB adheres to [Semantic Versioning](https://semver.org/), with one exception:

#### 6.x Releases

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

---

## 6.29.0

Released July 20, 2024

- **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)
- **New**: [#1575](https://github.com/groue/GRDB.swift/pull/1575) by [@Jason-Abbott](https://github.com/Jason-Abbott): Show comments when tracing expanded statements

## 6.28.0

Released July 11, 2024
Expand Down
6 changes: 4 additions & 2 deletions Documentation/ReleaseProcess.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@ To release a new GRDB version:
- README.md
- Support/Info.plist
- Commit and tag
- Check tag authors: `git for-each-ref --format '%(refname) %(authorname)' refs/tags`
- Push to the master & development branch
- Look for undesired tags: `git for-each-ref --format '%(refname) %(authorname)' refs/tags`
- Push to the `master` branch
- Push to the `development` branch
- Push to the `GRDB6` branch
- `pod trunk push --allow-warnings GRDB.swift.podspec`
- Update [performance comparison](https://github.com/groue/GRDB.swift/wiki/Performance):

Expand Down
2 changes: 1 addition & 1 deletion GRDB.swift.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'GRDB.swift'
s.version = '6.28.0'
s.version = '6.29.0'

s.license = { :type => 'MIT', :file => 'LICENSE' }
s.summary = 'A toolkit for SQLite databases, with a focus on application development.'
Expand Down
7 changes: 6 additions & 1 deletion GRDB/Core/Database.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2083,7 +2083,8 @@ extension Database {
/// ```
public var sql: String {
if let unexpandedSQL {
return String(cString: unexpandedSQL).trimmedSQLStatement
let sql = String(cString: unexpandedSQL)
return sql.hasPrefix("--") ? sql : sql.trimmedSQLStatement
} else {
return String(cString: sqlite3_sql(sqliteStatement)).trimmedSQLStatement
}
Expand All @@ -2101,6 +2102,10 @@ extension Database {
/// information from leaking in unexpected locations, so use this
/// property with care.
public var expandedSQL: String {
if let unexpandedSQL {
let sql = String(cString: unexpandedSQL)
if sql.hasPrefix("--") { return sql }
}
guard let cString = sqlite3_expanded_sql(sqliteStatement) else {
return ""
}
Expand Down
54 changes: 51 additions & 3 deletions GRDB/Record/FetchableRecord+Decodable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,12 @@ private struct _RowDecoder<R: FetchableRecord>: Decoder {

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

// Unknown key
//
// Should be throw an error? Well... The use case is the following:
// Should we throw an error? Well... The use case is the following:
//
// // SELECT book.*, author.* FROM book
// // JOIN author ON author.id = book.authorId
Expand Down Expand Up @@ -487,6 +491,50 @@ private struct _RowDecoder<R: FetchableRecord>: Decoder {
}
}

private struct SingleValueRowDecoder<R: FetchableRecord>: SingleValueDecodingContainer {
var columnDecoder: ColumnDecoder<R>
var columnDecodingStrategy: DatabaseColumnDecodingStrategy
let codingPath: [any CodingKey] = []

func decodeNil() -> Bool { columnDecoder.decodeNil() }
func decode(_ type: Bool.Type) throws -> Bool { try columnDecoder.decode(type) }
func decode(_ type: String.Type) throws -> String { try columnDecoder.decode(type) }
func decode(_ type: Double.Type) throws -> Double { try columnDecoder.decode(type) }
func decode(_ type: Float.Type) throws -> Float { try columnDecoder.decode(type) }
func decode(_ type: Int.Type) throws -> Int { try columnDecoder.decode(type) }
func decode(_ type: Int8.Type) throws -> Int8 { try columnDecoder.decode(type) }
func decode(_ type: Int16.Type) throws -> Int16 { try columnDecoder.decode(type) }
func decode(_ type: Int32.Type) throws -> Int32 { try columnDecoder.decode(type) }
func decode(_ type: Int64.Type) throws -> Int64 { try columnDecoder.decode(type) }
#if compiler(>=6)
@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *)
func decode(_ type: Int128.Type) throws -> Int128 { try columnDecoder.decode(type) }
#endif
func decode(_ type: UInt.Type) throws -> UInt { try columnDecoder.decode(type) }
func decode(_ type: UInt8.Type) throws -> UInt8 { try columnDecoder.decode(type) }
func decode(_ type: UInt16.Type) throws -> UInt16 { try columnDecoder.decode(type) }
func decode(_ type: UInt32.Type) throws -> UInt32 { try columnDecoder.decode(type) }
func decode(_ type: UInt64.Type) throws -> UInt64 { try columnDecoder.decode(type) }
#if compiler(>=6)
@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *)
func decode(_ type: UInt128.Type) throws -> UInt128 { try columnDecoder.decode(type) }
#endif

func decode<T>(_ type: T.Type) throws -> T where T: Decodable {
if let type = T.self as? any FetchableRecord.Type {
// Prefer FetchableRecord decoding over Decodable.
return try type.init(row: columnDecoder.row) as! T
} else {
let decoder = _RowDecoder<R>(
row: columnDecoder.row,
codingPath: [],
columnDecodingStrategy: columnDecodingStrategy
)
return try T(from: decoder)
}
}
}

// MARK: - PrefetchedRowsDecoder

private struct PrefetchedRowsDecoder<R: FetchableRecord>: Decoder {
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<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>
</p>

**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)
**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)

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

Expand Down
2 changes: 1 addition & 1 deletion Support/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>6.28.0</string>
<string>6.29.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
Expand Down
66 changes: 66 additions & 0 deletions Tests/GRDBTests/FetchableRecordDecodableTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1913,4 +1913,70 @@ extension FetchableRecordDecodableTests {
}
}
}

// Regression test for <https://github.com/groue/GRDB.swift/issues/1572>
func testSingleValueContainer() throws {
struct Struct: Decodable {
let value: String
}

struct Wrapper<Model: Decodable>: FetchableRecord, Decodable {
var model: Model
var otherValue: String

enum CodingKeys: String, CodingKey {
case otherValue
}

init(from decoder: any Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
otherValue = try container.decode(String.self, forKey: . otherValue)

let singleValueContainer = try decoder.singleValueContainer()
model = try singleValueContainer.decode(Model.self)
}
}

let row = Row(["value": "foo", "otherValue": "bar"])

let wrapper = try Wrapper<Struct>(row: row)
XCTAssertEqual(wrapper.model.value, "foo")
XCTAssertEqual(wrapper.otherValue, "bar")
}

// Regression test for <https://github.com/groue/GRDB.swift/issues/1572>
// Here we test that `FetchableRecord` takes precedence over `Decodable`
// when a record is encoded with a `SingleValueEncodingContainer`.
func testSingleValueContainerWithFetchableRecord() throws {
struct Struct: Decodable, FetchableRecord {
let value: String

init(row: Row) throws {
value = row["actualValue"]
}
}

struct Wrapper<Model: Decodable>: FetchableRecord, Decodable {
var model: Model
var otherValue: String

enum CodingKeys: String, CodingKey {
case otherValue
}

init(from decoder: any Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
otherValue = try container.decode(String.self, forKey: . otherValue)

let singleValueContainer = try decoder.singleValueContainer()
model = try singleValueContainer.decode(Model.self)
}
}

let row = Row(["value": "foo", "otherValue": "bar", "actualValue": "test"])

let wrapper = try Wrapper<Struct>(row: row)
XCTAssertEqual(wrapper.model.value, "test")
XCTAssertEqual(wrapper.otherValue, "bar")
}
}

0 comments on commit 1cd6c7c

Please sign in to comment.