Skip to content

Commit c2cdd47

Browse files
fabianfett0xTim
andauthored
Add docs outlining changes we made to PostgresRow/column(name:) (#312)
Co-authored-by: Tim Condon <[email protected]>
1 parent a12d09f commit c2cdd47

File tree

4 files changed

+145
-3
lines changed

4 files changed

+145
-3
lines changed

Sources/PostgresNIO/Data/PostgresRow.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ public struct PostgresRandomAccessRow {
131131
let cells: [ByteBuffer?]
132132
let lookupTable: [String: Int]
133133

134-
init(_ row: PostgresRow) {
134+
public init(_ row: PostgresRow) {
135135
self.cells = [ByteBuffer?](row.data)
136136
self.columns = row.columns
137137
self.lookupTable = row.lookupTable

Sources/PostgresNIO/Docs.docc/index.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ Features:
1515

1616
## Topics
1717

18+
### Articles
19+
20+
- <doc:migrations>
21+
1822
### Connections
1923

2024
- ``PostgresConnection``
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
# Adopting the new PostgresRow cell API
2+
3+
This article describes how to adopt the new ``PostgresRow`` cell APIs in existing Postgres code
4+
which use the ``PostgresRow/column(_:)`` API today.
5+
6+
## TLDR
7+
8+
1. Map your sequence of ``PostgresRow``s to ``PostgresRandomAccessRow``s.
9+
2. Use the ``PostgresRandomAccessRow/subscript(name:)`` API to receive a ``PostgresCell``
10+
3. Decode the ``PostgresCell`` into a Swift type using the ``PostgresCell/decode(_:file:line:)`` method.
11+
12+
```swift
13+
let rows: [PostgresRow] // your existing return value
14+
for row in rows.map({ PostgresRandomAccessRow($0) }) {
15+
let id = try row["id"].decode(UUID.self)
16+
let name = try row["name"].decode(String.self)
17+
let email = try row["email"].decode(String.self)
18+
let age = try row["age"].decode(Int.self)
19+
}
20+
```
21+
22+
## Overview
23+
24+
When Postgres [`1.9.0`] was released we changed the default behaviour of ``PostgresRow``s.
25+
Previously for each row we created an internal lookup table, that allowed you to access the rows'
26+
cells by name:
27+
28+
```swift
29+
connection.query("SELECT id, name, email, age FROM users").whenComplete {
30+
switch $0 {
31+
case .success(let result):
32+
for row in result.rows {
33+
let id = row.column("id").uuid
34+
let name = row.column("name").string
35+
let email = row.column("email").string
36+
let age = row.column("age").int
37+
// do further processing
38+
}
39+
case .failure(let error):
40+
// handle the error
41+
}
42+
}
43+
```
44+
45+
During the last year we introduced a new API that let's you consume ``PostgresRow`` by iterating
46+
its cells. This approach has the performance benefit of not needing to create an internal cell
47+
lookup table for each row:
48+
49+
```swift
50+
connection.query("SELECT id, name, email, age FROM users").whenComplete {
51+
switch $0 {
52+
case .success(let result):
53+
for row in result.rows {
54+
let (id, name, email, age) = try row.decode((UUID, String, String, Int).self)
55+
// do further processing
56+
}
57+
case .failure(let error):
58+
// handle the error
59+
}
60+
}
61+
```
62+
63+
However, since we still supported the ``PostgresRow/column(_:)`` API, which requires a precomputed
64+
lookup table within the row, users were not seeing any performance benefits. To allow users to
65+
benefit of the new fastpath, we changed ``PostgresRow``'s behavior:
66+
67+
By default the ``PostgresRow`` does not create an internal lookup table for its cells on creation
68+
anymore. Because of this, when using the ``PostgresRow/column(_:)`` API, a throwaway lookup table
69+
needs to be produced on every call. Since this is wasteful we have deprecated this API. Instead we
70+
allow users now to explicitly opt-in into the cell lookup API by using the new
71+
``PostgresRandomAccessRow``.
72+
73+
```swift
74+
connection.query("SELECT id, name, email, age FROM users").whenComplete {
75+
switch $0 {
76+
case .success(let result):
77+
for row in result.rows.map { PostgresRandomAccessRow($0) } {
78+
let id = try row["id"].decode(UUID.self)
79+
let name = try row["name"].decode(String.self)
80+
let email = try row["email"].decode(String.self)
81+
let age = try row["age"].decode(Int.self)
82+
// do further processing
83+
}
84+
case .failure(let error):
85+
// handle the error
86+
}
87+
}
88+
```
89+
90+
## Topics
91+
92+
### Relevant types
93+
94+
- ``PostgresConnection``
95+
- ``PostgresQuery``
96+
- ``PostgresBindings``
97+
- ``PostgresRow``
98+
- ``PostgresRandomAccessRow``
99+
- ``PostgresEncodable``
100+
- ``PostgresDecodable``
101+
102+
[`1.9.0`]: https://github.com/vapor/postgres-nio/releases/tag/1.9.0

Sources/PostgresNIO/New/PostgresCell.swift

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,26 @@
11
import NIOCore
22

3+
/// A representation of a cell value within a ``PostgresRow`` and ``PostgresRandomAccessRow``.
34
public struct PostgresCell: Equatable {
5+
/// The cell's value as raw bytes.
46
public var bytes: ByteBuffer?
7+
/// The cell's data type. This is important metadata when decoding the cell.
58
public var dataType: PostgresDataType
9+
/// The format in which the cell's bytes are encoded.
610
public var format: PostgresFormat
711

12+
/// The cell's column name within the row.
813
public var columnName: String
14+
/// The cell's column index within the row.
915
public var columnIndex: Int
1016

11-
init(bytes: ByteBuffer?, dataType: PostgresDataType, format: PostgresFormat, columnName: String, columnIndex: Int) {
17+
public init(
18+
bytes: ByteBuffer?,
19+
dataType: PostgresDataType,
20+
format: PostgresFormat,
21+
columnName: String,
22+
columnIndex: Int
23+
) {
1224
self.bytes = bytes
1325
self.dataType = dataType
1426
self.format = format
@@ -19,7 +31,14 @@ public struct PostgresCell: Equatable {
1931
}
2032

2133
extension PostgresCell {
22-
34+
/// Decode the cell into a Swift type, that conforms to ``PostgresDecodable``
35+
///
36+
/// - Parameters:
37+
/// - _: The Swift type, which conforms to ``PostgresDecodable``, to decode from the cell's ``PostgresCell/bytes`` values.
38+
/// - context: A ``PostgresDecodingContext`` to supply a custom ``PostgresJSONDecoder`` for decoding JSON fields.
39+
/// - file: The source file in which this method was called. Used in the error case in ``PostgresDecodingError``.
40+
/// - line: The source file line in which this method was called. Used in the error case in ``PostgresDecodingError``.
41+
/// - Returns: A decoded Swift type.
2342
@inlinable
2443
public func decode<T: PostgresDecodable, JSONDecoder: PostgresJSONDecoder>(
2544
_: T.Type,
@@ -49,6 +68,23 @@ extension PostgresCell {
4968
)
5069
}
5170
}
71+
72+
73+
/// Decode the cell into a Swift type, that conforms to ``PostgresDecodable``
74+
///
75+
/// - Parameters:
76+
/// - _: The Swift type, which conforms to ``PostgresDecodable``, to decode from the cell's ``PostgresCell/bytes`` values.
77+
/// - file: The source file in which this method was called. Used in the error case in ``PostgresDecodingError``.
78+
/// - line: The source file line in which this method was called. Used in the error case in ``PostgresDecodingError``.
79+
/// - Returns: A decoded Swift type.
80+
@inlinable
81+
public func decode<T: PostgresDecodable>(
82+
_: T.Type,
83+
file: String = #file,
84+
line: Int = #line
85+
) throws -> T {
86+
try self.decode(T.self, context: .default, file: file, line: line)
87+
}
5288
}
5389

5490
#if swift(>=5.6)

0 commit comments

Comments
 (0)