This is an automated email from the ASF dual-hosted git repository.

JingsongLi pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/paimon.git


The following commit(s) were added to refs/heads/master by this push:
     new 9431b894f5 [core] Support batch writes with file indexes (#10603)
9431b894f5 is described below

commit 9431b894f53338ed331c2d79c3a4824cf58398b0
Author: jianguotian <[email protected]>
AuthorDate: Fri Oct 16 13:57:31 2026 +0800

    [core] Support batch writes with file indexes (#10603)
---
 .../org/apache/paimon/io/RowDataFileWriter.java    |  47 +++-
 .../apache/paimon/io/RowDataFileWriterTest.java    | 257 ++++++++++++++++++++-
 .../format/mosaic/MosaicReaderWriterTest.java      | 105 +++++++++
 3 files changed, 397 insertions(+), 12 deletions(-)

diff --git 
a/paimon-core/src/main/java/org/apache/paimon/io/RowDataFileWriter.java 
b/paimon-core/src/main/java/org/apache/paimon/io/RowDataFileWriter.java
index 8d5fb5ede3..9dfbaf7ea1 100644
--- a/paimon-core/src/main/java/org/apache/paimon/io/RowDataFileWriter.java
+++ b/paimon-core/src/main/java/org/apache/paimon/io/RowDataFileWriter.java
@@ -116,17 +116,48 @@ public class RowDataFileWriter extends 
StatsCollectingSingleFileWriter<InternalR
 
     @Override
     public void writeBundle(BundleRecords bundle) throws IOException {
-        if (auxiliaryFileWriters.isEmpty()
-                && sequenceNumberTracker.supportsRowCountUpdate()
-                && !requiresPerRecordStats()) {
-            long rowCount = bundle.rowCount();
-            super.writeBundle(bundle);
-            sequenceNumberTracker.updateByRowCount(rowCount);
+        if (!canForwardBundle(bundle)) {
+            for (InternalRow row : bundle) {
+                write(row);
+            }
             return;
         }
 
-        for (InternalRow row : bundle) {
-            write(row);
+        long rowCount = bundle.rowCount();
+        writeFileIndexes(bundle);
+        super.writeBundle(bundle);
+        sequenceNumberTracker.updateByRowCount(rowCount);
+    }
+
+    private boolean canForwardBundle(BundleRecords bundle) {
+        if (!sequenceNumberTracker.supportsRowCountUpdate() || 
requiresPerRecordStats()) {
+            return false;
+        }
+        return auxiliaryFileWriters.isEmpty()
+                || (bundle instanceof VectorizedBundleRecords
+                        && auxiliaryFileWriters.stream()
+                                .allMatch(
+                                        writer -> writer instanceof 
DataFileIndexAuxiliaryWriter));
+    }
+
+    private void writeFileIndexes(BundleRecords bundle) throws IOException {
+        if (auxiliaryFileWriters.isEmpty()) {
+            return;
+        }
+        if (closed) {
+            throw new RuntimeException("Writer has already closed!");
+        }
+        // Vectorized bundles expose repeatable row views without consuming 
the batch.
+        // Read index fields before the format writer may transfer the 
borrowed buffers.
+        try {
+            for (InternalRow row : bundle) {
+                for (DataFileAuxiliaryWriter auxiliaryFileWriter : 
auxiliaryFileWriters) {
+                    auxiliaryFileWriter.write(row);
+                }
+            }
+        } catch (Throwable e) {
+            abort();
+            throw e;
         }
     }
 
diff --git 
a/paimon-core/src/test/java/org/apache/paimon/io/RowDataFileWriterTest.java 
b/paimon-core/src/test/java/org/apache/paimon/io/RowDataFileWriterTest.java
index 192ba65e69..887b969ceb 100644
--- a/paimon-core/src/test/java/org/apache/paimon/io/RowDataFileWriterTest.java
+++ b/paimon-core/src/test/java/org/apache/paimon/io/RowDataFileWriterTest.java
@@ -21,33 +21,50 @@ package org.apache.paimon.io;
 import org.apache.paimon.CoreOptions;
 import org.apache.paimon.data.GenericRow;
 import org.apache.paimon.data.InternalRow;
+import org.apache.paimon.data.columnar.ColumnVector;
+import org.apache.paimon.data.columnar.VectorizedColumnBatch;
+import org.apache.paimon.data.columnar.heap.HeapIntVector;
+import org.apache.paimon.fileindex.FileIndexFormat;
 import org.apache.paimon.fileindex.FileIndexOptions;
+import org.apache.paimon.fileindex.FileIndexReader;
+import org.apache.paimon.fileindex.bitmap.BitmapIndexResult;
 import org.apache.paimon.format.BundleFormatWriter;
 import org.apache.paimon.format.FileFormat;
 import org.apache.paimon.format.FormatWriter;
 import org.apache.paimon.format.FormatWriterFactory;
 import org.apache.paimon.format.SimpleColStats;
 import org.apache.paimon.format.SupportsDirectWrite;
+import org.apache.paimon.fs.ByteArraySeekableStream;
 import org.apache.paimon.fs.FileIO;
 import org.apache.paimon.fs.Path;
 import org.apache.paimon.fs.PositionOutputStream;
+import org.apache.paimon.fs.local.LocalFileIO;
 import org.apache.paimon.manifest.FileSource;
 import org.apache.paimon.options.Options;
+import org.apache.paimon.predicate.FieldRef;
 import org.apache.paimon.table.SpecialFields;
 import org.apache.paimon.types.DataTypes;
 import org.apache.paimon.types.RowType;
 import org.apache.paimon.utils.LongCounter;
+import org.apache.paimon.utils.RoaringBitmap32;
 
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
 
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Iterator;
 import java.util.List;
 
+import static org.apache.paimon.io.DataFilePathFactory.dataFileToFileIndexPath;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -57,6 +74,8 @@ class RowDataFileWriterTest {
     private static final Path PATH = new Path("file:/tmp/data-file");
     private static final RowType ROW_TYPE = RowType.builder().field("id", 
DataTypes.INT()).build();
 
+    @TempDir java.nio.file.Path tempDir;
+
     @Test
     void testEligibleBundleIsForwardedWithoutIteration() throws Exception {
         FileIO fileIO = fileIO();
@@ -224,9 +243,9 @@ class RowDataFileWriterTest {
                         formatWriter,
                         statsProducer,
                         sequenceCounter,
-                        new FileIndexOptions());
+                        bitmapOptions());
 
-        writer.writeBundle(rows(GenericRow.of(1), GenericRow.of(2)));
+        writer.writeBundle(vectorizedRows(1, 2));
 
         assertThat(formatWriter.bundleWrites).isZero();
         assertThat(formatWriter.rowWrites).isEqualTo(2);
@@ -288,6 +307,165 @@ class RowDataFileWriterTest {
         assertThat(writer.result().embeddedIndex()).isNotNull();
     }
 
+    @ParameterizedTest
+    @ValueSource(booleans = {false, true})
+    void testVectorizedBundlesPreserveBitmapPositions(boolean externalIndex) 
throws Exception {
+        Options options = new Options();
+        options.set("file-index.bitmap.columns", "id");
+        options.set("file-index.in-manifest-threshold", externalIndex ? "0B" : 
"1MB");
+        FileIndexOptions indexOptions = new FileIndexOptions(new 
CoreOptions(options));
+        LongCounter sequenceCounter = new LongCounter(5);
+        List<TestingBundleFormatWriter> formatWriters = new ArrayList<>();
+        RollingFileWriterImpl<InternalRow, DataFileMeta> writer =
+                new RollingFileWriterImpl<>(
+                        () -> {
+                            TestingBundleFormatWriter formatWriter =
+                                    new TestingBundleFormatWriter();
+                            formatWriters.add(formatWriter);
+                            Path path =
+                                    new Path(
+                                            tempDir.resolve("data-" + 
formatWriters.size())
+                                                    .toUri());
+                            return createIndexedWriter(
+                                    path, formatWriter, sequenceCounter, 
indexOptions);
+                        },
+                        Long.MAX_VALUE,
+                        7L);
+        // The first file mixes row writes, selected batches and an empty 
batch.
+        writer.write(GenericRow.of(1));
+        VectorizedBundleRecords source = vectorizedRows(1, null, 2, 1);
+        writer.writeBundle(new VectorizedBundleRecords(source.batch(), new 
int[] {2, 0, 1, 2}));
+        writer.writeBundle(vectorizedRows());
+        writer.writeBundle(vectorizedRows(2, 1));
+        // Index positions must restart at zero after rolling to the second 
file.
+        writer.writeBundle(vectorizedRows(2, 1));
+        writer.close();
+
+        assertThat(formatWriters).extracting(w -> 
w.bundleWrites).containsExactly(3, 1);
+        assertThat(formatWriters).extracting(w -> 
w.rowWrites).containsExactly(1, 0);
+        assertThat(writer.recordCount()).isEqualTo(9);
+        assertThat(sequenceCounter.getValue()).isEqualTo(14);
+        List<DataFileMeta> files = writer.result();
+        
assertThat(files).extracting(DataFileMeta::rowCount).containsExactly(7L, 2L);
+        
assertThat(files).extracting(DataFileMeta::minSequenceNumber).containsExactly(5L,
 12L);
+        
assertThat(files).extracting(DataFileMeta::maxSequenceNumber).containsExactly(11L,
 13L);
+        try (FileIndexFormat.Reader reader = readFileIndex(files.get(0), 
externalIndex)) {
+            FileIndexReader index = 
reader.readColumnIndex("id").iterator().next();
+            assertBitmapPositions(index, 1, 0, 2, 6);
+            assertBitmapPositions(index, 2, 1, 4, 5);
+            assertBitmapPositions(index, null, 3);
+        }
+        try (FileIndexFormat.Reader reader = readFileIndex(files.get(1), 
externalIndex)) {
+            FileIndexReader index = 
reader.readColumnIndex("id").iterator().next();
+            assertBitmapPositions(index, 1, 1);
+            assertBitmapPositions(index, 2, 0);
+            assertBitmapPositions(index, null);
+        }
+    }
+
+    @Test
+    void testIndexedVectorizedBundleAllowsFormatRowFallback() throws Exception 
{
+        TestingFallbackBundleFormatWriter formatWriter = new 
TestingFallbackBundleFormatWriter();
+        RowDataFileWriter writer =
+                createWriter(
+                        fileIO(),
+                        ROW_TYPE,
+                        formatWriter,
+                        SimpleStatsProducer.disabledProducer(),
+                        new LongCounter(),
+                        bitmapOptions());
+        writer.writeBundle(vectorizedRows(1, 2));
+        assertThat(formatWriter.bundleWrites).isEqualTo(1);
+        assertThat(formatWriter.rowWrites).isEqualTo(2);
+        writer.close();
+        assertThat(writer.result().embeddedIndex()).isNotNull();
+    }
+
+    @Test
+    void testIndexFailureAbortsBeforeWritingBatch() throws Exception {
+        FileIO fileIO = fileIO();
+        TestingBundleFormatWriter formatWriter = new 
TestingBundleFormatWriter();
+        LongCounter sequenceCounter = new LongCounter(5);
+        RowDataFileWriter writer =
+                createWriter(
+                        fileIO,
+                        ROW_TYPE,
+                        formatWriter,
+                        SimpleStatsProducer.disabledProducer(),
+                        sequenceCounter,
+                        bitmapOptions());
+        RuntimeException failure = new RuntimeException("index field access 
failed");
+        HeapIntVector vector =
+                new HeapIntVector(2) {
+                    @Override
+                    public int getInt(int rowId) {
+                        if (rowId == 1) {
+                            throw failure;
+                        }
+                        return 1;
+                    }
+                };
+        VectorizedColumnBatch batch = new VectorizedColumnBatch(new 
ColumnVector[] {vector});
+        batch.setNumRows(2);
+        assertThatThrownBy(() -> writer.writeBundle(new 
VectorizedBundleRecords(batch, null)))
+                .isSameAs(failure);
+        assertThat(formatWriter.bundleWrites).isZero();
+        assertThat(formatWriter.closeCalls).isEqualTo(1);
+        assertThat(writer.recordCount()).isZero();
+        assertThat(sequenceCounter.getValue()).isEqualTo(5);
+        verify(fileIO).deleteQuietly(PATH);
+        verify(fileIO, 
atLeastOnce()).deleteQuietly(dataFileToFileIndexPath(PATH));
+    }
+
+    @Test
+    void testClosedWriterRejectsBundleBeforeReadingIndexFields() throws 
Exception {
+        FileIO fileIO = fileIO();
+        RowDataFileWriter writer =
+                createWriter(
+                        fileIO,
+                        ROW_TYPE,
+                        new TestingBundleFormatWriter(),
+                        SimpleStatsProducer.disabledProducer(),
+                        new LongCounter(),
+                        bitmapOptions());
+        writer.close();
+        VectorizedBundleRecords bundle =
+                new VectorizedBundleRecords(vectorizedRows(1).batch(), null) {
+                    @Override
+                    public Iterator<InternalRow> iterator() {
+                        throw new AssertionError("Closed writer must not 
consume the index batch");
+                    }
+                };
+        assertThatThrownBy(() -> writer.writeBundle(bundle))
+                .isInstanceOf(RuntimeException.class)
+                .hasMessage("Writer has already closed!");
+        verify(fileIO, never()).deleteQuietly(PATH);
+        verify(fileIO, never()).deleteQuietly(dataFileToFileIndexPath(PATH));
+    }
+
+    @Test
+    void testBundleFailureAlsoAbortsPreparedIndex() throws Exception {
+        FileIO fileIO = fileIO();
+        IOException failure = new IOException("bundle write failed");
+        TestingThrowingBundleFormatWriter formatWriter =
+                new TestingThrowingBundleFormatWriter(failure);
+        LongCounter sequenceCounter = new LongCounter(5);
+        RowDataFileWriter writer =
+                createWriter(
+                        fileIO,
+                        ROW_TYPE,
+                        formatWriter,
+                        SimpleStatsProducer.disabledProducer(),
+                        sequenceCounter,
+                        bitmapOptions());
+        assertThatThrownBy(() -> writer.writeBundle(vectorizedRows(1, 
2))).isSameAs(failure);
+        assertThat(formatWriter.closeCalls).isEqualTo(1);
+        assertThat(writer.recordCount()).isZero();
+        assertThat(sequenceCounter.getValue()).isEqualTo(5);
+        verify(fileIO).deleteQuietly(PATH);
+        verify(fileIO, 
atLeastOnce()).deleteQuietly(dataFileToFileIndexPath(PATH));
+    }
+
     @Test
     void testRowSidecarFallsBackToRows() throws Exception {
         FileIO fileIO = fileIO();
@@ -304,11 +482,11 @@ class RowDataFileWriterTest {
                         formatWriter,
                         SimpleStatsProducer.disabledProducer(),
                         new LongCounter(),
-                        new FileIndexOptions(),
+                        bitmapOptions(),
                         rowSidecarFormat,
                         rowSidecarPath);
 
-        writer.writeBundle(rows(GenericRow.of(1), GenericRow.of(2)));
+        writer.writeBundle(vectorizedRows(1, 2));
 
         assertThat(formatWriter.bundleWrites).isZero();
         assertThat(formatWriter.rowWrites).isEqualTo(2);
@@ -324,6 +502,77 @@ class RowDataFileWriterTest {
         return fileIO;
     }
 
+    private static RowDataFileWriter createIndexedWriter(
+            Path path,
+            FormatWriter formatWriter,
+            LongCounter sequenceCounter,
+            FileIndexOptions indexOptions) {
+        return new RowDataFileWriter(
+                LocalFileIO.create(),
+                new FileWriterContext(
+                        (out, compression) -> formatWriter,
+                        SimpleStatsProducer.disabledProducer(),
+                        "none"),
+                path,
+                ROW_TYPE,
+                1L,
+                () -> sequenceCounter,
+                indexOptions,
+                FileSource.APPEND,
+                false,
+                false,
+                false,
+                null,
+                null,
+                null);
+    }
+
+    private FileIndexFormat.Reader readFileIndex(DataFileMeta file, boolean 
externalIndex)
+            throws IOException {
+        if (externalIndex) {
+            assertThat(file.embeddedIndex()).isNull();
+            assertThat(file.extraFiles()).hasSize(1);
+            return FileIndexFormat.createReader(
+                    LocalFileIO.create()
+                            .newInputStream(
+                                    new 
Path(tempDir.resolve(file.extraFiles().get(0)).toUri())),
+                    ROW_TYPE);
+        }
+        assertThat(file.extraFiles()).isEmpty();
+        assertThat(file.embeddedIndex()).isNotNull();
+        return FileIndexFormat.createReader(
+                new ByteArraySeekableStream(file.embeddedIndex()), ROW_TYPE);
+    }
+
+    private static void assertBitmapPositions(
+            FileIndexReader index, Integer value, int... positions) {
+        FieldRef field = new FieldRef(0, "id", DataTypes.INT());
+        BitmapIndexResult result =
+                (BitmapIndexResult)
+                        (value == null ? index.visitIsNull(field) : 
index.visitEqual(field, value));
+        
assertThat(result.get()).isEqualTo(RoaringBitmap32.bitmapOf(positions));
+    }
+
+    private static FileIndexOptions bitmapOptions() {
+        Options options = new Options();
+        options.set("file-index.bitmap.columns", "id");
+        return new FileIndexOptions(new CoreOptions(options));
+    }
+
+    private static VectorizedBundleRecords vectorizedRows(Integer... values) {
+        HeapIntVector vector = new HeapIntVector(values.length);
+        for (int i = 0; i < values.length; i++) {
+            if (values[i] == null) {
+                vector.setNullAt(i);
+            } else {
+                vector.setInt(i, values[i]);
+            }
+        }
+        VectorizedColumnBatch batch = new VectorizedColumnBatch(new 
ColumnVector[] {vector});
+        batch.setNumRows(values.length);
+        return new VectorizedBundleRecords(batch, null);
+    }
+
     private static RowDataFileWriter createWriter(
             FileIO fileIO,
             RowType rowType,
diff --git 
a/paimon-mosaic/src/test/java/org/apache/paimon/format/mosaic/MosaicReaderWriterTest.java
 
b/paimon-mosaic/src/test/java/org/apache/paimon/format/mosaic/MosaicReaderWriterTest.java
index 3037987232..d9b3d70011 100644
--- 
a/paimon-mosaic/src/test/java/org/apache/paimon/format/mosaic/MosaicReaderWriterTest.java
+++ 
b/paimon-mosaic/src/test/java/org/apache/paimon/format/mosaic/MosaicReaderWriterTest.java
@@ -18,33 +18,47 @@
 
 package org.apache.paimon.format.mosaic;
 
+import org.apache.paimon.CoreOptions;
 import org.apache.paimon.arrow.ArrowBundleRecords;
 import org.apache.paimon.arrow.ArrowUtils;
 import org.apache.paimon.data.BinaryString;
 import org.apache.paimon.data.GenericRow;
 import org.apache.paimon.data.InternalRow;
 import org.apache.paimon.data.serializer.InternalRowSerializer;
+import org.apache.paimon.fileindex.FileIndexFormat;
+import org.apache.paimon.fileindex.FileIndexOptions;
+import org.apache.paimon.fileindex.FileIndexReader;
+import org.apache.paimon.fileindex.bitmap.BitmapIndexResult;
 import org.apache.paimon.format.BundleFormatWriter;
 import org.apache.paimon.format.FileFormatFactory;
 import org.apache.paimon.format.FormatReaderContext;
 import org.apache.paimon.format.FormatReaderFactory;
 import org.apache.paimon.format.FormatWriter;
 import org.apache.paimon.format.FormatWriterFactory;
+import org.apache.paimon.fs.ByteArraySeekableStream;
 import org.apache.paimon.fs.Path;
 import org.apache.paimon.fs.SeekableInputStream;
 import org.apache.paimon.fs.local.LocalFileIO;
+import org.apache.paimon.io.DataFileMeta;
 import org.apache.paimon.io.DataFileRecordReader;
+import org.apache.paimon.io.FileWriterContext;
+import org.apache.paimon.io.RollingFileWriter;
+import org.apache.paimon.io.RowDataFileWriter;
 import org.apache.paimon.io.VectorizedBundleRecords;
+import org.apache.paimon.manifest.FileSource;
 import org.apache.paimon.options.MemorySize;
 import org.apache.paimon.options.Options;
+import org.apache.paimon.predicate.FieldRef;
 import org.apache.paimon.predicate.Predicate;
 import org.apache.paimon.predicate.PredicateBuilder;
 import org.apache.paimon.reader.FileRecordIterator;
 import org.apache.paimon.reader.FileRecordReader;
 import org.apache.paimon.reader.RecordReader;
 import org.apache.paimon.reader.VectorizedRecordIterator;
+import org.apache.paimon.statistics.SimpleColStatsCollector;
 import org.apache.paimon.types.DataTypes;
 import org.apache.paimon.types.RowType;
+import org.apache.paimon.utils.LongCounter;
 import org.apache.paimon.utils.RoaringBitmap32;
 
 import org.apache.arrow.memory.RootAllocator;
@@ -145,6 +159,97 @@ class MosaicReaderWriterTest {
         assertThat(result.get(1).getString(1).toString()).isEqualTo("world");
     }
 
+    @Test
+    void testColumnBatchRewritePreservesVinBitmap() throws IOException {
+        RowType rowType = RowType.builder().field("vin", 
DataTypes.STRING()).build();
+        Path sourcePath = newPath();
+        Path targetPath = newPath();
+        BinaryString a = BinaryString.fromString("A");
+        BinaryString b = BinaryString.fromString("B");
+        writeRows(rowType, sourcePath, GenericRow.of(a), GenericRow.of(b), 
GenericRow.of(a));
+
+        LocalFileIO fileIO = new LocalFileIO();
+        MosaicFileFormat format = createFormat("vin");
+        Options options = new Options();
+        options.set("file-index.bitmap.columns", "vin");
+        FormatWriterFactory factory =
+                (out, compression) ->
+                        new MosaicRecordsWriter(
+                                out,
+                                rowType,
+                                new FileFormatFactory.FormatContext(new 
Options(), 2, 2),
+                                Collections.singletonList("vin"),
+                                null) {
+                            @Override
+                            public void addElement(InternalRow row) {
+                                throw new AssertionError(
+                                        "Indexed column batches must not fall 
back to row writes");
+                            }
+                        };
+        RowDataFileWriter writer =
+                new RowDataFileWriter(
+                        fileIO,
+                        new FileWriterContext(
+                                factory,
+                                RollingFileWriter.createStatsProducer(
+                                        format,
+                                        rowType,
+                                        
SimpleColStatsCollector.createFullStatsFactories(1)),
+                                "zstd"),
+                        targetPath,
+                        rowType,
+                        0L,
+                        LongCounter::new,
+                        new FileIndexOptions(new CoreOptions(options)),
+                        FileSource.COMPACT,
+                        false,
+                        false,
+                        false,
+                        null,
+                        null,
+                        null);
+        try (RecordReader<InternalRow> reader =
+                format.createReaderFactory(rowType, rowType, null)
+                        .createReader(
+                                new FormatReaderContext(
+                                        fileIO,
+                                        sourcePath,
+                                        fileIO.getFileSize(sourcePath),
+                                        null,
+                                        null))) {
+            RecordReader.RecordIterator<InternalRow> records;
+            while ((records = reader.readBatch()) != null) {
+                try {
+                    
assertThat(records).isInstanceOf(VectorizedRecordIterator.class);
+                    writer.writeBundle(
+                            new VectorizedBundleRecords(
+                                    ((VectorizedRecordIterator) 
records).batch(), null));
+                } finally {
+                    records.releaseBatch();
+                }
+            }
+        }
+        writer.close();
+
+        assertThat(readAll(rowType, rowType, targetPath, null))
+                .extracting(row -> row.getString(0))
+                .containsExactly(a, b, a);
+        DataFileMeta file = writer.result();
+        assertThat(file.rowCount()).isEqualTo(3);
+        assertThat(file.valueStats().minValues().getString(0)).isEqualTo(a);
+        assertThat(file.valueStats().maxValues().getString(0)).isEqualTo(b);
+        try (FileIndexFormat.Reader indexReader =
+                FileIndexFormat.createReader(
+                        new ByteArraySeekableStream(file.embeddedIndex()), 
rowType)) {
+            FileIndexReader index = 
indexReader.readColumnIndex("vin").iterator().next();
+            FieldRef field = new FieldRef(0, "vin", DataTypes.STRING());
+            assertThat(((BitmapIndexResult) index.visitEqual(field, a)).get())
+                    .isEqualTo(RoaringBitmap32.bitmapOf(0, 2));
+            assertThat(((BitmapIndexResult) index.visitEqual(field, b)).get())
+                    .isEqualTo(RoaringBitmap32.bitmapOf(1));
+        }
+    }
+
     @Test
     void testPublicWriterFactoryFallsBackForCrossRootArrowBundle() throws 
IOException {
         RowType rowType = RowType.builder().field("value", 
DataTypes.INT()).build();

Reply via email to