|
41 | 41 | import org.apache.parquet.io.PositionOutputStream; |
42 | 42 | import org.apache.parquet.io.SeekableInputStream; |
43 | 43 | import org.apache.parquet.io.api.Binary; |
44 | | -import org.apache.parquet.io.api.RecordConsumer; |
45 | 44 | import org.apache.parquet.schema.MessageType; |
46 | 45 | import org.apache.parquet.schema.MessageTypeParser; |
47 | 46 | import org.junit.Rule; |
@@ -470,45 +469,20 @@ public void testUnknownPrimitiveType() throws IOException { |
470 | 469 | } |
471 | 470 |
|
472 | 471 | /** |
473 | | - * Shredding a container that holds an unknown primitive type raises an exception. The shredding |
474 | | - * writer walks children through {@link Variant#getElementAtIndex}/{@link Variant#getFieldAtIndex}, |
475 | | - * each of which materializes (and therefore validates) a child Variant. An unknown primitive type |
476 | | - * cannot be materialized, so the known elements shred into their typed_value columns and the |
477 | | - * unknown element aborts the write rather than being silently dropped or corrupted. |
478 | | - * |
479 | | - * <p>The current specification is: "new types may be added to the specification without |
480 | | - * incrementing the version ID. In such a situation, an implementation should be able to read the |
481 | | - * rest of the Variant value if desired." Reading the unknown value is optional; the shredding |
482 | | - * writer here declines and raises when it cannot reconstruct the value. A future policy may always |
483 | | - * reject on reconstruction. |
| 472 | + * Reconstructing a residual {@code value} column that carries an unknown primitive type fails to |
| 473 | + * parse. On the shredded read path the residual {@code value} column is the only channel that can |
| 474 | + * carry an unknown variant type (a {@code typed_value} column's type is fixed by the Parquet |
| 475 | + * schema), and it is replayed through {@link VariantBuilder#appendEncodedValue} — exactly what |
| 476 | + * {@code VariantConverters.VariantValueConverter} does on read. |
484 | 477 | */ |
485 | 478 | @Test |
486 | | - public void testUnknownPrimitiveTypeShredding() { |
487 | | - // Shredded schema: metadata + residual value + a typed_value LIST whose element shreds INT8. |
488 | | - // Known BYTE elements land in element.typed_value; an unknown element cannot be reconstructed. |
489 | | - MessageType shreddedSchema = MessageTypeParser.parseMessageType("message variant {" |
490 | | - + " required binary metadata;" |
491 | | - + " optional binary value;" |
492 | | - + " optional group typed_value (LIST) {" |
493 | | - + " repeated group list {" |
494 | | - + " required group element {" |
495 | | - + " optional binary value;" |
496 | | - + " optional int32 typed_value (INTEGER(8, true));" |
497 | | - + " }" |
498 | | - + " }" |
499 | | - + " }" |
500 | | - + "}"); |
501 | | - |
502 | | - // Array [BYTE(10), unknown-type-63, BYTE(20)]: the top-level array is well-formed, so it |
503 | | - // constructs; the unknown element is only rejected when the writer descends into it. |
504 | | - final byte unknownPrimitive = (byte) 0xFC; |
505 | | - byte[] value = {0b0011, 0x03, 0x00, 0x02, 0x03, 0x05, 0x0C, 0x0A, unknownPrimitive, 0x0C, 0x14}; |
506 | | - Variant variant = new Variant(ByteBuffer.wrap(value), ByteBuffer.wrap(EMPTY_METADATA)); |
507 | | - assertThat(variant.getType()).isEqualTo(Variant.Type.ARRAY); |
| 479 | + public void testUnknownShreddededPrimitiveTypeReconstruction() { |
| 480 | + final byte unknownPrimitive = (byte) 0xFC; // primitive typeInfo 63 |
508 | 481 |
|
509 | | - assertThatThrownBy(() -> VariantValueWriter.write(new NoOpRecordConsumer(), shreddedSchema, variant)) |
510 | | - .isInstanceOf(IllegalArgumentException.class) |
511 | | - .hasMessageContaining("Unknown primitive type"); |
| 482 | + assertThatThrownBy( |
| 483 | + () -> new VariantBuilder().appendEncodedValue(ByteBuffer.wrap(new byte[] {unknownPrimitive}))) |
| 484 | + .isInstanceOf(UnsupportedOperationException.class) |
| 485 | + .hasMessageContaining("Unknown type in Variant"); |
512 | 486 | } |
513 | 487 |
|
514 | 488 | /** |
@@ -651,45 +625,6 @@ private byte[][] roundTrip(byte[] metadata, byte[] value) throws IOException { |
651 | 625 | } |
652 | 626 | } |
653 | 627 |
|
654 | | - /** A {@link RecordConsumer} that discards everything: used to drive a shredded write to the point of failure. */ |
655 | | - private static final class NoOpRecordConsumer extends RecordConsumer { |
656 | | - @Override |
657 | | - public void startMessage() {} |
658 | | - |
659 | | - @Override |
660 | | - public void endMessage() {} |
661 | | - |
662 | | - @Override |
663 | | - public void startField(String field, int index) {} |
664 | | - |
665 | | - @Override |
666 | | - public void endField(String field, int index) {} |
667 | | - |
668 | | - @Override |
669 | | - public void startGroup() {} |
670 | | - |
671 | | - @Override |
672 | | - public void endGroup() {} |
673 | | - |
674 | | - @Override |
675 | | - public void addInteger(int value) {} |
676 | | - |
677 | | - @Override |
678 | | - public void addLong(long value) {} |
679 | | - |
680 | | - @Override |
681 | | - public void addBoolean(boolean value) {} |
682 | | - |
683 | | - @Override |
684 | | - public void addBinary(Binary value) {} |
685 | | - |
686 | | - @Override |
687 | | - public void addFloat(float value) {} |
688 | | - |
689 | | - @Override |
690 | | - public void addDouble(double value) {} |
691 | | - } |
692 | | - |
693 | 628 | /** |
694 | 629 | * ParquetReader.Builder<Group> over an InputFile, using GroupReadSupport. |
695 | 630 | */ |
|
0 commit comments