]>
Commit | Line | Data |
---|---|---|
1d09f67e TL |
1 | /* |
2 | * Licensed to the Apache Software Foundation (ASF) under one or more | |
3 | * contributor license agreements. See the NOTICE file distributed with | |
4 | * this work for additional information regarding copyright ownership. | |
5 | * The ASF licenses this file to You under the Apache License, Version 2.0 | |
6 | * (the "License"); you may not use this file except in compliance with | |
7 | * the License. You may obtain a copy of the License at | |
8 | * | |
9 | * http://www.apache.org/licenses/LICENSE-2.0 | |
10 | * | |
11 | * Unless required by applicable law or agreed to in writing, software | |
12 | * distributed under the License is distributed on an "AS IS" BASIS, | |
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | * See the License for the specific language governing permissions and | |
15 | * limitations under the License. | |
16 | */ | |
17 | ||
18 | package org.apache.arrow.vector; | |
19 | ||
20 | import static org.junit.Assert.assertEquals; | |
21 | import static org.junit.Assert.assertTrue; | |
22 | import static org.junit.Assert.fail; | |
23 | ||
24 | import java.math.BigDecimal; | |
25 | import java.math.BigInteger; | |
26 | ||
27 | import org.apache.arrow.memory.ArrowBuf; | |
28 | import org.apache.arrow.memory.BufferAllocator; | |
29 | import org.apache.arrow.vector.types.pojo.ArrowType; | |
30 | import org.junit.After; | |
31 | import org.junit.Before; | |
32 | import org.junit.Test; | |
33 | ||
34 | public class TestDecimalVector { | |
35 | ||
36 | private static long[] intValues; | |
37 | ||
38 | static { | |
39 | intValues = new long[60]; | |
40 | for (int i = 0; i < intValues.length / 2; i++) { | |
41 | intValues[i] = 1 << i + 1; | |
42 | intValues[2 * i] = -1 * (1 << i + 1); | |
43 | } | |
44 | } | |
45 | ||
46 | private int scale = 3; | |
47 | ||
48 | private BufferAllocator allocator; | |
49 | ||
50 | @Before | |
51 | public void init() { | |
52 | allocator = new DirtyRootAllocator(Long.MAX_VALUE, (byte) 100); | |
53 | } | |
54 | ||
55 | @After | |
56 | public void terminate() throws Exception { | |
57 | allocator.close(); | |
58 | } | |
59 | ||
60 | @Test | |
61 | public void testValuesWriteRead() { | |
62 | try (DecimalVector decimalVector = TestUtils.newVector(DecimalVector.class, "decimal", | |
63 | new ArrowType.Decimal(10, scale, 128), allocator);) { | |
64 | ||
65 | try (DecimalVector oldConstructor = new DecimalVector("decimal", allocator, 10, scale);) { | |
66 | assertEquals(decimalVector.getField().getType(), oldConstructor.getField().getType()); | |
67 | } | |
68 | ||
69 | decimalVector.allocateNew(); | |
70 | BigDecimal[] values = new BigDecimal[intValues.length]; | |
71 | for (int i = 0; i < intValues.length; i++) { | |
72 | BigDecimal decimal = new BigDecimal(BigInteger.valueOf(intValues[i]), scale); | |
73 | values[i] = decimal; | |
74 | decimalVector.setSafe(i, decimal); | |
75 | } | |
76 | ||
77 | decimalVector.setValueCount(intValues.length); | |
78 | ||
79 | for (int i = 0; i < intValues.length; i++) { | |
80 | BigDecimal value = decimalVector.getObject(i); | |
81 | assertEquals("unexpected data at index: " + i, values[i], value); | |
82 | } | |
83 | } | |
84 | } | |
85 | ||
86 | @Test | |
87 | public void testBigDecimalDifferentScaleAndPrecision() { | |
88 | try (DecimalVector decimalVector = TestUtils.newVector(DecimalVector.class, "decimal", | |
89 | new ArrowType.Decimal(4, 2, 128), allocator);) { | |
90 | decimalVector.allocateNew(); | |
91 | ||
92 | // test BigDecimal with different scale | |
93 | boolean hasError = false; | |
94 | try { | |
95 | BigDecimal decimal = new BigDecimal(BigInteger.valueOf(0), 3); | |
96 | decimalVector.setSafe(0, decimal); | |
97 | } catch (UnsupportedOperationException ue) { | |
98 | hasError = true; | |
99 | } finally { | |
100 | assertTrue(hasError); | |
101 | } | |
102 | ||
103 | // test BigDecimal with larger precision than initialized | |
104 | hasError = false; | |
105 | try { | |
106 | BigDecimal decimal = new BigDecimal(BigInteger.valueOf(12345), 2); | |
107 | decimalVector.setSafe(0, decimal); | |
108 | } catch (UnsupportedOperationException ue) { | |
109 | hasError = true; | |
110 | } finally { | |
111 | assertTrue(hasError); | |
112 | } | |
113 | } | |
114 | } | |
115 | ||
116 | @Test | |
117 | public void testWriteBigEndian() { | |
118 | try (DecimalVector decimalVector = TestUtils.newVector(DecimalVector.class, "decimal", | |
119 | new ArrowType.Decimal(38, 9, 128), allocator);) { | |
120 | decimalVector.allocateNew(); | |
121 | BigDecimal decimal1 = new BigDecimal("123456789.000000000"); | |
122 | BigDecimal decimal2 = new BigDecimal("11.123456789"); | |
123 | BigDecimal decimal3 = new BigDecimal("1.000000000"); | |
124 | BigDecimal decimal4 = new BigDecimal("0.111111111"); | |
125 | BigDecimal decimal5 = new BigDecimal("987654321.123456789"); | |
126 | BigDecimal decimal6 = new BigDecimal("222222222222.222222222"); | |
127 | BigDecimal decimal7 = new BigDecimal("7777777777777.666666667"); | |
128 | BigDecimal decimal8 = new BigDecimal("1212121212.343434343"); | |
129 | ||
130 | byte[] decimalValue1 = decimal1.unscaledValue().toByteArray(); | |
131 | byte[] decimalValue2 = decimal2.unscaledValue().toByteArray(); | |
132 | byte[] decimalValue3 = decimal3.unscaledValue().toByteArray(); | |
133 | byte[] decimalValue4 = decimal4.unscaledValue().toByteArray(); | |
134 | byte[] decimalValue5 = decimal5.unscaledValue().toByteArray(); | |
135 | byte[] decimalValue6 = decimal6.unscaledValue().toByteArray(); | |
136 | byte[] decimalValue7 = decimal7.unscaledValue().toByteArray(); | |
137 | byte[] decimalValue8 = decimal8.unscaledValue().toByteArray(); | |
138 | ||
139 | decimalVector.setBigEndian(0, decimalValue1); | |
140 | decimalVector.setBigEndian(1, decimalValue2); | |
141 | decimalVector.setBigEndian(2, decimalValue3); | |
142 | decimalVector.setBigEndian(3, decimalValue4); | |
143 | decimalVector.setBigEndian(4, decimalValue5); | |
144 | decimalVector.setBigEndian(5, decimalValue6); | |
145 | decimalVector.setBigEndian(6, decimalValue7); | |
146 | decimalVector.setBigEndian(7, decimalValue8); | |
147 | ||
148 | decimalVector.setValueCount(8); | |
149 | assertEquals(8, decimalVector.getValueCount()); | |
150 | assertEquals(decimal1, decimalVector.getObject(0)); | |
151 | assertEquals(decimal2, decimalVector.getObject(1)); | |
152 | assertEquals(decimal3, decimalVector.getObject(2)); | |
153 | assertEquals(decimal4, decimalVector.getObject(3)); | |
154 | assertEquals(decimal5, decimalVector.getObject(4)); | |
155 | assertEquals(decimal6, decimalVector.getObject(5)); | |
156 | assertEquals(decimal7, decimalVector.getObject(6)); | |
157 | assertEquals(decimal8, decimalVector.getObject(7)); | |
158 | } | |
159 | } | |
160 | ||
161 | @Test | |
162 | public void testLongReadWrite() { | |
163 | try (DecimalVector decimalVector = TestUtils.newVector(DecimalVector.class, "decimal", | |
164 | new ArrowType.Decimal(38, 0, 128), allocator)) { | |
165 | decimalVector.allocateNew(); | |
166 | ||
167 | long[] longValues = {0L, -2L, Long.MAX_VALUE, Long.MIN_VALUE, 187L}; | |
168 | ||
169 | for (int i = 0; i < longValues.length; ++i) { | |
170 | decimalVector.set(i, longValues[i]); | |
171 | } | |
172 | ||
173 | decimalVector.setValueCount(longValues.length); | |
174 | ||
175 | for (int i = 0; i < longValues.length; ++i) { | |
176 | assertEquals(new BigDecimal(longValues[i]), decimalVector.getObject(i)); | |
177 | } | |
178 | } | |
179 | } | |
180 | ||
181 | ||
182 | @Test | |
183 | public void testBigDecimalReadWrite() { | |
184 | try (DecimalVector decimalVector = TestUtils.newVector(DecimalVector.class, "decimal", | |
185 | new ArrowType.Decimal(38, 9, 128), allocator);) { | |
186 | decimalVector.allocateNew(); | |
187 | BigDecimal decimal1 = new BigDecimal("123456789.000000000"); | |
188 | BigDecimal decimal2 = new BigDecimal("11.123456789"); | |
189 | BigDecimal decimal3 = new BigDecimal("1.000000000"); | |
190 | BigDecimal decimal4 = new BigDecimal("-0.111111111"); | |
191 | BigDecimal decimal5 = new BigDecimal("-987654321.123456789"); | |
192 | BigDecimal decimal6 = new BigDecimal("-222222222222.222222222"); | |
193 | BigDecimal decimal7 = new BigDecimal("7777777777777.666666667"); | |
194 | BigDecimal decimal8 = new BigDecimal("1212121212.343434343"); | |
195 | ||
196 | decimalVector.set(0, decimal1); | |
197 | decimalVector.set(1, decimal2); | |
198 | decimalVector.set(2, decimal3); | |
199 | decimalVector.set(3, decimal4); | |
200 | decimalVector.set(4, decimal5); | |
201 | decimalVector.set(5, decimal6); | |
202 | decimalVector.set(6, decimal7); | |
203 | decimalVector.set(7, decimal8); | |
204 | ||
205 | decimalVector.setValueCount(8); | |
206 | assertEquals(8, decimalVector.getValueCount()); | |
207 | assertEquals(decimal1, decimalVector.getObject(0)); | |
208 | assertEquals(decimal2, decimalVector.getObject(1)); | |
209 | assertEquals(decimal3, decimalVector.getObject(2)); | |
210 | assertEquals(decimal4, decimalVector.getObject(3)); | |
211 | assertEquals(decimal5, decimalVector.getObject(4)); | |
212 | assertEquals(decimal6, decimalVector.getObject(5)); | |
213 | assertEquals(decimal7, decimalVector.getObject(6)); | |
214 | assertEquals(decimal8, decimalVector.getObject(7)); | |
215 | } | |
216 | } | |
217 | ||
218 | /** | |
219 | * Test {@link DecimalVector#setBigEndian(int, byte[])} which takes BE layout input and stores in native-endian (NE) | |
220 | * layout. | |
221 | * Cases to cover: input byte array in different lengths in range [1-16] and negative values. | |
222 | */ | |
223 | @Test | |
224 | public void decimalBE2NE() { | |
225 | try (DecimalVector decimalVector = TestUtils.newVector(DecimalVector.class, "decimal", | |
226 | new ArrowType.Decimal(21, 2, 128), allocator)) { | |
227 | decimalVector.allocateNew(); | |
228 | ||
229 | BigInteger[] testBigInts = new BigInteger[] { | |
230 | new BigInteger("0"), | |
231 | new BigInteger("-1"), | |
232 | new BigInteger("23"), | |
233 | new BigInteger("234234"), | |
234 | new BigInteger("-234234234"), | |
235 | new BigInteger("234234234234"), | |
236 | new BigInteger("-56345345345345"), | |
237 | new BigInteger("29823462983462893462934679234653456345"), // converts to 16 byte array | |
238 | new BigInteger("-3894572983475982374598324598234346536"), // converts to 16 byte array | |
239 | new BigInteger("-345345"), | |
240 | new BigInteger("754533") | |
241 | }; | |
242 | ||
243 | int insertionIdx = 0; | |
244 | insertionIdx++; // insert a null | |
245 | for (BigInteger val : testBigInts) { | |
246 | decimalVector.setBigEndian(insertionIdx++, val.toByteArray()); | |
247 | } | |
248 | insertionIdx++; // insert a null | |
249 | // insert a zero length buffer | |
250 | decimalVector.setBigEndian(insertionIdx++, new byte[0]); | |
251 | ||
252 | // Try inserting a buffer larger than 16bytes and expect a failure | |
253 | try { | |
254 | decimalVector.setBigEndian(insertionIdx, new byte[17]); | |
255 | fail("above statement should have failed"); | |
256 | } catch (IllegalArgumentException ex) { | |
257 | assertTrue(ex.getMessage().equals("Invalid decimal value length. Valid length in [1 - 16], got 17")); | |
258 | } | |
259 | decimalVector.setValueCount(insertionIdx); | |
260 | ||
261 | // retrieve values and check if they are correct | |
262 | int outputIdx = 0; | |
263 | assertTrue(decimalVector.isNull(outputIdx++)); | |
264 | for (BigInteger expected : testBigInts) { | |
265 | final BigDecimal actual = decimalVector.getObject(outputIdx++); | |
266 | assertEquals(expected, actual.unscaledValue()); | |
267 | } | |
268 | assertTrue(decimalVector.isNull(outputIdx++)); | |
269 | assertEquals(BigInteger.valueOf(0), decimalVector.getObject(outputIdx).unscaledValue()); | |
270 | } | |
271 | } | |
272 | ||
273 | @Test | |
274 | public void setUsingArrowBufOfInts() { | |
275 | try (DecimalVector decimalVector = TestUtils.newVector(DecimalVector.class, "decimal", | |
276 | new ArrowType.Decimal(5, 2, 128), allocator); | |
277 | ArrowBuf buf = allocator.buffer(8);) { | |
278 | decimalVector.allocateNew(); | |
279 | ||
280 | // add a positive value equivalent to 705.32 | |
281 | int val = 70532; | |
282 | buf.setInt(0, val); | |
283 | decimalVector.setSafe(0, 0, buf, 4); | |
284 | ||
285 | // add a -ve value equivalent to -705.32 | |
286 | val = -70532; | |
287 | buf.setInt(4, val); | |
288 | decimalVector.setSafe(1, 4, buf, 4); | |
289 | ||
290 | decimalVector.setValueCount(2); | |
291 | ||
292 | BigDecimal [] expectedValues = new BigDecimal[] {BigDecimal.valueOf(705.32), BigDecimal | |
293 | .valueOf(-705.32)}; | |
294 | for (int i = 0; i < 2; i ++) { | |
295 | BigDecimal value = decimalVector.getObject(i); | |
296 | assertEquals(expectedValues[i], value); | |
297 | } | |
298 | } | |
299 | ||
300 | } | |
301 | ||
302 | @Test | |
303 | public void setUsingArrowLongBytes() { | |
304 | try (DecimalVector decimalVector = TestUtils.newVector(DecimalVector.class, "decimal", | |
305 | new ArrowType.Decimal(18, 0, 128), allocator); | |
306 | ArrowBuf buf = allocator.buffer(16);) { | |
307 | decimalVector.allocateNew(); | |
308 | ||
309 | long val = Long.MAX_VALUE; | |
310 | buf.setLong(0, val); | |
311 | decimalVector.setSafe(0, 0, buf, 8); | |
312 | ||
313 | val = Long.MIN_VALUE; | |
314 | buf.setLong(8, val); | |
315 | decimalVector.setSafe(1, 8, buf, 8); | |
316 | ||
317 | decimalVector.setValueCount(2); | |
318 | ||
319 | BigDecimal [] expectedValues = new BigDecimal[] {BigDecimal.valueOf(Long.MAX_VALUE), BigDecimal | |
320 | .valueOf(Long.MIN_VALUE)}; | |
321 | for (int i = 0; i < 2; i ++) { | |
322 | BigDecimal value = decimalVector.getObject(i); | |
323 | assertEquals(expectedValues[i], value); | |
324 | } | |
325 | } | |
326 | } | |
327 | ||
328 | @Test | |
329 | public void setUsingArrowBufOfBEBytes() { | |
330 | try (DecimalVector decimalVector = TestUtils.newVector(DecimalVector.class, "decimal", | |
331 | new ArrowType.Decimal(5, 2, 128), allocator); | |
332 | ArrowBuf buf = allocator.buffer(9);) { | |
333 | BigDecimal [] expectedValues = new BigDecimal[] {BigDecimal.valueOf(705.32), BigDecimal | |
334 | .valueOf(-705.32), BigDecimal.valueOf(705.32)}; | |
335 | verifyWritingArrowBufWithBigEndianBytes(decimalVector, buf, expectedValues, 3); | |
336 | } | |
337 | ||
338 | try (DecimalVector decimalVector = TestUtils.newVector(DecimalVector.class, "decimal", | |
339 | new ArrowType.Decimal(36, 2, 128), allocator); | |
340 | ArrowBuf buf = allocator.buffer(45);) { | |
341 | BigDecimal[] expectedValues = new BigDecimal[] {new BigDecimal("2982346298346289346293467923465345.63"), | |
342 | new BigDecimal("-2982346298346289346293467923465345.63"), | |
343 | new BigDecimal("2982346298346289346293467923465345.63")}; | |
344 | verifyWritingArrowBufWithBigEndianBytes(decimalVector, buf, expectedValues, 15); | |
345 | } | |
346 | } | |
347 | ||
348 | private void verifyWritingArrowBufWithBigEndianBytes(DecimalVector decimalVector, | |
349 | ArrowBuf buf, BigDecimal[] expectedValues, | |
350 | int length) { | |
351 | decimalVector.allocateNew(); | |
352 | for (int i = 0; i < expectedValues.length; i++) { | |
353 | byte []bigEndianBytes = expectedValues[i].unscaledValue().toByteArray(); | |
354 | buf.setBytes(length * i , bigEndianBytes, 0 , bigEndianBytes.length); | |
355 | decimalVector.setBigEndianSafe(i, length * i, buf, bigEndianBytes.length); | |
356 | } | |
357 | ||
358 | decimalVector.setValueCount(3); | |
359 | ||
360 | for (int i = 0; i < expectedValues.length; i ++) { | |
361 | BigDecimal value = decimalVector.getObject(i); | |
362 | assertEquals(expectedValues[i], value); | |
363 | } | |
364 | } | |
365 | } |