forked from vapor/postgres-kit
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathPostgreSQLArrayCustomConvertible.swift
128 lines (104 loc) · 4.39 KB
/
PostgreSQLArrayCustomConvertible.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
import Foundation
/// Representable by a `T[]` column on the PostgreSQL database.
public protocol PostgreSQLArrayCustomConvertible: PostgreSQLDataCustomConvertible, Codable {
/// The associated array element type
associatedtype PostgreSQLArrayElement: PostgreSQLDataCustomConvertible
/// Convert an array of elements to self.
static func convertFromPostgreSQLArray(_ data: [PostgreSQLArrayElement]) -> Self
/// Convert self to an array of elements.
func convertToPostgreSQLArray() -> [PostgreSQLArrayElement]
}
extension PostgreSQLArrayCustomConvertible {
/// See `PostgreSQLDataCustomConvertible.postgreSQLDataType`
public static var postgreSQLDataType: PostgreSQLDataType {
return PostgreSQLArrayElement.postgreSQLDataArrayType
}
/// See `PostgreSQLDataCustomConvertible.convertFromPostgreSQLData(_:)`
public static func convertFromPostgreSQLData(_ data: PostgreSQLData) throws -> Self {
guard var value = data.data else {
throw PostgreSQLError(identifier: "nullArray", reason: "Unable to decode PostgreSQL array from `null` data.")
}
/// Extract and convert each element.
var array: [PostgreSQLArrayElement] = []
let hasData = value.extract(Int32.self).bigEndian
if hasData == 1 {
/// grab the array metadata from the beginning of the data
let metadata = value.extract(PostgreSQLArrayMetadata.self)
for _ in 0..<metadata.count {
let count = Int(value.extract(Int32.self).bigEndian)
let subValue = value.extract(count: count)
let psqlData = PostgreSQLData(type: metadata.type, format: data.format, data: subValue)
let element = try PostgreSQLArrayElement.convertFromPostgreSQLData(psqlData)
array.append(element)
}
} else {
array = []
}
return convertFromPostgreSQLArray(array)
}
/// See `PostgreSQLDataCustomConvertible.convertToPostgreSQLData()`
public func convertToPostgreSQLData() throws -> PostgreSQLData {
let elements = try convertToPostgreSQLArray().map {
try $0.convertToPostgreSQLData()
}
var data = Data()
data += Int32(1).data // non-null
data += Int32(0).data // b
data += PostgreSQLArrayElement.postgreSQLDataType.raw.data // type
data += Int32(elements.count).data // length
data += Int32(1).data // dimensions
for element in elements {
if let value = element.data {
data += Int32(value.count).data
data += value
} else {
data += Int32(0).data
}
}
return PostgreSQLData(type: PostgreSQLArrayElement.postgreSQLDataArrayType, format: .binary, data: data)
}
}
fileprivate struct PostgreSQLArrayMetadata {
/// Unknown
private let _b: Int32
/// The big-endian array element type
private let _type: Int32
/// The big-endian length of the array
private let _count: Int32
/// The big-endian number of dimensions
private let _dimensions: Int32
/// Converts the raw array elemetn type to DataType
var type: PostgreSQLDataType {
return .init(_type.bigEndian)
}
/// The length of the array
var count: Int32 {
return _count.bigEndian
}
/// The number of dimensions
var dimensions: Int32 {
return _dimensions.bigEndian
}
}
extension PostgreSQLArrayMetadata: CustomStringConvertible {
/// See `CustomStringConvertible.description`
var description: String {
return "\(type)[\(count)]"
}
}
extension Array: PostgreSQLArrayCustomConvertible where Element: Codable, Element: PostgreSQLDataCustomConvertible {
/// See `PostgreSQLArrayCustomConvertible.postgreSQLDataArrayType`
public static var postgreSQLDataArrayType: PostgreSQLDataType {
return Element.postgreSQLDataArrayType
}
/// See `PostgreSQLArrayCustomConvertible.PostgreSQLArrayElement`
public typealias PostgreSQLArrayElement = Element
/// See `PostgreSQLArrayCustomConvertible.convertFromPostgreSQLArray(_:)`
public static func convertFromPostgreSQLArray(_ data: [Element]) -> Array<Element> {
return data
}
/// See `PostgreSQLArrayCustomConvertible.convertToPostgreSQLArray(_:)`
public func convertToPostgreSQLArray() -> [Element] {
return self
}
}