From 7e29ac23da2c62540d3313c0e5a1f421c0037ae9 Mon Sep 17 00:00:00 2001 From: jichen0919 Date: Fri, 31 Oct 2025 12:32:48 +0800 Subject: [PATCH] IMPALA-14092 Part2: Support querying of paimon data table via JNI This patch mainly implement the querying of paimon data table through JNI based scanner. Features implemented: - support column pruning. The partition pruning and predicate push down will be submitted as the third part of the patch. We implemented this by treating the paimon table as normal unpartitioned table. When querying paimon table: - PaimonScanNode will decide paimon splits need to be scanned, and then transfer splits to BE do the jni-based scan operation. - We also collect the required columns that need to be scanned, and pass the columns to Scanner for column pruning. This is implemented by passing the field ids of the columns to BE, instead of column position to support schema evolution. - In the original implementation, PaimonJniScanner will directly pass paimon row object to BE, and call corresponding paimon row field accessor, which is a java method to convert row fields to impala row batch tuples. We find it is slow due to overhead of JVM method calling. To minimize the overhead, we refashioned the implementation, the PaimonJniScanner will convert the paimon row batches to arrow recordbatch, which stores data in offheap region of impala JVM. And PaimonJniScanner will pass the arrow offheap record batch memory pointer to the BE backend. BE PaimonJniScanNode will directly read data from JVM offheap region, and convert the arrow record batch to impala row batch. The benchmark shows the later implementation is 2.x better than the original implementation. The lifecycle of arrow row batch is mainly like this: the arrow row batch is generated in FE,and passed to BE. After the record batch is imported to BE successfully, BE will be in charge of freeing the row batch. There are two free paths: the normal path, and the exception path. For the normal path, when the arrow batch is totally consumed by BE, BE will call jni to fetch the next arrow batch. For this case, the arrow batch is freed automatically. For the exceptional path, it happends when query is cancelled, or memory failed to allocate. For these corner cases, arrow batch is freed in the method close if it is not totally consumed by BE. Current supported impala data types for query includes: - BOOLEAN - TINYINT - SMALLINT - INTEGER - BIGINT - FLOAT - DOUBLE - STRING - DECIMAL(P,S) - TIMESTAMP - CHAR(N) - VARCHAR(N) - BINARY - DATE TODO: - Patches pending submission: - Support tpcds/tpch data-loading for paimon data table. - Virtual Column query support for querying paimon data table. - Query support with time travel. - Query support for paimon meta tables. - WIP: - Snapshot incremental read. - Complex type query support. - Native paimon table scanner, instead of jni based. Testing: - Create tests table in functional_schema_template.sql - Add TestPaimonScannerWithLimit in test_scanners.py - Add test_paimon_query in test_paimon.py. - Already passed the tpcds/tpch test for paimon table, due to the testing table data is currently generated by spark, and it is not supported by impala now, we have to do this since hive doesn't support generating paimon table for dynamic-partitioned tables. we plan to submit a separate patch for tpcds/tpch data loading and associated tpcds/tpch query tests. - JVM Offheap memory leak tests, have run looped tpch tests for 1 day, no obvious offheap memory increase is observed, offheap memory usage is within 10M. Change-Id: Ie679a89a8cc21d52b583422336b9f747bdf37384 Reviewed-on: http://gerrit.cloudera.org:8080/23613 Tested-by: Impala Public Jenkins Reviewed-by: Zoltan Borok-Nagy Reviewed-by: Riza Suminto --- CMakeLists.txt | 3 + be/CMakeLists.txt | 4 +- be/src/exec/CMakeLists.txt | 1 + be/src/exec/exec-node.cc | 5 + be/src/exec/paimon/CMakeLists.txt | 108 ++++++ be/src/exec/paimon/paimon-jni-row-reader.cc | 315 ++++++++++++++++++ be/src/exec/paimon/paimon-jni-row-reader.h | 82 +++++ be/src/exec/paimon/paimon-jni-scan-node.cc | 267 +++++++++++++++ be/src/exec/paimon/paimon-jni-scan-node.h | 154 +++++++++ be/src/exec/paimon/paimon-jni-scanner.cc | 108 ++++++ be/src/exec/paimon/paimon-jni-scanner.h | 77 +++++ be/src/exec/paimon/paimon-scan-plan-node.cc | 31 ++ be/src/exec/paimon/paimon-scan-plan-node.h | 36 ++ be/src/runtime/descriptors.cc | 13 + be/src/runtime/descriptors.h | 10 + be/src/scheduling/scheduler.cc | 2 +- be/src/service/impalad-main.cc | 2 + bin/bootstrap_toolchain.py | 2 +- bin/impala-config.sh | 2 + cmake_modules/FindArrow.cmake | 54 +++ common/thrift/CatalogObjects.thrift | 5 + common/thrift/PlanNodes.thrift | 24 ++ common/thrift/Types.thrift | 4 +- fe/pom.xml | 119 +++++++ .../org/apache/impala/catalog/Column.java | 10 + .../java/org/apache/impala/catalog/Table.java | 6 + .../java/org/apache/impala/catalog/Type.java | 14 +- .../catalog/local/LocalPaimonTable.java | 23 +- .../impala/catalog/local/LocalTable.java | 6 + .../impala/catalog/paimon/PaimonColumn.java | 66 ++++ .../catalog/paimon/PaimonHiveTypeUtils.java | 31 +- .../catalog/paimon/PaimonImpalaTypeUtils.java | 10 +- .../catalog/paimon/PaimonStructField.java | 87 +++++ .../impala/catalog/paimon/PaimonTable.java | 15 +- .../impala/catalog/paimon/PaimonUtil.java | 54 ++- .../apache/impala/planner/PaimonScanNode.java | 303 +++++++++++++++++ .../impala/planner/PaimonScanPlanner.java | 64 ++++ .../impala/planner/SingleNodePlanner.java | 4 + .../impala/planner/paimon/PaimonSplit.java | 45 +++ .../util/paimon/ArrowRootAllocation.java | 44 +++ .../paimon/PaimonArrowFieldTypeFactory.java | 40 +++ .../paimon/PaimonArrowFieldWriterFactory.java | 83 +++++ .../paimon/PaimonArrowFormatNativeWriter.java | 94 ++++++ .../util/paimon/PaimonArrowFormatWriter.java | 126 +++++++ .../impala/util/paimon/PaimonArrowUtils.java | 137 ++++++++ .../impala/util/paimon/PaimonJniScanner.java | 221 ++++++++++++ .../catalog/paimon/ImpalaTypeUtilsTest.java | 10 +- java/pom.xml | 1 + testdata/data/paimon_test/README.md | 115 +++++++ ...38ac-cf80-4caa-b9c3-43854884f257-0.parquet | Bin 0 -> 9033 bytes ...d1a4-637d-4275-8439-d4edd1a4583e-0.parquet | Bin 0 -> 8846 bytes ...ded8-6883-42ef-a39f-a99fc8a83938-0.parquet | Bin 0 -> 8946 bytes ...aa3a-5863-4cb5-bdb4-84d0d9002dd0-0.parquet | Bin 0 -> 9406 bytes ...0e92-de0c-4ea1-bf5e-989ecb844645-0.parquet | Bin 0 -> 9028 bytes ...bc25-fd98-471d-adca-02aa6c8f2751-0.parquet | Bin 0 -> 8907 bytes ...cb51-8c3a-448b-a0f3-605ed71e7caa-0.parquet | Bin 0 -> 8878 bytes ...a770-9eb6-4b91-b3c8-f6c84fbaf7ce-0.parquet | Bin 0 -> 8956 bytes ...b655-eed4-4ee0-9a2e-024ff4596832-0.parquet | Bin 0 -> 8494 bytes ...00a3-de35-4a74-96df-cd0fb7d08070-0.parquet | Bin 0 -> 9112 bytes ...0374-4033-48c7-b6d9-ebca0e7d0a4d-0.parquet | Bin 0 -> 9201 bytes ...4c7a-9856-4817-8f1f-a2a7dbc331c5-0.parquet | Bin 0 -> 9169 bytes ...est-fab63fbc-acd6-424a-ac4e-2a848ea51f52-0 | Bin 0 -> 2715 bytes ...ist-fa3d4f50-7f45-4d37-b5e7-22e6cf3f2cb0-0 | Bin 0 -> 884 bytes ...ist-fa3d4f50-7f45-4d37-b5e7-22e6cf3f2cb0-1 | Bin 0 -> 986 bytes .../alltypes_paimon/schema/schema-0 | 65 ++++ .../alltypes_paimon/snapshot/EARLIEST | 1 + .../alltypes_paimon/snapshot/LATEST | 1 + .../alltypes_paimon/snapshot/snapshot-1 | 18 + ...d395-e55d-4720-bdf9-d0aea3177a01-0.parquet | Bin 0 -> 4824 bytes ...cbcd-bd56-43ef-8b00-543a83cc5e6f-0.parquet | Bin 0 -> 4841 bytes ...0349-ce6f-4228-9031-f1d556dfc808-0.parquet | Bin 0 -> 4835 bytes ...9d5c-33d4-4fa2-bed0-40e8966ed9e0-0.parquet | Bin 0 -> 4729 bytes ...864e-0aa9-4b5e-8b6d-4261540072f3-0.parquet | Bin 0 -> 4808 bytes ...116d-3e51-40b6-914d-950170ece098-0.parquet | Bin 0 -> 4742 bytes ...f48f-e00f-45dc-8d44-62582cf6d881-0.parquet | Bin 0 -> 4739 bytes ...fa1b-ff44-4088-a2d6-22902d35e795-0.parquet | Bin 0 -> 4731 bytes ...7551-249f-4a13-9ec9-43be271f7b21-0.parquet | Bin 0 -> 4711 bytes ...002b-cf8d-4d17-ba96-0389d2dfb456-0.parquet | Bin 0 -> 4820 bytes ...5d5b-c764-429f-82c5-aa76bbf15a63-0.parquet | Bin 0 -> 4673 bytes ...74f8-719a-4ed5-84d0-244a6c26604b-0.parquet | Bin 0 -> 4812 bytes ...est-85ea4264-6480-4517-9d44-2a5ba022a922-0 | Bin 0 -> 2454 bytes ...ist-67f31438-ac2d-4d03-9bd9-4e1d55120c5c-0 | Bin 0 -> 884 bytes ...ist-67f31438-ac2d-4d03-9bd9-4e1d55120c5c-1 | Bin 0 -> 985 bytes .../alltypes_structs_paimon/schema/schema-0 | 67 ++++ .../alltypes_structs_paimon/snapshot/EARLIEST | 1 + .../alltypes_structs_paimon/snapshot/LATEST | 1 + .../snapshot/snapshot-1 | 18 + ...1102-7ce1-4410-8022-50afc44aa215-0.parquet | Bin 0 -> 850 bytes ...cd70-7bfc-4e04-8a37-52bb441bccc1-0.parquet | Bin 0 -> 1205 bytes ...c604-2f10-4bea-bd61-32787ba71065-0.parquet | Bin 0 -> 1205 bytes ...1f02-46fd-4114-8c89-cbae117a1b61-0.parquet | Bin 0 -> 1205 bytes ...aae7-ed6e-4f04-8648-be059c9e2942-0.parquet | Bin 0 -> 1205 bytes ...22a5-302c-4e14-bf86-dd897d868b42-0.parquet | Bin 0 -> 1205 bytes ...33f7-0e36-49e0-bd10-7b1f94ca1408-0.parquet | Bin 0 -> 1205 bytes ...526d-a35f-4c0f-91e8-def352b6432e-0.parquet | Bin 0 -> 1205 bytes ...est-1e64bd23-89a3-40ab-8f11-b11467e340fe-0 | Bin 0 -> 2468 bytes ...est-8c8eef70-e1cc-4019-bd2b-1df0c773321a-0 | Bin 0 -> 2147 bytes ...ist-b15e96d1-cea5-44c2-a5b4-dc8c7041463f-0 | Bin 0 -> 884 bytes ...ist-b15e96d1-cea5-44c2-a5b4-dc8c7041463f-1 | Bin 0 -> 989 bytes ...ist-b9930407-daa0-43cc-b4fa-107aef9a5ffc-0 | Bin 0 -> 989 bytes ...ist-b9930407-daa0-43cc-b4fa-107aef9a5ffc-1 | Bin 0 -> 989 bytes .../paimon_decimal_tbl/schema/schema-0 | 34 ++ .../paimon_decimal_tbl/snapshot/EARLIEST | 1 + .../paimon_decimal_tbl/snapshot/LATEST | 1 + .../paimon_decimal_tbl/snapshot/snapshot-1 | 18 + .../paimon_decimal_tbl/snapshot/snapshot-2 | 18 + ...bff4-abe9-4ab9-8837-081b21a80e70-0.parquet | Bin 0 -> 2927 bytes ...38a3-8481-4c98-8125-856ff5b42a12-0.parquet | Bin 0 -> 2911 bytes ...fabf-ccba-47d6-a88a-0571d4e0e7a0-0.parquet | Bin 0 -> 2925 bytes ...5077-3cc7-46cf-b0c8-f7b5f42c19f7-0.parquet | Bin 0 -> 2926 bytes ...0979-5cb2-4876-b7eb-1a8d8dcd90f7-0.parquet | Bin 0 -> 2926 bytes ...a36c-e45c-48c3-8004-9055399d7147-0.parquet | Bin 0 -> 2263 bytes ...2c17-a695-4a31-98da-ac1da8ca242f-0.parquet | Bin 0 -> 2927 bytes ...est-32d94d25-0a6f-499e-9953-e4d2dd8de146-0 | Bin 0 -> 2012 bytes ...est-65f28661-1546-4a1a-930e-f31118e55bd9-0 | Bin 0 -> 2392 bytes ...ist-5db2c8c6-e253-4fba-b0c2-71dc19d66678-0 | Bin 0 -> 884 bytes ...ist-5db2c8c6-e253-4fba-b0c2-71dc19d66678-1 | Bin 0 -> 985 bytes ...ist-c6343add-3b4b-4a00-a470-de159bd83b3d-0 | Bin 0 -> 985 bytes ...ist-c6343add-3b4b-4a00-a470-de159bd83b3d-1 | Bin 0 -> 989 bytes .../paimon_primitive_alltypes/schema/schema-0 | 66 ++++ .../snapshot/EARLIEST | 1 + .../paimon_primitive_alltypes/snapshot/LATEST | 1 + .../snapshot/snapshot-1 | 18 + .../snapshot/snapshot-2 | 18 + .../functional/functional_schema_template.sql | 52 ++- .../functional/schema_constraints.csv | 6 +- .../queries/QueryTest/paimon-negative.test | 6 +- .../queries/QueryTest/paimon-query.test | 168 ++++++++++ tests/query_test/test_paimon.py | 4 + tests/query_test/test_scanners.py | 35 ++ 130 files changed, 3604 insertions(+), 52 deletions(-) create mode 100644 be/src/exec/paimon/CMakeLists.txt create mode 100644 be/src/exec/paimon/paimon-jni-row-reader.cc create mode 100644 be/src/exec/paimon/paimon-jni-row-reader.h create mode 100644 be/src/exec/paimon/paimon-jni-scan-node.cc create mode 100644 be/src/exec/paimon/paimon-jni-scan-node.h create mode 100644 be/src/exec/paimon/paimon-jni-scanner.cc create mode 100644 be/src/exec/paimon/paimon-jni-scanner.h create mode 100644 be/src/exec/paimon/paimon-scan-plan-node.cc create mode 100644 be/src/exec/paimon/paimon-scan-plan-node.h create mode 100644 cmake_modules/FindArrow.cmake create mode 100644 fe/src/main/java/org/apache/impala/catalog/paimon/PaimonColumn.java create mode 100644 fe/src/main/java/org/apache/impala/catalog/paimon/PaimonStructField.java create mode 100644 fe/src/main/java/org/apache/impala/planner/PaimonScanNode.java create mode 100644 fe/src/main/java/org/apache/impala/planner/PaimonScanPlanner.java create mode 100644 fe/src/main/java/org/apache/impala/planner/paimon/PaimonSplit.java create mode 100644 fe/src/main/java/org/apache/impala/util/paimon/ArrowRootAllocation.java create mode 100644 fe/src/main/java/org/apache/impala/util/paimon/PaimonArrowFieldTypeFactory.java create mode 100644 fe/src/main/java/org/apache/impala/util/paimon/PaimonArrowFieldWriterFactory.java create mode 100644 fe/src/main/java/org/apache/impala/util/paimon/PaimonArrowFormatNativeWriter.java create mode 100644 fe/src/main/java/org/apache/impala/util/paimon/PaimonArrowFormatWriter.java create mode 100644 fe/src/main/java/org/apache/impala/util/paimon/PaimonArrowUtils.java create mode 100644 fe/src/main/java/org/apache/impala/util/paimon/PaimonJniScanner.java create mode 100644 testdata/data/paimon_test/README.md create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_paimon/bucket-0/data-275038ac-cf80-4caa-b9c3-43854884f257-0.parquet create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_paimon/bucket-0/data-4724d1a4-637d-4275-8439-d4edd1a4583e-0.parquet create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_paimon/bucket-0/data-7201ded8-6883-42ef-a39f-a99fc8a83938-0.parquet create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_paimon/bucket-0/data-7e80aa3a-5863-4cb5-bdb4-84d0d9002dd0-0.parquet create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_paimon/bucket-0/data-81c20e92-de0c-4ea1-bf5e-989ecb844645-0.parquet create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_paimon/bucket-0/data-9a6dbc25-fd98-471d-adca-02aa6c8f2751-0.parquet create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_paimon/bucket-0/data-9b24cb51-8c3a-448b-a0f3-605ed71e7caa-0.parquet create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_paimon/bucket-0/data-c079a770-9eb6-4b91-b3c8-f6c84fbaf7ce-0.parquet create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_paimon/bucket-0/data-d943b655-eed4-4ee0-9a2e-024ff4596832-0.parquet create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_paimon/bucket-0/data-dffe00a3-de35-4a74-96df-cd0fb7d08070-0.parquet create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_paimon/bucket-0/data-e6940374-4033-48c7-b6d9-ebca0e7d0a4d-0.parquet create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_paimon/bucket-0/data-eef44c7a-9856-4817-8f1f-a2a7dbc331c5-0.parquet create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_paimon/manifest/manifest-fab63fbc-acd6-424a-ac4e-2a848ea51f52-0 create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_paimon/manifest/manifest-list-fa3d4f50-7f45-4d37-b5e7-22e6cf3f2cb0-0 create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_paimon/manifest/manifest-list-fa3d4f50-7f45-4d37-b5e7-22e6cf3f2cb0-1 create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_paimon/schema/schema-0 create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_paimon/snapshot/EARLIEST create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_paimon/snapshot/LATEST create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_paimon/snapshot/snapshot-1 create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_structs_paimon/bucket-0/data-04a2d395-e55d-4720-bdf9-d0aea3177a01-0.parquet create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_structs_paimon/bucket-0/data-0ff3cbcd-bd56-43ef-8b00-543a83cc5e6f-0.parquet create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_structs_paimon/bucket-0/data-15fe0349-ce6f-4228-9031-f1d556dfc808-0.parquet create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_structs_paimon/bucket-0/data-43259d5c-33d4-4fa2-bed0-40e8966ed9e0-0.parquet create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_structs_paimon/bucket-0/data-4e2d864e-0aa9-4b5e-8b6d-4261540072f3-0.parquet create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_structs_paimon/bucket-0/data-602c116d-3e51-40b6-914d-950170ece098-0.parquet create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_structs_paimon/bucket-0/data-6a5bf48f-e00f-45dc-8d44-62582cf6d881-0.parquet create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_structs_paimon/bucket-0/data-72d3fa1b-ff44-4088-a2d6-22902d35e795-0.parquet create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_structs_paimon/bucket-0/data-7f917551-249f-4a13-9ec9-43be271f7b21-0.parquet create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_structs_paimon/bucket-0/data-8b7a002b-cf8d-4d17-ba96-0389d2dfb456-0.parquet create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_structs_paimon/bucket-0/data-b5785d5b-c764-429f-82c5-aa76bbf15a63-0.parquet create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_structs_paimon/bucket-0/data-f71174f8-719a-4ed5-84d0-244a6c26604b-0.parquet create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_structs_paimon/manifest/manifest-85ea4264-6480-4517-9d44-2a5ba022a922-0 create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_structs_paimon/manifest/manifest-list-67f31438-ac2d-4d03-9bd9-4e1d55120c5c-0 create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_structs_paimon/manifest/manifest-list-67f31438-ac2d-4d03-9bd9-4e1d55120c5c-1 create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_structs_paimon/schema/schema-0 create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_structs_paimon/snapshot/EARLIEST create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_structs_paimon/snapshot/LATEST create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_structs_paimon/snapshot/snapshot-1 create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_decimal_tbl/bucket-0/data-58e01102-7ce1-4410-8022-50afc44aa215-0.parquet create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_decimal_tbl/bucket-0/data-6d2bcd70-7bfc-4e04-8a37-52bb441bccc1-0.parquet create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_decimal_tbl/bucket-0/data-7f39c604-2f10-4bea-bd61-32787ba71065-0.parquet create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_decimal_tbl/bucket-0/data-8def1f02-46fd-4114-8c89-cbae117a1b61-0.parquet create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_decimal_tbl/bucket-0/data-a100aae7-ed6e-4f04-8648-be059c9e2942-0.parquet create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_decimal_tbl/bucket-0/data-bddf22a5-302c-4e14-bf86-dd897d868b42-0.parquet create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_decimal_tbl/bucket-0/data-cf7233f7-0e36-49e0-bd10-7b1f94ca1408-0.parquet create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_decimal_tbl/bucket-0/data-ddc2526d-a35f-4c0f-91e8-def352b6432e-0.parquet create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_decimal_tbl/manifest/manifest-1e64bd23-89a3-40ab-8f11-b11467e340fe-0 create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_decimal_tbl/manifest/manifest-8c8eef70-e1cc-4019-bd2b-1df0c773321a-0 create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_decimal_tbl/manifest/manifest-list-b15e96d1-cea5-44c2-a5b4-dc8c7041463f-0 create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_decimal_tbl/manifest/manifest-list-b15e96d1-cea5-44c2-a5b4-dc8c7041463f-1 create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_decimal_tbl/manifest/manifest-list-b9930407-daa0-43cc-b4fa-107aef9a5ffc-0 create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_decimal_tbl/manifest/manifest-list-b9930407-daa0-43cc-b4fa-107aef9a5ffc-1 create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_decimal_tbl/schema/schema-0 create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_decimal_tbl/snapshot/EARLIEST create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_decimal_tbl/snapshot/LATEST create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_decimal_tbl/snapshot/snapshot-1 create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_decimal_tbl/snapshot/snapshot-2 create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_primitive_alltypes/bucket-0/data-3728bff4-abe9-4ab9-8837-081b21a80e70-0.parquet create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_primitive_alltypes/bucket-0/data-493438a3-8481-4c98-8125-856ff5b42a12-0.parquet create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_primitive_alltypes/bucket-0/data-59e8fabf-ccba-47d6-a88a-0571d4e0e7a0-0.parquet create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_primitive_alltypes/bucket-0/data-6db45077-3cc7-46cf-b0c8-f7b5f42c19f7-0.parquet create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_primitive_alltypes/bucket-0/data-a73a0979-5cb2-4876-b7eb-1a8d8dcd90f7-0.parquet create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_primitive_alltypes/bucket-0/data-af00a36c-e45c-48c3-8004-9055399d7147-0.parquet create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_primitive_alltypes/bucket-0/data-b9ea2c17-a695-4a31-98da-ac1da8ca242f-0.parquet create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_primitive_alltypes/manifest/manifest-32d94d25-0a6f-499e-9953-e4d2dd8de146-0 create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_primitive_alltypes/manifest/manifest-65f28661-1546-4a1a-930e-f31118e55bd9-0 create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_primitive_alltypes/manifest/manifest-list-5db2c8c6-e253-4fba-b0c2-71dc19d66678-0 create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_primitive_alltypes/manifest/manifest-list-5db2c8c6-e253-4fba-b0c2-71dc19d66678-1 create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_primitive_alltypes/manifest/manifest-list-c6343add-3b4b-4a00-a470-de159bd83b3d-0 create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_primitive_alltypes/manifest/manifest-list-c6343add-3b4b-4a00-a470-de159bd83b3d-1 create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_primitive_alltypes/schema/schema-0 create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_primitive_alltypes/snapshot/EARLIEST create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_primitive_alltypes/snapshot/LATEST create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_primitive_alltypes/snapshot/snapshot-1 create mode 100644 testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_primitive_alltypes/snapshot/snapshot-2 create mode 100644 testdata/workloads/functional-query/queries/QueryTest/paimon-query.test diff --git a/CMakeLists.txt b/CMakeLists.txt index 11f19a87e..cfd75ddc5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -435,6 +435,9 @@ include_directories(${OPENTELEMETRY_CPP_INCLUDE_DIR}) # See be/src/kudu/security/krb5_realm_override.cc for more information. set(KRB5_REALM_OVERRIDE -Wl,--undefined=krb5_realm_override_loaded krb5_realm_override) +# find Arrow headers and libs +find_package(Arrow REQUIRED) +IMPALA_ADD_THIRDPARTY_LIB(arrow ${ARROW_INCLUDE_DIR} ${ARROW_STATIC_LIB} "") ################################################################### # System dependencies diff --git a/be/CMakeLists.txt b/be/CMakeLists.txt index 88ffb0cc8..12af823db 100644 --- a/be/CMakeLists.txt +++ b/be/CMakeLists.txt @@ -528,6 +528,7 @@ set (IMPALA_LIBS ExecSequence ExecText ExecIcebergMetadata + ExecPaimon Exprs ExprsIr GlobalFlags @@ -681,7 +682,8 @@ set (IMPALA_DEPENDENCIES java_jvm kudu_client cctz - curl) + curl + arrow) # When building with Clang, linking fails because it is trying to # use a symbol in kudu_client, but that symbol is discarded. To diff --git a/be/src/exec/CMakeLists.txt b/be/src/exec/CMakeLists.txt index 441708a78..243705b13 100644 --- a/be/src/exec/CMakeLists.txt +++ b/be/src/exec/CMakeLists.txt @@ -25,6 +25,7 @@ add_subdirectory(rcfile) add_subdirectory(sequence) add_subdirectory(text) add_subdirectory(iceberg-metadata) +add_subdirectory(paimon) # where to put generated libraries set(LIBRARY_OUTPUT_PATH "${BUILD_OUTPUT_ROOT_DIRECTORY}/exec") diff --git a/be/src/exec/exec-node.cc b/be/src/exec/exec-node.cc index 78b8ff555..0a34ede5b 100644 --- a/be/src/exec/exec-node.cc +++ b/be/src/exec/exec-node.cc @@ -44,6 +44,7 @@ #include "exec/kudu/kudu-scan-node.h" #include "exec/kudu/kudu-util.h" #include "exec/nested-loop-join-node.h" +#include "exec/paimon/paimon-scan-plan-node.h" #include "exec/partial-sort-node.h" #include "exec/partitioned-hash-join-node.h" #include "exec/select-node.h" @@ -239,6 +240,9 @@ Status PlanNode::CreatePlanNode( case TPlanNodeType::ICEBERG_MERGE_NODE: *node = pool->Add(new IcebergMergePlanNode()); break; + case TPlanNodeType::PAIMON_SCAN_NODE: + *node = pool->Add(new PaimonScanPlanNode()); + break; default: map::const_iterator i = _TPlanNodeType_VALUES_TO_NAMES.find(tnode.node_type); @@ -432,6 +436,7 @@ void ExecNode::CollectScanNodes(vector* nodes) { CollectNodes(TPlanNodeType::HDFS_SCAN_NODE, nodes); CollectNodes(TPlanNodeType::HBASE_SCAN_NODE, nodes); CollectNodes(TPlanNodeType::KUDU_SCAN_NODE, nodes); + CollectNodes(TPlanNodeType::PAIMON_SCAN_NODE, nodes); } Status ExecNode::ExecDebugActionImpl(TExecNodePhase::type phase, RuntimeState* state) { diff --git a/be/src/exec/paimon/CMakeLists.txt b/be/src/exec/paimon/CMakeLists.txt new file mode 100644 index 000000000..f1bf79616 --- /dev/null +++ b/be/src/exec/paimon/CMakeLists.txt @@ -0,0 +1,108 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# where to put generated libraries +set(LIBRARY_OUTPUT_PATH "${BUILD_OUTPUT_ROOT_DIRECTORY}/exec/paimon") + +# where to put generated binaries +set(EXECUTABLE_OUTPUT_PATH "${BUILD_OUTPUT_ROOT_DIRECTORY}/exec/paimon") + +add_library(ExecPaimon + paimon-jni-row-reader.cc + paimon-jni-scan-node.cc + paimon-jni-scanner.cc + paimon-scan-plan-node.cc +) + +# Below is a work-around to fix clang compile issue. For the normal build option, +# the compile will be successful. The issue will occur if CMAKE_BUILD_TYPE has +# following types: ADDRESS_SANITIZER, TIDY, UBSAN,UBSAN_FULL, TSAN, TSAN_FULL. +# For the normal build option, will invoke g++, however for the above build type, +# build script will use clang++ instead of g++ to compile, in this case, following +# error will occur: +# The detailed errors are: +# error: cannot cast private base class 'std::__detail::__variant::_Variant_storage +# , +# std::vector > >' to +# 'std::variant, +# std::vector > >' +# return static_cast&>(__rhs); +# +# +# looks like it hit bug: https://bugs.llvm.org/show_bug.cgi?id=31852 +# and the following patch is already applied: +# https://gcc.gnu.org/cgit/gcc/commit/?id=aafaa3254ec6fc3d5e3a15a40185950d3af04432 +# in the libc++ header of our toolchain. +# but looks like the patch above is only fixes the method get +# it doesn't fix the method static_cast. +# here we use the same work-ground for method static_cast. +# NOTE: the fix below only applies to current version of toolchain. +# need to adapt if toolchain is upgraded. +set(PAIMON_PATCH_GCC_HEADER_DIR + gcc-$ENV{IMPALA_GCC_VERSION}/include/c++/$ENV{IMPALA_GCC_VERSION}) + +set(PAIMON_PATCH_WORK_DIR + $ENV{IMPALA_TOOLCHAIN_PACKAGES_HOME}/${PAIMON_PATCH_GCC_HEADER_DIR}) + +set(PAIMON_PATCH_TARGET_FILE + ${PAIMON_PATCH_WORK_DIR}/variant) + +# change this if upgraded toolchain still haven't fix the issue. +set(PAIMON_PATCH_TARGET_FILE_CHECKSUM_ORIG + 4daf8153a09ee07bab2ac339e21b9725e17a40854a42284c85b3d2ba3c0862e3) + +set(PAIMON_PATCH_SENTINEL_FILE + ${PAIMON_PATCH_WORK_DIR}/variant_patched) +file(SHA256 ${PAIMON_PATCH_TARGET_FILE} + PAIMON_PATCH_TARGET_FILE_CHECKSUM_CURR) +message("Current Hash Is: " + ${PAIMON_PATCH_TARGET_FILE_CHECKSUM_CURR}) +message("Original Hash Is: " + ${PAIMON_PATCH_TARGET_FILE_CHECKSUM_ORIG}) + +if(PAIMON_PATCH_TARGET_FILE_CHECKSUM_ORIG STREQUAL PAIMON_PATCH_TARGET_FILE_CHECKSUM_CURR + ) + + message(STATUS "apply variant patch to fix clang++ static_cast issue.") + # To fix the compile error, the following tiny patch should be applied to + # toolchain-packages-gcc10.4.0/gcc-10.4.0/include/c++/10.4.0/variant + file(WRITE /tmp/variant.patch + "1296a1297,1299 + > #if defined(__clang__) && __clang_major__ <= 7 + > : public __detail::__variant::_Variant_base<_Types...>, + > #else + 1297a1301 + > #endif + ") + add_custom_command( + OUTPUT ${PAIMON_PATCH_SENTINEL_FILE} + COMMAND cd ${PAIMON_PATCH_WORK_DIR} && + patch -b -i /tmp/variant.patch ${PAIMON_PATCH_TARGET_FILE} && + touch ${PAIMON_PATCH_SENTINEL_FILE} + DEPENDS ${PAIMON_PATCH_TARGET_FILE} + COMMENT "Patching variant file in GCC include directory to fix static_cast issue." + VERBATIM + ) + add_custom_target(patch_variant + DEPENDS ${PAIMON_PATCH_SENTINEL_FILE} + COMMENT "Variant patch applied." + ) + add_dependencies(ExecPaimon patch_variant gen-deps) +else() + message(STATUS "skip apply patch since hash is changed.") + add_dependencies(ExecPaimon gen-deps) +endif() diff --git a/be/src/exec/paimon/paimon-jni-row-reader.cc b/be/src/exec/paimon/paimon-jni-row-reader.cc new file mode 100644 index 000000000..00990c537 --- /dev/null +++ b/be/src/exec/paimon/paimon-jni-row-reader.cc @@ -0,0 +1,315 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +#include "exec/paimon/paimon-jni-row-reader.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "common/global-types.h" +#include "exec/exec-node.inline.h" +#include "exec/parquet/parquet-common.h" +#include "exec/read-write-util.h" +#include "gutil/walltime.h" +#include "runtime/collection-value-builder.h" +#include "runtime/decimal-value.h" +#include "runtime/runtime-state.h" +#include "runtime/timestamp-value.h" +#include "runtime/timestamp-value.inline.h" +#include "runtime/tuple-row.h" +#include "util/jni-util.h" + +namespace impala { + +PaimonJniRowReader::PaimonJniRowReader() {} + +Status PaimonJniRowReader::MaterializeTuple(const arrow::RecordBatch& recordBatch, + const int row_index, const TupleDescriptor* tuple_desc, Tuple* tuple, + MemPool* tuple_data_pool, RuntimeState* state) { + DCHECK(tuple != nullptr); + DCHECK(tuple_data_pool != nullptr); + DCHECK(tuple_desc != nullptr); + int col = 0; + DCHECK(recordBatch.num_columns() == tuple_desc->slots().size()); + for (const SlotDescriptor* slot_desc : tuple_desc->slots()) { + std::shared_ptr arr = recordBatch.column(col); + DCHECK(arr != nullptr); + RETURN_IF_ERROR( + WriteSlot(arr.get(), row_index, slot_desc, tuple, tuple_data_pool, state)); + col++; + } + return Status::OK(); +} + +template +Status PaimonJniRowReader::WriteSlot(const AT* arrow_array, int row_idx, void* slot) { + T value = arrow_array->Value(row_idx); + *reinterpret_cast(slot) = value; + return Status::OK(); +} + +template +Status PaimonJniRowReader::CastAndWriteSlot( + const arrow::Array* arrow_array, const int row_idx, void* slot) { + auto derived_array = static_cast(arrow_array); + return WriteSlot(derived_array, row_idx, slot); +} + +Status PaimonJniRowReader::WriteSlot(const arrow::Array* array, int row_index, + const SlotDescriptor* slot_desc, Tuple* tuple, MemPool* tuple_data_pool, + RuntimeState* state) { + if (array->IsNull(row_index)) { + tuple->SetNull(slot_desc->null_indicator_offset()); + return Status::OK(); + } + void* slot = tuple->GetSlot(slot_desc->tuple_offset()); + const ColumnType& type = slot_desc->type(); + switch (type.type) { + case TYPE_CHAR: { + RETURN_IF_ERROR(WriteVarCharOrCharSlot( + array, row_index, slot_desc->type().len, slot, tuple_data_pool)); + break; + } + case TYPE_STRING: + case TYPE_VARCHAR: { + if (type.IsBinaryType()) { // byte[] + RETURN_IF_ERROR(WriteStringOrBinarySlot( + array, row_index, slot, tuple_data_pool)); + } else { + RETURN_IF_ERROR(WriteStringOrBinarySlot( + array, row_index, slot, tuple_data_pool)); + } + break; + } + case TYPE_BOOLEAN: { + RETURN_IF_ERROR( + (CastAndWriteSlot(array, row_index, slot))); + break; + } + case TYPE_DATE: { + RETURN_IF_ERROR(WriteDateSlot(array, row_index, slot)); + break; + } + case TYPE_TINYINT: { + RETURN_IF_ERROR( + (CastAndWriteSlot(array, row_index, slot))); + break; + } + case TYPE_SMALLINT: { + RETURN_IF_ERROR( + (CastAndWriteSlot(array, row_index, slot))); + break; + } + case TYPE_INT: { + RETURN_IF_ERROR( + (CastAndWriteSlot(array, row_index, slot))); + break; + } + case TYPE_BIGINT: { + RETURN_IF_ERROR( + (CastAndWriteSlot(array, row_index, slot))); + break; + } + case TYPE_FLOAT: { + RETURN_IF_ERROR( + (CastAndWriteSlot(array, row_index, slot))); + break; + } + case TYPE_DOUBLE: { + RETURN_IF_ERROR( + (CastAndWriteSlot(array, row_index, slot))); + break; + } + case TYPE_DECIMAL: { + RETURN_IF_ERROR(WriteDecimalSlot(array, row_index, type, slot)); + break; + } + case TYPE_TIMESTAMP: { + RETURN_IF_ERROR( + WriteTimeStampSlot(array, state->local_time_zone(), row_index, slot)); + break; + } + case TYPE_STRUCT: { + // TODO: implement struct type support later + tuple->SetNull(slot_desc->null_indicator_offset()); + break; + } + case TYPE_ARRAY: { + // TODO: implement array type support later + tuple->SetNull(slot_desc->null_indicator_offset()); + break; + } + case TYPE_MAP: { + // TODO: implement map type support later + tuple->SetNull(slot_desc->null_indicator_offset()); + break; + } + default: + DCHECK(false) << "Unsupported column type: " << slot_desc->type().type; + tuple->SetNull(slot_desc->null_indicator_offset()); + } + return Status::OK(); +} + +Status PaimonJniRowReader::WriteDateSlot( + const arrow::Array* array, const int row_idx, void* slot) { + const arrow::Date32Array* date_array = static_cast(array); + int32_t days_since_epoch = date_array->Value(row_idx); + + // This will set the value to DateValue::INVALID_DAYS_SINCE_EPOCH if it is out of + // range. + DateValue result(days_since_epoch); + *reinterpret_cast(slot) = result.Value(); + return Status::OK(); +} + +// Sets the decimal value in the slot. Inline method to avoid nested switch statements. +static const std::string ERROR_INVALID_DECIMAL = "Invalid Decimal Format"; +inline Status SetDecimalVal( + const ColumnType& type, const uint8_t* buffer, int len, void* slot) { + switch (type.GetByteSize()) { + case 4: { + Decimal4Value* val = reinterpret_cast(slot); + if (UNLIKELY( + (ParquetPlainEncoder::Decode(buffer, buffer + len, len, val)) + < 0)) { + return Status(ERROR_INVALID_DECIMAL); + } + break; + } + case 8: { + Decimal8Value* val = reinterpret_cast(slot); + if (UNLIKELY( + (ParquetPlainEncoder::Decode(buffer, buffer + len, len, val)) + < 0)) { + return Status(ERROR_INVALID_DECIMAL); + } + break; + } + case 16: { + Decimal16Value* val = reinterpret_cast(slot); + if (UNLIKELY( + (ParquetPlainEncoder::Decode(buffer, buffer + len, len, val)) + < 0)) { + return Status(ERROR_INVALID_DECIMAL); + } + break; + } + default: + DCHECK(false); + } + return Status::OK(); +} + +Status PaimonJniRowReader::WriteDecimalSlot( + const arrow::Array* array, const int row_idx, const ColumnType& type, void* slot) { + const arrow::BinaryArray* binary_array = static_cast(array); + int byte_length = 0; + const uint8_t* data = binary_array->GetValue(row_idx, &byte_length); + DCHECK(byte_length > 0 && byte_length <= 16); + return SetDecimalVal(type, data, byte_length, slot); +} + +Status PaimonJniRowReader::WriteTimeStampSlot( + const arrow::Array* array, const Timezone* timezone, const int row_idx, void* slot) { + const arrow::TimestampArray* date_array = (const arrow::TimestampArray*)array; + int64_t value = date_array->Value(row_idx); + const auto& type = static_cast(*date_array->type()); + const std::string& tz_name = type.timezone(); + const Timezone* tz = tz_name.empty() ? UTCPTR : timezone; + switch (type.unit()) { + case ::arrow::TimeUnit::NANO: + *reinterpret_cast(slot) = TimestampValue::FromUnixTimeNanos( + value / NANOS_PER_SEC, value % NANOS_PER_SEC, tz); + break; + case ::arrow::TimeUnit::MICRO: + *reinterpret_cast(slot) = + TimestampValue::FromUnixTimeMicros(value, tz); + break; + case ::arrow::TimeUnit::MILLI: + *reinterpret_cast(slot) = + TimestampValue::FromUnixTimeMicros(value * 1000L, tz); + break; + case ::arrow::TimeUnit::SECOND: + *reinterpret_cast(slot) = TimestampValue::FromUnixTime(value, tz); + break; + } + + return Status::OK(); +} + +template +Status PaimonJniRowReader::WriteVarCharOrCharSlot(const arrow::Array* array, + const int row_idx, int dst_len, void* slot, MemPool* tuple_data_pool) { + const arrow::StringArray* nchar_array = static_cast(array); + std::string_view v = nchar_array->Value(row_idx); + + int src_len = v.size(); + int unpadded_len = std::min(dst_len, src_len); + // Allocate memory and copy the bytes from the JVM to the RowBatch. + char* dst_char = reinterpret_cast(slot); + memcpy(dst_char, v.data(), unpadded_len); + StringValue::PadWithSpaces(dst_char, dst_len, unpadded_len); + return Status::OK(); +} + +/// Obtain bytes from arrow string/binary batch first, Then the data has to be copied +/// to the tuple_data_pool, because the Fe PaimonJniScanner releases the JVM offheap +/// memory later. +template +Status PaimonJniRowReader::WriteStringOrBinarySlot( + const arrow::Array* array, const int row_idx, void* slot, MemPool* tuple_data_pool) { + std::string_view v; + uint32_t jbuffer_size = 0; + if constexpr (IS_BINARY) { + const arrow::BinaryArray* binary_array = + static_cast(array); + v = binary_array->Value(row_idx); + } else { + const arrow::StringArray* string_array = + static_cast(array); + v = string_array->Value(row_idx); + } + + jbuffer_size = v.size(); + // Allocate memory and copy the bytes from the JVM to the RowBatch. + char* buffer = + reinterpret_cast(tuple_data_pool->TryAllocateUnaligned(jbuffer_size)); + if (UNLIKELY(buffer == nullptr)) { + string details = strings::Substitute("Failed to allocate $0 bytes for $1.", + jbuffer_size, IS_BINARY ? "binary" : "string"); + return tuple_data_pool->mem_tracker()->MemLimitExceeded( + nullptr, details, jbuffer_size); + } + + memcpy(buffer, v.data(), jbuffer_size); + reinterpret_cast(slot)->Assign(buffer, jbuffer_size); + return Status::OK(); +} +} // namespace impala diff --git a/be/src/exec/paimon/paimon-jni-row-reader.h b/be/src/exec/paimon/paimon-jni-row-reader.h new file mode 100644 index 000000000..9464f00ba --- /dev/null +++ b/be/src/exec/paimon/paimon-jni-row-reader.h @@ -0,0 +1,82 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +#pragma once + +#include "common/global-types.h" +#include "common/status.h" +#include "runtime/types.h" + +#include + +namespace impala { + +class MemPool; +class RuntimeState; +class ScanNode; +class Status; +class SlotDescriptor; +class Tuple; +class TupleDescriptor; + +/// Row reader for Paimon table scans, it translates a row of arrow RecordBatch to +/// Impala row batch tuples. +class PaimonJniRowReader { + public: + PaimonJniRowReader(); + + /// Materialize the Arrow batch into Impala rows. + Status MaterializeTuple(const arrow::RecordBatch& recordBatch, const int row_idx, + const TupleDescriptor* tuple_desc, Tuple* tuple, MemPool* tuple_data_pool, + RuntimeState* state); + + private: + // Writes a row denoted by 'row_idx' from an arrow batch into the target tuple. + Status WriteSlot(const arrow::Array* array, const int row_idx, + const SlotDescriptor* slot_desc, Tuple* tuple, MemPool* tuple_data_pool, + RuntimeState* state) WARN_UNUSED_RESULT; + + /// Template method that writes a value from 'arrow_array' to 'slot'. + /// 'T' is the type of the slot, + /// 'AT' is the proper subtype of the arrow::Array. + template + Status WriteSlot(const AT* arrow_array, int row_idx, void* slot) WARN_UNUSED_RESULT; + + template + Status CastAndWriteSlot( + const arrow::Array* arrow_array, const int row_idx, void* slot) WARN_UNUSED_RESULT; + + Status WriteDateSlot( + const arrow::Array* array, const int row_idx, void* slot) WARN_UNUSED_RESULT; + + Status WriteDecimalSlot(const arrow::Array* array, const int row_idx, + const ColumnType& type, void* slot) WARN_UNUSED_RESULT; + + /// Paimon TimeStamp is parsed into TimestampValue. + Status WriteTimeStampSlot(const arrow::Array* array, const Timezone* timezone, + const int row_idx, void* slot) WARN_UNUSED_RESULT; + + template + Status WriteStringOrBinarySlot(const arrow::Array* array, const int row_idx, void* slot, + MemPool* tuple_data_pool) WARN_UNUSED_RESULT; + + template + Status WriteVarCharOrCharSlot(const arrow::Array* array, const int row_idx, int max_len, + void* slot, MemPool* tuple_data_pool) WARN_UNUSED_RESULT; +}; + +} // namespace impala diff --git a/be/src/exec/paimon/paimon-jni-scan-node.cc b/be/src/exec/paimon/paimon-jni-scan-node.cc new file mode 100644 index 000000000..63cb689a4 --- /dev/null +++ b/be/src/exec/paimon/paimon-jni-scan-node.cc @@ -0,0 +1,267 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +#include "exec/paimon/paimon-jni-scan-node.h" +#include "common/status.h" +#include "exec/exec-node-util.h" +#include "exec/exec-node.inline.h" +#include "exec/paimon/paimon-jni-row-reader.h" +#include "exec/paimon/paimon-jni-scanner.h" +#include "exec/paimon/paimon-scan-plan-node.h" +#include "rpc/thrift-util.h" +#include "runtime/exec-env.h" +#include "runtime/runtime-state.h" +#include "runtime/tuple-row.h" +#include "util/jni-util.h" +#include "util/periodic-counter-updater.h" +#include "util/runtime-profile-counters.h" + +#include +#include +#include +#include +using namespace impala; + +PaimonJniScanNode::PaimonJniScanNode( + ObjectPool* pool, const PaimonScanPlanNode& pnode, const DescriptorTbl& descs) + : ScanNode(pool, pnode, descs), + tuple_id_(pnode.tnode_->paimon_table_scan_node.tuple_id), + table_name_(pnode.tnode_->paimon_table_scan_node.table_name), + splits_empty_(false), + paimon_last_arrow_record_batch_consumed_bytes_(0), + arrow_record_batch_row_count_(0), + arrow_record_batch_row_index_(0) {} + +Status PaimonJniScanNode::Prepare(RuntimeState* state) { + RETURN_IF_ERROR(ScanNode::Prepare(state)); + DCHECK(scan_range_params_ != NULL) + << "Must call SetScanRanges() before calling Prepare()"; + + scan_open_timer_ = ADD_TIMER(runtime_profile(), "ScanOpenTime"); + paimon_api_scan_timer_ = ADD_TIMER(runtime_profile(), "PaimonApiScanTime"); + + tuple_desc_ = state->desc_tbl().GetTupleDescriptor(tuple_id_); + if (tuple_desc_ == nullptr) { + return Status( + "Failed to get tuple descriptor, tuple id: " + std::to_string(tuple_id_)); + } + arrow_batch_mem_tracker_.reset( + new MemTracker(-1, "Arrow Batch", this->mem_tracker(), false)); + /// Construct the jni scan param, the param will be used in PaimonJniScanner. + paimon_jni_scan_param_.__set_paimon_table_obj( + plan_node_.tnode_->paimon_table_scan_node.paimon_table_obj); + /// update projection id, will get the top-level field ids of each tuple. + std::vector field_ids; + RETURN_IF_ERROR(CollectProjectionFieldIds(tuple_desc_, field_ids)); + paimon_jni_scan_param_.__set_projection(field_ids); + LOG(INFO) << table_name_ << " Contains " << field_ids.size() << " field ids." + << std::endl; + paimon_jni_scan_param_.__set_mem_limit_bytes( + arrow_batch_mem_tracker_->GetLowestLimit(MemLimit::HARD)); + paimon_jni_scan_param_.__set_batch_size(state->batch_size()); + paimon_jni_scan_param_.__set_fragment_id(state->fragment_instance_id()); + std::vector scan_range_vector; + for (const ScanRangeParamsPB& params : *scan_range_params_) { + DCHECK(params.scan_range().has_file_metadata()); + const std::string& split = params.scan_range().file_metadata(); + scan_range_vector.push_back(split); + } + paimon_jni_scan_param_.__set_splits(scan_range_vector); + /// Check if splits is empty + splits_empty_ = scan_range_vector.empty(); + impala::ThriftSerializer serializer(false); + /// serialize the jni scan param to binary. + RETURN_IF_ERROR(serializer.SerializeToString( + &paimon_jni_scan_param_, &paimon_jni_scan_param_serialized_)); + return Status::OK(); +} + +Status PaimonJniScanNode::Open(RuntimeState* state) { + SCOPED_TIMER(scan_open_timer_); + RETURN_IF_ERROR(ScanNode::Open(state)); + /// Skip if splits is empty. + if (splits_empty_) { + return Status::OK(); + } + JNIEnv* env = JniUtil::GetJNIEnv(); + if (env == nullptr) return Status("Failed to get/create JVM"); + jni_scanner_.reset( + new PaimonJniScanner(paimon_jni_scan_param_serialized_, tuple_desc_, table_name_)); + RETURN_IF_ERROR(jni_scanner_->Init(env)); + paimon_row_reader_.reset(new PaimonJniRowReader()); + SCOPED_TIMER(paimon_api_scan_timer_); + RETURN_IF_ERROR(jni_scanner_->ScanTable(env)); + return Status::OK(); +} + +Status PaimonJniScanNode::CollectProjectionFieldIds( + const TupleDescriptor* tuple_desc_, vector& projection) { + for (const SlotDescriptor* slot_desc : tuple_desc_->slots()) { + int field_id = -1; + if (slot_desc->col_path().size() == 1) { + // Top level slots have ColumnDescriptors that store the field ids. + field_id = tuple_desc_->table_desc()->GetColumnDesc(slot_desc).field_id(); + } else { + // TODO: support the nested field later. + return Status("Paimon Scanner currently doesn't support nested type now"); + } + DCHECK_NE(field_id, -1); + projection.push_back(field_id); + } + return Status::OK(); +} + +Status PaimonJniScanNode::GetNextBatchIfNeeded(bool* is_empty_batch) { + /// Check if we need to fetch next arrow record batch from jni. if yes, + /// fetch it. + if (paimon_arrow_record_batch_holder_ == nullptr + || arrow_record_batch_row_index_ >= arrow_record_batch_row_count_) { + SCOPED_TIMER(paimon_api_scan_timer_); + JNIEnv* env = JniUtil::GetJNIEnv(); + struct ArrowArray* array; + struct ArrowSchema* schema; + long row_count; + long offheap_consumed_bytes; + + DCHECK(is_empty_batch != nullptr); + + RETURN_IF_ERROR(jni_scanner_->GetNextBatchDirect( + env, &array, &schema, &row_count, &offheap_consumed_bytes)); + + *is_empty_batch = false; + if (row_count > 0) { + auto resultImportVectorSchemaRoot = arrow::ImportRecordBatch(array, schema); + /// since the result type is arrow::Status, need to check status manually + /// without using macro RETURN_IF_ERROR + if (!resultImportVectorSchemaRoot.ok()) { + return Status(resultImportVectorSchemaRoot.status().message()); + } + paimon_arrow_record_batch_holder_ = resultImportVectorSchemaRoot.ValueUnsafe(); + arrow_record_batch_row_count_ = row_count; + DCHECK_EQ(row_count, paimon_arrow_record_batch_holder_->num_rows()); + arrow_record_batch_row_index_ = 0; + /// update allocated offheap memory reported from Jni. + /// need to do for each batch since we need to check the + /// memory usage hit the limit of mem tracker. + OffheapTrackFree(); + RETURN_IF_ERROR(OffheapTrackAllocation(offheap_consumed_bytes)); + + } else { + /// No more batches to fetch. + paimon_arrow_record_batch_holder_ = nullptr; + arrow_record_batch_row_count_ = 0; + arrow_record_batch_row_index_ = 0; + OffheapTrackFree(); + } + } + + *is_empty_batch = arrow_record_batch_row_count_ > 0 + && arrow_record_batch_row_index_ < arrow_record_batch_row_count_; + + return Status::OK(); +} + +Status PaimonJniScanNode::GetNext(RuntimeState* state, RowBatch* row_batch, bool* eos) { + SCOPED_TIMER(runtime_profile_->total_time_counter()); + ScopedGetNextEventAdder ea(this, eos); + RETURN_IF_ERROR(ExecDebugAction(TExecNodePhase::GETNEXT, state)); + RETURN_IF_CANCELLED(state); + RETURN_IF_ERROR(QueryMaintenance(state)); + + /// Return eos if empty splits or reached limit. + if (splits_empty_ || ReachedLimit()) { + *eos = true; + return Status::OK(); + } + + /// Set eos to false initially for the batch. + *eos = false; + + /// Allocate buffer for RowBatch and init the tuple + uint8_t* tuple_buffer; + int64_t tuple_buffer_size; + RETURN_IF_ERROR( + row_batch->ResizeAndAllocateTupleBuffer(state, &tuple_buffer_size, &tuple_buffer)); + Tuple* tuple = reinterpret_cast(tuple_buffer); + tuple->Init(tuple_buffer_size); + + SCOPED_TIMER(materialize_tuple_timer()); + while (!ReachedLimit() && !row_batch->AtCapacity()) { + /// Break the loop if is canceled, or maintainance is needed. + if (state->is_cancelled() || !QueryMaintenance(state).ok()) { + break; + } + int row_idx = row_batch->AddRow(); + TupleRow* tuple_row = row_batch->GetRow(row_idx); + tuple_row->SetTuple(0, tuple); + + /// Get the next arrow batch from 'org.apache.impala.util.paimon.PaimonJniScanner' + /// if the current arrow batch is already consumed. + bool fetched; + RETURN_IF_ERROR(GetNextBatchIfNeeded(&fetched)); + /// When fetched is false, there are no more arrow batches to read + if (!fetched) { + *eos = true; + break; + } + DCHECK(paimon_arrow_record_batch_holder_ != nullptr); + COUNTER_ADD(rows_read_counter(), 1); + RETURN_IF_ERROR(paimon_row_reader_->MaterializeTuple( + *paimon_arrow_record_batch_holder_, arrow_record_batch_row_index_, tuple_desc_, + tuple, row_batch->tuple_data_pool(), state)); + /// Evaluate conjuncts on this tuple row + if (ExecNode::EvalConjuncts( + conjunct_evals().data(), conjunct_evals().size(), tuple_row)) { + row_batch->CommitLastRow(); + tuple = reinterpret_cast( + reinterpret_cast(tuple) + tuple_desc_->byte_size()); + IncrementNumRowsReturned(1); + } else { + /// Reset the null bits, everyhing else will be overwritten + Tuple::ClearNullBits( + tuple, tuple_desc_->null_bytes_offset(), tuple_desc_->num_null_bytes()); + } + /// will process the next row of arrow RecordBatch in next iteration. + arrow_record_batch_row_index_++; + } + if (ReachedLimit()) { + *eos = true; + } + return Status::OK(); +} + +void PaimonJniScanNode::Close(RuntimeState* state) { + if (is_closed()) return; + /// Eagerly release arrow batch before calling + /// frontend method close, in case of + /// mem leak. leak was observed when query was + /// canceled. + if (paimon_arrow_record_batch_holder_ != nullptr) { + paimon_arrow_record_batch_holder_.reset(); + arrow_record_batch_row_index_ = 0; + arrow_record_batch_row_count_ = 0; + } + /// Close jni scanner if splits is not empty. + if (!splits_empty_) { + jni_scanner_->Close(state); + } + OffheapTrackFree(); + if (arrow_batch_mem_tracker_ != nullptr) { + arrow_batch_mem_tracker_->Close(); + } + ScanNode::Close(state); +} diff --git a/be/src/exec/paimon/paimon-jni-scan-node.h b/be/src/exec/paimon/paimon-jni-scan-node.h new file mode 100644 index 000000000..9ff10f3eb --- /dev/null +++ b/be/src/exec/paimon/paimon-jni-scan-node.h @@ -0,0 +1,154 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +#pragma once + +#include "common/global-types.h" +#include "exec/paimon/paimon-jni-row-reader.h" +#include "exec/paimon/paimon-jni-scanner.h" +#include "exec/scan-node.h" +#include "runtime/descriptors.h" + +#include +#include + +namespace impala { + +class ExecNode; +class PaimonJniRowReader; +class RuntimeState; +class Status; + +/// Scan node for an Paimon table. +/// Since Paimon API can be used to scan both paimon data tables and metadata tables. +/// The current jni scanner works as a generic solution to scan both data table and +/// metadata tables. +/// For scanning these paimon tables this scanner calls into the JVM and creates an +/// 'PaimonJniScanner' object that does the scanning. Once the Paimon scan is done, +/// To minimize the jni overhead, the PaimonJniScanner will first write batch of +/// InternalRows to the arrow BatchRecord into the offheap, and the offheap memory +/// pointer will pass to the native side to directly read the batch record and +/// convert the arrow format into native impala RowBatch. The benchmark shows 2.x +/// better performance than pure jni implementation. +/// +/// The flow of scanning is: +/// 1. Backend: Get the splits/table obj from plan node, generate thrift +/// encoded param and passes to the JNI scanner. +/// 2. Backend: Creates an PaimonJniScanner object on the Java heap. +/// 3. Backend: Triggers a table scan on the Frontend +/// 4. Frontend: Executes the scan +/// 5. Backend: Calls GetNext that calls PaimonJniScanner's GetNext +/// 6. Frontend: PaimonJniScanner's GetNextBatchDirect will return the offheap pointer, +/// as well as consumed offheap bytes size to the arrow row batch. +/// 7. Backend: Consume the arrow RecordBatch into impala RowBatch. +/// +/// Note: +/// This scan node can be executed on any executor. + +class PaimonScanPlanNode; +class PaimonJniScanNode : public ScanNode { + public: + PaimonJniScanNode( + ObjectPool* pool, const PaimonScanPlanNode& pnode, const DescriptorTbl& descs); + + /// Initializes counters, executes Paimon table scan and initializes accessors. + Status Prepare(RuntimeState* state) override; + + /// Creates the Paimon row reader. + Status Open(RuntimeState* state) override; + + /// Fills the next rowbatch with the results returned by the Paimon scan. + Status GetNext(RuntimeState* state, RowBatch* row_batch, bool* eos) override; + + /// Finalize and close this operator. + void Close(RuntimeState* state) override; + + protected: + Status CollectProjectionFieldIds( + const TupleDescriptor* tuple_desc, vector& projection); + Status OffheapTrackAllocation(long bytes); + void OffheapTrackFree(); + + private: + /// Adapter that helps preparing the table and executes an Paimon table scan + /// on Java side. Allows the ScanNode to fetch the Arrow RecordBatch from the + /// Java Heap. + std::unique_ptr jni_scanner_; + + /// Helper class to transform Paimon rows to Impala tuples. + std::unique_ptr paimon_row_reader_; + + /// Get the next arrow row record batch from the jni scanner. + /// returns false if no record is available anymore, true if valid row is returned. + Status GetNextBatchIfNeeded(bool* is_empty_batch); + + /// The TupleId and TupleDescriptor of the tuple that this scan node will populate. + const TupleId tuple_id_; + const TupleDescriptor* tuple_desc_ = nullptr; + + /// The table name. + const std::string table_name_; + + // Paimon scan param. + TPaimonJniScanParam paimon_jni_scan_param_; + /// Indicate whether the splits is empty. + /// It is used to control whether the + /// jni scanner should be created. If splits is empty, + /// it will bypass the JNI operation, in other words, + /// all jni related operation will be skipped,directly + /// set eof flag to true, and return empty scan + /// result. + bool splits_empty_; + /// MemTracker for tracing arrow used JVM offheap memory. + /// Initialized in Prepare(). Owned by RuntimeState. + std::unique_ptr arrow_batch_mem_tracker_; + /// last consumed offheap bytes for arrow batch + long paimon_last_arrow_record_batch_consumed_bytes_; + /// Thrift serialized paimon scan param + std::string paimon_jni_scan_param_serialized_; + /// current unconsumed arrow record batch. + std::shared_ptr paimon_arrow_record_batch_holder_; + /// current row_count of the arrow record batch. + long arrow_record_batch_row_count_; + /// current row_index of the arrow record batch. + long arrow_record_batch_row_index_; + /// Paimon scan specific counters. + RuntimeProfile::Counter* scan_open_timer_; + RuntimeProfile::Counter* paimon_api_scan_timer_; +}; + +inline Status PaimonJniScanNode::OffheapTrackAllocation(long consumed_bytes) { + if (consumed_bytes > 0) { + if (arrow_batch_mem_tracker_->TryConsume(consumed_bytes)) { + paimon_last_arrow_record_batch_consumed_bytes_ = consumed_bytes; + return Status::OK(); + } else { + return Status::MemLimitExceeded("Arrow batch size exceed the mem limit."); + } + } else { + return Status::OK(); + } +} + +inline void PaimonJniScanNode::OffheapTrackFree() { + if (paimon_last_arrow_record_batch_consumed_bytes_ > 0) { + arrow_batch_mem_tracker_->Release(paimon_last_arrow_record_batch_consumed_bytes_); + paimon_last_arrow_record_batch_consumed_bytes_ = 0; + } +} + +} // namespace impala diff --git a/be/src/exec/paimon/paimon-jni-scanner.cc b/be/src/exec/paimon/paimon-jni-scanner.cc new file mode 100644 index 000000000..460ff9930 --- /dev/null +++ b/be/src/exec/paimon/paimon-jni-scanner.cc @@ -0,0 +1,108 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +#include "exec/paimon/paimon-jni-scanner.h" +#include +#include "util/jni-util.h" + +namespace impala { + +PaimonJniScanner::PaimonJniScanner(const std::string& scan_param, + const TupleDescriptor* tuple_desc, const std::string& table_name) + : paimon_scan_param_(scan_param), tuple_desc_(tuple_desc), table_name_(table_name) {} + +Status PaimonJniScanner::InitJNI() { + DCHECK(paimon_jni_scanner_cl_ == nullptr) << "InitJNI() already called!"; + JNIEnv* env = JniUtil::GetJNIEnv(); + if (env == nullptr) return Status("Failed to get/create JVM"); + + // Global class references: + RETURN_IF_ERROR(JniUtil::GetGlobalClassRef( + env, "org/apache/impala/util/paimon/PaimonJniScanner", &paimon_jni_scanner_cl_)); + + // Method ids: + RETURN_IF_ERROR(JniUtil::GetMethodID( + env, paimon_jni_scanner_cl_, "", "([B)V", &paimon_jni_scanner_ctor_)); + RETURN_IF_ERROR(JniUtil::GetMethodID( + env, paimon_jni_scanner_cl_, "ScanTable", "()V", &paimon_jni_scanner_scan_table_)); + RETURN_IF_ERROR(JniUtil::GetMethodID(env, paimon_jni_scanner_cl_, "GetNextBatch", + "([J)J", &paimon_jni_scanner_get_next_)); + RETURN_IF_ERROR(JniUtil::GetMethodID( + env, paimon_jni_scanner_cl_, "close", "()V", &paimon_jni_scanner_close_)); + + return Status::OK(); +} + +Status PaimonJniScanner::Init(JNIEnv* env) { + jbyteArray jbytes_scan_param = env->NewByteArray(paimon_scan_param_.size()); + RETURN_ERROR_IF_EXC(env); + env->SetByteArrayRegion(jbytes_scan_param, 0, paimon_scan_param_.size(), + reinterpret_cast(paimon_scan_param_.data())); + RETURN_ERROR_IF_EXC(env); + jobject j_jni_scanner = + env->NewObject(paimon_jni_scanner_cl_, paimon_jni_scanner_ctor_, jbytes_scan_param); + RETURN_ERROR_IF_EXC(env); + RETURN_IF_ERROR(JniUtil::LocalToGlobalRef(env, j_jni_scanner, &j_jni_scanner_)); + return Status::OK(); +} + +Status PaimonJniScanner::ScanTable(JNIEnv* env) { + env->CallObjectMethod(j_jni_scanner_, paimon_jni_scanner_scan_table_); + RETURN_ERROR_IF_EXC(env); + return Status::OK(); +} + +Status PaimonJniScanner::GetNextBatchDirect(JNIEnv* env, struct ArrowArray** array, + struct ArrowSchema** schema, long* rows, long* offheap_used) { + /// Will pass a long array to java method, and the returned are two memory address. + /// 1st is the schema memory address, the second is the memory address of arrow + /// array vector. the two memory address are in the offheap region of JVM, + /// and the offheap usage in bytes. + jlongArray address_array = env->NewLongArray(3); + RETURN_ERROR_IF_EXC(env); + jlong result = + env->CallLongMethod(j_jni_scanner_, paimon_jni_scanner_get_next_, address_array); + RETURN_ERROR_IF_EXC(env); + jlong values[3]; + env->GetLongArrayRegion(address_array, 0, 3, &values[0]); + *schema = (struct ArrowSchema*)values[0]; + *array = (struct ArrowArray*)values[1]; + *offheap_used = values[2]; + *rows = result; + env->DeleteLocalRef(address_array); + return Status::OK(); +} + +void PaimonJniScanner::Close(RuntimeState* state) { + JNIEnv* env = JniUtil::GetJNIEnv(); + if (env != nullptr) { + if (j_jni_scanner_ != nullptr) { + /// Call close method to free resources of java PaimonJniScanner. + env->CallObjectMethod(j_jni_scanner_, paimon_jni_scanner_close_); + env->DeleteGlobalRef(j_jni_scanner_); + } + } +} + +string PaimonJniScanner::DebugString() { + std::stringstream out; + out << "PaimonJniScanner: [ Paimon table name: " << table_name_ << "; "; + out << tuple_desc_->DebugString() << "]"; + return out.str(); +} + +} // namespace impala diff --git a/be/src/exec/paimon/paimon-jni-scanner.h b/be/src/exec/paimon/paimon-jni-scanner.h new file mode 100644 index 000000000..6684a6c35 --- /dev/null +++ b/be/src/exec/paimon/paimon-jni-scanner.h @@ -0,0 +1,77 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +#pragma once + +#include "common/status.h" +#include "runtime/descriptors.h" + +#include +#include +#include + +namespace impala { + +class RuntimeState; + +/// Adapter class of the FE PaimonJniScanner, wraps the JNI calls as C++ methods. +class PaimonJniScanner { + public: + PaimonJniScanner(const std::string& scan_param, const TupleDescriptor* tuple_desc, + const std::string& table_name); + + /// JNI setup. Creates global references for Java classes and finds method ids. + /// Initializes static members, should be called once per process lifecycle. + static Status InitJNI() WARN_UNUSED_RESULT; + + // Initializes this object, creates the java metadata scanner object. + Status Init(JNIEnv* env) WARN_UNUSED_RESULT; + + /// Executes an Paimon scan through JNI. + Status ScanTable(JNIEnv* env) WARN_UNUSED_RESULT; + + /// Gets the next arrow batch from 'org.apache.impala.util.paimon.PaimonJniScanner'. + Status GetNextBatchDirect(JNIEnv* env, struct ArrowArray** array, + struct ArrowSchema** schema, long* rows, long* offheap_used) WARN_UNUSED_RESULT; + + /// Removes global references. + void Close(RuntimeState* state); + + private: + /// Global class references created with JniUtil. + inline static jclass paimon_jni_scanner_cl_ = nullptr; + + /// Method references created with JniUtil. + inline static jmethodID paimon_jni_scanner_ctor_ = nullptr; + inline static jmethodID paimon_jni_scanner_scan_table_ = nullptr; + inline static jmethodID paimon_jni_scanner_get_next_ = nullptr; + inline static jmethodID paimon_jni_scanner_close_ = nullptr; + + /// The Paimon table scan parameters. + const std::string& paimon_scan_param_; + /// Top level TupleDescriptor. + const TupleDescriptor* tuple_desc_; + /// metastore table name + const std::string& table_name_; + /// Paimon scanner Java object, it helps preparing the table and + /// executes an Paimon table scan. Allows the ScanNode to fetch the row batch from + /// the Java Off Heap. + jobject j_jni_scanner_; + + std::string DebugString(); +}; +} // namespace impala diff --git a/be/src/exec/paimon/paimon-scan-plan-node.cc b/be/src/exec/paimon/paimon-scan-plan-node.cc new file mode 100644 index 000000000..cb3738761 --- /dev/null +++ b/be/src/exec/paimon/paimon-scan-plan-node.cc @@ -0,0 +1,31 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +#include "common/status.h" +#include "exec/paimon/paimon-scan-plan-node.h" +#include "exec/paimon/paimon-jni-scan-node.h" +#include "runtime/exec-env.h" +#include "runtime/runtime-state.h" +#include "util/jni-util.h" + +using namespace impala; + +Status PaimonScanPlanNode::CreateExecNode(RuntimeState* state, ExecNode** node) const { + ObjectPool* pool = state->obj_pool(); + *node = pool->Add(new PaimonJniScanNode(pool, *this, state->desc_tbl())); + return Status::OK(); +} \ No newline at end of file diff --git a/be/src/exec/paimon/paimon-scan-plan-node.h b/be/src/exec/paimon/paimon-scan-plan-node.h new file mode 100644 index 000000000..28bccbeab --- /dev/null +++ b/be/src/exec/paimon/paimon-scan-plan-node.h @@ -0,0 +1,36 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +#pragma once + +#include +#include "exec/scan-node.h" + +namespace impala { + +class ExecNode; +class RuntimeState; +class ScanPlanNode; +class Status; + +class PaimonScanPlanNode : public ScanPlanNode { + public: + Status CreateExecNode(RuntimeState* state, ExecNode** node) const override; + ~PaimonScanPlanNode() {} +}; + +} // namespace impala diff --git a/be/src/runtime/descriptors.cc b/be/src/runtime/descriptors.cc index 0227cc88a..d7de726d1 100644 --- a/be/src/runtime/descriptors.cc +++ b/be/src/runtime/descriptors.cc @@ -368,6 +368,16 @@ string SystemTableDescriptor::DebugString() const { return out.str(); } +PaimonTableDescriptor::PaimonTableDescriptor(const TTableDescriptor& tdesc) + : TableDescriptor(tdesc), paimon_api_table_(tdesc.paimonTable) {} + +string PaimonTableDescriptor::DebugString() const { + stringstream out; + out << "PaimonTable(" << TableDescriptor::DebugString(); + out << ")"; + return out.str(); +} + TupleDescriptor::TupleDescriptor(const TTupleDescriptor& tdesc) : id_(tdesc.id), byte_size_(tdesc.byteSize), @@ -626,6 +636,9 @@ Status DescriptorTbl::CreateTblDescriptorInternal(const TTableDescriptor& tdesc, case TTableType::SYSTEM_TABLE: *desc = pool->Add(new SystemTableDescriptor(tdesc)); break; + case TTableType::PAIMON_TABLE: + *desc = pool->Add(new PaimonTableDescriptor(tdesc)); + break; default: DCHECK(false) << "invalid table type: " << tdesc.tableType; } diff --git a/be/src/runtime/descriptors.h b/be/src/runtime/descriptors.h index b0962b891..071c32a17 100644 --- a/be/src/runtime/descriptors.h +++ b/be/src/runtime/descriptors.h @@ -604,6 +604,16 @@ class SystemTableDescriptor : public TableDescriptor { TSystemTableName::type table_name_; }; +// Descriptor for a Paimon Table +class PaimonTableDescriptor : public TableDescriptor { + public: + PaimonTableDescriptor(const TTableDescriptor& tdesc); + virtual std::string DebugString() const; + + private: + impala::TPaimonTable paimon_api_table_; +}; + class TupleDescriptor { public: int byte_size() const { return byte_size_; } diff --git a/be/src/scheduling/scheduler.cc b/be/src/scheduling/scheduler.cc index 71d9cd796..8f59dd8ce 100644 --- a/be/src/scheduling/scheduler.cc +++ b/be/src/scheduling/scheduler.cc @@ -70,7 +70,7 @@ static const string SCHEDULER_WARNING_KEY("Scheduler Warning"); static const vector SCAN_NODE_TYPES{TPlanNodeType::HDFS_SCAN_NODE, TPlanNodeType::HBASE_SCAN_NODE, TPlanNodeType::DATA_SOURCE_NODE, TPlanNodeType::KUDU_SCAN_NODE, TPlanNodeType::ICEBERG_METADATA_SCAN_NODE, - TPlanNodeType::SYSTEM_TABLE_SCAN_NODE}; + TPlanNodeType::SYSTEM_TABLE_SCAN_NODE, TPlanNodeType::PAIMON_SCAN_NODE}; // Consistent scheduling requires picking up to k distinct candidates out of n nodes. // Since each iteration can pick a node that it already picked (i.e. it is sampling with diff --git a/be/src/service/impalad-main.cc b/be/src/service/impalad-main.cc index a37e25dae..1233ace4b 100644 --- a/be/src/service/impalad-main.cc +++ b/be/src/service/impalad-main.cc @@ -31,6 +31,7 @@ #include "exec/hbase/hbase-table-writer.h" #include "exec/iceberg-metadata/iceberg-metadata-scanner.h" #include "exec/iceberg-metadata/iceberg-row-reader.h" +#include "exec/paimon/paimon-jni-scanner.h" #include "exprs/hive-udf-call.h" #include "exprs/timezone_db.h" #include "gen-cpp/ImpalaService.h" @@ -68,6 +69,7 @@ int ImpaladMain(int argc, char** argv) { ABORT_IF_ERROR(HBaseTableWriter::InitJNI()); ABORT_IF_ERROR(IcebergMetadataScanner::InitJNI()); ABORT_IF_ERROR(IcebergRowReader::InitJNI()); + ABORT_IF_ERROR(PaimonJniScanner::InitJNI()); ABORT_IF_ERROR(HiveUdfCall::InitEnv()); ABORT_IF_ERROR(JniCatalogCacheUpdateIterator::InitJNI()); InitFeSupport(); diff --git a/bin/bootstrap_toolchain.py b/bin/bootstrap_toolchain.py index ecc0354f9..c58a17b6c 100755 --- a/bin/bootstrap_toolchain.py +++ b/bin/bootstrap_toolchain.py @@ -473,7 +473,7 @@ def get_toolchain_downloads(): gcc_package = ToolchainPackage("gcc") toolchain_packages += [llvm_package, llvm_package_asserts, gcc_package] toolchain_packages += [ToolchainPackage(p) for p in - ["avro", "binutils", "boost", "breakpad", "bzip2", "calloncehack", "cctz", + ["arrow", "avro", "binutils", "boost", "breakpad", "bzip2", "calloncehack", "cctz", "cloudflarezlib", "cmake", "crcutil", "curl", "flatbuffers", "gdb", "gflags", "glog", "gperftools", "jwt-cpp", "libev", "libunwind", "lz4", "mold", "openldap", "opentelemetry-cpp", "orc", "protobuf", "python", "rapidjson", "re2", diff --git a/bin/impala-config.sh b/bin/impala-config.sh index 848732b0d..ffe94fa7c 100755 --- a/bin/impala-config.sh +++ b/bin/impala-config.sh @@ -207,6 +207,8 @@ export IMPALA_TPC_H_VERSION=2.17.0 unset IMPALA_TPC_H_URL export IMPALA_ZLIB_VERSION=1.3.1 unset IMPALA_ZLIB_URL +export IMPALA_ARROW_VERSION=13.0.0 +unset IMPALA_ARROW_URL export IMPALA_CLOUDFLAREZLIB_VERSION=7aa510344e unset IMPALA_CLOUDFLAREZLIB_URL export IMPALA_CALLONCEHACK_VERSION=1.0.0 diff --git a/cmake_modules/FindArrow.cmake b/cmake_modules/FindArrow.cmake new file mode 100644 index 000000000..8b44f7b5c --- /dev/null +++ b/cmake_modules/FindArrow.cmake @@ -0,0 +1,54 @@ +############################################################################## +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +############################################################################## + +# - Find Arrow (headers and libarrow.a) with ARROW_ROOT hinting a location +# This module defines +# ARROW_INCLUDE_DIR, directory containing headers +# ARROW_STATIC_LIB, path to libarrow.a +# ARROW_FOUND +set(ARROW_ROOT $ENV{IMPALA_TOOLCHAIN_PACKAGES_HOME}/arrow-$ENV{IMPALA_ARROW_VERSION}) + +set(ARROW_SEARCH_HEADER_PATHS ${ARROW_ROOT}/include) + +set(ARROW_SEARCH_LIB_PATH ${ARROW_ROOT}/lib) + +find_path(ARROW_INCLUDE_DIR NAMES arrow/api.h arrow/c/bridge.h PATHS + ${ARROW_SEARCH_HEADER_PATHS} + # make sure we don't accidentally pick up a different version + NO_DEFAULT_PATH) + +find_library(ARROW_STATIC_LIB NAMES libarrow.a libarrow_bundled_dependencies.a PATHS + ${ARROW_SEARCH_LIB_PATH}) + +if(NOT ARROW_STATIC_LIB) + message(FATAL_ERROR "Arrow includes and libraries NOT found. " + "Looked for headers in ${ARROW_SEARCH_HEADER_PATHS}, " + "and for libs in ${ARROW_SEARCH_LIB_PATH}") + set(ARROW_FOUND FALSE) +else() + set(ARROW_FOUND TRUE) +endif () + +set(ARROW_FOUND ${ARROW_STATIC_LIB_FOUND}) + +mark_as_advanced( + ARROW_INCLUDE_DIR + ARROW_STATIC_LIB + ARROW_FOUND +) diff --git a/common/thrift/CatalogObjects.thrift b/common/thrift/CatalogObjects.thrift index 6f466a58f..7b0fd2f88 100644 --- a/common/thrift/CatalogObjects.thrift +++ b/common/thrift/CatalogObjects.thrift @@ -334,6 +334,11 @@ struct TColumn { // Key and value field id for Iceberg column with Map type. 22: optional i32 iceberg_field_map_key_id 23: optional i32 iceberg_field_map_value_id + // The followings are Paimon-specific column properties, + // will reuse the iceberg_field_id, is_key, is_nullable + // for Paimon table. + 26: optional bool is_paimon_column + } // Represents an HDFS file in a partition. diff --git a/common/thrift/PlanNodes.thrift b/common/thrift/PlanNodes.thrift index f8f44fcff..cba7d2131 100644 --- a/common/thrift/PlanNodes.thrift +++ b/common/thrift/PlanNodes.thrift @@ -57,6 +57,7 @@ enum TPlanNodeType { TUPLE_CACHE_NODE = 20 SYSTEM_TABLE_SCAN_NODE = 21 ICEBERG_MERGE_NODE = 22 + PAIMON_SCAN_NODE=23 } // phases of an execution node @@ -417,6 +418,28 @@ struct TSystemTableScanNode { 2: required CatalogObjects.TSystemTableName table_name } +struct TPaimonJniScanParam { + // Serialized paimon api table object. + 1: required binary paimon_table_obj + // Thrift serialized splits for the Jni Scanner. + 2: required list splits + // Field id list for projection. + 3: required list projection + // mem limit from backend. + // not set means no limit. + 4: optional i64 mem_limit_bytes + // arrow batch size + 5: optional i32 batch_size + // fragment id + 6: Types.TUniqueId fragment_id; +} + +struct TPaimonScanNode { + 1: required Types.TTupleId tuple_id + 2: required binary paimon_table_obj + 3: required string table_name; +} + struct TEqJoinCondition { // left-hand side of " = " 1: required Exprs.TExpr left; @@ -838,6 +861,7 @@ struct TPlanNode { 28: optional TTupleCacheNode tuple_cache_node 29: optional TSystemTableScanNode system_table_scan_node + 31: optional TPaimonScanNode paimon_table_scan_node } // A flattened representation of a tree of PlanNodes, obtained by depth-first diff --git a/common/thrift/Types.thrift b/common/thrift/Types.thrift index 4903dfac0..9d25ac818 100644 --- a/common/thrift/Types.thrift +++ b/common/thrift/Types.thrift @@ -77,8 +77,10 @@ struct TScalarType { struct TStructField { 1: required string name 2: optional string comment - // Valid for Iceberg tables + // Valid for Iceberg and Paimon tables. 3: optional i32 field_id + // Valid for paimon tables. + 4: optional bool is_nullable } struct TTypeNode { diff --git a/fe/pom.xml b/fe/pom.xml index 18cd27e4c..248ba8ce4 100644 --- a/fe/pom.xml +++ b/fe/pom.xml @@ -558,7 +558,126 @@ under the License. + + org.apache.paimon + paimon-arrow + ${paimon.version} + + + org.apache.arrow + arrow-vector + ${arrow.version} + + + log4j + log4j + + + org.apache.logging.log4j + log4j-1.2-api + + + org.apache.logging.log4j + log4j-slf4j-impl + + + org.apache.ant + ant + + + org.apache.ant + ant-launcher + + + flatbuffers-java + com.google.flatbuffers + + + + + org.apache.arrow + arrow-c-data + ${arrow.version} + + + log4j + log4j + + + org.apache.logging.log4j + log4j-1.2-api + + + org.apache.logging.log4j + log4j-slf4j-impl + + + org.apache.ant + ant + + + org.apache.ant + ant-launcher + + + + + + org.apache.arrow + arrow-memory-core + ${arrow.version} + + + log4j + log4j + + + org.apache.logging.log4j + log4j-1.2-api + + + org.apache.logging.log4j + log4j-slf4j-impl + + + org.apache.ant + ant + + + org.apache.ant + ant-launcher + + + + + + org.apache.arrow + arrow-memory-unsafe + ${arrow.version} + + + log4j + log4j + + + org.apache.logging.log4j + log4j-1.2-api + + + org.apache.logging.log4j + log4j-slf4j-impl + + + org.apache.ant + ant + + + org.apache.ant + ant-launcher + + + diff --git a/fe/src/main/java/org/apache/impala/catalog/Column.java b/fe/src/main/java/org/apache/impala/catalog/Column.java index 4251f14d9..ca85b6546 100644 --- a/fe/src/main/java/org/apache/impala/catalog/Column.java +++ b/fe/src/main/java/org/apache/impala/catalog/Column.java @@ -22,6 +22,8 @@ import java.util.List; import org.apache.hadoop.hive.metastore.api.ColumnStatisticsData; import org.apache.hadoop.hive.metastore.api.FieldSchema; +import org.apache.impala.catalog.paimon.PaimonColumn; +import org.apache.impala.catalog.paimon.PaimonStructField; import org.apache.impala.common.ImpalaRuntimeException; import org.apache.impala.thrift.TColumn; import org.apache.impala.thrift.TColumnDescriptor; @@ -102,6 +104,10 @@ public class Column { col = new IcebergColumn(columnDesc.getColumnName(), type, comment, position, columnDesc.getIceberg_field_id(), columnDesc.getIceberg_field_map_key_id(), columnDesc.getIceberg_field_map_value_id(), columnDesc.isIs_nullable()); + } else if (columnDesc.isIs_paimon_column()) { + Preconditions.checkState(columnDesc.isSetIceberg_field_id()); + col = new PaimonColumn(columnDesc.getColumnName(), type, comment, position, + columnDesc.getIceberg_field_id(), columnDesc.isIs_nullable()); } else if (columnDesc.isIs_hbase_column()) { // HBase table column. The HBase column qualifier (column name) is not be set for // the HBase row key, so it being set in the thrift struct is not a precondition. @@ -159,6 +165,10 @@ public class Column { IcebergColumn iCol = (IcebergColumn) col; fields.add(new IcebergStructField(iCol.getName(), iCol.getType(), iCol.getComment(), iCol.getFieldId())); + } else if (col instanceof PaimonColumn) { + PaimonColumn pCol = (PaimonColumn) col; + fields.add(new PaimonStructField(pCol.getName(), pCol.getType(), + pCol.getComment(), pCol.getFieldId(), pCol.isNullable())); } else { fields.add(new StructField(col.getName(), col.getType(), col.getComment())); } diff --git a/fe/src/main/java/org/apache/impala/catalog/Table.java b/fe/src/main/java/org/apache/impala/catalog/Table.java index 315a334f2..ef7808076 100644 --- a/fe/src/main/java/org/apache/impala/catalog/Table.java +++ b/fe/src/main/java/org/apache/impala/catalog/Table.java @@ -40,6 +40,8 @@ import org.apache.hadoop.hive.metastore.api.PrincipalType; import org.apache.impala.analysis.TableName; import org.apache.impala.catalog.events.InFlightEvents; import org.apache.impala.catalog.monitor.CatalogMonitor; +import org.apache.impala.catalog.paimon.PaimonColumn; +import org.apache.impala.catalog.paimon.PaimonStructField; import org.apache.impala.catalog.paimon.PaimonTable; import org.apache.impala.catalog.paimon.PaimonUtil; import org.apache.impala.common.ImpalaRuntimeException; @@ -667,6 +669,10 @@ public abstract class Table extends CatalogObjectImpl implements FeTable { IcebergColumn iCol = (IcebergColumn) col; return new IcebergStructField(iCol.getName(), iCol.getType(), iCol.getComment(), iCol.getFieldId()); + } else if (col instanceof PaimonColumn) { + PaimonColumn pCol = (PaimonColumn) col; + return new PaimonStructField(pCol.getName(), pCol.getType(), pCol.getComment(), + pCol.getFieldId(), pCol.isNullable()); } else { return new StructField(col.getName(), col.getType(), col.getComment()); } diff --git a/fe/src/main/java/org/apache/impala/catalog/Type.java b/fe/src/main/java/org/apache/impala/catalog/Type.java index 640daad15..3b9afe79a 100644 --- a/fe/src/main/java/org/apache/impala/catalog/Type.java +++ b/fe/src/main/java/org/apache/impala/catalog/Type.java @@ -25,6 +25,7 @@ import org.apache.impala.analysis.CreateTableStmt; import org.apache.impala.analysis.Parser; import org.apache.impala.analysis.StatementBase; import org.apache.impala.analysis.TypeDef; +import org.apache.impala.catalog.paimon.PaimonStructField; import org.apache.impala.common.AnalysisException; import org.apache.impala.common.Pair; import org.apache.impala.thrift.TColumnType; @@ -527,9 +528,16 @@ public abstract class Type { Pair res = fromThrift(col, nodeIdx); nodeIdx = res.second.intValue(); if (thriftField.isSetField_id()) { - // We create 'IcebergStructField' for Iceberg tables which have field id. - structFields.add(new IcebergStructField(name, res.first, comment, - thriftField.getField_id())); + if (!thriftField.isSetIs_nullable()) { + // We create 'IcebergStructField' for Iceberg tables which have field id. + // if nullable is not set. + structFields.add(new IcebergStructField( + name, res.first, comment, thriftField.getField_id())); + } else { + // nullable is set, it is a PaimonStructField + structFields.add(new PaimonStructField(name, res.first, comment, + thriftField.getField_id(), thriftField.isIs_nullable())); + } } else { structFields.add(new StructField(name, res.first, comment)); } diff --git a/fe/src/main/java/org/apache/impala/catalog/local/LocalPaimonTable.java b/fe/src/main/java/org/apache/impala/catalog/local/LocalPaimonTable.java index 55622cd6a..54ad744ed 100644 --- a/fe/src/main/java/org/apache/impala/catalog/local/LocalPaimonTable.java +++ b/fe/src/main/java/org/apache/impala/catalog/local/LocalPaimonTable.java @@ -19,7 +19,7 @@ package org.apache.impala.catalog.local; import com.google.common.base.Preconditions; -import org.apache.hadoop.hive.metastore.api.MetaException; +import org.apache.impala.catalog.Column; import org.apache.impala.catalog.TableLoadingException; import org.apache.impala.catalog.paimon.FePaimonTable; import org.apache.impala.catalog.paimon.PaimonUtil; @@ -29,6 +29,7 @@ import org.apache.log4j.Logger; import org.apache.paimon.table.Table; import java.io.IOException; +import java.util.List; import java.util.Set; /** @@ -45,18 +46,28 @@ public class LocalPaimonTable extends LocalTable implements FePaimonTable { Preconditions.checkNotNull(msTbl); Preconditions.checkNotNull(ref); try { - LocalPaimonTable localPaimonTable = new LocalPaimonTable(db, msTbl, ref); + Table table = PaimonUtil.createFileStoreTable(msTbl); + List paimonColumns = PaimonUtil.toImpalaColumn(table); + ColumnMap colMap = new ColumnMap(paimonColumns, + /*numClusteringCols=*/table.partitionKeys().size(), + db.getName() + "." + msTbl.getTableName(), + /*isFullAcidSchema=*/false); + LocalPaimonTable localPaimonTable = + new LocalPaimonTable(db, msTbl, ref, colMap, table); return localPaimonTable; - } catch (MetaException ex) { + } catch (Exception ex) { throw new TableLoadingException("Failed to load table" + msTbl.getTableName(), ex); } } protected LocalPaimonTable(LocalDb db, org.apache.hadoop.hive.metastore.api.Table msTbl, - MetaProvider.TableMetaRef ref) throws MetaException { - super(db, msTbl, ref); - table_ = PaimonUtil.createFileStoreTable(msTbl); + MetaProvider.TableMetaRef ref, ColumnMap columnMap, Table table) { + super(db, msTbl, ref, columnMap); + table_ = table; + /// TODO: add virtual column later if it is supported. + /// addVirtualColumns(ref.getVirtualColumns()); applyPaimonTableStatsIfPresent(); + applyPaimonColumnStatsIfPresent(); } @Override diff --git a/fe/src/main/java/org/apache/impala/catalog/local/LocalTable.java b/fe/src/main/java/org/apache/impala/catalog/local/LocalTable.java index 2f780d3f5..ea74b0ca4 100644 --- a/fe/src/main/java/org/apache/impala/catalog/local/LocalTable.java +++ b/fe/src/main/java/org/apache/impala/catalog/local/LocalTable.java @@ -49,6 +49,8 @@ import org.apache.impala.catalog.SystemTable; import org.apache.impala.catalog.TableLoadingException; import org.apache.impala.catalog.VirtualColumn; import org.apache.impala.catalog.local.MetaProvider.TableMetaRef; +import org.apache.impala.catalog.paimon.PaimonColumn; +import org.apache.impala.catalog.paimon.PaimonStructField; import org.apache.impala.catalog.paimon.PaimonUtil; import org.apache.impala.common.Pair; import org.apache.impala.common.RuntimeEnv; @@ -451,6 +453,10 @@ abstract class LocalTable implements FeTable { IcebergColumn iCol = (IcebergColumn) col; fields.add(new IcebergStructField(iCol.getName(), iCol.getType(), iCol.getComment(), iCol.getFieldId())); + } else if (col instanceof PaimonColumn) { + PaimonColumn pCol = (PaimonColumn) col; + fields.add(new PaimonStructField(pCol.getName(), pCol.getType(), + pCol.getComment(), pCol.getFieldId(), pCol.isNullable())); } else { fields.add(new StructField(col.getName(), col.getType(), col.getComment())); } diff --git a/fe/src/main/java/org/apache/impala/catalog/paimon/PaimonColumn.java b/fe/src/main/java/org/apache/impala/catalog/paimon/PaimonColumn.java new file mode 100644 index 000000000..2f2f9ca84 --- /dev/null +++ b/fe/src/main/java/org/apache/impala/catalog/paimon/PaimonColumn.java @@ -0,0 +1,66 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.impala.catalog.paimon; + +import org.apache.impala.catalog.Column; +import org.apache.impala.catalog.Type; +import org.apache.impala.thrift.TColumn; +import org.apache.impala.thrift.TColumnDescriptor; + +/** + * Represents a Paimon column. + * + * This class extends Column with the Paimon-specific field id. Field ids are used in + * schema evolution to uniquely identify columns.. + */ +public class PaimonColumn extends Column { + private final int fieldId_; + // False for required Paimon field, true for optional Paimon field + private final boolean isNullable_; + + public PaimonColumn(String name, Type type, String comment, int position, int fieldId, + boolean isNullable) { + super(name.toLowerCase(), type, comment, position); + fieldId_ = fieldId; + isNullable_ = isNullable; + } + + public PaimonColumn(String name, Type type, String comment, int position, int fieldId) { + this(name, type, comment, position, fieldId, true); + } + + public int getFieldId() { return fieldId_; } + + public boolean isNullable() { return isNullable_; } + + @Override + public TColumn toThrift() { + TColumn tcol = super.toThrift(); + tcol.setIs_paimon_column(true); + tcol.setIceberg_field_id(fieldId_); + tcol.setIs_nullable(isNullable_); + return tcol; + } + + @Override + public TColumnDescriptor toDescriptor() { + TColumnDescriptor desc = super.toDescriptor(); + desc.setIcebergFieldId(fieldId_); + return desc; + } +} diff --git a/fe/src/main/java/org/apache/impala/catalog/paimon/PaimonHiveTypeUtils.java b/fe/src/main/java/org/apache/impala/catalog/paimon/PaimonHiveTypeUtils.java index a35e8fa51..b62ceab29 100644 --- a/fe/src/main/java/org/apache/impala/catalog/paimon/PaimonHiveTypeUtils.java +++ b/fe/src/main/java/org/apache/impala/catalog/paimon/PaimonHiveTypeUtils.java @@ -63,11 +63,11 @@ import org.apache.paimon.types.VariantType; * Utils for paimon and hive Type conversions, the class is from * org.apache.paimon.hive.HiveTypeUtils, refactor to fix the * following incompatible conversion issue: - * paimon type LocalZonedTimestampType will convert to - * org.apache.hadoop.hive.serde2.typeinfo.TimestampLocalTZTypeInfo + * paimon type ${@link LocalZonedTimestampType} will convert to + * ${@link org.apache.hadoop.hive.serde2.typeinfo.TimestampLocalTZTypeInfo} * however, it is not supported in impala, TableLoadingException * will raise while loading the table in method: - * apache.impala.catalog.FeCatalogUtils#parseColumnType + * ${@link org.apache.impala.catalog.FeCatalogUtils#parseColumnType} * To fix the issue LocalZonedTimestampType will be converted to * hive timestamp type. */ @@ -206,31 +206,26 @@ public class PaimonHiveTypeUtils { } static DataType visit(TypeInfo type, HiveToPaimonTypeVisitor visitor) { - if (!(type instanceof StructTypeInfo)) { - if (type instanceof MapTypeInfo) { - MapTypeInfo mapTypeInfo = (MapTypeInfo)type; - return DataTypes.MAP(visit(mapTypeInfo.getMapKeyTypeInfo(), visitor), - visit(mapTypeInfo.getMapValueTypeInfo(), visitor)); - } else if (type instanceof ListTypeInfo) { - ListTypeInfo listTypeInfo = (ListTypeInfo)type; - return DataTypes.ARRAY( - visit(listTypeInfo.getListElementTypeInfo(), visitor)); - } else { - return visitor.atomic(type); - } - } else { + if (type instanceof StructTypeInfo) { StructTypeInfo structTypeInfo = (StructTypeInfo)type; ArrayList fieldNames = structTypeInfo.getAllStructFieldNames(); ArrayList typeInfos = structTypeInfo .getAllStructFieldTypeInfos(); RowType.Builder builder = RowType.builder(); - for(int i = 0; i < fieldNames.size(); ++i) { builder.field((String)fieldNames.get(i), visit((TypeInfo)typeInfos.get(i), visitor)); } - return builder.build(); + } else if (type instanceof MapTypeInfo) { + MapTypeInfo mapTypeInfo = (MapTypeInfo) type; + return DataTypes.MAP(visit(mapTypeInfo.getMapKeyTypeInfo(), visitor), + visit(mapTypeInfo.getMapValueTypeInfo(), visitor)); + } else if (type instanceof ListTypeInfo) { + ListTypeInfo listTypeInfo = (ListTypeInfo) type; + return DataTypes.ARRAY(visit(listTypeInfo.getListElementTypeInfo(), visitor)); + } else { + return visitor.atomic(type); } } diff --git a/fe/src/main/java/org/apache/impala/catalog/paimon/PaimonImpalaTypeUtils.java b/fe/src/main/java/org/apache/impala/catalog/paimon/PaimonImpalaTypeUtils.java index 9405168c0..c66b6a3a5 100644 --- a/fe/src/main/java/org/apache/impala/catalog/paimon/PaimonImpalaTypeUtils.java +++ b/fe/src/main/java/org/apache/impala/catalog/paimon/PaimonImpalaTypeUtils.java @@ -180,8 +180,9 @@ public class PaimonImpalaTypeUtils { rowType.getFields() .stream() .map(dataField - -> new StructField( - dataField.name().toLowerCase(), dataField.type().accept(this))) + -> new PaimonStructField(dataField.name().toLowerCase(), + dataField.type().accept(this), dataField.description(), + dataField.id(), dataField.type().isNullable())) .collect(Collectors.toList()); return new StructType(structFields); @@ -254,12 +255,12 @@ public class PaimonImpalaTypeUtils { public static boolean isSupportedPrimitiveType(PrimitiveType primitiveType) { Preconditions.checkNotNull(primitiveType); switch (primitiveType) { + case DOUBLE: + case FLOAT: case BIGINT: case INT: case SMALLINT: case TINYINT: - case DOUBLE: - case FLOAT: case BOOLEAN: case STRING: case TIMESTAMP: @@ -267,7 +268,6 @@ public class PaimonImpalaTypeUtils { case DATE: case BINARY: case CHAR: - case DATETIME: case VARCHAR: return true; default: return false; } diff --git a/fe/src/main/java/org/apache/impala/catalog/paimon/PaimonStructField.java b/fe/src/main/java/org/apache/impala/catalog/paimon/PaimonStructField.java new file mode 100644 index 000000000..87079fbf1 --- /dev/null +++ b/fe/src/main/java/org/apache/impala/catalog/paimon/PaimonStructField.java @@ -0,0 +1,87 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.impala.catalog.paimon; + +import java.util.Objects; + +import org.apache.impala.catalog.StructField; +import org.apache.impala.catalog.Type; +import org.apache.impala.thrift.TColumnType; +import org.apache.impala.thrift.TStructField; +import org.apache.impala.thrift.TTypeNode; + +/** + * Represents a Paimon StructField. + * + * This class extends StructField with Paimon-specific field. + * Paimon uses field IDs for schema evolution and compatibility, similar to Iceberg. + * We keep field id by this class, so we can use field id to resolve column on backend. + */ +public class PaimonStructField extends StructField { + private final int fieldId_; + // False for required Paimon field, true for optional Paimon field + private final boolean isNullable_; + + public PaimonStructField(String name, Type type, String comment, int fieldId) { + this(name, type, comment, fieldId, true); + } + + public PaimonStructField( + String name, Type type, String comment, int fieldId, boolean isNullable) { + super(name, type, comment); + fieldId_ = fieldId; + isNullable_ = isNullable; + } + + public int getFieldId() { return fieldId_; } + + public boolean isNullable() { return isNullable_; } + + @Override + public void toThrift(TColumnType container, TTypeNode node) { + TStructField field = new TStructField(); + field.setName(name_); + if (comment_ != null) field.setComment(comment_); + field.setField_id(fieldId_); + // Paimon-specific metadata - nullable and key properties could be added to + // extended metadata if the Thrift definition supports it + field.setIs_nullable(isNullable_); + node.struct_fields.add(field); + type_.toThrift(container); + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof PaimonStructField)) return false; + PaimonStructField otherStructField = (PaimonStructField) other; + return otherStructField.name_.equals(name_) && otherStructField.type_.equals(type_) + && otherStructField.fieldId_ == fieldId_ + && otherStructField.isNullable_ == isNullable_; + } + + @Override + public int hashCode() { + return Objects.hash(name_, type_, fieldId_, isNullable_); + } + + @Override + public String toString() { + return String.format("PaimonStructField{name=%s, type=%s, fieldId=%d, nullable=%s}", + name_, type_, fieldId_, isNullable_); + } +} \ No newline at end of file diff --git a/fe/src/main/java/org/apache/impala/catalog/paimon/PaimonTable.java b/fe/src/main/java/org/apache/impala/catalog/paimon/PaimonTable.java index f6ade36a5..45ea962de 100644 --- a/fe/src/main/java/org/apache/impala/catalog/paimon/PaimonTable.java +++ b/fe/src/main/java/org/apache/impala/catalog/paimon/PaimonTable.java @@ -28,6 +28,7 @@ import org.apache.hadoop.hive.metastore.IMetaStoreClient; import org.apache.hadoop.hive.metastore.api.FieldSchema; import org.apache.impala.catalog.Column; import org.apache.impala.catalog.Db; +import org.apache.impala.catalog.StructType; import org.apache.impala.catalog.Table; import org.apache.impala.catalog.TableLoadingException; import org.apache.impala.catalog.VirtualColumn; @@ -135,7 +136,8 @@ public class PaimonTable extends Table implements FePaimonTable { public void loadSchemaFromPaimon() throws TableLoadingException, ImpalaRuntimeException { loadSchema(); - addVirtualColumns(); + // TODO: add virtual column later if it is supported. + // addVirtualColumns(); } /** @@ -213,6 +215,17 @@ public class PaimonTable extends Table implements FePaimonTable { addVirtualColumn(VirtualColumn.BUCKET_ID); } + @Override + public void addColumn(Column col) { + Preconditions.checkState(col instanceof PaimonColumn); + PaimonColumn pCol = (PaimonColumn) col; + colsByPos_.add(pCol); + colsByName_.put(pCol.getName().toLowerCase(), col); + ((StructType) type_.getItemType()) + .addField(new PaimonStructField(col.getName(), col.getType(), col.getComment(), + pCol.getFieldId(), pCol.isNullable())); + } + /** * Loads the metadata of a Paimon table. *

!w_DymsEttQlB2pd9&5Fw(9RnaO! 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 LSJGnmZ>RqPhX?2q literal 0 HcmV?d00001 diff --git a/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_structs_paimon/bucket-0/data-7f917551-249f-4a13-9ec9-43be271f7b21-0.parquet b/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_structs_paimon/bucket-0/data-7f917551-249f-4a13-9ec9-43be271f7b21-0.parquet new file mode 100644 index 0000000000000000000000000000000000000000..b2d96f6b7a4897c729600e72dc30e65f8bccab2f GIT binary patch literal 4711 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 literal 0 HcmV?d00001 diff --git a/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_structs_paimon/manifest/manifest-85ea4264-6480-4517-9d44-2a5ba022a922-0 b/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_structs_paimon/manifest/manifest-85ea4264-6480-4517-9d44-2a5ba022a922-0 new file mode 100644 index 0000000000000000000000000000000000000000..d897e95275657052d901d53dde19acd40a16c7ff GIT binary patch literal 2454 zcmeZI%3@>@ODrqO*DFrWNX<>0AYH9gQdy9yWTjM;nw(#hqNJmgmzayeFD^(-1_|aD zrRyaE*%_&N1&Nut`FVO^!_rgpQi~ExQbF3&GE;L>ij}OQt6?U^hq(p?d;0qUC82g@ z=9MVb>L3(&d-}QHmJM(W3h@j-~-QA$ZoODxSP zQL+N*tc|TjPK;nj!2JU;5^gm1Tmnf6@ep6awGRED0L5IeYhb9WpEEEuhx$6X1|iZI zW-0_GG2&GRJA1hLI>vhnLR!|}ip(=`GfG;mWO86TfZAZ3n!Xpl3a z)PZXUg)lzNNO=R83w;p54l@BBX1IbMpXtE(bPe(Y7SjQaAs(21gBG9|CIGW(>TN@7t8!>b(^XSC<H#_%!K=zfCe1Z1U8sT+(s*QL~Vfc?LtLq|}F1 zs*bg%KbT}Vz`8@6G z^~1+2qFRx-RC-#gpcnI5{5_+^kr$qW9OFH?G(Ao%xizbj|jkr~hiIub%2^ zV#aS|q#zhJJvlEt_iNqc{FSHUB$V~veyh8v=XjIj0fVq-0|QsFZG7(T|8HyFuRT-p z`n8trHc=5dhIu|*Y%DLB4=-GNuFfzf`}X=B?+f$G*J_8Zc{9Cxncu>wE-l(87N$x5g}A;O@m!9$=!N1@5T<7meZ6(b27ohhCy6MGws)Dv_jJoP&y zRFJrB$9>UxTuvr)Qk6SbUu3rI{;lWMCZ%9@#@IMfabr%vvA)#au5+H;=?n(H=42G6 pAFx&X_WO>)lRS1iQ~#*&RiP>zo=F}l9f=yCw~G)+O3_cDGe1f5mUB$8!nQ_DJ7 zWE7Nv1|l}1(^P_vVwOSKT$XEGAR7|P{3Tp9(wT2uB5ws+xv1s0EE=Cd4Z1bA^2Ml+ p@OHNUp$6sA;_ruf#KObJLQ)=45$(L39K34m>FxRU{lP@>=_l+^9S8sb literal 0 HcmV?d00001 diff --git a/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_structs_paimon/manifest/manifest-list-67f31438-ac2d-4d03-9bd9-4e1d55120c5c-1 b/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_structs_paimon/manifest/manifest-list-67f31438-ac2d-4d03-9bd9-4e1d55120c5c-1 new file mode 100644 index 0000000000000000000000000000000000000000..b3b74c1a664b30131cada8f1ce0ad23422dd8e24 GIT binary patch literal 985 zcmeZI%3@>@ODrqO*DFrWNXij}OQt6?U^hq(p?d;0qUC82g@ z=9MVb>L3)jdHT4<`#Ji$B9s)D6lLb6W2y@Fj6zbClaHpxFVr{Q(Z$8pB_3pEFv2{n zDqUQCTtkRZ8{ile;u!+;LVR$DV+g`j0)ZDFkEsz$@c4TA#fLfigd#gPsj?)s7{&L# zjuAwt@(cCxiFfu7^+R%Hv{GJaPL2{VyrIFLSX7i)2@IOdlGI#KOhM(9z+n_y8>^#~ zlA4xSnp2`=1=3j?TZVXi*pSxbu6*jf>A+E30;Nlg~2DlSRPOGzwBVW@r;mGPos-MNc}FaCIKQVL*N z!lS5P+xKP(u9yrL%NlDY;ncLG3n4t>VR7ksgZlSdg>{Sm>Xv^$C0my_7zH zH=#%I;4^sg34&)26`YL_Yp-4;3p1Ny$cm6ds76nU z5XGPETmsrM2~Z@!nUJEHH|d88*MyYN)!;g3{FvAB=1G}aC8;K=losC^nBlic%t5{( z(TI&wY&2Gk2W`wEW15h-87p?IOcN{4vw@sHMNat3abhQ2bzZge7CCdaT$&p@bGCBM zyLJ}Z;WNfr-sB5LJt>?Gz0uVqxZdlHteapo?hN{t+E@0zvI?z9r|VmO%TX0u6_0$k nb!a<5+dZ<2e&w*(_Ps!rRp1mWzTfh8MN86! z>^XY$X7M0;DC8jMQ9KnX;=!Mb6)c`TNDI!}-RwTxBxGji`{sM|W+qv=amxV$MX=!b z`QvwI450W}0F4lP|Jo^8xJd{lgebX>Dj^6NP=aSiUx!+Txiw5;Seii26*XQ?-@)`Q;5q)g&)t}$$seStZ-kQ-NqNEr5PY%y#vXYVx zrv{7SAbyc@T@_b}4N%VFur{ij>xnDDV4_V|2Gd{_ z%Oc!h9(b`g%)wgh4k&bDx35G5OzkY3+ibME8|!zsTla!)1I7u^Nvc!dnH=>nkY?&N=wYM zym~sc)I-H&lxb8}ej}83RhT2zFlWF-B3%ikm)RP|>(am_7uC~6km9obK>-;1pu#+{ z2KXaL_1#ea4(njNo?Vn|1W(JXQ%BZEWPYTZ4UfOK*wswO1Q{A5VAn0wkH`Yz3B*@PgZLkULrAHI<@%&jr=at;IVLiP!;;m_TV zzmI0vx5sQG9=b4id)h$y8JLG}?_b@^XXwwy^odJP|Nf9i*0p_jX+)dwlpa0#`Af+} zB`HmxL@Hj|F{-IDKT5m+)mZFS#(8UMVhZpChDV8NYHACF>HkyH)Qn4@D@jgjV#ct} z6j;Tyh^8|Syx7{yK_fPIDRg6VC`AMe?JgeKY<7AZ>uZDdjiA?rNdk0+s?>L8Cb7E5 zf#@DzQ5obAm<@p<&=4qcU1&wC|hZ$uHY}HiiQ8S`nMy#+k(HkBfbHx*&$m0SO*#`+q#PoEK6Q$Wu zX^DBJS5KRkTBw+`GWCkeFSzoK4D-a?%jqzcNV}o*GHal}E;L*)Q9T_5!7u9X7l5(X zGAs~tmx}u3Q2#pXqQ9OUl&lAL%ZyW&CGP!|@4b_U1~G^DVW(xQBxM^)Shw*YrWUVT z)+=fsaP9ATtQ$L^jG792Ct<&>rOjr4Ydg4kw%0dq1^q#1W8H8{j$3jJtF_(f`i9>s xJ5|@QYks9Q=az$ZrRLgxbVD1ZBJG`>p0F&C>}HAMyofVFmxN{sC$?!7Ts) literal 0 HcmV?d00001 diff --git a/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_decimal_tbl/bucket-0/data-8def1f02-46fd-4114-8c89-cbae117a1b61-0.parquet b/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_decimal_tbl/bucket-0/data-8def1f02-46fd-4114-8c89-cbae117a1b61-0.parquet new file mode 100644 index 0000000000000000000000000000000000000000..d818d98ea93a964c68f31fe83b60af5f85226d7d GIT binary patch literal 1205 zcmZWp&ubG=5Po^PjoYB{SLU$~7R{wg1dZ8lVp9kOdumFMC^_kpl4KVQB>jhmo%coGFC+RMaorKT*Wg$IrGBGq;9VIZU~8PhHL1Sq z!5UMbhGh}1F$*luJBUHubB7cgr62<`LTJZO+J*86Rq%-;gf@^XARw|71kfVD zDA%~9i}~|{LgHLh17{g!i>#(ArSUMvZ$>iiXvSZ7d8`$0fFiHskYYy?WQlHRAp1(Q zuF^8IbgL8$E$~p$A7u~?D}NBm0~r>GE>=3}OPj8Ah1D@$3JpBuqI$3hp?FyTFb<4; zmSK_TL)_{>Kkw?_XB~`}qKguZ;AxR{in79I{}5*nrl3yrz7%d-!Eak%!oE!wG4Mp+ zqS3JSsn9;2!oIOn%BZfeZxYTsv%KEuZEQJ@ZghL;$4+mvy|$V*vWA&8(wXK~yJM&A zX5J{8MsD6NH0R8`(<;oHIlDNQYuOFQxMeu`T+z0hjceS}2nqklcie?4{$c$CGMV(| literal 0 HcmV?d00001 diff --git a/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_decimal_tbl/bucket-0/data-a100aae7-ed6e-4f04-8648-be059c9e2942-0.parquet b/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_decimal_tbl/bucket-0/data-a100aae7-ed6e-4f04-8648-be059c9e2942-0.parquet new file mode 100644 index 0000000000000000000000000000000000000000..ac3ea6e8d028987c9dd7d91d035a6180b9b6bc2b GIT binary patch literal 1205 zcmZWp&ui0g6o36DE!oiFr0+`tVdkIchHHv!yGmv_kdw9az{c)pw!;l$(>fWzrA&Hw{y$uw{RAaEi#$8*5WL6_cVg) z_?DDW41lEoC?X4hq7Tdw&(E<5Dx!fRL1aTn3!pMV8}u#^L^hGD5a4G?1dt_wGeuP# zU82z_6$uK8Uk#q%jIZ)dU8;w}2){WP!DzxKx;)iNHz1Mc1uXK<0+v8`R1{;Sr9f$& zJGxU(hL(7w7>_cErj_6L%KIX&fPR=$aW0m&1L+Okq)A!gM(DK6I%Qeuv%mXi@8_`z`iOpjN!u!M+eQNQZMukw=l3le zO=}1 wsRC?}B)Lr8}l3_iafo{2EGC(I%3-@p`t>}VyX1YcsN4(C76TZpWY8CLLW@%m8HFphw03x!<)~(Jzjf#-!JLl z$_%g(Ngy4V+?>&9fClDUXLS{$(WVjb{9Wm&aQ11}O3}0V#GQL5>)< z4sxJ0?I~55ZP?{-XrYIS!6?J1sQiH|_hgtO2H&4#AZ>Zli>!+AvM_MLMfGqIgt(~x zH35u$kzt+~eJbh~JpEPH!gx8lDA5R>7Fnk#OFa8GKf4!)DlxkFMulxF25sv~*tf|d zh92)*G%9KzbL~+a`^JtbqlUu1OE~XrWvkZN-gWO@Zg9TZ#zJs1SY)t z@OOAd2a1mhsDxPWu8v6K79o@nBIS-1N)Xba45O#dzsMQp)`WR8hY9#W_C>Ja*R#=! zVut-_!bW1h@nq`tb);X01^9jR>2W?oe>Zk3x6s4yBlYsolm@zuD<+Zrs~$-w%3CC=#GERHME#Q^f9G0HS-L z4cjJ1z-$DRfJQ(m1g4P}=C}xzzycW|v@v8vPz9kC{3-~cP35Wxgjp&AXc1tP>)bO{ z91X9kMB!mIaF$WF#MVr;9uFh_XT*v|6F=eQu~xhRio8^y#J)>VCZ?x@oG8skO3TbM zy?Q#d)I-H&lxb8{ejh6D%CJDpgPac2iL@I@udsED*M)%#E~=-CAjC!eLj@T7EW;8p z_o%2}iS+NXF2?KGMaf3+w9GnXS>oA0!?SnuuujaOB(|-Zv~4J1-zJNgdSTzPQBnIO z)IQE*-`ELd)KuA53FmDs_nU*Qo#4Th-oSVm47NL)8-`PM+_Ga>t({KSH~dz`skx3l s?^j!MZY5|}=Uv;c&Dm|g893LRpkmj2ztz0NJ)IEoM}FWgtl|snKjlfu2LJ#7 literal 0 HcmV?d00001 diff --git a/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_decimal_tbl/bucket-0/data-ddc2526d-a35f-4c0f-91e8-def352b6432e-0.parquet b/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_decimal_tbl/bucket-0/data-ddc2526d-a35f-4c0f-91e8-def352b6432e-0.parquet new file mode 100644 index 0000000000000000000000000000000000000000..254d834b2fafbf13c6db6da255275be58d112622 GIT binary patch literal 1205 zcmZWp&ubG=5Po^PiQ7_(O6IXo7THUeEojVk6PrRP*i$V%R4E>0sU+D214+}SR`4eA zXwQZI2OdQ9AQbV(!He|NgQy^Q62TwGQZFeuZ+El%=q4dEJKy)dnKv`Z%Jo|g5GaBL zkDd*`O&CD&u>cw&_N%KWWbq~;ln|oirpE|D$bb@jc>8+qOoq8RV)l?PUdX-#WjKER zUL4uB+lIu>s0i9M(p4b3Jh-7|+Coay|7W!nxC>aXk}~=vq>fp13h=Fb!6* zEW!=uffsxGIarI`T?(Dp9Vih2k9HQ%ZZz86-um6G*1e$HfN=tJit5yNrpB?m7l0U^ zu&4}b2+V{)5oib$1u%&`amGcc2sWq)p^YFjgenLvY-vX$}}n~zY)qiD$EmWKWD&XBJGCK%WMtfb!p&|i|XkjNO4*JumFr5sIWk+ zT`KEWLj60egYkNHQL+)tmRYAPOV0gM@ODrqO*DFrWNX<>0AYH9gQdy9yWTjM;nw(#hqNJmgmzayeFD^(-1_|aD zrRyaE*%_&N1&Nut`FVO^!_rgpQi~ExQbF3&GE;L>ij}OQt6?U^hq(p?d;0qUC82g@ z=9MVb>L3(&d-}QHmJM(W3h@j-~-QA$ZoODxSP zQL+N*tc|TjPK;nj!2JU;5^gm1Tmnf6@ep6awGRED0L5IeYhb9WpEEEuhx$6X1|iZI zW-0_GG2&GRJA1hLI>vhnLR!|}ip(=`GfG;mWO86TfZAZ3n!Xpl3a z)PZXUg)lzNNO=R83w;p54l@BBX1IbMpXtE(bPe(Y7SjQaAs(21gBG9|CIGW(>TN@7t8L*Oiz-IG5?=)Udl7m4L4RTNmjrLk52FN24z2*b2R<<1P7 zm+*>x*!r{b6x)FfpII4vS-!<@-`DU;F7&C}rLQK+^VHwHi#xbQg?ZIQ#odnD=AM%l z9a_v>uzRnfNv*tg&&iY1m{J*188R7^eViujxU3X>NHV}s!PZ<=aC2tMlV%rgF1;Bu zcbXjbIkV@`8RfLHG%-Wt?gHlrN6zp(XyO%4V>A5R&boP0aL-eQ#g6+-KIG+_aYk=0 zy0lNJ_wU-;>%aHSy-p1veaY&%pKsjz zw>y&Sc4cAY;F7&em)ltIyNkFR%YRDncU7|qjZ{!TVd*49)mmQeA>`LFh5d0Uld&lNE?PAkiiXwoBT_Dr8Y$L zv*}Nq{QK^TH7}QW^1T&|Pt%_wr4m=NZWfCu`*t^Lo|kHuI~_`ye$0+lin_6o9X*Kx E0Jr9Ip#T5? literal 0 HcmV?d00001 diff --git a/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_decimal_tbl/manifest/manifest-8c8eef70-e1cc-4019-bd2b-1df0c773321a-0 b/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_decimal_tbl/manifest/manifest-8c8eef70-e1cc-4019-bd2b-1df0c773321a-0 new file mode 100644 index 0000000000000000000000000000000000000000..1f4cbd2582bdf6f593a56b6f11813a0820c96d63 GIT binary patch literal 2147 zcmeZI%3@>@ODrqO*DFrWNX<>0AYH9gQdy9yWTjM;nw(#hqNJmgmzayeFD^(-1_|aD zrRyaE*%_&N1&Nut`FVO^!_rgpQi~ExQbF3&GE;L>ij}OQt6?U^hq(p?d;0qUC82g@ z=9MVb>L3(&d-}QHmJM(W3h@j-~-QA$ZoODxSP zQL+N*tc|TjPK;nj!2JU;5^gm1Tmnf6@ep6awGRED0L5IeYhb9WpEEEuhx$6X1|iZI zW-0_GG2&GRJA1hLI>vhnLR!|}ip(=`GfG;mWO86TfZAZ3n!Xpl3a z)PZXUg)lzNNO=R83w;p54l@BBX1IbMpXtE(bPe(Y7SjQaAs(21gBG9|CIGW(>TN@7t8!;Q?4O_7Yz=F5J~QFwIV`E0g%tQuSO|1x-(axt(4+FLk0 zRg--nr;u>*xZTAkDclDcMCORj(5PbJ__{0q+r5^#R-Q8J_WZuMTkuxy?54?rOn%S--ZIK_FOI zY2of`Wme2;5%!vjcUF~1?0>GbyKTbkJo&nHw=Z@~w2IKPi*r*6P-|rg;qG|h_;e$K zu&X+wn!<#`k4i-LlpH+NDyng0`cYPH?wwAW3FRu!zD;8H3<(p-xK;G^#kw=xjseXM g4li8hFAMd(GeJRZwl~iV{$^gY2IR%z3bX2Ldl^l5w~Uqd|G zjJj4kbnceq`~CU8-9BD|pGBgj{*=Y^<75ToV#YuR2<9gu0Byi&T)U?;>Z`{@ zOf;IGr%apCFiyC(7Mn261eHwD$03V?6m*8GFC{WSecR|+d9m;cxdd6e1Y8>!5eu5A zgrri0d{Rf*#zO@aB|k8hYZ9pINGD3uF7>t|b%c?#1NIK~9mB~-AhrXYQuUAU2&ogd z#A6y389?k-+Z`<=OTSHT!uuUimWwgz_FSvd=HW?oiZmfgKUCM1UbOSo#JE4O=#oDFE>qLyE?sC))B=-%Ai7o!2f p``P}7>Sd?vzwajj^IOYQQXWtd99+J=KH9Cu`%Cluh#faCJ^|gy8eaea literal 0 HcmV?d00001 diff --git a/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_decimal_tbl/manifest/manifest-list-b15e96d1-cea5-44c2-a5b4-dc8c7041463f-1 b/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_decimal_tbl/manifest/manifest-list-b15e96d1-cea5-44c2-a5b4-dc8c7041463f-1 new file mode 100644 index 0000000000000000000000000000000000000000..8a2ba77bbf0c4ee6929378da4e4c7371a1066758 GIT binary patch literal 989 zcmeZI%3@>@ODrqO*DFrWNXij}OQt6?U^hq(p?d;0qUC82g@ z=9MVb>L3)jdHT4<`#Ji$B9s)D6lLb6W2y@Fj6zbClaHpxFVr{Q(Z$8pB_3pEFv2{n zDqUQCTtkRZ8{ile;u!+;LVR$DV+g`j0)ZDFkEsz$@c4TA#fLfigd#gPsj?)s7{&L# zjuAwt@(cCxiFfu7^+R%Hv{GJaPL2{VyrIFLSX7i)2@IOdlGI#KOhM(9z+n_y8>^#~ zlA4xSnp2`=1=3j?TZVXi*pSxbu6*jf>A+E30;Nlg~2DlSRPOGzwBVYskq+q70`sr4DVr#1IH%Mf8& z!>F-U|1X0_E)#}o hvbnjjv5{e-uE8-yHUHA^;K2I}-o^ literal 0 HcmV?d00001 diff --git a/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_decimal_tbl/manifest/manifest-list-b9930407-daa0-43cc-b4fa-107aef9a5ffc-0 b/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_decimal_tbl/manifest/manifest-list-b9930407-daa0-43cc-b4fa-107aef9a5ffc-0 new file mode 100644 index 0000000000000000000000000000000000000000..98c37faef18567f35d819b59b2d078628519069d GIT binary patch literal 989 zcmbVKO^?z*7%qf(+JiB~cy&&Dpa_cz$gN9fL$htsZA**298duv4z~937?bQUVxuZf*O#<9#$FJG)w$S+hLmAWGrH?h--$tcFk332KD;oL zo}OMVyu6Tuq_PH_DJn#|q`@^L=FAI6+bBbMlSBE@21d6uB*`+AQKo6E^>!j`UpwRLrylWSEkPxDXf37{{H2$l4O?tftO1D%Mb$XVbd*uLx z6W<4E-i!D#V|bcI<8c~fom$+3c}BxzSOE@o@x&<%=$Tq?Z{aOk--=3Ay literal 0 HcmV?d00001 diff --git a/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_decimal_tbl/manifest/manifest-list-b9930407-daa0-43cc-b4fa-107aef9a5ffc-1 b/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_decimal_tbl/manifest/manifest-list-b9930407-daa0-43cc-b4fa-107aef9a5ffc-1 new file mode 100644 index 0000000000000000000000000000000000000000..013f03a3cc8a940461f9e7126185b34322f1bd75 GIT binary patch literal 989 zcmbVKJx|*}7&cMbq3VQC71kT74zU}P2+}ObjjzJO9|+q~)yO@a?Q<~lM|3_arN}b_ zV}C-|u3aleegHfCif)y#qmZ}+wE^7?_wjt(`@Cnk$7}Uh_zO*i%sV16{{8Y4u*sN! z9-zcasSlbU!ST{Q8)L6LOzB9##rlW{V;sh5B22|bBq0N%Xc=WHpDL&*y@%0l4N0;LWmIaK3%v)CGE||p z0`@>Uv=pl-0hyL$EmZ##9zx5+2jbBb9StPau0GeDFp~XMy(WEJ0j1ljiu#7FS6(>; z$w?G}BJWxJm{L3`q6ry^%TCSi!89W$cbdQ_0X~Ts=#|Xe$*avW`+HE6kRtyh+_I$i zc7GpQbI{zSZ2po(f{^Kzwf0!@z%d)3@1LOKDYVp`?Is| zuU$_#uNhb7b?wL6o1bt0aC>o$lk4@*F;2pOWQ-TdcE|O%+I;sNZu6ZMcKL1~io7d| ho$Vdc?z95Jw=Q45c;4U|T#YN{-MF}Ea~t=KZUOV@HrD_E literal 0 HcmV?d00001 diff --git a/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_decimal_tbl/schema/schema-0 b/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_decimal_tbl/schema/schema-0 new file mode 100644 index 000000000..cab2ba164 --- /dev/null +++ b/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_decimal_tbl/schema/schema-0 @@ -0,0 +1,34 @@ +{ + "version" : 3, + "id" : 0, + "fields" : [ { + "id" : 0, + "name" : "d1", + "type" : "DECIMAL(9, 0)" + }, { + "id" : 1, + "name" : "d2", + "type" : "DECIMAL(10, 0)" + }, { + "id" : 2, + "name" : "d3", + "type" : "DECIMAL(20, 10)" + }, { + "id" : 3, + "name" : "d4", + "type" : "DECIMAL(38, 38)" + }, { + "id" : 4, + "name" : "d5", + "type" : "DECIMAL(10, 5)" + } ], + "highestFieldId" : 4, + "partitionKeys" : [ ], + "primaryKeys" : [ ], + "options" : { + "owner" : "jichen", + "path" : "hdfs://localhost:20500/test-warehouse/paimon_test/paimon_catalog/warehouse/functional.db/paimon_decimal_tbl", + "external" : "true" + }, + "timeMillis" : 1757003848556 +} \ No newline at end of file diff --git a/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_decimal_tbl/snapshot/EARLIEST b/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_decimal_tbl/snapshot/EARLIEST new file mode 100644 index 000000000..56a6051ca --- /dev/null +++ b/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_decimal_tbl/snapshot/EARLIEST @@ -0,0 +1 @@ +1 \ No newline at end of file diff --git a/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_decimal_tbl/snapshot/LATEST b/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_decimal_tbl/snapshot/LATEST new file mode 100644 index 000000000..d8263ee98 --- /dev/null +++ b/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_decimal_tbl/snapshot/LATEST @@ -0,0 +1 @@ +2 \ No newline at end of file diff --git a/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_decimal_tbl/snapshot/snapshot-1 b/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_decimal_tbl/snapshot/snapshot-1 new file mode 100644 index 000000000..76180f207 --- /dev/null +++ b/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_decimal_tbl/snapshot/snapshot-1 @@ -0,0 +1,18 @@ +{ + "version" : 3, + "id" : 1, + "schemaId" : 0, + "baseManifestList" : "manifest-list-b15e96d1-cea5-44c2-a5b4-dc8c7041463f-0", + "baseManifestListSize" : 884, + "deltaManifestList" : "manifest-list-b15e96d1-cea5-44c2-a5b4-dc8c7041463f-1", + "deltaManifestListSize" : 989, + "changelogManifestList" : null, + "commitUser" : "0144f4f7-db7f-412f-91ad-82ef14642b76", + "commitIdentifier" : 9223372036854775807, + "commitKind" : "APPEND", + "timeMillis" : 1757003851433, + "logOffsets" : { }, + "totalRecordCount" : 3, + "deltaRecordCount" : 3, + "changelogRecordCount" : 0 +} \ No newline at end of file diff --git a/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_decimal_tbl/snapshot/snapshot-2 b/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_decimal_tbl/snapshot/snapshot-2 new file mode 100644 index 000000000..dd3d042cd --- /dev/null +++ b/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_decimal_tbl/snapshot/snapshot-2 @@ -0,0 +1,18 @@ +{ + "version" : 3, + "id" : 2, + "schemaId" : 0, + "baseManifestList" : "manifest-list-b9930407-daa0-43cc-b4fa-107aef9a5ffc-0", + "baseManifestListSize" : 989, + "deltaManifestList" : "manifest-list-b9930407-daa0-43cc-b4fa-107aef9a5ffc-1", + "deltaManifestListSize" : 989, + "changelogManifestList" : null, + "commitUser" : "3836dff6-6464-496a-914e-dcdd8d34b26f", + "commitIdentifier" : 9223372036854775807, + "commitKind" : "OVERWRITE", + "timeMillis" : 1757042364907, + "logOffsets" : { }, + "totalRecordCount" : 5, + "deltaRecordCount" : 2, + "changelogRecordCount" : 0 +} \ No newline at end of file diff --git a/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_primitive_alltypes/bucket-0/data-3728bff4-abe9-4ab9-8837-081b21a80e70-0.parquet b/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_primitive_alltypes/bucket-0/data-3728bff4-abe9-4ab9-8837-081b21a80e70-0.parquet new file mode 100644 index 0000000000000000000000000000000000000000..c51ab3bd094f34d619a90427961cf5b33a9c5f35 GIT binary patch literal 2927 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 literal 0 HcmV?d00001 diff --git a/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_primitive_alltypes/bucket-0/data-a73a0979-5cb2-4876-b7eb-1a8d8dcd90f7-0.parquet b/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_primitive_alltypes/bucket-0/data-a73a0979-5cb2-4876-b7eb-1a8d8dcd90f7-0.parquet new file mode 100644 index 0000000000000000000000000000000000000000..bfc4c15496d2953791b36bb734bdafcf6e2ae812 GIT binary patch literal 2926 zcmb7GU1(fI6rR1=Zf-U=e|2ZrE0?f{xkk{KHQD@>MXC={N+~t8c`*>~=B{R8v)gPo zL4Ar43`mhg3W{2YQB*=6@}O8+DENa9qOuiRisT{YLHZB{#X^L*c+Tw1oe7spT#~!z zo^$5=&YW}R%$|FCEKLb*rz!gJTbI9Vcc=lcF=`Uh``Hu!kW-_C7=)0f2myV9x^!oE z?^aXD?K$a&&i~uzUV4HqFV`;{A@BEP%yDp@n)WJBrtkyt zJWPk^^_!nxITl*8D%U`D$bwhl%V32?yXm`o-|V-BR;0xWnUU4I`^k_~kdTjtdFn7B zH&<@`7Eax)3}EM__h@M{im>SY!C$vukA!fQ_uHf2R>9luGVEl8;6F8NMjDDJwv3(R zUq?`5H-I;93I$@y(%Z>E)WNXUu!@&lTO|t%tE8x{GG!&fEJe*6S7o!4+Kc95$|?bZ z6s0+0Ct)Kz6NbnH7678;bxg*o7L}7NANzX!_iS4j)dR|BykwC0R z=$O#f3_2mi&8EO%F&b#Mm3Z|Soff`pG3pC(shOUUV9f#Ux)~tUg$U6dbew9t=oMzl zrOCOe>HLgG9cZQ=BN@+?%f-nBuQ;E-0NrSys#++$+)(v&XAEca0kaGC#BHr}GhR{n z_oOmWFm?vUmO`m2pkjnZ0jececu{CfLQT-LzECWC(ko7A9CVgnp3fEY!qI9hZS(U} z1t1fF#Ay`9VY@Sd-x!UBkzz*(2a@S@9BF71#Sd)9mCec(N?t{5@IpfD+4rg8) zPW7h8xsQ-{IAje5c~r!ydWl1xW)p7B$g1(9B!CWvlG5rFZ*&61YpwJt;%>s_YRXh- z%^9R(vkBtHLDqO;-0DpS{SZOFZpBnAMjaX#poh9U2z@aMq01SlBn&;Q)mg3~%bz^- zZD5hvIec6Knr)p!6m8r0-b_% zl1>g1utUZU9UQ83>fq3+NQVv%jv0a+&LS0j@A~pya+9-44)V|Yzu&#D{~cL>@YsL= zYH$v|3r>HeFqFto1*!ndf4KV#tQr7F0Dw67-|Ju634J=I&jNBmNk!Zk!{ zS;uIDN4*#i0}&oeymdFu!*dbt%e-|U!lMLl?I=7v6Jd9PxAs{`Hu&=TYI?`=Dz)lX zqrB}_EQrbDmXl_acCF@bwJpEpZUS-?w5jP;UtBfi=1d76-cYB>n!;SFQL%j1KbJLS zlAPknNw3;uw^9I<$*m@B?-^@R0aQs#>ZxC|hP{#iCP|y-)LORhvX0snO*?McBZIMt zB%us5@EU$Xzv*l=I@9vDRc9T5Ifd~sk#1Y{vD}QvX|`9j>TH3V8MbD}YO)FBG!tQ; z-ifY2Yc{t0=1Vq6#+=1_)A%;Yvb;+2;qK)!=1sge!*WQ{u^!}A41sQ{K+#7x8fr*F z_t08iy}w8bpY$Og@(M&3DVRa;RX7Lo8{{=Pl;S+N+=$2lm7hb|iwQbHnDwG||H5LuGR9JQ#+q$I(`3?8Pj3@vA literal 0 HcmV?d00001 diff --git a/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_primitive_alltypes/bucket-0/data-b9ea2c17-a695-4a31-98da-ac1da8ca242f-0.parquet b/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_primitive_alltypes/bucket-0/data-b9ea2c17-a695-4a31-98da-ac1da8ca242f-0.parquet new file mode 100644 index 0000000000000000000000000000000000000000..314f6e0a16d0d44795d9538975bcf7f730258271 GIT binary patch literal 2927 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 literal 0 HcmV?d00001 diff --git a/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_primitive_alltypes/manifest/manifest-list-5db2c8c6-e253-4fba-b0c2-71dc19d66678-1 b/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_primitive_alltypes/manifest/manifest-list-5db2c8c6-e253-4fba-b0c2-71dc19d66678-1 new file mode 100644 index 0000000000000000000000000000000000000000..1845eecc1ad48d9e6cde2a8eaba9b13ebc4747a9 GIT binary patch literal 985 zcmeZI%3@>@ODrqO*DFrWNXij}OQt6?U^hq(p?d;0qUC82g@ z=9MVb>L3)jdHT4<`#Ji$B9s)D6lLb6W2y@Fj6zbClaHpxFVr{Q(Z$8pB_3pEFv2{n zDqUQCTtkRZ8{ile;u!+;LVR$DV+g`j0)ZDFkEsz$@c4TA#fLfigd#gPsj?)s7{&L# zjuAwt@(cCxiFfu7^+R%Hv{GJaPL2{VyrIFLSX7i)2@IOdlGI#KOhM(9z+n_y8>^#~ zlA4xSnp2`=1=3j?TZVXi*pSxbu6*jf>A+E30;Nlg~2DlSRPOGzwBVc2kNvfINWuQ`?`9P{Cb^pIs* z!lR+!H0|V@M<1&k6xsLw_qW)X8pQ;i qha0?B%zJdpapty}8QPl3FGPahODNn-Rd}V*#KdsmfiZ&!x+MTht4gZ? literal 0 HcmV?d00001 diff --git a/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_primitive_alltypes/manifest/manifest-list-c6343add-3b4b-4a00-a470-de159bd83b3d-0 b/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_primitive_alltypes/manifest/manifest-list-c6343add-3b4b-4a00-a470-de159bd83b3d-0 new file mode 100644 index 0000000000000000000000000000000000000000..6bb5ca5c748e19b4c6241cebc12926cbed65146e GIT binary patch literal 985 zcmeZI%3@>@ODrqO*DFrWNXij}OQt6?U^hq(p?d;0qUC82g@ z=9MVb>L3)jdHT4<`#Ji$B9s)D6lLb6W2y@Fj6zbClaHpxFVr{Q(Z$8pB_3pEFv2{n zDqUQCTtkRZ8{ile;u!+;LVR$DV+g`j0)ZDFkEsz$@c4TA#fLfigd#gPsj?)s7{&L# zjuAwt@(cCxiFfu7^+R%Hv{GJaPL2{VyrIFLSX7i)2@IOdlGI#KOhM(9z+n_y8>^#~ zlA4xSnp2`=1=3j?TZVXi*pSxbu6*jf>A+E30;Nlg~2DlSRPOGzwBVKC6$8~0k4S$Td*-S^C>pUg~4 z7&W%)|7Gy-WnyqT!Y{eORF>zk16wQmw!Pb$iywWTrk#BA=wp?GBKzL|{uUckqnM!c qaD&&1d5>;6&fGRLLt8WXg-Gyw35A=f3a>Ppm>3Q`FlG=zw*&yk<4FAg literal 0 HcmV?d00001 diff --git a/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_primitive_alltypes/manifest/manifest-list-c6343add-3b4b-4a00-a470-de159bd83b3d-1 b/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_primitive_alltypes/manifest/manifest-list-c6343add-3b4b-4a00-a470-de159bd83b3d-1 new file mode 100644 index 0000000000000000000000000000000000000000..e3031178f3161430cbaf99686bdbf4fe03ab5589 GIT binary patch literal 989 zcmbVKJxjwt7>=c*E+V4qf#A?oV{OrHu{milNyQ{ZlzLo~OO52CTu!lS2LFh^z`;du zaPX)21DthHFe%kWD}He|+{g2A@AF=xeK3}v#z!|)msgJNH-LjpoEBPrc+iDyTkx_kPjgqm@2NRDgrN*mn-}-7BIhBS|q$v5(Hs|l*?^< Ym0!G?8|P+{k>is2Htz0A+|*~ISAecL6951J literal 0 HcmV?d00001 diff --git a/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_primitive_alltypes/schema/schema-0 b/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_primitive_alltypes/schema/schema-0 new file mode 100644 index 000000000..024ab5ebb --- /dev/null +++ b/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_primitive_alltypes/schema/schema-0 @@ -0,0 +1,66 @@ +{ + "version" : 3, + "id" : 0, + "fields" : [ { + "id" : 0, + "name" : "bool_value", + "type" : "BOOLEAN" + }, { + "id" : 1, + "name" : "tiny_value", + "type" : "TINYINT" + }, { + "id" : 2, + "name" : "small_value", + "type" : "SMALLINT" + }, { + "id" : 3, + "name" : "int_value", + "type" : "INT" + }, { + "id" : 4, + "name" : "big_value", + "type" : "BIGINT" + }, { + "id" : 5, + "name" : "float_value", + "type" : "FLOAT" + }, { + "id" : 6, + "name" : "double_value", + "type" : "DOUBLE" + }, { + "id" : 7, + "name" : "decimal_value", + "type" : "DECIMAL(10, 2)" + }, { + "id" : 8, + "name" : "char_value", + "type" : "CHAR(10)" + }, { + "id" : 9, + "name" : "varchar_value", + "type" : "VARCHAR(100)" + }, { + "id" : 10, + "name" : "binary_value", + "type" : "BINARY(100)" + }, { + "id" : 11, + "name" : "date_value", + "type" : "DATE" + }, { + "id" : 12, + "name" : "ts_ltz_value", + "type" : "TIMESTAMP(6) WITH LOCAL TIME ZONE" + }, { + "id" : 13, + "name" : "ts_value", + "type" : "TIMESTAMP(6)" + } ], + "highestFieldId" : 13, + "partitionKeys" : [ ], + "primaryKeys" : [ ], + "options" : { }, + "timeMillis" : 1754907467147 +} \ No newline at end of file diff --git a/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_primitive_alltypes/snapshot/EARLIEST b/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_primitive_alltypes/snapshot/EARLIEST new file mode 100644 index 000000000..56a6051ca --- /dev/null +++ b/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_primitive_alltypes/snapshot/EARLIEST @@ -0,0 +1 @@ +1 \ No newline at end of file diff --git a/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_primitive_alltypes/snapshot/LATEST b/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_primitive_alltypes/snapshot/LATEST new file mode 100644 index 000000000..d8263ee98 --- /dev/null +++ b/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_primitive_alltypes/snapshot/LATEST @@ -0,0 +1 @@ +2 \ No newline at end of file diff --git a/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_primitive_alltypes/snapshot/snapshot-1 b/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_primitive_alltypes/snapshot/snapshot-1 new file mode 100644 index 000000000..ca6afedf2 --- /dev/null +++ b/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_primitive_alltypes/snapshot/snapshot-1 @@ -0,0 +1,18 @@ +{ + "version" : 3, + "id" : 1, + "schemaId" : 0, + "baseManifestList" : "manifest-list-5db2c8c6-e253-4fba-b0c2-71dc19d66678-0", + "baseManifestListSize" : 884, + "deltaManifestList" : "manifest-list-5db2c8c6-e253-4fba-b0c2-71dc19d66678-1", + "deltaManifestListSize" : 985, + "changelogManifestList" : null, + "commitUser" : "77fe8f4e-7a57-4193-b07c-b0e961e36e14", + "commitIdentifier" : 9223372036854775807, + "commitKind" : "APPEND", + "timeMillis" : 1754907470727, + "logOffsets" : { }, + "totalRecordCount" : 1, + "deltaRecordCount" : 1, + "changelogRecordCount" : 0 +} \ No newline at end of file diff --git a/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_primitive_alltypes/snapshot/snapshot-2 b/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_primitive_alltypes/snapshot/snapshot-2 new file mode 100644 index 000000000..cc3729393 --- /dev/null +++ b/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_primitive_alltypes/snapshot/snapshot-2 @@ -0,0 +1,18 @@ +{ + "version" : 3, + "id" : 2, + "schemaId" : 0, + "baseManifestList" : "manifest-list-c6343add-3b4b-4a00-a470-de159bd83b3d-0", + "baseManifestListSize" : 985, + "deltaManifestList" : "manifest-list-c6343add-3b4b-4a00-a470-de159bd83b3d-1", + "deltaManifestListSize" : 989, + "changelogManifestList" : null, + "commitUser" : "97054845-d99a-46b8-8721-9ec01ea673df", + "commitIdentifier" : 9223372036854775807, + "commitKind" : "APPEND", + "timeMillis" : 1757001982034, + "logOffsets" : { }, + "totalRecordCount" : 7, + "deltaRecordCount" : 6, + "changelogRecordCount" : 0 +} \ No newline at end of file diff --git a/testdata/datasets/functional/functional_schema_template.sql b/testdata/datasets/functional/functional_schema_template.sql index d23e03fe1..8c9d0e629 100644 --- a/testdata/datasets/functional/functional_schema_template.sql +++ b/testdata/datasets/functional/functional_schema_template.sql @@ -4837,5 +4837,53 @@ STORED AS PAIMON LOCATION '/test-warehouse/paimon_test/paimon_catalog/warehouse/functional.db/paimon_non_partitioned'; ---- DEPENDENT_LOAD `hadoop fs -mkdir -p /test-warehouse/paimon_test/paimon_catalog/warehouse/functional.db && \ -hadoop fs -put -f ${IMPALA_HOME}//testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_non_partitioned /test-warehouse/paimon_test/paimon_catalog/warehouse/functional.db -==== \ No newline at end of file +hadoop fs -put -f ${IMPALA_HOME}/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_non_partitioned /test-warehouse/paimon_test/paimon_catalog/warehouse/functional.db +==== +---- DATASET +functional +---- BASE_TABLE_NAME +paimon_primitive_alltypes +---- CREATE +CREATE EXTERNAL TABLE IF NOT EXISTS {db_name}{db_suffix}.{table_name} +STORED AS PAIMON +LOCATION '/test-warehouse/paimon_test/paimon_catalog/warehouse/functional.db/paimon_primitive_alltypes'; +---- DEPENDENT_LOAD +`hadoop fs -mkdir -p /test-warehouse/paimon_test/paimon_catalog/warehouse/functional.db && \ +hadoop fs -put -f ${IMPALA_HOME}/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_primitive_alltypes /test-warehouse/paimon_test/paimon_catalog/warehouse/functional.db +==== +---- DATASET +functional +---- BASE_TABLE_NAME +paimon_decimal_tbl +---- CREATE +CREATE EXTERNAL TABLE IF NOT EXISTS {db_name}{db_suffix}.{table_name} +STORED AS PAIMON +LOCATION '/test-warehouse/paimon_test/paimon_catalog/warehouse/functional.db/paimon_decimal_tbl'; +---- DEPENDENT_LOAD +`hadoop fs -mkdir -p /test-warehouse/paimon_test/paimon_catalog/warehouse/functional.db && \ +hadoop fs -put -f ${IMPALA_HOME}/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/paimon_decimal_tbl /test-warehouse/paimon_test/paimon_catalog/warehouse/functional.db +==== +---- DATASET +functional +---- BASE_TABLE_NAME +alltypes_paimon +---- CREATE +CREATE EXTERNAL TABLE IF NOT EXISTS {db_name}{db_suffix}.{table_name} +STORED AS PAIMON +LOCATION '/test-warehouse/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_paimon'; +---- DEPENDENT_LOAD +`hadoop fs -mkdir -p /test-warehouse/paimon_test/paimon_catalog/warehouse/functional.db && \ +hadoop fs -put -f ${IMPALA_HOME}/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_paimon /test-warehouse/paimon_test/paimon_catalog/warehouse/functional.db +==== +---- DATASET +functional +---- BASE_TABLE_NAME +alltypes_structs_paimon +---- CREATE +CREATE EXTERNAL TABLE IF NOT EXISTS {db_name}{db_suffix}.{table_name} +STORED AS PAIMON +LOCATION '/test-warehouse/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_structs_paimon'; +---- DEPENDENT_LOAD +`hadoop fs -mkdir -p /test-warehouse/paimon_test/paimon_catalog/warehouse/functional.db && \ +hadoop fs -put -f ${IMPALA_HOME}/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_structs_paimon /test-warehouse/paimon_test/paimon_catalog/warehouse/functional.db + diff --git a/testdata/datasets/functional/schema_constraints.csv b/testdata/datasets/functional/schema_constraints.csv index ec1d997e0..568e5c04d 100644 --- a/testdata/datasets/functional/schema_constraints.csv +++ b/testdata/datasets/functional/schema_constraints.csv @@ -444,4 +444,8 @@ table_name:timestamp_with_tz_str, constraint:restrict_to, table_format:parquet/n # Paimon tests are executed in the PARQUET file format dimension table_name:paimon_partitioned, constraint:restrict_to, table_format:parquet/none/none -table_name:paimon_non_partitioned, constraint:restrict_to, table_format:parquet/none/none \ No newline at end of file +table_name:paimon_non_partitioned, constraint:restrict_to, table_format:parquet/none/none +table_name:paimon_primitive_alltypes, constraint:restrict_to, table_format:parquet/none/none +table_name:paimon_decimal_tbl, constraint:restrict_to, table_format:parquet/none/none +table_name:alltypes_paimon, constraint:restrict_to, table_format:parquet/none/none +table_name:alltypes_structs_paimon, constraint:restrict_to, table_format:parquet/none/none \ No newline at end of file diff --git a/testdata/workloads/functional-query/queries/QueryTest/paimon-negative.test b/testdata/workloads/functional-query/queries/QueryTest/paimon-negative.test index 73e2fa1c4..9d0a52341 100644 --- a/testdata/workloads/functional-query/queries/QueryTest/paimon-negative.test +++ b/testdata/workloads/functional-query/queries/QueryTest/paimon-negative.test @@ -161,8 +161,4 @@ ON a.userid = source.userid WHEN MATCHED THEN UPDATE SET movieid = source.movieid; ---- CATCH AnalysisException: Target table must be an Iceberg table: functional_parquet.paimon_partitioned -==== ----- QUERY -SELECT * FROM functional_parquet.paimon_partitioned; ----- CATCH -NotImplementedException: Query is not supported for PAIMON table now + diff --git a/testdata/workloads/functional-query/queries/QueryTest/paimon-query.test b/testdata/workloads/functional-query/queries/QueryTest/paimon-query.test new file mode 100644 index 000000000..075cfb8ae --- /dev/null +++ b/testdata/workloads/functional-query/queries/QueryTest/paimon-query.test @@ -0,0 +1,168 @@ +==== +---- QUERY +# Test the Paimon DataSource +# count(*) with a predicate evaluated by Impala +select count(*) from functional_parquet.paimon_primitive_alltypes +where float_value = 0 and varchar_value is not NULL +---- RESULTS +1 +---- TYPES +BIGINT +==== +---- QUERY +# count(*) with no predicates has no materialized slots +select count(*) from functional_parquet.paimon_primitive_alltypes +---- RESULTS +7 +---- TYPES +BIGINT +==== +---- QUERY +# Gets all types including a row with a NULL value. The predicate pushed to +# the DataSource. +select bool_value,tiny_value,small_value,int_value,big_value,float_value,double_value,decimal_value,char_value,varchar_value,binary_value,date_value,ts_value +from functional_parquet.paimon_primitive_alltypes +where int_value > 12 limit 5; +---- RESULTS +true,127,32767,2147483647,9223372036854775807,3.402823466385289e+38,1.797693134862316e+308,1234567.89,'char ','varchar','\x01\x02\x03',2023-03-01,2025-08-11 18:17:27.653000000 +true,3,3,13,30,3.299999952316284,30.3,345.67,'char3 ','varchar3','NULL',2009-01-02,2009-01-02 00:13:00.480000000 +false,4,4,14,40,4.400000095367432,40.4,456.78,'char4 ','varchar4','NULL',2009-01-02,2009-01-02 00:14:00.510000000 +true,0,0,20,0,0.0,0.0,0.00,'char0 ','varchar0','NULL',2009-01-03,2009-01-03 00:20:00.900000000 +---- TYPES +BOOLEAN, TINYINT, SMALLINT, INT, BIGINT, FLOAT, DOUBLE, DECIMAL, CHAR, VARCHAR, BINARY, DATE, TIMESTAMP +==== +---- QUERY +# Gets specified columns. +select int_value, bool_value, small_value, float_value, double_value, date_value +from functional_parquet.paimon_primitive_alltypes +where int_value > 12 limit 5; +---- RESULTS +2147483647,true,32767,3.402823466385289e+38,1.797693134862316e+308,2023-03-01 +13,true,3,3.299999952316284,30.3,2009-01-02 +14,false,4,4.400000095367432,40.4,2009-01-02 +20,true,0,0.0,0.0,2009-01-03 +---- TYPES +INT, BOOLEAN, SMALLINT, FLOAT, DOUBLE, DATE +==== +---- QUERY +# Inner join with a non Paimon table +select a.int_value, b.int_col +from functional_parquet.paimon_primitive_alltypes a inner join functional.alltypes b on (a.int_value = b.id) +where a.int_value = 11 +---- RESULTS +11,1 +---- TYPES +INT, INT +==== +---- QUERY +# Gets specified columns based on date predicate with operator '='. +select int_value, bool_value, small_value, float_value, double_value, date_value +from functional_parquet.paimon_primitive_alltypes +where date_value = DATE '2009-01-02' order by int_value limit 5; +---- RESULTS +11,true,1,1.100000023841858,10.1,2009-01-02 +12,false,2,2.200000047683716,20.2,2009-01-02 +13,true,3,3.299999952316284,30.3,2009-01-02 +14,false,4,4.400000095367432,40.4,2009-01-02 +---- TYPES +INT, BOOLEAN, SMALLINT, FLOAT, DOUBLE, DATE +==== +---- QUERY +select * from functional_parquet.paimon_decimal_tbl; +---- RESULTS +1234,2222,1.2345678900,0.12345678900000000000000000000000000000,12345.78900 +2345,111,12.3456789000,0.12345678900000000000000000000000000000,3.14100 +12345,333,123.4567890000,0.12345678900000000000000000000000000000,11.22000 +12345,333,1234.5678900000,0.12345678900000000000000000000000000000,0.10000 +132842,333,12345.6789000000,0.12345678900000000000000000000000000000,0.77889 +---- TYPES +DECIMAL,DECIMAL,DECIMAL,DECIMAL,DECIMAL +==== +---- QUERY +select * from functional_parquet.paimon_decimal_tbl where d3 < 10.00; +---- RESULTS +1234,2222,1.2345678900,0.12345678900000000000000000000000000000,12345.78900 +---- TYPES +DECIMAL,DECIMAL,DECIMAL,DECIMAL,DECIMAL +==== +---- QUERY +select * from functional_parquet.paimon_decimal_tbl where d3 <= 123.45678900; +---- RESULTS +1234,2222,1.2345678900,0.12345678900000000000000000000000000000,12345.78900 +2345,111,12.3456789000,0.12345678900000000000000000000000000000,3.14100 +12345,333,123.4567890000,0.12345678900000000000000000000000000000,11.22000 +---- TYPES +DECIMAL,DECIMAL,DECIMAL,DECIMAL,DECIMAL +==== +---- QUERY +select * from functional_parquet.paimon_decimal_tbl where d3 = 123.45678900; +---- RESULTS +12345,333,123.4567890000,0.12345678900000000000000000000000000000,11.22000 +---- TYPES +DECIMAL,DECIMAL,DECIMAL,DECIMAL,DECIMAL +==== +---- QUERY +select * from functional_parquet.paimon_decimal_tbl where d3 != 123.45678900; +---- RESULTS +1234,2222,1.2345678900,0.12345678900000000000000000000000000000,12345.78900 +2345,111,12.3456789000,0.12345678900000000000000000000000000000,3.14100 +12345,333,1234.5678900000,0.12345678900000000000000000000000000000,0.10000 +132842,333,12345.6789000000,0.12345678900000000000000000000000000000,0.77889 +---- TYPES +DECIMAL,DECIMAL,DECIMAL,DECIMAL,DECIMAL +==== +---- QUERY +select * from functional_parquet.paimon_decimal_tbl where d3 >= 123.45678900; +---- RESULTS +12345,333,123.4567890000,0.12345678900000000000000000000000000000,11.22000 +12345,333,1234.5678900000,0.12345678900000000000000000000000000000,0.10000 +132842,333,12345.6789000000,0.12345678900000000000000000000000000000,0.77889 +---- TYPES +DECIMAL,DECIMAL,DECIMAL,DECIMAL,DECIMAL +==== +---- QUERY +select * from functional_parquet.paimon_decimal_tbl where d3 > 123.45678900; +---- RESULTS +12345,333,1234.5678900000,0.12345678900000000000000000000000000000,0.10000 +132842,333,12345.6789000000,0.12345678900000000000000000000000000000,0.77889 +---- TYPES +DECIMAL,DECIMAL,DECIMAL,DECIMAL,DECIMAL +==== +---- QUERY +select * from functional_parquet.paimon_decimal_tbl where d3 >= 123.45678900 and d5 < 10.0; +---- RESULTS +12345,333,1234.5678900000,0.12345678900000000000000000000000000000,0.10000 +132842,333,12345.6789000000,0.12345678900000000000000000000000000000,0.77889 +---- TYPES +DECIMAL,DECIMAL,DECIMAL,DECIMAL,DECIMAL +==== +---- QUERY +SELECT * FROM functional_parquet.alltypes_structs_paimon WHERE year=2009 AND month=1 AND id > 300 +---- RESULTS +301,2009,1 +302,2009,1 +303,2009,1 +304,2009,1 +305,2009,1 +306,2009,1 +307,2009,1 +308,2009,1 +309,2009,1 +---- TYPES +INT,INT,INT +==== +---- QUERY +select struct_val from functional_parquet.alltypes_structs_paimon where year=2009 and month=1 and id > 300 +---- CATCH +AnalysisException: Local Paimon Table: functional_parquet.alltypes_structs_paimon is not supported when querying STRUCT type STRUCT +==== +---- QUERY +select struct_val.bool_col from functional_parquet.alltypes_structs_paimon where year=2009 and month=1 and id > 300 +---- CATCH +AnalysisException: Paimon Scanner doesn't support nested columns. +==== +---- QUERY +select INPUT__FILE__NAME from functional_parquet.alltypes_structs_paimon where year=2009 and month=1 and id > 300 +---- CATCH +AnalysisException: Could not resolve column/field reference: 'input__file__name' + diff --git a/tests/query_test/test_paimon.py b/tests/query_test/test_paimon.py index a0297950b..0a92d096b 100644 --- a/tests/query_test/test_paimon.py +++ b/tests/query_test/test_paimon.py @@ -52,3 +52,7 @@ class TestCreatingPaimonTable(ImpalaTestSuite): def test_paimon_negative(self, vector, unique_database): self.run_test_case('QueryTest/paimon-negative', vector, unique_database) + + def test_paimon_query(self, vector, unique_database): + self.run_test_case('QueryTest/paimon-query', + vector, unique_database) diff --git a/tests/query_test/test_scanners.py b/tests/query_test/test_scanners.py index c044cf0a5..04d3bb4b9 100644 --- a/tests/query_test/test_scanners.py +++ b/tests/query_test/test_scanners.py @@ -2014,3 +2014,38 @@ class TestSingleFileTable(ImpalaTestSuite): select_stmt = "select count(*) from {db}.{tbl}".format(**params) res = self.execute_query_expect_success(self.client, select_stmt, options) assert res.data[0].split("\t")[0] == '1' + + +class TestPaimonScannerWithLimit(ImpalaTestSuite): + """Test paimon scanners with a simple limit clause. The limit clause triggers + cancellation in the scanner code paths.""" + + @classmethod + def add_test_dimensions(cls): + super(TestPaimonScannerWithLimit, cls).add_test_dimensions() + # Use a small batch size so changing the limit affects the timing of cancellation + cls.ImpalaTestMatrix.add_dimension( + create_exec_option_dimension(batch_sizes=[100])) + cls.ImpalaTestMatrix.add_constraint( + lambda v: v.get_value('table_format').file_format == 'parquet') + + def test_limit(self, vector): + vector.get_value('exec_option')['abort_on_error'] = 1 + self._test_limit(vector) + # IMPALA-3337: when continuing on error, the error log should not show errors + # (e.g. "Cancelled"). + vector.get_value('exec_option')['abort_on_error'] = 0 + self._test_limit(vector) + + def _test_limit(self, vector): + iterations = 50 + query_template = "select * from functional_parquet.alltypes_paimon limit %s" + for i in range(1, iterations): + # Vary the limit to vary the timing of cancellation + limit = (i * 100) % 1001 + 1 + query = query_template % limit + result = self.execute_query(query, vector.get_value('exec_option'), + table_format=vector.get_value('table_format')) + assert len(result.data) == limit + # IMPALA-3337: The error log should be empty. + assert not result.log

diff --git a/fe/src/main/java/org/apache/impala/catalog/paimon/PaimonUtil.java b/fe/src/main/java/org/apache/impala/catalog/paimon/PaimonUtil.java index f73870864..df3284009 100644 --- a/fe/src/main/java/org/apache/impala/catalog/paimon/PaimonUtil.java +++ b/fe/src/main/java/org/apache/impala/catalog/paimon/PaimonUtil.java @@ -104,7 +104,6 @@ import org.apache.paimon.types.SmallIntType; import org.apache.paimon.types.TinyIntType; import org.apache.paimon.utils.InternalRowPartitionComputer; import org.apache.thrift.TException; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -200,6 +199,17 @@ public class PaimonUtil { return ret; } + /** + * function to close autoClosable object quitely. + */ + public static void closeQuitely(AutoCloseable closeable) { + if (closeable != null) { + try { + closeable.close(); + } catch (Exception e) { LOG.warn("Error closing " + closeable, e); } + } + } + /** * Converts Paimon schema to an Impala schema. */ @@ -209,7 +219,8 @@ public class PaimonUtil { int pos = 0; for (DataField dataField : schema.getFields()) { Type colType = PaimonImpalaTypeUtils.toImpalaType(dataField.type()); - ret.add(new Column(dataField.name().toLowerCase(), colType, pos++)); + ret.add(new PaimonColumn(dataField.name().toLowerCase(), colType, + dataField.description(), pos++, dataField.id(), dataField.type().isNullable())); } return ret; } @@ -861,4 +872,43 @@ public class PaimonUtil { } return result; } + + /** + * convert paimon api table schema to impala columns + */ + public static List toImpalaColumn(Table table) throws ImpalaRuntimeException { + RowType rowType = table.rowType(); + List dataFields = rowType.getFields(); + List partitionKeys = table.partitionKeys() + .stream() + .map(String::toLowerCase) + .collect(Collectors.toList()); + List impalaFields = convertToImpalaSchema(rowType); + List impalaNonPartitionedFields = Lists.newArrayList(); + List impalaPartitionedFields = Lists.newArrayList(); + List columns = Lists.newArrayList(); + // lookup the clustering columns + for (String name : partitionKeys) { + int colIndex = PaimonUtil.getFieldIndexByNameIgnoreCase(rowType, name); + Preconditions.checkArgument(colIndex >= 0); + impalaPartitionedFields.add(impalaFields.get(colIndex)); + } + // put non-clustering columns in natural order + for (int i = 0; i < dataFields.size(); i++) { + if (!partitionKeys.contains(dataFields.get(i).name().toLowerCase())) { + impalaNonPartitionedFields.add(impalaFields.get(i)); + } + } + + int colPos = 0; + for (Column col : impalaPartitionedFields) { + col.setPosition(colPos++); + columns.add(col); + } + for (Column col : impalaNonPartitionedFields) { + col.setPosition(colPos++); + columns.add(col); + } + return columns; + } } diff --git a/fe/src/main/java/org/apache/impala/planner/PaimonScanNode.java b/fe/src/main/java/org/apache/impala/planner/PaimonScanNode.java new file mode 100644 index 000000000..509c3f525 --- /dev/null +++ b/fe/src/main/java/org/apache/impala/planner/PaimonScanNode.java @@ -0,0 +1,303 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.impala.planner; + +import com.google.common.base.MoreObjects; +import com.google.common.base.Preconditions; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import org.apache.commons.lang3.SerializationUtils; +import org.apache.impala.analysis.Analyzer; +import org.apache.impala.analysis.Expr; +import org.apache.impala.analysis.MultiAggregateInfo; +import org.apache.impala.analysis.SlotDescriptor; +import org.apache.impala.analysis.TupleDescriptor; +import org.apache.impala.catalog.paimon.FePaimonTable; +import org.apache.impala.catalog.paimon.PaimonColumn; +import org.apache.impala.common.AnalysisException; +import org.apache.impala.common.ImpalaException; +import org.apache.impala.common.ImpalaRuntimeException; +import org.apache.impala.common.ThriftSerializationCtx; +import org.apache.impala.planner.paimon.PaimonSplit; +import org.apache.impala.thrift.TExplainLevel; +import org.apache.impala.thrift.TNetworkAddress; +import org.apache.impala.thrift.TPaimonScanNode; +import org.apache.impala.thrift.TPlanNode; +import org.apache.impala.thrift.TPlanNodeType; +import org.apache.impala.thrift.TQueryOptions; +import org.apache.impala.thrift.TScanRange; +import org.apache.impala.thrift.TScanRangeLocation; +import org.apache.impala.thrift.TScanRangeLocationList; +import org.apache.impala.thrift.TScanRangeSpec; +import org.apache.impala.util.ExecutorMembershipSnapshot; +import org.apache.paimon.table.Table; +import org.apache.paimon.table.source.DataSplit; +import org.apache.paimon.table.source.ReadBuilder; +import org.apache.paimon.table.source.Split; +import org.apache.paimon.types.DataField; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * Jni-based scan node of a single paimon table. + */ +public class PaimonScanNode extends ScanNode { + private final static Logger LOG = LoggerFactory.getLogger(PaimonScanNode.class); + private final static long PAIMON_ROW_AVG_SIZE_OVERHEAD = 4L; + // FeTable object + private final FePaimonTable table_; + + // paimon table object + private final Table paimonApiTable_; + + // Indexes for the set of hosts that will be used for the query. + // From analyzer.getHostIndex().getIndex(address) + private final Set hostIndexSet_ = new HashSet<>(); + // Array of top level field ids used for top level column scan pruning. + private int[] projection_; + // top level field id map + private Set fieldIdMap_ = Sets.newHashSet(); + // Splits generated for paimon scanning during planning stage. + private List splits_; + + public PaimonScanNode(PlanNodeId id, TupleDescriptor desc, List conjuncts, + MultiAggregateInfo aggInfo, FePaimonTable table) { + super(id, desc, "SCAN PAIMON"); + conjuncts_ = conjuncts; + aggInfo_ = aggInfo; + table_ = table; + paimonApiTable_ = table.getPaimonApiTable(); + } + + @Override + public void init(Analyzer analyzer) throws ImpalaException { + super.init(analyzer); + conjuncts_ = orderConjunctsByCost(conjuncts_); + // TODO: implement predicate push down later here. + + // materialize slots in remaining conjuncts_ + analyzer.materializeSlots(conjuncts_); + collectProjectionId(); + computeMemLayout(analyzer); + computeScanRangeLocations(analyzer); + computePaimonStats(analyzer); + } + + public void computePaimonStats(Analyzer analyzer) { + computeNumNodes(analyzer); + // Update the cardinality, hint value will be used when table has no stats. + inputCardinality_ = cardinality_ = estimateTableRowCount(); + cardinality_ = applyConjunctsSelectivity(cardinality_); + cardinality_ = capCardinalityAtLimit(cardinality_); + avgRowSize_ = estimateAvgRowSize(); + if (LOG.isTraceEnabled()) { + LOG.trace("computeStats paimonScan: cardinality=" + Long.toString(cardinality_)); + } + } + + /** + * Collect and analyze top-level columns. + */ + public void collectProjectionId() throws AnalysisException { + projection_ = new int[desc_.getSlots().size()]; + for (int i = 0; i < desc_.getSlots().size(); i++) { + SlotDescriptor sd = desc_.getSlots().get(i); + if (sd.isVirtualColumn()) { + throw new AnalysisException("Paimon Scanner doesn't support virtual columns."); + } + if (sd.getPath().getRawPath() != null && sd.getPath().getRawPath().size() > 1) { + throw new AnalysisException("Paimon Scanner doesn't support nested columns."); + } + PaimonColumn paimonColumn = (PaimonColumn) desc_.getSlots().get(i).getColumn(); + projection_[i] = paimonColumn.getFieldId(); + fieldIdMap_.add(paimonColumn.getFieldId()); + } + Preconditions.checkArgument(projection_.length == desc_.getSlots().size()); + LOG.info(String.format("table %s projection fields: %s", table_.getFullName(), + Arrays.toString(projection_))); + } + + protected long estimateSplitRowCount(Split s) { + if (s instanceof DataSplit) { + DataSplit dataSplit = (DataSplit) s; + if (dataSplit.mergedRowCountAvailable()) { return dataSplit.mergedRowCount(); } + } + return s.rowCount(); + } + + protected long estimateTableRowCount() { + return splits_.stream() + .map(this ::estimateSplitRowCount) + .reduce(Long::sum) + .orElse(-1L); + } + + protected long estimateAvgRowSize() { + List dataColumns = paimonApiTable_.rowType().getFields(); + return dataColumns.stream() + .filter(df -> fieldIdMap_.contains(df.id())) + .mapToInt(column -> column.type().defaultSize()) + .sum() + + PAIMON_ROW_AVG_SIZE_OVERHEAD; + } + /** + * Compute the scan range locations for the given table using the scan tokens. + */ + private void computeScanRangeLocations(Analyzer analyzer) + throws ImpalaRuntimeException { + scanRangeSpecs_ = new TScanRangeSpec(); + ReadBuilder readBuilder = paimonApiTable_.newReadBuilder(); + + // 2. Plan splits in 'Coordinator'. + splits_ = readBuilder.newScan().plan().splits(); + + if (splits_.size() <= 0) { + LOG.info("no paimon data available"); + return; + } + + for (Split split : splits_) { + List locations = new ArrayList<>(); + // TODO: Currently, set to dummy network address for random executor scheduling, + // don't forget to get actual location for data locality after native table scan + // is supported. + // + { + TNetworkAddress address = new TNetworkAddress("localhost", 12345); + // Use the network address to look up the host in the global list + Integer hostIndex = analyzer.getHostIndex().getOrAddIndex(address); + locations.add(new TScanRangeLocation(hostIndex)); + hostIndexSet_.add(hostIndex); + } + + TScanRange scanRange = new TScanRange(); + // TODO: apply predicate push down later. + PaimonSplit paimonSplit = new PaimonSplit(split, null); + byte[] split_data_serialized = SerializationUtils.serialize(paimonSplit); + scanRange.setFile_metadata(split_data_serialized); + TScanRangeLocationList locs = new TScanRangeLocationList(); + locs.setScan_range(scanRange); + locs.setLocations(locations); + scanRangeSpecs_.addToConcrete_ranges(locs); + } + } + + @Override + protected double computeSelectivity() { + List allConjuncts = Lists.newArrayList(Iterables.concat(conjuncts_)); + return computeCombinedSelectivity(allConjuncts); + } + + /** + * Estimate the number of impalad nodes that this scan node will execute on (which is + * ultimately determined by the scheduling done by the backend's Scheduler). + * As of now, scan ranges are scheduled round-robin, since they have no location + * information. . + */ + protected void computeNumNodes(Analyzer analyzer) { + ExecutorMembershipSnapshot cluster = ExecutorMembershipSnapshot.getCluster(); + final int maxInstancesPerNode = getMaxInstancesPerNode(analyzer); + final int maxPossibleInstances = + analyzer.numExecutorsForPlanning() * maxInstancesPerNode; + int totalNodes = 0; + int totalInstances = 0; + int numRemoteRanges = splits_.size(); + + // The remote ranges are round-robined across all the impalads. + int numRemoteNodes = Math.min(numRemoteRanges, analyzer.numExecutorsForPlanning()); + // The remote assignments may overlap, but we don't know by how much + // so conservatively assume no overlap. + totalNodes = Math.min(numRemoteNodes, analyzer.numExecutorsForPlanning()); + + totalInstances = Math.min(numRemoteRanges, totalNodes * maxInstancesPerNode); + + numNodes_ = Math.max(totalNodes, 1); + numInstances_ = Math.max(totalInstances, 1); + } + + @Override + public void computeNodeResourceProfile(TQueryOptions queryOptions) { + // current batch size is from query options, so estimated bytes + // is calculated as BATCH_SIZE * average row size * 2 + long batchSize = getRowBatchSize(queryOptions); + long memSize = batchSize * (long) getAvgRowSize() * 2; + nodeResourceProfile_ = + new ResourceProfileBuilder().setMemEstimateBytes(memSize).build(); + } + + @Override + protected void toThrift(TPlanNode msg, ThriftSerializationCtx serialCtx) { + toThrift(msg); + } + + @Override + protected void toThrift(TPlanNode node) { + node.node_type = TPlanNodeType.PAIMON_SCAN_NODE; + node.paimon_table_scan_node = new TPaimonScanNode(desc_.getId().asInt(), + ByteBuffer.wrap(SerializationUtils.serialize(paimonApiTable_)), + table_.getFullName()); + } + + @Override + public void computeProcessingCost(TQueryOptions queryOptions) { + processingCost_ = computeScanProcessingCost(queryOptions); + } + + @Override + protected String getNodeExplainString( + String prefix, String detailPrefix, TExplainLevel detailLevel) { + StringBuilder result = new StringBuilder(); + + String aliasStr = desc_.hasExplicitAlias() ? " " + desc_.getAlias() : ""; + result.append(String.format("%s%s:%s [%s%s]\n", prefix, id_.toString(), displayName_, + table_.getFullName(), aliasStr)); + + switch (detailLevel) { + case MINIMAL: break; + case STANDARD: // Fallthrough intended. + case EXTENDED: // Fallthrough intended. + case VERBOSE: { + if (!conjuncts_.isEmpty()) { + result.append(detailPrefix + + "predicates: " + Expr.getExplainString(conjuncts_, detailLevel) + "\n"); + } + if (!runtimeFilters_.isEmpty()) { + result.append(detailPrefix + "runtime filters: "); + result.append(getRuntimeFilterExplainString(false, detailLevel)); + } + } + } + return result.toString(); + } + + @Override + protected String debugString() { + MoreObjects.ToStringHelper helper = MoreObjects.toStringHelper(this); + helper.addValue(super.debugString()); + helper.addValue("paimonTable=" + table_.getFullName()); + return helper.toString(); + } +} diff --git a/fe/src/main/java/org/apache/impala/planner/PaimonScanPlanner.java b/fe/src/main/java/org/apache/impala/planner/PaimonScanPlanner.java new file mode 100644 index 000000000..a9d300a6a --- /dev/null +++ b/fe/src/main/java/org/apache/impala/planner/PaimonScanPlanner.java @@ -0,0 +1,64 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.impala.planner; + +import com.google.common.base.Preconditions; +import org.apache.impala.analysis.Analyzer; +import org.apache.impala.analysis.Expr; +import org.apache.impala.analysis.MultiAggregateInfo; +import org.apache.impala.analysis.TableRef; +import org.apache.impala.catalog.paimon.FePaimonTable; +import org.apache.impala.common.ImpalaException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; + +/** + * ScanNode factory class for Paimon, currently, only Jni based is supported + * Will add native scanNode later. + */ +public class PaimonScanPlanner { + private static final Logger LOG = LoggerFactory.getLogger(PaimonScanPlanner.class); + + private Analyzer analyzer_; + private PlannerContext ctx_; + private TableRef tblRef_; + private List conjuncts_; + private MultiAggregateInfo aggInfo_; + + private FePaimonTable table_; + + public PaimonScanPlanner(Analyzer analyzer, PlannerContext ctx, TableRef paimonTblRef, + List conjuncts, MultiAggregateInfo aggInfo) throws ImpalaException { + Preconditions.checkState(paimonTblRef.getTable() instanceof FePaimonTable); + analyzer_ = analyzer; + ctx_ = ctx; + tblRef_ = paimonTblRef; + conjuncts_ = conjuncts; + aggInfo_ = aggInfo; + table_ = (FePaimonTable) paimonTblRef.getTable(); + } + + public PlanNode createPaimonScanPlan() throws ImpalaException { + PaimonScanNode ret = new PaimonScanNode( + ctx_.getNextNodeId(), tblRef_.getDesc(), conjuncts_, aggInfo_, table_); + ret.init(analyzer_); + return ret; + } +} diff --git a/fe/src/main/java/org/apache/impala/planner/SingleNodePlanner.java b/fe/src/main/java/org/apache/impala/planner/SingleNodePlanner.java index a6e83b62c..effe17fee 100644 --- a/fe/src/main/java/org/apache/impala/planner/SingleNodePlanner.java +++ b/fe/src/main/java/org/apache/impala/planner/SingleNodePlanner.java @@ -1910,6 +1910,10 @@ public class SingleNodePlanner implements SingleNodePlannerIntf { conjuncts); scanNode.init(analyzer); return scanNode; + } else if (table instanceof FePaimonTable) { + PaimonScanPlanner paimonScanPlanner = + new PaimonScanPlanner(analyzer, ctx_, tblRef, conjuncts, aggInfo); + return paimonScanPlanner.createPaimonScanPlan(); } else if (table instanceof FeHBaseTable) { // HBase table scanNode = new HBaseScanNode(ctx_.getNextNodeId(), tblRef.getDesc()); diff --git a/fe/src/main/java/org/apache/impala/planner/paimon/PaimonSplit.java b/fe/src/main/java/org/apache/impala/planner/paimon/PaimonSplit.java new file mode 100644 index 000000000..c1430dd50 --- /dev/null +++ b/fe/src/main/java/org/apache/impala/planner/paimon/PaimonSplit.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.impala.planner.paimon; + +import org.apache.paimon.predicate.Predicate; +import org.apache.paimon.table.source.Split; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +/** + * Paimon split entity used by paimon jni scanner. + * */ +public class PaimonSplit implements Serializable { + // Paimon split instance to perform scan. + private final Split split_; + // predicates that can be pushed to paimon source. + private final ArrayList predicates_; + + public PaimonSplit(Split split, ArrayList predicates) { + split_ = split; + predicates_ = predicates; + } + + public Split getSplit() { return split_; } + + public List getPredicates() { return predicates_; } +} diff --git a/fe/src/main/java/org/apache/impala/util/paimon/ArrowRootAllocation.java b/fe/src/main/java/org/apache/impala/util/paimon/ArrowRootAllocation.java new file mode 100644 index 000000000..41eaaf28c --- /dev/null +++ b/fe/src/main/java/org/apache/impala/util/paimon/ArrowRootAllocation.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.impala.util.paimon; + +import org.apache.arrow.memory.RootAllocator; + +/* + * + * Arrow Root Allocation singleton + * Note: all Arrow code should use this as root allocator. + * + * */ +public class ArrowRootAllocation { + private static RootAllocator ROOT_ALLOCATOR; + + private ArrowRootAllocation() {} + + public static RootAllocator rootAllocator() { + synchronized (ArrowRootAllocation.class) { + if (ROOT_ALLOCATOR == null) { + ROOT_ALLOCATOR = new RootAllocator(Long.MAX_VALUE); + Runtime.getRuntime().addShutdownHook(new Thread(() -> ROOT_ALLOCATOR.close())); + } + } + return ROOT_ALLOCATOR; + } +} diff --git a/fe/src/main/java/org/apache/impala/util/paimon/PaimonArrowFieldTypeFactory.java b/fe/src/main/java/org/apache/impala/util/paimon/PaimonArrowFieldTypeFactory.java new file mode 100644 index 000000000..eecbd8890 --- /dev/null +++ b/fe/src/main/java/org/apache/impala/util/paimon/PaimonArrowFieldTypeFactory.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.impala.util.paimon; + +import org.apache.arrow.vector.types.pojo.ArrowType; +import org.apache.arrow.vector.types.pojo.FieldType; +import org.apache.paimon.arrow.ArrowFieldTypeConversion; +import org.apache.paimon.types.DecimalType; + +/** + * It is an extension of {@link ArrowFieldTypeConversion.ArrowFieldTypeVisitor} class. + * To change the decimal conversion behavior. + * Paimon decimal type will convert to arrow Decimal128 type, it involves byte copy + * and padding, which will cause additional overhead to pass data to BE. + * To Eliminate the overhead, will directly pass the decimal unscaled bytes to BE, So + * Arrow binary type will be used instead of Decimal128 data type. + */ +public class PaimonArrowFieldTypeFactory + extends ArrowFieldTypeConversion.ArrowFieldTypeVisitor { + @Override + public FieldType visit(DecimalType decimalType) { + return new FieldType(decimalType.isNullable(), new ArrowType.Binary(), null); + } +} diff --git a/fe/src/main/java/org/apache/impala/util/paimon/PaimonArrowFieldWriterFactory.java b/fe/src/main/java/org/apache/impala/util/paimon/PaimonArrowFieldWriterFactory.java new file mode 100644 index 000000000..ad6873418 --- /dev/null +++ b/fe/src/main/java/org/apache/impala/util/paimon/PaimonArrowFieldWriterFactory.java @@ -0,0 +1,83 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.impala.util.paimon; + +import org.apache.arrow.vector.FieldVector; +import org.apache.arrow.vector.VarBinaryVector; +import org.apache.paimon.arrow.writer.ArrowFieldWriter; +import org.apache.paimon.arrow.writer.ArrowFieldWriterFactory; +import org.apache.paimon.arrow.writer.ArrowFieldWriterFactoryVisitor; +import org.apache.paimon.data.DataGetters; +import org.apache.paimon.data.Decimal; +import org.apache.paimon.data.columnar.ColumnVector; +import org.apache.paimon.data.columnar.DecimalColumnVector; +import org.apache.paimon.types.DecimalType; + +import javax.annotation.Nullable; + +/** + * It is an extension of {@link ArrowFieldWriterFactoryVisitor} class. + * To change the decimal field writer behavior. + * Will directly convert paimon decimal type to arrow binary value. + */ +public class PaimonArrowFieldWriterFactory extends ArrowFieldWriterFactoryVisitor { + @Override + public ArrowFieldWriterFactory visit(DecimalType decimalType) { + return (fieldVector, isNullable) + -> new DecimalWriter(fieldVector, decimalType.getPrecision(), + decimalType.getScale(), isNullable); + } + + public static class DecimalWriter extends ArrowFieldWriter { + // decimal precision + private final int precision_; + // decimal scale + private final int scale_; + + public DecimalWriter( + FieldVector fieldVector, int precision, int scale, boolean isNullable) { + super(fieldVector, isNullable); + this.precision_ = precision; + this.scale_ = scale; + } + + protected void doWrite(ColumnVector columnVector, @Nullable int[] pickedInColumn, + int startIndex, int batchRows) { + VarBinaryVector decimalVector = (VarBinaryVector) this.fieldVector; + + for (int i = 0; i < batchRows; ++i) { + int row = this.getRowNumber(startIndex, i, pickedInColumn); + if (columnVector.isNullAt(row)) { + decimalVector.setNull(i); + } else { + Decimal value = ((DecimalColumnVector) columnVector) + .getDecimal(row, this.precision_, this.scale_); + byte[] bytes = value.toUnscaledBytes(); + decimalVector.setSafe(i, bytes); + } + } + } + + protected void doWrite(int rowIndex, DataGetters getters, int pos) { + ((VarBinaryVector) this.fieldVector) + .setSafe(rowIndex, + getters.getDecimal(pos, this.precision_, this.scale_).toUnscaledBytes()); + } + } +} diff --git a/fe/src/main/java/org/apache/impala/util/paimon/PaimonArrowFormatNativeWriter.java b/fe/src/main/java/org/apache/impala/util/paimon/PaimonArrowFormatNativeWriter.java new file mode 100644 index 000000000..ff65ff96b --- /dev/null +++ b/fe/src/main/java/org/apache/impala/util/paimon/PaimonArrowFormatNativeWriter.java @@ -0,0 +1,94 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.impala.util.paimon; + +import org.apache.arrow.c.ArrowArray; +import org.apache.arrow.c.ArrowSchema; +import org.apache.arrow.memory.BufferAllocator; +import org.apache.arrow.vector.VectorSchemaRoot; +import org.apache.paimon.arrow.vector.ArrowCStruct; +import org.apache.paimon.arrow.vector.ArrowFormatCWriter; +import org.apache.paimon.data.InternalRow; +import org.apache.paimon.types.RowType; + +/** + * The wrapper of {@link PaimonArrowFormatWriter} to expose JVM off heap address to + * BE. + * TODO: this class is based on ${@link ArrowFormatCWriter} to allow the customization + * of Field writer. will remove if relevant PR is accepted by paimon + * community. Refer to + * ${@link ...} + * for more detail. + */ +public class PaimonArrowFormatNativeWriter implements AutoCloseable { + // arrow array vector + private final ArrowArray array_; + // arrow schema + private final ArrowSchema schema_; + // arrow RecordBatch writer. + private final PaimonArrowFormatWriter realWriter_; + + public PaimonArrowFormatNativeWriter( + RowType rowType, int writeBatchSize, boolean caseSensitive) { + this(new PaimonArrowFormatWriter(rowType, writeBatchSize, caseSensitive)); + } + + public PaimonArrowFormatNativeWriter(RowType rowType, int writeBatchSize, + boolean caseSensitive, BufferAllocator allocator) { + this(new PaimonArrowFormatWriter(rowType, writeBatchSize, caseSensitive, allocator)); + } + + private PaimonArrowFormatNativeWriter(PaimonArrowFormatWriter arrowFormatWriter) { + this.realWriter_ = arrowFormatWriter; + BufferAllocator allocator = realWriter_.getAllocator(); + array_ = ArrowArray.allocateNew(allocator); + schema_ = ArrowSchema.allocateNew(allocator); + } + + public boolean write(InternalRow currentRow) { return realWriter_.write(currentRow); } + + public ArrowCStruct flush() { + realWriter_.flush(); + VectorSchemaRoot vectorSchemaRoot = realWriter_.getVectorSchemaRoot(); + return PaimonArrowUtils.serializeToCStruct( + vectorSchemaRoot, array_, schema_, realWriter_.getAllocator()); + } + + public void reset() { realWriter_.reset(); } + + public boolean empty() { return realWriter_.empty(); } + + public void release() { + array_.release(); + schema_.release(); + } + + @Override + public void close() { + array_.close(); + schema_.close(); + realWriter_.close(); + } + + public VectorSchemaRoot getVectorSchemaRoot() { + return realWriter_.getVectorSchemaRoot(); + } + + public BufferAllocator getAllocator() { return realWriter_.getAllocator(); } +} diff --git a/fe/src/main/java/org/apache/impala/util/paimon/PaimonArrowFormatWriter.java b/fe/src/main/java/org/apache/impala/util/paimon/PaimonArrowFormatWriter.java new file mode 100644 index 000000000..12d427c31 --- /dev/null +++ b/fe/src/main/java/org/apache/impala/util/paimon/PaimonArrowFormatWriter.java @@ -0,0 +1,126 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.impala.util.paimon; + +import org.apache.arrow.memory.BufferAllocator; +import org.apache.arrow.memory.RootAllocator; +import org.apache.arrow.vector.VectorSchemaRoot; +import org.apache.arrow.vector.util.OversizedAllocationException; +import org.apache.paimon.arrow.vector.ArrowFormatWriter; +import org.apache.paimon.arrow.writer.ArrowFieldWriter; +import org.apache.paimon.arrow.writer.ArrowFieldWriterFactoryVisitor; +import org.apache.paimon.data.InternalRow; +import org.apache.paimon.types.DataType; +import org.apache.paimon.types.RowType; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Write paimon Internal rows to arrow RecordBatch batch in Java side. + * TODO: this class is based on ${@link ArrowFormatWriter} to allow the customization + * of Field writer. will remove if relevant PR is accepted by paimon + * community. Refer to + * ${@link ...} + * for more detail. + */ +public class PaimonArrowFormatWriter implements AutoCloseable { + private static final Logger LOG = + LoggerFactory.getLogger(PaimonArrowFormatWriter.class); + // writer factory + private static final ArrowFieldWriterFactoryVisitor FIELD_WRITER_FACTORY = + new PaimonArrowFieldWriterFactory(); + // field type factory + private static final PaimonArrowFieldTypeFactory FIELD_TYPE_FACTORY = + new PaimonArrowFieldTypeFactory(); + // arrow vector schema root + private final VectorSchemaRoot vectorSchemaRoot_; + // arrow field writers + private final ArrowFieldWriter[] fieldWriters_; + // arrow RecordBatch batch size + private final int batchSize_; + // buffer allocator. + private final BufferAllocator allocator_; + // rowid for current batch. + private int rowId_; + + public PaimonArrowFormatWriter( + RowType rowType, int writeBatchSize, boolean caseSensitive) { + this(rowType, writeBatchSize, caseSensitive, new RootAllocator()); + } + + public PaimonArrowFormatWriter(RowType rowType, int writeBatchSize, + boolean caseSensitive, BufferAllocator allocator) { + this(rowType, writeBatchSize, caseSensitive, allocator, FIELD_WRITER_FACTORY); + } + + public PaimonArrowFormatWriter(RowType rowType, int writeBatchSize, + boolean caseSensitive, BufferAllocator allocator, + ArrowFieldWriterFactoryVisitor fieldWriterFactory) { + this.allocator_ = allocator; + + vectorSchemaRoot_ = PaimonArrowUtils.createVectorSchemaRoot( + rowType, allocator, caseSensitive, FIELD_TYPE_FACTORY); + + fieldWriters_ = new ArrowFieldWriter[rowType.getFieldCount()]; + + for (int i = 0; i < fieldWriters_.length; i++) { + DataType type = rowType.getFields().get(i).type(); + fieldWriters_[i] = type.accept(fieldWriterFactory) + .create(vectorSchemaRoot_.getVector(i), type.isNullable()); + } + + this.batchSize_ = writeBatchSize; + } + + public void flush() { vectorSchemaRoot_.setRowCount(rowId_); } + + public boolean write(InternalRow currentRow) { + if (rowId_ >= batchSize_) { return false; } + for (int i = 0; i < currentRow.getFieldCount(); i++) { + try { + fieldWriters_[i].write(rowId_, currentRow, i); + } catch (OversizedAllocationException | IndexOutOfBoundsException e) { + // maybe out of memory + LOG.warn("Arrow field writer failed while writing", e); + return false; + } + } + + rowId_++; + return true; + } + + public boolean empty() { return rowId_ == 0; } + + public void reset() { + for (ArrowFieldWriter fieldWriter : fieldWriters_) { fieldWriter.reset(); } + rowId_ = 0; + } + + @Override + public void close() { + vectorSchemaRoot_.close(); + allocator_.close(); + } + + public VectorSchemaRoot getVectorSchemaRoot() { return vectorSchemaRoot_; } + + public BufferAllocator getAllocator() { return allocator_; } +} diff --git a/fe/src/main/java/org/apache/impala/util/paimon/PaimonArrowUtils.java b/fe/src/main/java/org/apache/impala/util/paimon/PaimonArrowUtils.java new file mode 100644 index 000000000..059cc6297 --- /dev/null +++ b/fe/src/main/java/org/apache/impala/util/paimon/PaimonArrowUtils.java @@ -0,0 +1,137 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.impala.util.paimon; + +import org.apache.arrow.c.ArrowArray; +import org.apache.arrow.c.ArrowSchema; +import org.apache.arrow.c.Data; +import org.apache.arrow.memory.BufferAllocator; +import org.apache.arrow.vector.VectorSchemaRoot; +import org.apache.arrow.vector.complex.ListVector; +import org.apache.arrow.vector.complex.MapVector; +import org.apache.arrow.vector.types.Types; +import org.apache.arrow.vector.types.pojo.Field; +import org.apache.arrow.vector.types.pojo.FieldType; +import org.apache.arrow.vector.types.pojo.Schema; +import org.apache.paimon.arrow.ArrowFieldTypeConversion; +import org.apache.paimon.arrow.ArrowUtils; +import org.apache.paimon.arrow.vector.ArrowCStruct; +import org.apache.paimon.table.SpecialFields; +import org.apache.paimon.types.ArrayType; +import org.apache.paimon.types.DataField; +import org.apache.paimon.types.DataType; +import org.apache.paimon.types.MapType; +import org.apache.paimon.types.RowType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +import static org.apache.paimon.utils.StringUtils.toLowerCaseIfNeed; + +/** + * Utilities for creating Arrow objects. + * TODO: this class is based on ${@link ArrowUtils} to allow the customization + * of Field writer. will remove if relevant PR is accepted by paimon + * community. Refer to + * ${@link ...} + * for more detail. + */ +public class PaimonArrowUtils { + static final String PARQUET_FIELD_ID = "PARQUET:field_id"; + + public static VectorSchemaRoot createVectorSchemaRoot(RowType rowType, + BufferAllocator allocator, boolean caseSensitive, + ArrowFieldTypeConversion.ArrowFieldTypeVisitor visitor) { + List fields = + rowType.getFields() + .stream() + .map(f + -> toArrowField(toLowerCaseIfNeed(f.name(), caseSensitive), f.id(), + f.type(), visitor, 0)) + .collect(Collectors.toList()); + return VectorSchemaRoot.create(new Schema(fields), allocator); + } + + public static Field toArrowField(String fieldName, int fieldId, DataType dataType, + ArrowFieldTypeConversion.ArrowFieldTypeVisitor visitor, int depth) { + FieldType fieldType = dataType.accept(visitor); + fieldType = new FieldType(fieldType.isNullable(), fieldType.getType(), + fieldType.getDictionary(), + Collections.singletonMap(PARQUET_FIELD_ID, String.valueOf(fieldId))); + List children = null; + if (dataType instanceof ArrayType) { + Field field = toArrowField(ListVector.DATA_VECTOR_NAME, fieldId, + ((ArrayType) dataType).getElementType(), visitor, depth + 1); + FieldType typeInner = field.getFieldType(); + field = new Field(field.getName(), + new FieldType(typeInner.isNullable(), typeInner.getType(), + typeInner.getDictionary(), + Collections.singletonMap(PARQUET_FIELD_ID, + String.valueOf( + SpecialFields.getArrayElementFieldId(fieldId, depth + 1)))), + field.getChildren()); + children = Collections.singletonList(field); + } else if (dataType instanceof MapType) { + MapType mapType = (MapType) dataType; + + Field keyField = toArrowField(MapVector.KEY_NAME, fieldId, + mapType.getKeyType().notNull(), visitor, depth + 1); + FieldType keyType = keyField.getFieldType(); + keyField = new Field(keyField.getName(), + new FieldType(keyType.isNullable(), keyType.getType(), keyType.getDictionary(), + Collections.singletonMap(PARQUET_FIELD_ID, + String.valueOf(SpecialFields.getMapKeyFieldId(fieldId, depth + 1)))), + keyField.getChildren()); + + Field valueField = toArrowField(MapVector.VALUE_NAME, fieldId, + mapType.getValueType().notNull(), visitor, depth + 1); + FieldType valueType = valueField.getFieldType(); + valueField = new Field(valueField.getName(), + new FieldType(valueType.isNullable(), valueType.getType(), + valueType.getDictionary(), + Collections.singletonMap(PARQUET_FIELD_ID, + String.valueOf(SpecialFields.getMapValueFieldId(fieldId, depth + 1)))), + valueField.getChildren()); + + FieldType structType = new FieldType(false, Types.MinorType.STRUCT.getType(), null, + Collections.singletonMap(PARQUET_FIELD_ID, String.valueOf(fieldId))); + Field mapField = new Field(MapVector.DATA_VECTOR_NAME, + // data vector, key vector and value vector CANNOT be null + structType, Arrays.asList(keyField, valueField)); + + children = Collections.singletonList(mapField); + } else if (dataType instanceof RowType) { + RowType rowType = (RowType) dataType; + children = new ArrayList<>(); + for (DataField field : rowType.getFields()) { + children.add(toArrowField(field.name(), field.id(), field.type(), visitor, 0)); + } + } + return new Field(fieldName, fieldType, children); + } + + public static ArrowCStruct serializeToCStruct(VectorSchemaRoot vsr, ArrowArray array, + ArrowSchema schema, BufferAllocator bufferAllocator) { + Data.exportVectorSchemaRoot(bufferAllocator, vsr, null, array, schema); + return ArrowCStruct.of(array, schema); + } +} diff --git a/fe/src/main/java/org/apache/impala/util/paimon/PaimonJniScanner.java b/fe/src/main/java/org/apache/impala/util/paimon/PaimonJniScanner.java new file mode 100644 index 000000000..6fdf36d21 --- /dev/null +++ b/fe/src/main/java/org/apache/impala/util/paimon/PaimonJniScanner.java @@ -0,0 +1,221 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.impala.util.paimon; + +import com.google.common.collect.Lists; +import org.apache.arrow.memory.BufferAllocator; +import org.apache.commons.lang3.SerializationUtils; +import org.apache.impala.catalog.paimon.PaimonUtil; +import org.apache.impala.common.ImpalaException; +import org.apache.impala.common.JniUtil; +import org.apache.impala.planner.paimon.PaimonSplit; +import org.apache.impala.thrift.TPaimonJniScanParam; +import org.apache.paimon.arrow.vector.ArrowCStruct; +import org.apache.paimon.data.InternalRow; +import org.apache.paimon.predicate.Predicate; +import org.apache.paimon.reader.RecordReader; +import org.apache.paimon.reader.RecordReaderIterator; +import org.apache.paimon.table.Table; +import org.apache.paimon.table.source.ReadBuilder; +import org.apache.paimon.table.source.Split; +import org.apache.paimon.types.DataField; +import org.apache.paimon.types.RowType; +import org.apache.thrift.protocol.TBinaryProtocol; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * The Fe Paimon Jni Scanner, used by backend PaimonJniScanner. + */ +public class PaimonJniScanner implements AutoCloseable { + private final static Logger LOG = LoggerFactory.getLogger(PaimonJniScanner.class); + + public final static int DEFAULT_ROWBATCH_SIZE = 1024; + public final static long DEFAULT_INITIAL_RESERVATION = 32 * 1024; + + private final static TBinaryProtocol.Factory protocolFactory_ = + new TBinaryProtocol.Factory(); + // Paimon api table. + private Table table_ = null; + // Paimon splits assigned to the scanner. + private List splits_ = null; + // Paimon schema after projection. + private RowType projectedSchema_; + // Paimon data record iterator. + private RecordReaderIterator iterator_; + // batch size; + int batchSize_; + // paimon to arrow RecordBatch writer. + private PaimonArrowFormatNativeWriter writer_; + // arrow off heap allocator. + private BufferAllocator bufferAllocator_; + // total rows metrics. + private long totalRows_ = 0; + // upper bound mem limit. + // -1 means no limit. + private long allocator_mem_limit_ = -1; + + /** + * Constructor for PaimonJniScanner, will be called in Open + * method of BE PaimonJniScanNode. + * @param jni_scan_param_thrift: thrift form of paimon scan param. + * */ + public PaimonJniScanner(byte[] jni_scan_param_thrift) { + TPaimonJniScanParam paimonJniScanParam = new TPaimonJniScanParam(); + try { + JniUtil.deserializeThrift( + protocolFactory_, paimonJniScanParam, jni_scan_param_thrift); + } catch (ImpalaException ex) { LOG.error("failed to get paimon jni scan param"); } + // table + table_ = SerializationUtils.deserialize(paimonJniScanParam.getPaimon_table_obj()); + // splits + splits_ = Lists.newArrayList(); + for (ByteBuffer split_data : paimonJniScanParam.getSplits()) { + ByteBuffer split_data_serialized = split_data.compact(); + splits_.add(SerializationUtils.deserialize(split_data_serialized.array())); + } + // projection field ids + int[] projectionFieldIds = + paimonJniScanParam.getProjection().stream().mapToInt(Integer::intValue).toArray(); + // projected fields and schema + DataField[] projectedFields = + Arrays.stream(projectionFieldIds) + .mapToObj(fieldId -> table_.rowType().getField(fieldId)) + .toArray(DataField[] ::new); + projectedSchema_ = RowType.of(projectedFields); + // get batch size + batchSize_ = paimonJniScanParam.getBatch_size(); + if (batchSize_ <= 0) { batchSize_ = DEFAULT_ROWBATCH_SIZE; } + // get mem limit + allocator_mem_limit_ = paimonJniScanParam.getMem_limit_bytes(); + String allocatorName = + "paimonscan_" + table_.uuid() + paimonJniScanParam.getFragment_id().toString(); + // create allocator + if (allocator_mem_limit_ > 0) { + bufferAllocator_ = ArrowRootAllocation.rootAllocator().newChildAllocator( + allocatorName, DEFAULT_INITIAL_RESERVATION, allocator_mem_limit_); + } else { + bufferAllocator_ = ArrowRootAllocation.rootAllocator().newChildAllocator( + allocatorName, DEFAULT_INITIAL_RESERVATION, Long.MAX_VALUE); + } + LOG.info(String.format("Open with mem_limit: %d bytes, batch_size:%d rows, " + + "Projection field ids:%s", + allocator_mem_limit_, batchSize_, Arrays.toString(projectionFieldIds))); + } + + /** + * Perform table splits scanning, will be called in Open + * method of BE PaimonJniScanNode. + * */ + public void ScanTable() { + // If we are on a stack frame that was created through JNI we need to set the context + // class loader as Paimon might use reflection to dynamically load classes and + // methods. + JniUtil.setContextClassLoaderForThisThread(this.getClass().getClassLoader()); + writer_ = new PaimonArrowFormatNativeWriter( + projectedSchema_, batchSize_, false, bufferAllocator_); + // Create and scan the metadata table + initReader(); + } + + /** + * Get the next arrow batch, will be called in GetNext + * method of BE PaimonJniScanNode. + * @param address: return three long values to BE + * address[0]: schema address of arrow batch. + * address[1]: vector address of arrow batch. + * address[2]: offheap memory consumption for current batch. + * */ + public long GetNextBatch(long[] address) { + if (!writer_.empty()) { writer_.reset(); } + int rows = 0; + for (int i = 0; i < batchSize_; i++) { + if (iterator_.hasNext()) { + boolean result = writer_.write(iterator_.next()); + if (result) { rows++; } + } else { + break; + } + } + totalRows_ += rows; + if (rows > 0) { + ArrowCStruct cStruct = writer_.flush(); + address[0] = cStruct.schemaAddress(); + address[1] = cStruct.arrayAddress(); + address[2] = bufferAllocator_.getAllocatedMemory(); + return rows; + } else { + return 0; + } + } + + protected boolean initReader() { + try { + ReadBuilder readBuilder = table_.newReadBuilder().withReadType(projectedSchema_); + // Apply push down predicates if present. + // Currently predicates are always null/empty, + // All conjuncts are evaluated by the C++ scanner. + List predicates = splits_.get(0).getPredicates(); + if (predicates != null && !predicates.isEmpty()) { + readBuilder.withFilter(predicates); + } + // Create Iterator for given splits. + List splits = + splits_.stream().map(PaimonSplit::getSplit).collect(Collectors.toList()); + RecordReader reader = readBuilder.newRead().createReader(splits); + iterator_ = new RecordReaderIterator<>(reader); + LOG.info( + String.format("Reading %d splits for %s", splits.size(), table_.fullName())); + return true; + } catch (Exception ex) { + LOG.error("failed to init reader for " + table_.fullName(), ex); + return false; + } + } + + /** + * Perform clean up operation , will be called in Close + * method of BE PaimonJniScanNode. + * */ + @Override + public void close() throws Exception { + // release writer resources. + PaimonUtil.closeQuitely(writer_); + + // release arrow allocator resources owned by current scanner. + PaimonUtil.closeQuitely(bufferAllocator_); + // used to check mem leak in more detail if arrow allocation + // debug is turned on. + if (bufferAllocator_.getAllocatedMemory() > 0) { + LOG.error( + String.format("Leaked memory for %s is %d bytes, dump:%s", table_.fullName(), + bufferAllocator_.getAllocatedMemory(), bufferAllocator_.toVerboseString())); + } + LOG.info(String.format("Peak memory for %s is %d bytes, total rows: %d", + table_.fullName(), bufferAllocator_.getPeakMemoryAllocation(), totalRows_)); + + // release iterator resources + PaimonUtil.closeQuitely(iterator_); + } +} diff --git a/fe/src/test/java/org/apache/impala/catalog/paimon/ImpalaTypeUtilsTest.java b/fe/src/test/java/org/apache/impala/catalog/paimon/ImpalaTypeUtilsTest.java index 53418b952..9544f4dec 100644 --- a/fe/src/test/java/org/apache/impala/catalog/paimon/ImpalaTypeUtilsTest.java +++ b/fe/src/test/java/org/apache/impala/catalog/paimon/ImpalaTypeUtilsTest.java @@ -97,11 +97,11 @@ public class ImpalaTypeUtilsTest { // Test row type RowType rowType = new RowType(Arrays.asList(new DataField(0, "id", new IntType()), new DataField(1, "name", DataTypes.STRING()))); - StructType expectedStructType = new StructType(Arrays.asList( - new StructField("id", Type.INT, - rowType.getField(0).description()), - new StructField("name", Type.STRING, - rowType.getField(1).description()))); + StructType expectedStructType = new StructType( + Arrays.asList(new PaimonStructField("id", Type.INT, + rowType.getField(0).description(), rowType.getField(0).id()), + new PaimonStructField("name", Type.STRING, rowType.getField(1).description(), + rowType.getField(1).id()))); assertEquals(expectedStructType, PaimonImpalaTypeUtils.toImpalaType(rowType)); // doesn't support time diff --git a/java/pom.xml b/java/pom.xml index 4eed32513..f9abb3b84 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -42,6 +42,7 @@ under the License. ${env.IMPALA_HIVE_DIST_TYPE} ${env.IMPALA_HUDI_VERSION} ${env.IMPALA_PAIMON_VERSION} + ${env.IMPALA_ARROW_VERSION} ${env.IMPALA_RANGER_VERSION} ${env.IMPALA_POSTGRES_JDBC_DRIVER_VERSION} ${env.IMPALA_HBASE_VERSION} diff --git a/testdata/data/paimon_test/README.md b/testdata/data/paimon_test/README.md new file mode 100644 index 000000000..752b2b9c6 --- /dev/null +++ b/testdata/data/paimon_test/README.md @@ -0,0 +1,115 @@ +# Paimon Test Tables + +This README.md explain the schema and usage of paimon test tables in the folder. +the table data in the folder will be directly copied to hdfs and external paimon +tables will be created accordingly for these tables. + +## paimon_non_partitioned +The subset of table ratings in movielens dataset, it is not partitioned. +the schema is: ++-----------+--------+---------+ +| userid | int | | +| movieid | int | | +| rating | float | | +| timestamp | bigint | | +-------------------------------- +## paimon_partitioned +The subset of table ratings in movielens dataset, it is partitioned. +the schema is: ++-----------+--------+---------+ +| userid | int | | +| movieid | int | | +| rating | float | | +| timestamp | bigint | | +-------------------------------- +## paimon_primitive_alltypes +The table contains all primitive types supported by paimon: +schema is: ++---------------+---------------+---------+ +| name | type | comment | ++---------------+---------------+---------+ +| bool_value | boolean | | +| tiny_value | tinyint | | +| small_value | smallint | | +| int_value | int | | +| big_value | bigint | | +| float_value | float | | +| double_value | double | | +| decimal_value | decimal(10,2) | | +| char_value | char(10) | | +| varchar_value | varchar(100) | | +| binary_value | binary | | +| date_value | date | | +| ts_ltz_value | timestamp | | +| ts_value | timestamp | | ++---------------+---------------+---------+ +## paimon_decimal_tbl +The table is used to support decimal related test,with various precision and scale. +the schema is: ++------+----------------+---------+ +| name | type | comment | ++------+----------------+---------+ +| d1 | decimal(9,0) | | +| d2 | decimal(10,0) | | +| d3 | decimal(20,10) | | +| d4 | decimal(38,38) | | +| d5 | decimal(10,5) | | ++------+----------------+---------+ +## paimon_decimal_tbl +The table is used to support decimal related test,with various precision and scale. +the schema is: ++------+----------------+---------+ +| name | type | comment | ++------+----------------+---------+ +| d1 | decimal(9,0) | | +| d2 | decimal(10,0) | | +| d3 | decimal(20,10) | | +| d4 | decimal(38,38) | | +| d5 | decimal(10,5) | | ++------+----------------+---------+ +## alltypes_paimon +the table is table alltypes with paimon format, it is used to support test test_scanner +for paimon format. +the schema is: ++-----------------+-----------+---------------+ +| name | type | comment | ++-----------------+-----------+---------------+ +| id | int | Add a comment | +| bool_col | boolean | | +| tinyint_col | tinyint | | +| smallint_col | smallint | | +| int_col | int | | +| bigint_col | bigint | | +| float_col | float | | +| double_col | double | | +| date_string_col | string | | +| string_col | string | | +| timestamp_col | timestamp | | +| year | int | | +| month | int | | ++-----------------+-----------+---------------+ +## alltypes_structs_paimon +the table is table alltypes_structs with paimon format, it is used to support negative +cases for complex and nested field query suuport. ++------------+---------------------------+---------+ +| name | type | comment | ++------------+---------------------------+---------+ +| id | int | | +| struct_val | struct< | | +| | bool_col:boolean, | | +| | tinyint_col:tinyint, | | +| | smallint_col:smallint, | | +| | int_col:int, | | +| | bigint_col:bigint, | | +| | float_col:float, | | +| | double_col:double, | | +| | date_string_col:string, | | +| | string_col:string | | +| | > | | +| year | int | | +| month | int | | +---------------------------------------------------- +## TODO: +Most of testing tables should be removed later once paimon write is supported +for impala, if these table generation can be easily implemented using DML or +CTAS statement. \ No newline at end of file diff --git a/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_paimon/bucket-0/data-275038ac-cf80-4caa-b9c3-43854884f257-0.parquet b/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_paimon/bucket-0/data-275038ac-cf80-4caa-b9c3-43854884f257-0.parquet new file mode 100644 index 0000000000000000000000000000000000000000..bd689f6d484b6e81df592ff45aa68223b8f16c81 GIT binary patch literal 9033 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 B6@>r* literal 0 HcmV?d00001 diff --git a/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_paimon/bucket-0/data-4724d1a4-637d-4275-8439-d4edd1a4583e-0.parquet b/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_paimon/bucket-0/data-4724d1a4-637d-4275-8439-d4edd1a4583e-0.parquet new file mode 100644 index 0000000000000000000000000000000000000000..c4fab82ec1315d48aa97e810c36ed574ee9e2ccc GIT binary patch literal 8846 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 Apa1{> literal 0 HcmV?d00001 diff --git a/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_paimon/bucket-0/data-7201ded8-6883-42ef-a39f-a99fc8a83938-0.parquet b/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_paimon/bucket-0/data-7201ded8-6883-42ef-a39f-a99fc8a83938-0.parquet new file mode 100644 index 0000000000000000000000000000000000000000..576ebb557c28987e184b9bc0d467c88a9b5b4348 GIT binary patch literal 8946 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 G%YOk`kphqa literal 0 HcmV?d00001 diff --git a/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_paimon/bucket-0/data-7e80aa3a-5863-4cb5-bdb4-84d0d9002dd0-0.parquet b/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_paimon/bucket-0/data-7e80aa3a-5863-4cb5-bdb4-84d0d9002dd0-0.parquet new file mode 100644 index 0000000000000000000000000000000000000000..db82bbf86f4c807557bfd79b164ac0f72ba0469f GIT binary patch literal 9406 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 v6=CbR*ecS&-YOz8EXvx>I%p{om01Kt!Wd>y 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 V_%~1<{=fMEG0^X?ZqVOY{ts;aCSFmEx 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 literal 0 HcmV?d00001 diff --git a/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_paimon/bucket-0/data-c079a770-9eb6-4b91-b3c8-f6c84fbaf7ce-0.parquet b/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_paimon/bucket-0/data-c079a770-9eb6-4b91-b3c8-f6c84fbaf7ce-0.parquet new file mode 100644 index 0000000000000000000000000000000000000000..282b23ab590a17656a0d937936b2a76ea826bcf4 GIT binary patch literal 8956 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 P4{!wjeb5K~r^J5&i0L!O literal 0 HcmV?d00001 diff --git a/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_paimon/bucket-0/data-dffe00a3-de35-4a74-96df-cd0fb7d08070-0.parquet b/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_paimon/bucket-0/data-dffe00a3-de35-4a74-96df-cd0fb7d08070-0.parquet new file mode 100644 index 0000000000000000000000000000000000000000..24163a021d2ca0e621dd43c588a810a159eb3cdf GIT binary patch literal 9112 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 p8yRW^1qXy^8)%2EtscV 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 literal 0 HcmV?d00001 diff --git a/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_paimon/bucket-0/data-eef44c7a-9856-4817-8f1f-a2a7dbc331c5-0.parquet b/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_paimon/bucket-0/data-eef44c7a-9856-4817-8f1f-a2a7dbc331c5-0.parquet new file mode 100644 index 0000000000000000000000000000000000000000..5e4ad052b56776d31e8bcf4d2993c53b14520ccb GIT binary patch literal 9169 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 literal 0 HcmV?d00001 diff --git a/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_paimon/manifest/manifest-fab63fbc-acd6-424a-ac4e-2a848ea51f52-0 b/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_paimon/manifest/manifest-fab63fbc-acd6-424a-ac4e-2a848ea51f52-0 new file mode 100644 index 0000000000000000000000000000000000000000..4a2f6fc3b6a5813dded7d3c24947011672ab2b74 GIT binary patch literal 2715 zcmeZI%3@>@ODrqO*DFrWNX<>0AYH9gQdy9yWTjM;nw(#hqNJmgmzayeFD^(-1_|aD zrRyaE*%_&N1&Nut`FVO^!_rgpQi~ExQbF3&GE;L>ij}OQt6?U^hq(p?d;0qUC82g@ z=9MVb>L3(&d-}QHmJM(W3h@j-~-QA$ZoODxSP zQL+N*tc|TjPK;nj!2JU;5^gm1Tmnf6@ep6awGRED0L5IeYhb9WpEEEuhx$6X1|iZI zW-0_GG2&GRJA1hLI>vhnLR!|}ip(=`GfG;mWO86TfZAZ3n!Xpl3a z)PZXUg)lzNNO=R83w;p54l@BBX1IbMpXtE(bPe(Y7SjQaAs(21gBG9|CIGW(>TN@7t8gIc#vtyWh~-O)oPlhe)D*h}>BX>8U1%i!@&Gkhs4tyz+p|!IZ_dN!pi_G%Pxtq;#y!S{pxFS zec0xGM!)`UsQ6k_T(xDx^7Ct^AJNcbeRr#25tEHYKt+Pf|K`_{}pvv1>>Q)L{> zTm1TGIZuiD^z0S`yGe<}8JS9k9=;DZ9G_fR96lp<{g(WrDfV;jU*Ej-e#!RlVY-%% zegRXKM8A!kaO?p09PuM8)thHU_qt3@;PC$YQZ#D5N#EDqcGlhhZRLCKuIt%%T1ALk zu4V^|=k`C5Yp;q3u9!F>iD#ld;|fO?A*(L`eHO}lgU;>ybavmh+Wit6`>tMl#+Q|_ zX#S+_vpKD;6T4g{PxR%JwSDmP-SHpcHTU~Zt$DuWzMOup)#K0HySQ9WH54$KhAHVN zG9J04E$Hm5ckABb$kxke_q{#VrdyXBKE+n?@BV91C!7?gr7Y93;#^fT@!{F7b46Dh z7jlUC^{@7r@sevLo9K5Xwe|bI&)r@cye<0o->bLw>FHGKXSYSZWvO&7ojIZEK=ab` z8JtP+d+N4Dy}dr|eRBMT+VCG6{@r|K{X?qZ$qkmuYyl_r&WY-8&T=>kE6TrZdgs=4 z_T?V+wVSh>?+0GHJ$-THQ8hN{Pj0_54{~tnZs9n{G$-ZB0weX-Z3nZ~H}tG1w3#Hs z>7_Cua6;3ff;kwE3PiX_C>}Cra-A?ifbjmBo_ulU&?QMlde zUbTsBi@>{&JTj8ZOWUk%IM>W)zr7{rPcw_$eXF42&ZB0l`NNlc>|jl-=}K>0E+ZWK z-)R|J5T{~j#*xynJ5yO&9T%|O`;h5evfx74{O^AxPxvUUUoii;>XeFLcK*KPbW?eL H^qdI*iG21J literal 0 HcmV?d00001 diff --git a/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_paimon/manifest/manifest-list-fa3d4f50-7f45-4d37-b5e7-22e6cf3f2cb0-0 b/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_paimon/manifest/manifest-list-fa3d4f50-7f45-4d37-b5e7-22e6cf3f2cb0-0 new file mode 100644 index 0000000000000000000000000000000000000000..d6ce643706f2f4769eca5b39ee1bf5c1436b743e GIT binary patch literal 884 zcmbVKPfNov6j$)*Nks2Ck9yxNwh@bICu=*zQ9{;cTcu4$nu&81Jb3p*`8hlYeg;7g zVl(Pm?a;YflK1=b-tVO|xZ2)1pw}YRvS7>-`no#@ayDV04Fn5P5rP)rG-=$k2@TX^ zDn=SD&SR!cXq2Q}TZoMqXM#$m=);J`VFud$`Ii#up{`?g?4nqDMO=csRRK;+jED^_ zR6#N+B0g%O9P_eg^6@l4akfx8TJZDBHuB=pE0lb=e1e8poi>yNsU-L1#rYkz|S5R9Od$ zjQrehfshU9G?t*Pn7NnO;oH;4-HrbE{Brc}-G2i77#}MD literal 0 HcmV?d00001 diff --git a/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_paimon/manifest/manifest-list-fa3d4f50-7f45-4d37-b5e7-22e6cf3f2cb0-1 b/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_paimon/manifest/manifest-list-fa3d4f50-7f45-4d37-b5e7-22e6cf3f2cb0-1 new file mode 100644 index 0000000000000000000000000000000000000000..83a4052ba46fc8237658a7b261520c0d72979220 GIT binary patch literal 986 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 literal 0 HcmV?d00001 diff --git a/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_structs_paimon/bucket-0/data-0ff3cbcd-bd56-43ef-8b00-543a83cc5e6f-0.parquet b/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_structs_paimon/bucket-0/data-0ff3cbcd-bd56-43ef-8b00-543a83cc5e6f-0.parquet new file mode 100644 index 0000000000000000000000000000000000000000..fd24e7cb08500c6e5287847d9b692d90d71e0d94 GIT binary patch literal 4841 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 V62AlT>wkTLa`>xiG5jaje*jrYe`5du literal 0 HcmV?d00001 diff --git a/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_structs_paimon/bucket-0/data-15fe0349-ce6f-4228-9031-f1d556dfc808-0.parquet b/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_structs_paimon/bucket-0/data-15fe0349-ce6f-4228-9031-f1d556dfc808-0.parquet new file mode 100644 index 0000000000000000000000000000000000000000..acbc979e6055d33de972474f9500bdd99e154775 GIT binary patch literal 4835 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 literal 0 HcmV?d00001 diff --git a/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_structs_paimon/bucket-0/data-4e2d864e-0aa9-4b5e-8b6d-4261540072f3-0.parquet b/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_structs_paimon/bucket-0/data-4e2d864e-0aa9-4b5e-8b6d-4261540072f3-0.parquet new file mode 100644 index 0000000000000000000000000000000000000000..06d457051d753269c65a2e7435e49ee4dbab9cc5 GIT binary patch literal 4808 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 Y>}wo=FIrv2?75|6zA1taFK>z>% literal 0 HcmV?d00001 diff --git a/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_structs_paimon/bucket-0/data-602c116d-3e51-40b6-914d-950170ece098-0.parquet b/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_structs_paimon/bucket-0/data-602c116d-3e51-40b6-914d-950170ece098-0.parquet new file mode 100644 index 0000000000000000000000000000000000000000..72f84d0f29ff984341a38acc3c0d6a6bb00feef6 GIT binary patch literal 4742 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 VY8LXl5WW7-4=6w`u0sDn{TD(9Aoc(N literal 0 HcmV?d00001 diff --git a/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_structs_paimon/bucket-0/data-6a5bf48f-e00f-45dc-8d44-62582cf6d881-0.parquet b/testdata/data/paimon_test/paimon_catalog/warehouse/functional.db/alltypes_structs_paimon/bucket-0/data-6a5bf48f-e00f-45dc-8d44-62582cf6d881-0.parquet new file mode 100644 index 0000000000000000000000000000000000000000..c569e3be223c4017dcb3c2d2b5289b4dc5b093f9 GIT binary patch literal 4739 zcmd^DdsGuw8ov`l5<-y2a5ISn2myuwLK4DL(85+lyX{sT>1^w6EeN!?e)PEj;DAFN4VeU{N(c0n!y8(U-Ri4bgwV{WXIJ7gYP*O@+lnde!cV5(yOV1 zn;rA{L@v_(TIs2nt09AH9mRY+7w&HBJbC%bzXlo{`Fso)=6<#GpD*?y|Q;;vBS*cT!{OX(i1UPULW{{!^nqoO83j1$1nHR3}iWUd?**}UR!#6NAH3G zlOuvxazXCa&X1S&rVd0oRD2MJxm)PB;xPATmq(mIMc9)dyycGT1@Cc>YmN6W9@q2U zcRenrcaz8E@V0tfzw@s0xK?}1J+5Z&B9E)dTjFswdTkz8gEztBTIG%Oxaz%_5-jeQ z$wR*=Z5>IfkC)$S{P;!9;YYDMdq;X+R2_cQHF9(I19$Yjvt?hTw2Xjym&Mig*)h+h z;jf&xc_&w7;gp}P@f;cchw~=i%+0j?vF+1B&-USS&OUw>mv7mr{B-2HbNHz93w{Nc zYuVA(eeC+MHP;J<-*I;FRa};3ds|oGb^Y*q=PACNvs$((yGHtd z7<%5x^S|XXEo|HQWBr$go^>ANALnLRwkprB=|3{`gmXV%#-&-_ZaY`lzkTQ-=Wc!x zXSQrno*U_N4$XA#T+J5$)F zA5uGC;Y&E9Wux-U$h98^?>b)KXK`_sx7s?7UAr{+m2O|_8?Dc^{<1Z_HMI5Xmh&z9 zTJ~igRUN+jn`6Ik$1<^YkO|tU+4yW>7Bh>Qh0hX-m?EkOFA@rwLaGoi6lO9rshRjp zp@1o%3h)9UpUJ24@q8hV$)ob{JRz6KrE>9HA&1GKa_}4>o5`lK@od4)*eN@17qXZv zDhtmNY>bVv;Woj_SSc%R6)cQ}vfvgWlgXqq@k}9u$)Gau3}FT{gPMWQ5Ym}+DjiQ3 z(wHo;zl8!iKpW6cp;97qvG(mJ0YT(HPdFoET*t2bPACo zk}OG+1Sux7$#gQ2EGDr@bP|yyCbEfiB9SPXSQBj`Okx6?Kqn9hqLDSyM#3n@v+;C1 z5iiEEadaFJC&sd|bSx1o8dw8uAPiy*8$-tsG2(P~Iz643F6vo5tta%Nj@8jRLMIX| zK@$WaYFRC2^o(bI@&Vi+4nhY?|-hSks-LL-K