forked from vapor/postgres-nio
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathPostgresData+Array.swift
104 lines (95 loc) · 3.45 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
extension PostgresData {
public init<T>(array: [T])
where T: PostgresDataConvertible
{
let elementType = T.postgresDataType
guard let arrayType = elementType.arrayType else {
fatalError("No array type for \(elementType)")
}
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 var value = item.postgresData?.value {
buffer.writeInteger(numericCast(value.readableBytes), as: UInt32.self)
buffer.writeBuffer(&value)
} else {
buffer.writeInteger(0, as: UInt32.self)
}
}
}
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 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: [T] = []
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)
guard let t = T(postgresData: data) else {
// if we fail to convert any data, fail the entire array
return nil
}
array.append(t)
}
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)
}
}