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 decimal types other than Foundation.Decimal #377

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
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: 1 addition & 1 deletion Sources/PostgresNIO/Data/PostgresData+Decimal.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,6 @@ extension Decimal: PostgresDataConvertible {
}

public var postgresData: PostgresData? {
return .init(numeric: PostgresNumeric(decimal: self))
return .init(numeric: PostgresNumeric(decimalString: self.description))
}
}
5 changes: 4 additions & 1 deletion Sources/PostgresNIO/Data/PostgresData+Numeric.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,11 @@ public struct PostgresNumeric: CustomStringConvertible, CustomDebugStringConvert
public init(decimal: Decimal) {
self.init(decimalString: decimal.description)
}

public init?(string: String) {
// validate string contents are decimal
// TODO: this won't work for all Big decimals
// TODO: how does this handle Nan and Infinity
guard Double(string) != nil else {
return nil
}
Expand Down Expand Up @@ -117,6 +119,7 @@ public struct PostgresNumeric: CustomStringConvertible, CustomDebugStringConvert
self.dscale = numericCast(dscale)
self.value = buffer
}


public var decimal: Decimal {
// force cast should always succeed since we know
Expand Down
48 changes: 3 additions & 45 deletions Sources/PostgresNIO/New/Data/Decimal+PostgresCodable.swift
Original file line number Diff line number Diff line change
@@ -1,49 +1,7 @@
import NIOCore
import struct Foundation.Decimal

extension Decimal: PostgresEncodable {
public static var psqlType: PostgresDataType {
.numeric
}

public static var psqlFormat: PostgresFormat {
.binary
}

public func encode<JSONEncoder: PostgresJSONEncoder>(
into byteBuffer: inout ByteBuffer,
context: PostgresEncodingContext<JSONEncoder>
) {
let numeric = PostgresNumeric(decimal: self)
byteBuffer.writeInteger(numeric.ndigits)
byteBuffer.writeInteger(numeric.weight)
byteBuffer.writeInteger(numeric.sign)
byteBuffer.writeInteger(numeric.dscale)
var value = numeric.value
byteBuffer.writeBuffer(&value)
}
}

extension Decimal: PostgresDecodable {
public init<JSONDecoder: PostgresJSONDecoder>(
from buffer: inout ByteBuffer,
type: PostgresDataType,
format: PostgresFormat,
context: PostgresDecodingContext<JSONDecoder>
) throws {
switch (format, type) {
case (.binary, .numeric):
guard let numeric = PostgresNumeric(buffer: &buffer) else {
throw PostgresDecodingError.Code.failure
}
self = numeric.decimal
case (.text, .numeric):
guard let string = buffer.readString(length: buffer.readableBytes), let value = Decimal(string: string) else {
throw PostgresDecodingError.Code.failure
}
self = value
default:
throw PostgresDecodingError.Code.typeMismatch
}
extension Decimal: ExpressibleByPostgresFloatingPointString {
public init?(floatingPointString: String) {
self.init(string: floatingPointString)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import NIOCore


/// This protocol allows using various implementations of a Decimal type for Postgres NUMERIC type
public protocol ExpressibleByPostgresFloatingPointString: PostgresEncodable, PostgresDecodable {
static var psqlType: PostgresDataType { get }
static var psqlFormat: PostgresFormat { get }

init?(floatingPointString: String)
var description: String { get }
}

extension ExpressibleByPostgresFloatingPointString {
public static var psqlType: PostgresDataType {
.numeric
}

public static var psqlFormat: PostgresFormat {
.binary
}

// PostgresEncodable conformance
public func encode<JSONEncoder: PostgresJSONEncoder>(
into byteBuffer: inout ByteBuffer,
context: PostgresEncodingContext<JSONEncoder>
) {
let numeric = PostgresNumeric(decimalString: self.description)
byteBuffer.writeInteger(numeric.ndigits)
byteBuffer.writeInteger(numeric.weight)
byteBuffer.writeInteger(numeric.sign)
byteBuffer.writeInteger(numeric.dscale)
var value = numeric.value
byteBuffer.writeBuffer(&value)
}

// PostgresDecodable conformance
public init<JSONDecoder: PostgresJSONDecoder>(
from buffer: inout ByteBuffer,
type: PostgresDataType,
format: PostgresFormat,
context: PostgresDecodingContext<JSONDecoder>
) throws {
switch (format, type) {
case (.binary, .numeric):
guard let numeric = PostgresNumeric(buffer: &buffer) else {
throw PostgresDecodingError.Code.failure
}
// numeric.string is valid decimal representation
self = Self(floatingPointString: numeric.string)!
case (.text, .numeric):
guard let string = buffer.readString(length: buffer.readableBytes), let value = Self(floatingPointString: string) else {
throw PostgresDecodingError.Code.failure
}
self = value
default:
throw PostgresDecodingError.Code.typeMismatch
}
}
}
Loading