Skip to content

Commit ffb5121

Browse files
authored
Add new protocol PostgresNonThrowingEncodable (#322)
1 parent b5cca72 commit ffb5121

15 files changed

+124
-30
lines changed

Sources/PostgresNIO/New/Data/Array+PostgresCodable.swift

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,43 @@ extension Array: PostgresEncodable where Element: PostgresArrayEncodable {
124124
}
125125
}
126126

127+
extension Array: PostgresNonThrowingEncodable where Element: PostgresArrayEncodable & PostgresNonThrowingEncodable {
128+
public static var psqlType: PostgresDataType {
129+
Element.psqlArrayType
130+
}
131+
132+
public static var psqlFormat: PostgresFormat {
133+
.binary
134+
}
135+
136+
@inlinable
137+
public func encode<JSONEncoder: PostgresJSONEncoder>(
138+
into buffer: inout ByteBuffer,
139+
context: PostgresEncodingContext<JSONEncoder>
140+
) {
141+
// 0 if empty, 1 if not
142+
buffer.writeInteger(self.isEmpty ? 0 : 1, as: UInt32.self)
143+
// b
144+
buffer.writeInteger(0, as: Int32.self)
145+
// array element type
146+
buffer.writeInteger(Element.psqlType.rawValue)
147+
148+
// continue if the array is not empty
149+
guard !self.isEmpty else {
150+
return
151+
}
152+
153+
// length of array
154+
buffer.writeInteger(numericCast(self.count), as: Int32.self)
155+
// dimensions
156+
buffer.writeInteger(1, as: Int32.self)
157+
158+
self.forEach { element in
159+
element.encodeRaw(into: &buffer, context: context)
160+
}
161+
}
162+
}
163+
127164

128165
extension Array: PostgresDecodable where Element: PostgresArrayDecodable, Element == Element._DecodableType {
129166
public init<JSONDecoder: PostgresJSONDecoder>(

Sources/PostgresNIO/New/Data/Bool+PostgresCodable.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ extension Bool: PostgresDecodable {
4343
}
4444
}
4545

46-
extension Bool: PostgresEncodable {
46+
extension Bool: PostgresNonThrowingEncodable {
4747
public static var psqlType: PostgresDataType {
4848
.bool
4949
}

Sources/PostgresNIO/New/Data/Bytes+PostgresCodable.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ extension PostgresEncodable where Self: Sequence, Self.Element == UInt8 {
66
public static var psqlType: PostgresDataType {
77
.bytea
88
}
9-
9+
1010
public static var psqlFormat: PostgresFormat {
1111
.binary
1212
}
@@ -20,7 +20,9 @@ extension PostgresEncodable where Self: Sequence, Self.Element == UInt8 {
2020
}
2121
}
2222

23-
extension ByteBuffer: PostgresEncodable {
23+
extension PostgresNonThrowingEncodable where Self: Sequence, Self.Element == UInt8 {}
24+
25+
extension ByteBuffer: PostgresNonThrowingEncodable {
2426
public static var psqlType: PostgresDataType {
2527
.bytea
2628
}

Sources/PostgresNIO/New/Data/Date+PostgresCodable.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import NIOCore
22
import struct Foundation.Date
33

4-
extension Date: PostgresEncodable {
4+
extension Date: PostgresNonThrowingEncodable {
55
public static var psqlType: PostgresDataType {
66
.timestamptz
77
}

Sources/PostgresNIO/New/Data/Float+PostgresCodable.swift

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

3-
extension Float: PostgresEncodable {
3+
extension Float: PostgresNonThrowingEncodable {
44
public static var psqlType: PostgresDataType {
55
.float4
66
}
@@ -48,7 +48,7 @@ extension Float: PostgresDecodable {
4848
}
4949
}
5050

51-
extension Double: PostgresEncodable {
51+
extension Double: PostgresNonThrowingEncodable {
5252
public static var psqlType: PostgresDataType {
5353
.float8
5454
}

Sources/PostgresNIO/New/Data/Int+PostgresCodable.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import NIOCore
22

33
// MARK: UInt8
44

5-
extension UInt8: PostgresEncodable {
5+
extension UInt8: PostgresNonThrowingEncodable {
66
public static var psqlType: PostgresDataType {
77
.char
88
}
@@ -43,7 +43,7 @@ extension UInt8: PostgresDecodable {
4343

4444
// MARK: Int16
4545

46-
extension Int16: PostgresEncodable {
46+
extension Int16: PostgresNonThrowingEncodable {
4747
public static var psqlType: PostgresDataType {
4848
.int2
4949
}
@@ -88,7 +88,7 @@ extension Int16: PostgresDecodable {
8888

8989
// MARK: Int32
9090

91-
extension Int32: PostgresEncodable {
91+
extension Int32: PostgresNonThrowingEncodable {
9292
public static var psqlType: PostgresDataType {
9393
.int4
9494
}
@@ -138,7 +138,7 @@ extension Int32: PostgresDecodable {
138138

139139
// MARK: Int64
140140

141-
extension Int64: PostgresEncodable {
141+
extension Int64: PostgresNonThrowingEncodable {
142142
public static var psqlType: PostgresDataType {
143143
.int8
144144
}
@@ -193,7 +193,7 @@ extension Int64: PostgresDecodable {
193193

194194
// MARK: Int
195195

196-
extension Int: PostgresEncodable {
196+
extension Int: PostgresNonThrowingEncodable {
197197
public static var psqlType: PostgresDataType {
198198
switch MemoryLayout<Int>.size {
199199
case 4:

Sources/PostgresNIO/New/Data/String+PostgresCodable.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import NIOCore
22
import struct Foundation.UUID
33

4-
extension String: PostgresEncodable {
4+
extension String: PostgresNonThrowingEncodable {
55
public static var psqlType: PostgresDataType {
66
.text
77
}

Sources/PostgresNIO/New/Data/UUID+PostgresCodable.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import NIOCore
22
import struct Foundation.UUID
33
import typealias Foundation.uuid_t
44

5-
extension UUID: PostgresEncodable {
5+
extension UUID: PostgresNonThrowingEncodable {
66
public static var psqlType: PostgresDataType {
77
.uuid
88
}

Sources/PostgresNIO/New/PostgresCodable.swift

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import Foundation
33

44
/// A type that can encode itself to a postgres wire binary representation.
55
public protocol PostgresEncodable {
6+
// TODO: Rename to `PostgresThrowingEncodable` with next major release
7+
68
/// identifies the data type that we will encode into `byteBuffer` in `encode`
79
static var psqlType: PostgresDataType { get }
810

@@ -14,6 +16,16 @@ public protocol PostgresEncodable {
1416
func encode<JSONEncoder: PostgresJSONEncoder>(into byteBuffer: inout ByteBuffer, context: PostgresEncodingContext<JSONEncoder>) throws
1517
}
1618

19+
/// A type that can encode itself to a postgres wire binary representation. It enforces that the
20+
/// ``PostgresEncodable/encode(into:context:)`` does not throw. This allows users
21+
/// to create ``PostgresQuery``s using the `ExpressibleByStringInterpolation` without
22+
/// having to spell `try`.
23+
public protocol PostgresNonThrowingEncodable: PostgresEncodable {
24+
// TODO: Rename to `PostgresEncodable` with next major release
25+
26+
func encode<JSONEncoder: PostgresJSONEncoder>(into byteBuffer: inout ByteBuffer, context: PostgresEncodingContext<JSONEncoder>)
27+
}
28+
1729
/// A type that can decode itself from a postgres wire binary representation.
1830
///
1931
/// If you want to conform a type to PostgresDecodable you must implement the decode method.
@@ -90,6 +102,26 @@ extension PostgresEncodable {
90102
}
91103
}
92104

105+
extension PostgresNonThrowingEncodable {
106+
@inlinable
107+
func encodeRaw<JSONEncoder: PostgresJSONEncoder>(
108+
into buffer: inout ByteBuffer,
109+
context: PostgresEncodingContext<JSONEncoder>
110+
) {
111+
// The length of the parameter value, in bytes (this count does not include
112+
// itself). Can be zero.
113+
let lengthIndex = buffer.writerIndex
114+
buffer.writeInteger(0, as: Int32.self)
115+
let startIndex = buffer.writerIndex
116+
// The value of the parameter, in the format indicated by the associated format
117+
// code. n is the above length.
118+
self.encode(into: &buffer, context: context)
119+
120+
// overwrite the empty length, with the real value
121+
buffer.setInteger(numericCast(buffer.writerIndex - startIndex), at: lengthIndex, as: Int32.self)
122+
}
123+
}
124+
93125
/// A context that is passed to Swift objects that are encoded into the Postgres wire format. Used
94126
/// to pass further information to the encoding method.
95127
public struct PostgresEncodingContext<JSONEncoder: PostgresJSONEncoder> {

Sources/PostgresNIO/New/PostgresQuery.swift

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,24 @@ extension PostgresQuery {
5959
self.sql.append(contentsOf: "$\(self.binds.count)")
6060
}
6161

62+
@inlinable
63+
public mutating func appendInterpolation<Value: PostgresNonThrowingEncodable>(_ value: Value) {
64+
self.binds.append(value, context: .default)
65+
self.sql.append(contentsOf: "$\(self.binds.count)")
66+
}
67+
68+
@inlinable
69+
public mutating func appendInterpolation<Value: PostgresNonThrowingEncodable>(_ value: Optional<Value>) {
70+
switch value {
71+
case .none:
72+
self.binds.appendNull()
73+
case .some(let value):
74+
self.binds.append(value, context: .default)
75+
}
76+
77+
self.sql.append(contentsOf: "$\(self.binds.count)")
78+
}
79+
6280
@inlinable
6381
public mutating func appendInterpolation<Value: PostgresEncodable, JSONEncoder: PostgresJSONEncoder>(
6482
_ value: Value,
@@ -139,6 +157,15 @@ public struct PostgresBindings: Hashable {
139157
self.metadata.append(.init(value: value))
140158
}
141159

160+
@inlinable
161+
public mutating func append<Value: PostgresNonThrowingEncodable, JSONEncoder: PostgresJSONEncoder>(
162+
_ value: Value,
163+
context: PostgresEncodingContext<JSONEncoder>
164+
) {
165+
value.encodeRaw(into: &self.bytes, context: context)
166+
self.metadata.append(.init(value: value))
167+
}
168+
142169
mutating func append(_ postgresData: PostgresData) {
143170
switch postgresData.value {
144171
case .none:

0 commit comments

Comments
 (0)