blob: 98e8e0a551d786ee58b5090b54a3f64402fc950b [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.
"""Floating-point key bounds preserve overlapping split groups."""
from decimal import Decimal
from types import SimpleNamespace
from unittest.mock import Mock
import pytest
from pypaimon.common.options.core_options import CoreOptions
from pypaimon.common.options.options import Options
from pypaimon.manifest.schema.data_file_meta import DataFileMeta
from pypaimon.manifest.schema.manifest_entry import ManifestEntry
from pypaimon.manifest.schema.simple_stats import SimpleStats
from pypaimon.execution.python.read.interval_partition import IntervalPartition, default_key_comparator
from pypaimon.execution.python.read.scanner.primary_key_table_split_generator import (PrimaryKeyTableSplitGenerator)
from pypaimon.schema.data_types import AtomicType, DataField
from pypaimon.table.row.generic_row import GenericRow, GenericRowDeserializer, GenericRowSerializer
def _key_fields(type_name):
return [DataField(0, 'f', AtomicType(type_name)), DataField(1, 'i', AtomicType('INT'))]
def _file(name, minimum, maximum, fields):
def key(values):
row = GenericRow(list(values), fields)
return GenericRowDeserializer.from_bytes(GenericRowSerializer.to_bytes(row), fields)
return DataFileMeta(
file_name=name, file_size=100, row_count=3,
min_key=key(minimum), max_key=key(maximum),
key_stats=SimpleStats.empty_stats(), value_stats=SimpleStats.empty_stats(),
min_sequence_number=0, max_sequence_number=0, schema_id=0, level=0, extra_files=[])
@pytest.mark.parametrize('type_name', ['FLOAT', 'DOUBLE'])
def test_signed_zero_key_ranges_keep_versions_in_one_split(type_name):
fields = _key_fields(type_name)
files = [
_file('broad', (-0.0, 0), (0.0, 10), fields),
_file('point', (-0.0, 100), (-0.0, 100), fields),
]
sections = IntervalPartition(files).partition()
assert len(sections) == 1
assert sorted([f.file_name for f in run.files] for run in sections[0]) == [['broad'], ['point']]
path_factory = Mock()
path_factory.bucket_path.return_value = '/tmp/interval-test/bucket-0'
path_factory.data_file_path.return_value = '/tmp/interval-test'
table = SimpleNamespace(table_path='/tmp/interval-test', options=CoreOptions(Options({})),
path_factory=lambda: path_factory)
entries = [ManifestEntry(0, GenericRow([], []), 0, 1, file) for file in files]
splits = PrimaryKeyTableSplitGenerator(
table, 1, 1, snapshot_id=7).create_splits(entries)
assert len(splits) == 1
assert splits[0].snapshot_id == 7
assert sorted(file.file_name for file in splits[0].files) == ['broad', 'point']
assert not splits[0].raw_convertible
def test_decimal_keys_keep_numeric_equality():
fields = _key_fields('DECIMAL(10, 2)')
left = GenericRow([Decimal('-0'), 1], fields)
right = GenericRow([Decimal('0'), 1], fields)
assert default_key_comparator(left, right) == 0
@pytest.mark.parametrize('type_name', ['FLOAT', 'DOUBLE'])
def test_disjoint_finite_ranges_still_form_separate_sections(type_name):
fields = _key_fields(type_name)
sections = IntervalPartition([
_file('high', (3.0, 0), (4.0, 0), fields),
_file('low', (1.0, 0), (2.0, 0), fields),
]).partition()
assert [[f.file_name for run in section for f in run.files] for section in sections] == [
['low'], ['high']]