Skip to content

Commit ab37f88

Browse files
committed
- add support for decimal types other than Foundation.Decimal
1 parent aa9273c commit ab37f88

File tree

4 files changed

+65
-57
lines changed

4 files changed

+65
-57
lines changed

Sources/PostgresNIO/Data/PostgresData+Decimal.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,6 @@ extension Decimal: PostgresDataConvertible {
3030
}
3131

3232
public var postgresData: PostgresData? {
33-
return .init(numeric: PostgresNumeric(decimal: self))
33+
return .init(numeric: PostgresNumeric(decimalString: self.description))
3434
}
3535
}

Sources/PostgresNIO/Data/PostgresData+Numeric.swift

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import NIOCore
2-
import struct Foundation.Decimal
32

43
public struct PostgresNumeric: CustomStringConvertible, CustomDebugStringConvertible, ExpressibleByStringLiteral {
54
/// The number of digits after this metadata
@@ -37,12 +36,10 @@ public struct PostgresNumeric: CustomStringConvertible, CustomDebugStringConvert
3736
return Double(self.string)
3837
}
3938

40-
public init(decimal: Decimal) {
41-
self.init(decimalString: decimal.description)
42-
}
43-
4439
public init?(string: String) {
4540
// validate string contents are decimal
41+
// TODO: this won't work for all Big decimals
42+
// TODO: how does this handle Nan and Infinity
4643
guard Double(string) != nil else {
4744
return nil
4845
}
@@ -117,12 +114,6 @@ public struct PostgresNumeric: CustomStringConvertible, CustomDebugStringConvert
117114
self.dscale = numericCast(dscale)
118115
self.value = buffer
119116
}
120-
121-
public var decimal: Decimal {
122-
// force cast should always succeed since we know
123-
// string returns a valid decimal
124-
return Decimal(string: self.string)!
125-
}
126117

127118
public var string: String {
128119
guard self.ndigits > 0 else {
Lines changed: 3 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,7 @@
1-
import NIOCore
21
import struct Foundation.Decimal
32

4-
extension Decimal: PostgresEncodable {
5-
public static var psqlType: PostgresDataType {
6-
.numeric
7-
}
8-
9-
public static var psqlFormat: PostgresFormat {
10-
.binary
11-
}
12-
13-
public func encode<JSONEncoder: PostgresJSONEncoder>(
14-
into byteBuffer: inout ByteBuffer,
15-
context: PostgresEncodingContext<JSONEncoder>
16-
) {
17-
let numeric = PostgresNumeric(decimal: self)
18-
byteBuffer.writeInteger(numeric.ndigits)
19-
byteBuffer.writeInteger(numeric.weight)
20-
byteBuffer.writeInteger(numeric.sign)
21-
byteBuffer.writeInteger(numeric.dscale)
22-
var value = numeric.value
23-
byteBuffer.writeBuffer(&value)
24-
}
25-
}
26-
27-
extension Decimal: PostgresDecodable {
28-
public init<JSONDecoder: PostgresJSONDecoder>(
29-
from buffer: inout ByteBuffer,
30-
type: PostgresDataType,
31-
format: PostgresFormat,
32-
context: PostgresDecodingContext<JSONDecoder>
33-
) throws {
34-
switch (format, type) {
35-
case (.binary, .numeric):
36-
guard let numeric = PostgresNumeric(buffer: &buffer) else {
37-
throw PostgresDecodingError.Code.failure
38-
}
39-
self = numeric.decimal
40-
case (.text, .numeric):
41-
guard let string = buffer.readString(length: buffer.readableBytes), let value = Decimal(string: string) else {
42-
throw PostgresDecodingError.Code.failure
43-
}
44-
self = value
45-
default:
46-
throw PostgresDecodingError.Code.typeMismatch
47-
}
3+
extension Decimal: ExpressibleByPostgresFloatingPointString {
4+
public init?(floatingPointString: String) {
5+
self.init(string: floatingPointString)
486
}
497
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import NIOCore
2+
3+
4+
/// This protocol allows using various implementations of a Decimal type for Postgres NUMERIC type
5+
public protocol ExpressibleByPostgresFloatingPointString: PostgresEncodable, PostgresDecodable {
6+
static var psqlType: PostgresDataType { get }
7+
static var psqlFormat: PostgresFormat { get }
8+
9+
init?(floatingPointString: String)
10+
var description: String { get }
11+
}
12+
13+
extension ExpressibleByPostgresFloatingPointString {
14+
public static var psqlType: PostgresDataType {
15+
.numeric
16+
}
17+
18+
public static var psqlFormat: PostgresFormat {
19+
.binary
20+
}
21+
22+
// PostgresEncodable conformance
23+
public func encode<JSONEncoder: PostgresJSONEncoder>(
24+
into byteBuffer: inout ByteBuffer,
25+
context: PostgresEncodingContext<JSONEncoder>
26+
) {
27+
let numeric = PostgresNumeric(decimalString: self.description)
28+
byteBuffer.writeInteger(numeric.ndigits)
29+
byteBuffer.writeInteger(numeric.weight)
30+
byteBuffer.writeInteger(numeric.sign)
31+
byteBuffer.writeInteger(numeric.dscale)
32+
var value = numeric.value
33+
byteBuffer.writeBuffer(&value)
34+
}
35+
36+
// PostgresDecodable conformance
37+
public init<JSONDecoder: PostgresJSONDecoder>(
38+
from buffer: inout ByteBuffer,
39+
type: PostgresDataType,
40+
format: PostgresFormat,
41+
context: PostgresDecodingContext<JSONDecoder>
42+
) throws {
43+
switch (format, type) {
44+
case (.binary, .numeric):
45+
guard let numeric = PostgresNumeric(buffer: &buffer) else {
46+
throw PostgresDecodingError.Code.failure
47+
}
48+
// numeric.string is valid decimal representation
49+
self = Self(floatingPointString: numeric.string)!
50+
case (.text, .numeric):
51+
guard let string = buffer.readString(length: buffer.readableBytes), let value = Self(floatingPointString: string) else {
52+
throw PostgresDecodingError.Code.failure
53+
}
54+
self = value
55+
default:
56+
throw PostgresDecodingError.Code.typeMismatch
57+
}
58+
}
59+
}

0 commit comments

Comments
 (0)