blob: 8aa66c2fa49e22835ebb3f7a6bde11ef509af401 [file]
# 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 date, datetime, time, timezone
from decimal import Decimal
import unittest
import pyarrow as pa
from pypaimon.execution.python.read.map_shared_shredding import assemble_normal_map_selected_keys
class NormalMapSelectedKeysTest(unittest.TestCase):
def test_first_match_nulls_and_slices(self):
cases = [
(pa.bool_(), True),
(pa.int8(), -128),
(pa.int16(), 32767),
(pa.int32(), -2147483648),
(pa.int64(), 9007199254740993),
(pa.float32(), float("inf")),
(pa.float64(), float("-inf")),
(pa.decimal128(28, 6), Decimal("12345678901234567890.123456")),
(pa.date32(), date(1960, 1, 1)),
(pa.time32("ms"), time(12, 34, 56, 123000)),
(pa.timestamp("us"), datetime(1960, 1, 1, 0, 0, 0, 123456)),
(pa.timestamp("us", "UTC"), datetime(1960, 1, 1, tzinfo=timezone.utc)),
(pa.binary(), b"\x00\xff"),
(pa.string(), "中文"),
(pa.list_(pa.int64()), [None, 9007199254740993]),
(pa.struct([("x", pa.int64())]), {"x": None}),
(pa.map_(pa.string(), pa.int64()), [("x", 1), ("x", None)]),
]
for key_type in [pa.string(), pa.large_string()]:
for value_type, value in cases:
with self.subTest(key_type=key_type, value_type=value_type):
rows = [
[("a", value)],
[("a", None), ("a", value)],
[("b", value), ("a", value), ("a", None)],
[],
None,
]
column = pa.array(rows, type=pa.map_(key_type, value_type))
expected = [
{"a": value, "missing": None},
{"a": None, "missing": None},
{"a": value, "missing": None},
{"a": None, "missing": None},
None,
]
for offset, size in [(0, 5), (1, 4), (2, 0), (2, 1)]:
actual = assemble_normal_map_selected_keys(
column.slice(offset, size), ["a", "missing"], value_type
)
end = offset + size
self.assertEqual(expected[offset:end], actual.to_pylist())
self.assertEqual(value_type, actual.type[0].type)
def test_null_map_hides_physical_entries(self):
column = pa.MapArray.from_arrays(
pa.array([0, None, 2, 2], type=pa.int32()), pa.array(["a", "a"]), pa.array([10, 20])
)
result = assemble_normal_map_selected_keys(column, ["a"], pa.int64())
self.assertEqual([{"a": 10}, None, {"a": None}], result.to_pylist())
self.assertEqual([10, None, None], result.field(0).to_pylist())
def test_restores_orc_time_after_selection(self):
column = pa.array([[("a", 1234)], [], None], type=pa.map_(pa.string(), pa.int32()))
actual = assemble_normal_map_selected_keys(column, ["a"], pa.time32("ms"))
self.assertEqual([{"a": time(0, 0, 1, 234000)}, {"a": None}, None], actual.to_pylist())
def test_non_string_keys_keep_comparison_semantics(self):
for key_type, key in [(pa.binary(), b"a"), (pa.int32(), 1)]:
column = pa.array([[(key, 10)]], type=pa.map_(key_type, pa.int64()))
result = assemble_normal_map_selected_keys(column, ["a"], pa.int64())
self.assertEqual([{"a": None}], result.to_pylist())
def test_extension_values_use_take_fallback(self):
class IntExtension(pa.ExtensionType):
def __init__(self):
super().__init__(pa.int64(), "paimon.test.map-int")
def __arrow_ext_serialize__(self):
return b""
@classmethod
def __arrow_ext_deserialize__(cls, storage_type, serialized):
return cls()
value_type = IntExtension()
values = pa.ExtensionArray.from_storage(value_type, pa.array([10, None, 20]))
column = pa.MapArray.from_arrays(
pa.array([0, 1, 3], type=pa.int32()), pa.array(["a", "a", "a"]), values
)
result = assemble_normal_map_selected_keys(column, ["a"], value_type)
self.assertEqual(value_type, result.field(0).type)
self.assertEqual([10, None], result.field(0).storage.to_pylist())