Skip to content

Commit ebfe647

Browse files
committed
add CodableDataEncoder
1 parent d6a3d90 commit ebfe647

21 files changed

+741
-210
lines changed

Sources/PostgreSQL/Codable/PostgreSQLDataEncoder.swift

Lines changed: 472 additions & 47 deletions
Large diffs are not rendered by default.

Sources/PostgreSQL/Codable/PostgreSQLRowEncoder.swift

Lines changed: 132 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -8,130 +8,144 @@ public struct PostgreSQLRowEncoder {
88
/// - parameters:
99
/// - encodable: Item to encode.
1010
/// - tableOID: Optional table OID to use when encoding.
11-
public func encode<E>(_ encodable: E, tableOID: UInt32? = nil) throws -> [PostgreSQLColumn: PostgreSQLData]
11+
public func encode<E>(_ encodable: E, tableOID: UInt32 = 0) throws -> [PostgreSQLColumn: PostgreSQLData]
1212
where E: Encodable
1313
{
14-
let encoder = _PostgreSQLRowEncoder(tableOID: tableOID)
15-
try encodable.encode(to: encoder)
16-
return encoder.data
17-
}
18-
}
19-
20-
21-
// MARK: Private
22-
23-
private final class _PostgreSQLRowEncoder: Encoder {
24-
let codingPath: [CodingKey] = []
25-
let userInfo: [CodingUserInfoKey: Any] = [:]
26-
var data: [PostgreSQLColumn: PostgreSQLData]
27-
let tableOID: UInt32?
28-
init(tableOID: UInt32?) {
29-
self.data = [:]
30-
self.tableOID = tableOID
31-
}
32-
33-
func container<Key>(keyedBy type: Key.Type) -> KeyedEncodingContainer<Key> where Key : CodingKey {
34-
let container = _PostgreSQLRowKeyedEncodingContainer<Key>(encoder: self)
35-
return KeyedEncodingContainer(container)
36-
}
37-
38-
func unkeyedContainer() -> UnkeyedEncodingContainer {
39-
unsupported()
40-
}
41-
42-
func singleValueContainer() -> SingleValueEncodingContainer {
43-
unsupported()
44-
}
45-
46-
}
47-
48-
private struct _PostgreSQLRowKeyedEncodingContainer<K>: KeyedEncodingContainerProtocol
49-
where K: CodingKey
50-
{
51-
var codingPath: [CodingKey]
52-
let encoder: _PostgreSQLRowEncoder
53-
init(encoder: _PostgreSQLRowEncoder) {
54-
self.encoder = encoder
55-
self.codingPath = []
56-
}
57-
58-
func set(_ key: CodingKey, to value: PostgreSQLDataConvertible) throws {
59-
let col = PostgreSQLColumn(tableOID: encoder.tableOID ?? 0, name: key.stringValue)
60-
self.encoder.data[col] = try value.convertToPostgreSQLData()
61-
}
62-
63-
mutating func encodeNil(forKey key: K) throws { try set(key, to: PostgreSQLData(null: .void)) }
64-
mutating func encode(_ value: Bool, forKey key: K) throws { try set(key, to: value) }
65-
mutating func encode(_ value: Int, forKey key: K) throws { try set(key, to: value) }
66-
mutating func encode(_ value: Int16, forKey key: K) throws { try set(key, to: value) }
67-
mutating func encode(_ value: Int32, forKey key: K) throws { try set(key, to: value) }
68-
mutating func encode(_ value: Int64, forKey key: K) throws { try set(key, to: value) }
69-
mutating func encode(_ value: UInt, forKey key: K) throws { try set(key, to: value) }
70-
mutating func encode(_ value: UInt8, forKey key: K) throws { try set(key, to: value) }
71-
mutating func encode(_ value: UInt16, forKey key: K) throws { try set(key, to: value) }
72-
mutating func encode(_ value: UInt32, forKey key: K) throws { try set(key, to: value) }
73-
mutating func encode(_ value: UInt64, forKey key: K) throws { try set(key, to: value) }
74-
mutating func encode(_ value: Double, forKey key: K) throws { try set(key, to: value) }
75-
mutating func encode(_ value: Float, forKey key: K) throws { try set(key, to: value) }
76-
mutating func encode(_ value: String, forKey key: K) throws { try set(key, to: value) }
77-
mutating func encodeIfPresent(_ value: Bool?, forKey key: K) throws { try _encodeIfPresent(value, forKey: key) }
78-
mutating func encodeIfPresent(_ value: Int?, forKey key: K) throws { try _encodeIfPresent(value, forKey: key) }
79-
mutating func encodeIfPresent(_ value: Int16?, forKey key: K) throws { try _encodeIfPresent(value, forKey: key) }
80-
mutating func encodeIfPresent(_ value: Int32?, forKey key: K) throws { try _encodeIfPresent(value, forKey: key) }
81-
mutating func encodeIfPresent(_ value: Int64?, forKey key: K) throws { try _encodeIfPresent(value, forKey: key) }
82-
mutating func encodeIfPresent(_ value: UInt?, forKey key: K) throws { try _encodeIfPresent(value, forKey: key) }
83-
mutating func encodeIfPresent(_ value: UInt8?, forKey key: K) throws { try _encodeIfPresent(value, forKey: key) }
84-
mutating func encodeIfPresent(_ value: UInt16?, forKey key: K) throws { try _encodeIfPresent(value, forKey: key) }
85-
mutating func encodeIfPresent(_ value: UInt32?, forKey key: K) throws { try _encodeIfPresent(value, forKey: key) }
86-
mutating func encodeIfPresent(_ value: UInt64?, forKey key: K) throws { try _encodeIfPresent(value, forKey: key) }
87-
mutating func encodeIfPresent(_ value: Double?, forKey key: K) throws { try _encodeIfPresent(value, forKey: key) }
88-
mutating func encodeIfPresent(_ value: Float?, forKey key: K) throws { try _encodeIfPresent(value, forKey: key) }
89-
mutating func encodeIfPresent(_ value: String?, forKey key: K) throws { try _encodeIfPresent(value, forKey: key) }
90-
mutating func encodeIfPresent<T>(_ value: T?, forKey key: K) throws where T: Encodable { try _encodeIfPresent(value, forKey: key) }
91-
92-
mutating func superEncoder() -> Encoder { return encoder }
93-
mutating func superEncoder(forKey key: K) -> Encoder { return encoder }
94-
mutating func _encodeIfPresent<T>(_ value: T?, forKey key: K) throws where T : Encodable {
95-
if let value = value {
96-
try encode(value, forKey: key)
97-
} else {
98-
if let convertibleType = T.self as? PostgreSQLDataConvertible.Type {
99-
try set(key, to: PostgreSQLData(null: convertibleType.postgreSQLDataType))
100-
} else {
101-
try encodeNil(forKey: key)
14+
let all = try CodableDataEncoder().encode(encodable)
15+
print(all)
16+
switch all {
17+
case .keyed(let keyed):
18+
var row: [PostgreSQLColumn: PostgreSQLData] = [:]
19+
for (key, val) in keyed {
20+
let col = PostgreSQLColumn(tableOID: tableOID, name: key)
21+
if let unwrapped = val.unwrapped {
22+
print(type(of: unwrapped))
23+
row[col] = try PostgreSQLDataEncoder().encode(unwrapped)
24+
} else {
25+
row[col] = PostgreSQLData(null: .null)
26+
}
10227
}
28+
return row
29+
default: throw PostgreSQLError(identifier: "rowEncoder", reason: "Unsupported row encode type: \(all).")
10330
}
10431
}
105-
106-
mutating func encode<T>(_ value: T, forKey key: K) throws where T: Encodable {
107-
guard let convertible = value as? PostgreSQLDataConvertible else {
108-
let type = Swift.type(of: value)
109-
throw PostgreSQLError(
110-
identifier: "convertible",
111-
reason: "Unsupported encodable type: \(type)",
112-
suggestedFixes: [
113-
"Conform \(type) to PostgreSQLDataCustomConvertible"
114-
]
115-
)
116-
}
117-
try set(key, to: convertible)
118-
}
119-
mutating func nestedContainer<NestedKey>(keyedBy keyType: NestedKey.Type, forKey key: K) -> KeyedEncodingContainer<NestedKey> where NestedKey : CodingKey {
120-
return encoder.container(keyedBy: NestedKey.self)
121-
}
122-
123-
mutating func nestedUnkeyedContainer(forKey key: K) -> UnkeyedEncodingContainer {
124-
return encoder.unkeyedContainer()
125-
}
12632
}
12733

128-
private func unsupported() -> Never {
129-
fatalError("""
130-
PostgreSQL rows only support a flat, keyed structure `[String: T]`.
13134

132-
Query data must be an encodable dictionary, struct, or class.
133-
134-
You can also conform nested types to `PostgreSQLJSONType` or `PostgreSQLArrayType`. (Nested types must be `PostgreSQLDataCustomConvertible`.)
135-
""")
136-
}
35+
// MARK: Private
13736

37+
//private final class _PostgreSQLRowEncoder: Encoder {
38+
// let codingPath: [CodingKey] = []
39+
// let userInfo: [CodingUserInfoKey: Any] = [:]
40+
// var data: [PostgreSQLColumn: PostgreSQLData]
41+
// let tableOID: UInt32?
42+
// init(tableOID: UInt32?) {
43+
// self.data = [:]
44+
// self.tableOID = tableOID
45+
// }
46+
//
47+
// func container<Key>(keyedBy type: Key.Type) -> KeyedEncodingContainer<Key> where Key : CodingKey {
48+
// let container = _PostgreSQLRowKeyedEncodingContainer<Key>(encoder: self)
49+
// return KeyedEncodingContainer(container)
50+
// }
51+
//
52+
// func unkeyedContainer() -> UnkeyedEncodingContainer {
53+
// unsupported()
54+
// }
55+
//
56+
// func singleValueContainer() -> SingleValueEncodingContainer {
57+
// unsupported()
58+
// }
59+
//
60+
//}
61+
//
62+
//private struct _PostgreSQLRowKeyedEncodingContainer<K>: KeyedEncodingContainerProtocol
63+
// where K: CodingKey
64+
//{
65+
// var codingPath: [CodingKey]
66+
// let encoder: _PostgreSQLRowEncoder
67+
// init(encoder: _PostgreSQLRowEncoder) {
68+
// self.encoder = encoder
69+
// self.codingPath = []
70+
// }
71+
//
72+
// func set(_ key: CodingKey, to value: PostgreSQLDataConvertible) throws {
73+
// let col = PostgreSQLColumn(tableOID: encoder.tableOID ?? 0, name: key.stringValue)
74+
// self.encoder.data[col] = try value.convertToPostgreSQLData()
75+
// }
76+
//
77+
// mutating func encodeNil(forKey key: K) throws { try set(key, to: PostgreSQLData(null: .void)) }
78+
// mutating func encode(_ value: Bool, forKey key: K) throws { try set(key, to: value) }
79+
// mutating func encode(_ value: Int, forKey key: K) throws { try set(key, to: value) }
80+
// mutating func encode(_ value: Int16, forKey key: K) throws { try set(key, to: value) }
81+
// mutating func encode(_ value: Int32, forKey key: K) throws { try set(key, to: value) }
82+
// mutating func encode(_ value: Int64, forKey key: K) throws { try set(key, to: value) }
83+
// mutating func encode(_ value: UInt, forKey key: K) throws { try set(key, to: value) }
84+
// mutating func encode(_ value: UInt8, forKey key: K) throws { try set(key, to: value) }
85+
// mutating func encode(_ value: UInt16, forKey key: K) throws { try set(key, to: value) }
86+
// mutating func encode(_ value: UInt32, forKey key: K) throws { try set(key, to: value) }
87+
// mutating func encode(_ value: UInt64, forKey key: K) throws { try set(key, to: value) }
88+
// mutating func encode(_ value: Double, forKey key: K) throws { try set(key, to: value) }
89+
// mutating func encode(_ value: Float, forKey key: K) throws { try set(key, to: value) }
90+
// mutating func encode(_ value: String, forKey key: K) throws { try set(key, to: value) }
91+
// mutating func encodeIfPresent(_ value: Bool?, forKey key: K) throws { try _encodeIfPresent(value, forKey: key) }
92+
// mutating func encodeIfPresent(_ value: Int?, forKey key: K) throws { try _encodeIfPresent(value, forKey: key) }
93+
// mutating func encodeIfPresent(_ value: Int16?, forKey key: K) throws { try _encodeIfPresent(value, forKey: key) }
94+
// mutating func encodeIfPresent(_ value: Int32?, forKey key: K) throws { try _encodeIfPresent(value, forKey: key) }
95+
// mutating func encodeIfPresent(_ value: Int64?, forKey key: K) throws { try _encodeIfPresent(value, forKey: key) }
96+
// mutating func encodeIfPresent(_ value: UInt?, forKey key: K) throws { try _encodeIfPresent(value, forKey: key) }
97+
// mutating func encodeIfPresent(_ value: UInt8?, forKey key: K) throws { try _encodeIfPresent(value, forKey: key) }
98+
// mutating func encodeIfPresent(_ value: UInt16?, forKey key: K) throws { try _encodeIfPresent(value, forKey: key) }
99+
// mutating func encodeIfPresent(_ value: UInt32?, forKey key: K) throws { try _encodeIfPresent(value, forKey: key) }
100+
// mutating func encodeIfPresent(_ value: UInt64?, forKey key: K) throws { try _encodeIfPresent(value, forKey: key) }
101+
// mutating func encodeIfPresent(_ value: Double?, forKey key: K) throws { try _encodeIfPresent(value, forKey: key) }
102+
// mutating func encodeIfPresent(_ value: Float?, forKey key: K) throws { try _encodeIfPresent(value, forKey: key) }
103+
// mutating func encodeIfPresent(_ value: String?, forKey key: K) throws { try _encodeIfPresent(value, forKey: key) }
104+
// mutating func encodeIfPresent<T>(_ value: T?, forKey key: K) throws where T: Encodable { try _encodeIfPresent(value, forKey: key) }
105+
//
106+
// mutating func superEncoder() -> Encoder { return encoder }
107+
// mutating func superEncoder(forKey key: K) -> Encoder { return encoder }
108+
// mutating func _encodeIfPresent<T>(_ value: T?, forKey key: K) throws where T : Encodable {
109+
// if let value = value {
110+
// try encode(value, forKey: key)
111+
// } else {
112+
// if let convertibleType = T.self as? PostgreSQLDataConvertible.Type {
113+
// try set(key, to: PostgreSQLData(null: convertibleType.postgreSQLDataType))
114+
// } else {
115+
// try encodeNil(forKey: key)
116+
// }
117+
// }
118+
// }
119+
//
120+
// mutating func encode<T>(_ value: T, forKey key: K) throws where T: Encodable {
121+
// guard let convertible = value as? PostgreSQLDataConvertible else {
122+
// let type = Swift.type(of: value)
123+
// throw PostgreSQLError(
124+
// identifier: "convertible",
125+
// reason: "Unsupported encodable type: \(type)",
126+
// suggestedFixes: [
127+
// "Conform \(type) to PostgreSQLDataCustomConvertible"
128+
// ]
129+
// )
130+
// }
131+
// try set(key, to: convertible)
132+
// }
133+
// mutating func nestedContainer<NestedKey>(keyedBy keyType: NestedKey.Type, forKey key: K) -> KeyedEncodingContainer<NestedKey> where NestedKey : CodingKey {
134+
// return encoder.container(keyedBy: NestedKey.self)
135+
// }
136+
//
137+
// mutating func nestedUnkeyedContainer(forKey key: K) -> UnkeyedEncodingContainer {
138+
// return encoder.unkeyedContainer()
139+
// }
140+
//}
141+
//
142+
//private func unsupported() -> Never {
143+
// fatalError("""
144+
// PostgreSQL rows only support a flat, keyed structure `[String: T]`.
145+
//
146+
// Query data must be an encodable dictionary, struct, or class.
147+
//
148+
// You can also conform nested types to `PostgreSQLJSONType` or `PostgreSQLArrayType`. (Nested types must be `PostgreSQLDataCustomConvertible`.)
149+
// """)
150+
//}
151+
//

Sources/PostgreSQL/Column/PostgreSQLColumn.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,12 @@ public struct PostgreSQLColumn: Hashable, Equatable {
88
}
99

1010
extension PostgreSQLColumn: CustomStringConvertible {
11+
/// See `CustomStringConvertible`.
1112
public var description: String {
12-
return "<\(tableOID)>.(\(name))"
13+
switch tableOID {
14+
case 0: return tableOID.description + "." + name
15+
default: return name
16+
}
1317
}
1418
}
1519

Sources/PostgreSQL/Column/PostgreSQLColumnType.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,11 @@ extension Query where Database == PostgreSQLDatabase {
7676
return .init(name: "TIMESTAMP")
7777
}
7878

79+
/// `JSON`
80+
public static var json: ColumnType {
81+
return .init(name: "JSON")
82+
}
83+
7984
public enum Default {
8085
case computed(Query.DML.ComputedColumn)
8186
case unescaped(String)

Sources/PostgreSQL/Column/PostgreSQLDataType.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import Foundation
44
/// Use `select * from pg_type where oid = <idhere>;` to lookup more information.
55
public struct PostgreSQLDataType: Codable, Equatable, ExpressibleByIntegerLiteral {
66
/// Recognized types
7+
public static let null = PostgreSQLDataType(0)
78
public static let bool = PostgreSQLDataType(16)
89
public static let bytea = PostgreSQLDataType(17)
910
public static let char = PostgreSQLDataType(18)

Sources/PostgreSQL/Connection/PostgreSQLResultFormat.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/// Specifies result format of `PostgreSQLConnection.query(...)`.
12
public struct PostgreSQLResultFormat {
23
/// The format codes
34
internal let formatCodes: [PostgreSQLMessage.FormatCode]

Sources/PostgreSQL/Data/PostgreSQLData+BinaryFloatingPoint.swift

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,7 @@ extension Data {
7474
extension FloatingPoint {
7575
/// Big-endian bytes for this floating-point number.
7676
internal var data: Data {
77-
var bytes = [UInt8](repeating: 0, count: MemoryLayout<Self>.size)
7877
var copy = self
79-
memcpy(&bytes, &copy, bytes.count)
80-
return Data(bytes.reversed())
78+
return .init(bytes: &copy, count: MemoryLayout<Self>.size)
8179
}
8280
}

Sources/PostgreSQL/Data/PostgreSQLData+Date.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import Foundation
2-
31
extension Date: PostgreSQLDataConvertible {
42
/// See `PostgreSQLDataConvertible`.
53
public static var postgreSQLDataType: PostgreSQLDataType { return .timestamp }
@@ -39,6 +37,8 @@ extension Date: PostgreSQLDataConvertible {
3937
}
4038
}
4139

40+
// MARK: Private
41+
4242
private let _microsecondsPerSecond: Int64 = 1_000_000
4343
private let _secondsInDay: Int32 = 24 * 60 * 60
4444
private let _psqlDateStart = Date(timeIntervalSince1970: 946_684_800) // values are stored as seconds before or after midnight 2000-01-01

Sources/PostgreSQL/Data/PostgreSQLData+Decimal.swift

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import Core
2-
31
extension Decimal: PostgreSQLDataConvertible {
42
/// See `PostgreSQLDataConvertible`
53
public static var postgreSQLDataType: PostgreSQLDataType {

Sources/PostgreSQL/Data/PostgreSQLData+FixedWidthInteger.swift

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -86,16 +86,14 @@ extension Data {
8686
guard count >= (I.bitWidth / 8) else {
8787
throw PostgreSQLError(identifier: "fixedWidthData", reason: "Not enough bytes to decode \(I.self): \(count)/\(I.bitWidth / 8)")
8888
}
89-
return unsafeCast(to: I.self).bigEndian
89+
return unsafeCast(to: I.self)
9090
}
9191
}
9292

9393
extension FixedWidthInteger {
94-
/// Big-endian bytes for this integer.
94+
/// Bytes for this integer.
9595
internal var data: Data {
96-
var bytes = [UInt8](repeating: 0, count: Self.bitWidth / 8)
97-
var intNetwork = bigEndian
98-
memcpy(&bytes, &intNetwork, bytes.count)
99-
return Data(bytes)
96+
var copy = self
97+
return .init(bytes: &copy, count: MemoryLayout<Self>.size)
10098
}
10199
}

0 commit comments

Comments
 (0)