|
33 | 33 | import java.util.function.BiConsumer; |
34 | 34 |
|
35 | 35 | import org.apache.arrow.adapter.jdbc.binder.ColumnBinder; |
| 36 | +import org.apache.arrow.memory.ArrowBuf; |
36 | 37 | import org.apache.arrow.memory.BufferAllocator; |
37 | 38 | import org.apache.arrow.memory.RootAllocator; |
38 | 39 | import org.apache.arrow.vector.BaseLargeVariableWidthVector; |
|
67 | 68 | import org.apache.arrow.vector.VarBinaryVector; |
68 | 69 | import org.apache.arrow.vector.VarCharVector; |
69 | 70 | import org.apache.arrow.vector.VectorSchemaRoot; |
| 71 | +import org.apache.arrow.vector.complex.ListVector; |
70 | 72 | import org.apache.arrow.vector.types.DateUnit; |
71 | 73 | import org.apache.arrow.vector.types.FloatingPointPrecision; |
72 | 74 | import org.apache.arrow.vector.types.TimeUnit; |
73 | 75 | import org.apache.arrow.vector.types.pojo.ArrowType; |
74 | 76 | import org.apache.arrow.vector.types.pojo.Field; |
| 77 | +import org.apache.arrow.vector.types.pojo.FieldType; |
75 | 78 | import org.apache.arrow.vector.types.pojo.Schema; |
76 | 79 | import org.junit.jupiter.api.AfterEach; |
77 | 80 | import org.junit.jupiter.api.BeforeEach; |
@@ -385,6 +388,91 @@ void decimal256() throws SQLException { |
385 | 388 | Arrays.asList(new BigDecimal("120.429"), new BigDecimal("-10590.123"), new BigDecimal("0.000"))); |
386 | 389 | } |
387 | 390 |
|
| 391 | + @Test |
| 392 | + void listOfDouble() throws SQLException { |
| 393 | + TriConsumer<ListVector, Integer, Double[]> setValue = (listVector, index, values) -> { |
| 394 | + org.apache.arrow.vector.complex.impl.UnionListWriter writer = listVector.getWriter(); |
| 395 | + writer.setPosition(index); |
| 396 | + writer.startList(); |
| 397 | + Arrays.stream(values).forEach(doubleValue -> writer.float8().writeFloat8(doubleValue)); |
| 398 | + writer.endList(); |
| 399 | + listVector.setLastSet(index); |
| 400 | + }; |
| 401 | + List<Double[]> values = Arrays.asList(new Double[]{0.0, Math.PI}, new Double[]{1.1, -352346.2, 2355.6}, |
| 402 | + new Double[]{-1024.3}, new Double[]{}); |
| 403 | + testListType(new ArrowType.FloatingPoint(FloatingPointPrecision.DOUBLE), setValue, ListVector::setNull, values); |
| 404 | + } |
| 405 | + |
| 406 | + @Test |
| 407 | + void listOfInt64() throws SQLException { |
| 408 | + TriConsumer<ListVector, Integer, Long[]> setValue = (listVector, index, values) -> { |
| 409 | + org.apache.arrow.vector.complex.impl.UnionListWriter writer = listVector.getWriter(); |
| 410 | + writer.setPosition(index); |
| 411 | + writer.startList(); |
| 412 | + Arrays.stream(values).forEach(longValue -> writer.bigInt().writeBigInt(longValue)); |
| 413 | + writer.endList(); |
| 414 | + listVector.setLastSet(index); |
| 415 | + }; |
| 416 | + List<Long[]> values = Arrays.asList(new Long[]{1L, 2L, 3L}, new Long[]{4L, 5L}, |
| 417 | + new Long[]{512L, 1024L, 2048L, 4096L}, new Long[]{}); |
| 418 | + testListType((ArrowType) new ArrowType.Int(64, true), setValue, ListVector::setNull, values); |
| 419 | + } |
| 420 | + |
| 421 | + @Test |
| 422 | + void listOfInt32() throws SQLException { |
| 423 | + TriConsumer<ListVector, Integer, Integer[]> setValue = (listVector, index, values) -> { |
| 424 | + org.apache.arrow.vector.complex.impl.UnionListWriter writer = listVector.getWriter(); |
| 425 | + writer.setPosition(index); |
| 426 | + writer.startList(); |
| 427 | + Arrays.stream(values).forEach(integerValue -> writer.integer().writeInt(integerValue)); |
| 428 | + writer.endList(); |
| 429 | + listVector.setLastSet(index); |
| 430 | + }; |
| 431 | + List<Integer[]> values = Arrays.asList(new Integer[]{1, 2, 3}, new Integer[]{4, 5}, |
| 432 | + new Integer[]{512, 1024, 2048, 4096}, new Integer[]{}); |
| 433 | + testListType((ArrowType) new ArrowType.Int(32, true), setValue, ListVector::setNull, values); |
| 434 | + } |
| 435 | + |
| 436 | + @Test |
| 437 | + void listOfBoolean() throws SQLException { |
| 438 | + TriConsumer<ListVector, Integer, Boolean[]> setValue = (listVector, index, values) -> { |
| 439 | + org.apache.arrow.vector.complex.impl.UnionListWriter writer = listVector.getWriter(); |
| 440 | + writer.setPosition(index); |
| 441 | + writer.startList(); |
| 442 | + Arrays.stream(values).forEach(booleanValue -> writer.bit().writeBit(booleanValue ? 1 : 0)); |
| 443 | + writer.endList(); |
| 444 | + listVector.setLastSet(index); |
| 445 | + }; |
| 446 | + List<Boolean[]> values = Arrays.asList(new Boolean[]{true, false}, |
| 447 | + new Boolean[]{false, false}, new Boolean[]{true, true, false, true}, new Boolean[]{}); |
| 448 | + testListType((ArrowType) new ArrowType.Bool(), setValue, ListVector::setNull, values); |
| 449 | + } |
| 450 | + |
| 451 | + @Test |
| 452 | + void listOfString() throws SQLException { |
| 453 | + TriConsumer<ListVector, Integer, String[]> setValue = (listVector, index, values) -> { |
| 454 | + org.apache.arrow.vector.complex.impl.UnionListWriter writer = listVector.getWriter(); |
| 455 | + writer.setPosition(index); |
| 456 | + writer.startList(); |
| 457 | + Arrays.stream(values).forEach(stringValue -> { |
| 458 | + if (stringValue != null) { |
| 459 | + byte[] stringValueBytes = stringValue.getBytes(StandardCharsets.UTF_8); |
| 460 | + try (ArrowBuf stringBuffer = allocator.buffer(stringValueBytes.length)) { |
| 461 | + stringBuffer.writeBytes(stringValueBytes); |
| 462 | + writer.varChar().writeVarChar(0, stringValueBytes.length, stringBuffer); |
| 463 | + } |
| 464 | + } else { |
| 465 | + writer.varChar().writeNull(); |
| 466 | + } |
| 467 | + }); |
| 468 | + writer.endList(); |
| 469 | + listVector.setLastSet(index); |
| 470 | + }; |
| 471 | + List<String[]> values = Arrays.asList(new String[]{"aaaa", "b1"}, |
| 472 | + new String[]{"c", null, "d"}, new String[]{"e", "f", "g", "h"}, new String[]{}); |
| 473 | + testListType((ArrowType) new ArrowType.Utf8(), setValue, ListVector::setNull, values); |
| 474 | + } |
| 475 | + |
388 | 476 | @FunctionalInterface |
389 | 477 | interface TriConsumer<T, U, V> { |
390 | 478 | void accept(T value1, U value2, V value3); |
@@ -483,4 +571,105 @@ <T, V extends FieldVector> void testSimpleType(ArrowType arrowType, int jdbcType |
483 | 571 | assertThat(binder.next()).isFalse(); |
484 | 572 | } |
485 | 573 | } |
| 574 | + |
| 575 | + <T, V extends FieldVector> void testListType(ArrowType arrowType, TriConsumer<V, Integer, T> setValue, |
| 576 | + BiConsumer<V, Integer> setNull, List<T> values) throws SQLException { |
| 577 | + int jdbcType = Types.ARRAY; |
| 578 | + Schema schema = new Schema(Collections.singletonList(new Field("field", FieldType.nullable( |
| 579 | + new ArrowType.List()), Collections.singletonList( |
| 580 | + new Field("element", FieldType.notNullable(arrowType), null) |
| 581 | + )))); |
| 582 | + try (final MockPreparedStatement statement = new MockPreparedStatement(); |
| 583 | + final VectorSchemaRoot root = VectorSchemaRoot.create(schema, allocator)) { |
| 584 | + final JdbcParameterBinder binder = |
| 585 | + JdbcParameterBinder.builder(statement, root).bindAll().build(); |
| 586 | + assertThat(binder.next()).isFalse(); |
| 587 | + |
| 588 | + @SuppressWarnings("unchecked") |
| 589 | + final V vector = (V) root.getVector(0); |
| 590 | + final ColumnBinder columnBinder = ColumnBinder.forVector(vector); |
| 591 | + assertThat(columnBinder.getJdbcType()).isEqualTo(jdbcType); |
| 592 | + |
| 593 | + setValue.accept(vector, 0, values.get(0)); |
| 594 | + setValue.accept(vector, 1, values.get(1)); |
| 595 | + setNull.accept(vector, 2); |
| 596 | + root.setRowCount(3); |
| 597 | + |
| 598 | + assertThat(binder.next()).isTrue(); |
| 599 | + assertThat(statement.getParamValue(1)).isEqualTo(values.get(0)); |
| 600 | + assertThat(binder.next()).isTrue(); |
| 601 | + assertThat(statement.getParamValue(1)).isEqualTo(values.get(1)); |
| 602 | + assertThat(binder.next()).isTrue(); |
| 603 | + assertThat(statement.getParamValue(1)).isNull(); |
| 604 | + assertThat(statement.getParamType(1)).isEqualTo(jdbcType); |
| 605 | + assertThat(binder.next()).isFalse(); |
| 606 | + |
| 607 | + binder.reset(); |
| 608 | + |
| 609 | + setNull.accept(vector, 0); |
| 610 | + setValue.accept(vector, 1, values.get(3)); |
| 611 | + setValue.accept(vector, 2, values.get(0)); |
| 612 | + setValue.accept(vector, 3, values.get(2)); |
| 613 | + setValue.accept(vector, 4, values.get(1)); |
| 614 | + root.setRowCount(5); |
| 615 | + |
| 616 | + assertThat(binder.next()).isTrue(); |
| 617 | + assertThat(statement.getParamValue(1)).isNull(); |
| 618 | + assertThat(statement.getParamType(1)).isEqualTo(jdbcType); |
| 619 | + assertThat(binder.next()).isTrue(); |
| 620 | + assertThat(statement.getParamValue(1)).isEqualTo(values.get(3)); |
| 621 | + assertThat(binder.next()).isTrue(); |
| 622 | + assertThat(statement.getParamValue(1)).isEqualTo(values.get(0)); |
| 623 | + assertThat(binder.next()).isTrue(); |
| 624 | + assertThat(statement.getParamValue(1)).isEqualTo(values.get(2)); |
| 625 | + assertThat(binder.next()).isTrue(); |
| 626 | + assertThat(statement.getParamValue(1)).isEqualTo(values.get(1)); |
| 627 | + assertThat(binder.next()).isFalse(); |
| 628 | + } |
| 629 | + |
| 630 | + // Non-nullable (since some types have a specialized binder) |
| 631 | + schema = new Schema(Collections.singletonList(new Field("field", FieldType.notNullable( |
| 632 | + new ArrowType.List()), Collections.singletonList( |
| 633 | + new Field("element", FieldType.notNullable(arrowType), null) |
| 634 | + )))); |
| 635 | + try (final MockPreparedStatement statement = new MockPreparedStatement(); |
| 636 | + final VectorSchemaRoot root = VectorSchemaRoot.create(schema, allocator)) { |
| 637 | + final JdbcParameterBinder binder = |
| 638 | + JdbcParameterBinder.builder(statement, root).bindAll().build(); |
| 639 | + assertThat(binder.next()).isFalse(); |
| 640 | + |
| 641 | + @SuppressWarnings("unchecked") |
| 642 | + final V vector = (V) root.getVector(0); |
| 643 | + setValue.accept(vector, 0, values.get(0)); |
| 644 | + setValue.accept(vector, 1, values.get(1)); |
| 645 | + root.setRowCount(2); |
| 646 | + |
| 647 | + assertThat(binder.next()).isTrue(); |
| 648 | + assertThat(statement.getParamValue(1)).isEqualTo(values.get(0)); |
| 649 | + assertThat(binder.next()).isTrue(); |
| 650 | + assertThat(statement.getParamValue(1)).isEqualTo(values.get(1)); |
| 651 | + assertThat(binder.next()).isFalse(); |
| 652 | + |
| 653 | + binder.reset(); |
| 654 | + |
| 655 | + setValue.accept(vector, 0, values.get(0)); |
| 656 | + setValue.accept(vector, 1, values.get(2)); |
| 657 | + setValue.accept(vector, 2, values.get(0)); |
| 658 | + setValue.accept(vector, 3, values.get(2)); |
| 659 | + setValue.accept(vector, 4, values.get(1)); |
| 660 | + root.setRowCount(5); |
| 661 | + |
| 662 | + assertThat(binder.next()).isTrue(); |
| 663 | + assertThat(statement.getParamValue(1)).isEqualTo(values.get(0)); |
| 664 | + assertThat(binder.next()).isTrue(); |
| 665 | + assertThat(statement.getParamValue(1)).isEqualTo(values.get(2)); |
| 666 | + assertThat(binder.next()).isTrue(); |
| 667 | + assertThat(statement.getParamValue(1)).isEqualTo(values.get(0)); |
| 668 | + assertThat(binder.next()).isTrue(); |
| 669 | + assertThat(statement.getParamValue(1)).isEqualTo(values.get(2)); |
| 670 | + assertThat(binder.next()).isTrue(); |
| 671 | + assertThat(statement.getParamValue(1)).isEqualTo(values.get(1)); |
| 672 | + assertThat(binder.next()).isFalse(); |
| 673 | + } |
| 674 | + } |
486 | 675 | } |
0 commit comments