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 0b67f36c3e [python] Enable Parquet page indexes when supported (#9976)
0b67f36c3e is described below

commit 0b67f36c3ed0d7cc139ce903e010f17263d4fc73
Author: XiaoHongbo <[email protected]>
AuthorDate: Sun Sep 20 13:31:01 2026 +0800

    [python] Enable Parquet page indexes when supported (#9976)
---
 docs/docs/pypaimon/writing.md                      |  10 +
 .../pypaimon/common/options/core_options.py        |  14 ++
 .../pypaimon/table/format/format_table_write.py    |  12 +-
 .../tests/parquet_write_page_index_test.py         | 250 +++++++++++++++++++++
 .../pypaimon/write/map_shared_shredding_writer.py  |   4 +-
 .../pypaimon/write/table_update_by_row_id.py       |   3 +-
 paimon-python/pypaimon/write/writer/data_writer.py |  12 +-
 .../write/writer/parquet_writer_options.py         |  31 +++
 .../pypaimon/write/writer/single_file_writer.py    |   4 +-
 .../pypaimon/write/writer/vector_writer.py         |   8 +-
 10 files changed, 338 insertions(+), 10 deletions(-)

diff --git a/docs/docs/pypaimon/writing.md b/docs/docs/pypaimon/writing.md
index d42f5c4b06..e1d79ce544 100644
--- a/docs/docs/pypaimon/writing.md
+++ b/docs/docs/pypaimon/writing.md
@@ -83,6 +83,16 @@ write_builder = table.new_batch_write_builder().overwrite()
 write_builder = table.new_batch_write_builder().overwrite({'dt': '2024-01-01'})
 ```
 
+### Parquet Page Indexes
+
+Python Parquet writers write page indexes by default on PyArrow >= 13; older
+versions omit them. Set `parquet.write-page-index.enabled=false` to disable 
them,
+or `true` to require them. Explicit `true` on PyArrow < 13 fails before 
creating files.
+
+This affects new files only, independently of read-side filtering. When 
enabled,
+page-level statistics move from page headers into the index. Check reader
+compatibility when upgrading; PyArrow reads do not currently use these indexes.
+
 ### Manifest Merging
 
 `manifest.merge.skip-on-write-only` defaults to `false` in both Python and 
Java,
diff --git a/paimon-python/pypaimon/common/options/core_options.py 
b/paimon-python/pypaimon/common/options/core_options.py
index 50e48ab3ca..bb312781bf 100644
--- a/paimon-python/pypaimon/common/options/core_options.py
+++ b/paimon-python/pypaimon/common/options/core_options.py
@@ -350,6 +350,17 @@ class CoreOptions:
     )
 
     # File format options
+    PARQUET_WRITE_PAGE_INDEX_ENABLED: ConfigOption[bool] = (
+        ConfigOptions.key("parquet.write-page-index.enabled")
+        .boolean_type()
+        .no_default_value()
+        .with_description(
+            "Whether Python Parquet writers write page indexes. Enabled by 
default on PyArrow >= 13, "
+            "disabled on older versions. Explicit true requires PyArrow >= 13. 
"
+            "Only affects newly written files; independent of read-side index 
filtering."
+        )
+    )
+
     FILE_FORMAT: ConfigOption[str] = (
         ConfigOptions.key("file.format")
         .string_type()
@@ -1325,6 +1336,9 @@ class CoreOptions:
     def file_format(self, default=None):
         return self.options.get(CoreOptions.FILE_FORMAT, default)
 
+    def parquet_write_page_index_enabled(self) -> Optional[bool]:
+        return self.options.get(CoreOptions.PARQUET_WRITE_PAGE_INDEX_ENABLED)
+
     def file_compression(self, default=None):
         return self.options.get(CoreOptions.FILE_COMPRESSION, default)
 
diff --git a/paimon-python/pypaimon/table/format/format_table_write.py 
b/paimon-python/pypaimon/table/format/format_table_write.py
index 75b22d7186..ed33243730 100644
--- a/paimon-python/pypaimon/table/format/format_table_write.py
+++ b/paimon-python/pypaimon/table/format/format_table_write.py
@@ -31,6 +31,7 @@ from pypaimon.table.format.format_table import (
     Format,
     FormatTable,
 )
+from pypaimon.write.writer.parquet_writer_options import 
create_parquet_writer_options
 
 
 def _partition_path(
@@ -100,9 +101,12 @@ class FormatTableWrite:
         )
         self._partition_only_value = opt.lower() == "true"
         self._file_format = table.format()
-        self._target_file_row_num = CoreOptions.from_dict(
-            table.options()
-        ).target_file_row_num()
+        options = CoreOptions.from_dict(table.options())
+        self._parquet_writer_options = (
+            create_parquet_writer_options(options)
+            if self._file_format == Format.PARQUET else {}
+        )
+        self._target_file_row_num = options.target_file_row_num()
         max_target_file_row_num = (
             CoreOptions.TARGET_FILE_ROW_NUM.default_value()
         )
@@ -215,7 +219,7 @@ class FormatTableWrite:
         if fmt == Format.PARQUET:
             import pyarrow.parquet as pq
             buf = io.BytesIO()
-            pq.write_table(tbl, buf, compression="zstd")
+            pq.write_table(tbl, buf, compression="zstd", 
**self._parquet_writer_options)
             raw = buf.getvalue()
         elif fmt == Format.CSV:
             import pyarrow.csv as csv
diff --git a/paimon-python/pypaimon/tests/parquet_write_page_index_test.py 
b/paimon-python/pypaimon/tests/parquet_write_page_index_test.py
new file mode 100644
index 0000000000..752637e9e2
--- /dev/null
+++ b/paimon-python/pypaimon/tests/parquet_write_page_index_test.py
@@ -0,0 +1,250 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+from unittest.mock import patch
+
+import pyarrow as pa
+import pyarrow.parquet as pq
+import pytest
+
+from pypaimon import CatalogFactory, Schema
+from pypaimon.table.format.format_table import Format, FormatTable
+from pypaimon.write.table_update_by_row_id import _RowIdUpdateFileWriter
+
+
+OPTION = 'parquet.write-page-index.enabled'
+MODES = ['buffered', 'changelog', 'shared_shredding', 'row_id', 'format_table']
+HAS_PAGE_INDEX = int(pa.__version__.split('.')[0]) >= 13
+
+
+def _data():
+    return pa.Table.from_pydict({
+        'id': [1, 2, 3],
+        'items': [[1, None], [], None],
+        'metrics': [[('a', 10), ('b', None)], [], None],
+        'info': [{'score': 5}, None, {'score': None}],
+    }, schema=pa.schema([
+        pa.field('id', pa.int32(), nullable=False),
+        ('items', pa.list_(pa.int32())),
+        ('metrics', pa.map_(pa.string(), pa.int64())),
+        ('info', pa.struct([('score', pa.int32())])),
+    ]))
+
+
+def _table(tmp_path, mode, setting, file_format='parquet'):
+    catalog = CatalogFactory.create({'warehouse': str(tmp_path)})
+    catalog.create_database('default', True)
+    options = {'file.format': file_format}
+    if setting is not None:
+        options[OPTION] = setting
+    if mode == 'shared_shredding':
+        options['fields.metrics.map.storage-layout'] = 'shared-shredding'
+        options['fields.metrics.map.shared-shredding.max-columns'] = '2'
+    if mode == 'changelog':
+        options.update({'bucket': '1', 'changelog-producer': 'input'})
+    catalog.create_table('default.data', Schema.from_pyarrow_schema(
+        _data().schema, options=options,
+        primary_keys=['id'] if mode == 'changelog' else []), False)
+    table = catalog.get_table('default.data')
+    if mode == 'format_table':
+        table = FormatTable(table.file_io, table.identifier, 
table.table_schema,
+                            str(tmp_path / 'format'), 
Format.parse(file_format))
+    return table
+
+
+def _write(table, mode, data):
+    if mode == 'row_id':
+        writer = _RowIdUpdateFileWriter(table, (), data.column_names)
+        writer.write_batches(iter(data.to_batches()))
+        return
+    builder = table.new_batch_write_builder()
+    writer = builder.new_write()
+    commit = builder.new_commit()
+    try:
+        writer.write_arrow(data)
+        commit.commit(writer.prepare_commit())
+    finally:
+        writer.close()
+        commit.close()
+
+
[email protected]('mode', MODES)
[email protected]('setting', [None, 'false', 'true'])
+def test_written_indexes_and_round_trip(tmp_path, mode, setting):
+    if setting == 'true' and not HAS_PAGE_INDEX:
+        pytest.skip('Writing page indexes requires PyArrow >= 13')
+    table = _table(tmp_path, mode, setting)
+    data = _data()
+    _write(table, mode, data)
+    paths = list(tmp_path.rglob('*.parquet'))
+    assert len(paths) == (2 if mode == 'changelog' else 1)
+    for path in paths:
+        metadata = pq.read_metadata(str(path))
+        if HAS_PAGE_INDEX:
+            for i in range(metadata.num_row_groups):
+                for j in range(metadata.num_columns):
+                    column = metadata.row_group(i).column(j)
+                    assert column.has_column_index == (setting != 'false')
+                    assert column.has_offset_index == (setting != 'false')
+        if mode != 'shared_shredding':
+            actual = pq.read_table(str(path)).select(data.column_names)
+            assert actual.to_pydict() == data.to_pydict()
+    if mode != 'row_id':
+        reader = table.new_read_builder()
+        actual = reader.new_read().to_arrow(reader.new_scan().plan().splits())
+        assert actual.to_pydict() == data.to_pydict()
+    if mode not in ('row_id', 'format_table'):
+        reader = table.new_read_builder()
+        
reader.with_filter(reader.new_predicate_builder().greater_or_equal('id', 2))
+        reader.with_projection(['id', 'items'])
+        actual = reader.new_read().to_arrow(reader.new_scan().plan().splits())
+        assert actual.to_pydict() == data.slice(1).select(['id', 
'items']).to_pydict()
+
+
[email protected]('mode', MODES)
+def test_unsupported_arrow_rejected_before_output(tmp_path, mode):
+    table = _table(tmp_path, mode, 'true')
+    with patch.object(pa, '__version__', '12.0.1'), \
+            patch.object(table.file_io, 'new_output_stream',
+                         wraps=table.file_io.new_output_stream) as 
open_stream, \
+            patch.object(table.file_io, 'write_parquet',
+                         wraps=table.file_io.write_parquet) as write_parquet:
+        with pytest.raises(ValueError, 
match=r'parquet.write-page-index.enabled.*PyArrow >= 13'):
+            _write(table, mode, _data())
+        open_stream.assert_not_called()
+        write_parquet.assert_not_called()
+    assert not list(tmp_path.rglob('*.parquet'))
+
+
[email protected]('setting', [None, 'false'])
[email protected]('arrow_version', ['6.0.1', '12.0.1'])
+def test_legacy_arrow_omits_new_argument(tmp_path, setting, arrow_version):
+    table = _table(tmp_path, 'buffered', setting)
+    original = table.file_io.write_parquet
+    with patch.object(pa, '__version__', arrow_version), \
+            patch.object(table.file_io, 'write_parquet', wraps=original) as 
write:
+        _write(table, 'buffered', _data())
+    assert write.called
+    assert 'write_page_index' not in write.call_args[1]
+
+
+def test_explicit_false_passed_to_supported_arrow(tmp_path):
+    if not HAS_PAGE_INDEX:
+        pytest.skip('Writing page indexes requires PyArrow >= 13')
+    table = _table(tmp_path, 'buffered', 'false')
+    with patch.object(table.file_io, 'write_parquet', 
wraps=table.file_io.write_parquet) as write:
+        _write(table, 'buffered', _data())
+    assert write.call_args[1]['write_page_index'] is False
+
+
[email protected]('mode', ['buffered', 'format_table'])
+def test_other_formats_ignore_parquet_option(tmp_path, mode):
+    pytest.importorskip('pyarrow.orc')
+    table = _table(tmp_path, mode, 'true', file_format='orc')
+    with patch.object(pa, '__version__', '12.0.1'):
+        _write(table, mode, _data())
+    assert list(tmp_path.rglob('*.orc'))
+
+
+def test_parquet_changelog_with_orc_data(tmp_path):
+    pytest.importorskip('pyarrow.orc')
+    if not HAS_PAGE_INDEX:
+        pytest.skip('Writing page indexes requires PyArrow >= 13')
+    table = _table(tmp_path, 'changelog', 'true', file_format='orc')
+    table = table.copy({'changelog-file.format': 'parquet'})
+    _write(table, 'changelog', _data())
+    assert len(list(tmp_path.rglob('*.orc'))) == 1
+    paths = list(tmp_path.rglob('*.parquet'))
+    assert len(paths) == 1
+    column = pq.read_metadata(str(paths[0])).row_group(0).column(0)
+    assert column.has_column_index
+    assert column.has_offset_index
+
+
+def test_format_overwrite_checks_support_before_deleting(tmp_path):
+    table = _table(tmp_path, 'format_table', None)
+    _write(table, 'format_table', _data())
+    paths = {path: path.read_bytes() for path in tmp_path.rglob('*.parquet')}
+    table.options()[OPTION] = 'true'
+    from pypaimon.table.format.format_table_write import FormatTableWrite
+    with patch.object(pa, '__version__', '12.0.1'):
+        with pytest.raises(ValueError, match='PyArrow >= 13'):
+            FormatTableWrite(table, overwrite=True).write_arrow(_data())
+    assert {path: path.read_bytes() for path in tmp_path.rglob('*.parquet')} 
== paths
+
+
+def _vector_table(tmp_path, file_format, setting):
+    data = pa.Table.from_pydict({
+        'id': [1, 2],
+        'embedding': [[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]],
+    }, schema=pa.schema([
+        ('id', pa.int64()),
+        ('embedding', pa.list_(pa.float32(), 3)),
+    ]))
+    options = {
+        'file.format': file_format,
+        'vector.file.format': 'parquet',
+        'row-tracking.enabled': 'true',
+        'data-evolution.enabled': 'true',
+    }
+    if setting is not None:
+        options[OPTION] = setting
+    catalog = CatalogFactory.create({'warehouse': str(tmp_path)})
+    catalog.create_database('default', True)
+    catalog.create_table('default.vectors', Schema.from_pyarrow_schema(
+        data.schema, options=options), False)
+    return catalog.get_table('default.vectors'), data
+
+
[email protected]('file_format', ['parquet', 'orc'])
[email protected]('setting', [None, 'false', 'true'])
+def test_vector_parquet_indexes_and_round_trip(tmp_path, file_format, setting):
+    if setting == 'true' and not HAS_PAGE_INDEX:
+        pytest.skip('Writing page indexes requires PyArrow >= 13')
+    if file_format == 'orc':
+        pytest.importorskip('pyarrow.orc')
+    table, data = _vector_table(tmp_path, file_format, setting)
+    _write(table, 'buffered', data)
+    paths = list(tmp_path.rglob('*.parquet'))
+    assert len(list(tmp_path.rglob('*.vector.parquet'))) == 1
+    assert len(paths) == (2 if file_format == 'parquet' else 1)
+    for path in paths:
+        metadata = pq.read_metadata(str(path))
+        if HAS_PAGE_INDEX:
+            for i in range(metadata.num_row_groups):
+                for j in range(metadata.num_columns):
+                    column = metadata.row_group(i).column(j)
+                    assert column.has_column_index == (setting != 'false')
+                    assert column.has_offset_index == (setting != 'false')
+    reader = table.new_read_builder()
+    actual = reader.new_read().to_arrow(reader.new_scan().plan().splits())
+    assert actual.to_pydict() == data.to_pydict()
+
+
+def test_vector_parquet_rejects_unsupported_arrow_before_output(tmp_path):
+    table, data = _vector_table(tmp_path, 'orc', 'true')
+    with patch.object(pa, '__version__', '12.0.1'), \
+            patch.object(table.file_io, 'write_parquet',
+                         wraps=table.file_io.write_parquet) as write_parquet, \
+            patch.object(table.file_io, 'write_orc',
+                         wraps=table.file_io.write_orc) as write_orc:
+        with pytest.raises(ValueError, 
match=r'parquet.write-page-index.enabled.*PyArrow >= 13'):
+            _write(table, 'buffered', data)
+        write_parquet.assert_not_called()
+        write_orc.assert_not_called()
+    assert not list(tmp_path.rglob('*.parquet'))
+    assert not list(tmp_path.rglob('*.orc'))
diff --git a/paimon-python/pypaimon/write/map_shared_shredding_writer.py 
b/paimon-python/pypaimon/write/map_shared_shredding_writer.py
index 07f21edf5c..539e9a0425 100644
--- a/paimon-python/pypaimon/write/map_shared_shredding_writer.py
+++ b/paimon-python/pypaimon/write/map_shared_shredding_writer.py
@@ -36,6 +36,7 @@ from pypaimon.schema.data_types import (
     VectorType,
 )
 from pypaimon.table.bucket_mode import BucketMode
+from pypaimon.write.writer.parquet_writer_options import 
create_parquet_writer_options
 
 
 _FIELD_MAPPING = "__field_mapping"
@@ -88,6 +89,7 @@ class MapSharedShreddingWriter:
 
         self._validate_format("file.format", file_format)
         self._validate_format("changelog.file.format", changelog_format)
+        self._parquet_writer_options = create_parquet_writer_options(options)
         self._validate_compression("file.compression", 
options.file_compression())
         if options.bucket() == BucketMode.POSTPONE_BUCKET.value:
             raise ValueError(
@@ -158,7 +160,7 @@ class MapSharedShreddingWriter:
         # Bound scratch space for slot mappings/indices as well as logical 
values.
         slots = sum(c.num_columns for c in converters.values())
         batch_rows = max(1, min(1024, _CONVERSION_BYTES // max(1, slots * 16)))
-        kwargs = {'compression': compression}
+        kwargs = dict(self._parquet_writer_options, compression=compression)
         if compression.lower() == 'zstd':
             kwargs['compression_level'] = zstd_level
         try:
diff --git a/paimon-python/pypaimon/write/table_update_by_row_id.py 
b/paimon-python/pypaimon/write/table_update_by_row_id.py
index 9226f8d823..9e6275fd4a 100644
--- a/paimon-python/pypaimon/write/table_update_by_row_id.py
+++ b/paimon-python/pypaimon/write/table_update_by_row_id.py
@@ -172,7 +172,8 @@ class _RowIdUpdateFileWriter:
                     file_writer = SingleFileWriter(
                         writer.file_io, file_path, batch.schema, 
writer.file_format,
                         writer.compression, writer.zstd_level,
-                        fields, writer._get_column_stats)
+                        fields, writer._get_column_stats,
+                        parquet_options=writer.parquet_writer_options)
                 file_writer.write(batch, row_group_size=batch.num_rows)
                 del batch
             if file_writer is None:
diff --git a/paimon-python/pypaimon/write/writer/data_writer.py 
b/paimon-python/pypaimon/write/writer/data_writer.py
index 79de18f606..ad336b0b39 100644
--- a/paimon-python/pypaimon/write/writer/data_writer.py
+++ b/paimon-python/pypaimon/write/writer/data_writer.py
@@ -31,6 +31,7 @@ from pypaimon.table.bucket_mode import BucketMode
 from pypaimon.table.row.generic_row import GenericRow
 from pypaimon.write.map_shared_shredding_writer import MapSharedShreddingWriter
 from pypaimon.write.writer.mosaic_writer_options import 
create_mosaic_writer_options
+from pypaimon.write.writer.parquet_writer_options import 
create_parquet_writer_options
 from pypaimon.write.writer.write_buffer import WriteBuffer
 
 
@@ -82,6 +83,13 @@ class DataWriter(ABC):
             self.options.changelog_file_format()
             or self.file_format
         )
+        self.parquet_writer_options = (
+            create_parquet_writer_options(self.options)
+            if self.file_format == CoreOptions.FILE_FORMAT_PARQUET
+            or (changelog_producer == ChangelogProducer.INPUT
+                and self.changelog_file_format == 
CoreOptions.FILE_FORMAT_PARQUET)
+            else {}
+        )
         self.write_cols = write_cols
         self.blob_as_descriptor = self.options.blob_as_descriptor()
 
@@ -381,7 +389,9 @@ class DataWriter(ABC):
         if self._map_shared_shredding.is_active():
             return self._map_shared_shredding.write_parquet(
                 self.file_io, path, data, self.compression, self.zstd_level)
-        self.file_io.write_parquet(path, data, compression=self.compression, 
zstd_level=self.zstd_level)
+        self.file_io.write_parquet(
+            path, data, compression=self.compression, 
zstd_level=self.zstd_level,
+            **self.parquet_writer_options)
         return {}
 
     def _create_data_file_meta(self, file_name, file_path, row_count,
diff --git a/paimon-python/pypaimon/write/writer/parquet_writer_options.py 
b/paimon-python/pypaimon/write/writer/parquet_writer_options.py
new file mode 100644
index 0000000000..7665f580d4
--- /dev/null
+++ b/paimon-python/pypaimon/write/writer/parquet_writer_options.py
@@ -0,0 +1,31 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+import pyarrow as pa
+
+from pypaimon.common.options.core_options import CoreOptions
+
+
+def create_parquet_writer_options(options: CoreOptions):
+    enabled = options.parquet_write_page_index_enabled()
+    if int(pa.__version__.split('.')[0]) >= 13:
+        return {'write_page_index': True if enabled is None else enabled}
+    if enabled:
+        raise ValueError(
+            "parquet.write-page-index.enabled requires PyArrow >= 13, got 
{}".format(pa.__version__))
+    # Older PyArrow versions do not accept even write_page_index=False.
+    return {}
diff --git a/paimon-python/pypaimon/write/writer/single_file_writer.py 
b/paimon-python/pypaimon/write/writer/single_file_writer.py
index 184d013822..5abe7e90b5 100644
--- a/paimon-python/pypaimon/write/writer/single_file_writer.py
+++ b/paimon-python/pypaimon/write/writer/single_file_writer.py
@@ -26,7 +26,7 @@ class SingleFileWriter:
     """Write one file incrementally; currently only Parquet is supported."""
 
     def __init__(self, file_io, path, schema, file_format, compression, 
zstd_level,
-                 stats_fields, stats_collector):
+                 stats_fields, stats_collector, parquet_options=None):
         if file_format != CoreOptions.FILE_FORMAT_PARQUET:
             raise NotImplementedError(
                 'SingleFileWriter only supports Parquet, got 
{}'.format(file_format))
@@ -41,7 +41,7 @@ class SingleFileWriter:
         self.row_count = 0
         self.column_stats = {}
 
-        kwargs = {'compression': compression}
+        kwargs = dict(parquet_options or {}, compression=compression)
         if compression.lower() == 'zstd':
             kwargs['compression_level'] = zstd_level
         try:
diff --git a/paimon-python/pypaimon/write/writer/vector_writer.py 
b/paimon-python/pypaimon/write/writer/vector_writer.py
index a7cd6bfc58..50663af7b0 100644
--- a/paimon-python/pypaimon/write/writer/vector_writer.py
+++ b/paimon-python/pypaimon/write/writer/vector_writer.py
@@ -26,6 +26,7 @@ from pypaimon.manifest.schema.data_file_meta import 
DataFileMeta
 from pypaimon.manifest.schema.simple_stats import SimpleStats
 from pypaimon.table.row.generic_row import GenericRow
 from pypaimon.write.writer.append_only_data_writer import AppendOnlyDataWriter
+from pypaimon.write.writer.parquet_writer_options import 
create_parquet_writer_options
 
 
 class VectorWriter(AppendOnlyDataWriter):
@@ -44,6 +45,10 @@ class VectorWriter(AppendOnlyDataWriter):
         self.vector_columns = vector_columns
         self.vector_file_format = vector_file_format
         self.file_format = vector_file_format
+        self.parquet_writer_options = (
+            create_parquet_writer_options(self.options)
+            if vector_file_format == CoreOptions.FILE_FORMAT_PARQUET else {}
+        )
         self.target_file_size = options.vector_target_file_size()
         # The parent coordinates rolling at logical group boundaries.
         self.rolling_managed_by_parent = rolling_managed_by_parent
@@ -88,7 +93,8 @@ class VectorWriter(AppendOnlyDataWriter):
         elif self.vector_file_format == CoreOptions.FILE_FORMAT_PARQUET:
             self.file_io.write_parquet(file_path, data,
                                        compression=self.compression,
-                                       zstd_level=self.zstd_level)
+                                       zstd_level=self.zstd_level,
+                                       **self.parquet_writer_options)
         elif self.vector_file_format == CoreOptions.FILE_FORMAT_VORTEX:
             self.file_io.write_vortex(file_path, data)
         else:

Reply via email to