|
16 | 16 | */ |
17 | 17 | package org.apache.arrow.vector; |
18 | 18 |
|
| 19 | +import static org.junit.jupiter.api.Assertions.assertArrayEquals; |
19 | 20 | import static org.junit.jupiter.api.Assertions.assertEquals; |
20 | 21 | import static org.junit.jupiter.api.Assertions.assertFalse; |
21 | 22 | import static org.junit.jupiter.api.Assertions.assertNull; |
22 | 23 | import static org.junit.jupiter.api.Assertions.assertSame; |
| 24 | +import static org.junit.jupiter.api.Assertions.assertThrows; |
23 | 25 | import static org.junit.jupiter.api.Assertions.assertTrue; |
24 | 26 |
|
25 | 27 | import java.nio.ByteBuffer; |
|
41 | 43 | import org.apache.arrow.vector.complex.writer.BaseWriter.MapWriter; |
42 | 44 | import org.apache.arrow.vector.complex.writer.FieldWriter; |
43 | 45 | import org.apache.arrow.vector.holder.UuidHolder; |
| 46 | +import org.apache.arrow.vector.holders.FixedSizeBinaryHolder; |
44 | 47 | import org.apache.arrow.vector.types.Types.MinorType; |
45 | 48 | import org.apache.arrow.vector.types.pojo.ArrowType; |
46 | 49 | import org.apache.arrow.vector.types.pojo.Field; |
@@ -1359,4 +1362,222 @@ public void testCopyFromForExtensionType() throws Exception { |
1359 | 1362 | assertEquals(u2, actualUuid); |
1360 | 1363 | } |
1361 | 1364 | } |
| 1365 | + |
| 1366 | + private FixedSizeBinaryHolder getFixedSizeBinaryHolder(byte[] array) { |
| 1367 | + FixedSizeBinaryHolder holder = new FixedSizeBinaryHolder(); |
| 1368 | + holder.byteWidth = array.length; |
| 1369 | + holder.buffer = allocator.buffer(array.length); |
| 1370 | + for (int i = 0; i < array.length; i++) { |
| 1371 | + holder.buffer.setByte(i, array[i]); |
| 1372 | + } |
| 1373 | + |
| 1374 | + return holder; |
| 1375 | + } |
| 1376 | + |
| 1377 | + /** |
| 1378 | + * Regression test for GH-586: UnionMapWriter.fixedSizeBinary() should properly delegate to the |
| 1379 | + * entry writer for both key and value paths. |
| 1380 | + */ |
| 1381 | + @Test |
| 1382 | + public void testFixedSizeBinaryWriter() { |
| 1383 | + try (MapVector mapVector = MapVector.empty("map_vector", allocator, false)) { |
| 1384 | + UnionMapWriter writer = mapVector.getWriter(); |
| 1385 | + writer.allocate(); |
| 1386 | + |
| 1387 | + // populate input vector with the following records |
| 1388 | + // {[11, 22] -> [32, 21]} |
| 1389 | + // {1 -> [11, 22], 2 -> [32, 21]} |
| 1390 | + // null |
| 1391 | + // {[11, 22] -> 1, [32, 21] -> 2} |
| 1392 | + // {[11, 22] -> null} |
| 1393 | + // {null -> [32, 21]} - wrong "for a given entry, the "key" is non-nullable" - todo: it |
| 1394 | + // shouldn't work. Should it? |
| 1395 | + FixedSizeBinaryHolder holder1 = getFixedSizeBinaryHolder(new byte[] {11, 22}); |
| 1396 | + FixedSizeBinaryHolder holder2 = getFixedSizeBinaryHolder(new byte[] {32, 21}); |
| 1397 | + |
| 1398 | + writer.setPosition(0); // optional |
| 1399 | + writer.startMap(); |
| 1400 | + writer.startEntry(); |
| 1401 | + writer |
| 1402 | + .key() |
| 1403 | + .fixedSizeBinary(holder1.byteWidth) |
| 1404 | + .write(holder1); // need to initialize with byteWidth - NPE otherwise |
| 1405 | + writer.value().fixedSizeBinary(holder2.byteWidth).write(holder2); |
| 1406 | + writer.endEntry(); |
| 1407 | + holder1.buffer.close(); |
| 1408 | + holder2.buffer.close(); |
| 1409 | + writer.endMap(); |
| 1410 | + |
| 1411 | + // {1 -> [11, 22], 2 -> [32, 21]} |
| 1412 | + holder1 = getFixedSizeBinaryHolder(new byte[] {11, 22}); |
| 1413 | + holder2 = getFixedSizeBinaryHolder(new byte[] {32, 21}); |
| 1414 | + writer.setPosition(1); |
| 1415 | + writer.startMap(); |
| 1416 | + writer.startEntry(); |
| 1417 | + writer.key().bigInt().writeBigInt(1); |
| 1418 | + writer.value().fixedSizeBinary().write(holder1); |
| 1419 | + writer.endEntry(); |
| 1420 | + holder1.buffer.close(); |
| 1421 | + writer.startEntry(); |
| 1422 | + writer.key().bigInt().writeBigInt(2); |
| 1423 | + writer.value().fixedSizeBinary().write(holder2); |
| 1424 | + writer.endEntry(); |
| 1425 | + writer.endMap(); |
| 1426 | + holder2.buffer.close(); |
| 1427 | + |
| 1428 | + // {[11, 22] -> 1, [32, 21] -> 2} |
| 1429 | + holder1 = getFixedSizeBinaryHolder(new byte[] {11, 22}); |
| 1430 | + holder2 = getFixedSizeBinaryHolder(new byte[] {32, 21}); |
| 1431 | + writer.setPosition(3); |
| 1432 | + writer.startMap(); |
| 1433 | + writer.startEntry(); |
| 1434 | + writer.key().fixedSizeBinary().write(holder1); |
| 1435 | + writer.value().bigInt().writeBigInt(1); |
| 1436 | + writer.endEntry(); |
| 1437 | + holder1.buffer.close(); |
| 1438 | + writer.startEntry(); |
| 1439 | + writer.key().fixedSizeBinary().write(holder2); |
| 1440 | + writer.value().bigInt().writeBigInt(2); |
| 1441 | + writer.endEntry(); |
| 1442 | + writer.endMap(); |
| 1443 | + holder2.buffer.close(); |
| 1444 | + |
| 1445 | + // {[11, 22] -> null} |
| 1446 | + holder1 = getFixedSizeBinaryHolder(new byte[] {11, 22}); |
| 1447 | + writer.setPosition(4); |
| 1448 | + writer.startMap(); |
| 1449 | + writer.startEntry(); |
| 1450 | + writer.key().fixedSizeBinary().write(holder1); |
| 1451 | + writer.endEntry(); |
| 1452 | + writer.endMap(); |
| 1453 | + holder1.buffer.close(); |
| 1454 | + |
| 1455 | + // {null -> [32, 21]} |
| 1456 | + holder2 = getFixedSizeBinaryHolder(new byte[] {32, 21}); |
| 1457 | + writer.setPosition(5); |
| 1458 | + writer.startMap(); |
| 1459 | + writer.startEntry(); |
| 1460 | + writer.value().fixedSizeBinary().write(holder2); |
| 1461 | + writer.endEntry(); |
| 1462 | + writer.endMap(); |
| 1463 | + holder2.buffer.close(); |
| 1464 | + |
| 1465 | + writer.setValueCount(6); |
| 1466 | + |
| 1467 | + // assert the output vector is correct |
| 1468 | + FieldReader reader = mapVector.getReader(); |
| 1469 | + assertTrue(reader.isSet(), "shouldn't be null"); |
| 1470 | + reader.setPosition(1); |
| 1471 | + assertTrue(reader.isSet(), "shouldn't be null"); |
| 1472 | + reader.setPosition(2); |
| 1473 | + assertFalse(reader.isSet(), "should be null"); |
| 1474 | + reader.setPosition(3); |
| 1475 | + assertTrue(reader.isSet(), "shouldn't be null"); |
| 1476 | + reader.setPosition(4); |
| 1477 | + assertTrue(reader.isSet(), "shouldn't be null"); |
| 1478 | + reader.setPosition(5); |
| 1479 | + assertTrue(reader.isSet(), "shouldn't be null"); |
| 1480 | + |
| 1481 | + /* index 0 */ |
| 1482 | + Object result = mapVector.getObject(0); |
| 1483 | + ArrayList<?> resultSet = (ArrayList<?>) result; |
| 1484 | + assertEquals(1, resultSet.size()); |
| 1485 | + Map<?, ?> resultStruct = (Map<?, ?>) resultSet.get(0); |
| 1486 | + assertTrue(resultStruct.containsKey(MapVector.KEY_NAME)); |
| 1487 | + assertTrue(resultStruct.containsKey(MapVector.VALUE_NAME)); |
| 1488 | + assertArrayEquals(new byte[] {11, 22}, (byte[]) resultStruct.get(MapVector.KEY_NAME)); |
| 1489 | + assertArrayEquals(new byte[] {32, 21}, (byte[]) resultStruct.get(MapVector.VALUE_NAME)); |
| 1490 | + |
| 1491 | + /* index 1 */ |
| 1492 | + result = mapVector.getObject(1); |
| 1493 | + resultSet = (ArrayList<?>) result; |
| 1494 | + assertEquals(2, resultSet.size()); |
| 1495 | + resultStruct = (Map<?, ?>) resultSet.get(0); |
| 1496 | + assertEquals(1L, getResultKey(resultStruct)); |
| 1497 | + assertTrue(resultStruct.containsKey(MapVector.VALUE_NAME)); |
| 1498 | + assertArrayEquals(new byte[] {11, 22}, (byte[]) resultStruct.get(MapVector.VALUE_NAME)); |
| 1499 | + resultStruct = (Map<?, ?>) resultSet.get(1); |
| 1500 | + assertEquals(2L, getResultKey(resultStruct)); |
| 1501 | + assertTrue(resultStruct.containsKey(MapVector.VALUE_NAME)); |
| 1502 | + assertArrayEquals(new byte[] {32, 21}, (byte[]) resultStruct.get(MapVector.VALUE_NAME)); |
| 1503 | + |
| 1504 | + /* index 2 */ |
| 1505 | + result = mapVector.getObject(2); |
| 1506 | + assertNull(result); |
| 1507 | + |
| 1508 | + /* index 3 */ |
| 1509 | + result = mapVector.getObject(3); |
| 1510 | + resultSet = (ArrayList<?>) result; |
| 1511 | + assertEquals(2, resultSet.size()); |
| 1512 | + resultStruct = (Map<?, ?>) resultSet.get(0); |
| 1513 | + assertTrue(resultStruct.containsKey(MapVector.KEY_NAME)); |
| 1514 | + assertArrayEquals(new byte[] {11, 22}, (byte[]) resultStruct.get(MapVector.KEY_NAME)); |
| 1515 | + assertEquals(1L, getResultValue(resultStruct)); |
| 1516 | + resultStruct = (Map<?, ?>) resultSet.get(1); |
| 1517 | + assertTrue(resultStruct.containsKey(MapVector.KEY_NAME)); |
| 1518 | + assertArrayEquals(new byte[] {32, 21}, (byte[]) resultStruct.get(MapVector.KEY_NAME)); |
| 1519 | + assertEquals(2L, getResultValue(resultStruct)); |
| 1520 | + |
| 1521 | + /* index 4 */ |
| 1522 | + result = mapVector.getObject(4); |
| 1523 | + resultSet = (ArrayList<?>) result; |
| 1524 | + assertEquals(1, resultSet.size()); |
| 1525 | + resultStruct = (Map<?, ?>) resultSet.get(0); |
| 1526 | + assertTrue(resultStruct.containsKey(MapVector.KEY_NAME)); |
| 1527 | + assertArrayEquals(new byte[] {11, 22}, (byte[]) resultStruct.get(MapVector.KEY_NAME)); |
| 1528 | + assertFalse(resultStruct.containsKey(MapVector.VALUE_NAME)); |
| 1529 | + |
| 1530 | + /* index 5 */ |
| 1531 | + result = mapVector.getObject(5); |
| 1532 | + resultSet = (ArrayList<?>) result; |
| 1533 | + assertEquals(1, resultSet.size()); |
| 1534 | + resultStruct = (Map<?, ?>) resultSet.get(0); |
| 1535 | + assertFalse(resultStruct.containsKey(MapVector.KEY_NAME)); |
| 1536 | + assertTrue(resultStruct.containsKey(MapVector.VALUE_NAME)); |
| 1537 | + assertArrayEquals(new byte[] {32, 21}, (byte[]) resultStruct.get(MapVector.VALUE_NAME)); |
| 1538 | + } |
| 1539 | + } |
| 1540 | + |
| 1541 | + @Test |
| 1542 | + public void testFixedSizeBinaryFirstInitialization() { |
| 1543 | + try (MapVector mapVector = MapVector.empty("map_vector", allocator, false)) { |
| 1544 | + UnionMapWriter writer = mapVector.getWriter(); |
| 1545 | + writer.allocate(); |
| 1546 | + |
| 1547 | + // populate input vector with the following records |
| 1548 | + // {[11, 22] -> [32, 21]} |
| 1549 | + FixedSizeBinaryHolder holder1 = getFixedSizeBinaryHolder(new byte[] {11, 22}); |
| 1550 | + FixedSizeBinaryHolder holder2 = getFixedSizeBinaryHolder(new byte[] {32, 21}); |
| 1551 | + |
| 1552 | + writer.setPosition(0); // optional |
| 1553 | + writer.startMap(); |
| 1554 | + writer.startEntry(); |
| 1555 | + // require byteWidth parameter for first-time initialization of `key` or `value` writers |
| 1556 | + assertThrows(NullPointerException.class, () -> writer.key().fixedSizeBinary().write(holder1)); |
| 1557 | + assertThrows( |
| 1558 | + NullPointerException.class, () -> writer.value().fixedSizeBinary().write(holder2)); |
| 1559 | + writer.key().fixedSizeBinary(holder1.byteWidth).write(holder1); |
| 1560 | + writer.value().fixedSizeBinary(holder2.byteWidth).write(holder2); |
| 1561 | + writer.endEntry(); |
| 1562 | + holder1.buffer.close(); |
| 1563 | + holder2.buffer.close(); |
| 1564 | + writer.endMap(); |
| 1565 | + |
| 1566 | + writer.setValueCount(1); |
| 1567 | + |
| 1568 | + // assert the output vector is correct |
| 1569 | + FieldReader reader = mapVector.getReader(); |
| 1570 | + assertTrue(reader.isSet(), "shouldn't be null"); |
| 1571 | + |
| 1572 | + /* index 0 */ |
| 1573 | + Object result = mapVector.getObject(0); |
| 1574 | + ArrayList<?> resultSet = (ArrayList<?>) result; |
| 1575 | + assertEquals(1, resultSet.size()); |
| 1576 | + Map<?, ?> resultStruct = (Map<?, ?>) resultSet.get(0); |
| 1577 | + assertTrue(resultStruct.containsKey(MapVector.KEY_NAME)); |
| 1578 | + assertTrue(resultStruct.containsKey(MapVector.VALUE_NAME)); |
| 1579 | + assertArrayEquals(new byte[] {11, 22}, (byte[]) resultStruct.get(MapVector.KEY_NAME)); |
| 1580 | + assertArrayEquals(new byte[] {32, 21}, (byte[]) resultStruct.get(MapVector.VALUE_NAME)); |
| 1581 | + } |
| 1582 | + } |
1362 | 1583 | } |
0 commit comments