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 997d882bd3 [python] Speed up full shared-shredding MAP reconstruction 
(#10148)
997d882bd3 is described below

commit 997d882bd3899085975d1597b1c98cc17fe12069
Author: zhigang <[email protected]>
AuthorDate: Thu Sep 24 10:47:48 2026 +0800

    [python] Speed up full shared-shredding MAP reconstruction (#10148)
---
 .../pypaimon/data/map_shared_shredding.py          | 14 +++-
 .../tests/shared_shredding_full_restore_test.py    | 98 ++++++++++++++++++++++
 2 files changed, 111 insertions(+), 1 deletion(-)

diff --git a/paimon-python/pypaimon/data/map_shared_shredding.py 
b/paimon-python/pypaimon/data/map_shared_shredding.py
index 13c15df75f..1cfa783161 100644
--- a/paimon-python/pypaimon/data/map_shared_shredding.py
+++ b/paimon-python/pypaimon/data/map_shared_shredding.py
@@ -240,7 +240,19 @@ def assemble_shared_shredding_map(
             pa.types.is_list(mapping_column.type)
             or pa.types.is_large_list(mapping_column.type)):
         raise TypeError("Shared-shredding field mapping must be an array")
-    mapping = mapping_column.to_pylist()
+    mapping_values = mapping_column.values
+    # Convert integer mapping buffers in bulk instead of boxing each Arrow 
scalar.
+    # Tiny batches do not amortize the offsets and list reconstruction 
overhead.
+    if len(column) >= 32 and num_columns > 0 and not mapping_values.null_count:
+        mapping_offsets, mapping_start, mapping_end = 
_normalized_offsets(mapping_column)
+        flat_mapping = mapping_values.slice(mapping_start, mapping_end - 
mapping_start).to_numpy().tolist()
+        mapping = [
+            None if is_null else flat_mapping[start:end]
+            for start, end, is_null in zip(mapping_offsets, 
mapping_offsets[1:], mapping_column.is_null().to_pylist())
+        ]
+        del flat_mapping
+    else:
+        mapping = mapping_column.to_pylist()
     null_rows = column.is_null().to_pylist()
     overflow_offsets = None
     overflow_keys = None
diff --git a/paimon-python/pypaimon/tests/shared_shredding_full_restore_test.py 
b/paimon-python/pypaimon/tests/shared_shredding_full_restore_test.py
new file mode 100644
index 0000000000..c8ed512905
--- /dev/null
+++ b/paimon-python/pypaimon/tests/shared_shredding_full_restore_test.py
@@ -0,0 +1,98 @@
+# 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 datetime import time
+import unittest
+
+import pyarrow as pa
+
+from pypaimon.data.map_shared_shredding import assemble_shared_shredding_map
+
+
+class SharedShreddingFullRestoreTest(unittest.TestCase):
+    def test_entry_order_duplicates_unknown_ids_and_slices(self):
+        for value_type, value in [
+            (pa.int64(), 9007199254740993),
+            (pa.list_(pa.int64()), [None, 1]),
+            (pa.struct([("x", pa.int64())]), {"x": 1}),
+            (pa.map_(pa.string(), pa.int64()), [("x", None)]),
+        ]:
+            for mapping_type in [pa.list_(pa.int32()), 
pa.large_list(pa.int32())]:
+                with self.subTest(value_type=value_type, 
mapping_type=mapping_type):
+                    mapping = pa.array([[0, 0], [-1, 99], None, [-1, -1], [1, 
0]], type=mapping_type)
+                    values = pa.array([value] * 5, type=value_type)
+                    overflow = pa.array(
+                        [[(0, value), (99, value), (-1, None)], [(1, None)], 
[(0, value)], None, []],
+                        type=pa.map_(pa.int32(), value_type),
+                    )
+                    column = pa.StructArray.from_arrays(
+                        [mapping, values, pa.nulls(5, type=value_type), 
overflow],
+                        names=["__field_mapping", "__col_0", "__col_1", 
"__overflow"],
+                        mask=pa.array([False, False, True, False, False]),
+                    )
+                    expected = [
+                        [("a", value), ("a", None), ("a", value), ("neg", 
None)],
+                        [("b", None)],
+                        None,
+                        [],
+                        [("b", value), ("a", None)],
+                    ]
+                    column = pa.concat_arrays([column] * 9)
+                    expected = expected * 9
+                    for start, size in [(0, 45), (1, 44), (3, 31), (3, 32), 
(3, 33), (3, 1), (2, 0)]:
+                        result = assemble_shared_shredding_map(
+                            column.slice(start, size), pa.map_(pa.string(), 
value_type), {0: "a", 1: "b", -1: "neg"}, 2
+                        )
+                        end = start + size
+                        self.assertEqual(expected[start:end], 
result.to_pylist())
+                        self.assertEqual(pa.map_(pa.string(), value_type), 
result.type)
+
+    def test_mapping_validation_and_missing_columns(self):
+        for mapping, message in [([0], "length"), ([None, 0], "contain null"), 
(None, "length"), ([0, 1], "Missing")]:
+            column = pa.StructArray.from_arrays(
+                [pa.array([[-1, -1]] * 32 + [mapping], 
type=pa.list_(pa.int32()))], names=["__field_mapping"]
+            )
+            with self.subTest(mapping=mapping):
+                with self.assertRaisesRegex(ValueError, message):
+                    assemble_shared_shredding_map(column, pa.map_(pa.string(), 
pa.int64()), {0: "a", 1: "b"}, 2)
+        column = pa.StructArray.from_arrays(
+            [pa.array([[-1, 99]], type=pa.list_(pa.int32()))], 
names=["__field_mapping"]
+        )
+        self.assertEqual(
+            [[]], assemble_shared_shredding_map(column, pa.map_(pa.string(), 
pa.int64()), {0: "a"}, 2).to_pylist()
+        )
+
+    def test_orc_time_and_no_physical_columns(self):
+        column = pa.StructArray.from_arrays(
+            [
+                pa.array([[], []], type=pa.list_(pa.int32())),
+                pa.array([[(0, 1234)], []], type=pa.map_(pa.int32(), 
pa.int32())),
+            ],
+            names=["__field_mapping", "__overflow"],
+        )
+        result = assemble_shared_shredding_map(column, pa.map_(pa.string(), 
pa.time32("ms")), {0: "a"}, 0)
+        self.assertEqual([[("a", time(0, 0, 1, 234000))], []], 
result.to_pylist())
+
+    def test_large_integer_ids_are_not_rounded(self):
+        field_id = 2**63 + 1
+        column = pa.StructArray.from_arrays(
+            [pa.array([[field_id]] * 34, type=pa.large_list(pa.uint64())), 
pa.array(range(34), type=pa.int64())],
+            names=["__field_mapping", "__col_0"],
+        )
+        result = assemble_shared_shredding_map(
+            column.slice(1, 32), pa.map_(pa.string(), pa.int64()), {field_id: 
"a"}, 1
+        )
+        self.assertEqual([[("a", row)] for row in range(1, 33)], 
result.to_pylist())

Reply via email to