Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Postgres inet type #413

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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
66 changes: 66 additions & 0 deletions Sources/PostgresNIO/New/Data/PostgresINET+PostgresCodable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
public struct PostgresINET {
public let ipFamily: UInt8
public let netmaskLength: UInt8
public let isCIDR: Bool
public let addressLength: UInt8
public let ipAddress: [UInt8]
}
Comment on lines +1 to +7
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is too close to the on the wire representation. In an ideal case this would be closer to the semantical meaning:

struct PostgresIPv4 {
  var value: (UInt8, UInt8, UInt8, UInt8)
}

struct PostgresIPv6 {
  var value: (UInt16, UInt16, UInt16, UInt16, UInt16, UInt16, UInt16, UInt16)
}

enum PostgresINET {
  case ipv4(PostgresIPv4, networkMask: UInt8?)
  case ipv4(PostgresIPv6, networkMask: UInt32?)
}

Since there are no currency types for this in Swift, we need to have our own minimal type for this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@fabianfett Without an explicit isCIDR property, would it be safe to infer that an instance uses CIDR notation based on whether the IPv4 network mask is 32 (or IPv6 network mask is 128)?


extension PostgresINET: PostgresDecodable {
public init<JSONDecoder: PostgresJSONDecoder>(from byteBuffer: inout ByteBuffer, type: PostgresDataType, format: PostgresFormat, context: PostgresDecodingContext<JSONDecoder>) throws {
// IP family
guard let ipFamily: UInt8 = byteBuffer.readInteger(as: UInt8.self) else {
throw PostgresDecodingError.Code.failure
}

// netmask length in bits
guard let netmaskLength: UInt8 = byteBuffer.readInteger(as: UInt8.self) else {
throw PostgresDecodingError.Code.failure
}

// whether it is a CIDR
let isCIDR: Bool
switch byteBuffer.readInteger(as: UInt8.self) {
case .some(0):
isCIDR = false
case .some(1):
isCIDR = true
default:
throw PostgresDecodingError.Code.failure
}

// address length in bytes
guard let addressLength: UInt8 = byteBuffer.readInteger(as: UInt8.self),
addressLength * 8 == netmaskLength,
let ipAddress: [UInt8] = byteBuffer.readBytes(length: Int(addressLength))
else {
throw PostgresDecodingError.Code.failure
}

self.init(
ipFamily: ipFamily,
netmaskLength: netmaskLength,
isCIDR: isCIDR,
addressLength: addressLength,
ipAddress: ipAddress
)
}
}

extension PostgresINET: PostgresEncodable & PostgresNonThrowingEncodable {
public static var psqlType: PostgresDataType { return .inet }
public static var psqlFormat: PostgresFormat { .binary }
public func encode<JSONEncoder: PostgresJSONEncoder>(into byteBuffer: inout ByteBuffer, context: PostgresEncodingContext<JSONEncoder>) {
byteBuffer.writeInteger(self.ipFamily, as: UInt8.self)
byteBuffer.writeInteger(self.netmaskLength, as: UInt8.self)
byteBuffer.writeInteger(self.isCIDR ? 1 : 0, as: UInt8.self)
byteBuffer.writeInteger(self.addressLength, as: UInt8.self)
byteBuffer.writeBytes(self.ipAddress)
}
}

extension PostgresINET: PostgresArrayDecodable {}

extension PostgresINET: PostgresArrayEncodable {
public static var psqlArrayType: PostgresDataType { return .inetArray }
}