blob: b21efe65aa7fcdbedb1d49712cc00282fa94c48c [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.
"""BTree posting format and Java interoperability tests."""
import json
from pathlib import Path
import unittest
from unittest.mock import patch
from pypaimon.execution.python.globalindex.btree.btree_posting_list import add_row_ids
from pypaimon.utils.roaring_bitmap import RoaringBitmap64
def _varint(value):
data = bytearray()
while value > 127:
data.append((value & 127) | 128)
value >>= 7
data.append(value)
return bytes(data)
class BTreePostingListTest(unittest.TestCase):
def test_java_postings_accumulate_without_expanding_roaring(self):
directory = Path(__file__).parent / 'resources' / 'btree'
expected = json.loads((directory / 'expected.json').read_text())
result = RoaringBitmap64()
result.add(7)
all_rows = [7]
for key, type_ in ((1, 0), (2, 1), (3, 2), (4, 2), (5, 2)):
data = (directory / ('posting-%d.bin' % key)).read_bytes()
self.assertEqual(type_, data[0])
if type_ == 2:
with patch.object(RoaringBitmap64, '__iter__', side_effect=AssertionError('Expanded bitmap')), \
patch.object(RoaringBitmap64, 'to_list', side_effect=AssertionError('Expanded bitmap')):
add_row_ids(data, 2, result)
else:
add_row_ids(data, 2, result)
all_rows.extend(expected[str(key)])
self.assertEqual(sorted(all_rows), result.to_list())
def test_java_roaring_crosses_high_word_buckets(self):
directory = Path(__file__).parent / 'resources' / 'btree'
data = (directory / 'posting-high-buckets.bin').read_bytes()
self.assertEqual(2, data[0])
result = RoaringBitmap64()
add_row_ids(data, 2, result)
self.assertEqual(list(range((1 << 32) - 64, (1 << 32) + 64)), result.to_list())
def test_single_and_delta_numeric_boundaries(self):
values = [0, 127, 128, 16383, 16384, (1 << 32) - 1, 1 << 32, (1 << 63) - 1]
for value in values:
with self.subTest(value=value):
result = RoaringBitmap64()
add_row_ids(b'\x00' + _varint(value), 2, result)
self.assertEqual([value], result.to_list())
data = b'\x01' + _varint(len(values)) + _varint(values[0])
data += b''.join(_varint(b - a) for a, b in zip(values, values[1:]))
result = RoaringBitmap64()
add_row_ids(data, 2, result)
self.assertEqual(values, result.to_list())
def test_v1_uses_absolute_ids_and_footer_version(self):
for values in ([10], [10, 20], [0, 128, 1 << 32, (1 << 63) - 1]):
with self.subTest(values=values):
result = RoaringBitmap64()
data = _varint(len(values)) + b''.join(_varint(v) for v in values)
add_row_ids(data, 1, result)
self.assertEqual(values, result.to_list())
def test_rejects_invalid_postings(self):
oversized = _varint(1 << 63)
invalid = [
b'', b'\x63', b'\x00', b'\x00\x80',
b'\x01', b'\x01\x80', b'\x01\x00', b'\x01\x01\x0a',
b'\x01' + _varint(1 << 31), b'\x01\x02',
b'\x01\x02\x80', b'\x01\x02\x0a', b'\x01\x02\x0a\x80',
b'\x01\x02\x0a\x00', b'\x00' + oversized,
b'\x01\x02' + oversized, b'\x01\x02\x00' + oversized,
b'\x01\x02' + _varint((1 << 63) - 1) + b'\x01',
b'\x02', b'\x02invalid', b'\x02' + RoaringBitmap64().serialize(),
]
bitmap = RoaringBitmap64()
bitmap.add(1 << 63)
invalid.append(b'\x02' + bitmap.serialize())
directory = Path(__file__).parent / 'resources' / 'btree'
invalid.append((directory / 'posting-3.bin').read_bytes()[:-1])
for data in invalid:
with self.subTest(data=data):
with self.assertRaises((ValueError, IndexError)):
add_row_ids(data, 2, RoaringBitmap64())
def test_rejects_unknown_version(self):
with self.assertRaisesRegex(ValueError, 'Unsupported BTree index file version: 3'):
add_row_ids(b'\x00\x0a', 3, RoaringBitmap64())