Skip to content

Commit 44d8d76

Browse files
gebit-costantinomp911de
authored andcommitted
Fix NumericDecodeUtils.decodeBinary(byteBuf) decoding.
Previously, decodeBinary returned a wrong result for BigDecimal between 1 and -1. Now the decoding is implemented as per the Postgres spec. [#558][resolves #556]
1 parent 569d303 commit 44d8d76

File tree

2 files changed

+97
-31
lines changed

2 files changed

+97
-31
lines changed

src/main/java/io/r2dbc/postgresql/codec/NumericDecodeUtils.java

Lines changed: 10 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import reactor.util.annotation.Nullable;
2424

2525
import java.math.BigDecimal;
26+
import java.math.RoundingMode;
2627

2728
import static io.r2dbc.postgresql.message.Format.FORMAT_BINARY;
2829

@@ -110,40 +111,18 @@ public static BigDecimal decodeBinary(ByteBuf byteBuf) {
110111
digits[i] = byteBuf.readShort();
111112
}
112113

113-
StringBuilder builder = new StringBuilder();
114-
// whole part
115-
builder.append(digits[0]);
116-
for (short i = 0; i < weight * 4; i++) {
117-
builder.append(0);
114+
StringBuilder sb = new StringBuilder();
115+
if (sign != 0) {
116+
sb.append("-");
118117
}
119-
// decimal part
120-
if (scale > 0) {
121-
builder.append('.');
122-
for (short i = 0; i < scale; i++) {
123-
builder.append(0);
124-
}
118+
sb.append("0.");
119+
for (short digit : digits) {
120+
sb.append(String.format("%04d", digit));
125121
}
126122

127-
int expectedLength = builder.length();
128-
int baseOffset = Short.toString(digits[0]).length();
129-
130-
for (short i = 1; i < numOfDigits; i++) {
131-
weight--;
132-
String temp = Short.toString(digits[i]);
133-
int offset = baseOffset + 4 * i - temp.length();
134-
if (weight < 0) {
135-
offset++; // dot between whole and decimal parts
136-
}
137-
builder.replace(offset, offset + temp.length(), temp);
138-
}
139-
140-
builder.setLength(expectedLength); // remove zeros from the end
141-
142-
if (sign == 0) {
143-
return new BigDecimal(builder.toString());
144-
} else {
145-
return new BigDecimal("-" + builder.toString());
146-
}
123+
return new BigDecimal(sb.toString())
124+
.movePointRight((weight + 1) * 4)
125+
.setScale(scale, RoundingMode.DOWN);
147126
}
148127

149128
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
package io.r2dbc.postgresql.codec;
2+
3+
import io.netty.buffer.ByteBuf;
4+
import io.r2dbc.postgresql.util.TestByteBufAllocator;
5+
import org.junit.jupiter.params.ParameterizedTest;
6+
import org.junit.jupiter.params.provider.MethodSource;
7+
import org.postgresql.util.ByteConverter;
8+
9+
import java.math.BigDecimal;
10+
import java.math.BigInteger;
11+
import java.math.RoundingMode;
12+
import java.util.stream.Stream;
13+
14+
import static org.assertj.core.api.Assertions.assertThat;
15+
16+
class NumericDecodeUtilsTest {
17+
18+
static Stream<Object[]> bigDecimalValues() {
19+
return Stream.of(
20+
new Object[]{new BigDecimal("0.1")},
21+
new Object[]{new BigDecimal("0.10")},
22+
new Object[]{new BigDecimal("0.01")},
23+
new Object[]{new BigDecimal("0.001")},
24+
new Object[]{new BigDecimal("0.0001")},
25+
new Object[]{new BigDecimal("0.00001")},
26+
new Object[]{new BigDecimal("-0.1")},
27+
new Object[]{new BigDecimal("-0.10")},
28+
new Object[]{new BigDecimal("-0.01")},
29+
new Object[]{new BigDecimal("-0.002")},
30+
new Object[]{new BigDecimal("-0.0033")},
31+
new Object[]{new BigDecimal("-0.004343")},
32+
new Object[]{new BigDecimal("1.0")},
33+
new Object[]{new BigDecimal("0.000000000000000000000000000000000000000000000000000")},
34+
new Object[]{new BigDecimal("0.100000000000000000000000000000000000000000000009900")},
35+
new Object[]{new BigDecimal("-1.0")},
36+
new Object[]{new BigDecimal("-1")},
37+
new Object[]{new BigDecimal("1.2")},
38+
new Object[]{new BigDecimal("-2.05")},
39+
new Object[]{new BigDecimal("0.000000000000000000000000000990")},
40+
new Object[]{new BigDecimal("-0.000000000000000000000000000990")},
41+
new Object[]{new BigDecimal("10.0000000000099")},
42+
new Object[]{new BigDecimal(".10000000000000")},
43+
new Object[]{new BigDecimal("1.10000000000000")},
44+
new Object[]{new BigDecimal("99999.2")},
45+
new Object[]{new BigDecimal("99999")},
46+
new Object[]{new BigDecimal("-99999.2")},
47+
new Object[]{new BigDecimal("-99999")},
48+
new Object[]{new BigDecimal("2147483647")},
49+
new Object[]{new BigDecimal("-2147483648")},
50+
new Object[]{new BigDecimal("2147483648")},
51+
new Object[]{new BigDecimal("-2147483649")},
52+
new Object[]{new BigDecimal("9223372036854775807")},
53+
new Object[]{new BigDecimal("-9223372036854775808")},
54+
new Object[]{new BigDecimal("9223372036854775808")},
55+
new Object[]{new BigDecimal("-9223372036854775809")},
56+
new Object[]{new BigDecimal("10223372036850000000")},
57+
new Object[]{new BigDecimal("19223372036854775807")},
58+
new Object[]{new BigDecimal("19223372036854775807.300")},
59+
new Object[]{new BigDecimal("-19223372036854775807.300")},
60+
new Object[]{new BigDecimal(BigInteger.valueOf(1234567890987654321L), -1)},
61+
new Object[]{new BigDecimal(BigInteger.valueOf(1234567890987654321L), -5)},
62+
new Object[]{new BigDecimal(BigInteger.valueOf(-1234567890987654321L), -3)},
63+
new Object[]{new BigDecimal(BigInteger.valueOf(6), -8)},
64+
new Object[]{new BigDecimal("30000")},
65+
new Object[]{new BigDecimal("40000").setScale(15, RoundingMode.UNNECESSARY)},
66+
new Object[]{new BigDecimal("20000.000000000000000000")},
67+
new Object[]{new BigDecimal("9990000").setScale(8, RoundingMode.UNNECESSARY)},
68+
new Object[]{new BigDecimal("1000000").setScale(31, RoundingMode.UNNECESSARY)},
69+
new Object[]{new BigDecimal("10000000000000000000000000000000000000").setScale(14, RoundingMode.UNNECESSARY)},
70+
new Object[]{new BigDecimal("90000000000000000000000000000000000000")},
71+
new Object[]{new BigDecimal("1234567890.12")},
72+
new Object[]{new BigDecimal("-3.141592653590")},
73+
new Object[]{new BigDecimal("3.141592653590")},
74+
new Object[]{new BigDecimal("-0.141592653590")},
75+
new Object[]{new BigDecimal("0.141592653590")}
76+
);
77+
}
78+
79+
@MethodSource("bigDecimalValues")
80+
@ParameterizedTest
81+
void decodeBinary2(BigDecimal value) {
82+
ByteBuf byteBuf = TestByteBufAllocator.TEST.buffer();
83+
byteBuf.writeBytes(ByteConverter.numeric(value));
84+
assertThat(NumericDecodeUtils.decodeBinary(byteBuf)).isEqualByComparingTo(value);
85+
}
86+
87+
}

0 commit comments

Comments
 (0)