Skip to content

Commit aa528ef

Browse files
vkilltanner0101
authored andcommitted
Add jsonb support (vapor#36)
* Add jsonb support * Keep JSONEncoder and JSONDecoder error for PostgresData convert * Rename JSONBCodable to PostgresJSONBCodable
1 parent 4c008b8 commit aa528ef

File tree

4 files changed

+110
-0
lines changed

4 files changed

+110
-0
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,8 @@ print(data.uint64) // UInt64?
198198

199199
print(data.bool) // Bool?
200200

201+
print(try data.jsonb(as: Foo.self)) // Foo?
202+
201203
print(data.float) // Float?
202204
print(data.double) // Double?
203205

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import Foundation
2+
3+
fileprivate let jsonBVersionBytes: [UInt8] = [0x01]
4+
5+
extension PostgresData {
6+
public init(jsonb jsonData: Data) {
7+
let jsonBDataBytes = [UInt8](jsonData)
8+
9+
var buffer = ByteBufferAllocator().buffer(capacity: jsonBVersionBytes.count + jsonBDataBytes.count)
10+
buffer.writeBytes(jsonBVersionBytes)
11+
buffer.writeBytes(jsonBDataBytes)
12+
13+
self.init(type: .jsonb, formatCode: .binary, value: buffer)
14+
}
15+
16+
public init<T>(jsonb value: T) throws where T: Encodable {
17+
let jsonData = try JSONEncoder().encode(value)
18+
self.init(jsonb: jsonData)
19+
}
20+
21+
public var jsonb: Data? {
22+
guard var value = self.value else {
23+
return nil
24+
}
25+
26+
guard let versionBytes = value.readBytes(length: jsonBVersionBytes.count), [UInt8](versionBytes) == jsonBVersionBytes else {
27+
return nil
28+
}
29+
30+
guard let dataBytes = value.readBytes(length: value.readableBytes) else {
31+
return nil
32+
}
33+
34+
return Data(dataBytes)
35+
}
36+
37+
public func jsonb<T>(as type: T.Type) throws -> T? where T: Decodable {
38+
guard let jsonData = jsonb else {
39+
return nil
40+
}
41+
42+
return try JSONDecoder().decode(T.self, from: jsonData)
43+
}
44+
}
45+
46+
public protocol PostgresJSONBCodable: Codable, PostgresDataConvertible {
47+
}
48+
49+
extension PostgresJSONBCodable {
50+
public static var postgresDataType: PostgresDataType {
51+
return .jsonb
52+
}
53+
54+
public var postgresData: PostgresData? {
55+
return try? .init(jsonb: self)
56+
}
57+
58+
public init?(postgresData: PostgresData) {
59+
guard let value = try? postgresData.jsonb(as: Self.self) else {
60+
return nil
61+
}
62+
self = value
63+
}
64+
}

Tests/PostgresNIOTests/NIOPostgresTests.swift

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,48 @@ final class NIOPostgresTests: XCTestCase {
387387
}
388388
}
389389

390+
func testJSONBSerialize() throws {
391+
struct Object: Codable {
392+
let foo: Int
393+
let bar: Int
394+
}
395+
396+
let conn = try PostgresConnection.test(on: eventLoop).wait()
397+
defer { try! conn.close().wait() }
398+
do {
399+
let postgresData = try PostgresData(jsonb: Object(foo: 1, bar: 2))
400+
let rows = try conn.query("select $1::jsonb as jsonb", [postgresData]).wait()
401+
402+
let object = try rows[0].column("jsonb")?.jsonb(as: Object.self)
403+
XCTAssertEqual(object?.foo, 1)
404+
XCTAssertEqual(object?.bar, 2)
405+
}
406+
407+
do {
408+
let rows = try conn.query("select jsonb_build_object('foo',1,'bar',2) as jsonb").wait()
409+
410+
let object = try rows[0].column("jsonb")?.jsonb(as: Object.self)
411+
XCTAssertEqual(object?.foo, 1)
412+
XCTAssertEqual(object?.bar, 2)
413+
}
414+
}
415+
416+
func testJSONBConvertible() throws {
417+
struct Object: PostgresJSONBCodable {
418+
let foo: Int
419+
let bar: Int
420+
}
421+
422+
XCTAssertEqual(Object.postgresDataType, .jsonb)
423+
424+
let postgresData = Object(foo: 1, bar: 2).postgresData
425+
XCTAssertEqual(postgresData?.type, .jsonb)
426+
427+
let object = Object(postgresData: postgresData!)
428+
XCTAssertEqual(object?.foo, 1)
429+
XCTAssertEqual(object?.bar, 2)
430+
}
431+
390432
func testRemoteTLSServer() throws {
391433
let url = "postgres://uymgphwj:7_tHbREdRwkqAdu4KoIS7hQnNxr8J1LA@elmer.db.elephantsql.com:5432/uymgphwj"
392434
let elg = MultiThreadedEventLoopGroup(numberOfThreads: 1)

Tests/PostgresNIOTests/XCTestManifests.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ extension NIOPostgresTests {
99
("testAverageLengthNumeric", testAverageLengthNumeric),
1010
("testBindInteger", testBindInteger),
1111
("testBoolSerialize", testBoolSerialize),
12+
("testJSONBSerialize", testJSONBSerialize),
13+
("testJSONBConvertible", testJSONBConvertible),
1214
("testColumnsInJoin", testColumnsInJoin),
1315
("testConnectAndClose", testConnectAndClose),
1416
("testDates", testDates),

0 commit comments

Comments
 (0)