forked from vapor/postgres-nio
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathPostgresData+Array.swift
130 lines (120 loc) · 3.99 KB
/
PostgresData+Array.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
129
130
extension PostgresData {
public init<T>(array: [T])
where T: PostgresDataConvertible
{
self.init(
array: array.map { $0.postgresData },
elementType: T.postgresDataType
)
}
public init(array: [PostgresData?], elementType: PostgresDataType) {
var buffer = ByteBufferAllocator().buffer(capacity: 0)
// 0 if empty, 1 if not
buffer.writeInteger(array.isEmpty ? 0 : 1, as: UInt32.self)
// b
buffer.writeInteger(0, as: UInt32.self)
// array element type
buffer.writeInteger(elementType.rawValue)
// continue if the array is not empty
if !array.isEmpty {
// length of array
buffer.writeInteger(numericCast(array.count), as: UInt32.self)
// dimensions
buffer.writeInteger(1, as: UInt32.self)
for item in array {
if let item = item, var value = item.value {
buffer.writeInteger(numericCast(value.readableBytes), as: UInt32.self)
buffer.writeBuffer(&value)
} else {
buffer.writeInteger(0, as: UInt32.self)
}
}
}
guard let arrayType = elementType.arrayType else {
fatalError("No array type for \(elementType)")
}
self.init(
type: arrayType,
typeModifier: nil,
formatCode: .binary,
value: buffer
)
}
public func array<T>(of type: T.Type = T.self) -> [T]?
where T: PostgresDataConvertible
{
guard let array = self.array else {
return nil
}
var items: [T] = []
for data in array {
guard let item = T(postgresData: data) else {
// if we fail to convert any data, fail the entire array
return nil
}
items.append(item)
}
return items
}
public var array: [PostgresData]? {
guard var value = self.value else {
return nil
}
// ensures the data type is actually an array
guard self.type.elementType != nil else {
return nil
}
guard let isNotEmpty = value.readInteger(as: UInt32.self) else {
return nil
}
guard let b = value.readInteger(as: UInt32.self) else {
return nil
}
assert(b == 0, "Array b field did not equal zero")
guard let type = value.readInteger(as: PostgresDataType.self) else {
return nil
}
guard isNotEmpty == 1 else {
return []
}
guard let length = value.readInteger(as: UInt32.self) else {
return nil
}
assert(length >= 0, "Invalid length")
guard let dimensions = value.readInteger(as: UInt32.self) else {
return nil
}
assert(dimensions == 1, "Multi-dimensional arrays not yet supported")
var array: [PostgresData] = []
while
let itemLength = value.readInteger(as: UInt32.self),
let itemValue = value.readSlice(length: numericCast(itemLength))
{
let data = PostgresData(
type: type,
typeModifier: nil,
formatCode: self.formatCode,
value: itemValue
)
array.append(data)
}
return array
}
}
extension Array: PostgresDataConvertible where Element: PostgresDataConvertible {
public static var postgresDataType: PostgresDataType {
guard let arrayType = Element.postgresDataType.arrayType else {
fatalError("No array type for \(Element.postgresDataType)")
}
return arrayType
}
public init?(postgresData: PostgresData) {
guard let array = postgresData.array(of: Element.self) else {
return nil
}
self = array
}
public var postgresData: PostgresData? {
return PostgresData(array: self)
}
}