blob: 7c0735b0bca103af9b3ffa0fb7c4b5e4e3d0c887 [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.
"""Global-index builds delegated to Rust, retaining Python's commit API."""
from pypaimon.execution.native.table import (_native_table, _option_value_to_string)
from pypaimon.execution.native.read.builder import (_predicate_to_native)
from pypaimon.execution.native.runtime import (native_runtime_available)
from pypaimon.execution.native.write.native_commit import _rest_catalog_supported, from_native_commit_messages
def build_native_global_index(builder, partition_filter, *, execution="local", concurrency=None, ray_remote_args=None):
"""Return unpublished messages, or None before building for an ineligible table.
Rust owns snapshot selection, range planning and index file generation.
Build errors propagate: a failed native build must not start a second build.
"""
table = builder._table
from pypaimon.globalindex.vindex_options import VINDEX_IDENTIFIERS
if (builder._index_type not in ('btree', 'bitmap', 'full-text') + tuple(VINDEX_IDENTIFIERS)
or not table.options.native_write_enabled()
or (table.options.deletion_vectors_enabled() and builder._index_type == 'full-text')
or table.options.query_auth_enabled
or table.is_primary_key_table
or table.options.file_format() != 'parquet'
or not native_runtime_available()
or not _rest_catalog_supported(table)):
return None
from pypaimon_rust.datafusion import GlobalIndexBuildBuilder
if not GlobalIndexBuildBuilder.supports_index_type(builder._index_type):
return None
native = _native_table(table).new_global_index_build_builder()
native.with_index_column(builder._index_columns[0]).with_index_type(builder._index_type)
options = {str(key): _option_value_to_string(value)
for key, value in builder._user_options.items() if value is not None}
if builder._index_type == 'full-text':
from pypaimon.globalindex.full_text_options import NativeFullTextIndexOptions
options.update({'full-text.' + key: value for key, value in
NativeFullTextIndexOptions.from_options(builder._options.to_map()).to_native_options().items()})
elif builder._index_type in VINDEX_IDENTIFIERS:
from pypaimon.globalindex.vindex_options import native_options, train_sample_ratio
column = builder._index_columns[0]
# Reuse the Java-compatible option mapping; Rust's low-level builder also
# accepts native aliases which Python's public API deliberately ignores.
mapped = native_options(table.field_dict[column].type, table.options.options.to_map(),
builder._index_type, column, builder._user_options)
options = {key: value for key, value in options.items()
if key in ('global-index.row-count-per-shard', 'global-index.build.parallelism',
'vindex.build.granule.enabled')
and (key != 'vindex.build.granule.enabled' or builder._index_type != 'diskann')}
options.update({'fields.' + column + '.' + key: value for key, value in mapped.items()
if key != 'index.type'})
options['fields.' + column + '.train.sample-ratio'] = str(train_sample_ratio(
table.options.options.to_map(), builder._index_type, column, builder._user_options))
if builder._index_type == 'full-text' or builder._index_type in VINDEX_IDENTIFIERS:
# Resolve typed values and explicit default overrides through the same
# ConfigOption conversion as the local builder before crossing the FFI.
try:
parallelism = int(builder._core_options.global_index_build_parallelism())
except (TypeError, ValueError, OverflowError) as error:
raise ValueError('Invalid global-index.build.parallelism: %s' % error) from error
options['global-index.build.parallelism'] = str(parallelism)
native.with_options(options)
if partition_filter is not None:
native.with_partition_filter(_predicate_to_native(partition_filter))
if execution == "ray":
from pypaimon.ray.vector_index_build import build_native_vector_index
# Planning may authorize and capture inputs; its errors must not select
# another engine or restart a build that may already have side effects.
plan = native.plan()
return build_native_vector_index(table, plan, concurrency, ray_remote_args)
return from_native_commit_messages(table, native.build())
def build_native_index_shard(table, shard):
"""Execute captured metadata in Core; workers never replan or publish."""
native = _native_table(table).new_global_index_build_builder()
return from_native_commit_messages(table, native.build_shard(shard))