This is an automated email from the ASF dual-hosted git repository.
JingsongLi pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/paimon-cpp.git
The following commit(s) were added to refs/heads/main by this push:
new 131b092 fix: sync missing changes after repository migration (#154)
131b092 is described below
commit 131b0921663a2f7384b863124657a9fcfc0f6f21
Author: Zhang Jiawei <[email protected]>
AuthorDate: Thu Jul 23 14:01:55 2026 +0800
fix: sync missing changes after repository migration (#154)
* fix(build): make package config relocatable (#306)
* fix: fix thread-safe problem for file store path factory (#323)
* feat(lumina): support null values in vector column during index building
(#310)
* feat: add CreateReader api with field_name & index_type (#391)
* test: add tests for executor (#396)
* fix: fix IsThreadSafe() in UnionGlobalIndexReader (#395)
* fix: release global index writer before reader to avoid mem issue when
write index failed (#398)
* fix: complete undefined shift fixes from #308
Migrate the two remaining hunks from
alibaba/paimon-cpp@620adbe3590d4fea2c9835eebc5bb66fe41eb633; the other hunks
are already present in Apache.
* chore: remove remaining Lance references
* feat: remove support for FLOAT/DOUBLE type partition field (#333)
* refactor: remove unused path-based reader builder
---------
Co-authored-by: Yonghao Fang <[email protected]>
Co-authored-by: lxy <[email protected]>
Co-authored-by: lszskye <[email protected]>
---
CMakeLists.txt | 37 +---
PaimonConfig.cmake.in | 132 +-------------
ci/scripts/build_paimon.sh | 14 +-
cmake_modules/BuildUtils.cmake | 4 +-
docs/source/build_system.rst | 32 ++--
examples/CMakeLists.txt | 22 ++-
include/paimon/format/reader_builder.h | 4 -
include/paimon/global_index/global_index_scan.h | 16 ++
src/paimon/common/data/binary_array_writer.cpp | 3 +-
src/paimon/common/data/binary_row_writer.h | 3 +-
.../common/executor/default_executor_test.cpp | 57 ++++++
.../global_index/union_global_index_reader.cpp | 11 +-
.../global_index/union_global_index_reader.h | 4 +-
.../union_global_index_reader_test.cpp | 27 ++-
.../prefetch_file_batch_reader_impl_test.cpp | 4 -
src/paimon/common/utils/file_type_test.cpp | 2 -
.../core/global_index/global_index_scan_impl.cpp | 33 +++-
.../core/global_index/global_index_scan_impl.h | 8 +-
.../core/global_index/global_index_write_task.cpp | 14 +-
src/paimon/core/io/field_mapping_reader.cpp | 10 --
src/paimon/core/io/field_mapping_reader_test.cpp | 71 +++-----
src/paimon/core/operation/abstract_split_read.cpp | 4 -
.../core/operation/data_evolution_split_read.cpp | 2 +-
.../core/operation/orphan_files_cleaner_impl.cpp | 2 +-
.../core/operation/orphan_files_cleaner_test.cpp | 2 -
src/paimon/core/operation/raw_file_split_read.cpp | 2 +-
src/paimon/core/schema/schema_validation.cpp | 16 +-
src/paimon/core/schema/schema_validation.h | 4 +-
src/paimon/core/schema/schema_validation_test.cpp | 57 +++---
src/paimon/core/stats/simple_stats_collector.cpp | 34 ----
.../core/stats/simple_stats_collector_test.cpp | 39 ++--
src/paimon/core/utils/field_mapping_test.cpp | 51 +++---
src/paimon/core/utils/file_store_path_factory.cpp | 28 ++-
src/paimon/core/utils/file_store_path_factory.h | 5 +-
.../core/utils/file_store_path_factory_test.cpp | 89 +++++++++
src/paimon/format/avro/avro_reader_builder.h | 4 -
src/paimon/format/blob/blob_reader_builder.h | 4 -
src/paimon/format/orc/orc_reader_builder.h | 4 -
src/paimon/format/parquet/parquet_reader_builder.h | 4 -
.../global_index/lumina/lumina_global_index.cpp | 71 +++++---
.../global_index/lumina/lumina_global_index.h | 2 +
.../lumina/lumina_global_index_test.cpp | 199 ++++++++++++++++++++-
.../testing/mock/mock_format_reader_builder.h | 4 -
test/inte/append_compaction_inte_test.cpp | 3 -
test/inte/blob_table_inte_test.cpp | 27 +--
test/inte/data_evolution_table_test.cpp | 42 ++---
test/inte/global_index_test.cpp | 124 ++++++++++---
test/inte/pk_compaction_inte_test.cpp | 5 +-
test/inte/scan_and_read_inte_test.cpp | 61 -------
test/inte/write_and_read_inte_test.cpp | 5 +-
test/inte/write_inte_test.cpp | 21 +--
51 files changed, 822 insertions(+), 601 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 886cd96..1726103 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -70,18 +70,12 @@ endif()
if(PAIMON_USE_CXX11_ABI)
add_definitions(-D_GLIBCXX_USE_CXX11_ABI=1)
else()
- # lance and lumina are provided in so file, cannot be recompiled with
C++11 ABI off.
- if(PAIMON_ENABLE_LANCE)
- message(FATAL_ERROR "Lance cannot be enabled with C++11 ABI off")
- endif()
+ # Lumina is provided as a shared library and cannot be recompiled with
C++11 ABI off.
if(PAIMON_ENABLE_LUMINA)
message(FATAL_ERROR "Lumina cannot be enabled with C++11 ABI off")
endif()
add_definitions(-D_GLIBCXX_USE_CXX11_ABI=0)
endif()
-if(PAIMON_ENABLE_LANCE)
- add_definitions(-DPAIMON_ENABLE_LANCE)
-endif()
if(PAIMON_ENABLE_LUMINA)
add_definitions(-DPAIMON_ENABLE_LUMINA)
endif()
@@ -100,9 +94,9 @@ set(PAIMON_SOURCE_DIR ${PROJECT_SOURCE_DIR})
set(PAIMON_BINARY_DIR ${PROJECT_BINARY_DIR})
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH}
"${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules")
set(BUILD_SUPPORT_DIR "${CMAKE_SOURCE_DIR}/build_support")
-set(PAIMON_CMAKE_INSTALL_DIR "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
include(GNUInstallDirs)
+set(PAIMON_CMAKE_INSTALL_DIR "${CMAKE_INSTALL_LIBDIR}/cmake/Paimon")
include(DefineOptions)
if(PAIMON_OPTIONAL_INSTALL)
@@ -292,13 +286,6 @@ set(PAIMON_SHARED_PRIVATE_LINK_LIBS
${PAIMON_STATIC_LINK_LIBS})
add_subdirectory(third_party/roaring_bitmap EXCLUDE_FROM_ALL)
add_subdirectory(third_party/xxhash EXCLUDE_FROM_ALL)
-if(PAIMON_ENABLE_LANCE)
- add_subdirectory(third_party/lance EXCLUDE_FROM_ALL)
- link_directories(third_party/lance/lance_lib/target/release)
- install(FILES
${PROJECT_SOURCE_DIR}/third_party/lance/lance_lib/target/release/liblance_lib_rc.so
- DESTINATION ${CMAKE_INSTALL_LIBDIR})
-endif()
-
list(APPEND PAIMON_LINK_LIBS ${CMAKE_DL_LIBS})
list(APPEND PAIMON_SHARED_INSTALL_INTERFACE_LIBS ${CMAKE_DL_LIBS})
@@ -330,10 +317,6 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
include_directories("${CMAKE_SOURCE_DIR}/third_party/roaring_bitmap")
include_directories("${CMAKE_SOURCE_DIR}/third_party/xxhash")
-if(PAIMON_ENABLE_LANCE)
- include_directories("${CMAKE_SOURCE_DIR}/third_party/lance")
-endif()
-
include_directories(SYSTEM ${ARROW_INCLUDE_DIR})
include_directories(SYSTEM ${TBB_INCLUDE_DIR})
@@ -383,11 +366,6 @@ if(PAIMON_BUILD_TESTS)
paimon_blob_file_format_shared
"-Wl,--as-needed")
- if(PAIMON_ENABLE_LANCE)
- list(APPEND TEST_STATIC_LINK_LIBS "-Wl,--no-as-needed")
- list(APPEND TEST_STATIC_LINK_LIBS paimon_lance_file_format_shared)
- list(APPEND TEST_STATIC_LINK_LIBS "-Wl,--as-needed")
- endif()
if(PAIMON_ENABLE_ORC)
list(APPEND TEST_STATIC_LINK_LIBS "-Wl,--no-as-needed")
list(APPEND TEST_STATIC_LINK_LIBS paimon_orc_file_format_shared)
@@ -425,11 +403,11 @@ write_basic_package_version_file(
configure_package_config_file("${CMAKE_CURRENT_SOURCE_DIR}/PaimonConfig.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/PaimonConfig.cmake"
- INSTALL_DESTINATION lib/cmake/Paimon)
+ INSTALL_DESTINATION ${PAIMON_CMAKE_INSTALL_DIR})
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/PaimonConfig.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/PaimonConfigVersion.cmake"
- DESTINATION lib/cmake/Paimon)
+ DESTINATION ${PAIMON_CMAKE_INSTALL_DIR})
config_summary_message()
@@ -442,9 +420,6 @@ add_subdirectory(src/paimon/format/blob)
add_subdirectory(src/paimon/format/orc)
add_subdirectory(src/paimon/format/parquet)
add_subdirectory(src/paimon/format/avro)
-if(PAIMON_ENABLE_LANCE)
- add_subdirectory(src/paimon/format/lance)
-endif()
if(PAIMON_ENABLE_LUMINA)
add_subdirectory(src/paimon/global_index/lumina)
endif()
@@ -452,3 +427,7 @@ add_subdirectory(src/paimon/global_index/lucene)
add_subdirectory(src/paimon/testing/mock)
add_subdirectory(src/paimon/testing/utils)
add_subdirectory(test/inte)
+
+install(EXPORT PaimonTargets
+ NAMESPACE Paimon::
+ DESTINATION ${PAIMON_CMAKE_INSTALL_DIR})
diff --git a/PaimonConfig.cmake.in b/PaimonConfig.cmake.in
index e56d9d5..559417a 100644
--- a/PaimonConfig.cmake.in
+++ b/PaimonConfig.cmake.in
@@ -14,134 +14,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-# Main library
-add_library(paimon_shared SHARED IMPORTED)
-set_target_properties(paimon_shared PROPERTIES
- IMPORTED_LOCATION
"@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_LIBDIR@/libpaimon.so"
- INTERFACE_INCLUDE_DIRECTORIES "@CMAKE_INSTALL_PREFIX@/include"
-)
-add_library(paimon_static STATIC IMPORTED)
-set_target_properties(paimon_static PROPERTIES
- IMPORTED_LOCATION
"@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_LIBDIR@/libpaimon.a"
- INTERFACE_INCLUDE_DIRECTORIES "@CMAKE_INSTALL_PREFIX@/include"
-)
+@PACKAGE_INIT@
-# paimon_parquet_file_format
-add_library(paimon_parquet_file_format_shared SHARED IMPORTED)
-set_target_properties(paimon_parquet_file_format_shared PROPERTIES
- IMPORTED_LOCATION
"@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_LIBDIR@/libpaimon_parquet_file_format.so"
- INTERFACE_INCLUDE_DIRECTORIES "@CMAKE_INSTALL_PREFIX@/include"
-)
-add_library(paimon_parquet_file_format_static STATIC IMPORTED)
-set_target_properties(paimon_parquet_file_format_static PROPERTIES
- IMPORTED_LOCATION
"@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_LIBDIR@/libpaimon_parquet_file_format.a"
- INTERFACE_INCLUDE_DIRECTORIES "@CMAKE_INSTALL_PREFIX@/include"
-)
+include("${CMAKE_CURRENT_LIST_DIR}/PaimonTargets.cmake")
-# paimon_orc_file_format
-add_library(paimon_orc_file_format_shared SHARED IMPORTED)
-set_target_properties(paimon_orc_file_format_shared PROPERTIES
- IMPORTED_LOCATION
"@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_LIBDIR@/libpaimon_orc_file_format.so"
- INTERFACE_INCLUDE_DIRECTORIES "@CMAKE_INSTALL_PREFIX@/include"
-)
-add_library(paimon_orc_file_format_static STATIC IMPORTED)
-set_target_properties(paimon_orc_file_format_static PROPERTIES
- IMPORTED_LOCATION
"@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_LIBDIR@/libpaimon_orc_file_format.a"
- INTERFACE_INCLUDE_DIRECTORIES "@CMAKE_INSTALL_PREFIX@/include"
-)
-
-# paimon_local_file_system
-add_library(paimon_local_file_system_shared SHARED IMPORTED)
-set_target_properties(paimon_local_file_system_shared PROPERTIES
- IMPORTED_LOCATION
"@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_LIBDIR@/libpaimon_local_file_system.so"
- INTERFACE_INCLUDE_DIRECTORIES "@CMAKE_INSTALL_PREFIX@/include"
-)
-add_library(paimon_local_file_system_static STATIC IMPORTED)
-set_target_properties(paimon_local_file_system_static PROPERTIES
- IMPORTED_LOCATION
"@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_LIBDIR@/libpaimon_local_file_system.a"
- INTERFACE_INCLUDE_DIRECTORIES "@CMAKE_INSTALL_PREFIX@/include"
-)
-
-# paimon_avro_file_format
-add_library(paimon_avro_file_format_shared SHARED IMPORTED)
-set_target_properties(paimon_avro_file_format_shared PROPERTIES
- IMPORTED_LOCATION
"@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_LIBDIR@/libpaimon_avro_file_format.so"
- INTERFACE_INCLUDE_DIRECTORIES "@CMAKE_INSTALL_PREFIX@/include"
-)
-add_library(paimon_avro_file_format_static STATIC IMPORTED)
-set_target_properties(paimon_avro_file_format_static PROPERTIES
- IMPORTED_LOCATION
"@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_LIBDIR@/libpaimon_avro_file_format.a"
- INTERFACE_INCLUDE_DIRECTORIES "@CMAKE_INSTALL_PREFIX@/include"
-)
-
-# paimon_blob_file_format
-add_library(paimon_blob_file_format_shared SHARED IMPORTED)
-set_target_properties(paimon_blob_file_format_shared PROPERTIES
- IMPORTED_LOCATION
"@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_LIBDIR@/libpaimon_blob_file_format.so"
- INTERFACE_INCLUDE_DIRECTORIES "@CMAKE_INSTALL_PREFIX@/include"
-)
-add_library(paimon_blob_file_format_static STATIC IMPORTED)
-set_target_properties(paimon_blob_file_format_static PROPERTIES
- IMPORTED_LOCATION
"@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_LIBDIR@/libpaimon_blob_file_format.a"
- INTERFACE_INCLUDE_DIRECTORIES "@CMAKE_INSTALL_PREFIX@/include"
-)
-
-# paimon_file_index
-add_library(paimon_file_index_shared SHARED IMPORTED)
-set_target_properties(paimon_file_index_shared PROPERTIES
- IMPORTED_LOCATION
"@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_LIBDIR@/libpaimon_file_index.so"
- INTERFACE_INCLUDE_DIRECTORIES "@CMAKE_INSTALL_PREFIX@/include"
-)
-add_library(paimon_file_index_static STATIC IMPORTED)
-set_target_properties(paimon_file_index_static PROPERTIES
- IMPORTED_LOCATION
"@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_LIBDIR@/libpaimon_file_index.a"
- INTERFACE_INCLUDE_DIRECTORIES "@CMAKE_INSTALL_PREFIX@/include"
-)
-
-# paimon_global_index
-add_library(paimon_global_index_shared SHARED IMPORTED)
-set_target_properties(paimon_global_index_shared PROPERTIES
- IMPORTED_LOCATION
"@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_LIBDIR@/libpaimon_global_index.so"
- INTERFACE_INCLUDE_DIRECTORIES "@CMAKE_INSTALL_PREFIX@/include"
-)
-add_library(paimon_global_index_static STATIC IMPORTED)
-set_target_properties(paimon_global_index_static PROPERTIES
- IMPORTED_LOCATION
"@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_LIBDIR@/libpaimon_global_index.a"
- INTERFACE_INCLUDE_DIRECTORIES "@CMAKE_INSTALL_PREFIX@/include"
-)
-
-# paimon_jindo_file_system
-add_library(paimon_jindo_file_system_shared SHARED IMPORTED)
-set_target_properties(paimon_jindo_file_system_shared PROPERTIES
- IMPORTED_LOCATION
"@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_LIBDIR@/libpaimon_jindo_file_system.so"
- INTERFACE_INCLUDE_DIRECTORIES "@CMAKE_INSTALL_PREFIX@/include"
-)
-add_library(paimon_jindo_file_system_static STATIC IMPORTED)
-set_target_properties(paimon_jindo_file_system_static PROPERTIES
- IMPORTED_LOCATION
"@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_LIBDIR@/libpaimon_jindo_file_system.a"
- INTERFACE_INCLUDE_DIRECTORIES "@CMAKE_INSTALL_PREFIX@/include"
-)
-
-# paimon_lance_file_format
-add_library(paimon_lance_file_format_shared SHARED IMPORTED)
-set_target_properties(paimon_lance_file_format_shared PROPERTIES
- IMPORTED_LOCATION
"@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_LIBDIR@/libpaimon_lance_file_format.so"
- INTERFACE_INCLUDE_DIRECTORIES "@CMAKE_INSTALL_PREFIX@/include"
-)
-add_library(paimon_lance_file_format_static STATIC IMPORTED)
-set_target_properties(paimon_lance_file_format_static PROPERTIES
- IMPORTED_LOCATION
"@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_LIBDIR@/libpaimon_lance_file_format.a"
- INTERFACE_INCLUDE_DIRECTORIES "@CMAKE_INSTALL_PREFIX@/include"
-)
-
-# paimon_lumina_index
-add_library(paimon_lumina_index_shared SHARED IMPORTED)
-set_target_properties(paimon_lumina_index_shared PROPERTIES
- IMPORTED_LOCATION
"@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_LIBDIR@/libpaimon_lumina_index.so"
- INTERFACE_INCLUDE_DIRECTORIES "@CMAKE_INSTALL_PREFIX@/include"
-)
-add_library(paimon_lumina_index_static STATIC IMPORTED)
-set_target_properties(paimon_lumina_index_static PROPERTIES
- IMPORTED_LOCATION
"@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_LIBDIR@/libpaimon_lumina_index.a"
- INTERFACE_INCLUDE_DIRECTORIES "@CMAKE_INSTALL_PREFIX@/include"
-)
+check_required_components(Paimon)
diff --git a/ci/scripts/build_paimon.sh b/ci/scripts/build_paimon.sh
index 235434a..16e6fe0 100755
--- a/ci/scripts/build_paimon.sh
+++ b/ci/scripts/build_paimon.sh
@@ -21,7 +21,7 @@ source_dir=${1}
enable_sanitizer=${2:-false}
check_clang_tidy=${3:-false}
build_type=${4:-Debug}
-build_dir=${1}/build
+build_dir="${source_dir}/build"
# Display ccache status if available
if command -v ccache &> /dev/null; then
@@ -32,8 +32,8 @@ else
echo "=== ccache not found, compiling without cache acceleration ==="
fi
-mkdir ${build_dir}
-pushd ${build_dir}
+mkdir -p "${build_dir}"
+pushd "${build_dir}"
ENABLE_LUMINA="ON"
if [[ "${CC:-}" == *"gcc-8"* ]] || [[ "${CXX:-}" == *"g++-8"* ]]; then
@@ -56,9 +56,9 @@ if [[ "${enable_sanitizer}" == "true" ]]; then
)
fi
-cmake "${CMAKE_ARGS[@]}" ${source_dir}
-cmake --build . -- -j$(nproc)
-ctest --output-on-failure -j $(nproc)
+cmake "${CMAKE_ARGS[@]}" "${source_dir}"
+cmake --build . -- -j "$(nproc)"
+ctest --output-on-failure -j "$(nproc)"
if [[ "${check_clang_tidy}" == "true" ]]; then
cmake --build . --target check-clang-tidy
@@ -72,4 +72,4 @@ fi
popd
-rm -rf ${build_dir}
+rm -rf "${build_dir}"
diff --git a/cmake_modules/BuildUtils.cmake b/cmake_modules/BuildUtils.cmake
index c57336d..5f93160 100644
--- a/cmake_modules/BuildUtils.cmake
+++ b/cmake_modules/BuildUtils.cmake
@@ -150,7 +150,7 @@ function(add_paimon_lib LIB_NAME)
-Wl,--gc-sections)
install(TARGETS ${LIB_NAME}_shared ${INSTALL_IS_OPTIONAL}
- EXPORT ${LIB_NAME}_targets
+ EXPORT PaimonTargets
RUNTIME DESTINATION ${RUNTIME_INSTALL_DIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
@@ -197,7 +197,7 @@ function(add_paimon_lib LIB_NAME)
PUBLIC
"$<BUILD_INTERFACE:paimon_sanitizer_flags>")
install(TARGETS ${LIB_NAME}_static ${INSTALL_IS_OPTIONAL}
- EXPORT ${LIB_NAME}_targets
+ EXPORT PaimonTargets
RUNTIME DESTINATION ${RUNTIME_INSTALL_DIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
diff --git a/docs/source/build_system.rst b/docs/source/build_system.rst
index 6a61b68..8bcf714 100644
--- a/docs/source/build_system.rst
+++ b/docs/source/build_system.rst
@@ -52,10 +52,10 @@ for data format and file system.
find_package(Paimon REQUIRED)
add_executable(my_example my_example.cc)
- target_link_libraries(my_example PRIVATE arrow_shared
- paimon_shared
- paimon_parquet_file_format_shared
- paimon_local_file_system_shared)
+ target_link_libraries(my_example PRIVATE Arrow::arrow_shared
+ Paimon::paimon_shared
+
Paimon::paimon_parquet_file_format_shared
+
Paimon::paimon_local_file_system_shared)
Available variables and targets
-------------------------------
@@ -64,10 +64,10 @@ The directive ``find_package(Paimon REQUIRED)`` instructs
CMake to locate a
Paimon C++ installation on your system. If successful, it sets ``Paimon_FOUND``
to true if the Paimon C++ libraries were found.
-It also defines the following linkable targets (plain strings, not variables):
+It also defines the following linkable imported targets:
-* ``paimon_shared`` links to the Paimon shared libraries
-* ``paimon_static`` links to the Paimon static libraries
+* ``Paimon::paimon_shared`` links to the Paimon shared libraries
+* ``Paimon::paimon_static`` links to the Paimon static libraries
In most cases, it is recommended to use the Paimon shared libraries.
@@ -78,21 +78,21 @@ Paimon provides a set of built-in optional plugins that you
can link to as neede
- File format plugins:
- - ``paimon_parquet_file_format_shared`` /
``paimon_parquet_file_format_static``
- - ``paimon_orc_file_format_shared`` / ``paimon_orc_file_format_static``
- - ``paimon_avro_file_format_shared`` / ``paimon_avro_file_format_static``
- - ``paimon_blob_file_format_shared`` / ``paimon_blob_file_format_static``
+ - ``Paimon::paimon_parquet_file_format_shared`` /
``Paimon::paimon_parquet_file_format_static``
+ - ``Paimon::paimon_orc_file_format_shared`` /
``Paimon::paimon_orc_file_format_static``
+ - ``Paimon::paimon_avro_file_format_shared`` /
``Paimon::paimon_avro_file_format_static``
+ - ``Paimon::paimon_blob_file_format_shared`` /
``Paimon::paimon_blob_file_format_static``
- File system plugins:
- - ``paimon_local_file_system_shared`` / ``paimon_local_file_system_static``
- - ``paimon_jindo_file_system_shared`` / ``paimon_jindo_file_system_static``
+ - ``Paimon::paimon_local_file_system_shared`` /
``Paimon::paimon_local_file_system_static``
+ - ``Paimon::paimon_jindo_file_system_shared`` /
``Paimon::paimon_jindo_file_system_static``
- Index plugins:
- - ``paimon_file_index_shared`` / ``paimon_file_index_static``
- - ``paimon_lumina_index_shared`` / ``paimon_lumina_index_static``
- - ``paimon_lucene_index_shared`` / ``paimon_lucene_index_static``
+ - ``Paimon::paimon_file_index_shared`` / ``Paimon::paimon_file_index_static``
+ - ``Paimon::paimon_lumina_index_shared`` /
``Paimon::paimon_lumina_index_static``
+ - ``Paimon::paimon_lucene_index_shared`` /
``Paimon::paimon_lucene_index_static``
.. note::
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index 082cf04..4fe7a8b 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -26,20 +26,18 @@ find_package(Paimon CONFIG REQUIRED)
add_executable(read_write_demo read_write_demo.cpp)
add_executable(clean_demo clean_demo.cpp)
-include_directories(${PAIMON_INCLUDE_DIRS})
-
set(CMAKE_EXE_LINKER_FLAGS "-Wl,--no-as-needed ${CMAKE_EXE_LINKER_FLAGS}")
target_link_libraries(read_write_demo
- PRIVATE arrow_shared
- paimon_shared
- paimon_parquet_file_format_shared
- paimon_orc_file_format_shared
- paimon_local_file_system_shared)
+ PRIVATE Arrow::arrow_shared
+ Paimon::paimon_shared
+ Paimon::paimon_parquet_file_format_shared
+ Paimon::paimon_orc_file_format_shared
+ Paimon::paimon_local_file_system_shared)
target_link_libraries(clean_demo
- PRIVATE arrow_shared
- paimon_shared
- paimon_parquet_file_format_shared
- paimon_orc_file_format_shared
- paimon_local_file_system_shared)
+ PRIVATE Arrow::arrow_shared
+ Paimon::paimon_shared
+ Paimon::paimon_parquet_file_format_shared
+ Paimon::paimon_orc_file_format_shared
+ Paimon::paimon_local_file_system_shared)
diff --git a/include/paimon/format/reader_builder.h
b/include/paimon/format/reader_builder.h
index 91b604d..ca2021c 100644
--- a/include/paimon/format/reader_builder.h
+++ b/include/paimon/format/reader_builder.h
@@ -19,7 +19,6 @@
#pragma once
#include <memory>
-#include <string>
#include "paimon/memory/memory_pool.h"
#include "paimon/reader/file_batch_reader.h"
@@ -38,9 +37,6 @@ class PAIMON_EXPORT ReaderBuilder {
/// Build a file batch reader based on the created `InputStream`.
virtual Result<std::unique_ptr<FileBatchReader>> Build(
const std::shared_ptr<InputStream>& path) const = 0;
-
- /// Build a file batch reader based on the file path.
- virtual Result<std::unique_ptr<FileBatchReader>> Build(const std::string&
path) const = 0;
};
} // namespace paimon
diff --git a/include/paimon/global_index/global_index_scan.h
b/include/paimon/global_index/global_index_scan.h
index d925e2f..d8ba799 100644
--- a/include/paimon/global_index/global_index_scan.h
+++ b/include/paimon/global_index/global_index_scan.h
@@ -100,6 +100,22 @@ class PAIMON_EXPORT GlobalIndexScan {
const std::string& field_name,
const std::optional<RowRangeIndex>& row_range_index) const = 0;
+ /// Creates a `GlobalIndexReader` for a specific field with a specific
index type.
+ /// @param field_name Name of the indexed column.
+ /// @param index_type Name of index type.
+ /// @param row_range_index Optional row range that limits the scan to a
sub-range of row ids.
+ /// If not provided, the entire row range is
considered.
+ /// @return A `Result` that is:
+ /// - Successful with a reader(with global row id) if the index
exists and loads
+ /// correctly;
+ /// - Successful with nullptr if no index was built for the given
field with the given
+ /// index type;
+ /// - Error returns when loading fails (e.g., file corruption, I/O
error,
+ /// unsupported format).
+ virtual Result<std::shared_ptr<GlobalIndexReader>> CreateReader(
+ const std::string& field_name, const std::string& index_type,
+ const std::optional<RowRangeIndex>& row_range_index) const = 0;
+
/// Creates several `GlobalIndexReader`s for a specific field (looked up
by id),
/// @param field_id Field id of the indexed column.
/// @param row_range_index Optional row range that limits the scan to a
sub-range of row ids.
diff --git a/src/paimon/common/data/binary_array_writer.cpp
b/src/paimon/common/data/binary_array_writer.cpp
index 7845311..2a0961f 100644
--- a/src/paimon/common/data/binary_array_writer.cpp
+++ b/src/paimon/common/data/binary_array_writer.cpp
@@ -98,7 +98,8 @@ int32_t BinaryArrayWriter::GetFieldOffset(int32_t pos) const {
}
void BinaryArrayWriter::SetOffsetAndSize(int32_t pos, int32_t offset, int64_t
size) {
- const int64_t offset_and_size = (static_cast<int64_t>(offset) << 32) |
size;
+ const auto offset_and_size =
+ static_cast<int64_t>((static_cast<uint64_t>(offset) << 32) |
static_cast<uint64_t>(size));
segment_.PutValue<int64_t>(GetElementOffset(pos, 8), offset_and_size);
}
diff --git a/src/paimon/common/data/binary_row_writer.h
b/src/paimon/common/data/binary_row_writer.h
index 06a5816..954ba1d 100644
--- a/src/paimon/common/data/binary_row_writer.h
+++ b/src/paimon/common/data/binary_row_writer.h
@@ -103,7 +103,8 @@ class BinaryRowWriter : public AbstractBinaryWriter {
}
void SetOffsetAndSize(int32_t pos, int32_t offset, int64_t size) override {
- const int64_t offset_and_size = (static_cast<int64_t>(offset) << 32) |
size;
+ const auto offset_and_size =
static_cast<int64_t>((static_cast<uint64_t>(offset) << 32) |
+
static_cast<uint64_t>(size));
segment_.PutValue<int64_t>(GetFieldOffset(pos), offset_and_size);
}
diff --git a/src/paimon/common/executor/default_executor_test.cpp
b/src/paimon/common/executor/default_executor_test.cpp
index 26d84f8..2745a69 100644
--- a/src/paimon/common/executor/default_executor_test.cpp
+++ b/src/paimon/common/executor/default_executor_test.cpp
@@ -124,4 +124,61 @@ TEST(DefaultExecutorTest,
TestAddTaskAfterShutdownNowIgnored) {
ASSERT_EQ(executed_count.load(), 0);
}
+TEST(DefaultExecutorTest, TestAddTaskFromMultipleThreads) {
+ auto executor = CreateDefaultExecutor(/*thread_count=*/4);
+
+ constexpr int32_t kSubmitterCount = 8;
+ constexpr int32_t kTaskCountPerSubmitter = 64;
+ constexpr int32_t kTotalTaskCount = kSubmitterCount *
kTaskCountPerSubmitter;
+
+ std::vector<std::atomic<int32_t>> executed_slots(kTotalTaskCount);
+ for (auto& executed_slot : executed_slots) {
+ executed_slot.store(0);
+ }
+ std::vector<std::promise<void>> task_promises(kTotalTaskCount);
+ std::vector<std::future<void>> task_futures;
+ task_futures.reserve(kTotalTaskCount);
+ for (auto& task_promise : task_promises) {
+ task_futures.push_back(task_promise.get_future());
+ }
+ std::atomic<int32_t> ready_submitter_count = 0;
+ std::atomic<int32_t> executed_count = 0;
+ std::promise<void> start_signal;
+ std::shared_future<void> start_future = start_signal.get_future().share();
+ std::vector<std::thread> submitters;
+ submitters.reserve(kSubmitterCount);
+
+ for (int32_t submitter_index = 0; submitter_index < kSubmitterCount;
++submitter_index) {
+ submitters.emplace_back([&, submitter_index]() {
+ ++ready_submitter_count;
+ start_future.wait();
+ for (int32_t task_index = 0; task_index < kTaskCountPerSubmitter;
++task_index) {
+ const int32_t slot_index = submitter_index *
kTaskCountPerSubmitter + task_index;
+ executor->Add([&, slot_index]() {
+ ++executed_slots[slot_index];
+ ++executed_count;
+ task_promises[slot_index].set_value();
+ });
+ }
+ });
+ }
+
+ for (int32_t retry = 0; retry < 100 && ready_submitter_count.load() <
kSubmitterCount;
+ ++retry) {
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
+ }
+ const int32_t ready_submitter_count_before_start =
ready_submitter_count.load();
+ start_signal.set_value();
+ for (auto& submitter : submitters) {
+ submitter.join();
+ }
+ ASSERT_EQ(kSubmitterCount, ready_submitter_count_before_start);
+ Wait(task_futures);
+
+ ASSERT_EQ(kTotalTaskCount, executed_count.load());
+ for (const auto& executed_slot : executed_slots) {
+ ASSERT_EQ(1, executed_slot.load());
+ }
+}
+
} // namespace paimon::test
diff --git a/src/paimon/common/global_index/union_global_index_reader.cpp
b/src/paimon/common/global_index/union_global_index_reader.cpp
index 7f4e5cc..a4de3b2 100644
--- a/src/paimon/common/global_index/union_global_index_reader.cpp
+++ b/src/paimon/common/global_index/union_global_index_reader.cpp
@@ -185,6 +185,15 @@ Result<std::shared_ptr<GlobalIndexResult>>
UnionGlobalIndexReader::Union(ReaderA
return merged_result;
}
+bool UnionGlobalIndexReader::IsThreadSafe() const {
+ for (const auto& reader : readers_) {
+ if (!reader->IsThreadSafe()) {
+ return false;
+ }
+ }
+ return true;
+}
+
template <typename R>
std::vector<R> UnionGlobalIndexReader::ExecuteAllReaders(
const std::function<R(const std::shared_ptr<GlobalIndexReader>&)>& action)
{
@@ -202,7 +211,7 @@ std::vector<R> UnionGlobalIndexReader::ExecuteAllReaders(
futures.reserve(readers_.size());
for (const auto& reader : readers_) {
futures.push_back(
- Via(executor_.get(), [&action, &reader]() -> R { return
action(reader); }));
+ Via(executor_.get(), [&action, reader]() -> R { return
action(reader); }));
}
return CollectAll(futures);
}
diff --git a/src/paimon/common/global_index/union_global_index_reader.h
b/src/paimon/common/global_index/union_global_index_reader.h
index 526324e..efd42df 100644
--- a/src/paimon/common/global_index/union_global_index_reader.h
+++ b/src/paimon/common/global_index/union_global_index_reader.h
@@ -61,9 +61,7 @@ class UnionGlobalIndexReader : public GlobalIndexReader {
Result<std::shared_ptr<GlobalIndexResult>> VisitFullTextSearch(
const std::shared_ptr<FullTextSearch>& full_text_search) override;
- bool IsThreadSafe() const override {
- return false;
- }
+ bool IsThreadSafe() const override;
std::string GetIndexType() const override {
return "union";
diff --git a/src/paimon/common/global_index/union_global_index_reader_test.cpp
b/src/paimon/common/global_index/union_global_index_reader_test.cpp
index 78efd34..8d55db4 100644
--- a/src/paimon/common/global_index/union_global_index_reader_test.cpp
+++ b/src/paimon/common/global_index/union_global_index_reader_test.cpp
@@ -64,6 +64,10 @@ class FakeReader : public GlobalIndexReader {
has_scored_result_ = true;
}
+ void SetThreadSafe(bool thread_safe) {
+ thread_safe_ = thread_safe;
+ }
+
/// Counts how many times any Visit* method was invoked. Useful to assert
all readers
/// are exercised by UnionGlobalIndexReader.
int InvocationCount() const {
@@ -150,7 +154,7 @@ class FakeReader : public GlobalIndexReader {
}
bool IsThreadSafe() const override {
- return true;
+ return thread_safe_;
}
std::string GetIndexType() const override {
@@ -179,6 +183,7 @@ class FakeReader : public GlobalIndexReader {
std::vector<int64_t> scored_row_ids_;
std::vector<float> scored_scores_;
bool has_scored_result_ = false;
+ bool thread_safe_ = true;
std::atomic<int32_t> invocation_count_{0};
};
@@ -518,12 +523,24 @@ TEST_F(UnionGlobalIndexReaderTest,
TestVisitVectorSearchErrorPropagation) {
ASSERT_NOK_WITH_MSG(union_reader.VisitVectorSearch(nullptr), "vector
search failure");
}
-TEST_F(UnionGlobalIndexReaderTest, TestIsThreadSafeAlwaysFalse) {
- auto reader = std::make_shared<FakeReader>();
- std::vector<std::shared_ptr<GlobalIndexReader>> readers = {reader};
+TEST_F(UnionGlobalIndexReaderTest,
TestIsThreadSafeReturnsTrueWhenAllReadersAreSafe) {
+ auto reader1 = std::make_shared<FakeReader>();
+ auto reader2 = std::make_shared<FakeReader>();
+
+ std::vector<std::shared_ptr<GlobalIndexReader>> readers = {reader1,
reader2};
+ UnionGlobalIndexReader union_reader(std::move(readers), nullptr);
+
+ ASSERT_TRUE(union_reader.IsThreadSafe());
+}
+
+TEST_F(UnionGlobalIndexReaderTest,
TestIsThreadSafeReturnsFalseWhenAnyReaderIsNotSafe) {
+ auto reader1 = std::make_shared<FakeReader>();
+ auto reader2 = std::make_shared<FakeReader>();
+ reader2->SetThreadSafe(false);
+
+ std::vector<std::shared_ptr<GlobalIndexReader>> readers = {reader1,
reader2};
UnionGlobalIndexReader union_reader(std::move(readers), nullptr);
- // UnionGlobalIndexReader is not thread-safe regardless of inner readers
ASSERT_FALSE(union_reader.IsThreadSafe());
}
diff --git a/src/paimon/common/reader/prefetch_file_batch_reader_impl_test.cpp
b/src/paimon/common/reader/prefetch_file_batch_reader_impl_test.cpp
index e9832c1..d9b5821 100644
--- a/src/paimon/common/reader/prefetch_file_batch_reader_impl_test.cpp
+++ b/src/paimon/common/reader/prefetch_file_batch_reader_impl_test.cpp
@@ -100,10 +100,6 @@ class ControlledMockFormatReaderBuilder : public
ReaderBuilder {
data_, schema_, read_batch_size_, read_ranges_, need_prefetch_,
set_read_ranges_status);
}
- Result<std::unique_ptr<FileBatchReader>> Build(const std::string& path)
const override {
- return Status::Invalid("do not support build reader with path in mock
format");
- }
-
private:
std::shared_ptr<arrow::Array> data_;
std::shared_ptr<arrow::DataType> schema_;
diff --git a/src/paimon/common/utils/file_type_test.cpp
b/src/paimon/common/utils/file_type_test.cpp
index a358b7a..b969813 100644
--- a/src/paimon/common/utils/file_type_test.cpp
+++ b/src/paimon/common/utils/file_type_test.cpp
@@ -80,8 +80,6 @@ TEST(FileTypeTest, TestDefaultData) {
FileType::kData);
ASSERT_EQ(FileTypeUtils::Classify("dfs://cluster/db/p=1/bucket-0/data-a1b2c3d4-0.blob"),
FileType::kData);
-
ASSERT_EQ(FileTypeUtils::Classify("dfs://cluster/db/p=1/bucket-0/data-a1b2c3d4-0.vector.lance"),
- FileType::kData);
ASSERT_EQ(FileTypeUtils::Classify("dfs://cluster/db/random/unknown.bin"),
FileType::kData);
}
diff --git a/src/paimon/core/global_index/global_index_scan_impl.cpp
b/src/paimon/core/global_index/global_index_scan_impl.cpp
index 40820a2..83f1239 100644
--- a/src/paimon/core/global_index/global_index_scan_impl.cpp
+++ b/src/paimon/core/global_index/global_index_scan_impl.cpp
@@ -131,27 +131,48 @@ Result<std::shared_ptr<GlobalIndexEvaluator>>
GlobalIndexScanImpl::GetOrCreateIn
Result<std::vector<std::shared_ptr<GlobalIndexReader>>>
GlobalIndexScanImpl::CreateReaders(
int32_t field_id, const std::optional<RowRangeIndex>& row_range_index)
const {
PAIMON_ASSIGN_OR_RAISE(DataField field, table_schema_->GetField(field_id));
- return CreateReaders(field, row_range_index);
+ return CreateReaders(field, /*index_type=*/std::nullopt, row_range_index);
}
Result<std::vector<std::shared_ptr<GlobalIndexReader>>>
GlobalIndexScanImpl::CreateReaders(
const std::string& field_name, const std::optional<RowRangeIndex>&
row_range_index) const {
PAIMON_ASSIGN_OR_RAISE(DataField field,
table_schema_->GetField(field_name));
- return CreateReaders(field, row_range_index);
+ return CreateReaders(field, /*index_type=*/std::nullopt, row_range_index);
+}
+
+Result<std::shared_ptr<GlobalIndexReader>> GlobalIndexScanImpl::CreateReader(
+ const std::string& field_name, const std::string& index_type,
+ const std::optional<RowRangeIndex>& row_range_index) const {
+ PAIMON_ASSIGN_OR_RAISE(DataField field,
table_schema_->GetField(field_name));
+ PAIMON_ASSIGN_OR_RAISE(
+ std::vector<std::shared_ptr<GlobalIndexReader>> readers,
+ CreateReaders(field, std::optional<std::string>(index_type),
row_range_index));
+ if (readers.empty()) {
+ return std::shared_ptr<GlobalIndexReader>();
+ }
+ if (readers.size() != 1) {
+ return Status::Invalid(
+ fmt::format("invalid global index reader size, expected 1, actual
{}", readers.size()));
+ }
+ return readers[0];
}
Result<std::vector<std::shared_ptr<GlobalIndexReader>>>
GlobalIndexScanImpl::CreateReaders(
- const DataField& field, const std::optional<RowRangeIndex>&
row_range_index) const {
+ const DataField& field, const std::optional<std::string>& index_type,
+ const std::optional<RowRangeIndex>& row_range_index) const {
auto field_iter = index_metas_.find(field.Id());
if (field_iter == index_metas_.end()) {
return std::vector<std::shared_ptr<GlobalIndexReader>>();
}
const auto& index_type_to_metas = field_iter->second;
std::vector<std::shared_ptr<GlobalIndexReader>> readers;
- readers.reserve(index_type_to_metas.size());
- for (const auto& [index_type, range_to_metas] : index_type_to_metas) {
+ readers.reserve(index_type ? 1 : index_type_to_metas.size());
+ for (const auto& [current_index_type, range_to_metas] :
index_type_to_metas) {
+ if (index_type && current_index_type != index_type.value()) {
+ continue;
+ }
PAIMON_ASSIGN_OR_RAISE(std::unique_ptr<GlobalIndexer> indexer,
- GlobalIndexerFactory::Get(index_type,
options_.ToMap()));
+ GlobalIndexerFactory::Get(current_index_type,
options_.ToMap()));
if (!indexer) {
continue;
}
diff --git a/src/paimon/core/global_index/global_index_scan_impl.h
b/src/paimon/core/global_index/global_index_scan_impl.h
index d5f10d0..a7921d7 100644
--- a/src/paimon/core/global_index/global_index_scan_impl.h
+++ b/src/paimon/core/global_index/global_index_scan_impl.h
@@ -21,6 +21,7 @@
#include <map>
#include <memory>
+#include <optional>
#include <string>
#include <vector>
@@ -50,6 +51,10 @@ class GlobalIndexScanImpl : public GlobalIndexScan {
const std::string& field_name,
const std::optional<RowRangeIndex>& row_range_index) const override;
+ Result<std::shared_ptr<GlobalIndexReader>> CreateReader(
+ const std::string& field_name, const std::string& index_type,
+ const std::optional<RowRangeIndex>& row_range_index) const override;
+
Result<std::vector<std::shared_ptr<GlobalIndexReader>>> CreateReaders(
int32_t field_id, const std::optional<RowRangeIndex>& row_range_index)
const override;
@@ -68,7 +73,8 @@ class GlobalIndexScanImpl : public GlobalIndexScan {
Result<std::shared_ptr<GlobalIndexEvaluator>> GetOrCreateIndexEvaluator();
Result<std::vector<std::shared_ptr<GlobalIndexReader>>> CreateReaders(
- const DataField& field, const std::optional<RowRangeIndex>&
row_range_index) const;
+ const DataField& field, const std::optional<std::string>& index_type,
+ const std::optional<RowRangeIndex>& row_range_index) const;
std::vector<GlobalIndexIOMeta> ToGlobalIndexIOMetas(
const std::vector<std::shared_ptr<IndexFileMeta>>& metas) const;
diff --git a/src/paimon/core/global_index/global_index_write_task.cpp
b/src/paimon/core/global_index/global_index_write_task.cpp
index 1d2932f..a42f39a 100644
--- a/src/paimon/core/global_index/global_index_write_task.cpp
+++ b/src/paimon/core/global_index/global_index_write_task.cpp
@@ -23,6 +23,7 @@
#include "paimon/common/table/special_fields.h"
#include "paimon/common/types/data_field.h"
#include "paimon/common/utils/arrow/status_utils.h"
+#include "paimon/common/utils/scope_guard.h"
#include "paimon/core/core_options.h"
#include "paimon/core/global_index/global_index_file_manager.h"
#include "paimon/core/io/data_increment.h"
@@ -207,16 +208,21 @@ Result<std::shared_ptr<CommitMessage>>
GlobalIndexWriteTask::WriteIndex(
std::shared_ptr<GlobalIndexFileManager> index_file_manager,
CreateGlobalIndexFileManager(table_path, table_schema, core_options,
pool));
+ // create batch reader
+ PAIMON_ASSIGN_OR_RAISE(
+ std::unique_ptr<BatchReader> batch_reader,
+ CreateBatchReader(table_path, field_name, indexed_split, core_options,
pool));
+
// create global index writer
PAIMON_ASSIGN_OR_RAISE(DataField field,
table_schema->GetField(field_name));
PAIMON_ASSIGN_OR_RAISE(
std::shared_ptr<GlobalIndexWriter> global_index_writer,
CreateGlobalIndexWriter(index_type, field, index_file_manager,
core_options, pool));
- // create batch reader
- PAIMON_ASSIGN_OR_RAISE(
- std::unique_ptr<BatchReader> batch_reader,
- CreateBatchReader(table_path, field_name, indexed_split, core_options,
pool));
+ ScopeGuard guard([&]() {
+ global_index_writer.reset();
+ batch_reader.reset();
+ });
// read from data split and write to index writer
PAIMON_ASSIGN_OR_RAISE(
diff --git a/src/paimon/core/io/field_mapping_reader.cpp
b/src/paimon/core/io/field_mapping_reader.cpp
index afd5a5e..148b62d 100644
--- a/src/paimon/core/io/field_mapping_reader.cpp
+++ b/src/paimon/core/io/field_mapping_reader.cpp
@@ -220,16 +220,6 @@ Result<std::shared_ptr<arrow::Array>>
FieldMappingReader::GenerateSinglePartitio
scalar = std::make_shared<arrow::Int64Scalar>(value);
break;
}
- case arrow::Type::type::FLOAT: {
- float value =
partition_.GetFloat(partition_info_.value().idx_in_partition[idx]);
- scalar = std::make_shared<arrow::FloatScalar>(value);
- break;
- }
- case arrow::Type::type::DOUBLE: {
- double value =
partition_.GetDouble(partition_info_.value().idx_in_partition[idx]);
- scalar = std::make_shared<arrow::DoubleScalar>(value);
- break;
- }
case arrow::Type::type::STRING: {
BinaryString value =
partition_.GetString(partition_info_.value().idx_in_partition[idx]);
diff --git a/src/paimon/core/io/field_mapping_reader_test.cpp
b/src/paimon/core/io/field_mapping_reader_test.cpp
index 52775fc..4b64a0b 100644
--- a/src/paimon/core/io/field_mapping_reader_test.cpp
+++ b/src/paimon/core/io/field_mapping_reader_test.cpp
@@ -209,72 +209,53 @@ class FieldMappingReaderTest : public ::testing::Test {
TEST_F(FieldMappingReaderTest, TestGenerateSinglePartitionArray) {
PartitionInfo partition_info;
- // read schema: p9-p0
- // partition key: p0-p9
- partition_info.partition_read_schema = {DataField(9, arrow::field("p9",
arrow::date32())),
- DataField(8, arrow::field("p8",
arrow::binary())),
- DataField(7, arrow::field("p7",
arrow::utf8())),
- DataField(6, arrow::field("p6",
arrow::float64())),
- DataField(5, arrow::field("p5",
arrow::float32())),
+ // read schema: p7-p0
+ // partition key: p0-p7
+ partition_info.partition_read_schema = {DataField(7, arrow::field("p7",
arrow::date32())),
+ DataField(6, arrow::field("p6",
arrow::binary())),
+ DataField(5, arrow::field("p5",
arrow::utf8())),
DataField(4, arrow::field("p4",
arrow::int64())),
DataField(3, arrow::field("p3",
arrow::int32())),
DataField(2, arrow::field("p2",
arrow::int16())),
DataField(1, arrow::field("p1",
arrow::int8())),
DataField(0, arrow::field("p0",
arrow::boolean()))};
- partition_info.idx_in_target_read_schema = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
- partition_info.idx_in_partition = {9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
+ partition_info.idx_in_target_read_schema = {0, 1, 2, 3, 4, 5, 6, 7};
+ partition_info.idx_in_partition = {7, 6, 5, 4, 3, 2, 1, 0};
NonPartitionInfo non_part_info;
auto field_mapping = std::make_unique<FieldMapping>(partition_info,
non_part_info,
/*non_exist_field_info=*/std::nullopt);
auto partition = BinaryRowGenerator::GenerateRow(
{false, static_cast<int8_t>(1), static_cast<int16_t>(2),
static_cast<int32_t>(3),
- static_cast<int64_t>(4), static_cast<float>(5.1), 6.21,
std::string("7"),
- std::make_shared<Bytes>("8", pool_.get()), 100},
+ static_cast<int64_t>(4), std::string("5"),
std::make_shared<Bytes>("6", pool_.get()), 100},
pool_.get());
auto mapping_reader = std::make_unique<FieldMappingReader>(
- /*field_count=*/10, /*reader=*/nullptr, partition,
std::move(field_mapping), pool_);
+ /*field_count=*/8, /*reader=*/nullptr, partition,
std::move(field_mapping), pool_);
- {
- ASSERT_OK_AND_ASSIGN(auto p9_array,
mapping_reader->GenerateSinglePartitionArray(
- /*idx in read schema*/ 0,
/*batch_size=*/2));
- ASSERT_EQ(p9_array->length(), 2);
-
ASSERT_EQ(arrow::internal::checked_cast<arrow::Date32Array*>(p9_array.get())->Value(0),
- 100);
- }
- {
- ASSERT_OK_AND_ASSIGN(auto p8_array,
mapping_reader->GenerateSinglePartitionArray(
- /*idx in read schema*/ 1,
/*batch_size=*/2));
- ASSERT_EQ(p8_array->length(), 2);
-
ASSERT_EQ(arrow::internal::checked_cast<arrow::BinaryArray*>(p8_array.get())->Value(0),
- "8");
- }
{
ASSERT_OK_AND_ASSIGN(auto p7_array,
mapping_reader->GenerateSinglePartitionArray(
- /*idx in read schema*/ 2,
/*batch_size=*/1));
- ASSERT_EQ(p7_array->length(), 1);
-
ASSERT_EQ(arrow::internal::checked_cast<arrow::StringArray*>(p7_array.get())->Value(0),
- "7");
+ /*idx=*/0, /*batch_size=*/2));
+ ASSERT_EQ(p7_array->length(), 2);
+
ASSERT_EQ(arrow::internal::checked_cast<arrow::Date32Array*>(p7_array.get())->Value(0),
+ 100);
}
{
ASSERT_OK_AND_ASSIGN(auto p6_array,
mapping_reader->GenerateSinglePartitionArray(
- /*idx in read schema*/ 3,
/*batch_size=*/1));
- ASSERT_EQ(
-
arrow::internal::checked_cast<arrow::NumericArray<arrow::DoubleType>*>(p6_array.get())
- ->Value(0),
- static_cast<double>(6.21));
+ /*idx=*/1, /*batch_size=*/2));
+ ASSERT_EQ(p6_array->length(), 2);
+
ASSERT_EQ(arrow::internal::checked_cast<arrow::BinaryArray*>(p6_array.get())->Value(0),
+ "6");
}
{
ASSERT_OK_AND_ASSIGN(auto p5_array,
mapping_reader->GenerateSinglePartitionArray(
- /*idx in read schema*/ 4,
/*batch_size=*/1));
- ASSERT_EQ(
-
arrow::internal::checked_cast<arrow::NumericArray<arrow::FloatType>*>(p5_array.get())
- ->Value(0),
- static_cast<float>(5.1));
+ /*idx=*/2, /*batch_size=*/1));
+ ASSERT_EQ(p5_array->length(), 1);
+
ASSERT_EQ(arrow::internal::checked_cast<arrow::StringArray*>(p5_array.get())->Value(0),
+ "5");
}
{
ASSERT_OK_AND_ASSIGN(auto p4_array,
mapping_reader->GenerateSinglePartitionArray(
- /*idx in read schema*/ 5,
/*batch_size=*/1));
+ /*idx=*/3, /*batch_size=*/1));
ASSERT_EQ(
arrow::internal::checked_cast<arrow::NumericArray<arrow::Int64Type>*>(p4_array.get())
->Value(0),
@@ -282,7 +263,7 @@ TEST_F(FieldMappingReaderTest,
TestGenerateSinglePartitionArray) {
}
{
ASSERT_OK_AND_ASSIGN(auto p3_array,
mapping_reader->GenerateSinglePartitionArray(
- /*idx in read schema*/ 6,
/*batch_size=*/1));
+ /*idx=*/4, /*batch_size=*/1));
ASSERT_EQ(
arrow::internal::checked_cast<arrow::NumericArray<arrow::Int32Type>*>(p3_array.get())
->Value(0),
@@ -290,7 +271,7 @@ TEST_F(FieldMappingReaderTest,
TestGenerateSinglePartitionArray) {
}
{
ASSERT_OK_AND_ASSIGN(auto p2_array,
mapping_reader->GenerateSinglePartitionArray(
- /*idx in read schema*/ 7,
/*batch_size=*/1));
+ /*idx=*/5, /*batch_size=*/1));
ASSERT_EQ(
arrow::internal::checked_cast<arrow::NumericArray<arrow::Int16Type>*>(p2_array.get())
->Value(0),
@@ -298,7 +279,7 @@ TEST_F(FieldMappingReaderTest,
TestGenerateSinglePartitionArray) {
}
{
ASSERT_OK_AND_ASSIGN(auto p1_array,
mapping_reader->GenerateSinglePartitionArray(
- /*idx in read schema*/ 8,
/*batch_size=*/1));
+ /*idx=*/6, /*batch_size=*/1));
ASSERT_EQ(
arrow::internal::checked_cast<arrow::NumericArray<arrow::Int8Type>*>(p1_array.get())
->Value(0),
@@ -306,7 +287,7 @@ TEST_F(FieldMappingReaderTest,
TestGenerateSinglePartitionArray) {
}
{
ASSERT_OK_AND_ASSIGN(auto p0_array,
mapping_reader->GenerateSinglePartitionArray(
- /*idx in read schema*/ 9,
/*batch_size=*/1));
+ /*idx=*/7, /*batch_size=*/1));
ASSERT_EQ(arrow::internal::checked_cast<arrow::BooleanArray*>(p0_array.get())->Value(0),
false);
}
diff --git a/src/paimon/core/operation/abstract_split_read.cpp
b/src/paimon/core/operation/abstract_split_read.cpp
index fb57c0e..30b2215 100644
--- a/src/paimon/core/operation/abstract_split_read.cpp
+++ b/src/paimon/core/operation/abstract_split_read.cpp
@@ -148,10 +148,6 @@ Result<std::unique_ptr<FileBatchReader>>
AbstractSplitRead::CreateFileBatchReade
const std::shared_ptr<DataFileMeta>& file_meta, const std::string&
data_file_path,
const ReaderBuilder* reader_builder) const {
PAIMON_ASSIGN_OR_RAISE(std::string file_format_identifier,
file_meta->FileFormat());
- if (file_format_identifier == "lance") {
- // lance do not support stream build with input stream
- return reader_builder->Build(data_file_path);
- }
if (context_->EnablePrefetch() && file_format_identifier != "blob" &&
file_format_identifier != "avro") {
PAIMON_ASSIGN_OR_RAISE(
diff --git a/src/paimon/core/operation/data_evolution_split_read.cpp
b/src/paimon/core/operation/data_evolution_split_read.cpp
index 5e01768..81c19c6 100644
--- a/src/paimon/core/operation/data_evolution_split_read.cpp
+++ b/src/paimon/core/operation/data_evolution_split_read.cpp
@@ -199,7 +199,7 @@ Result<std::unique_ptr<FileBatchReader>>
DataEvolutionSplitRead::ApplyIndexAndDv
std::unique_ptr<FileBatchReader> reader;
if (!file_reader->SupportPreciseBitmapSelection() && selection_row_ids) {
- // several format(e.g. lance, blob) will return accurate batch result,
where
+ // Some formats (for example blob) return an accurate batch result,
where
// ApplyBitmapIndexBatchReader is not necessary
reader = std::make_unique<ApplyBitmapIndexBatchReader>(
std::move(file_reader), std::move(selection_row_ids).value());
diff --git a/src/paimon/core/operation/orphan_files_cleaner_impl.cpp
b/src/paimon/core/operation/orphan_files_cleaner_impl.cpp
index 5790d30..e582ae4 100644
--- a/src/paimon/core/operation/orphan_files_cleaner_impl.cpp
+++ b/src/paimon/core/operation/orphan_files_cleaner_impl.cpp
@@ -78,7 +78,7 @@ bool OrphanFilesCleanerImpl::SupportToClean(const
std::string& file_name) {
return true;
}
}
- static std::vector<std::string> supported_formats = {".orc", ".parquet",
".avro", ".lance"};
+ static std::vector<std::string> supported_formats = {".orc", ".parquet",
".avro"};
for (const auto& format : supported_formats) {
if (StringUtils::StartsWith(file_name, "data-") &&
StringUtils::EndsWith(file_name, format)) {
diff --git a/src/paimon/core/operation/orphan_files_cleaner_test.cpp
b/src/paimon/core/operation/orphan_files_cleaner_test.cpp
index 4fb94f8..3773104 100644
--- a/src/paimon/core/operation/orphan_files_cleaner_test.cpp
+++ b/src/paimon/core/operation/orphan_files_cleaner_test.cpp
@@ -41,8 +41,6 @@ TEST(OrphanFilesCleanerTest, TestSupportToClean) {
OrphanFilesCleanerImpl::SupportToClean("data-2d5ea1ea-77c1-47ff-bb87-19a509962a37-0.avro"));
ASSERT_TRUE(OrphanFilesCleanerImpl::SupportToClean(
"data-2d5ea1ea-77c1-47ff-bb87-19a509962a37-0.parquet"));
- ASSERT_TRUE(OrphanFilesCleanerImpl::SupportToClean(
- "data-2d5ea1ea-77c1-47ff-bb87-19a509962a37-0.lance"));
ASSERT_TRUE(
OrphanFilesCleanerImpl::SupportToClean("manifest-3ea5ee21-d399-4f1c-a749-2fc63dbf0852-0"));
ASSERT_TRUE(OrphanFilesCleanerImpl::SupportToClean(
diff --git a/src/paimon/core/operation/raw_file_split_read.cpp
b/src/paimon/core/operation/raw_file_split_read.cpp
index c351254..c06fb05 100644
--- a/src/paimon/core/operation/raw_file_split_read.cpp
+++ b/src/paimon/core/operation/raw_file_split_read.cpp
@@ -182,7 +182,7 @@ Result<std::unique_ptr<FileBatchReader>>
RawFileSplitRead::ApplyIndexAndDvReader
std::unique_ptr<FileBatchReader> reader;
if (!file_reader->SupportPreciseBitmapSelection() && actual_selection) {
- // several format(e.g. lance, blob) will return accurate batch result,
where
+ // Some formats (for example blob) return an accurate batch result,
where
// ApplyBitmapIndexBatchReader is not necessary
reader =
std::make_unique<ApplyBitmapIndexBatchReader>(std::move(file_reader),
std::move(actual_selection).value());
diff --git a/src/paimon/core/schema/schema_validation.cpp
b/src/paimon/core/schema/schema_validation.cpp
index 28949a4..17037ed 100644
--- a/src/paimon/core/schema/schema_validation.cpp
+++ b/src/paimon/core/schema/schema_validation.cpp
@@ -73,9 +73,9 @@ Status SchemaValidation::ValidateTableSchema(const
TableSchema& schema) {
ValidateOnlyContainPrimitiveType(schema.Fields(),
schema.PrimaryKeys(), "primary key"));
PAIMON_RETURN_NOT_OK(
ValidateOnlyContainPrimitiveType(schema.Fields(),
schema.PartitionKeys(), "partition"));
- // TODO(lisizhuo.lsz): C++ Paimon do not support timestamp & decimal type
in partition keys for
- // now.
- PAIMON_RETURN_NOT_OK(ValidateNotContainComplexType(schema.Fields(),
schema.PartitionKeys()));
+ // TODO(lisizhuo.lsz): C++ Paimon do not support timestamp & decimal &
float & double type in
+ // partition keys for now.
+ PAIMON_RETURN_NOT_OK(ValidateNotContainSpecificType(schema.Fields(),
schema.PartitionKeys()));
PAIMON_ASSIGN_OR_RAISE(CoreOptions options,
CoreOptions::FromMap(schema.Options()));
PAIMON_RETURN_NOT_OK(ValidateBucket(schema, options));
@@ -163,7 +163,7 @@ Status SchemaValidation::ValidateOnlyContainPrimitiveType(
return Status::OK();
}
-Status SchemaValidation::ValidateNotContainComplexType(
+Status SchemaValidation::ValidateNotContainSpecificType(
const std::vector<DataField>& fields, const std::vector<std::string>&
field_names) {
if (field_names.empty()) {
return Status::OK();
@@ -178,8 +178,12 @@ Status SchemaValidation::ValidateNotContainComplexType(
auto field = it->second;
if (IsComplexType(field)) {
return Status::Invalid(
- fmt::format("The field {} in partition field {} is
unsupported",
- field->ToString(), it->first));
+ fmt::format("partition field {} cannot be
TIMESTAMP/DECIMAL/BLOB", field_name));
+ }
+ if (field->type()->id() == arrow::Type::FLOAT ||
+ field->type()->id() == arrow::Type::DOUBLE) {
+ return Status::Invalid(
+ fmt::format("partition field {} cannot be FLOAT/DOUBLE",
field_name));
}
} else {
assert(false);
diff --git a/src/paimon/core/schema/schema_validation.h
b/src/paimon/core/schema/schema_validation.h
index af73ee6..778f8a3 100644
--- a/src/paimon/core/schema/schema_validation.h
+++ b/src/paimon/core/schema/schema_validation.h
@@ -54,8 +54,8 @@ class SchemaValidation {
static Status ValidateOnlyContainPrimitiveType(const
std::vector<DataField>& fields,
const
std::vector<std::string>& field_names,
const std::string&
error_message_intro);
- static Status ValidateNotContainComplexType(const std::vector<DataField>&
fields,
- const
std::vector<std::string>& field_names);
+ static Status ValidateNotContainSpecificType(const std::vector<DataField>&
fields,
+ const
std::vector<std::string>& field_names);
static Status ValidateBucket(const TableSchema& schema, const CoreOptions&
options);
static Status ValidateDefaultValues(const TableSchema& schema) {
return Status::NotImplemented("validate default values not
implemented");
diff --git a/src/paimon/core/schema/schema_validation_test.cpp
b/src/paimon/core/schema/schema_validation_test.cpp
index 0a08efe..9dc7472 100644
--- a/src/paimon/core/schema/schema_validation_test.cpp
+++ b/src/paimon/core/schema/schema_validation_test.cpp
@@ -393,19 +393,34 @@ TEST(SchemaValidationTest,
NonPrimitivePartitionKeyStruct) {
"field f1 is unsupported");
}
-TEST(SchemaValidationTest, TestComplexPartitionKey) {
- auto f0 = arrow::field("f0", arrow::utf8());
- auto f1 = arrow::field("f1", arrow::decimal128(5, 2));
- auto f2 = arrow::field("f2", arrow::float64());
- arrow::FieldVector fields = {f0, f1, f2};
- auto schema = arrow::schema(fields);
- std::vector<std::string> primary_keys = {"f0", "f1"};
- std::vector<std::string> partition_keys = {"f1"};
- ASSERT_OK_AND_ASSIGN(
- std::shared_ptr<TableSchema> table_schema,
- TableSchema::Create(/*schema_id=*/0, schema, partition_keys,
primary_keys, {}));
- ASSERT_NOK_WITH_MSG(SchemaValidation::ValidateTableSchema(*table_schema),
- "partition field f1 is unsupported");
+TEST(SchemaValidationTest, TestSpecificPartitionKey) {
+ {
+ auto f0 = arrow::field("f0", arrow::utf8());
+ auto f1 = arrow::field("f1", arrow::decimal128(5, 2));
+ auto f2 = arrow::field("f2", arrow::float64());
+ arrow::FieldVector fields = {f0, f1, f2};
+ auto schema = arrow::schema(fields);
+ std::vector<std::string> primary_keys = {"f0", "f1"};
+ std::vector<std::string> partition_keys = {"f1"};
+ ASSERT_OK_AND_ASSIGN(
+ std::shared_ptr<TableSchema> table_schema,
+ TableSchema::Create(/*schema_id=*/0, schema, partition_keys,
primary_keys, {}));
+
ASSERT_NOK_WITH_MSG(SchemaValidation::ValidateTableSchema(*table_schema),
+ "partition field f1 cannot be
TIMESTAMP/DECIMAL/BLOB");
+ }
+ {
+ auto f0 = arrow::field("f0", arrow::utf8());
+ auto f1 = arrow::field("f1", arrow::float64());
+ arrow::FieldVector fields = {f0, f1};
+ auto schema = arrow::schema(fields);
+ std::vector<std::string> primary_keys = {"f0", "f1"};
+ std::vector<std::string> partition_keys = {"f1"};
+ ASSERT_OK_AND_ASSIGN(
+ std::shared_ptr<TableSchema> table_schema,
+ TableSchema::Create(/*schema_id=*/0, schema, partition_keys,
primary_keys, {}));
+
ASSERT_NOK_WITH_MSG(SchemaValidation::ValidateTableSchema(*table_schema),
+ "partition field f1 cannot be FLOAT/DOUBLE");
+ }
}
TEST(SchemaValidationTest, TestComplexPartitionKeyWithBlob) {
@@ -419,7 +434,7 @@ TEST(SchemaValidationTest, TestComplexPartitionKeyWithBlob)
{
std::shared_ptr<TableSchema> table_schema,
TableSchema::Create(/*schema_id=*/0, schema, partition_keys,
/*primary_keys=*/{}, {}));
ASSERT_NOK_WITH_MSG(SchemaValidation::ValidateTableSchema(*table_schema),
- "partition field f1 is unsupported");
+ "partition field f1 cannot be TIMESTAMP/DECIMAL/BLOB");
}
TEST(SchemaValidationTest, TestDateTypePartitionKey) {
@@ -511,8 +526,8 @@ TEST(SchemaValidationTest, ValidateBucket) {
"The number of buckets needs to be greater than
0.");
}
{
- std::vector<std::string> primary_keys = {"f0", "f1"};
- std::vector<std::string> partition_keys = {"f2"};
+ std::vector<std::string> primary_keys = {"f0", "f2"};
+ std::vector<std::string> partition_keys = {"f1"};
std::map<std::string, std::string> options = {{Options::BUCKET, "2"},
{Options::BUCKET_KEY,
"f0"}};
ASSERT_OK_AND_ASSIGN(
@@ -524,7 +539,7 @@ TEST(SchemaValidationTest, ValidateBucket) {
}
{
std::vector<std::string> primary_keys = {};
- std::vector<std::string> partition_keys = {"f2"};
+ std::vector<std::string> partition_keys = {"f1"};
std::map<std::string, std::string> options = {{Options::BUCKET, "2"}};
ASSERT_OK_AND_ASSIGN(
std::shared_ptr<TableSchema> table_schema,
@@ -533,7 +548,7 @@ TEST(SchemaValidationTest, ValidateBucket) {
"You should define a 'bucket-key' for bucketed
append mode");
}
{
- std::vector<std::string> partition_keys = {"f2"};
+ std::vector<std::string> partition_keys = {"f1"};
std::map<std::string, std::string> options =
{{"full-compaction.delta-commits", "2"}};
ASSERT_OK_AND_ASSIGN(std::shared_ptr<TableSchema> table_schema,
TableSchema::Create(/*schema_id=*/0, schema,
partition_keys,
@@ -633,11 +648,11 @@ TEST(SchemaValidationTest, ValidateSequenceField) {
std::map<std::string, std::string> options = {{Options::BUCKET, "-1"},
{Options::SEQUENCE_FIELD, "f0,f1,f2"}};
ASSERT_OK_AND_ASSIGN(std::shared_ptr<TableSchema> table_schema,
- TableSchema::Create(/*schema_id=*/0, schema,
/*partition_keys=*/{"f2"},
- /*primary_keys=*/{"f0",
"f1"}, options));
+ TableSchema::Create(/*schema_id=*/0, schema,
/*partition_keys=*/{"f1"},
+ /*primary_keys=*/{"f0",
"f2"}, options));
ASSERT_NOK_WITH_MSG(SchemaValidation::ValidateTableSchema(*table_schema),
"You cannot use sequence.field in cross partition
update case (Primary "
- "key constraint 'f0, f1' not including all
partition fields 'f2').");
+ "key constraint 'f0, f2' not including all
partition fields 'f1').");
}
}
diff --git a/src/paimon/core/stats/simple_stats_collector.cpp
b/src/paimon/core/stats/simple_stats_collector.cpp
index 7cd0440..c0f2c8f 100644
--- a/src/paimon/core/stats/simple_stats_collector.cpp
+++ b/src/paimon/core/stats/simple_stats_collector.cpp
@@ -133,40 +133,6 @@ Status SimpleStatsCollector::Collect(const BinaryRow& row)
{
}
break;
}
- case arrow::Type::FLOAT: {
- if (column_stats_[i] == nullptr) {
- column_stats_[i] = ColumnStats::CreateFloatColumnStats(
- std::nullopt, std::nullopt, std::nullopt);
- }
- auto typed_stats =
dynamic_cast<FloatColumnStats*>(column_stats_[i].get());
- if (typed_stats == nullptr) {
- assert(false);
- return Status::Invalid("cast typed stats failed");
- }
- if (!row.IsNullAt(i)) {
- typed_stats->Collect(row.GetFloat(i));
- } else {
- typed_stats->Collect(std::nullopt);
- }
- break;
- }
- case arrow::Type::DOUBLE: {
- if (column_stats_[i] == nullptr) {
- column_stats_[i] = ColumnStats::CreateDoubleColumnStats(
- std::nullopt, std::nullopt, std::nullopt);
- }
- auto typed_stats =
dynamic_cast<DoubleColumnStats*>(column_stats_[i].get());
- if (typed_stats == nullptr) {
- assert(false);
- return Status::Invalid("cast typed stats failed");
- }
- if (!row.IsNullAt(i)) {
- typed_stats->Collect(row.GetDouble(i));
- } else {
- typed_stats->Collect(std::nullopt);
- }
- break;
- }
case arrow::Type::STRING:
case arrow::Type::BINARY: {
if (column_stats_[i] == nullptr) {
diff --git a/src/paimon/core/stats/simple_stats_collector_test.cpp
b/src/paimon/core/stats/simple_stats_collector_test.cpp
index bac30b4..d3a860a 100644
--- a/src/paimon/core/stats/simple_stats_collector_test.cpp
+++ b/src/paimon/core/stats/simple_stats_collector_test.cpp
@@ -41,9 +41,8 @@ TEST(SimpleStatsCollectorTest, TestSimple) {
arrow::FieldVector fields = {
arrow::field("f0", arrow::boolean()), arrow::field("f1",
arrow::int8()),
arrow::field("f2", arrow::int16()), arrow::field("f3",
arrow::int32()),
- arrow::field("f4", arrow::int64()), arrow::field("f5",
arrow::float32()),
- arrow::field("f6", arrow::float64()), arrow::field("f7",
arrow::utf8()),
- arrow::field("f8", arrow::date32()),
+ arrow::field("f4", arrow::int64()), arrow::field("f5",
arrow::utf8()),
+ arrow::field("f6", arrow::date32()),
};
auto schema = arrow::schema(fields);
@@ -51,25 +50,21 @@ TEST(SimpleStatsCollectorTest, TestSimple) {
auto pool = GetDefaultPool();
ASSERT_OK(collector.Collect(BinaryRowGenerator::GenerateRow(
{true, static_cast<int8_t>(1), static_cast<int16_t>(1),
static_cast<int32_t>(1),
- static_cast<int64_t>(1), static_cast<float>(3.0),
static_cast<double>(3.0),
- std::string("abc"), 2025},
+ static_cast<int64_t>(1), std::string("abc"), 2025},
pool.get())));
ASSERT_OK(collector.Collect(BinaryRowGenerator::GenerateRow(
{false, static_cast<int8_t>(2), static_cast<int16_t>(2),
static_cast<int32_t>(2),
- static_cast<int64_t>(2), static_cast<float>(6.0),
static_cast<double>(6.0),
- std::string("bcd"), 2026},
+ static_cast<int64_t>(2), std::string("bcd"), 2026},
pool.get())));
ASSERT_OK_AND_ASSIGN(auto col_stats, collector.GetResult());
ASSERT_OK_AND_ASSIGN(SimpleStats stats,
SimpleStatsConverter::ToBinary(col_stats, pool.get()));
auto expected_stats = BinaryRowGenerator::GenerateStats(
{false, static_cast<int8_t>(1), static_cast<int16_t>(1),
static_cast<int32_t>(1),
- static_cast<int64_t>(1), static_cast<float>(3.0),
static_cast<double>(3.0),
- std::string("abc"), 2025},
+ static_cast<int64_t>(1), std::string("abc"), 2025},
{true, static_cast<int8_t>(2), static_cast<int16_t>(2),
static_cast<int32_t>(2),
- static_cast<int64_t>(2), static_cast<float>(6.0),
static_cast<double>(6.0),
- std::string("bcd"), 2026},
- std::vector<int64_t>({0, 0, 0, 0, 0, 0, 0, 0, 0}),
GetDefaultPool().get());
+ static_cast<int64_t>(2), std::string("bcd"), 2026},
+ std::vector<int64_t>({0, 0, 0, 0, 0, 0, 0}), GetDefaultPool().get());
ASSERT_EQ(stats, expected_stats);
}
@@ -78,29 +73,27 @@ TEST(SimpleStatsCollectorTest, TestNull) {
arrow::FieldVector fields = {
arrow::field("f0", arrow::boolean()), arrow::field("f1",
arrow::int8()),
arrow::field("f2", arrow::int16()), arrow::field("f3",
arrow::int32()),
- arrow::field("f4", arrow::int64()), arrow::field("f5",
arrow::float32()),
- arrow::field("f6", arrow::float64()), arrow::field("f7",
arrow::utf8()),
- arrow::field("f8", arrow::date32()), arrow::field("key",
arrow::int32()),
+ arrow::field("f4", arrow::int64()), arrow::field("f5",
arrow::utf8()),
+ arrow::field("f6", arrow::date32()), arrow::field("key",
arrow::int32()),
};
auto schema = arrow::schema(fields);
SimpleStatsCollector collector(schema);
auto pool = GetDefaultPool();
- ASSERT_OK(collector.Collect(
- BinaryRowGenerator::GenerateRow({NullType(), NullType(), NullType(),
NullType(), NullType(),
- NullType(), NullType(), NullType(),
NullType(), 100},
- pool.get())));
+ ASSERT_OK(collector.Collect(BinaryRowGenerator::GenerateRow(
+ {NullType(), NullType(), NullType(), NullType(), NullType(),
NullType(), NullType(), 100},
+ pool.get())));
ASSERT_OK_AND_ASSIGN(auto col_stats, collector.GetResult());
- ASSERT_EQ(10, col_stats.size());
+ ASSERT_EQ(8, col_stats.size());
ASSERT_OK_AND_ASSIGN(SimpleStats stats,
SimpleStatsConverter::ToBinary(col_stats, pool.get()));
ASSERT_EQ(stats.MinValues(), stats.MaxValues());
- for (size_t i = 0; i < 9; ++i) {
+ for (size_t i = 0; i < 7; ++i) {
ASSERT_TRUE(stats.MinValues().IsNullAt(i));
}
- ASSERT_EQ(stats.MinValues().GetInt(9), 100);
+ ASSERT_EQ(stats.MinValues().GetInt(7), 100);
ASSERT_OK_AND_ASSIGN(std::vector<int64_t> expected,
stats.NullCounts().ToLongArray());
- ASSERT_EQ(expected, std::vector<int64_t>({1l, 1l, 1l, 1l, 1l, 1l, 1l, 1l,
1l, 0l}));
+ ASSERT_EQ(expected, std::vector<int64_t>({1l, 1l, 1l, 1l, 1l, 1l, 1l,
0l}));
}
TEST(SimpleStatsCollectorTest, TestInvalidPartition) {
diff --git a/src/paimon/core/utils/field_mapping_test.cpp
b/src/paimon/core/utils/field_mapping_test.cpp
index ed1b056..153932d 100644
--- a/src/paimon/core/utils/field_mapping_test.cpp
+++ b/src/paimon/core/utils/field_mapping_test.cpp
@@ -154,15 +154,19 @@ TEST_F(FieldMappingTest, TestPartitionKeysEqualSchema) {
FieldType::INT, Literal(20));
ASSERT_OK_AND_ASSIGN(auto predicate, PredicateBuilder::And({equal,
not_equal}));
- std::vector<std::string> partition_keys = {"f0", "f1", "f2", "f3"};
+ std::vector<DataField> fields = {DataField(0, arrow::field("f0",
arrow::utf8())),
+ DataField(1, arrow::field("f1",
arrow::int32())),
+ DataField(2, arrow::field("f2",
arrow::int32()))};
+ std::shared_ptr<arrow::Schema> schema =
DataField::ConvertDataFieldsToArrowSchema(fields);
+ std::vector<std::string> partition_keys = {"f0", "f1", "f2"};
ASSERT_OK_AND_ASSIGN(auto mapping_builder,
- FieldMappingBuilder::Create(schema_, partition_keys,
predicate));
- ASSERT_OK_AND_ASSIGN(auto mapping,
mapping_builder->CreateFieldMapping(schema_));
+ FieldMappingBuilder::Create(schema, partition_keys,
predicate));
+ ASSERT_OK_AND_ASSIGN(auto mapping,
mapping_builder->CreateFieldMapping(schema));
PartitionInfo expected_part_info;
- expected_part_info.partition_read_schema = fields_;
- expected_part_info.idx_in_target_read_schema = {0, 1, 2, 3};
- expected_part_info.idx_in_partition = {0, 1, 2, 3};
+ expected_part_info.partition_read_schema = fields;
+ expected_part_info.idx_in_target_read_schema = {0, 1, 2};
+ expected_part_info.idx_in_partition = {0, 1, 2};
expected_part_info.partition_filter =
std::dynamic_pointer_cast<PredicateFilter>(predicate);
CheckPartitionInfo(mapping->partition_info.value(), expected_part_info);
@@ -356,7 +360,7 @@ TEST_F(FieldMappingTest, TestSchemaEvolution) {
// add field / delete field / rename / casting
// without predicate
std::vector<DataField> data_fields = {DataField(0, arrow::field("key0",
arrow::int32())),
- DataField(1, arrow::field("key1",
arrow::float64())),
+ DataField(1, arrow::field("key1",
arrow::int64())),
DataField(2, arrow::field("a",
arrow::int32())),
DataField(3, arrow::field("b",
arrow::int32())),
DataField(4, arrow::field("c",
arrow::int32())),
@@ -365,7 +369,7 @@ TEST_F(FieldMappingTest, TestSchemaEvolution) {
DataField::ConvertDataFieldsToArrowSchema(data_fields);
std::vector<DataField> read_fields = {DataField(0, arrow::field("key0",
arrow::int32())),
- DataField(1, arrow::field("key1",
arrow::float64())),
+ DataField(1, arrow::field("key1",
arrow::int64())),
DataField(3, arrow::field("c",
arrow::int64())),
DataField(5, arrow::field("a",
arrow::float32())),
DataField(7, arrow::field("d",
arrow::int32())),
@@ -380,9 +384,8 @@ TEST_F(FieldMappingTest, TestSchemaEvolution) {
ASSERT_OK_AND_ASSIGN(auto mapping,
mapping_builder->CreateFieldMapping(data_fields));
PartitionInfo expected_part_info;
- expected_part_info.partition_read_schema = {
- DataField(0, arrow::field("key0", arrow::int32())),
- DataField(1, arrow::field("key1", arrow::float64()))};
+ expected_part_info.partition_read_schema = {DataField(0,
arrow::field("key0", arrow::int32())),
+ DataField(1,
arrow::field("key1", arrow::int64()))};
expected_part_info.idx_in_target_read_schema = {0, 1};
expected_part_info.idx_in_partition = {0, 1};
expected_part_info.partition_filter = nullptr;
@@ -420,7 +423,7 @@ TEST_F(FieldMappingTest, TestSchemaEvolutionWithPredicate) {
// field_7 is added to the middle;
// field_8 is added to the last field.
std::vector<DataField> data_fields = {DataField(0, arrow::field("key0",
arrow::int32())),
- DataField(1, arrow::field("key1",
arrow::float64())),
+ DataField(1, arrow::field("key1",
arrow::int64())),
DataField(2, arrow::field("a",
arrow::int32())),
DataField(3, arrow::field("b",
arrow::decimal128(5, 2))),
DataField(4, arrow::field("c",
arrow::int32())),
@@ -428,7 +431,7 @@ TEST_F(FieldMappingTest, TestSchemaEvolutionWithPredicate) {
DataField(6, arrow::field("k",
arrow::int32()))};
std::vector<DataField> table_fields = {DataField(0, arrow::field("key0",
arrow::int32())),
- DataField(1, arrow::field("key1",
arrow::float64())),
+ DataField(1, arrow::field("key1",
arrow::int64())),
DataField(6, arrow::field("k",
arrow::int32())),
DataField(3, arrow::field("c",
arrow::decimal128(6, 3))),
DataField(7, arrow::field("d",
arrow::int32())),
@@ -441,7 +444,7 @@ TEST_F(FieldMappingTest, TestSchemaEvolutionWithPredicate) {
auto greater_or_equal = PredicateBuilder::GreaterOrEqual(
/*field_index=*/0, /*field_name=*/"key0", FieldType::INT, Literal(4));
auto equal = PredicateBuilder::Equal(/*field_index=*/1,
/*field_name=*/"key1",
- FieldType::DOUBLE, Literal(3.0));
+ FieldType::BIGINT, Literal(3l));
auto less_or_equal = PredicateBuilder::LessOrEqual(/*field_index=*/2,
/*field_name=*/"k",
FieldType::INT,
Literal(10));
// greater_than will not be pushed down, as with casting, only integer
predicates can be pushed
@@ -466,9 +469,8 @@ TEST_F(FieldMappingTest, TestSchemaEvolutionWithPredicate) {
ASSERT_OK_AND_ASSIGN(auto mapping,
mapping_builder->CreateFieldMapping(data_fields));
PartitionInfo expected_part_info;
- expected_part_info.partition_read_schema = {
- DataField(0, arrow::field("key0", arrow::int32())),
- DataField(1, arrow::field("key1", arrow::float64()))};
+ expected_part_info.partition_read_schema = {DataField(0,
arrow::field("key0", arrow::int32())),
+ DataField(1,
arrow::field("key1", arrow::int64()))};
expected_part_info.idx_in_target_read_schema = {0, 1};
expected_part_info.idx_in_partition = {0, 1};
expected_part_info.partition_filter =
@@ -514,7 +516,7 @@ TEST_F(FieldMappingTest, TestSchemaEvolutionWithPredicate2)
{
// field_7 is added to the middle;
// field_8 is added to the last field.
std::vector<DataField> data_fields = {DataField(0, arrow::field("key0",
arrow::int32())),
- DataField(1, arrow::field("key1",
arrow::float64())),
+ DataField(1, arrow::field("key1",
arrow::int64())),
DataField(2, arrow::field("a",
arrow::int32())),
DataField(3, arrow::field("b",
arrow::decimal128(5, 2))),
DataField(4, arrow::field("c",
arrow::int32())),
@@ -522,7 +524,7 @@ TEST_F(FieldMappingTest, TestSchemaEvolutionWithPredicate2)
{
DataField(6, arrow::field("k",
arrow::int32()))};
std::vector<DataField> table_fields = {DataField(0, arrow::field("key0",
arrow::int32())),
- DataField(1, arrow::field("key1",
arrow::float64())),
+ DataField(1, arrow::field("key1",
arrow::int64())),
DataField(6, arrow::field("k",
arrow::int32())),
DataField(3, arrow::field("c",
arrow::decimal128(6, 3))),
DataField(7, arrow::field("d",
arrow::int32())),
@@ -531,7 +533,7 @@ TEST_F(FieldMappingTest, TestSchemaEvolutionWithPredicate2)
{
// the field order of read schema and table schema is inconsistent
std::vector<DataField> read_fields = {DataField(7, arrow::field("d",
arrow::int32())),
- DataField(1, arrow::field("key1",
arrow::float64())),
+ DataField(1, arrow::field("key1",
arrow::int64())),
DataField(5, arrow::field("a",
arrow::int64())),
DataField(8, arrow::field("e",
arrow::int32())),
DataField(6, arrow::field("k",
arrow::int32())),
@@ -543,7 +545,7 @@ TEST_F(FieldMappingTest, TestSchemaEvolutionWithPredicate2)
{
auto greater_or_equal = PredicateBuilder::GreaterOrEqual(
/*field_index=*/6, /*field_name=*/"key0", FieldType::INT, Literal(4));
auto equal = PredicateBuilder::Equal(/*field_index=*/1,
/*field_name=*/"key1",
- FieldType::DOUBLE, Literal(3.0));
+ FieldType::BIGINT, Literal(3l));
auto less_or_equal = PredicateBuilder::LessOrEqual(/*field_index=*/4,
/*field_name=*/"k",
FieldType::INT,
Literal(10));
// greater_than will not be pushed down, as with casting, only integer
predicates can be pushed
@@ -568,15 +570,14 @@ TEST_F(FieldMappingTest,
TestSchemaEvolutionWithPredicate2) {
ASSERT_OK_AND_ASSIGN(auto mapping,
mapping_builder->CreateFieldMapping(data_fields));
PartitionInfo expected_part_info;
- expected_part_info.partition_read_schema = {
- DataField(0, arrow::field("key0", arrow::int32())),
- DataField(1, arrow::field("key1", arrow::float64()))};
+ expected_part_info.partition_read_schema = {DataField(0,
arrow::field("key0", arrow::int32())),
+ DataField(1,
arrow::field("key1", arrow::int64()))};
expected_part_info.idx_in_target_read_schema = {6, 1};
expected_part_info.idx_in_partition = {0, 1};
auto greater_or_equal_new = PredicateBuilder::GreaterOrEqual(
/*field_index=*/0, /*field_name=*/"key0", FieldType::INT, Literal(4));
auto equal_new = PredicateBuilder::Equal(/*field_index=*/1,
/*field_name=*/"key1",
- FieldType::DOUBLE, Literal(3.0));
+ FieldType::BIGINT, Literal(3l));
expected_part_info.partition_filter =
PredicateBuilder::And({greater_or_equal_new,
equal_new}).value_or(nullptr);
CheckPartitionInfo(mapping->partition_info.value(), expected_part_info);
diff --git a/src/paimon/core/utils/file_store_path_factory.cpp
b/src/paimon/core/utils/file_store_path_factory.cpp
index 97a3bea..d17b223 100644
--- a/src/paimon/core/utils/file_store_path_factory.cpp
+++ b/src/paimon/core/utils/file_store_path_factory.cpp
@@ -221,24 +221,44 @@ Result<std::string>
FileStorePathFactory::GetPartitionString(const BinaryRow& pa
if (partition.GetSizeInBytes() == 0) {
return Status::Invalid("invalid binary row partition");
}
- auto iter = row_to_str_cache_.find(partition);
- if (PAIMON_LIKELY(iter != row_to_str_cache_.end())) {
- return iter->second;
+ {
+ std::shared_lock<std::shared_mutex> read_lock(cache_mutex_);
+ auto iter = row_to_str_cache_.find(partition);
+ if (PAIMON_LIKELY(iter != row_to_str_cache_.end())) {
+ return iter->second;
+ }
}
+
std::vector<std::pair<std::string, std::string>> part_values;
PAIMON_ASSIGN_OR_RAISE(part_values,
partition_computer_->GeneratePartitionVector(partition));
PAIMON_ASSIGN_OR_RAISE(std::string part_str,
PartitionPathUtils::GeneratePartitionPath(part_values));
+
+ std::unique_lock<std::shared_mutex> write_lock(cache_mutex_);
+ auto iter = row_to_str_cache_.find(partition);
+ if (PAIMON_LIKELY(iter != row_to_str_cache_.end())) {
+ return iter->second;
+ }
return row_to_str_cache_.insert({partition, part_str}).first->second;
}
Result<BinaryRow> FileStorePathFactory::ToBinaryRow(
const std::map<std::string, std::string>& partition) const {
+ {
+ std::shared_lock<std::shared_mutex> read_lock(cache_mutex_);
+ auto iter = map_to_row_cache_.find(partition);
+ if (PAIMON_LIKELY(iter != map_to_row_cache_.end())) {
+ return iter->second;
+ }
+ }
+
+ PAIMON_ASSIGN_OR_RAISE(BinaryRow row,
partition_computer_->ToBinaryRow(partition));
+
+ std::unique_lock<std::shared_mutex> write_lock(cache_mutex_);
auto iter = map_to_row_cache_.find(partition);
if (PAIMON_LIKELY(iter != map_to_row_cache_.end())) {
return iter->second;
}
- PAIMON_ASSIGN_OR_RAISE(BinaryRow row,
partition_computer_->ToBinaryRow(partition));
return map_to_row_cache_.insert({partition, row}).first->second;
}
diff --git a/src/paimon/core/utils/file_store_path_factory.h
b/src/paimon/core/utils/file_store_path_factory.h
index 8e07bce..1890f2b 100644
--- a/src/paimon/core/utils/file_store_path_factory.h
+++ b/src/paimon/core/utils/file_store_path_factory.h
@@ -22,6 +22,8 @@
#include <cstdint>
#include <map>
#include <memory>
+#include <mutex>
+#include <shared_mutex>
#include <string>
#include <unordered_map>
#include <utility>
@@ -99,7 +101,7 @@ class FileStorePathFactory : public
std::enable_shared_from_this<FileStorePathFa
return global_index_external_path_;
}
- /// @note This method is NOT THREAD SAFE.
+ /// Thread-safe for concurrent calls. Cache accesses are protected by
internal locks.
Result<std::string> GetPartitionString(const BinaryRow& partition) const;
std::string NewManifestFile() const {
return PathUtil::JoinPath(
@@ -169,6 +171,7 @@ class FileStorePathFactory : public
std::enable_shared_from_this<FileStorePathFa
mutable std::map<std::map<std::string, std::string>, BinaryRow>
map_to_row_cache_;
mutable std::unordered_map<BinaryRow, std::string> row_to_str_cache_;
+ mutable std::shared_mutex cache_mutex_;
};
} // namespace paimon
diff --git a/src/paimon/core/utils/file_store_path_factory_test.cpp
b/src/paimon/core/utils/file_store_path_factory_test.cpp
index f76f836..41a9635 100644
--- a/src/paimon/core/utils/file_store_path_factory_test.cpp
+++ b/src/paimon/core/utils/file_store_path_factory_test.cpp
@@ -18,7 +18,10 @@
#include "paimon/core/utils/file_store_path_factory.h"
+#include <atomic>
+#include <mutex>
#include <optional>
+#include <thread>
#include <variant>
#include "arrow/type.h"
@@ -360,6 +363,92 @@ TEST_F(FileStorePathFactoryTest,
TestToBinaryRowAndToPartitionString) {
}
}
+TEST_F(FileStorePathFactoryTest,
TestConcurrentToBinaryRowAndGetPartitionString) {
+ auto dir = UniqueTestDirectory::Create();
+ ASSERT_TRUE(dir);
+
+ arrow::FieldVector fields = {
+ arrow::field("f0", arrow::boolean()), arrow::field("f1",
arrow::int8()),
+ arrow::field("f2", arrow::int8()), arrow::field("f3",
arrow::int16()),
+ arrow::field("f4", arrow::int16()), arrow::field("f5",
arrow::int32()),
+ arrow::field("hr", arrow::int32()), arrow::field("f7",
arrow::int64()),
+ arrow::field("dt", arrow::utf8()), arrow::field("f9",
arrow::float32()),
+ arrow::field("f10", arrow::float64()), arrow::field("f11",
arrow::utf8()),
+ arrow::field("f12", arrow::binary()),
arrow::field("non-partition-field", arrow::int32())};
+ auto schema = arrow::schema(fields);
+
+ ASSERT_OK_AND_ASSIGN(
+ std::shared_ptr<FileStorePathFactory> path_factory,
+ FileStorePathFactory::Create(dir->Str(), schema, {"dt", "hr"},
"default",
+ /*identifier=*/"mock_format",
/*data_file_prefix=*/"data-",
+ /*legacy_partition_name_enabled=*/true,
+
/*external_paths=*/std::vector<std::string>(),
+
/*global_index_external_path=*/std::nullopt,
+ /*index_file_in_data_file_dir=*/false,
mem_pool_));
+
+ const std::vector<std::pair<std::map<std::string, std::string>,
std::string>> test_cases = {
+ {{{"dt", "20211224"}, {"hr", "23"}}, "dt=20211224/hr=23/"},
+ {{{"dt", "default"}, {"hr", "17"}}, "dt=default/hr=17/"},
+ {{{"dt", "20240812"}, {"hr", "5"}}, "dt=20240812/hr=5/"},
+ {{{"dt", " a "}, {"hr", "22"}}, "dt= a /hr=22/"},
+ };
+
+ std::atomic<bool> has_error = false;
+ std::mutex error_mutex;
+ std::string first_error;
+
+ constexpr int32_t kThreadNum = 8;
+ constexpr int32_t kIterationsPerThread = 500;
+ std::vector<std::thread> threads;
+ threads.reserve(kThreadNum);
+
+ for (int32_t thread_id = 0; thread_id < kThreadNum; ++thread_id) {
+ threads.emplace_back([&, thread_id]() {
+ for (int32_t i = 0; i < kIterationsPerThread; ++i) {
+ const auto& [partition_map, expected] =
+ test_cases[(thread_id + i) % test_cases.size()];
+
+ auto row_result = path_factory->ToBinaryRow(partition_map);
+ if (!row_result.ok()) {
+ std::lock_guard<std::mutex> lock(error_mutex);
+ has_error.store(true, std::memory_order_relaxed);
+ if (first_error.empty()) {
+ first_error = "ToBinaryRow failed: " +
row_result.status().ToString();
+ }
+ return;
+ }
+
+ auto partition_str_result =
path_factory->GetPartitionString(row_result.value());
+ if (!partition_str_result.ok()) {
+ std::lock_guard<std::mutex> lock(error_mutex);
+ has_error.store(true, std::memory_order_relaxed);
+ if (first_error.empty()) {
+ first_error = "GetPartitionString failed: " +
+ partition_str_result.status().ToString();
+ }
+ return;
+ }
+
+ if (partition_str_result.value() != expected) {
+ std::lock_guard<std::mutex> lock(error_mutex);
+ has_error.store(true, std::memory_order_relaxed);
+ if (first_error.empty()) {
+ first_error = "Unexpected partition string, expected="
+ expected +
+ ", actual=" +
partition_str_result.value();
+ }
+ return;
+ }
+ }
+ });
+ }
+
+ for (auto& thread : threads) {
+ thread.join();
+ }
+
+ ASSERT_FALSE(has_error.load(std::memory_order_relaxed)) << first_error;
+}
+
TEST_F(FileStorePathFactoryTest, TestCreateIndexFileFactory) {
{
// test without external path & index_file_in_data_file_dir = false
diff --git a/src/paimon/format/avro/avro_reader_builder.h
b/src/paimon/format/avro/avro_reader_builder.h
index b4100d1..bd1da1a 100644
--- a/src/paimon/format/avro/avro_reader_builder.h
+++ b/src/paimon/format/avro/avro_reader_builder.h
@@ -47,10 +47,6 @@ class AvroReaderBuilder : public ReaderBuilder {
return AvroFileBatchReader::Create(path, batch_size_, pool_);
}
- Result<std::unique_ptr<FileBatchReader>> Build(const std::string& path)
const override {
- return Status::Invalid("do not support build reader with path in avro
format");
- }
-
private:
const int32_t batch_size_;
std::shared_ptr<MemoryPool> pool_;
diff --git a/src/paimon/format/blob/blob_reader_builder.h
b/src/paimon/format/blob/blob_reader_builder.h
index 0bffbf3..85bebe9 100644
--- a/src/paimon/format/blob/blob_reader_builder.h
+++ b/src/paimon/format/blob/blob_reader_builder.h
@@ -48,10 +48,6 @@ class BlobReaderBuilder : public ReaderBuilder {
return BlobFileBatchReader::Create(input_stream, batch_size_,
blob_as_descriptor, pool_);
}
- Result<std::unique_ptr<FileBatchReader>> Build(const std::string& path)
const override {
- return Status::Invalid("do not support build reader with path in blob
format");
- }
-
private:
int32_t batch_size_;
std::shared_ptr<MemoryPool> pool_;
diff --git a/src/paimon/format/orc/orc_reader_builder.h
b/src/paimon/format/orc/orc_reader_builder.h
index 3fd88d8..e7ef540 100644
--- a/src/paimon/format/orc/orc_reader_builder.h
+++ b/src/paimon/format/orc/orc_reader_builder.h
@@ -54,10 +54,6 @@ class OrcReaderBuilder : public ReaderBuilder {
return OrcFileBatchReader::Create(std::move(input_stream), pool_,
options_, batch_size_);
}
- Result<std::unique_ptr<FileBatchReader>> Build(const std::string& path)
const override {
- return Status::Invalid("do not support build reader with path in orc
format");
- }
-
private:
int32_t batch_size_ = -1;
std::shared_ptr<MemoryPool> pool_;
diff --git a/src/paimon/format/parquet/parquet_reader_builder.h
b/src/paimon/format/parquet/parquet_reader_builder.h
index 2a93696..c0d9931 100644
--- a/src/paimon/format/parquet/parquet_reader_builder.h
+++ b/src/paimon/format/parquet/parquet_reader_builder.h
@@ -53,10 +53,6 @@ class ParquetReaderBuilder : public ReaderBuilder {
batch_size_);
}
- Result<std::unique_ptr<FileBatchReader>> Build(const std::string& path)
const override {
- return Status::Invalid("do not support build reader with path in
parquet format");
- }
-
private:
int32_t batch_size_ = -1;
std::shared_ptr<MemoryPool> pool_;
diff --git a/src/paimon/global_index/lumina/lumina_global_index.cpp
b/src/paimon/global_index/lumina/lumina_global_index.cpp
index 5976acd..2d13aa1 100644
--- a/src/paimon/global_index/lumina/lumina_global_index.cpp
+++ b/src/paimon/global_index/lumina/lumina_global_index.cpp
@@ -179,8 +179,12 @@ Result<std::shared_ptr<GlobalIndexReader>>
LuminaGlobalIndex::CreateReader(
class LuminaDataset : public ::lumina::api::Dataset {
public:
LuminaDataset(int64_t element_count, uint32_t dimension,
- const std::vector<std::shared_ptr<arrow::FloatArray>>&
array_vec)
- : element_count_(element_count), dimension_(dimension),
array_vec_(array_vec) {}
+ const std::vector<std::shared_ptr<arrow::FloatArray>>&
array_vec,
+ const std::vector<int64_t>& start_ids)
+ : element_count_(element_count),
+ dimension_(dimension),
+ array_vec_(array_vec),
+ start_ids_(start_ids) {}
uint32_t Dim() const noexcept override {
return dimension_;
@@ -202,8 +206,8 @@ class LuminaDataset : public ::lumina::api::Dataset {
vector_buffer.resize(value_array_length);
memcpy(vector_buffer.data(), value_ptr, sizeof(float) *
value_array_length);
id_buffer.resize(element_count);
- std::iota(id_buffer.begin(), id_buffer.end(), id_);
- id_ += element_count;
+ std::iota(id_buffer.begin(), id_buffer.end(),
+
static_cast<::lumina::core::vector_id_t>(start_ids_[cursor_]));
// release the array when copy to vector_buffer
value_array.reset();
@@ -215,8 +219,8 @@ class LuminaDataset : public ::lumina::api::Dataset {
int64_t element_count_;
uint32_t dimension_;
std::vector<std::shared_ptr<arrow::FloatArray>> array_vec_;
+ std::vector<int64_t> start_ids_;
size_t cursor_ = 0;
- ::lumina::core::vector_id_t id_ = 0;
};
LuminaIndexWriter::LuminaIndexWriter(const std::string& field_name,
@@ -256,35 +260,62 @@ Status LuminaIndexWriter::AddBatch(::ArrowArray*
arrow_array,
auto list_field_array =
std::dynamic_pointer_cast<arrow::ListArray>(field_array);
CHECK_NOT_NULL(list_field_array,
"invalid input array in LuminaIndexWriter, field array must
be list array");
- auto value_array =
std::dynamic_pointer_cast<arrow::FloatArray>(list_field_array->values());
- CHECK_NOT_NULL(
- value_array,
- "invalid input array in LuminaIndexWriter, field value array must be
float array");
- if (value_array->null_count() != 0) {
- return Status::Invalid("field value array in LuminaIndexWriter is
invalid, must not null");
- }
- if (value_array->length() != field_length * dimension_) {
- return Status::Invalid(fmt::format(
- "invalid input array in LuminaIndexWriter, length of field array
[{}] multiplied "
- "dimension [{}] must match length of field value array [{}]",
- field_length, dimension_, value_array->length()));
+
+ // Split into contiguous non-null segments, skipping null rows in the list
field.
+ int64_t segment_start = -1;
+ for (int64_t i = 0; i <= field_length; i++) {
+ bool is_null = (i < field_length) && list_field_array->IsNull(i);
+ bool is_end = (i == field_length);
+
+ if (!is_null && !is_end && segment_start == -1) {
+ segment_start = i;
+ }
+
+ if ((is_null || is_end) && segment_start != -1) {
+ int64_t segment_len = i - segment_start;
+ // Use value_offset to precisely locate the float range for this
segment
+ auto value_start_offset =
list_field_array->value_offset(segment_start);
+ auto value_end_offset =
list_field_array->value_offset(segment_start + segment_len);
+ int64_t value_length = value_end_offset - value_start_offset;
+ auto sliced_values = std::dynamic_pointer_cast<arrow::FloatArray>(
+ list_field_array->values()->Slice(value_start_offset,
value_length));
+ CHECK_NOT_NULL(sliced_values,
+ "invalid sliced value array in LuminaIndexWriter,
must be float array");
+ if (sliced_values->null_count() != 0) {
+ return Status::Invalid(
+ "field value array in LuminaIndexWriter is invalid, must
not null");
+ }
+ if (sliced_values->length() != segment_len *
static_cast<int64_t>(dimension_)) {
+ return Status::Invalid(fmt::format(
+ "invalid input array in LuminaIndexWriter, length of field
array [{}] "
+ "multiplied dimension [{}] must match length of field
value array [{}]",
+ segment_len, dimension_, sliced_values->length()));
+ }
+ array_vec_.push_back(std::move(sliced_values));
+ array_start_ids_.push_back(count_ + segment_start);
+ indexed_count_ += segment_len;
+ segment_start = -1;
+ }
}
+
count_ += array->length();
- array_vec_.push_back(std::move(value_array));
return Status::OK();
}
Result<std::vector<GlobalIndexIOMeta>> LuminaIndexWriter::Finish() {
+ if (indexed_count_ == 0) {
+ return std::vector<GlobalIndexIOMeta>();
+ }
::lumina::core::MemoryResourceConfig memory_resource(pool_.get());
PAIMON_ASSIGN_OR_RAISE_FROM_LUMINA(
::lumina::api::LuminaBuilder builder,
::lumina::api::LuminaBuilder::Create(builder_options_,
memory_resource));
// pretrain
- LuminaDataset dataset1(count_, dimension_, array_vec_);
+ LuminaDataset dataset1(indexed_count_, dimension_, array_vec_,
array_start_ids_);
PAIMON_RETURN_NOT_OK_FROM_LUMINA(builder.PretrainFrom(dataset1));
// insert data
- LuminaDataset dataset2(count_, dimension_, array_vec_);
+ LuminaDataset dataset2(indexed_count_, dimension_, array_vec_,
array_start_ids_);
std::vector<std::shared_ptr<arrow::FloatArray>>().swap(array_vec_);
PAIMON_RETURN_NOT_OK_FROM_LUMINA(builder.InsertFrom(dataset2));
diff --git a/src/paimon/global_index/lumina/lumina_global_index.h
b/src/paimon/global_index/lumina/lumina_global_index.h
index 281f518..572361a 100644
--- a/src/paimon/global_index/lumina/lumina_global_index.h
+++ b/src/paimon/global_index/lumina/lumina_global_index.h
@@ -93,6 +93,7 @@ class LuminaIndexWriter : public GlobalIndexWriter {
private:
int64_t count_ = 0;
+ int64_t indexed_count_ = 0;
std::shared_ptr<LuminaMemoryPool> pool_;
std::string field_name_;
std::shared_ptr<arrow::DataType> arrow_type_;
@@ -102,6 +103,7 @@ class LuminaIndexWriter : public GlobalIndexWriter {
::lumina::api::IOOptions io_options_;
std::map<std::string, std::string> lumina_options_;
std::vector<std::shared_ptr<arrow::FloatArray>> array_vec_;
+ std::vector<int64_t> array_start_ids_;
};
class LuminaIndexReader : public GlobalIndexReader {
diff --git a/src/paimon/global_index/lumina/lumina_global_index_test.cpp
b/src/paimon/global_index/lumina/lumina_global_index_test.cpp
index d0bda5f..0f31477 100644
--- a/src/paimon/global_index/lumina/lumina_global_index_test.cpp
+++ b/src/paimon/global_index/lumina/lumina_global_index_test.cpp
@@ -160,7 +160,7 @@ class LuminaGlobalIndexTest : public ::testing::Test {
return struct_array;
}
- private:
+ protected:
std::shared_ptr<MemoryPool> pool_ = GetDefaultPool();
std::shared_ptr<FileSystem> fs_ = std::make_shared<LocalFileSystem>();
std::map<std::string, std::string> options_ = {{"lumina.index.dimension",
"4"},
@@ -472,4 +472,201 @@ TEST_F(LuminaGlobalIndexTest,
TestHighCardinalityAndMultiThreadSearch) {
}
}
+TEST_F(LuminaGlobalIndexTest, TestWriteWithNullRows) {
+ auto test_root_dir = paimon::test::UniqueTestDirectory::Create();
+ ASSERT_TRUE(test_root_dir);
+ std::string test_root = test_root_dir->Str();
+
+ // Array with null at row 1 (middle): rows 0,2,3 are valid, row 1 is null
+ // This should split into two segments: [0,0] and [2,3]
+ std::shared_ptr<arrow::Array> array_with_null =
+ arrow::ipc::internal::json::ArrayFromJSON(data_type_,
+ R"([
+ [[0.0, 0.0, 0.0, 0.0]],
+ [null],
+ [[1.0, 0.0, 1.0, 0.0]],
+ [[1.0, 1.0, 1.0, 1.0]]
+ ])")
+ .ValueOrDie();
+
+ ASSERT_OK_AND_ASSIGN(
+ auto meta, WriteGlobalIndex(test_root, data_type_, options_,
array_with_null, Range(0, 3)));
+ ASSERT_OK_AND_ASSIGN(auto reader,
+ CreateGlobalIndexReader(test_root, data_type_,
options_, meta));
+ {
+ // Search should return ids 0, 2, 3 (skipping null row 1)
+ ASSERT_OK_AND_ASSIGN(
+ auto scored_result,
+ reader->VisitVectorSearch(std::make_shared<VectorSearch>(
+ /*field_name=*/"f0", /*limit=*/4, query_, /*filter=*/nullptr,
+ /*predicate=*/nullptr, /*distance_type=*/std::nullopt,
/*options=*/options_)));
+ // Only 3 vectors indexed (row 1 is null), so limit=4 returns 3
+ CheckResult(scored_result, {3l, 2l, 0l}, {0.01f, 2.21f, 4.21f});
+ }
+}
+
+TEST_F(LuminaGlobalIndexTest, TestWriteWithMultipleNullSegments) {
+ auto test_root_dir = paimon::test::UniqueTestDirectory::Create();
+ ASSERT_TRUE(test_root_dir);
+ std::string test_root = test_root_dir->Str();
+
+ // Nulls at rows 0, 2, 5: valid rows are 1, 3, 4
+ // Splits into segments: [1,1], [3,4]
+ std::shared_ptr<arrow::Array> array_with_nulls =
+ arrow::ipc::internal::json::ArrayFromJSON(data_type_,
+ R"([
+ [null],
+ [[0.0, 1.0, 0.0, 1.0]],
+ [null],
+ [[1.0, 0.0, 1.0, 0.0]],
+ [[1.0, 1.0, 1.0, 1.0]],
+ [null]
+ ])")
+ .ValueOrDie();
+
+ ASSERT_OK_AND_ASSIGN(auto meta, WriteGlobalIndex(test_root, data_type_,
options_,
+ array_with_nulls,
Range(0, 5)));
+ ASSERT_OK_AND_ASSIGN(auto reader,
+ CreateGlobalIndexReader(test_root, data_type_,
options_, meta));
+ {
+ ASSERT_OK_AND_ASSIGN(
+ auto scored_result,
+ reader->VisitVectorSearch(std::make_shared<VectorSearch>(
+ /*field_name=*/"f0", /*limit=*/4, query_, /*filter=*/nullptr,
+ /*predicate=*/nullptr, /*distance_type=*/std::nullopt,
/*options=*/options_)));
+ // Only 3 vectors indexed at ids 1, 3, 4
+ CheckResult(scored_result, {4l, 1l, 3l}, {0.01f, 2.01f, 2.21f});
+ }
+}
+
+TEST_F(LuminaGlobalIndexTest, TestWriteWithAllNullRows) {
+ auto test_root_dir = paimon::test::UniqueTestDirectory::Create();
+ ASSERT_TRUE(test_root_dir);
+ std::string test_root = test_root_dir->Str();
+
+ // All rows are null — no vectors to index
+ std::shared_ptr<arrow::Array> all_null_array =
+ arrow::ipc::internal::json::ArrayFromJSON(data_type_,
+ R"([
+ [null],
+ [null],
+ [null]
+ ])")
+ .ValueOrDie();
+
+ auto global_index = std::make_shared<LuminaGlobalIndex>(options_);
+ auto path_factory = std::make_shared<FakeIndexPathFactory>(test_root);
+ auto file_writer = std::make_shared<GlobalIndexFileManager>(fs_,
path_factory);
+
+ ASSERT_OK_AND_ASSIGN(
+ std::shared_ptr<GlobalIndexWriter> global_writer,
+ global_index->CreateWriter("f0", CreateArrowSchema(data_type_).get(),
file_writer, pool_));
+
+ ArrowArray c_array;
+ ASSERT_TRUE(arrow::ExportArray(*all_null_array, &c_array).ok());
+ std::vector<int64_t> row_ids = {0, 1, 2};
+ ASSERT_OK(global_writer->AddBatch(&c_array, std::move(row_ids)));
+ // Finish with zero indexed vectors — returns empty metas
+ ASSERT_OK_AND_ASSIGN(auto result_metas, global_writer->Finish());
+ ASSERT_TRUE(result_metas.empty());
+}
+
+TEST_F(LuminaGlobalIndexTest, TestWriteWithNullAndFilter) {
+ auto test_root_dir = paimon::test::UniqueTestDirectory::Create();
+ ASSERT_TRUE(test_root_dir);
+ std::string test_root = test_root_dir->Str();
+
+ // Null at row 2: valid rows are 0, 1, 3
+ std::shared_ptr<arrow::Array> array_with_null =
+ arrow::ipc::internal::json::ArrayFromJSON(data_type_,
+ R"([
+ [[0.0, 0.0, 0.0, 0.0]],
+ [[0.0, 1.0, 0.0, 1.0]],
+ [null],
+ [[1.0, 1.0, 1.0, 1.0]]
+ ])")
+ .ValueOrDie();
+
+ ASSERT_OK_AND_ASSIGN(
+ auto meta, WriteGlobalIndex(test_root, data_type_, options_,
array_with_null, Range(0, 3)));
+ ASSERT_OK_AND_ASSIGN(auto reader,
+ CreateGlobalIndexReader(test_root, data_type_,
options_, meta));
+ {
+ // Filter: only allow ids < 3 (filters out id=3), so only ids 0, 1
remain
+ auto filter = [](int64_t id) -> bool { return id < 3; };
+ ASSERT_OK_AND_ASSIGN(
+ auto scored_result,
+ reader->VisitVectorSearch(std::make_shared<VectorSearch>(
+ /*field_name=*/"f0", /*limit=*/4, query_, filter,
+ /*predicate=*/nullptr, /*distance_type=*/std::nullopt,
/*options=*/options_)));
+ CheckResult(scored_result, {1l, 0l}, {2.01f, 4.21f});
+ }
+}
+
+TEST_F(LuminaGlobalIndexTest, TestWriteWithNullAcrossMultipleBatches) {
+ auto test_root_dir = paimon::test::UniqueTestDirectory::Create();
+ ASSERT_TRUE(test_root_dir);
+ std::string test_root = test_root_dir->Str();
+
+ // Batch 1: rows 0-2, null at row 1 → indexed ids: {0, 2}
+ std::shared_ptr<arrow::Array> batch1 =
arrow::ipc::internal::json::ArrayFromJSON(data_type_,
+
R"([
+ [[0.0, 0.0, 0.0, 0.0]],
+ [null],
+ [[1.0, 0.0, 1.0, 0.0]]
+ ])")
+ .ValueOrDie();
+
+ // Batch 2: rows 3-5, null at row 3 → indexed ids: {4, 5}
+ std::shared_ptr<arrow::Array> batch2 =
arrow::ipc::internal::json::ArrayFromJSON(data_type_,
+
R"([
+ [null],
+ [[1.0, 1.0, 1.0, 1.0]],
+ [[0.0, 1.0, 0.0, 1.0]]
+ ])")
+ .ValueOrDie();
+
+ auto global_index = std::make_shared<LuminaGlobalIndex>(options_);
+ auto path_factory = std::make_shared<FakeIndexPathFactory>(test_root);
+ auto file_writer = std::make_shared<GlobalIndexFileManager>(fs_,
path_factory);
+
+ ASSERT_OK_AND_ASSIGN(
+ std::shared_ptr<GlobalIndexWriter> global_writer,
+ global_index->CreateWriter("f0", CreateArrowSchema(data_type_).get(),
file_writer, pool_));
+
+ // AddBatch 1: row_ids {0, 1, 2}
+ {
+ ArrowArray c_array;
+ ASSERT_TRUE(arrow::ExportArray(*batch1, &c_array).ok());
+ std::vector<int64_t> row_ids = {0, 1, 2};
+ ASSERT_OK(global_writer->AddBatch(&c_array, std::move(row_ids)));
+ }
+ // AddBatch 2: row_ids {3, 4, 5}
+ {
+ ArrowArray c_array;
+ ASSERT_TRUE(arrow::ExportArray(*batch2, &c_array).ok());
+ std::vector<int64_t> row_ids = {3, 4, 5};
+ ASSERT_OK(global_writer->AddBatch(&c_array, std::move(row_ids)));
+ }
+
+ ASSERT_OK_AND_ASSIGN(auto result_metas, global_writer->Finish());
+ ASSERT_EQ(result_metas.size(), 1);
+
+ ASSERT_OK_AND_ASSIGN(auto reader,
+ CreateGlobalIndexReader(test_root, data_type_,
options_, result_metas[0]));
+ {
+ // Search all: should return ids {0, 2, 4, 5}, never {1, 3}
+ ASSERT_OK_AND_ASSIGN(
+ auto scored_result,
+ reader->VisitVectorSearch(std::make_shared<VectorSearch>(
+ /*field_name=*/"f0", /*limit=*/10, query_, /*filter=*/nullptr,
+ /*predicate=*/nullptr, /*distance_type=*/std::nullopt,
/*options=*/options_)));
+ // id 0: [0,0,0,0] → L2 dist to [1,1,1,1.1] = 4.21
+ // id 2: [1,0,1,0] → L2 dist = 2.21
+ // id 4: [1,1,1,1] → L2 dist = 0.01
+ // id 5: [0,1,0,1] → L2 dist = 2.01
+ CheckResult(scored_result, {4l, 5l, 2l, 0l}, {0.01f, 2.01f, 2.21f,
4.21f});
+ }
+}
+
} // namespace paimon::lumina::test
diff --git a/src/paimon/testing/mock/mock_format_reader_builder.h
b/src/paimon/testing/mock/mock_format_reader_builder.h
index 0a7b471..7f8906c 100644
--- a/src/paimon/testing/mock/mock_format_reader_builder.h
+++ b/src/paimon/testing/mock/mock_format_reader_builder.h
@@ -54,10 +54,6 @@ class MockFormatReaderBuilder : public ReaderBuilder {
return std::make_unique<MockFileBatchReader>(data_, schema_,
read_batch_size_);
}
- Result<std::unique_ptr<FileBatchReader>> Build(const std::string& path)
const override {
- return Status::Invalid("do not support build reader with path in mock
format");
- }
-
private:
std::shared_ptr<arrow::Array> data_;
std::shared_ptr<arrow::DataType> schema_;
diff --git a/test/inte/append_compaction_inte_test.cpp
b/test/inte/append_compaction_inte_test.cpp
index eda4d6c..88d8115 100644
--- a/test/inte/append_compaction_inte_test.cpp
+++ b/test/inte/append_compaction_inte_test.cpp
@@ -149,9 +149,6 @@ std::vector<std::string>
GetTestValuesForAppendCompactionInteTest() {
#ifdef PAIMON_ENABLE_ORC
values.emplace_back("orc");
#endif
-#ifdef PAIMON_ENABLE_LANCE
- values.emplace_back("lance");
-#endif
#ifdef PAIMON_ENABLE_AVRO
values.emplace_back("avro");
#endif
diff --git a/test/inte/blob_table_inte_test.cpp
b/test/inte/blob_table_inte_test.cpp
index eb3ba04..ca6df4f 100644
--- a/test/inte/blob_table_inte_test.cpp
+++ b/test/inte/blob_table_inte_test.cpp
@@ -250,9 +250,6 @@ std::vector<std::string>
GetTestValuesForBlobTableInteTest() {
#ifdef PAIMON_ENABLE_ORC
values.emplace_back("orc");
#endif
-#ifdef PAIMON_ENABLE_LANCE
- values.emplace_back("lance");
-#endif
#ifdef PAIMON_ENABLE_AVRO
values.emplace_back("avro");
#endif
@@ -434,7 +431,7 @@ TEST_P(BlobTableInteTest, TestBasic) {
.ValueOrDie());
ASSERT_OK(ScanAndRead(table_path, schema->field_names(), expected_array));
- if (GetParam() != "lance") {
+ {
// read with row tracking
auto expected_row_tracking_array =
std::dynamic_pointer_cast<arrow::StructArray>(
arrow::ipc::internal::json::ArrayFromJSON(
@@ -539,7 +536,7 @@ TEST_P(BlobTableInteTest, TestMultipleAppends) {
.ValueOrDie());
ASSERT_OK(ScanAndRead(table_path, schema->field_names(), expected_array));
- if (GetParam() != "lance") {
+ {
// read with row tracking
auto expected_row_tracking_array =
std::dynamic_pointer_cast<arrow::StructArray>(
arrow::ipc::internal::json::ArrayFromJSON(arrow::struct_({
@@ -659,7 +656,7 @@ TEST_P(BlobTableInteTest,
TestMultipleAppendsDifferentFirstRowIds) {
.ValueOrDie());
ASSERT_OK(ScanAndRead(table_path, schema->field_names(), expected_array));
- if (GetParam() != "lance") {
+ {
// read with row tracking
auto expected_row_tracking_array =
std::dynamic_pointer_cast<arrow::StructArray>(
arrow::ipc::internal::json::ArrayFromJSON(arrow::struct_({
@@ -814,7 +811,7 @@ TEST_P(BlobTableInteTest, TestExternalPath) {
.ValueOrDie());
ASSERT_OK(ScanAndRead(table_path, schema->field_names(), expected_array));
- if (GetParam() != "lance") {
+ {
// read with row tracking
auto expected_row_tracking_array =
std::dynamic_pointer_cast<arrow::StructArray>(
arrow::ipc::internal::json::ArrayFromJSON(
@@ -833,9 +830,6 @@ TEST_P(BlobTableInteTest, TestExternalPath) {
TEST_P(BlobTableInteTest, TestPartitionWithPredicate) {
auto file_format = GetParam();
- if (file_format == "lance") {
- return;
- }
std::vector<std::string> partition_keys = {"f0"};
std::map<std::string, std::string> options = {
{Options::MANIFEST_FORMAT, "orc"}, {Options::FILE_FORMAT,
GetParam()},
@@ -953,8 +947,8 @@ TEST_P(BlobTableInteTest, TestPartitionWithPredicate) {
}
TEST_P(BlobTableInteTest, TestPredicate) {
- if (GetParam() == "lance" || GetParam() == "avro") {
- // lance and avro do not have stats
+ if (GetParam() == "avro") {
+ // Avro does not have stats.
return;
}
CreateTable();
@@ -1023,9 +1017,6 @@ TEST_P(BlobTableInteTest, TestPredicate) {
}
TEST_P(BlobTableInteTest, TestIOException) {
- if (GetParam() == "lance") {
- return;
- }
std::string table_path;
// write and commit with I/O exception
bool write_run_complete = false;
@@ -1098,7 +1089,7 @@ TEST_P(BlobTableInteTest, TestIOException) {
TEST_P(BlobTableInteTest, TestReadTableWithDenseStats) {
auto file_format = GetParam();
- if (file_format == "lance" || file_format == "avro") {
+ if (file_format == "avro") {
return;
}
std::string table_path =
@@ -1162,7 +1153,7 @@ TEST_P(BlobTableInteTest, TestReadTableWithDenseStats) {
TEST_P(BlobTableInteTest, TestDataEvolutionAndAlterTable) {
auto file_format = GetParam();
- if (file_format == "lance" || file_format == "avro") {
+ if (file_format == "avro") {
return;
}
std::string table_path = paimon::test::GetDataDir() + file_format +
@@ -1505,7 +1496,7 @@ TEST_P(BlobTableInteTest, TestAppendWriteWithNullBlob) {
TEST_P(BlobTableInteTest, TestReadTableWithMultiBlobFields) {
auto file_format = GetParam();
- if (file_format == "lance" || file_format == "avro") {
+ if (file_format == "avro") {
return;
}
std::string table_path = paimon::test::GetDataDir() + file_format +
diff --git a/test/inte/data_evolution_table_test.cpp
b/test/inte/data_evolution_table_test.cpp
index efcd7b0..4c040fc 100644
--- a/test/inte/data_evolution_table_test.cpp
+++ b/test/inte/data_evolution_table_test.cpp
@@ -288,7 +288,7 @@ TEST_P(DataEvolutionTableTest, TestBasic) {
.ValueOrDie());
ASSERT_OK(ScanAndRead(table_path, schema->field_names(), expected_array));
- if (GetParam() != "lance") {
+ {
// read with row tracking
auto expected_row_tracking_array =
std::dynamic_pointer_cast<arrow::StructArray>(
arrow::ipc::internal::json::ArrayFromJSON(
@@ -417,7 +417,7 @@ TEST_P(DataEvolutionTableTest, TestMultipleAppends) {
/*predicate=*/nullptr,
/*row_ranges=*/row_ranges));
}
- if (GetParam() != "lance") {
+ {
// read with row tracking
auto expected_row_tracking_array =
std::dynamic_pointer_cast<arrow::StructArray>(
arrow::ipc::internal::json::ArrayFromJSON(arrow::struct_({
@@ -492,7 +492,7 @@ TEST_P(DataEvolutionTableTest, TestOnlySomeColumns) {
.ValueOrDie());
ASSERT_OK(ScanAndRead(table_path, schema->field_names(), expected_array));
- if (GetParam() != "lance") {
+ {
// read with row tracking
auto expected_row_tracking_array =
std::dynamic_pointer_cast<arrow::StructArray>(
arrow::ipc::internal::json::ArrayFromJSON(arrow::struct_({
@@ -561,7 +561,7 @@ TEST_P(DataEvolutionTableTest, TestNullValues) {
.ValueOrDie());
ASSERT_OK(ScanAndRead(table_path, schema->field_names(), expected_array));
- if (GetParam() != "lance") {
+ {
// read with row tracking
auto expected_row_tracking_array =
std::dynamic_pointer_cast<arrow::StructArray>(
arrow::ipc::internal::json::ArrayFromJSON(arrow::struct_({
@@ -644,7 +644,7 @@ TEST_P(DataEvolutionTableTest,
TestMultipleAppendsDifferentFirstRowIds) {
.ValueOrDie());
ASSERT_OK(ScanAndRead(table_path, schema->field_names(), expected_array));
- if (GetParam() != "lance") {
+ {
// read with row tracking
auto expected_row_tracking_array =
std::dynamic_pointer_cast<arrow::StructArray>(
arrow::ipc::internal::json::ArrayFromJSON(arrow::struct_({
@@ -735,7 +735,7 @@ TEST_P(DataEvolutionTableTest, TestOnlyRowTrackingEnabled) {
ASSERT_OK_AND_ASSIGN(auto commit_msgs, WriteArray(table_path, write_cols0,
src_array0));
ASSERT_OK(Commit(table_path, commit_msgs));
- if (GetParam() != "lance") {
+ {
// read with row tracking
auto expected_row_tracking_array =
std::dynamic_pointer_cast<arrow::StructArray>(
arrow::ipc::internal::json::ArrayFromJSON(
@@ -803,7 +803,7 @@ TEST_P(DataEvolutionTableTest, TestExternalPath) {
.ValueOrDie());
ASSERT_OK(ScanAndRead(table_path, schema->field_names(), expected_array));
- if (GetParam() != "lance") {
+ {
// read with row tracking
auto expected_row_tracking_array =
std::dynamic_pointer_cast<arrow::StructArray>(
arrow::ipc::internal::json::ArrayFromJSON(
@@ -881,7 +881,7 @@ TEST_P(DataEvolutionTableTest, TestWithPartitionSimple) {
.ValueOrDie());
ASSERT_OK(ScanAndRead(table_path, schema->field_names(), expected_array));
- if (GetParam() != "lance") {
+ {
// test only read partition fields
auto expected_array_only_partition =
std::dynamic_pointer_cast<arrow::StructArray>(
arrow::ipc::internal::json::ArrayFromJSON(arrow::struct_({fields_[1]}), R"([
@@ -992,7 +992,7 @@ TEST_P(DataEvolutionTableTest,
TestWithPartitionWithoutPartitionFieldsInFile) {
/*row_ranges=*/row_ranges));
}
- if (GetParam() != "lance") {
+ {
// read with row tracking
auto expected_row_tracking_array =
std::dynamic_pointer_cast<arrow::StructArray>(
arrow::ipc::internal::json::ArrayFromJSON(
@@ -1012,7 +1012,7 @@ TEST_P(DataEvolutionTableTest,
TestWithPartitionWithoutPartitionFieldsInFile) {
TEST_P(DataEvolutionTableTest, TestPartitionWithPredicate) {
auto file_format = GetParam();
- if (file_format == "lance" || file_format == "avro") {
+ if (file_format == "avro") {
return;
}
std::vector<std::string> partition_keys = {"f1"};
@@ -1185,7 +1185,7 @@ TEST_P(DataEvolutionTableTest,
TestPartitionWithPredicate) {
TEST_P(DataEvolutionTableTest, TestAlterTable) {
auto file_format = GetParam();
- if (file_format == "lance" || file_format == "avro") {
+ if (file_format == "avro") {
return;
}
std::string table_path = paimon::test::GetDataDir() + file_format +
@@ -1282,7 +1282,7 @@ TEST_P(DataEvolutionTableTest, TestAlterTable) {
TEST_P(DataEvolutionTableTest, TestReadCompactFiles) {
auto file_format = GetParam();
- if (file_format == "lance" || file_format == "avro") {
+ if (file_format == "avro") {
return;
}
std::string table_path =
@@ -1312,7 +1312,7 @@ TEST_P(DataEvolutionTableTest, TestReadCompactFiles) {
TEST_P(DataEvolutionTableTest, TestReadTableWithDenseStats) {
auto file_format = GetParam();
- if (file_format == "lance" || file_format == "avro") {
+ if (file_format == "avro") {
return;
}
std::string table_path = paimon::test::GetDataDir() + file_format +
@@ -1393,7 +1393,7 @@ TEST_P(DataEvolutionTableTest,
TestReadTableWithDenseStats) {
TEST_P(DataEvolutionTableTest, TestScanAndReadWithIndex) {
auto file_format = GetParam();
- if (file_format == "lance" || file_format == "avro") {
+ if (file_format == "avro") {
return;
}
// only f2 has index
@@ -1533,8 +1533,8 @@ TEST_P(DataEvolutionTableTest, TestScanAndReadWithIndex) {
}
TEST_P(DataEvolutionTableTest, TestPredicate) {
- if (GetParam() == "lance" || GetParam() == "avro") {
- // lance and avro do not have stats
+ if (GetParam() == "avro") {
+ // Avro does not have stats.
return;
}
CreateTable();
@@ -1602,9 +1602,6 @@ TEST_P(DataEvolutionTableTest, TestPredicate) {
}
TEST_P(DataEvolutionTableTest, TestIOException) {
- if (GetParam() == "lance") {
- return;
- }
std::string table_path;
// write and commit with I/O exception
bool write_run_complete = false;
@@ -1839,8 +1836,8 @@ TEST_P(DataEvolutionTableTest, TestWithRowIds) {
/*predicate=*/nullptr,
/*row_ranges=*/row_ranges));
}
- if (GetParam() == "lance" || GetParam() == "avro") {
- // as lance and avro do not support stats
+ if (GetParam() == "avro") {
+ // Avro does not support stats.
return;
}
{
@@ -1905,9 +1902,6 @@ std::vector<std::string>
GetTestValuesForDataEvolutionTableTest() {
#ifdef PAIMON_ENABLE_ORC
values.emplace_back("orc");
#endif
-#ifdef PAIMON_ENABLE_LANCE
- values.emplace_back("lance");
-#endif
#ifdef PAIMON_ENABLE_AVRO
values.emplace_back("avro");
#endif
diff --git a/test/inte/global_index_test.cpp b/test/inte/global_index_test.cpp
index dce39d8..3091dc4 100644
--- a/test/inte/global_index_test.cpp
+++ b/test/inte/global_index_test.cpp
@@ -294,6 +294,41 @@ TEST_P(GlobalIndexTest, TestWriteLuminaIndex) {
ASSERT_TRUE(expected_commit_message->TEST_Equal(*index_commit_msg_impl));
}
+TEST_P(GlobalIndexTest, TestWriteLuminaIndexWithMismatchedDimension) {
+ arrow::FieldVector fields = {arrow::field("f0", arrow::utf8()),
+ arrow::field("f1",
arrow::list(arrow::float32()))};
+ auto schema = arrow::schema(fields);
+ std::map<std::string, std::string> lumina_options =
{{"lumina.index.dimension", "3"},
+ {"lumina.index.type",
"bruteforce"},
+
{"lumina.distance.metric", "l2"},
+
{"lumina.encoding.type", "rawf32"},
+
{"lumina.search.parallel_number", "10"}};
+
+ std::map<std::string, std::string> options = {
+ {Options::MANIFEST_FORMAT, "orc"}, {Options::FILE_FORMAT,
file_format_},
+ {Options::FILE_SYSTEM, "local"},
{Options::ROW_TRACKING_ENABLED, "true"},
+ {Options::DATA_EVOLUTION_ENABLED, "true"}, {Options::READ_BATCH_SIZE,
"1"}};
+
+ CreateTable(/*partition_keys=*/{}, schema, options);
+ std::string table_path = PathUtil::JoinPath(dir_->Str(), "foo.db/bar");
+
+ std::vector<std::string> write_cols = schema->field_names();
+ auto src_array =
arrow::ipc::internal::json::ArrayFromJSON(arrow::struct_(fields), R"([
+ ["a", [0.0, 0.0, 0.0]],
+ ["b", [0.0, 0.0, 0.0, 0.0]]
+ ])")
+ .ValueOrDie();
+
+ ASSERT_OK_AND_ASSIGN(auto commit_msgs, WriteArray(table_path, write_cols,
src_array));
+ ASSERT_OK(Commit(table_path, commit_msgs));
+
+ ASSERT_NOK_WITH_MSG(
+ WriteIndex(table_path, /*partition_filters=*/{}, "f1", "lumina",
+ /*options=*/lumina_options, Range(0, 1)),
+ "invalid input array in LuminaIndexWriter, length of field array [1]
multiplied "
+ "dimension [3] must match length of field value array [4]");
+}
+
TEST_P(GlobalIndexTest, TestWriteIndex) {
CreateTable();
std::string table_path = PathUtil::JoinPath(dir_->Str(), "foo.db/bar");
@@ -428,7 +463,7 @@ TEST_P(GlobalIndexTest, TestWriteIndexWithPartition) {
#endif
TEST_P(GlobalIndexTest, TestScanIndex) {
- if (file_format_ == "lance" || file_format_ == "avro") {
+ if (file_format_ == "avro") {
return;
}
@@ -597,7 +632,7 @@ TEST_P(GlobalIndexTest, TestScanIndex) {
}
TEST_P(GlobalIndexTest, TestScanIndexWithSpecificSnapshot) {
- if (file_format_ == "lance" || file_format_ == "avro") {
+ if (file_format_ == "avro") {
return;
}
@@ -646,7 +681,7 @@ TEST_P(GlobalIndexTest, TestScanIndexWithSpecificSnapshot) {
}
TEST_P(GlobalIndexTest, TestScanIndexWithSpecificSnapshotWithNoIndex) {
- if (file_format_ == "lance" || file_format_ == "avro") {
+ if (file_format_ == "avro") {
return;
}
@@ -671,7 +706,7 @@ TEST_P(GlobalIndexTest,
TestScanIndexWithSpecificSnapshotWithNoIndex) {
}
TEST_P(GlobalIndexTest, TestScanIndexWithRange) {
- if (file_format_ == "lance" || file_format_ == "avro") {
+ if (file_format_ == "avro") {
return;
}
@@ -707,7 +742,7 @@ TEST_P(GlobalIndexTest, TestScanIndexWithRange) {
}
TEST_P(GlobalIndexTest, TestScanIndexWithPartition) {
- if (file_format_ == "lance" || file_format_ == "avro") {
+ if (file_format_ == "avro") {
return;
}
@@ -762,7 +797,7 @@ TEST_P(GlobalIndexTest, TestScanIndexWithPartition) {
}
TEST_P(GlobalIndexTest, TestScanUnregisteredIndex) {
- if (file_format_ == "lance" || file_format_ == "avro") {
+ if (file_format_ == "avro") {
return;
}
auto factory_creator = FactoryCreator::GetInstance();
@@ -991,8 +1026,10 @@ TEST_P(GlobalIndexTest,
TestWriteCommitScanReadIndexWithScore) {
["Bob", [0.0, 1.0, 0.0, 1.0], 10, 12.1],
["Emily", [1.0, 0.0, 1.0, 0.0], 10, 13.1],
["Tony", [1.0, 1.0, 1.0, 1.0], 10, 14.1],
+["NullGuy1", null, 10, 20.0],
["Lucy", [10.0, 10.0, 10.0, 10.0], 20, 15.1],
["Bob", [10.0, 11.0, 10.0, 11.0], 20, 16.1],
+["NullGuy2", null, 20, 21.0],
["Tony", [11.0, 10.0, 11.0, 10.0], 20, 17.1],
["Alice", [11.0, 11.0, 11.0, 11.0], 20, 18.1],
["Paul", [10.0, 10.0, 10.0, 10.0], 20, 19.1]
@@ -1004,7 +1041,7 @@ TEST_P(GlobalIndexTest,
TestWriteCommitScanReadIndexWithScore) {
// write and commit lumina index
ASSERT_OK(WriteIndex(table_path, /*partition_filters=*/{}, "f1", "lumina",
- /*options=*/lumina_options, Range(0, 8)));
+ /*options=*/lumina_options, Range(0, 10)));
auto scan_and_check_result = [&](const std::vector<Range>& read_row_ranges,
const std::shared_ptr<arrow::Array>&
expected_array,
@@ -1022,8 +1059,8 @@ TEST_P(GlobalIndexTest,
TestWriteCommitScanReadIndexWithScore) {
result_fields.insert(result_fields.begin(),
SpecialFields::ValueKind().ArrowField());
result_fields.push_back(SpecialFields::IndexScore().ArrowField());
std::map<int64_t, float> id_to_score = {{0, 4.21f}, {1, 2.01f}, {2,
2.21f},
- {3, 0.01f}, {4, 322.21f}, {5,
360.01f},
- {6, 360.21f}, {7, 398.01}, {8,
322.21f}};
+ {3, 0.01f}, {5, 322.21f}, {6,
360.01f},
+ {8, 360.21f}, {9, 398.01f}, {10,
322.21f}};
{
auto expected_array =
arrow::ipc::internal::json::ArrayFromJSON(arrow::struct_(result_fields), R"([
@@ -1038,7 +1075,8 @@ TEST_P(GlobalIndexTest,
TestWriteCommitScanReadIndexWithScore) {
[0, "Paul", [10.0, 10.0, 10.0, 10.0], 20, 19.1, 322.21]
])")
.ValueOrDie();
- scan_and_check_result({Range(0, 8)}, expected_array, id_to_score);
+ scan_and_check_result({Range(0, 3), Range(5, 6), Range(8, 10)},
expected_array,
+ id_to_score);
}
{
auto expected_array =
@@ -1049,7 +1087,7 @@ TEST_P(GlobalIndexTest,
TestWriteCommitScanReadIndexWithScore) {
[0, "Paul", [10.0, 10.0, 10.0, 10.0], 20, 19.1, 322.21]
])")
.ValueOrDie();
- scan_and_check_result({Range(2, 3), Range(7, 8)}, expected_array,
id_to_score);
+ scan_and_check_result({Range(2, 3), Range(9, 10)}, expected_array,
id_to_score);
}
{
auto expected_array =
@@ -1057,7 +1095,7 @@ TEST_P(GlobalIndexTest,
TestWriteCommitScanReadIndexWithScore) {
[0, "Bob", [10.0, 11.0, 10.0, 11.0], 20, 16.1, 360.01]
])")
.ValueOrDie();
- scan_and_check_result({Range(5, 5)}, expected_array, id_to_score);
+ scan_and_check_result({Range(6, 6)}, expected_array, id_to_score);
}
{
auto expected_array =
@@ -1068,7 +1106,31 @@ TEST_P(GlobalIndexTest,
TestWriteCommitScanReadIndexWithScore) {
[0, "Paul", [10.0, 10.0, 10.0, 10.0], 20, 19.1, null]
])")
.ValueOrDie();
- scan_and_check_result({Range(2, 3), Range(7, 8)}, expected_array,
/*id_to_score=*/{});
+ scan_and_check_result({Range(2, 3), Range(9, 10)}, expected_array,
/*id_to_score=*/{});
+ }
+ {
+ // Verify null rows (id 4 and 7) are never recalled by vector search
+ ASSERT_OK_AND_ASSIGN(
+ std::shared_ptr<GlobalIndexScan> global_index_scan,
+ GlobalIndexScan::Create(table_path, /*snapshot_id=*/std::nullopt,
+ /*partitions=*/std::nullopt,
lumina_options, fs_,
+ /*executor=*/nullptr, pool_));
+ ASSERT_OK_AND_ASSIGN(auto lumina_readers,
+ global_index_scan->CreateReaders("f1",
std::nullopt));
+ ASSERT_EQ(lumina_readers.size(), 1u);
+ std::vector<float> query = {1.0f, 1.0f, 1.0f, 1.1f};
+ auto vector_search = std::make_shared<VectorSearch>(
+ "f1", /*limit=*/20, query, /*filter=*/nullptr,
+ /*predicate=*/nullptr, /*distance_type=*/std::nullopt,
/*options=*/lumina_options);
+ ASSERT_OK_AND_ASSIGN(auto scored_result,
+
lumina_readers[0]->VisitVectorSearch(vector_search));
+ auto typed_result =
std::dynamic_pointer_cast<BitmapScoredGlobalIndexResult>(scored_result);
+ ASSERT_TRUE(typed_result);
+ // Should recall 9 vectors (11 total rows - 2 null rows)
+ ASSERT_EQ(typed_result->bitmap_.Cardinality(), 9u);
+ // Null row ids 4 and 7 must not be in the result
+ ASSERT_FALSE(typed_result->bitmap_.Contains(4));
+ ASSERT_FALSE(typed_result->bitmap_.Contains(7));
}
}
#endif
@@ -1672,9 +1734,6 @@ TEST_P(GlobalIndexTest,
TestDataEvolutionBatchScanWithExternalPath) {
}
TEST_P(GlobalIndexTest, TestIOException) {
- if (file_format_ == "lance") {
- return;
- }
arrow::FieldVector fields = {
arrow::field("f0", arrow::utf8()), arrow::field("f1",
arrow::list(arrow::float32())),
arrow::field("f2", arrow::int32()), arrow::field("f3",
arrow::float64())};
@@ -2633,6 +2692,33 @@ TEST_P(GlobalIndexTest, TestBTreeAndBitmapCoexist) {
ASSERT_OK_AND_ASSIGN(auto index_readers,
global_index_scan->CreateReaders("f0", std::nullopt));
ASSERT_EQ(index_readers.size(), 2u);
+ ASSERT_OK_AND_ASSIGN(std::shared_ptr<GlobalIndexReader> btree_reader,
+ global_index_scan->CreateReader("f0", "btree",
std::nullopt));
+ ASSERT_TRUE(btree_reader);
+ ASSERT_OK_AND_ASSIGN(std::shared_ptr<GlobalIndexResult> btree_result,
+ btree_reader->VisitEqual(Literal(FieldType::STRING,
"Bob", 3)));
+ ASSERT_TRUE(btree_result);
+ ASSERT_EQ(btree_result->ToString(), "{1,2}");
+ ASSERT_OK_AND_ASSIGN(std::shared_ptr<GlobalIndexResult>
btree_less_than_result,
+
btree_reader->VisitLessThan(Literal(FieldType::STRING, "Emily", 5)));
+ ASSERT_TRUE(btree_less_than_result);
+ ASSERT_EQ(btree_less_than_result->ToString(), "{0,1,2}");
+
+ ASSERT_OK_AND_ASSIGN(std::shared_ptr<GlobalIndexReader> bitmap_reader,
+ global_index_scan->CreateReader("f0", "bitmap",
std::nullopt));
+ ASSERT_TRUE(bitmap_reader);
+ ASSERT_OK_AND_ASSIGN(std::shared_ptr<GlobalIndexResult> bitmap_result,
+ bitmap_reader->VisitEqual(Literal(FieldType::STRING,
"Bob", 3)));
+ ASSERT_TRUE(bitmap_result);
+ ASSERT_EQ(bitmap_result->ToString(), "{1,2}");
+ ASSERT_OK_AND_ASSIGN(std::shared_ptr<GlobalIndexResult>
bitmap_less_than_result,
+
bitmap_reader->VisitLessThan(Literal(FieldType::STRING, "Emily", 5)));
+ ASSERT_FALSE(bitmap_less_than_result);
+
+ ASSERT_OK_AND_ASSIGN(std::shared_ptr<GlobalIndexReader> missing_reader,
+ global_index_scan->CreateReader("f0", "lucene",
std::nullopt));
+ ASSERT_FALSE(missing_reader);
+
// Each reader individually should return the same result for Equal("Bob")
for (const auto& index_reader : index_readers) {
ASSERT_OK_AND_ASSIGN(auto result,
@@ -2722,7 +2808,7 @@ TEST_P(GlobalIndexTest, TestBTreeAndBitmapCoexist) {
}
TEST_P(GlobalIndexTest, TestBTreeScanWithPartitionWithMultiMeta) {
- if (file_format_ == "lance" || file_format_ == "avro") {
+ if (file_format_ == "avro") {
return;
}
std::string table_path =
@@ -3024,10 +3110,6 @@ std::vector<ParamType> GetTestValuesForGlobalIndexTest()
{
values.emplace_back("orc", false);
values.emplace_back("orc", true);
#endif
-#ifdef PAIMON_ENABLE_LANCE
- values.emplace_back("lance", false);
- values.emplace_back("lance", true);
-#endif
#ifdef PAIMON_ENABLE_AVRO
values.emplace_back("avro", false);
values.emplace_back("avro", true);
diff --git a/test/inte/pk_compaction_inte_test.cpp
b/test/inte/pk_compaction_inte_test.cpp
index 6e2bb87..b996220 100644
--- a/test/inte/pk_compaction_inte_test.cpp
+++ b/test/inte/pk_compaction_inte_test.cpp
@@ -2907,7 +2907,7 @@ TEST_F(PkCompactionInteTest,
RemoteLookupFileWithSchemaEvolution) {
// 6. ScanAndVerify after full compact
TEST_P(PkCompactionInteTest, TestLookupCompatibility) {
auto file_format = GetParam();
- if (file_format == "lance" || file_format == "avro") {
+ if (file_format == "avro") {
return;
}
// Step 1: Copy pk_compact_lookup table to temp dir.
@@ -3084,9 +3084,6 @@ std::vector<std::string>
GetTestValuesForCompactionInteTest() {
#ifdef PAIMON_ENABLE_ORC
values.emplace_back("orc");
#endif
-#ifdef PAIMON_ENABLE_LANCE
- values.emplace_back("lance");
-#endif
#ifdef PAIMON_ENABLE_AVRO
values.emplace_back("avro");
#endif
diff --git a/test/inte/scan_and_read_inte_test.cpp
b/test/inte/scan_and_read_inte_test.cpp
index 572d402..ad4c66f 100644
--- a/test/inte/scan_and_read_inte_test.cpp
+++ b/test/inte/scan_and_read_inte_test.cpp
@@ -2182,67 +2182,6 @@ TEST_P(ScanAndReadInteTest,
TestScanWithPredicateAndReadWithUnorderedFieldForPar
ASSERT_TRUE(expected->Equals(read_result)) << read_result->ToString();
}
-#ifdef PAIMON_ENABLE_LANCE
-TEST_F(ScanAndReadInteTest,
TestScanWithPredicateAndReadWithUnorderedFieldForLance) {
- auto test_dir = UniqueTestDirectory::Create("local");
- arrow::FieldVector fields = {arrow::field("f0", arrow::utf8()),
- arrow::field("f1", arrow::int32()),
- arrow::field("f2", arrow::float64())};
- auto schema = arrow::schema(fields);
- std::map<std::string, std::string> options = {{Options::MANIFEST_FORMAT,
"orc"},
- {Options::FILE_FORMAT,
"lance"},
- {Options::TARGET_FILE_SIZE,
"1024"},
- {Options::BUCKET, "-1"},
- {Options::FILE_SYSTEM,
"local"}};
- ASSERT_OK_AND_ASSIGN(auto helper,
- TestHelper::Create(test_dir->Str(), schema,
/*partition_keys=*/{},
- /*primary_keys=*/{}, options,
- /*is_streaming_mode=*/false));
- std::string table_path = test_dir->Str() + "/foo.db/bar";
- int64_t commit_identifier = 0;
- std::string data = R"([
- ["banana", 2, 3.5],
- ["dog", 1, 2000.5],
- ["lucy", 14, 10000.5],
- ["mouse", 100, 10.5]
- ])";
- ASSERT_OK_AND_ASSIGN(std::unique_ptr<RecordBatch> batch,
- TestHelper::MakeRecordBatch(arrow::struct_(fields),
data,
- /*partition_map=*/{},
/*bucket=*/0, {}));
- ASSERT_OK_AND_ASSIGN(auto commit_msgs,
- helper->WriteAndCommit(std::move(batch),
commit_identifier++,
-
/*expected_commit_messages=*/std::nullopt));
- ScanContextBuilder scan_context_builder(table_path);
- // predicate does not take effective as lance file does not have stats
- auto predicate = PredicateBuilder::GreaterThan(
- /*field_index=*/2, /*field_name=*/"f2", FieldType::DOUBLE,
Literal(50000.2));
- scan_context_builder.SetPredicate(predicate);
- ASSERT_OK_AND_ASSIGN(auto scan_context, scan_context_builder.Finish());
- ASSERT_OK_AND_ASSIGN(auto table_scan,
TableScan::Create(std::move(scan_context)));
- ASSERT_OK_AND_ASSIGN(auto result_plan, table_scan->CreatePlan());
- ASSERT_EQ(result_plan->SnapshotId().value(), 1);
-
- ReadContextBuilder read_context_builder(table_path);
- read_context_builder.SetReadSchema({"f2", "f0"});
- ASSERT_OK_AND_ASSIGN(auto read_context, read_context_builder.Finish());
- ASSERT_OK_AND_ASSIGN(auto table_read,
TableRead::Create(std::move(read_context)));
- ASSERT_OK_AND_ASSIGN(auto batch_reader,
table_read->CreateReader(result_plan->Splits()));
- ASSERT_OK_AND_ASSIGN(auto read_result,
ReadResultCollector::CollectResult(batch_reader.get()));
-
- // check result
- auto expected = std::make_shared<arrow::ChunkedArray>(
- arrow::ipc::internal::json::ArrayFromJSON(
- arrow::struct_({arrow::field("_VALUE_KIND", arrow::int8()),
fields[2], fields[0]}),
- R"([[0, 3.5, "banana"],
- [0, 2000.5, "dog"],
- [0, 10000.5, "lucy"],
- [0, 10.5, "mouse"]])")
- .ValueOrDie());
- ASSERT_TRUE(expected);
- ASSERT_TRUE(expected->Equals(read_result)) << read_result->ToString();
-}
-#endif
-
TEST_P(ScanAndReadInteTest, TestAppendTableWithMultipleFileFormat) {
auto [file_format, enable_prefetch] = GetParam();
if (file_format != "parquet") {
diff --git a/test/inte/write_and_read_inte_test.cpp
b/test/inte/write_and_read_inte_test.cpp
index 603e32d..3f9fc62 100644
--- a/test/inte/write_and_read_inte_test.cpp
+++ b/test/inte/write_and_read_inte_test.cpp
@@ -181,7 +181,7 @@ TEST_P(WriteAndReadInteTest, TestPKSimple) {
}
TEST_P(WriteAndReadInteTest, TestNestedType) {
- // map use list(struct(key, value)) as lance does not support map
+ // Represent a map as list(struct(key, value)) for cross-format
compatibility.
arrow::FieldVector fields = {
arrow::field("f1", arrow::list(arrow::struct_({arrow::field("key",
arrow::int8()),
arrow::field("value",
arrow::int16())}))),
@@ -858,9 +858,6 @@ std::vector<std::pair<std::string, std::string>>
GetTestValuesForWriteAndReadInt
values.emplace_back("orc", "local");
// values.emplace_back("parquet", "jindo");
#endif
-#ifdef PAIMON_ENABLE_LANCE
- values.emplace_back("lance", "local");
-#endif
#ifdef PAIMON_ENABLE_AVRO
values.emplace_back("avro", "local");
#endif
diff --git a/test/inte/write_inte_test.cpp b/test/inte/write_inte_test.cpp
index 236c637..62c61b7 100644
--- a/test/inte/write_inte_test.cpp
+++ b/test/inte/write_inte_test.cpp
@@ -196,10 +196,10 @@ class WriteInteTest : public testing::Test, public
::testing::WithParamInterface
std::shared_ptr<DataFileMeta> ReconstructDataFileMeta(
const std::shared_ptr<DataFileMeta>& file_meta) const {
- if (GetParam() != "lance" && GetParam() != "avro") {
+ if (GetParam() != "avro") {
return file_meta;
}
- // for lance and avro format, all stats is null
+ // For the avro format, all stats are null.
auto new_meta = std::make_shared<DataFileMeta>(
file_meta->file_name, file_meta->file_size, file_meta->row_count,
file_meta->min_key,
file_meta->max_key, file_meta->key_stats, file_meta->value_stats,
@@ -338,9 +338,6 @@ std::vector<std::string> GetTestValuesForWriteInteTest() {
#ifdef PAIMON_ENABLE_ORC
values.emplace_back("orc");
#endif
-#ifdef PAIMON_ENABLE_LANCE
- values.emplace_back("lance");
-#endif
#ifdef PAIMON_ENABLE_AVRO
values.emplace_back("avro");
#endif
@@ -760,10 +757,6 @@ TEST_P(WriteInteTest,
TestAppendTableStreamWriteWithPartitionAndMultiBuckets) {
}
TEST_P(WriteInteTest, TestAppendTableWriteWithComplexType) {
- if (GetParam() == "lance") {
- // lance do not support map
- return;
- }
auto dir = UniqueTestDirectory::Create();
arrow::FieldVector fields = {
arrow::field("f1", arrow::map(arrow::int8(), arrow::int16())),
@@ -1585,10 +1578,6 @@ TEST_P(WriteInteTest,
TestPkTableWriteWithNoPartitionKey) {
}
TEST_P(WriteInteTest, TestPkTableWriteWithComplexType) {
- if (GetParam() == "lance") {
- // lance do not support map
- return;
- }
auto dir = UniqueTestDirectory::Create();
ASSERT_TRUE(dir);
arrow::FieldVector fields = {
@@ -2432,7 +2421,7 @@ TEST_P(WriteInteTest, TestWriteAndCommitIOException) {
TEST_P(WriteInteTest, TestWriteWithFieldId) {
auto file_format = GetParam();
- if (file_format == "lance" || file_format == "avro") {
+ if (file_format == "avro") {
return;
}
// prepare write schema and write data
@@ -3303,10 +3292,6 @@ TEST_P(WriteInteTest, TestWriteMemoryUse) {
}
TEST_P(WriteInteTest, TestAppendTableWithAllNull) {
- if (GetParam() == "lance") {
- // lance do not support map
- return;
- }
auto dir = UniqueTestDirectory::Create();
arrow::FieldVector fields = {
arrow::field("f0", arrow::boolean()),