Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,8 @@ print(data.uint64) // UInt64?

print(data.bool) // Bool?

print(try data.jsonb(as: Foo.self)) // Foo?

print(data.float) // Float?
print(data.double) // Double?

Expand Down
64 changes: 64 additions & 0 deletions Sources/PostgresNIO/Data/PostgresData+JSONB.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import Foundation

fileprivate let jsonBVersionBytes: [UInt8] = [0x01]

extension PostgresData {
public init(jsonb jsonData: Data) {
let jsonBDataBytes = [UInt8](jsonData)

var buffer = ByteBufferAllocator().buffer(capacity: jsonBVersionBytes.count + jsonBDataBytes.count)
buffer.writeBytes(jsonBVersionBytes)
buffer.writeBytes(jsonBDataBytes)

self.init(type: .jsonb, formatCode: .binary, value: buffer)
}

public init<T>(jsonb value: T) throws where T: Encodable {
let jsonData = try JSONEncoder().encode(value)
self.init(jsonb: jsonData)
}

public var jsonb: Data? {
guard var value = self.value else {
return nil
}

guard let versionBytes = value.readBytes(length: jsonBVersionBytes.count), [UInt8](versionBytes) == jsonBVersionBytes else {
return nil
}

guard let dataBytes = value.readBytes(length: value.readableBytes) else {
return nil
}

return Data(dataBytes)
}

public func jsonb<T>(as type: T.Type) throws -> T? where T: Decodable {
guard let jsonData = jsonb else {
return nil
}

return try JSONDecoder().decode(T.self, from: jsonData)
}
}

public protocol PostgresJSONBCodable: Codable, PostgresDataConvertible {
}

extension PostgresJSONBCodable {
public static var postgresDataType: PostgresDataType {
return .jsonb
}

public var postgresData: PostgresData? {
return try? .init(jsonb: self)
}

public init?(postgresData: PostgresData) {
guard let value = try? postgresData.jsonb(as: Self.self) else {
return nil
}
self = value
}
}
42 changes: 42 additions & 0 deletions Tests/PostgresNIOTests/NIOPostgresTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,48 @@ final class NIOPostgresTests: XCTestCase {
}
}

func testJSONBSerialize() throws {
struct Object: Codable {
let foo: Int
let bar: Int
}

let conn = try PostgresConnection.test(on: eventLoop).wait()
defer { try! conn.close().wait() }
do {
let postgresData = try PostgresData(jsonb: Object(foo: 1, bar: 2))
let rows = try conn.query("select $1::jsonb as jsonb", [postgresData]).wait()

let object = try rows[0].column("jsonb")?.jsonb(as: Object.self)
XCTAssertEqual(object?.foo, 1)
XCTAssertEqual(object?.bar, 2)
}

do {
let rows = try conn.query("select jsonb_build_object('foo',1,'bar',2) as jsonb").wait()

let object = try rows[0].column("jsonb")?.jsonb(as: Object.self)
XCTAssertEqual(object?.foo, 1)
XCTAssertEqual(object?.bar, 2)
}
}

func testJSONBConvertible() throws {
struct Object: PostgresJSONBCodable {
let foo: Int
let bar: Int
}

XCTAssertEqual(Object.postgresDataType, .jsonb)

let postgresData = Object(foo: 1, bar: 2).postgresData
XCTAssertEqual(postgresData?.type, .jsonb)

let object = Object(postgresData: postgresData!)
XCTAssertEqual(object?.foo, 1)
XCTAssertEqual(object?.bar, 2)
}

func testRemoteTLSServer() throws {
let url = "postgres://uymgphwj:[email protected]:5432/uymgphwj"
let elg = MultiThreadedEventLoopGroup(numberOfThreads: 1)
Expand Down
2 changes: 2 additions & 0 deletions Tests/PostgresNIOTests/XCTestManifests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ extension NIOPostgresTests {
("testAverageLengthNumeric", testAverageLengthNumeric),
("testBindInteger", testBindInteger),
("testBoolSerialize", testBoolSerialize),
("testJSONBSerialize", testJSONBSerialize),
("testJSONBConvertible", testJSONBConvertible),
("testColumnsInJoin", testColumnsInJoin),
("testConnectAndClose", testConnectAndClose),
("testDates", testDates),
Expand Down