blob: a79b7cd1ac0dd6b4d6c67eac16079831567eb934 [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.
"""External MAP pairs can construct ARRAY elements in existing row updates."""
from decimal import Decimal
import pyarrow as pa
import pytest
from pypaimon.tests.native_update_container_casts_test import _apply, _check, _table
from pypaimon.tests.native_update_encoded_inputs_test import _wrap
pytestmark = pytest.mark.native_plan
def _pairs(kind):
keys = [99, 1, 3, 99, 99, 5, 99]
values = [99, 2, None, 99, 99, -6, 99]
target = pa.int32()
key_type, value_type = pa.int64(), pa.int64()
if kind == 'float':
values = [1e100, 2.75, None, 1e100, 1e100, -6.75, 1e100]
value_type = pa.float64()
elif kind == 'decimal':
values = [Decimal(str(v)) if v is not None else None for v in values]
values[1], values[5] = Decimal('2.75'), Decimal('-6.75')
value_type = pa.decimal128(10, 2)
elif kind in ('string', 'binary'):
keys = ['padding', '一', '', 'hidden', 'hidden', 'z', 'padding']
values = ['padding', '二', None, 'hidden', 'hidden', 'a\0b', 'padding']
key_type, value_type, target = pa.string(), pa.large_string(), pa.string()
if kind == 'binary':
keys = [v.encode() for v in keys]
values = [None if v is None else v.encode() for v in values]
key_type, value_type, target = pa.binary(), pa.large_binary(), pa.binary()
elif kind == 'array':
keys = [[v] for v in keys]
values = [None if v is None else [v, None] for v in values]
key_type, value_type, target = pa.list_(pa.int32()), pa.large_list(pa.int64()), pa.list_(pa.int32())
source = pa.MapArray.from_arrays(
pa.array([0, 1, 3, 5, 5, 6, 7], type=pa.int32()),
pa.array(keys, type=key_type), pa.array(values, type=value_type),
mask=pa.array([False, False, True, False, False, False]),
).slice(1, 4)
expected = [[[keys[1], values[1]], [keys[2], values[2]]], None, [], [[keys[5], values[5]]]]
if kind in ('float', 'decimal'):
expected[0][0][1], expected[3][0][1] = 2, -6
return source, pa.list_(pa.list_(target)), expected
@pytest.mark.parametrize('native', [False, True], ids=['python', 'native'])
@pytest.mark.parametrize('stream', [False, True], ids=['batch', 'stream'])
@pytest.mark.parametrize('operation', ['row_id', 'incremental', 'upsert'])
@pytest.mark.parametrize('shape', ['root', 'row', 'array', 'map'])
@pytest.mark.parametrize('kind', ['integer', 'float', 'decimal', 'string', 'binary', 'array'])
@pytest.mark.parametrize('encoded', [False, True], ids=['map', 'dictionary_map'])
def test_map_pairs_construct_nested_arrays(tmp_path, native, stream, operation, shape, kind, encoded):
source, target, expected = _pairs(kind)
if encoded:
source = pa.DictionaryArray.from_arrays(pa.array([0, 1, 2, 3], type=pa.int8()), source)
source, target, expected = _wrap(source, target, expected, shape)
table = _table(tmp_path, target, native)
_apply(table, source, native, stream, operation)
_check(table, expected)
@pytest.mark.parametrize('native', [False, True], ids=['python', 'native'])
@pytest.mark.parametrize('operation', ['predicate_array', 'predicate_callable', 'merge'])
@pytest.mark.parametrize('shape', ['root', 'row', 'array', 'map'])
def test_map_pairs_keep_safe_assignment_boundary(tmp_path, native, operation, shape):
source, target, expected = _wrap(*_pairs('integer'), shape)
table = _table(tmp_path, target, native)
snapshot = table.snapshot_manager().get_latest_snapshot().id
with pytest.raises((ValueError, TypeError, NotImplementedError)):
_apply(table, source, native, False, operation)
assert table.snapshot_manager().get_latest_snapshot().id == snapshot
_check(table, [None] * len(expected))
@pytest.mark.parametrize('native', [False, True], ids=['python', 'native'])
@pytest.mark.parametrize('operation', ['row_id', 'incremental', 'upsert'])
@pytest.mark.parametrize('side', ['key', 'value'])
def test_map_pair_integer_overflow_cannot_change_rows(tmp_path, native, operation, side):
keys, values = [1, 2, 3, 4], [5, 6, 7, 8]
(keys if side == 'key' else values)[2] = 2 ** 40
source = pa.MapArray.from_arrays(pa.array(range(5), type=pa.int32()), pa.array(keys), pa.array(values))
table = _table(tmp_path, pa.list_(pa.list_(pa.int32())), native)
snapshot = table.snapshot_manager().get_latest_snapshot().id
with pytest.raises((ValueError, TypeError, OverflowError)):
_apply(table, source, native, False, operation)
assert table.snapshot_manager().get_latest_snapshot().id == snapshot
_check(table, [None] * 4)