Skip to content

Commit 12cb4f5

Browse files
committed
When generating a string representation of a PostgresNumeric, we were misinterpreting the weight value for values with very small fractional parts. This caused decoding as Decimal to return incorrect values. Thanks to @patchthecode for reporting this issue!
1 parent db1eae1 commit 12cb4f5

File tree

3 files changed

+35
-5
lines changed

3 files changed

+35
-5
lines changed

Sources/PostgresNIO/Data/PostgresData+Numeric.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import struct Foundation.Decimal
44
public struct PostgresNumeric: CustomStringConvertible, CustomDebugStringConvertible, ExpressibleByStringLiteral {
55
/// The number of digits after this metadata
66
internal var ndigits: Int16
7-
/// How many of the digits are before the decimal point (always add 1)
7+
/// How many positions before or after the deicmal point the value is offset by
88
internal var weight: Int16
99
/// If 0x4000, this number is negative. See NUMERIC_NEG in
1010
/// https://github.com/postgres/postgres/blob/master/src/backend/utils/adt/numeric.c
@@ -159,8 +159,10 @@ public struct PostgresNumeric: CustomStringConvertible, CustomDebugStringConvert
159159
let offset: Int16
160160
if self.weight > 0 {
161161
offset = (self.weight + 1) - self.ndigits
162+
} else if self.weight < 0 {
163+
offset = self.ndigits - (abs(self.weight) + 1)
162164
} else {
163-
offset = abs(self.weight) - self.ndigits
165+
offset = 0
164166
}
165167
if offset > 0 {
166168
for _ in 0..<offset {

Tests/IntegrationTests/PostgresNIOTests.swift

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -619,24 +619,52 @@ final class PostgresNIOTests: XCTestCase {
619619
let b = PostgresNumeric(string: "-123456.789123")!
620620
let c = PostgresNumeric(string: "3.14159265358979")!
621621
let d = PostgresNumeric(string: "1234567898765")!
622+
let e = PostgresNumeric(string: "0.00000080216390553684")!
623+
let f = PostgresNumeric(string: "0.0000080216390553684")!
624+
let g = PostgresNumeric(string: "0.000080216390553684")!
625+
let h = PostgresNumeric(string: "802163905536840000.0")!
626+
let i = PostgresNumeric(string: "8021639055368400000.0")!
627+
let j = PostgresNumeric(string: "80216390553684000000.0")!
628+
let k = PostgresNumeric(string: "802163905536840000.000080216390553684")!
622629
var rows: PostgresQueryResult?
623630
XCTAssertNoThrow(rows = try conn?.query("""
624631
select
625632
$1::numeric as a,
626633
$2::numeric as b,
627634
$3::numeric as c,
628-
$4::numeric as d
635+
$4::numeric as d,
636+
$5::numeric as e,
637+
$6::numeric as f,
638+
$7::numeric as g,
639+
$8::numeric as h,
640+
$9::numeric as i,
641+
$10::numeric as j,
642+
$11::numeric as k
629643
""", [
630644
.init(numeric: a),
631645
.init(numeric: b),
632646
.init(numeric: c),
633-
.init(numeric: d)
647+
.init(numeric: d),
648+
.init(numeric: e),
649+
.init(numeric: f),
650+
.init(numeric: g),
651+
.init(numeric: h),
652+
.init(numeric: i),
653+
.init(numeric: j),
654+
.init(numeric: k)
634655
]).wait())
635656
let row = rows?.first?.makeRandomAccess()
636657
XCTAssertEqual(row?[data: "a"].decimal, Decimal(string: "123456.789123")!)
637658
XCTAssertEqual(row?[data: "b"].decimal, Decimal(string: "-123456.789123")!)
638659
XCTAssertEqual(row?[data: "c"].decimal, Decimal(string: "3.14159265358979")!)
639660
XCTAssertEqual(row?[data: "d"].decimal, Decimal(string: "1234567898765")!)
661+
XCTAssertEqual(row?[data: "e"].decimal, Decimal(string: "0.00000080216390553684")!)
662+
XCTAssertEqual(row?[data: "f"].decimal, Decimal(string: "0.0000080216390553684")!)
663+
XCTAssertEqual(row?[data: "g"].decimal, Decimal(string: "0.000080216390553684")!)
664+
XCTAssertEqual(row?[data: "h"].decimal, Decimal(string: "802163905536840000.0")!)
665+
XCTAssertEqual(row?[data: "i"].decimal, Decimal(string: "8021639055368400000.0")!)
666+
XCTAssertEqual(row?[data: "j"].decimal, Decimal(string: "80216390553684000000.0")!)
667+
XCTAssertEqual(row?[data: "k"].decimal, Decimal(string: "802163905536840000.000080216390553684")!)
640668
}
641669

642670
func testDecimalStringSerialization() {

Tests/PostgresNIOTests/New/Data/Decimal+PSQLCodableTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import NIOCore
55
class Decimal_PSQLCodableTests: XCTestCase {
66

77
func testRoundTrip() {
8-
let values: [Decimal] = [1.1, .pi, -5e-12]
8+
let values: [Decimal] = [1.1, .pi, -5e-12, 0.00000080216390553684]
99

1010
for value in values {
1111
var buffer = ByteBuffer()

0 commit comments

Comments
 (0)