blob: 7bc668456228b9e1831eb6f57450308281f100d9 [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.
"""Whole-partition deletes do not use the row-level changelog writer."""
from contextlib import ExitStack
from unittest.mock import MagicMock, patch
import pyarrow as pa
import pytest
from pypaimon import Schema
from pypaimon.execution.python.write.table_update import PythonTableUpdate
from pypaimon.common.predicate import Predicate
from pypaimon.common.predicate_builder import PredicateBuilder
from pypaimon.execution.native.runtime import (native_runtime_available)
from pypaimon.execution.native.write.native_update import create_native_predicate_delete, create_native_update
pytestmark = [pytest.mark.python_write, pytest.mark.python_commit]
_ENGINES = [False, pytest.param(True, marks=pytest.mark.skipif(
not native_runtime_available(), reason='pypaimon-rust is not installed'))]
def _table(catalog, native=False, guarded=False):
schema = pa.schema([('id', pa.int32()), ('pt', pa.string()), ('value', pa.int32())])
catalog.create_table('default.t', Schema.from_pyarrow_schema(
schema, partition_keys=['pt'], primary_keys=['id', 'pt'], options={
'bucket': '1', 'changelog-producer': 'input', 'write.native.enabled': 'false',
'read.native.enabled': 'false', 'scan.native-plan.enabled': 'false',
'commit.native.enabled': 'false', 'query-auth.enabled': str(guarded).lower(),
}), False)
table = catalog.get_table('default.t')
builder = table.new_batch_write_builder()
writer, commit = builder.new_write(), builder.new_commit()
try:
writer.write_arrow(pa.Table.from_pylist([
dict(id=1, pt='a', value=10), dict(id=2, pt='b', value=20),
], schema=schema))
messages = writer.prepare_commit()
assert sum(len(message.changelog_files) for message in messages) == 2
commit.commit(messages)
finally:
writer.close()
commit.close()
return table.copy({'write.native.enabled': str(native).lower()})
def test_only_partition_delete_qualifies_a_native_changelog_table(native_rest_catalog):
table = _table(native_rest_catalog, True)
predicates = table.new_read_builder().new_predicate_builder()
native_table = MagicMock()
with patch('pypaimon.execution.native.write.native_update.native_runtime_available', return_value=True), \
patch(
'pypaimon.execution.native.write.native_update.create_native_write_table',
return_value=native_table) as create:
assert create_native_predicate_delete(table, 'partition', predicates.equal('pt', 'a')) is not None
create.assert_called_once()
create.reset_mock()
for predicate in [None, predicates.equal('id', 1), PredicateBuilder.and_predicates([
predicates.equal('pt', 'a'), predicates.equal('value', 10)]),
Predicate('equal', 1, 'pt', ['a'], field_path=['value']),
Predicate('equal', 1, 'pt', ['a'], field_path=['pt', 'nested'])]:
assert create_native_predicate_delete(table, 'rows', predicate) is None
assert create_native_update(table, 'rows', ['value']) is None
create.assert_not_called()
assert create_native_predicate_delete(object(), 'unsupported', None) is None
@pytest.mark.parametrize('native', _ENGINES)
@pytest.mark.parametrize('guarded', [False, True])
def test_input_changelog_partition_delete_keeps_other_partitions(native_rest_catalog, native, guarded):
table = _table(native_rest_catalog, native, guarded)
predicate = table.new_read_builder().new_predicate_builder().equal('pt', 'a')
builder = table.new_batch_write_builder()
with ExitStack() as stack:
if native:
stack.enter_context(patch.object(
PythonTableUpdate, '_delete_by_partition_filter', side_effect=AssertionError('Python delete fallback')))
messages = builder.new_update().delete_by_predicate(predicate)
assert [tuple(message.partition) for message in messages] == [('a',)]
assert sum(len(message.deleted_files) for message in messages) == 1
assert all(not message.new_files and not message.changelog_files for message in messages)
commit = builder.new_commit()
try:
commit.commit(messages)
finally:
commit.close()
read = table.new_read_builder()
assert read.new_read().to_arrow(read.new_scan().plan().splits()).to_pylist() == [
dict(id=2, pt='b', value=20)]